Index: docs/CommandGuide/FileCheck.rst =================================================================== --- docs/CommandGuide/FileCheck.rst +++ docs/CommandGuide/FileCheck.rst @@ -480,3 +480,58 @@ letting us set the :program:`FileCheck` variable ``DLOC`` to the desired value ``0x00000233``, extracted from the line immediately preceding "``intd``". + +Repeated checks +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sometimes some checks should be repeated. In this cases you can use repeated checks. +For example + +.. code-block:: llvm + + //CHECK{3} : {{[a-z]+}} + +It’s equal to + +.. code-block:: llvm + + //CHECK: {{[a-z]+}} + //CHECK: {{[a-z]+}} + //CHECK: {{[a-z]+}} + +There can be used next quantifiers: +* - The asterisk indicates zero or more occurrences of the preceding element. ++ - The plus sign indicates one or more occurrences of the preceding element. +{n} - The preceding item is matched exactly n times. +{min,} - The preceding item is matched min or more times. +{,max} - The preceding item is matched zero or more times, but not more than max times. +{min,max} - The preceding item is matched at least min times, but not more than max times. + +You should remember that there may be extra lines which can be matched with pattern. If you need check there is no more such pattern use CHECK-NOT or CHECK-NEXT +Example: + +.. code-block:: llvm + sub1 + sub2 + sub3 + sub4 + add1 + + //CHECK{3} : {{[a-z]+}} + //CHECK: add1 + +Check will be successful, because there are no limits for characters, which are between two checks. + +.. code-block:: llvm + + //CHECK{3} : {{[a-z]+}} + //CHECK-NOT: {{[a-z]+}} + //CHECK: add1 + +Check will be failed, because there is limit that same pattern can’t be between two matches. + +.. code-block:: llvm + + //CHECK{3} : {{[a-z]+}} + //CHECK-NEXT: add1 + +Check will be failed, because there is limit that next match should be on next line. \ No newline at end of file Index: test/FileCheck/check-next-repeats.txt =================================================================== --- /dev/null +++ test/FileCheck/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/check-repeats.txt =================================================================== --- /dev/null +++ test/FileCheck/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: utils/FileCheck/FileCheck.cpp =================================================================== --- utils/FileCheck/FileCheck.cpp +++ utils/FileCheck/FileCheck.cpp @@ -88,6 +88,8 @@ CheckNot, CheckDAG, CheckLabel, + CheckRepeated, + CheckNextRepeated, /// MatchEOF - When set, this pattern only matches the end of file. This is /// used for trailing CHECK-NOTs. @@ -123,14 +125,24 @@ /// 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; } + /// 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 @@ -696,68 +708,115 @@ 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; +static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix, + size_t &CheckTypeSize, + int &MinRepeatNumber, + int &MaxRepeatNumber) { + MinRepeatNumber = 0; + MaxRepeatNumber = -1; + CheckTypeSize = 0; + bool NextStatement = false; - case Check::CheckSame: - return sizeof("-SAME:") - 1; + size_t StringEnd = Buffer.find(':'); - 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 (StringEnd == StringRef::npos) { + return Check::CheckNone; } + std::string StringForMatch; + StringRef Rest; - llvm_unreachable("Bad check type"); -} + StringForMatch = + Buffer.substr(Prefix.size(), StringEnd - Prefix.size() + 1).str(); + Rest = Buffer.drop_front(Prefix.size()); -static Check::CheckType FindCheckType(StringRef Buffer, StringRef Prefix) { - char NextChar = Buffer[Prefix.size()]; + if (Rest.startswith("-NEXT")) { + CheckTypeSize += sizeof("-NEXT") - 1; + StringForMatch = StringForMatch.substr(5); + NextStatement = true; + } + 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 (NextStatement) { + return Check::CheckBadNot; + } + + 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; + } // 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:")) + 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:")) return Check::CheckBadNot; return Check::CheckNone; @@ -780,9 +839,13 @@ // prefixes then AA-CHECK: should match the second one. static StringRef FindFirstCandidateMatch(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; @@ -818,7 +881,8 @@ if (PrefixLoc != 0 && IsPartOfWord(Buffer[PrefixLoc - 1])) FirstTy = Check::CheckNone; else - FirstTy = FindCheckType(Rest, Prefix); + FirstTy = FindCheckType(Rest, Prefix, FirstTypeSize, MinRepeatNumber, + MaxRepeatNumber); FirstLoc = PrefixLoc; FirstPrefix = Prefix; @@ -832,15 +896,21 @@ CheckTy = FirstTy; CheckLoc = FirstLoc; + CheckTypeSize = FirstTypeSize; return FirstPrefix; } static StringRef FindFirstMatchingPrefix(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(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'); @@ -910,13 +980,19 @@ 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, LineNumber, CheckTy, - PrefixLoc); + PrefixLoc, + CheckTypeSize, + MinRepeatNumber, + MaxRepeatNumber); if (UsedPrefix.empty()) break; @@ -926,7 +1002,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) { @@ -947,7 +1023,7 @@ SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); // Parse the pattern. - Pattern P(CheckTy); + Pattern P(CheckTy, MinRepeatNumber, MaxRepeatNumber); if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber)) return true; @@ -963,9 +1039,13 @@ Buffer = Buffer.substr(EOL); // Verify that CHECK-NEXT lines have at least one CHECK line before them. - if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckSame) && + if ((CheckTy == Check::CheckNext || CheckTy == Check::CheckNextRepeated || + CheckTy == Check::CheckSame) && CheckStrings.empty()) { - StringRef Type = CheckTy == Check::CheckNext ? "NEXT" : "SAME"; + StringRef Type = + CheckTy == Check::CheckNext || CheckTy == Check::CheckNextRepeated + ? "NEXT" + : "SAME"; SM.PrintMessage(SMLoc::getFromPointer(UsedPrefixStart), SourceMgr::DK_Error, "found '" + UsedPrefix + "-" + Type + "' without previous '" @@ -1077,8 +1157,55 @@ return StringRef::npos; } - // Match itself from the last position after matching CHECK-DAG. 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. + MatchBuffer = Buffer.substr(LastPos); size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable); if (MatchPos == StringRef::npos) { PrintCheckFailed(SM, *this, MatchBuffer, VariableTable); @@ -1110,7 +1237,8 @@ } bool CheckString::CheckNext(const SourceMgr &SM, StringRef Buffer) const { - if (Pat.getCheckTy() != Check::CheckNext) + if (Pat.getCheckTy() != Check::CheckNext && + Pat.getCheckTy() != Check::CheckNextRepeated) return false; // Count the number of newlines between the previous match and this one.