Index: test/FileCheck/FileCheckImprovement/check-group-dag.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-group-dag.txt +++ test/FileCheck/FileCheckImprovement/check-group-dag.txt @@ -0,0 +1,94 @@ +// RUN: FileCheck -input-file %s %s +// XFAIL: * + +for (int i = 0; i < n; i++) +{ + a = b + c; +} + +for (int j = 0; j < n; j++) +{ + g++; + d = e*f; +} + +// ------------- Without label --------------- +// CHECK-GROUP-DAG: for (int i = 0; i < n; i++) +// CHECK-DAG: a = b + c; +// CHECK: } +// CHECK-GROUP-END +// CHECK-GROUP-DAG: for (int j = 0; j < n; j++) +// CHECK-DAG: d = e*f; +// CHECK-DAG: g++; +// CHECK: } +// CHECK-GROUP-END + +int main() +{ + for (int i = 0; i < n; i++) + { + e /= f; + } + while (j < count) + { + j++; + } +} +// ------------- With label ---------------- +// CHECK-LABEL: int main() +// CHECK-GROUP: for (int i = 0; i < n; i++) +// CHECK-DAG: e /= f; +// CHECK: } +// CHECK-GROUP-END +// CHECK-GROUP-DAG: while (j < count) +// CHECK: j++; +// CHECK: } +// CHECK-GROUP-END + +void helloworld() +{ + for (int i = 0; i < n; i++) + { + if (i != j - 1) + find = true; + } + for (int j = i; j < n; j++) + { + a = a*a + b; + } +} + +int something() +{ + for (int i = 0; i < n; i++) + { + printf("================="); + } + for (int j = 0; j < n; j++) + { + d = a[i + 1]; + d = a[i + 1]; + } +} +// ------------ With label-dag ------------- +// CHECK-LABEL-DAG: int something() +// CHECK-GROUP-DAG loop: for (int i = 0; i < n; i++) +// CHECK-NOT: a[i] == b[j]; +// CHECK: } +// CHECK-GROUP-END +// CHECK-GROUP-DAG nextloop: for (int j = 0; j < n; j++) +// CHECK {2}: d = a[i + 1]; +// CHECK: } +// CHECK-GROUP-END + +// CHECK-LABEL-DAG: void helloworld() +// CHECK-GROUP-DAG: for (int i = 0; i < n; i++) +// CHECK: if (i != j - 1) +// CHECK-NEXT: find = {{true|false}}; +// CHECK: } +// CHECK-GROUP-END +// CHECK-GROUP-DAG: for (int j = i; j < n; j++) +// CHECK {1, 3}: a +// CHECK-SAME:b +// CHECK: } +// CHECK-GROUP-END Index: test/FileCheck/FileCheckImprovement/check-group.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-group.txt +++ test/FileCheck/FileCheckImprovement/check-group.txt @@ -0,0 +1,88 @@ +// RUN: FileCheck -input-file %s %s +// XFAIL: * + +for (int i = 0; i < n; i++) +{ + for (int j = i; j < n; j++) + { + g++; + a = b + c; + d = e*f; + } +} +// ------------- Without label --------------- +// CHECK-GROUP: for (int i = 0; i < n; i++) +// CHECK-GROUP: for (int j = i; j < n; j++) +// CHECK-DAG: a = b + c; +// CHECK-DAG: d = e*f; +// CHECK-DAG: g++; +// CHECK: } +// CHECK-GROUP-END +// CHECK: } +// CHECK-GROUP-END + +int main() +{ + for (int i = 0; i < n; i++) + { + for (int j = i; j < n; j++) + { + e /= f; + getch(); + } + } +} +// ------------- With label ---------------- +// CHECK-LABEL: int main() +// CHECK-GROUP: for (int i = 0; i < n; i++) +// CHECK-GROUP: for (int j = i; j < n; j++) +// CHECK-DAG: e /= f; +// CHECK-DAG: +// CHECK: } +// CHECK-GROUP-END +// CHECK: } +// CHECK-GROUP-END + +void helloworld() +{ + for (int i = 0; i < n; i++) + { + for (int j = i; j < n; j++) + { + if (i != j - 1) + find = true; + } + } +} + +int something() +{ + for (int i = 0; i < n; i++) + { + for (int j = i; j < n; j++) + { + a[i] == b[j]; + } + d = a[i + 1]; + } +} +// ------------ With label-dag ------------- +// CHECK-LABEL-DAG: int something() +// CHECK-GROUP loop: for (int i = 0; i < n; i++) +// CHECK-GROUP innerloop: for (int j = i; j < n; j++) +// CHECK-DAG: a[i] == b[j]; +// CHECK: } +// CHECK-GROUP-END +// CHECK-DAG: d = a[i + 1]; +// CHECK: } +// CHECK-GROUP-END + +// CHECK-LABEL-DAG: void helloworld() +// CHECK-GROUP: for (int i = 0; i < n; i++) +// CHECK-GROUP: for (int j = i; j < n; j++) +// CHECK: if (i != j - 1) +// CHECK-NEXT: find = {{true|false}}; +// CHECK: } +// CHECK-GROUP-END +// CHECK: } +// CHECK-GROUP-END Index: test/FileCheck/FileCheckImprovement/check-include.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-include.txt +++ test/FileCheck/FileCheckImprovement/check-include.txt @@ -0,0 +1,26 @@ +// RUN: FileCheck -I=$LLVM_SRC_ROOT/test -input-file %s %s + +op1; +op2; +op33; + +first and +This is second line + +one two three + +four! +dog +line +op0xA1; +op0xc2; +op0x33; +reg0x0 +// CHECK-INCLUDE: FileCheck/FileCheckImprovement/included.txt +ADD r0xA, reg0x10 +// CHECK: {{\#(operation)\:(OpName)\=(ADD)}} +TEMP = TEMP+4 +// CHECK: {{\#(inc)\:(Number)\=(4)\:(VarName)\=(TEMP)}} +check y +// CHECK: {{\#altincheck}} + Index: test/FileCheck/FileCheckImprovement/check-label-dag.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-label-dag.txt +++ test/FileCheck/FileCheckImprovement/check-label-dag.txt @@ -0,0 +1,27 @@ +// RUN: FileCheck -input-file %s %s + +// CHECK-LABEL-DAG: {{^}}start first +// CHECK: 1 +// CHECK-DAG:4 +// CHECK-DAG:3 +// CHECK-NEXT: {{[0-2]}} +// CHECK-SAME: 12 + +// CHECK-LABEL-DAG: {{^}}start second +// CHECK-DAG: sec3 +// CHECK-DAG: sec1 +// CHECK: second ends + +start second +sec1 +sec3 +second ends + +start first + +1 + +3 + +4 +2 12 Index: test/FileCheck/FileCheckImprovement/check-next-repeats.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-next-repeats.txt +++ test/FileCheck/FileCheckImprovement/check-next-repeats.txt @@ -0,0 +1,26 @@ +// RUN: FileCheck -input-file %s %s + +op1; +op2; +op3; +first and +second +R0 +R2 +R5 +Reg3 +SOMETHING TWO TIMES +SOMETHING TWO TIMES +ADD R0, R2 +ADD R0, R7 +ADD R15, R18 +ADD Reg, reg1 +SUB R1, R0 +SUB +// CHECK{3}: {{[a-z]+[0-9]+;}} +// CHECK-NEXT{,4}: {{[a-z]+ *[a-z]*$}} +// CHECK-NEXT: [[REG:R[0-9]+]] +// CHECK-NEXT{1,3}: SOMETHING TWO {{TIMES}} +// CHECK-NEXT+: ADD [[REG]], {{R[0-9]+}} +// CHECK-NEXT*: SUB {{R[0-9]+}}, [[REG]] +// CHECK-NEXT*: MUL [[REG]], [[REG]] Index: test/FileCheck/FileCheckImprovement/check-pattern.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-pattern.txt +++ test/FileCheck/FileCheckImprovement/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{3}: {{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: test/FileCheck/FileCheckImprovement/check-regexp-prefix.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-regexp-prefix.txt +++ test/FileCheck/FileCheckImprovement/check-regexp-prefix.txt @@ -0,0 +1,16 @@ +// RUN: FileCheck -check-prefix=A -regex-prefixes -input-file %s %s +// RUN: FileCheck -check-prefix=b -regex-prefixes -input-file %s %s + +for B check +first +next-line + +// {{[A-Z]+}}: first +// {{[a-z0-9]+}}: for {{B}} + + +// {{[A-C0-9]+}}-NEXT: {{next-line}} + +same for both +// {{[A-Za-z0-9]+}}: same +// {{[A-Za-z0-9]+}}-SAME: both Index: test/FileCheck/FileCheckImprovement/check-repeats.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-repeats.txt +++ test/FileCheck/FileCheckImprovement/check-repeats.txt @@ -0,0 +1,32 @@ +// RUN: FileCheck -input-file %s %s + +op1; +op2; +op3; +// CHECK{3}: {{[a-z]+[0-9]+;}} + +first and +second +// CHECK{, 4}: {{[a-z]+[ \t]*[a-z]*}} + +r0 +r2 +r5 +reg3 +// CHECK{2,3}: [[REG:r[0-9]+]] + +something two times +something two times +// CHECK{1,}: something two times + +ADD r5, r2 +ADD r5, r7 +ADD r15, r18 +ADD reg, reg1 +// CHECK+: ADD [[REG]], {{r[0-9]+}} + +SUB r1, r5 +SUB +// CHECK*: SUB {{r[0-9]+}}, [[REG]] + +// CHECK*: MUL [[REG]], [[REG]] Index: test/FileCheck/FileCheckImprovement/check-wildcard-prefix.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-wildcard-prefix.txt +++ test/FileCheck/FileCheckImprovement/check-wildcard-prefix.txt @@ -0,0 +1,13 @@ +// RUN: FileCheck -check-prefix=A -regex-prefixes -input-file %s %s +// RUN: not FileCheck -check-prefix=B -regex-prefixes -input-file %s %s + +for B check +first +next-line +// A: first +// B: for B +// {{*}}-NEXT: {{next-line}} + +same for both +// {{*}}: same +// {{*}}-SAME: both \ No newline at end of file Index: test/FileCheck/FileCheckImprovement/check-word.txt =================================================================== --- test/FileCheck/FileCheckImprovement/check-word.txt +++ test/FileCheck/FileCheckImprovement/check-word.txt @@ -0,0 +1,26 @@ +// RUN: FileCheck -input-file %s %s + +op1; +op2; +op33; +// CHECK-WORD: op{{[0-9]}}; +// CHECk-WORD-NEXT: op{{[0-9]}}; +// CHECk-WORD-NOT: op{{[0-9]}} + +first and +// CHECK-WORD: first +// CHECK-WORD-SAME: and +This is second line +// CHECK-WORD: second + +one two three +// CHECK-WORD-DAG: two +// CHECK-WORD-DAG: three +// CHECK-WORD-DAG: one + +four! +// CHECK-WORD: four +dog +line +// CHECK-WORD-NOT: cat +// CHECK: line \ No newline at end of file Index: test/FileCheck/FileCheckImprovement/included.txt =================================================================== --- test/FileCheck/FileCheckImprovement/included.txt +++ test/FileCheck/FileCheckImprovement/included.txt @@ -0,0 +1,22 @@ +// CHECK-WORD: op{{[0-9]}}; +// CHECk-WORD-NEXT: op{{[0-9]}}; +// CHECk-WORD-NOT: op{{[0-9]}} +// CHECK-WORD: first +// CHECK-WORD-SAME: and +// CHECK-WORD: second +// CHECK-WORD-DAG: two +// CHECK-WORD-DAG: three +// CHECK-WORD-DAG: one +// CHECK-WORD: four +// CHECK-WORD-NOT: cat +// CHECK: line + +// 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{3}: {{op\#(hex_num)}}; +// CHECk: {{\#(reg)\:(RegName)\=(reg)}} Index: test/FileCheck/FileCheckImprovement/repeat-in-regex.txt =================================================================== --- test/FileCheck/FileCheckImprovement/repeat-in-regex.txt +++ test/FileCheck/FileCheckImprovement/repeat-in-regex.txt @@ -0,0 +1,14 @@ +// RUN: FileCheck -input-file %s %s + +text to be matchedmatchedmatchedmatchedmatched +// CHECK: {{(matched){5}}} + +r12 +r1, is register is register is register is register is register +// CHECK{2}: [[REG:r[0-9]{1,2}]] +// CHECK-SAME: {{(is register ){1,}}} + +r2r13r1 +dag +// CHECK-DAG: {{dag{1}}} +// CHECK-DAG: {{[[REG]]{,2}}} Index: test/FileCheck/check-prefixes.txt =================================================================== --- test/FileCheck/check-prefixes.txt +++ test/FileCheck/check-prefixes.txt @@ -6,4 +6,4 @@ ; We use regex to match the colon so that FileCheck won't think it is a check ; prefix. -; CHECK-NONEXISTENT-PREFIX: error: no check strings found with prefix 'PREFIX{{:}}' +; CHECK-NONEXISTENT-PREFIX: error: no check strings found with prefixes 'PREFIX{{:}}' Index: utils/FileCheck/FileCheck.cpp =================================================================== --- utils/FileCheck/FileCheck.cpp +++ utils/FileCheck/FileCheck.cpp @@ -27,6 +27,7 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" // #include #include #include @@ -33,6 +34,13 @@ #include #include #include + +// +#include + +#define MAX_INCLUDING_DEPTH 20 // maximum depth for including files. +// + using namespace llvm; static cl::opt @@ -57,6 +65,16 @@ "this pattern occur which are not matched by a positive pattern"), cl::value_desc("pattern")); +// +static cl::list IncludedDirectories( + "I", + cl::desc("Add directories for searching included files in CHECK-INCLUDE")); + +static cl::opt +RegexPrefixesOn ("regex-prefixes", + cl::desc("Use checks with prefixes which can be described as regular expressions.")); +// + static cl::opt AllowEmptyInput( "allow-empty", cl::init(false), cl::desc("Allow the input file to be empty. This is useful when making\n" @@ -83,6 +101,18 @@ CheckNot, CheckDAG, CheckLabel, + // + CheckLabelDag, + CheckRepeated, + CheckNextRepeated, + CheckInclude, + CheckWord, + CheckWordNext, + CheckWordSame, + CheckWordNot, + CheckWordDAG, + CheckPattern, + // /// MatchEOF - When set, this pattern only matches the end of file. This is /// used for trailing CHECK-NOTs. @@ -118,14 +148,34 @@ /// E.g. for the pattern "foo[[bar:.*]]baz", VariableDefs will map "bar" to 1. std::map VariableDefs; + // + /// MinRepeatNumber - minimum number of repeats for repeated patten type. + int MinRepeatNumber; + + /// MaxRepeatNumber - maximum number of repeats for repeated patten type. Infinity = -1. + int MaxRepeatNumber; + // + public: - Pattern(Check::CheckType Ty) - : CheckTy(Ty) { } + Pattern(Check::CheckType Ty, int MinNumber = 0, int MaxNumber = -1) + : CheckTy(Ty), MinRepeatNumber(MinNumber), MaxRepeatNumber(MaxNumber) { } // /// getLoc - Return the location in source code. SMLoc getLoc() const { return PatternLoc; } + // + + int getMinRepeatNumber() const { return MinRepeatNumber; } + + int getMaxRepeatNumber() const { return MaxRepeatNumber; } + + StringRef getFixedString() const { return FixedStr; } + + std::string getRegExStr() const { return RegExStr; } + + // + /// 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 @@ -175,9 +225,159 @@ /// \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 parametrs. + 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 parametrs. + bool ParseTemplate(StringRef TemplateString, + StringRef Prefix, + SourceMgr &SM, + unsigned LineNumber); + + /// Get template name. + /// \return template name. + std::string GetTemplateName() const; + + /// Get number of parametrs which this template includes. + /// \return parameters number. + size_t GetParametersNumber() 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. + StringRef GetRegularExpression(std::map &ParametersValues) const; +}; + +bool PatternTemplate::ParseTemplate(StringRef TemplateString, + StringRef Prefix, + SourceMgr &SM, + unsigned LineNumber) { + + if (TemplatePattern.ParsePattern(TemplateString, Prefix, SM, LineNumber)) + return true; + + // Find parametrs in pattern + SmallVector Matches; + Regex Parametr(StringRef("\\\\:\\(([a-zA-Z_0-9]+)\\)")); + size_t DiffInLoc = 0; + StringRef PatternString = StringRef(TemplatePattern.getRegExStr()); + + while (Parametr.match(PatternString, &Matches)) { + size_t ParametrPos = Matches[0].data() - PatternString.data(); + Parameters.insert(std::pair(ParametrPos + DiffInLoc, Matches[1])); + PatternString = PatternString.drop_front(ParametrPos + Matches[1].size() + 3); + DiffInLoc += ParametrPos + Matches[1].size() + 3; + } + + return false; +} + +std::string PatternTemplate::GetTemplateName() const { + return TemplateName; +} + +size_t PatternTemplate::GetParametersNumber() const { + std::set UniqueParametrs; + for (const auto &Parameter : Parameters) { + UniqueParametrs.insert(Parameter.second); + } + return UniqueParametrs.size(); +} + +bool PatternTemplate::HasParameter(StringRef ParameterName) const { + for (const auto &Parameter : Parameters) { + if (Parameter.second == ParameterName) { + return true; + } + } + return false; +} + +StringRef PatternTemplate::GetRegularExpression(std::map &ParametersValues) const { + std::string Regex = TemplatePattern.getRegExStr(); + + int DiffInLoc = 0; // variable for shifts after replacing parametrs' names by parametrs' values + + // Change all parameters to their values. + for (const auto &Parameter : Parameters) { + StringRef Value = ParametersValues[Parameter.second]; + std::string RegexStart = StringRef(Regex.substr(0, Parameter.first + DiffInLoc)).str(); + StringRef RegexEnd = Regex.substr(Parameter.first + DiffInLoc + Parameter.second.size() + 4); + Regex = RegexStart + "(" + Value.str() + ")" + RegexEnd.str(); + DiffInLoc += Value.size() - Parameter.second.size() - 2; + } + + return StringRef(Regex); +} + +class TemplatesCollection { + /// Described templates + static std::map Templates; + /// Last added template (for generating error) + static std::string LastAddedTemplate; + +public: + /// Add template + static bool AddTemplate(PatternTemplate *AddedTemplate); + /// Get template by its name. + static PatternTemplate* GetTemplate(const StringRef &TemplateName); + /// Get last added template + static PatternTemplate* GetLastAddedTemplate(); + /// Check if there is current template in collection. + static bool HasTemplate(const StringRef &TemplateName); +}; + +std::string TemplatesCollection::LastAddedTemplate; +std::map TemplatesCollection::Templates; + +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) { + if (HasTemplate(TemplateName)) { + return Templates[TemplateName.str()]; + } + + return NULL; +} + +PatternTemplate* TemplatesCollection::GetLastAddedTemplate() { + return Templates[LastAddedTemplate]; +} + +bool TemplatesCollection::HasTemplate(const StringRef &TemplateName) { + return Templates.find(TemplateName.str()) != Templates.end(); +} +// + bool Pattern::ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, @@ -201,9 +401,9 @@ } // Check to see if this is a fixed string, or if it has regex pieces. - if (!MatchFullLinesHere && - (PatternStr.size() < 2 || (PatternStr.find("{{") == StringRef::npos && - PatternStr.find("[[") == StringRef::npos))) { + if (!MatchFullLinesHere && (PatternStr.size() < 2 || + (PatternStr.find("{{") == StringRef::npos && + PatternStr.find("[[") == StringRef::npos))) { FixedStr = PatternStr; return false; } @@ -232,6 +432,15 @@ return true; } + // + size_t NextChar = End + 2; + while (NextChar < PatternStr.size() && PatternStr[NextChar] == '}') { + NextChar++; + } + + End = NextChar - 2; + // + // Enclose {{}} patterns in parens just like [[]] even though we're not // capturing the result for any purpose. This is required in case the // expression contains an alternation like: CHECK: abc{{x|z}}def. We @@ -239,7 +448,18 @@ RegExStr += '('; ++CurParen; - if (AddRegExToRegEx(PatternStr.substr(2, End-2), CurParen, SM)) + // + StringRef RegexPart = PatternStr.substr(2, End-2); + // Find quantifier for replacing. + size_t NullQuantifier = RegexPart.find("{,"); + if (NullQuantifier != StringRef::npos && (NullQuantifier == 0 || RegexPart[NullQuantifier - 1] != '\\')) { + // Need replacing. + StringRef FirstPart = RegexPart.drop_back(RegexPart.size() - NullQuantifier - 1); + StringRef SecondPart = RegexPart.drop_front(NullQuantifier + 1); + RegexPart = StringRef(FirstPart.str() + "0" + SecondPart.str()); + } + // + if (AddRegExToRegEx(RegexPart, CurParen, SM)) return true; RegExStr += ')'; @@ -354,8 +574,97 @@ return false; } +// +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::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::GetTemplate(MatchInfo[1]); + + // Get template parametres. + std::map 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 parametr 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->GetParametersNumber() != ParametersValues.size()) { + SM.PrintMessage(SMLoc::getFromPointer(RS.data() + DiffInLoc), SourceMgr::DK_Error, + "not all parametrs of template " + Temp->GetTemplateName() + " are defined"); + return true; + } + + // Get regular expression by template. + StringRef FullTemplateRegex = Temp->GetRegularExpression(ParametersValues); + // Add template regex. + ResultRegex = ResultRegex + FullTemplateRegex.str(); + + } + // Add left part without templates. + ResultRegex = ResultRegex + RegexStr.str(); + return false; +} +// + 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)) { @@ -416,6 +725,7 @@ // If there are variable uses, we need to create a temporary string with the // actual value. StringRef RegExToMatch = RegExStr; + std::string TmpStr; if (!VariableUses.empty()) { TmpStr = RegExStr; @@ -627,9 +937,12 @@ /// file). std::vector DagNotStrings; - CheckString(const Pattern &P, StringRef S, SMLoc L) - : Pat(P), Prefix(S), Loc(L) {} + CheckString(const Pattern &P, + StringRef S, + SMLoc L) + : Pat(P), Prefix(S), Loc(L) {} + /// Check - Match check string and its "not strings" and/or "dag strings". size_t Check(const SourceMgr &SM, StringRef Buffer, bool IsLabelScanMode, size_t &MatchLen, StringMap &VariableTable) const; @@ -637,6 +950,11 @@ /// CheckNext - Verify there is a single line in the given buffer. bool CheckNext(const SourceMgr &SM, StringRef Buffer) const; + // + /// CheckWord - Verify there is a single word. + bool CheckWord(const size_t &MatchPos, const size_t &MatchLen, StringRef Buffer) const; + // + /// CheckSame - Verify there is no newline in the given buffer. bool CheckSame(const SourceMgr &SM, StringRef Buffer) const; @@ -691,72 +1009,167 @@ 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; +// +static Check::CheckType FindCheckType(SourceMgr &SM, StringRef Buffer, StringRef Prefix, size_t &CheckTypeSize, + int &MinRepeatNumber, int &MaxRepeatNumber) { + MinRepeatNumber = 0; + MaxRepeatNumber = -1; + CheckTypeSize = 0; + bool NextStatement = false; - case Check::CheckPlain: - return sizeof(":") - 1; + size_t StartRegexInPrefix = Buffer.find("{{"); + size_t EndRegexInPrefix = Buffer.find("}}"); + size_t StringEnd = Buffer.find(':'); - case Check::CheckNext: - return sizeof("-NEXT:") - 1; + if (StringEnd == StringRef::npos) { + return Check::CheckNone; + } + std::string StringForMatch; + StringRef Rest; + if (RegexPrefixesOn && EndRegexInPrefix < StringEnd && + (StartRegexInPrefix == StringRef::npos || StartRegexInPrefix > EndRegexInPrefix)) { + StringForMatch = Buffer.substr(EndRegexInPrefix + 2, StringEnd - EndRegexInPrefix - 1).str(); + Rest = Buffer.drop_front(EndRegexInPrefix + 2); + } else { + StringForMatch = Buffer.substr(Prefix.size(), StringEnd - Prefix.size() + 1).str(); + Rest = Buffer.drop_front(Prefix.size()); + } - 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"); + if (Rest.startswith("-NEXT")) { + CheckTypeSize += sizeof("-NEXT") - 1; + StringForMatch = StringForMatch.substr(5); + NextStatement = true; } - llvm_unreachable("Bad check type"); -} - -static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) { - char NextChar = Buffer[Prefix.size()]; - + SmallVector Matches; + StringRef MatchedString = StringRef(StringForMatch); // Verify that the : is present after the prefix. - if (NextChar == ':') + if (MatchedString.startswith(":")) { + CheckTypeSize += 1; + if (NextStatement) { + return Check::CheckNext; + } return Check::CheckPlain; + } + + if (Regex(StringRef("^((\\{([0-9]+)\\})|(\\{([0-9]*),([0-9]*)\\})):"), Regex::Newline).match(MatchedString, &Matches)) { + CheckTypeSize += Matches[0].size(); + // Was match with exact number. + if (Matches[3].size() != 0) { + MinRepeatNumber = std::stoi(Matches[3].str().c_str()); + MaxRepeatNumber = MinRepeatNumber; + } else { + if (Matches[5].size() == 0 && Matches[6].size() == 0) { + return Check::CheckNone; + } + if (Matches[5].size() != 0) { + MinRepeatNumber = std::stoi(Matches[5].str().c_str()); + } + if (Matches[6].size() != 0) { + MaxRepeatNumber = std::stoi(Matches[6].str().c_str()); + } + } - if (NextChar != '-') - return Check::CheckNone; + if (NextStatement) { + return Check::CheckNextRepeated; + } + return Check::CheckRepeated; + } - StringRef Rest = Buffer.drop_front(Prefix.size() + 1); - if (Rest.startswith("NEXT:")) - return Check::CheckNext; + if (MatchedString.startswith("*:")) { + CheckTypeSize += 2; + if (NextStatement) { + return Check::CheckNextRepeated; + } + return Check::CheckRepeated; + } - if (Rest.startswith("SAME:")) + if (MatchedString.startswith("+:")) { + CheckTypeSize += 2; + MinRepeatNumber = 1; + if (NextStatement) { + return Check::CheckNextRepeated; + } + return Check::CheckRepeated; + } + + if (MatchedString.startswith("-SAME:")) { + CheckTypeSize = sizeof("-SAME:") - 1; return Check::CheckSame; + } - if (Rest.startswith("NOT:")) + if (MatchedString.startswith("-NOT:")) { + CheckTypeSize = sizeof("-NOT:") - 1; return Check::CheckNot; + } - if (Rest.startswith("DAG:")) + if (MatchedString.startswith("-DAG:")) { + CheckTypeSize = sizeof("-DAG:") - 1; return Check::CheckDAG; + } - if (Rest.startswith("LABEL:")) + if (MatchedString.startswith("-LABEL:")) { + CheckTypeSize = sizeof("-LABEL:") - 1; return Check::CheckLabel; + } + if (MatchedString.startswith("-LABEL-DAG:")) { + CheckTypeSize = sizeof("-LABEL-DAG:") - 1; + return Check::CheckLabelDag; + } + + if (MatchedString.startswith("-INCLUDE:")) { + CheckTypeSize = sizeof("-INCLUDE:") - 1; + return Check::CheckInclude; + } + + if (MatchedString.startswith("-WORD:")) { + CheckTypeSize = sizeof("-WORD:") - 1; + return Check::CheckWord; + } + + if (MatchedString.startswith("-WORD-NEXT:")) { + CheckTypeSize = sizeof("-WORD-NEXT:") - 1; + return Check::CheckWordNext; + } + + if (MatchedString.startswith("-WORD-SAME:")) { + CheckTypeSize = sizeof("-WORD-SAME:") - 1; + return Check::CheckWordSame; + } + + if (MatchedString.startswith("-WORD-NOT:")) { + CheckTypeSize = sizeof("-WORD-NOT:") - 1; + return Check::CheckWordNot; + } + + if (MatchedString.startswith("-WORD-DAG:")) { + CheckTypeSize = sizeof("-WORD-DAG:") - 1; + return Check::CheckWordDAG; + } + + if (Regex(StringRef("-PATTERN ([a-zA-Z0-9_]+):"), Regex::Newline).match(MatchedString, &Matches)) { + if (!TemplatesCollection::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(); + return Check::CheckPattern; + } + // You can't combine -NOT with another suffix. if (Rest.startswith("DAG-NOT:") || Rest.startswith("NOT-DAG:") || Rest.startswith("NEXT-NOT:") || Rest.startswith("NOT-NEXT:") || - Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:")) + Rest.startswith("SAME-NOT:") || Rest.startswith("NOT-SAME:")) { + CheckTypeSize = 0; return Check::CheckBadNot; + } return Check::CheckNone; } +// // From the given position, find the next character after the word. static size_t SkipWord(StringRef Str, size_t Loc) { @@ -773,11 +1186,17 @@ // 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, + int &MinRepeatNumber, + int &MaxRepeatNumber) { + StringRef FirstPrefix; size_t FirstLoc = StringRef::npos; + size_t FirstTypeSize = StringRef::npos; size_t SearchLoc = StringRef::npos; Check::CheckType FirstTy = Check::CheckNone; @@ -787,6 +1206,41 @@ for (StringRef Prefix : CheckPrefixes) { size_t PrefixLoc = Buffer.find(Prefix); + // + if (RegexPrefixesOn) { + SmallVector Matches; + // Try ti find prefixes defined as regexes. + Regex PrefixesWithRegexes(StringRef("\\{\\{(.+)\\}\\}(-.+)?:.+$")); + StringRef BufferCopy = Buffer; + size_t DiffInLoc = 0; + + while (PrefixesWithRegexes.match(BufferCopy, &Matches) && (DiffInLoc < PrefixLoc || PrefixLoc == StringRef::npos)) { + // Get regex part + StringRef RegexPart = Matches[1]; + if (RegexPart.find("}}") != StringRef::npos) { + RegexPart = RegexPart.substr(0, RegexPart.find("}}")); + } + + size_t RegexPrefixLoc = Matches[1].data()-BufferCopy.data(); + DiffInLoc += RegexPrefixLoc; + // Check if current prefix matches with found. + if (Regex(RegexPart).match(Prefix)) { + if (DiffInLoc < PrefixLoc) { + PrefixLoc = DiffInLoc; + Prefix = StringRef(RegexPart.str() + "}}"); + } + } + + if (BufferCopy.find("\n", RegexPrefixLoc) != StringRef::npos) { + DiffInLoc += BufferCopy.find("\n", RegexPrefixLoc) - RegexPrefixLoc; + BufferCopy = BufferCopy.drop_front(BufferCopy.find("\n", RegexPrefixLoc)); + } else { + BufferCopy = BufferCopy.drop_front(RegexPrefixLoc); + } + } + } + // + if (PrefixLoc == StringRef::npos) continue; @@ -795,6 +1249,7 @@ // since a partial match could be a substring of a later, valid prefix. // Need to skip to the end of the word, otherwise we could end up // matching a prefix in a substring later. + if (PrefixLoc < SearchLoc) SearchLoc = SkipWord(Buffer, PrefixLoc); @@ -801,6 +1256,7 @@ // We only want to find the first match to avoid skipping some. if (PrefixLoc > FirstLoc) continue; + // If one matching check-prefix is a prefix of another, choose the // longer one. if (PrefixLoc == FirstLoc && Prefix.size() < FirstPrefix.size()) @@ -807,6 +1263,7 @@ continue; StringRef Rest = Buffer.drop_front(PrefixLoc); + // Make sure we have actually found the prefix, and not a word containing // it. This should also prevent matching the wrong prefix when one is a // substring of another. @@ -813,8 +1270,8 @@ if (PrefixLoc != 0 && IsPartOfWord(Buffer[PrefixLoc - 1])) FirstTy = Check::CheckNone; else - FirstTy = FindCheckType(Rest, Prefix); - + FirstTy = FindCheckType(SM, Rest, Prefix, FirstTypeSize, MinRepeatNumber, MaxRepeatNumber); // + FirstLoc = PrefixLoc; FirstPrefix = Prefix; } @@ -827,15 +1284,20 @@ 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, + int &MinRepeatNumber, + int &MaxRepeatNumber) { while (!Buffer.empty()) { - StringRef Prefix = FindFirstCandidateMatch(Buffer, CheckTy, CheckLoc); + StringRef Prefix = FindFirstCandidateMatch(SM, Buffer, CheckTy, CheckLoc, CheckTypeSize, MinRepeatNumber, MaxRepeatNumber); // If we found a real match, we are done. if (!Prefix.empty()) { LineNumber += Buffer.substr(0, CheckLoc).count('\n'); @@ -859,11 +1321,14 @@ /// expected strings. The strings are added to the CheckStrings vector. /// Returns true in case of an error, false otherwise. static bool ReadCheckFile(SourceMgr &SM, - std::vector &CheckStrings) { + std::vector &CheckStrings, + std::string Filename = CheckFilename, + int IncludingLevel = 0) { + ErrorOr> FileOrErr = - MemoryBuffer::getFileOrSTDIN(CheckFilename); + MemoryBuffer::getFileOrSTDIN(Filename); if (std::error_code EC = FileOrErr.getError()) { - errs() << "Could not open check file '" << CheckFilename + errs() << "Could not open check file '" << Filename << "': " << EC.message() << '\n'; return true; } @@ -905,13 +1370,22 @@ while (1) { Check::CheckType CheckTy; + // + int MinRepeatNumber; + int MaxRepeatNumber; + // 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, + MinRepeatNumber, + MaxRepeatNumber); if (UsedPrefix.empty()) break; @@ -921,7 +1395,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) { @@ -941,43 +1415,90 @@ // Remember the location of the start of the pattern, for diagnostics. SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); + // + if (CheckTy == Check::CheckType::CheckPattern) { + if (TemplatesCollection::GetLastAddedTemplate()->ParseTemplate(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber)) { + return true; + } + Buffer = Buffer.substr(EOL); + continue; + } + // + // Parse the pattern. - Pattern P(CheckTy); + Pattern P(CheckTy, MinRepeatNumber, MaxRepeatNumber); + if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber)) return true; - // Verify that CHECK-LABEL lines do not define or use variables - if ((CheckTy == Check::CheckLabel) && P.hasVariable()) { - SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), - SourceMgr::DK_Error, - "found '" + UsedPrefix + "-LABEL:'" - " with variable definition or use"); - return true; - } + // + // if included another file + if (CheckTy == Check::CheckInclude) { + // Check for escaping infinite including + if (IncludingLevel > MAX_INCLUDING_DEPTH) { + SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), + SourceMgr::DK_Error, + "Maximum including level!"); + return true; + } + // Get included file name. + StringRef IncludedFileName = P.getFixedString(); - Buffer = Buffer.substr(EOL); + std::vector IncludedCheckStrings; + // Try find file in included directories. + ErrorOr> FileOrErr = + MemoryBuffer::getFileOrSTDIN(StringRef(IncludedFileName)); + if (FileOrErr.getError()) { + for (std::string Directory : IncludedDirectories) { + StringRef FullIncludedFileName = StringRef(Directory + '/' + IncludedFileName.str()); + FileOrErr = MemoryBuffer::getFileOrSTDIN(FullIncludedFileName); + if (!FileOrErr.getError()) { + IncludedFileName = FullIncludedFileName; + break; + } + } + } + + // Read it and get check strings from it. + ReadCheckFile(SM, IncludedCheckStrings, IncludedFileName, IncludingLevel+1); + CheckStrings.insert(CheckStrings.end(), IncludedCheckStrings.begin(), IncludedCheckStrings.end()); + } else { + // + // Verify that CHECK-LABEL lines do not define or use variables + if ((CheckTy == Check::CheckLabel || CheckTy == Check::CheckLabelDag) && P.hasVariable()) { + SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), + SourceMgr::DK_Error, + "found '" + UsedPrefix + "-LABEL:'" + " with variable definition or use"); + return true; + } - // Verify that CHECK-NEXT lines have at least one CHECK line before them. - if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame) && - CheckStrings.empty()) { - StringRef Type = CheckTy == Check::CheckNext ? "NEXT" : "SAME"; - SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), - SourceMgr::DK_Error, - "found '" + UsedPrefix + "-" + Type + "' without previous '" - + UsedPrefix + ": line"); - return true; - } + Buffer = Buffer.substr(EOL); - // Handle CHECK-DAG/-NOT. - if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot) { - DagNotMatches.push_back(P); - continue; + // Verify that CHECK-NEXT lines have at least one CHECK line before them. + if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckNextRepeated || CheckTy == Check::CheckSame || + CheckTy == Check::CheckWordNext || CheckTy == Check::CheckWordSame) && + CheckStrings.empty()) { + StringRef Type = CheckTy == Check::CheckNext || Check::CheckWordNext || Check::CheckNextRepeated ? "NEXT" : "SAME"; + SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), + SourceMgr::DK_Error, + "found '" + UsedPrefix + "-" + Type + "' without previous '" + + UsedPrefix + ": line"); + return true; + } + + // Handle CHECK-DAG/-NOT. + if (CheckTy == Check::CheckDAG || CheckTy == Check::CheckNot || + CheckTy == Check::CheckWordDAG || CheckTy == Check::CheckWordNot) { + DagNotMatches.push_back(P); + continue; + } + + // Okay, add the string we captured to the output vector and move on. + CheckStrings.emplace_back(P, UsedPrefix, PatternLoc); + std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); + DagNotMatches = ImplicitNegativeChecks; } - - // Okay, add the string we captured to the output vector and move on. - CheckStrings.emplace_back(P, UsedPrefix, PatternLoc); - std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); - DagNotMatches = ImplicitNegativeChecks; } // Add an EOF pattern for any trailing CHECK-DAG/-NOTs, and use the first @@ -997,7 +1518,7 @@ errs() << "\'" << *I << ":'"; ++I; } - for (; I != E; ++I) + for (; I != E; ++I) errs() << ", \'" << *I << ":'"; errs() << '\n'; @@ -1059,6 +1580,7 @@ bool IsLabelScanMode, size_t &MatchLen, StringMap &VariableTable) const { size_t LastPos = 0; + MatchLen = 0; std::vector NotStrings; // IsLabelScanMode is true when we are scanning forward to find CHECK-LABEL @@ -1072,8 +1594,54 @@ return StringRef::npos; } + // + StringRef MatchBuffer = Buffer.substr(LastPos); + + // If pattern should be checked several times. + if (Pat.getCheckTy() == Check::CheckType::CheckRepeated || Pat.getCheckTy() == Check::CheckType::CheckNextRepeated) { + size_t MatchPos = 0; + int count = 0; + size_t PrevMatchLen = 0; + bool NextFall = false; + size_t BackupMatchLen = MatchLen; + size_t BackupMatchPos = MatchPos; + size_t BackupPrevMatchLen = MatchLen; + + // Match much as possible. + while (MatchPos != StringRef::npos && !NextFall && (Pat.getMaxRepeatNumber() == -1 || count < Pat.getMaxRepeatNumber())) { + StringRef MatchBuffer = Buffer.substr(LastPos); + BackupMatchLen = MatchLen; + BackupMatchPos = MatchPos; + MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable); + if (MatchPos != StringRef::npos) { + BackupPrevMatchLen = PrevMatchLen; + PrevMatchLen = MatchLen; + LastPos += MatchPos + MatchLen; + count++; + } + if (!IsLabelScanMode) { + StringRef SkippedRegion = Buffer.substr(LastPos, MatchPos); + if (CheckNext(SM, SkippedRegion)) { + NextFall = true; + MatchLen = BackupMatchLen; + MatchPos = BackupMatchPos; + PrevMatchLen = BackupPrevMatchLen; + } + } + + } + + if (count < Pat.getMinRepeatNumber()) { + PrintCheckFailed(SM, *this, MatchBuffer, VariableTable); + return StringRef::npos; + } + + return LastPos - PrevMatchLen; + } + // + // Match itself from the last position after matching CHECK-DAG. - StringRef MatchBuffer = Buffer.substr(LastPos); + size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable); if (MatchPos == StringRef::npos) { PrintCheckFailed(SM, *this, MatchBuffer, VariableTable); @@ -1097,16 +1665,48 @@ // If this match had "not strings", verify that they don't exist in the // skipped region. - if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable)) + if (CheckNot(SM, SkippedRegion, NotStrings, VariableTable)) { return StringRef::npos; + } + + // + //if this check is a "CHECK-WORD", verify the bounds of string. + if (CheckWord(LastPos + MatchPos, MatchLen, Buffer)) + return StringRef::npos; + // + } return LastPos + MatchPos; } +// +bool CheckString::CheckWord(const size_t &MatchPos, const size_t &MatchLen, StringRef Buffer) const { + bool isWord = true; + if (Pat.getCheckTy() != Check::CheckWord && Pat.getCheckTy() != Check::CheckWordNext && + Pat.getCheckTy() != Check::CheckWordSame && Pat.getCheckTy() != Check::CheckWordDAG && + Pat.getCheckTy() != Check::CheckWordNot) + return false; + // Check previous and next characters to understand word or not. + if (MatchPos != 0) { + if (!isspace(Buffer[MatchPos - 1]) && !ispunct(Buffer[MatchPos - 1])) { + isWord = false; + } + } + if (MatchPos + MatchLen < Buffer.size() && + !isspace(Buffer[MatchPos + MatchLen ]) && !ispunct(Buffer[MatchPos + MatchLen])) { + isWord = false; + } + return (Pat.getCheckTy() == Check::CheckWordNot && isWord) || (Pat.getCheckTy() != Check::CheckWordNot && !isWord); +} +// + bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { - if (Pat.getCheckTy() != Check::CheckNext) + // + if (Pat.getCheckTy() != Check::CheckNext && Pat.getCheckTy() != Check::CheckNextRepeated && + Pat.getCheckTy() != Check::CheckWordNext) return false; + // // Count the number of newlines between the previous match and this one. assert(Buffer.data() != @@ -1125,6 +1725,7 @@ SourceMgr::DK_Note, "'next' match was here"); SM.PrintMessage(SMLoc::getFromPointer(Buffer.data()), SourceMgr::DK_Note, "previous match ended here"); + return true; } @@ -1137,6 +1738,7 @@ "previous match ended here"); SM.PrintMessage(SMLoc::getFromPointer(FirstNewLine), SourceMgr::DK_Note, "non-matching line after previous match is here"); + return true; } @@ -1144,7 +1746,7 @@ } bool CheckString::CheckSame(const SourceMgr &SM, StringRef Buffer) const { - if (Pat.getCheckTy() != Check::CheckSame) + if (Pat.getCheckTy() != Check::CheckSame && Pat.getCheckTy() != Check::CheckWordSame) // return false; // Count the number of newlines between the previous match and this one. @@ -1175,7 +1777,7 @@ const std::vector &NotStrings, StringMap &VariableTable) const { for (const Pattern *Pat : NotStrings) { - assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); + assert((Pat->getCheckTy() == Check::CheckNot || Pat->getCheckTy() == Check::CheckWordNot) && "Expect CHECK-NOT!"); // size_t MatchLen = 0; size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable); @@ -1204,15 +1806,17 @@ for (const Pattern &Pat : DagNotStrings) { assert((Pat.getCheckTy() == Check::CheckDAG || - Pat.getCheckTy() == Check::CheckNot) && + Pat.getCheckTy() == Check::CheckNot || + Pat.getCheckTy() == Check::CheckWordDAG || + Pat.getCheckTy() == Check::CheckWordNot) && "Invalid CHECK-DAG or CHECK-NOT!"); - if (Pat.getCheckTy() == Check::CheckNot) { + if (Pat.getCheckTy() == Check::CheckNot || Pat.getCheckTy() == Check::CheckWordNot) { NotStrings.push_back(&Pat); continue; } - assert((Pat.getCheckTy() == Check::CheckDAG) && "Expect CHECK-DAG!"); + assert((Pat.getCheckTy() == Check::CheckDAG || Pat.getCheckTy() == Check::CheckWordDAG) && "Expect CHECK-DAG!"); size_t MatchLen = 0, MatchPos; @@ -1296,11 +1900,20 @@ static void AddCheckPrefixIfNeeded() { if (CheckPrefixes.empty()) CheckPrefixes.push_back("CHECK"); + // + // Prefix wild-card which will be always matched. + if (RegexPrefixesOn) { + if (std::find(CheckPrefixes.begin(), CheckPrefixes.end(), "{{*}}") == CheckPrefixes.end()) + CheckPrefixes.push_back("{{*}}"); + } + // } int main(int argc, char **argv) { + sys::PrintStackTraceOnErrorSignal(); PrettyStackTraceProgram X(argc, argv); + cl::ParseCommandLineOptions(argc, argv); if (!ValidateCheckPrefixes()) { @@ -1350,10 +1963,16 @@ bool hasError = false; - unsigned i = 0, j = 0, e = CheckStrings.size(); + unsigned i = 0, j = 0, k = 0, e = CheckStrings.size(); + while (true) { StringRef CheckRegion; + // + StringRef InnerCheckRegion; + std::map CheckLabelDagPositions; + std::vector CheckLabelDagsIndexes; + // if (j == e) { CheckRegion = Buffer; } else { @@ -1376,10 +1995,75 @@ Buffer = Buffer.substr(MatchLabelPos + MatchLabelLen); ++j; } + // + k = 0; + // Choose all CHECK-LABEL-DAG in current check region. + while (k != j) { + const CheckString &CheckLabelDagStr = CheckStrings[k]; + if (CheckLabelDagStr.Pat.getCheckTy() != Check::CheckLabelDag) { + ++k; + continue; + } - for ( ; i != j; ++i) { + // Find and remember matches of all CHECK-LABEL-DAG + size_t MatchLabelLen = 0; + size_t MatchLabelPos = CheckLabelDagStr.Check(SM, CheckRegion, true, + MatchLabelLen, VariableTable); + + if (MatchLabelPos == StringRef::npos) { + hasError = true; + break; + } + CheckLabelDagPositions.insert(std::pair(MatchLabelPos + MatchLabelLen, k)); + CheckLabelDagsIndexes.push_back(k); + k++; + } + + size_t Before = 0; + bool wasLabelDag = false; + for (const auto &MatchLabelDag : CheckLabelDagPositions) { + + unsigned PrevLabelDagInDescription; + std::vector::iterator It; + + // Get next label dag in description + It = find (CheckLabelDagsIndexes.begin(), CheckLabelDagsIndexes.end(), MatchLabelDag.second); + if (It != CheckLabelDagsIndexes.begin()) { + PrevLabelDagInDescription = *(--It); + } else { + PrevLabelDagInDescription = -1; + } + + // Create check region for usual checks + InnerCheckRegion = CheckRegion.substr(Before, MatchLabelDag.first - Before); + Before = MatchLabelDag.first; + + // For this check region run all checks between two label dags in description + for (i = wasLabelDag ? PrevLabelDagInDescription + 1 : 0; i != wasLabelDag ? MatchLabelDag.second : CheckLabelDagsIndexes[0]; ++i) { + // Cycle + const CheckString &CheckStr = CheckStrings[i]; + + // Check each string within the scanned region, including a second check + // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG) + size_t MatchLen = 0; + size_t MatchPos = CheckStr.Check(SM, InnerCheckRegion, false, MatchLen, + VariableTable); + + if (MatchPos == StringRef::npos) { + hasError = true; + i = j; + break; + } + + InnerCheckRegion = InnerCheckRegion.substr(MatchPos + MatchLen); + } + wasLabelDag = true; + } + + for ( ; i != j && !wasLabelDag; ++i) { + // const CheckString &CheckStr = CheckStrings[i]; - + // Check each string within the scanned region, including a second check // of any final CHECK-LABEL (to verify CHECK-NOT and CHECK-DAG) size_t MatchLen = 0;