Index: docs/CommandGuide/FileCheck.rst =================================================================== --- docs/CommandGuide/FileCheck.rst +++ docs/CommandGuide/FileCheck.rst @@ -480,3 +480,63 @@ letting us set the :program:`FileCheck` variable ``DLOC`` to the desired value ``0x00000233``, extracted from the line immediately preceding "``intd``". + +Templates +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some regular expressions are used very often. In this case directive for describing template +of regular expression may be used . + +For example + +.. code-block:: c++ + + // CHECK-PATTERN hex_num: {{0x[0-9a-fA-F]+}} + +This directive only describes template and doesn’t match anything. +After describing new template it’s possible to use this regular expression by its template +name in any directive CHECK, CHECK-NEXT, CHECK-SAME and etc. + +For example + +.. code-block:: c++ + + // CHECK : reg\#(hex_num) + +This check is equal to + +.. code-block:: c++ + + // CHECK : reg{{0x[0-9a-fA-F]+}} + +Templates can have parameters for cases when there is same part, but also there are always +some changing parts. + +For example + +.. code-block:: c++ + + // CHECK-PATTERN reg_hex_num: {{\:(regName)0x[0-9a-fA-F]+}} + +regName is parameter instead of which will be placed another string at the moment of matching. +There can be mane parameters and parameters can be used some times in different parts of template. +To use such templates you need to set values for each parameter. + +Example + +.. code-block:: c++ + + // CHECK : INC \#( reg_hex_num)\:(regName)\=(reg) + +This is equal to + +.. code-block:: c++ + + // CHECK : INC reg{{0x[0-9a-fA-F]+}} + +Parameters are useful if there are a lot of similar checks in file. + +Syntax: +\#(template_name) - use of template 'template_name' +\:(Variable_name) - template variable with name 'variable_name' +\=(value) - current value of template variable(it's needed when you use template with variables). \ No newline at end of file Index: test/FileCheck/check-pattern-prefixes.txt =================================================================== --- /dev/null +++ test/FileCheck/check-pattern-prefixes.txt @@ -0,0 +1,11 @@ +// RUN: FileCheck -input-file %s %s -check-prefix=FOO +// RUN: FileCheck -input-file %s %s -check-prefix=BAR + +// FOO-PATTERN thing: foo-thing +// BAR-PATTERN thing: bar-thing + +foo-thing +bar-thing + +// FOO: {{\#(thing)}} +// BAR: {{\#(thing)}} Index: test/FileCheck/check-pattern.txt =================================================================== --- /dev/null +++ test/FileCheck/check-pattern.txt @@ -0,0 +1,24 @@ +// RUN: FileCheck -input-file %s %s + +op0xA1; +op0xc2; +op0x33; +// CHECK-PATTERN hex_num: {{0x[0-9a-fA-F]+}} +// CHECK-PATTERN reg: {{\:(RegName)\#(hex_num)}} +// CHECK-PATTERN operation: {{\:(OpName) \#(reg)\:(RegName)\=(r), \#(reg)\:(RegName)\=(reg)}} +// CHECK-PATTERN inc: {{\:(VarName) = \:(VarName)\+\:(Number)}} +// CHECK-PATTERN alt: {{x|y}} +// CHECK-PATTERN altincheck: check {{\#(alt)}} + +// CHECK: {{op\#(hex_num)}}; +reg0x0 +// CHECK: {{\#(reg)\:(RegName)\=(reg)}} + +ADD r0xA, reg0x10 +// CHECK: {{\#(operation)\:(OpName)\=(ADD)}} + +TEMP = TEMP+4 +// CHECK: {{\#(inc)\:(Number)\=(4)\:(VarName)\=(TEMP)}} + +check y +// CHECK: {{\#altincheck}} Index: utils/FileCheck/FileCheck.cpp =================================================================== --- utils/FileCheck/FileCheck.cpp +++ utils/FileCheck/FileCheck.cpp @@ -16,6 +16,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" @@ -88,6 +89,7 @@ CheckNot, CheckDAG, CheckLabel, + CheckPattern, /// MatchEOF - When set, this pattern only matches the end of file. This is /// used for trailing CHECK-NOTs. @@ -131,6 +133,12 @@ /// getLoc - Return the location in source code. SMLoc getLoc() const { return PatternLoc; } + /// getRegExStr - Return regex pattern. + std::string getRegExStr() const { return RegExStr; } + + /// getRegExStr - Return fixed string. + std::string getFixedStr() const {return FixedStr.str();} + /// ParsePattern - Parse the given string into the Pattern. Prefix provides /// which prefix is being matched, SM provides the SourceMgr used for error /// reports, and LineNumber is the line number in the input file from which @@ -180,8 +188,171 @@ /// \return offset of the closing sequence within Str, or npos if it was not /// found. size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); + + /// Replace templates if they are to associated with them regular expressions. + /// \return true if there is error in regular expression + bool ReplaceTemplates(StringRef RS, SourceMgr &SM, std::string &ResultRegex); +}; + +class PatternTemplate { + /// Name of template. + std::string TemplateName; + /// Pattern which describes this template. + Pattern TemplatePattern; + /// Template parameters. + std::map Parameters; + +public: + PatternTemplate(std::string Name, + Check::CheckType Ty = Check::CheckType::CheckNone) + : TemplateName(Name), TemplatePattern(Pattern(Ty)) {} + + PatternTemplate(Check::CheckType Ty = Check::CheckType::CheckNone) + : TemplatePattern(Pattern(Ty)) {} + + /// Parse the given string for getting parameters of template. + /// \return true if string has error in description regular expression or + /// parameters. + bool ParseTemplate(StringRef TemplateString, StringRef Prefix, SourceMgr &SM, + unsigned LineNumber); + + /// Get template name. + /// \return template name. + const std::string &GetTemplateName() const; + + /// Get number of parameters which this template includes. + size_t getNumParameters() const; + + /// Checks if this template has such parameter + /// \return true if template has. + bool HasParameter(StringRef ParameterName) const; + + /// Generate result regular expression for matching with inserted parameters + /// values. + std::string + GetRegularExpression(StringMap &ParametersValues) const; }; +bool PatternTemplate::ParseTemplate(StringRef TemplateString, StringRef Prefix, + SourceMgr &SM, unsigned LineNumber) { + + if (TemplatePattern.ParsePattern(TemplateString, Prefix, SM, LineNumber)) + return true; + + // Find parameters in pattern + SmallVector Matches; + Regex Parameter(StringRef("\\\\:\\(([a-zA-Z_0-9]+)\\)")); + size_t DiffInLoc = 0; + StringRef PatternString = StringRef(TemplatePattern.getRegExStr()); + + while (Parameter.match(PatternString, &Matches)) { + size_t ParameterPos = Matches[0].data() - PatternString.data(); + Parameters.emplace(ParameterPos + DiffInLoc, Matches[1]); + PatternString = + PatternString.drop_front(ParameterPos + Matches[1].size() + 3); + DiffInLoc += ParameterPos + Matches[1].size() + 3; + } + + return false; +} + +const std::string &PatternTemplate::GetTemplateName() const { + return TemplateName; +} + +size_t PatternTemplate::getNumParameters() const { + SmallSet UniqueParameters; + for (const auto &Parameter : Parameters) + UniqueParameters.insert(Parameter.second); + return UniqueParameters.size(); +} + +bool PatternTemplate::HasParameter(StringRef ParameterName) const { + for (const auto &Parameter : Parameters) { + if (Parameter.second == ParameterName) { + return true; + } + } + return false; +} + +std::string PatternTemplate::GetRegularExpression( + StringMap &ParametersValues) const { + std::string Regex = TemplatePattern.getRegExStr(); + + if (Regex.empty()) + Regex = "(" + TemplatePattern.getFixedStr() + ")"; + + int RegexWithoutParametersLoc = + 0; // variable for place of regex part without variables. + SmallVector RegexComponents; + // Change all parameters to their values. + for (const auto &Parameter : Parameters) { + StringRef Value = ParametersValues[Parameter.second]; + RegexComponents.push_back( + Regex.substr(RegexWithoutParametersLoc, + Parameter.first - RegexWithoutParametersLoc)); + RegexWithoutParametersLoc = Parameter.first + Parameter.second.size() + 4; + RegexComponents.push_back("(" + Value.str() + ")"); + } + RegexComponents.push_back(Regex.substr(RegexWithoutParametersLoc)); + if (!Parameters.empty()) + Regex = join(RegexComponents.begin(), RegexComponents.end(), ""); + + return Regex; +} + +class TemplatesCollection { + /// Described templates + StringMap Templates; + /// Last added template (for generating error) + std::string LastAddedTemplate; + + TemplatesCollection(); + +public: + static TemplatesCollection &Instance() { + static TemplatesCollection singleton; + return singleton; + } + /// Add template + bool AddTemplate(PatternTemplate *AddedTemplate); + /// Get template by its name. + PatternTemplate *GetTemplate(const StringRef &TemplateName); + /// Get last added template + PatternTemplate *GetLastAddedTemplate(); + /// Check if there is current template in collection. + bool HasTemplate(const StringRef &TemplateName); +}; + +TemplatesCollection::TemplatesCollection() {} + +bool TemplatesCollection::AddTemplate(PatternTemplate *AddedTemplate) { + if (Templates.find(AddedTemplate->GetTemplateName()) != Templates.end()) { + return false; + } + LastAddedTemplate = AddedTemplate->GetTemplateName(); + return Templates + .insert(std::pair( + AddedTemplate->GetTemplateName(), AddedTemplate)) + .second; +} + +PatternTemplate * +TemplatesCollection::GetTemplate(const StringRef &TemplateName) { + const auto PatternIt = Templates.find(TemplateName); + if (PatternIt == Templates.end()) + return nullptr; + return PatternIt->second; +} + +PatternTemplate *TemplatesCollection::GetLastAddedTemplate() { + return Templates[LastAddedTemplate]; +} + +bool TemplatesCollection::HasTemplate(const StringRef &TemplateName) { + return Templates.find(TemplateName) != Templates.end(); +} bool Pattern::ParsePattern(StringRef PatternStr, StringRef Prefix, @@ -361,6 +532,11 @@ bool Pattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) { + std::string ResultRegex; + if (ReplaceTemplates(RS, SM, ResultRegex)) + return true; + + RS = StringRef(ResultRegex); Regex R(RS); std::string Error; if (!R.isValid(Error)) { @@ -607,6 +783,101 @@ return StringRef::npos; } +bool Pattern::ReplaceTemplates(StringRef RS, SourceMgr &SM, + std::string &ResultRegex) { + // Replace templates with regular expressions. + SmallVector MatchInfo; + size_t DiffInLoc = 0; + StringRef RegexStr = StringRef(RS); + + while (Regex(StringRef("\\\\#\\(([a-zA-Z0-9_]+)\\)")) + .match(RegexStr, &MatchInfo)) { + // Check if template was declared + if (!TemplatesCollection::Instance().HasTemplate(MatchInfo[1])) { + SM.PrintMessage( + SMLoc::getFromPointer(RS.data() + DiffInLoc + + (MatchInfo[1].data() - RegexStr.data())), + SourceMgr::DK_Error, + "template " + MatchInfo[1].str() + " wasn't declared"); + return true; + } + // Add pat of regex before template usage. + ResultRegex = + ResultRegex + + RegexStr.substr(0, MatchInfo[0].data() - RegexStr.data()).str(); + RegexStr = RegexStr.drop_front(MatchInfo[0].data() - RegexStr.data() + + MatchInfo[0].size()); + DiffInLoc += MatchInfo[0].data() - RegexStr.data() + MatchInfo[0].size(); + PatternTemplate *Temp = + TemplatesCollection::Instance().GetTemplate(MatchInfo[1]); + + // Get template parameteres. + StringMap ParametersValues; + while (RegexStr.startswith("\\:(")) { + size_t ParameterNameEnd = RegexStr.find(')'); + if (ParameterNameEnd == StringRef::npos) { + SM.PrintMessage(SMLoc::getFromPointer(RS.data() + DiffInLoc), + SourceMgr::DK_Error, + "wrong description of parameter. No closed bracket."); + return true; + } + StringRef ParameterName = RegexStr.substr(3, ParameterNameEnd - 3); + + // Check if template has such parameter in description. + if (!Temp->HasParameter(ParameterName)) { + SM.PrintMessage(SMLoc::getFromPointer(RS.data() + DiffInLoc), + SourceMgr::DK_Error, + "template " + MatchInfo[1].str() + + " doesn't have parameter " + ParameterName.str()); + return true; + } + + // Find value of parameter. + DiffInLoc += ParameterNameEnd + 1; + RegexStr = RegexStr.drop_front(ParameterNameEnd + 1); + if (!RegexStr.startswith("\\=(")) { + SM.PrintMessage(SMLoc::getFromPointer(RS.data() + DiffInLoc), + SourceMgr::DK_Error, + "parameter " + ParameterName.str() + " has no value"); + return true; + } + + size_t ParameterValueEnd = RegexStr.find(')'); + if (ParameterValueEnd == StringRef::npos) { + SM.PrintMessage( + SMLoc::getFromPointer(RS.data() + DiffInLoc), SourceMgr::DK_Error, + "wrong description of parameter value. No closed bracket."); + return true; + } + StringRef ParameterValue = RegexStr.substr(3, ParameterValueEnd - 3); + + // TODO - may value be template? + ParametersValues.insert( + std::pair(ParameterName, ParameterValue)); + + DiffInLoc += ParameterValueEnd + 1; + RegexStr = RegexStr.drop_front(ParameterValueEnd + 1); + } + + if (Temp->getNumParameters() != ParametersValues.size()) { + SM.PrintMessage(SMLoc::getFromPointer(RS.data() + DiffInLoc), + SourceMgr::DK_Error, + "not all parameters of template " + + Temp->GetTemplateName() + " are defined"); + return true; + } + + // Get regular expression by template. + std::string FullTemplateRegex = + Temp->GetRegularExpression(ParametersValues); + // Add template regex. + ResultRegex = ResultRegex + FullTemplateRegex; + } + // Add left part without templates. + ResultRegex = ResultRegex + RegexStr.str(); + return false; +} + //===----------------------------------------------------------------------===// // Check Strings. @@ -696,63 +967,60 @@ return (isalnum(c) || c == '-' || c == '_'); } -// Get the size of the prefix extension. -static size_t CheckTypeSize(Check::CheckType Ty) { - switch (Ty) { - case Check::CheckNone: - case Check::CheckBadNot: - return 0; - - case Check::CheckPlain: - return sizeof(":") - 1; - - case Check::CheckNext: - return sizeof("-NEXT:") - 1; - - case Check::CheckSame: - return sizeof("-SAME:") - 1; - - case Check::CheckNot: - return sizeof("-NOT:") - 1; - - case Check::CheckDAG: - return sizeof("-DAG:") - 1; - - case Check::CheckLabel: - return sizeof("-LABEL:") - 1; - - case Check::CheckEOF: - llvm_unreachable("Should not be using EOF size"); - } - - llvm_unreachable("Bad check type"); -} - -static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) { +static Check::CheckType FindCheckType(SourceMgr &SM, StringRef Buffer, + StringRef Prefix, size_t &CheckTypeSize) { char NextChar = Buffer[Prefix.size()]; + CheckTypeSize = 0; // Verify that the : is present after the prefix. - if (NextChar == ':') + if (NextChar == ':') { + CheckTypeSize = 1; return Check::CheckPlain; + } if (NextChar != '-') return Check::CheckNone; StringRef Rest = Buffer.drop_front(Prefix.size() + 1); - if (Rest.startswith("NEXT:")) + if (Rest.startswith("NEXT:")) { + CheckTypeSize = sizeof("-NEXT:") - 1; return Check::CheckNext; + } - if (Rest.startswith("SAME:")) + if (Rest.startswith("SAME:")) { + CheckTypeSize = sizeof("-SAME:") - 1; return Check::CheckSame; + } - if (Rest.startswith("NOT:")) + if (Rest.startswith("NOT:")) { + CheckTypeSize = sizeof("-NOT:") - 1; return Check::CheckNot; + } - if (Rest.startswith("DAG:")) + if (Rest.startswith("DAG:")) { + CheckTypeSize = sizeof("-DAG:") - 1; return Check::CheckDAG; + } - if (Rest.startswith("LABEL:")) + if (Rest.startswith("LABEL:")) { + CheckTypeSize = sizeof("-LABEL:") - 1; return Check::CheckLabel; + } + + SmallVector Matches; + + if (Regex(StringRef("PATTERN ([a-zA-Z0-9_]+):"), Regex::Newline) + .match(Rest, &Matches)) { + if (!TemplatesCollection::Instance().AddTemplate( + new PatternTemplate(Matches[1]))) { + SM.PrintMessage( + SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Warning, + "the template name " + Matches[1].str() + "is already used"); + return Check::CheckNone; + } + CheckTypeSize = Matches[0].size() + 1; + return Check::CheckPattern; + } // You can't combine -NOT with another suffix. if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") || @@ -778,11 +1046,13 @@ // string and the location will be StringRef::npos. If one prefix is a substring // of another, the maximal match should be found. e.g. if "A" and "AA" are // prefixes then AA-CHECK: should match the second one. -static StringRef FindFirstCandidateMatch(StringRef &Buffer, +static StringRef FindFirstCandidateMatch(SourceMgr &SM, StringRef &Buffer, Check::CheckType &CheckTy, - size_t &CheckLoc) { + size_t &CheckLoc, + size_t &CheckTypeSize) { StringRef FirstPrefix; size_t FirstLoc = StringRef::npos; + size_t FirstTypeSize = StringRef::npos; size_t SearchLoc = StringRef::npos; Check::CheckType FirstTy = Check::CheckNone; @@ -818,7 +1088,7 @@ if (PrefixLoc != 0 && IsPartOfWord(Buffer[PrefixLoc - 1])) FirstTy = Check::CheckNone; else - FirstTy = FindCheckType(Rest, Prefix); + FirstTy = FindCheckType(SM, Rest, Prefix, FirstTypeSize); FirstLoc = PrefixLoc; FirstPrefix = Prefix; @@ -832,15 +1102,18 @@ CheckTy = FirstTy; CheckLoc = FirstLoc; + CheckTypeSize = FirstTypeSize; return FirstPrefix; } -static StringRef FindFirstMatchingPrefix(StringRef &Buffer, +static StringRef FindFirstMatchingPrefix(SourceMgr &SM, StringRef &Buffer, unsigned &LineNumber, Check::CheckType &CheckTy, - size_t &CheckLoc) { + size_t &CheckLoc, + size_t &CheckTypeSize) { while (!Buffer.empty()) { - StringRef Prefix = FindFirstCandidateMatch(Buffer, CheckTy, CheckLoc); + StringRef Prefix = + FindFirstCandidateMatch(SM, Buffer, CheckTy, CheckLoc, CheckTypeSize); // If we found a real match, we are done. if (!Prefix.empty()) { LineNumber += Buffer.substr(0, CheckLoc).count('\n'); @@ -911,12 +1184,14 @@ while (1) { Check::CheckType CheckTy; size_t PrefixLoc; + size_t CheckTypeSize; // See if a prefix occurs in the memory buffer. - StringRef UsedPrefix = FindFirstMatchingPrefix(Buffer, + StringRef UsedPrefix = FindFirstMatchingPrefix(SM, Buffer, LineNumber, CheckTy, - PrefixLoc); + PrefixLoc, + CheckTypeSize); if (UsedPrefix.empty()) break; @@ -926,7 +1201,7 @@ const char *UsedPrefixStart = Buffer.data() + (PrefixLoc == 0 ? 0 : 1); // PrefixLoc is to the start of the prefix. Skip to the end. - Buffer = Buffer.drop_front(UsedPrefix.size() + CheckTypeSize(CheckTy)); + Buffer = Buffer.drop_front(UsedPrefix.size() + CheckTypeSize); // Complain about useful-looking but unsupported suffixes. if (CheckTy == Check::CheckBadNot) { @@ -946,6 +1221,15 @@ // Remember the location of the start of the pattern, for diagnostics. SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); + if (CheckTy == Check::CheckType::CheckPattern) { + if (TemplatesCollection::Instance().GetLastAddedTemplate()->ParseTemplate( + Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber)) { + return true; + } + Buffer = Buffer.substr(EOL); + continue; + } + // Parse the pattern. Pattern P(CheckTy); if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber))