diff --git a/llvm/docs/CommandGuide/FileCheck.rst b/llvm/docs/CommandGuide/FileCheck.rst --- a/llvm/docs/CommandGuide/FileCheck.rst +++ b/llvm/docs/CommandGuide/FileCheck.rst @@ -499,7 +499,7 @@ ``CHECK-LABEL:`` directives cannot contain variable definitions or uses. -FileCheck Pattern Matching Syntax +FileCheck Regex Matching Syntax ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ All FileCheck directives take a pattern to match. @@ -525,14 +525,15 @@ braces explicitly from the input, you can use something ugly like ``{{[{][{]}}`` as your pattern. -FileCheck Pattern Expressions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +FileCheck String Substitution Blocks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is often useful to match a pattern and then verify that it occurs again -later in the file. For codegen tests, this can be useful to allow any register, -but verify that that register is used consistently later. To do this, -:program:`FileCheck` supports pattern expressions that allow pattern variables -to be defined and substituted into patterns. Here is a simple example: +later in the file. For codegen tests, this can be useful to allow any +register, but verify that that register is used consistently later. To do +this, :program:`FileCheck` supports string substitution blocks that allow +string variables to be defined and substituted into patterns. Here is a simple +example: .. code-block:: llvm @@ -541,15 +542,16 @@ ; CHECK: andw {{.*}}[[REGISTER]] The first check line matches a regex ``%[a-z]+`` and captures it into the -variable ``REGISTER``. The second line verifies that whatever is in -``REGISTER`` occurs later in the file after an "``andw``". :program:`FileCheck` -variable references are always contained in ``[[ ]]`` pairs, and their names can -be formed with the regex ``[a-zA-Z_][a-zA-Z0-9_]*``. If a colon follows the name, -then it is a definition of the variable; otherwise, it is a use. +string variable ``REGISTER``. The second line verifies that whatever is in +``REGISTER`` occurs later in the file after an "``andw``". :program:`FileCheck` +string substitution block are always contained in ``[[ ]]`` pairs, and string +variable names can be formed with the regex ``[a-zA-Z_][a-zA-Z0-9_]*``. If a +colon follows the name, then it is a definition of the variable; otherwise, it +is a substitution. -:program:`FileCheck` variables can be defined multiple times, and uses always -get the latest value. Variables can also be used later on the same line they -were defined on. For example: +:program:`FileCheck` variables can be defined multiple times, and substitutions +always get the latest value. Variables can also be substituted later on the +same line they were defined on. For example: .. code-block:: llvm @@ -565,16 +567,17 @@ This makes it easier to ensure that individual tests are not affected by variables set in preceding tests. -FileCheck Numeric Variables and Expressions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +FileCheck Numeric Substitution Blocks +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:program:`FileCheck` also allows checking for numeric values that satisfy a -numeric expression constraint based on numeric variables. This allows -``CHECK:`` directives to verify a numeric relation between two numbers, such as -the need for consecutive registers to be used. +:program:`FileCheck` also supports numeric substitution blocks that allow +checking for numeric values that satisfy a numeric expression constraint based +on numeric variables. This allows ``CHECK:`` directives to verify a numeric +relation between two numbers, such as the need for consecutive registers to be +used. -The syntax to check a numeric expression constraint is -``[[#]]`` where: +The syntax of a numeric substitution block is ``[[#]]`` +where: * ```` is the name of a numeric variable defined on the command line. @@ -585,6 +588,10 @@ the numeric operation . It must be present if ```` is present, absent otherwise. +Spaces are accepted before, after and between any of these elements. + +Unlike string substitution blocks, numeric substitution blocks only introduce +numeric substitutions which substitution a numeric expression for its value. For example: .. code-block:: llvm @@ -606,7 +613,7 @@ due to ``7`` being unequal to ``5 + 1``. The ``--enable-var-scope`` option has the same effect on numeric variables as -on pattern variables. +on string variables. FileCheck Pseudo Numeric Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -632,9 +639,9 @@ // CHECK-NEXT: {{^ ;}} int a -To support legacy uses of ``@LINE`` as a special pattern variable, -:program:`FileCheck` also accepts the following uses of ``@LINE`` with pattern -variable syntax: ``[[@LINE]]``, ``[[@LINE+]]`` and +To support legacy uses of ``@LINE`` as a special string variable, +:program:`FileCheck` also accepts the following uses of ``@LINE`` with string +substitution block syntax: ``[[@LINE]]``, ``[[@LINE+]]`` and ``[[@LINE-]]`` without any spaces inside the brackets and where ``offset`` is an integer. diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -37,7 +37,7 @@ }; //===----------------------------------------------------------------------===// -// Numeric expression handling code. +// Numeric substitution handling code. //===----------------------------------------------------------------------===// /// Class representing a numeric variable with a given value in a numeric @@ -105,60 +105,82 @@ class FileCheckPatternContext; -/// Class representing a substitution to perform in the string to match. -class FileCheckPatternSubstitution { -private: - /// Pointer to a class instance holding the table with the values of live - /// pattern variables at the start of any given CHECK line. Used for - /// substituting pattern variables (numeric variables have their value in the - /// FileCheckNumExpr class instance pointed to by NumExpr). +/// Class representing a substitution to perform in the RegExStr string to +/// match. +class FileCheckSubstitution { +protected: + /// Pointer to a class instance holding, among other things, the table with + /// the values of live string variables at the start of any given CHECK line. + /// Used for substituting string variables for the text they were defined to. + /// Numeric expression are linked to the numeric variables they use at parse + /// time and access directly the value of the numeric variable to evaluate + /// their value. FileCheckPatternContext *Context; - /// Whether this represents a numeric expression substitution. - bool IsNumExpr; - /// The string that needs to be substituted for something else. For a - /// pattern variable this is its name, otherwise this is the whole numeric + /// string variable this is its name, otherwise this is the whole numeric /// expression. StringRef FromStr; - /// If this is a numeric expression substitution, this is the pointer to the - /// class representing that numeric expression. - FileCheckNumExpr *NumExpr = nullptr; - // Index in RegExStr of where to do the substitution. size_t InsertIdx; public: - /// Constructor for a pattern variable substitution. - FileCheckPatternSubstitution(FileCheckPatternContext *Context, - StringRef VarName, size_t InsertIdx) - : Context(Context), IsNumExpr(false), FromStr(VarName), - InsertIdx(InsertIdx) {} - - /// Constructor for a numeric expression substitution. - FileCheckPatternSubstitution(FileCheckPatternContext *Context, StringRef Expr, - FileCheckNumExpr *NumExpr, size_t InsertIdx) - : Context(Context), IsNumExpr(true), FromStr(Expr), NumExpr(NumExpr), - InsertIdx(InsertIdx) {} + FileCheckSubstitution(FileCheckPatternContext *Context, StringRef VarName, + size_t InsertIdx) + : Context(Context), FromStr(VarName), InsertIdx(InsertIdx) {} - /// \returns whether this is a numeric expression substitution. - bool isNumExpr() const { return IsNumExpr; } + virtual ~FileCheckSubstitution() = default; - /// \returns the string to be substituted. + /// \returns the string to be substituted for something else. StringRef getFromString() const { return FromStr; } - /// \returns the index where the substitution is to be performed. + /// \returns the index where the substitution is to be performed in RegExStr. size_t getIndex() const { return InsertIdx; } /// \returns the result of the substitution represented by this class /// instance or None if substitution failed. Numeric expressions are - /// substituted by their values. Pattern variables are simply replaced by the + /// substituted by their values. String variables are simply replaced by the /// text their definition matched. + virtual llvm::Optional getResult() const = 0; + + /// \returns the name of the variable used in this substitution if undefined, + /// or an empty string otherwise. + virtual StringRef getUndefVarName() const = 0; +}; + +class FileCheckStringSubstitution : public FileCheckSubstitution { +public: + FileCheckStringSubstitution(FileCheckPatternContext *Context, + StringRef VarName, size_t InsertIdx) + : FileCheckSubstitution(Context, VarName, InsertIdx) {} + + /// \returns the text that the string variable in this substitution matched + /// when defined, or None if the variable is undefined. + llvm::Optional getResult() const; + + /// \returns the name of the string variable used in this substitution if + /// undefined, or an empty string otherwise. + StringRef getUndefVarName() const; +}; + +class FileCheckNumericSubstitution : public FileCheckSubstitution { +private: + /// Pointer to the class representing the numeric expression whose value is + /// to be substituted. + FileCheckNumExpr *NumExpr; + +public: + FileCheckNumericSubstitution(FileCheckPatternContext *Context, StringRef Expr, + FileCheckNumExpr *NumExpr, size_t InsertIdx) + : FileCheckSubstitution(Context, Expr, InsertIdx), NumExpr(NumExpr) {} + + /// \returns the result of evaluating the numeric expression in this + /// substitution as a string, or None if evaluation failed. llvm::Optional getResult() const; - /// \returns the name of the undefined variable used in this substitution, if - /// any, or an empty string otherwise. + /// \returns the name of the numeric variable used in this substitution if + /// undefined, or an empty string otherwise. StringRef getUndefVarName() const; }; @@ -216,14 +238,14 @@ friend class FileCheckPattern; private: - /// When matching a given pattern, this holds the value of all the FileCheck - /// pattern variables defined in previous patterns. In a pattern, only the - /// last definition for a given variable is recorded in this table. + /// When matching a given pattern, this holds the value of all the string + /// variables defined in previous patterns. In a pattern, only the last + /// definition for a given variable is recorded in this table. /// Back-references are used for uses after any the other definition. StringMap GlobalVariableTable; - /// Map of all pattern variables defined so far. Used at parse time to detect - /// a name conflict between a numeric variable and a pattern variable when + /// Map of all string variables defined so far. Used at parse time to detect + /// a name conflict between a numeric variable and a string variable when /// the former is defined on a later line than the latter. StringMap DefinedVariableTable; @@ -242,12 +264,16 @@ /// automatically free them once they are guaranteed to no longer be used. std::vector> NumericVariables; + /// Vector holding pointers to all substitutions. Used to automatically free + /// them once they are guaranteed to no longer be used. + std::vector> Substitutions; + public: - /// \returns the value of pattern variable \p VarName or None if no such + /// \returns the value of string variable \p VarName or None if no such /// variable has been defined. llvm::Optional getPatternVarValue(StringRef VarName); - /// Defines pattern and numeric variables from definitions given on the + /// Defines string and numeric variables from definitions given on the /// command line, passed as a vector of [#]VAR=VAL strings in /// \p CmdlineDefines. Reports any error to \p SM and \returns whether an /// error occured. @@ -255,7 +281,9 @@ SourceMgr &SM); /// Undefines local variables (variables whose name does not start with a '$' - /// sign), i.e. removes them from GlobalVariableTable. + /// sign), i.e. removes them from GlobalVariableTable and + /// GlobalNumericVariableTable and for numeric variable clear their value as + /// well. void clearLocalVars(); private: @@ -268,6 +296,17 @@ /// Makes a new numeric variable and registers it for destruction when the /// context is destroyed. FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value); + + /// Makes a new string substitution and registers it for destruction when the + /// context is destroyed. + FileCheckSubstitution *makeStringSubstitution(StringRef VarName, + size_t InsertIdx); + + /// Makes a new numeric substitution and registers it for destruction when + /// the context is destroyed. + FileCheckSubstitution *makeNumericSubstitution(StringRef Expr, + FileCheckNumExpr *NumExpr, + size_t InsertIdx); }; class FileCheckPattern { @@ -281,17 +320,15 @@ /// a fixed string to match. std::string RegExStr; - /// Entries in this vector represent uses of a pattern variable or a numeric - /// expression in the pattern that need to be substituted in the regexp - /// pattern at match time, e.g. "foo[[bar]]baz[[#N+1]]". In this case, the + /// Entries in this vector represent a substitution of a string variable or a + /// numeric expression in the RegExStr regex at match time. For example, in + /// the case of a CHECK directive with the pattern "foo[[bar]]baz[[#N+1]]", /// RegExStr will contain "foobaz" and we'll get two entries in this vector - /// that tells us to insert the value of pattern variable "bar" at offset 3 - /// and the value of numeric expression "N+1" at offset 6. Uses are - /// represented by a FileCheckPatternSubstitution class to abstract whether - /// it is a pattern variable or a numeric expression. - std::vector Substitutions; + /// that tells us to insert the value of string variable "bar" at offset 3 + /// and the value of numeric expression "N+1" at offset 6. + std::vector Substitutions; - /// Maps names of pattern variables defined in a pattern to the parenthesized + /// Maps names of string variables defined in a pattern to the parenthesized /// capture numbers of their last definition. /// /// E.g. for the pattern "foo[[bar:.*]]baz[[bar]]quux[[bar:.*]]", @@ -304,9 +341,9 @@ /// Pointer to a class instance holding the global state shared by all /// patterns: - /// - separate tables with the values of live pattern and numeric variables + /// - separate tables with the values of live string and numeric variables /// respectively at the start of any given CHECK line; - /// - table holding whether a pattern variable has been defined at any given + /// - table holding whether a string variable has been defined at any given /// point during the parsing phase. FileCheckPatternContext *Context; @@ -335,14 +372,14 @@ /// character that is part of the variable name. Otherwise, only /// \returns true. static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx); - /// Parses a numeric expression involving (pseudo if \p IsPseudo is true) + /// Parses a numeric substitution involving (pseudo if \p IsPseudo is true) /// variable \p Name with the string corresponding to the operation being /// performed in \p Trailer. \returns the class representing the numeric - /// expression or nullptr if parsing fails in which case errors are reported - /// on \p SM. - FileCheckNumExpr *parseNumericExpression(StringRef Name, bool IsPseudo, - StringRef Trailer, - const SourceMgr &SM) const; + /// expression being substituted or nullptr if parsing fails, in which case + /// errors are reported on \p SM. + FileCheckNumExpr *parseNumericSubstitution(StringRef Name, bool IsPseudo, + StringRef Trailer, + const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. /// @@ -361,7 +398,7 @@ /// string. /// /// The GlobalVariableTable StringMap in the FileCheckPatternContext class - /// instance provides the current values of FileCheck pattern variables and + /// instance provides the current values of FileCheck string variables and /// is updated if this match defines new values. size_t match(StringRef Buffer, size_t &MatchLen) const; /// Prints the value of successful substitutions or the name of the undefined diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -52,14 +52,14 @@ return StringRef(); } -llvm::Optional FileCheckPatternSubstitution::getResult() const { - if (IsNumExpr) { - llvm::Optional EvaluatedValue = NumExpr->eval(); - if (!EvaluatedValue) - return llvm::None; - return utostr(*EvaluatedValue); - } +llvm::Optional FileCheckNumericSubstitution::getResult() const { + llvm::Optional EvaluatedValue = NumExpr->eval(); + if (!EvaluatedValue) + return llvm::None; + return utostr(*EvaluatedValue); +} +llvm::Optional FileCheckStringSubstitution::getResult() const { // Look up the value and escape it so that we can put it into the regex. llvm::Optional VarVal = Context->getPatternVarValue(FromStr); if (!VarVal) @@ -67,12 +67,13 @@ return Regex::escape(*VarVal); } -StringRef FileCheckPatternSubstitution::getUndefVarName() const { - if (IsNumExpr) - // Although a use of an undefined numeric variable is detected at parse - // time, a numeric variable can be undefined later by ClearLocalVariables. - return NumExpr->getUndefVarName(); +StringRef FileCheckNumericSubstitution::getUndefVarName() const { + // Although a use of an undefined numeric variable is detected at parse + // time, a numeric variable can be undefined later by ClearLocalVariables. + return NumExpr->getUndefVarName(); +} +StringRef FileCheckStringSubstitution::getUndefVarName() const { if (!Context->getPatternVarValue(FromStr)) return FromStr; @@ -129,9 +130,9 @@ } FileCheckNumExpr * -FileCheckPattern::parseNumericExpression(StringRef Name, bool IsPseudo, - StringRef Trailer, - const SourceMgr &SM) const { +FileCheckPattern::parseNumericSubstitution(StringRef Name, bool IsPseudo, + StringRef Trailer, + const SourceMgr &SM) const { if (IsPseudo && !Name.equals("@LINE")) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, "invalid pseudo numeric variable '" + Name + "'"); @@ -288,12 +289,12 @@ continue; } - // Pattern and numeric expression matches. Pattern expressions come in two - // forms: [[foo:.*]] and [[foo]]. The former matches .* (or some other - // regex) and assigns it to the FileCheck variable 'foo'. The latter - // substitutes foo's value. Numeric expressions start with a '#' sign after - // the double brackets and only have the substitution form. Both pattern - // and numeric variables must satisfy the regular expression + // String and numeric substitution blocks. String substitution blocks come + // in two forms: [[foo:.*]] and [[foo]]. The former matches .* (or some + // other regex) and assigns it to the string variable 'foo'. The latter + // substitutes foo's value. Numeric substitution blocks start with a + // '#' sign after the double brackets and only have the substitution form. + // Both string and numeric variables must satisfy the regular expression // "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common // errors. if (PatternStr.startswith("[[")) { @@ -302,23 +303,21 @@ // offset relative to the beginning of the match string. size_t End = FindRegexVarEnd(UnparsedPatternStr, SM); StringRef MatchStr = UnparsedPatternStr.substr(0, End); - bool IsNumExpr = MatchStr.consume_front("#"); - const char *RefTypeStr = - IsNumExpr ? "numeric expression" : "pattern variable"; + bool IsNumBlock = MatchStr.consume_front("#"); if (End == StringRef::npos) { - SM.PrintMessage( - SMLoc::getFromPointer(PatternStr.data()), SourceMgr::DK_Error, - Twine("Invalid ") + RefTypeStr + " reference, no ]] found"); + SM.PrintMessage(SMLoc::getFromPointer(PatternStr.data()), + SourceMgr::DK_Error, + "Invalid substitution block, no ]] found"); return true; } - // Strip the subtitution we are parsing. End points to the start of the - // "]]" closing the expression so account for it in computing the index - // of the first unparsed character. + // Strip the subtitution block we are parsing. End points to the start of + // the "]]" closing the expression so account for it in computing the + // index of the first unparsed character. PatternStr = UnparsedPatternStr.substr(End + 2); size_t VarEndIdx = MatchStr.find(":"); - if (IsNumExpr) + if (IsNumBlock) MatchStr = MatchStr.ltrim(SpaceChars); else { size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t"); @@ -329,7 +328,7 @@ } } - // Get the regex name (e.g. "foo") and verify it is well formed. + // Get the variable name (e.g. "foo") and verify it is well formed. bool IsPseudo; unsigned TrailIdx; if (parseVariable(MatchStr, IsPseudo, TrailIdx)) { @@ -349,11 +348,11 @@ if (IsPseudo || !Trailer.consume_front(":")) { SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error, - "invalid name in pattern variable definition"); + "invalid name in string variable definition"); return true; } - // Detect collisions between pattern and numeric variables when the + // Detect collisions between string and numeric variables when the // former is created later than the latter. if (Context->GlobalNumericVariableTable.find(Name) != Context->GlobalNumericVariableTable.end()) { @@ -364,18 +363,18 @@ } } - if (IsNumExpr || (!IsVarDef && IsPseudo)) { - NumExpr = parseNumericExpression(Name, IsPseudo, Trailer, SM); + if (IsNumBlock || (!IsVarDef && IsPseudo)) { + NumExpr = parseNumericSubstitution(Name, IsPseudo, Trailer, SM); if (NumExpr == nullptr) return true; - IsNumExpr = true; + IsNumBlock = true; } - // Handle variable use: [[foo]] and [[#]]. + // Handle substitutions: [[foo]] and [[#]]. if (!IsVarDef) { - // Handle use of pattern variables that were defined earlier on the - // same line by emitting a backreference. - if (!IsNumExpr && VariableDefs.find(Name) != VariableDefs.end()) { + // Handle substitution of string variables that were defined earlier on + // the same line by emitting a backreference. + if (!IsNumBlock && VariableDefs.find(Name) != VariableDefs.end()) { unsigned CaptureParen = VariableDefs[Name]; if (CaptureParen < 1 || CaptureParen > 9) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), @@ -385,19 +384,19 @@ } AddBackrefToRegEx(CaptureParen); } else { - // Handle use of pattern variables ([[]]) defined in previous - // CHECK pattern or use of a numeric expression. - FileCheckPatternSubstitution Substitution = - IsNumExpr ? FileCheckPatternSubstitution(Context, MatchStr, - NumExpr, SubstInsertIdx) - : FileCheckPatternSubstitution(Context, MatchStr, - SubstInsertIdx); + // Handle substitution of string variables ([[]]) defined in + // previous CHECK pattern or substitution of a numeric expression. + FileCheckSubstitution *Substitution = + IsNumBlock + ? Context->makeNumericSubstitution(MatchStr, NumExpr, + SubstInsertIdx) + : Context->makeStringSubstitution(MatchStr, SubstInsertIdx); Substitutions.push_back(Substitution); } continue; } - // Handle [[foo:.*]]. + // Handle variable definitions: [[foo:.*]]. VariableDefs[Name] = CurParen; RegExStr += '('; ++CurParen; @@ -460,7 +459,7 @@ // Regex match. - // If there are variable uses, we need to create a temporary string with the + // If there are substitutions, we need to create a temporary string with the // actual value. StringRef RegExToMatch = RegExStr; std::string TmpStr; @@ -468,17 +467,17 @@ TmpStr = RegExStr; size_t InsertOffset = 0; - // Substitute all pattern variables and numeric expressions whose value is - // known just now. Use of pattern variables defined on the same line are + // Substitute all string variables and numeric expressions whose value is + // only known now. Use of string variables defined on the same line are // handled by back-references. for (const auto &Substitution : Substitutions) { // Substitute and check for failure (e.g. use of undefined variable). - llvm::Optional Value = Substitution.getResult(); + llvm::Optional Value = Substitution->getResult(); if (!Value) return StringRef::npos; // Plop it into the regex at the adjusted offset. - TmpStr.insert(TmpStr.begin() + Substitution.getIndex() + InsertOffset, + TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset, Value->begin(), Value->end()); InsertOffset += Value->size(); } @@ -495,7 +494,7 @@ assert(!MatchInfo.empty() && "Didn't get any match"); StringRef FullMatch = MatchInfo[0]; - // If this defines any pattern variables, remember their values. + // If this defines any string variables, remember their values. for (const auto &VariableDef : VariableDefs) { assert(VariableDef.second < MatchInfo.size() && "Internal paren error"); Context->GlobalVariableTable[VariableDef.first] = @@ -529,30 +528,25 @@ void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer, SMRange MatchRange) const { - // Print what we know about substitutions. This covers both uses of pattern - // variables and numeric subsitutions. + // Print what we know about substitutions. if (!Substitutions.empty()) { for (const auto &Substitution : Substitutions) { SmallString<256> Msg; raw_svector_ostream OS(Msg); - bool IsNumExpr = Substitution.isNumExpr(); - llvm::Optional MatchedValue = Substitution.getResult(); + llvm::Optional MatchedValue = Substitution->getResult(); // Substitution failed or is not known at match time, print the undefined // variable it uses. if (!MatchedValue) { - StringRef UndefVarName = Substitution.getUndefVarName(); + StringRef UndefVarName = Substitution->getUndefVarName(); if (UndefVarName.empty()) continue; OS << "uses undefined variable \""; OS.write_escaped(UndefVarName) << "\""; } else { // Substitution succeeded. Print substituted value. - if (IsNumExpr) - OS << "with numeric expression \""; - else - OS << "with variable \""; - OS.write_escaped(Substitution.getFromString()) << "\" equal to \""; + OS << "with \""; + OS.write_escaped(Substitution->getFromString()) << "\" equal to \""; OS.write_escaped(*MatchedValue) << "\""; } @@ -656,6 +650,21 @@ return NumericVariables.back().get(); } +FileCheckSubstitution * +FileCheckPatternContext::makeStringSubstitution(StringRef VarName, + size_t InsertIdx) { + Substitutions.push_back( + llvm::make_unique(this, VarName, InsertIdx)); + return Substitutions.back().get(); +} + +FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( + StringRef Expr, FileCheckNumExpr *NumExpr, size_t InsertIdx) { + Substitutions.push_back(llvm::make_unique( + this, Expr, NumExpr, InsertIdx)); + return Substitutions.back().get(); +} + size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) { // Offset keeps track of the current offset within the input Str size_t Offset = 0; @@ -1585,13 +1594,13 @@ continue; } - // Detect collisions between pattern and numeric variables when the - // latter is created later than the former. + // Detect collisions between string and numeric variables when the latter + // is created later than the former. if (DefinedVariableTable.find(CmdlineName) != DefinedVariableTable.end()) { SM.PrintMessage( SMLoc::getFromPointer(CmdlineName.data()), SourceMgr::DK_Error, - "pattern variable with name '" + CmdlineName + "' already exists"); + "string variable with name '" + CmdlineName + "' already exists"); ErrorFound = true; continue; } @@ -1611,7 +1620,7 @@ // Record this variable definition. GlobalNumericVariableTable[CmdlineName] = DefinedNumericVariable; } else { - // Pattern variable definition. + // String variable definition. std::pair CmdlineNameVal = CmdlineDef.split('='); StringRef Name = CmdlineNameVal.first; bool IsPseudo; @@ -1619,14 +1628,14 @@ if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) || IsPseudo || TrailIdx != Name.size() || Name.empty()) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, - "invalid name in pattern variable definition '" + Name + + "invalid name in string variable definition '" + Name + "'"); ErrorFound = true; continue; } - // Detect collisions between pattern and numeric variables when the - // former is created later than the latter. + // Detect collisions between string and numeric variables when the former + // is created later than the latter. if (GlobalNumericVariableTable.find(Name) != GlobalNumericVariableTable.end()) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, @@ -1636,12 +1645,12 @@ continue; } GlobalVariableTable.insert(CmdlineNameVal); - // Mark the pattern variable as defined to detect collisions between - // pattern and numeric variables in DefineCmdlineVariables when the - // latter is created later than the former. We cannot reuse - // GlobalVariableTable for that by populating it with an empty string - // since we would then lose the ability to detect the use of an undefined - // variable in Match(). + // Mark the string variable as defined to detect collisions between + // string and numeric variables in DefineCmdlineVariables when the latter + // is created later than the former. We cannot reuse GlobalVariableTable + // for that by populating it with an empty string since we would then + // lose the ability to detect the use of an undefined variable in + // match(). DefinedVariableTable[Name] = true; } } @@ -1655,12 +1664,12 @@ if (Var.first()[0] != '$') LocalPatternVars.push_back(Var.first()); - // Numeric expression substitution reads the value of a variable directly, - // not via GlobalNumericVariableTable. Therefore, we clear local variables by - // clearing their value which will lead to a numeric expression substitution - // failure. We also mark the variable for removal from - // GlobalNumericVariableTable since this is what defineCmdlineVariables - // checks to decide that no global variable has been defined. + // Numeric substitution reads the value of a variable directly, not via + // GlobalNumericVariableTable. Therefore, we clear local variables by + // clearing their value which will lead to a numeric substitution failure. We + // also mark the variable for removal from GlobalNumericVariableTable since + // this is what defineCmdlineVariables checks to decide that no global + // variable has been defined. for (const auto &Var : GlobalNumericVariableTable) if (Var.first()[0] != '$') { Var.getValue()->clearValue(); diff --git a/llvm/test/FileCheck/line-count.txt b/llvm/test/FileCheck/line-count.txt --- a/llvm/test/FileCheck/line-count.txt +++ b/llvm/test/FileCheck/line-count.txt @@ -23,7 +23,7 @@ 23 arst CHECK: [[@LINE]] {{a}}rst 24 25 BAD1: [[@LINE:cant-have-regex]] -26 ERR1: line-count.txt:[[#@LINE-1]]:12: error: invalid name in pattern variable definition +26 ERR1: line-count.txt:[[#@LINE-1]]:12: error: invalid name in string variable definition 27 28 BAD2: [[ @LINE]] 29 ERR2: line-count.txt:[[#@LINE-1]]:12: error: unexpected whitespace diff --git a/llvm/test/FileCheck/numeric-defines.txt b/llvm/test/FileCheck/numeric-defines.txt --- a/llvm/test/FileCheck/numeric-defines.txt +++ b/llvm/test/FileCheck/numeric-defines.txt @@ -14,9 +14,9 @@ NUMERRMSG: defines.txt:[[#@LINE-3]]:11: error: CHECKNUM: expected string not found in input NUMERRMSG: defines.txt:1:1: note: scanning from here -NUMERRMSG: defines.txt:1:1: note: with numeric expression "NUMVAL" equal to "8" +NUMERRMSG: defines.txt:1:1: note: with "NUMVAL" equal to "8" NUMERRMSG: defines.txt:[[#@LINE-7]]:1: note: possible intended match here NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:13: error: {{NUMNOT}}-NOT: excluded string found in input NOT-NUMERRMSG: defines.txt:[[#@LINE-10]]:1: note: found here -NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with numeric expression "NUMVAL" equal to "12" +NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with "NUMVAL" equal to "12" diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -71,7 +71,7 @@ INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: VAR1*2: {{\[\[#VAR1\*2\]\]}} INVAL-OP-MSG-NEXT: {{^ \^$}} -; Name conflict between Numeric variable definition and pattern variable +; Name conflict between Numeric variable definition and string variable ; definition RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 --check-prefixes CONFLICT,CONFLICT1 --input-file %s %s 2>&1 \ RUN: | FileCheck --strict-whitespace --check-prefix CLI-INPUT-PAT-CONFLICT %s @@ -90,6 +90,6 @@ CLI-CLI-PAT-CONFLICT: Global defines:3:19: error: numeric variable with name 'NUMVAR' already exists CLI-CLI-PAT-CONFLICT-NEXT: Global define #3: NUMVAR=foobar CLI-CLI-PAT-CONFLICT-NEXT: {{^ \^$}} -CLI-CLI-NUM-CONFLICT: Global defines:3:20: error: pattern variable with name 'PATVAR' already exists +CLI-CLI-NUM-CONFLICT: Global defines:3:20: error: string variable with name 'PATVAR' already exists CLI-CLI-NUM-CONFLICT-NEXT: Global define #3: #PATVAR=42 CLI-CLI-NUM-CONFLICT-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/pattern-defines-diagnostics.txt b/llvm/test/FileCheck/string-defines-diagnostics.txt rename from llvm/test/FileCheck/pattern-defines-diagnostics.txt rename to llvm/test/FileCheck/string-defines-diagnostics.txt --- a/llvm/test/FileCheck/pattern-defines-diagnostics.txt +++ b/llvm/test/FileCheck/string-defines-diagnostics.txt @@ -16,19 +16,19 @@ RUN: not FileCheck -D=10 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --check-prefix ERRCLIVAR1 -ERRCLIVAR1: Missing pattern variable name in command-line definition '-D=10' +ERRCLIVAR1: Missing variable name in command-line definition '-D=10' ; Missing variable name. RUN: not FileCheck -D= --input-file %s %s 2>&1 \ RUN: | FileCheck %s --check-prefix ERRCLIVAR2 -ERRCLIVAR2: Missing pattern variable name in command-line definition '-D=' +ERRCLIVAR2: Missing variable name in command-line definition '-D=' ; Invalid variable name: starts with a digit. RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLIFMT -ERRCLIFMT: Global defines:1:19: error: invalid name in pattern variable definition '10VALUE' +ERRCLIFMT: Global defines:1:19: error: invalid name in string variable definition '10VALUE' ERRCLIFMT-NEXT: Global define #1: 10VALUE=10 ERRCLIFMT-NEXT: {{^ \^$}} @@ -36,7 +36,7 @@ RUN: not FileCheck -D@VALUE=10 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLIPSEUDO -ERRCLIPSEUDO: Global defines:1:19: error: invalid name in pattern variable definition '@VALUE' +ERRCLIPSEUDO: Global defines:1:19: error: invalid name in string variable definition '@VALUE' ERRCLIPSEUDO-NEXT: Global define #1: @VALUE=10 ERRCLIPSEUDO-NEXT: {{^ \^$}} @@ -44,6 +44,6 @@ RUN: not FileCheck -D'VALUE + 2=10' --input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLITRAIL -ERRCLITRAIL: Global defines:1:19: error: invalid name in pattern variable definition 'VALUE + 2' +ERRCLITRAIL: Global defines:1:19: error: invalid name in string variable definition 'VALUE + 2' ERRCLITRAIL-NEXT: Global define #1: VALUE + 2=10 ERRCLITRAIL-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/pattern-defines.txt b/llvm/test/FileCheck/string-defines.txt rename from llvm/test/FileCheck/pattern-defines.txt rename to llvm/test/FileCheck/string-defines.txt --- a/llvm/test/FileCheck/pattern-defines.txt +++ b/llvm/test/FileCheck/string-defines.txt @@ -1,7 +1,7 @@ -; Test functionality of -D option: pattern variables are defined to the right +; Test functionality of -D option: string variables are defined to the right ; value and CHECK directives using them match as expected given the value set. -; Pattern variable correctly defined to a non-empty string. +; String variable correctly defined to a non-empty string. RUN: FileCheck -DVALUE=10 --input-file %s %s RUN: not FileCheck -DVALUE=20 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --check-prefix ERRMSG @@ -15,14 +15,14 @@ ERRMSG: defines.txt:[[@LINE-3]]:8: error: CHECK: expected string not found in input ERRMSG: defines.txt:1:1: note: scanning from here -ERRMSG: defines.txt:1:1: note: with variable "VALUE" equal to "20" +ERRMSG: defines.txt:1:1: note: with "VALUE" equal to "20" ERRMSG: defines.txt:[[@LINE-7]]:1: note: possible intended match here NOT-ERRMSG: defines.txt:[[@LINE-7]]:10: error: {{NOT}}-NOT: excluded string found in input NOT-ERRMSG: defines.txt:[[@LINE-10]]:1: note: found here -NOT-ERRMSG: defines.txt:[[@LINE-11]]:1: note: with variable "VALUE" equal to "10" +NOT-ERRMSG: defines.txt:[[@LINE-11]]:1: note: with "VALUE" equal to "10" -; Definition of pattern variable to an empty string. +; Definition of string variable to an empty string. RUN: FileCheck -DVALUE= --check-prefix EMPTY --input-file %s %s 2>&1 Empty value = @@ diff --git a/llvm/test/FileCheck/verbose.txt b/llvm/test/FileCheck/verbose.txt --- a/llvm/test/FileCheck/verbose.txt +++ b/llvm/test/FileCheck/verbose.txt @@ -41,7 +41,7 @@ V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here V-NEXT: {{^}}NUMVAR:42{{$}} V-NEXT: {{^}}^~~~~~~~~{{$}} -V-NEXT: verbose.txt:[[#@LINE-12]]:1: note: with numeric expression "NUMVAR" equal to "42" +V-NEXT: verbose.txt:[[#@LINE-12]]:1: note: with "NUMVAR" equal to "42" V-NEXT: {{^}}NUMVAR:42{{$}} V-NEXT: {{^}}^~~~~~~~~{{$}} @@ -51,7 +51,7 @@ V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here V-NEXT: {{^}}NUMVAR - 1:41{{$}} V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} -V-NEXT: verbose.txt:[[#@LINE-21]]:1: note: with numeric expression "NUMVAR - 1" equal to "41" +V-NEXT: verbose.txt:[[#@LINE-21]]:1: note: with "NUMVAR - 1" equal to "41" V-NEXT: {{^}}NUMVAR - 1:41{{$}} V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -171,7 +171,7 @@ StringRef NameTrailerRef = bufferize(SM, NameTrailer); StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size()); StringRef TrailerRef = NameTrailerRef.substr(VarName.size()); - return P.parseNumericExpression(VarNameRef, IsPseudo, TrailerRef, SM) == + return P.parseNumericSubstitution(VarNameRef, IsPseudo, TrailerRef, SM) == nullptr; } }; @@ -228,10 +228,10 @@ GlobalDefines.emplace_back(std::string("FOO=BAR")); Context.defineCmdlineVariables(GlobalDefines, SM); - // Substitution of undefined pattern variable fails. - FileCheckPatternSubstitution PatternSubstitution = - FileCheckPatternSubstitution(&Context, "VAR404", 42); - EXPECT_FALSE(PatternSubstitution.getResult()); + // Substitution of undefined string variable fails. + FileCheckStringSubstitution StringSubstitution = + FileCheckStringSubstitution(&Context, "VAR404", 42); + EXPECT_FALSE(StringSubstitution.getResult()); // Substitutions of defined pseudo and non-pseudo numeric variables return // the right value. @@ -239,10 +239,10 @@ FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10); FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0); FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3); - FileCheckPatternSubstitution SubstitutionLine = - FileCheckPatternSubstitution(&Context, "@LINE", &NumExprLine, 12); - FileCheckPatternSubstitution SubstitutionN = - FileCheckPatternSubstitution(&Context, "N", &NumExprN, 30); + FileCheckNumericSubstitution SubstitutionLine = + FileCheckNumericSubstitution(&Context, "@LINE", &NumExprLine, 12); + FileCheckNumericSubstitution SubstitutionN = + FileCheckNumericSubstitution(&Context, "N", &NumExprN, 30); llvm::Optional Value = SubstitutionLine.getResult(); EXPECT_TRUE(Value); EXPECT_EQ("42", *Value); @@ -256,10 +256,10 @@ NVar.clearValue(); EXPECT_FALSE(SubstitutionN.getResult()); - // Substitution of defined pattern variable returns the right value. + // Substitution of a defined string variable returns the right value. FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context); - PatternSubstitution = FileCheckPatternSubstitution(&Context, "FOO", 42); - Value = PatternSubstitution.getResult(); + StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); + Value = StringSubstitution.getResult(); EXPECT_TRUE(Value); EXPECT_EQ("BAR", *Value); } @@ -271,31 +271,32 @@ GlobalDefines.emplace_back(std::string("FOO=BAR")); Context.defineCmdlineVariables(GlobalDefines, SM); - // getUndefVarName() on a pattern variable substitution with an undefined - // variable returns that variable. - FileCheckPatternSubstitution Substitution = - FileCheckPatternSubstitution(&Context, "VAR404", 42); - StringRef UndefVar = Substitution.getUndefVarName(); + // getUndefVarName() on a string substitution with an undefined variable + // returns that variable. + FileCheckStringSubstitution StringSubstitution = + FileCheckStringSubstitution(&Context, "VAR404", 42); + StringRef UndefVar = StringSubstitution.getUndefVarName(); EXPECT_EQ("VAR404", UndefVar); - // getUndefVarName() on a pattern variable substitution with a defined - // variable returns an empty string. - Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42); - UndefVar = Substitution.getUndefVarName(); + // getUndefVarName() on a string substitution with a defined variable returns + // an empty string. + StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); + UndefVar = StringSubstitution.getUndefVarName(); EXPECT_EQ("", UndefVar); - // getUndefVarName() on a numeric expression substitution with a defined - // variable returns an empty string. + // getUndefVarName() on a numeric substitution with a defined variable + // returns an empty string. FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42); FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0); - Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12); - UndefVar = Substitution.getUndefVarName(); + FileCheckNumericSubstitution NumericSubstitution = + FileCheckNumericSubstitution(&Context, "@LINE", &NumExpr, 12); + UndefVar = NumericSubstitution.getUndefVarName(); EXPECT_EQ("", UndefVar); - // getUndefVarName() on a numeric expression substitution with an undefined - // variable returns that variable. + // getUndefVarName() on a numeric substitution with an undefined variable + // returns that variable. LineVar.clearValue(); - UndefVar = Substitution.getUndefVarName(); + UndefVar = NumericSubstitution.getUndefVarName(); EXPECT_EQ("@LINE", UndefVar); } @@ -360,7 +361,7 @@ llvm::Optional LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt); FileCheckNumExpr *NumExpr = - P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM); + P.parseNumericSubstitution(LocalNumVarRef, false /*IsPseudo*/, "", SM); llvm::Optional EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); llvm::Optional UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); EXPECT_TRUE(LocalVar); @@ -384,7 +385,7 @@ EXPECT_FALSE(NumExpr->eval()); P = FileCheckPattern(Check::CheckPlain, &Cxt); NumExpr = - P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM); + P.parseNumericSubstitution(LocalNumVarRef, false /*IsPseudo*/, "", SM); EXPECT_FALSE(NumExpr); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_FALSE(EmptyVar); @@ -401,7 +402,7 @@ EXPECT_EQ(*GlobalVar, "BAR"); P = FileCheckPattern(Check::CheckPlain, &Cxt); NumExpr = - P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM); + P.parseNumericSubstitution(GlobalNumVarRef, false /*IsPseudo*/, "", SM); EXPECT_TRUE(NumExpr); NumExprVal = NumExpr->eval(); EXPECT_TRUE(NumExprVal); @@ -413,7 +414,7 @@ EXPECT_TRUE(GlobalVar); P = FileCheckPattern(Check::CheckPlain, &Cxt); NumExpr = - P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM); + P.parseNumericSubstitution(GlobalNumVarRef, false /*IsPseudo*/, "", SM); EXPECT_TRUE(NumExpr); NumExprVal = NumExpr->eval(); EXPECT_TRUE(NumExprVal); diff --git a/llvm/utils/FileCheck/FileCheck.cpp b/llvm/utils/FileCheck/FileCheck.cpp --- a/llvm/utils/FileCheck/FileCheck.cpp +++ b/llvm/utils/FileCheck/FileCheck.cpp @@ -537,8 +537,8 @@ continue; } if (EqIdx == 0) { - errs() << "Missing pattern variable name in command-line definition '-D" - << G << "'\n"; + errs() << "Missing variable name in command-line definition '-D" << G + << "'\n"; GlobalDefineError = true; continue; }