Index: docs/CommandGuide/FileCheck.rst =================================================================== --- docs/CommandGuide/FileCheck.rst +++ docs/CommandGuide/FileCheck.rst @@ -436,6 +436,21 @@ Can be useful if you want the operands of ``op`` to be the same register, and don't care exactly which register it is. +In order to clear previously defined variables you can match a special +variable ``@CLEAR``. This variable always matches and does not consume +any input. Instead, all variables that match supplied pattern get +undefined. This is useful for keeping individual tests in the same +file hermetic. + +.. code-block:: llvm + + ; CHECK: test5: + ; CHECK: notw [[REGISTER:%[a-z]+]] + ; CHECK: andw {{.*}}[[REGISTER]] + ; CHECK: new_test: [[@CLEAR:R.*]] + ; -- Following line will always fail because REGISTER is no longer defined. + ; CHECK: foo {{.*}}[[REGISTER]] + FileCheck Expressions ~~~~~~~~~~~~~~~~~~~~~ Index: test/FileCheck/regex-clear.txt =================================================================== --- /dev/null +++ test/FileCheck/regex-clear.txt @@ -0,0 +1,29 @@ +// RUN: FileCheck -check-prefix X -input-file %s %s +// RUN: FileCheck -check-prefix Y -input-file %s %s +// RUN: FileCheck -check-prefixes X,Y -input-file %s %s +// RUN: FileCheck -check-prefixes X,CY -input-file %s %s +// RUN: FileCheck -check-prefixes Y,CX -input-file %s %s +// RUN: not FileCheck -check-prefix CI -input-file %s %s +// RUN: not FileCheck -check-prefixes X,CA -input-file %s %s +// RUN: not FileCheck -check-prefixes Y,CA -input-file %s %s +// RUN: not FileCheck -check-prefixes X,Y,CX -input-file %s %s +// RUN: not FileCheck -check-prefixes X,Y,CY -input-file %s %s + +op x1 +op y2 +op x1 +op y2 +op x1 +op y2 +; X: [[REGX:x[0-9]]] +; Y: [[REGY:y[0-9]]] +; X: op [[REGX]] +; Y: op [[REGY]] +; CX: [[@CLEAR:REGX$]] +; CY: [[@CLEAR:REGY$]] +; CA: [[@CLEAR:.*]] +; X: op [[REGX]] +; Y: op [[REGY]] + +; Make sure we do fail on invalid regex. +; CI: [[@CLEAR:(]] Index: utils/FileCheck/FileCheck.cpp =================================================================== --- utils/FileCheck/FileCheck.cpp +++ utils/FileCheck/FileCheck.cpp @@ -120,6 +120,7 @@ /// E.g. for the pattern "foo[[bar:.*]]baz", VariableDefs will map "bar" to /// 1. std::map VariableDefs; + std::map ExpressionOperands; Check::CheckType CheckTy; @@ -134,7 +135,7 @@ bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, unsigned LineNumber); - size_t Match(StringRef Buffer, size_t &MatchLen, + size_t Match(const SourceMgr &SM, StringRef Buffer, size_t &MatchLen, StringMap &VariableTable) const; void PrintFailureInfo(const SourceMgr &SM, StringRef Buffer, const StringMap &VariableTable) const; @@ -249,7 +250,7 @@ PatternStr = PatternStr.substr(End + 4); // Get the regex name (e.g. "foo"). - size_t NameEnd = MatchStr.find(':'); + size_t NameEnd = MatchStr.find_first_of(":+"); StringRef Name = MatchStr.substr(0, NameEnd); if (Name.empty()) { @@ -258,18 +259,13 @@ return true; } - // Verify that the name/expression is well formed. FileCheck currently - // supports @LINE, @LINE+number, @LINE-number expressions. The check here - // is relaxed, more strict check is performed in \c EvaluateExpression. + // Verify that the name/expression is well formed. FileCheck + // currently supports @LINE, @LINE+number, @LINE-number, @CLEAR:regex + // expressions. The check here is relaxed, more strict check is + // performed in \c EvaluateExpression. bool IsExpression = false; for (unsigned i = 0, e = Name.size(); i != e; ++i) { if (i == 0 && Name[i] == '@') { - if (NameEnd != StringRef::npos) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), - SourceMgr::DK_Error, - "invalid name in named regex definition"); - return true; - } IsExpression = true; continue; } @@ -312,7 +308,19 @@ RegExStr += '('; ++CurParen; - if (AddRegExToRegEx(MatchStr.substr(NameEnd + 1), CurParen, SM)) + if (IsExpression) { + // Expression inserts always-matching zero-width group and saves + // pattern as an operand to be processed when the group match happens. + StringRef ExpressionArg = MatchStr.substr(NameEnd + 1); + Regex R(ExpressionArg); + std::string Error; + if (!R.isValid(Error)) { + SM.PrintMessage(SMLoc::getFromPointer(ExpressionArg.data()), + SourceMgr::DK_Error, "invalid regex: " + Error); + return true; + } + ExpressionOperands[CurParen - 1] = ExpressionArg; + } else if (AddRegExToRegEx(MatchStr.substr(NameEnd + 1), CurParen, SM)) return true; RegExStr += ')'; @@ -376,6 +384,28 @@ return true; } +// Clears all variables that match \p VarNameRegEx. +// Returns true on error. +static bool ClearVariables(const SourceMgr &SM, StringRef VarNameRegEx, + StringMap &VariableTable) { + Regex VarNameRE(VarNameRegEx); + std::string Error; + if (!VarNameRE.isValid(Error)) { + SM.PrintMessage(SMLoc::getFromPointer(VarNameRegEx.data()), + SourceMgr::DK_Error, "invalid regex: " + Error); + return true; + } + + SmallVector VarsToClear; + for (const auto &Var : VariableTable) + if (VarNameRE.match(Var.first())) + VarsToClear.push_back(Var.first()); + + for (const auto &Var : VarsToClear) + VariableTable.erase(Var); + return false; +} + /// Matches the pattern string against the input buffer \p Buffer /// /// This returns the position that is matched or npos if there is no match. If @@ -384,7 +414,7 @@ /// /// The \p VariableTable StringMap provides the current values of filecheck /// variables and is updated if this match defines new values. -size_t Pattern::Match(StringRef Buffer, size_t &MatchLen, +size_t Pattern::Match(const SourceMgr &SM, StringRef Buffer, size_t &MatchLen, StringMap &VariableTable) const { // If this is the EOF pattern, match it immediately. if (CheckTy == Check::CheckEOF) { @@ -446,7 +476,11 @@ // If this defines any variables, remember their values. for (const auto &VariableDef : VariableDefs) { assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); - VariableTable[VariableDef.first] = MatchInfo[VariableDef.second]; + if (VariableDef.first == "@CLEAR") { + ClearVariables(SM, ExpressionOperands.at(VariableDef.second), + VariableTable); + } else + VariableTable[VariableDef.first] = MatchInfo[VariableDef.second]; } MatchLen = FullMatch.size(); @@ -1009,7 +1043,7 @@ // Match itself from the last position after matching CHECK-DAG. StringRef MatchBuffer = Buffer.substr(LastPos); - size_t MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable); + size_t MatchPos = Pat.Match(SM, MatchBuffer, MatchLen, VariableTable); if (MatchPos == StringRef::npos) { PrintCheckFailed(SM, *this, MatchBuffer, VariableTable); return StringRef::npos; @@ -1117,7 +1151,7 @@ assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); size_t MatchLen = 0; - size_t Pos = Pat->Match(Buffer, MatchLen, VariableTable); + size_t Pos = Pat->Match(SM, Buffer, MatchLen, VariableTable); if (Pos == StringRef::npos) continue; @@ -1158,7 +1192,7 @@ // CHECK-DAG always matches from the start. StringRef MatchBuffer = Buffer.substr(StartPos); - MatchPos = Pat.Match(MatchBuffer, MatchLen, VariableTable); + MatchPos = Pat.Match(SM, MatchBuffer, MatchLen, VariableTable); // With a group of CHECK-DAGs, a single mismatching means the match on // that group of CHECK-DAGs fails immediately. if (MatchPos == StringRef::npos) {