Index: docs/CommandGuide/FileCheck.rst =================================================================== --- docs/CommandGuide/FileCheck.rst +++ docs/CommandGuide/FileCheck.rst @@ -480,3 +480,70 @@ letting us set the :program:`FileCheck` variable ``DLOC`` to the desired value ``0x00000233``, extracted from the line immediately preceding "``intd``". + +Templates +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Syntax: +\#(template_name) - use of template 'template_name'. It can occur in CHECK-DEFINE-PATTERN line, when +description of one template includes other templates described before. +\:(Variable_name) - template variable with name 'variable_name' +\=(value) - current value of template variable(it's needed when you use template with variables). + +Some regular expressions are used very often. In this case directive for describing template +of regular expression may be used. + +CHECK-DEFINE-PATTERN directive is used for describing templates. + + .. code-block:: c++ + + // CHECK-DEFINE-PATTERN [name of describing template]: [pattern of template] + +For example + +.. code-block:: c++ + + // CHECK-DEFINE-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-DEFINE-PATTERN reg_hex_num: {{\:(regName)0x[0-9a-fA-F]+}} + +regName is parameter name. It will be placed by it's value which should be defined in check string. +There can be many 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. 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-DEFINE-PATTERN thing: foo-thing +// BAR-DEFINE-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-DEFINE-PATTERN hex_num: {{0x[0-9a-fA-F]+}} +// CHECK-DEFINE-PATTERN reg: {{\:(RegName)\#(hex_num)}} +// CHECK-DEFINE-PATTERN operation: {{\:(OpName) \#(reg)\:(RegName)\=(r), \#(reg)\:(RegName)\=(reg)}} +// CHECK-DEFINE-PATTERN inc: {{\:(VarName) = \:(VarName)\+\:(Number)}} +// CHECK-DEFINE-PATTERN alt: {{x|y}} +// CHECK-DEFINE-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,11 +16,14 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Regex.h" @@ -88,6 +91,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 +135,12 @@ /// getLoc - Return the location in source code. SMLoc getLoc() const { return PatternLoc; } + /// getRegExStr - Return regex pattern. + StringRef getRegExStr() const { return StringRef(RegExStr); } + + /// getFixedStr - Return fixed string. + StringRef getFixedStr() const { return FixedStr; } + /// 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 +190,170 @@ /// \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 regular expression with replaced templates + Expected ReplaceTemplates(StringRef RS, SourceMgr &SM); +}; + +class PatternTemplate { + /// Name of template. + std::string TemplateName; + /// Pattern which describes this template. + Pattern TemplatePattern; + /// Template parameters. + std::map Parameters; + +public: + PatternTemplate(StringRef Name, + Check::CheckType Ty = Check::CheckType::CheckNone) + : TemplateName(Name.str()), 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. + 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 = 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().str(); + + if (Regex.empty()) + Regex = "(" + TemplatePattern.getFixedStr().str() + ")"; + + 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(std::unique_ptr AddedTemplate); + /// Get template by its name. + PatternTemplate *GetTemplate(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( + std::unique_ptr AddedTemplate) { + if (Templates.find(AddedTemplate->GetTemplateName()) != Templates.end()) { + return false; + } + LastAddedTemplate = AddedTemplate->GetTemplateName(); + return Templates + .insert(std::pair>( + AddedTemplate->GetTemplateName(), std::move(AddedTemplate))) + .second; +} + +PatternTemplate *TemplatesCollection::GetTemplate(StringRef TemplateName) { + const auto PatternIt = Templates.find(TemplateName); + if (PatternIt == Templates.end()) + return nullptr; + return PatternIt->second.get(); +} + +PatternTemplate *TemplatesCollection::GetLastAddedTemplate() { + return Templates[LastAddedTemplate].get(); +} + +bool TemplatesCollection::HasTemplate(const StringRef &TemplateName) { + return Templates.find(TemplateName) != Templates.end(); +} bool Pattern::ParsePattern(StringRef PatternStr, StringRef Prefix, @@ -361,6 +533,11 @@ bool Pattern::AddRegExToRegEx(StringRef RS, unsigned &CurParen, SourceMgr &SM) { + auto ResultRegex = ReplaceTemplates(RS, SM); + if (!ResultRegex) + return true; + + RS = StringRef(*ResultRegex); Regex R(RS); std::string Error; if (!R.isValid(Error)) { @@ -607,6 +784,115 @@ return StringRef::npos; } +Expected Pattern::ReplaceTemplates(StringRef RS, SourceMgr &SM) { + // Replace templates with regular expressions. + SmallVector MatchInfo; + size_t DiffInLoc = 0; + StringRef RegexStr = StringRef(RS); + std::string ResultRegex; + + 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 llvm::make_error( + "Template " + MatchInfo[1].str() + " wasn't declared", + llvm::inconvertibleErrorCode()); + } + // 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 llvm::make_error( + "Failed to parse parameter. No closed bracket.", + llvm::inconvertibleErrorCode()); + } + 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 llvm::make_error( + "Template " + MatchInfo[1].str() + " doesn't have parameter " + + ParameterName.str(), + llvm::inconvertibleErrorCode()); + } + + // 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 llvm::make_error( + "Parameter " + ParameterName.str() + " has no value", + llvm::inconvertibleErrorCode()); + } + + 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 llvm::make_error( + "Wrong description of parameter value. No closed bracket.", + llvm::inconvertibleErrorCode()); + } + 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 llvm::make_error( + "Not all parameters of template " + Temp->GetTemplateName() + + " are defined", + llvm::inconvertibleErrorCode()); + } + + // 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 ResultRegex; +} + //===----------------------------------------------------------------------===// // Check Strings. @@ -696,63 +982,61 @@ 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("DEFINE-PATTERN ([a-zA-Z0-9_]+):"), Regex::Newline) + .match(Rest, &Matches)) { + std::unique_ptr Template = + make_unique(Matches[1]); + if (!TemplatesCollection::Instance().AddTemplate(std::move(Template))) { + 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 +1062,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 +1104,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 +1118,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 +1200,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 +1217,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 +1237,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))