Index: llvm/trunk/docs/CommandGuide/FileCheck.rst =================================================================== --- llvm/trunk/docs/CommandGuide/FileCheck.rst +++ llvm/trunk/docs/CommandGuide/FileCheck.rst @@ -77,6 +77,15 @@ -verify``. With this option FileCheck will verify that input does not contain warnings not covered by any ``CHECK:`` patterns. +.. option:: --enable-var-scope + + Enables scope for regex variables. + + Variables with names that start with ``$`` are considered global and + remain set throughout the file. + + All other variables get undefined after each encountered ``CHECK-LABEL``. + .. option:: -version Show the version number of this program. @@ -344,6 +353,9 @@ other unique identifiers. Conceptually, the presence of ``CHECK-LABEL`` divides the input stream into separate blocks, each of which is processed independently, preventing a ``CHECK:`` directive in one block matching a line in another block. +If ``--enable-var-scope`` is in effect, all local variables are cleared at the +beginning of the block. + For example, .. code-block:: llvm @@ -436,6 +448,13 @@ Can be useful if you want the operands of ``op`` to be the same register, and don't care exactly which register it is. +If ``--enable-var-scope`` is in effect, variables with names that +start with ``$`` are considered to be global. All others variables are +local. All local variables get undefined at the beginning of each +CHECK-LABEL block. Global variables are not affected by CHECK-LABEL. +This makes it easier to ensure that individual tests are not affected +by variables set in preceding tests. + FileCheck Expressions ~~~~~~~~~~~~~~~~~~~~~ Index: llvm/trunk/test/FileCheck/line-count.txt =================================================================== --- llvm/trunk/test/FileCheck/line-count.txt +++ llvm/trunk/test/FileCheck/line-count.txt @@ -1,15 +1,15 @@ ; RUN: FileCheck -input-file %s %s -2 -3 aaa -4 bbb -5 ccc -6 CHECK: [[@LINE-3]] {{a}}aa -7 CHECK: [[@LINE-3]] {{b}}bb -8 CHECK: [[@LINE-3]] {{c}}cc -9 foobar -10 CHECK: [[@LINE-1]] {{foo}}bar -11 -12 arst CHECK: [[@LINE]] {{a}}rst -13 +; RUN: not FileCheck -check-prefix BAD -input-file %s %s +3 +4 aaa +5 bbb +6 ccc +7 CHECK: [[@LINE-3]] {{a}}aa +8 CHECK: [[@LINE-3]] {{b}}bb +9 CHECK: [[@LINE-3]] {{c}}cc +10 foobar +11 CHECK: [[@LINE-1]] {{foo}}bar +12 +13 arst CHECK: [[@LINE]] {{a}}rst 14 - +15 BAD: [[@LINE:cant-have-regex]] Index: llvm/trunk/test/FileCheck/regex-scope.txt =================================================================== --- llvm/trunk/test/FileCheck/regex-scope.txt +++ llvm/trunk/test/FileCheck/regex-scope.txt @@ -0,0 +1,23 @@ +// RUN: FileCheck -check-prefix CHECK -input-file %s %s +// RUN: FileCheck -check-prefixes CHECK,GLOBAL -input-file %s %s +// RUN: FileCheck -check-prefixes CHECK,LOCAL -input-file %s %s +// RUN: FileCheck -check-prefixes CHECK,GLOBAL --enable-var-scope -input-file %s %s +// RUN: not FileCheck -check-prefixes CHECK,LOCAL --enable-var-scope -input-file %s %s + +local +global +; CHECK: [[LOCAL:loc.*]] +; CHECK: [[$GLOBAL:glo.*]] + +local2 +global2 +; CHECK: [[LOCAL]]2 +; CHECK: [[$GLOBAL]]2 + +barrier: +; CHECK-LABEL: barrier + +local3 +global3 +; LOCAL: [[LOCAL]]3 +; GLOBAL: [[$GLOBAL]]3 Index: llvm/trunk/utils/FileCheck/FileCheck.cpp =================================================================== --- llvm/trunk/utils/FileCheck/FileCheck.cpp +++ llvm/trunk/utils/FileCheck/FileCheck.cpp @@ -73,6 +73,12 @@ "Allows leading and trailing whitespace if --strict-whitespace\n" "is not also passed.")); +static cl::opt EnableVarScope( + "enable-var-scope", cl::init(false), + cl::desc("Enables scope for regex variables. Variables with names that\n" + "do not start with '$' will be reset at the beginning of\n" + "each CHECK-LABEL block.")); + typedef cl::list::const_iterator prefix_iterator; //===----------------------------------------------------------------------===// @@ -263,15 +269,19 @@ // 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; + if (i == 0) { + if (Name[i] == '$') // Global vars start with '$' + continue; + if (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; } - IsExpression = true; - continue; } if (Name[i] != '_' && !isalnum(Name[i]) && (!IsExpression || (Name[i] != '+' && Name[i] != '-'))) { @@ -1262,6 +1272,18 @@ errs() << "\n"; } +// Remove local variables from \p VariableTable. Global variables +// (start with '$') are preserved. +static void ClearLocalVars(StringMap &VariableTable) { + SmallVector LocalVars; + for (const auto &Var : VariableTable) + if (Var.first()[0] != '$') + LocalVars.push_back(Var.first()); + + for (const auto &Var : LocalVars) + VariableTable.erase(Var); +} + /// Check the input to FileCheck provided in the \p Buffer against the \p /// CheckStrings read from the check file. /// @@ -1298,6 +1320,9 @@ ++j; } + if (EnableVarScope) + ClearLocalVars(VariableTable); + for (; i != j; ++i) { const CheckString &CheckStr = CheckStrings[i];