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 @@ -525,14 +525,14 @@ braces explicitly from the input, you can use something ugly like ``{{[{][{]}}`` as your pattern. -FileCheck Pattern Expressions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +FileCheck Pattern Substitutions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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: +:program:`FileCheck` supports pattern substitutions that allow pattern +variables to be defined and substituted into patterns. Here is a simple example: .. code-block:: llvm @@ -565,18 +565,30 @@ 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 Substitutions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -: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 allows defining numeric variables and checking for +numeric values that satisfy a numeric expression constraint based on those +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 define a numeric variable is ``[[#:]]`` where +```` is the name of the numeric variable to define to the matching +value. + +For example: + +.. code-block:: llvm + + ; CHECK: mov r[[#REG:]], 42 + +would match ``mov r5, 42`` and set ``REG`` to the value ``5``. The syntax to check a numeric expression constraint is ``[[#]]`` where: -* ```` is the name of a numeric variable defined on the command line. +* ```` is the name of a defined numeric variable. * ```` is an optional numeric operation to perform on the value of ````. Currently supported numeric operations are ``+`` and ``-``. @@ -589,25 +601,31 @@ .. code-block:: llvm - ; CHECK: add r[[#REG]], r[[#REG]], r[[#REG+1]] + ; CHECK: load r[[#REG:]], [r0] + ; CHECK: load r[[#REG+1]], [r1] -The above example would match the line: +The above example would match the text: .. code-block:: text - add r5, r5, r6 + load r5, [r0] + load r6, [r1] -but would not match the line: +but would not match the text: .. code-block:: text - add r5, r5, r7 + load r5, [r0] + load r7, [r1] due to ``7`` being unequal to ``5 + 1``. The ``--enable-var-scope`` option has the same effect on numeric variables as on pattern variables. +Important note: In its current implementation, a numeric expression cannot use +a numeric variable defined on the same line. + FileCheck Pseudo Numeric Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -617,9 +635,9 @@ line numbers in the same file, which have to be updated whenever line numbers change due to text addition or deletion. -To support this case, FileCheck understands the ``@LINE`` pseudo numeric -variable which evaluates to the line number of the CHECK pattern where it is -found. +To support this case, FileCheck numeric expressions understand the ``@LINE`` +pseudo numeric variable which evaluates to the line number of the CHECK pattern +where it is found. This way match patterns can be put near the relevant test lines and include relative line number references, for example: 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,11 +37,12 @@ }; //===----------------------------------------------------------------------===// -// Numeric expression handling code. +// Numeric substitution handling code. //===----------------------------------------------------------------------===// /// Class representing a numeric variable with a given value in a numeric -/// expression. +/// expression. Each definition of a variable gets its own instance of this +/// class. Variable uses share the same instance as the respective definition. class FileCheckNumericVariable { private: /// Name of the numeric variable. @@ -50,7 +51,15 @@ /// Value of numeric variable, if defined, or None otherwise. llvm::Optional Value; + /// Line number where this variable is defined. Used to determine whether a + /// variable is defined on the same line as a given use. + size_t DefLineNumber; + public: + /// Constructor for a variable \p Name defined at line \p DefLineNumber. + FileCheckNumericVariable(size_t DefLineNumber, StringRef Name) + : Name(Name), Value(llvm::None), DefLineNumber(DefLineNumber) {} + /// Constructor for numeric variable \p Name with a known \p Value at parse /// time (e.g. the @LINE numeric variable). FileCheckNumericVariable(StringRef Name, uint64_t Value) @@ -69,6 +78,9 @@ /// Clears value of this numeric variable. \returns whether the variable was /// already undefined. bool clearValue(); + + /// \returns the line number where this variable is defined. + unsigned getDefLineNumber() { return DefLineNumber; } }; /// Type of functions evaluating a given binary operation. @@ -114,7 +126,7 @@ /// FileCheckNumExpr class instance pointed to by NumExpr). FileCheckPatternContext *Context; - /// Whether this represents a numeric expression substitution. + /// Whether this represents a numeric substitution. bool IsNumExpr; /// The string that needs to be substituted for something else. For a @@ -122,27 +134,27 @@ /// expression. StringRef FromStr; - /// If this is a numeric expression substitution, this is the pointer to the - /// class representing that numeric expression. + /// If this is a numeric substitution, this is the pointer to the class + /// representing the numeric expression whose value is to be substituted. FileCheckNumExpr *NumExpr = nullptr; // Index in RegExStr of where to do the substitution. size_t InsertIdx; public: - /// Constructor for a pattern variable substitution. + /// Constructor for a pattern substitution. FileCheckPatternSubstitution(FileCheckPatternContext *Context, StringRef VarName, size_t InsertIdx) : Context(Context), IsNumExpr(false), FromStr(VarName), InsertIdx(InsertIdx) {} - /// Constructor for a numeric expression substitution. + /// Constructor for a numeric substitution. FileCheckPatternSubstitution(FileCheckPatternContext *Context, StringRef Expr, FileCheckNumExpr *NumExpr, size_t InsertIdx) : Context(Context), IsNumExpr(true), FromStr(Expr), NumExpr(NumExpr), InsertIdx(InsertIdx) {} - /// \returns whether this is a numeric expression substitution. + /// \returns whether this is a numeric substitution. bool isNumExpr() const { return IsNumExpr; } /// \returns the string to be substituted. @@ -209,6 +221,20 @@ struct FileCheckDiag; +/// Structure representing the definition of a numeric variable in a pattern. +/// It holds the pointer to the class representing the numeric variable whose +/// value is being defined and the parenthesized subexpression number in +/// RegExStr to capture that value. +struct FileCheckNumExprMatch { + /// Pointer to class representing the numeric variable whose value is being + /// defined. + FileCheckNumericVariable *DefinedNumericVariable; + + /// Parenthesized subexpression number in RegExStr that captures the value + /// of this numeric variable definition. + unsigned CaptureParen; +}; + /// Class holding the FileCheckPattern global state, shared by all patterns: /// tables holding values of variables and whether they are defined or not at /// any given time in the matching process. @@ -229,8 +255,11 @@ /// When matching a given pattern, this holds the pointers to the classes /// representing the last definitions of numeric variables defined in - /// previous patterns. Earlier definition of the variables, if any, have - /// their own class instance not referenced by this table. + /// previous patterns. Earlier definitions of the variables, if any, have + /// their own class instance not referenced by this table. When matching a + /// pattern all definitions for that pattern are recorded in + /// NumericVariableDefs table in the FileCheckPattern instance of that + /// pattern. StringMap GlobalNumericVariableTable; /// Vector holding pointers to all parsed numeric expressions. Used to @@ -267,7 +296,8 @@ /// Makes a new numeric variable and registers it for destruction when the /// context is destroyed. - FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value); + template + FileCheckNumericVariable *makeNumericVariable(Types... args); }; class FileCheckPattern { @@ -292,7 +322,7 @@ std::vector Substitutions; /// Maps names of pattern variables defined in a pattern to the parenthesized - /// capture numbers of their last definition. + /// subexpression numbers in RegExStr of their last definition. /// /// E.g. for the pattern "foo[[bar:.*]]baz[[bar]]quux[[bar:.*]]", /// VariableDefs will map "bar" to 2 corresponding to the second definition @@ -302,6 +332,11 @@ /// iterating over values. std::map VariableDefs; + /// Holds the capture parentheses number and pointer to the corresponding + /// FileCheckNumericVariable class instance of all numeric variable + /// definitions. Used to set the matched value of all those variables. + StringMap NumericVariableDefs; + /// Pointer to a class instance holding the global state shared by all /// patterns: /// - separate tables with the values of live pattern and numeric variables @@ -312,13 +347,14 @@ Check::FileCheckType CheckTy; - /// Contains the number of line this pattern is in. + /// Line number for this CHECK pattern. Used to determine whether a variable + /// definition is made on an earlier line to the one with this CHECK. unsigned LineNumber; public: explicit FileCheckPattern(Check::FileCheckType Ty, - FileCheckPatternContext *Context) - : Context(Context), CheckTy(Ty) {} + FileCheckPatternContext *Context, unsigned Line) + : Context(Context), CheckTy(Ty), LineNumber(Line) {} /// \returns the location in source code. SMLoc getLoc() const { return PatternLoc; } @@ -335,25 +371,31 @@ /// 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) - /// 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; + /// Parses \p Expr for use or definition (if \p IsDefinition is true) of a + /// numeric variable. \returns whether parsing fails in which case errors are + /// reported on \p SM. Otherwise, sets \p Name to the name of the parsed + /// numeric variable. + bool parseNumericVariable(StringRef &Expr, StringRef &Name, bool IsDefinition, + const SourceMgr &SM) const; + /// Parses \p Expr for a numeric substitution. \returns the class + /// representing the AST of the numeric expression whose value must be + /// substituted, or nullptr if parsing fails, in which case errors are + /// reported on \p SM. Sets \p DefinedNumericVariable to point to the class + /// representing the numeric variable defined in this numeric substitution, + /// or nullptr if this substitution does not define any variable. + FileCheckNumExpr * + parseNumericSubstitution(StringRef Expr, + FileCheckNumericVariable *&DefinedNumericVariable, + const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. /// /// \p Prefix provides which prefix is being matched, \p Req describes the /// global options that influence the parsing such as whitespace - /// canonicalization, \p SM provides the SourceMgr used for error reports, - /// and \p LineNumber is the line number in the input file from which the - /// pattern string was read. \returns true in case of an error, false - /// otherwise. - bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, - unsigned LineNumber, const FileCheckRequest &Req); + /// canonicalization, \p SM provides the SourceMgr used for error reports. + /// \returns true in case of an error, false otherwise. + bool parsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, + const FileCheckRequest &Req); /// Matches the pattern string against the input buffer \p Buffer /// /// \returns the position that is matched or npos if there is no match. If @@ -362,8 +404,11 @@ /// /// The GlobalVariableTable StringMap in the FileCheckPatternContext class /// instance provides the current values of FileCheck pattern variables and - /// is updated if this match defines new values. - size_t match(StringRef Buffer, size_t &MatchLen) const; + /// is updated if this match defines new values. Likewise, the + /// GlobalNumericVariableTable StringMap in the same class provides the + /// current values of FileCheck numeric variables and is updated if this + /// match defines new numeric values. + size_t match(StringRef Buffer, size_t &MatchLen, const SourceMgr &SM) const; /// Prints the value of successful substitutions or the name of the undefined /// pattern or numeric variable preventing such a successful substitution. void printSubstitutions(const SourceMgr &SM, StringRef Buffer, @@ -393,6 +438,12 @@ /// \returns the offset of the closing sequence within Str, or npos if it /// was not found. size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); + + /// Parses \p Expr for a binary operation. + /// \returns the class representing the binary operation of the numeric + /// expression, or nullptr if parsing fails, in which case errors are + /// reported on \p SM. + FileCheckNumExpr *parseBinop(StringRef &Expr, const SourceMgr &SM) const; }; //===----------------------------------------------------------------------===// 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 @@ -39,11 +39,12 @@ } llvm::Optional FileCheckNumExpr::eval() const { - llvm::Optional LeftOp = this->LeftOp->getValue(); + assert(LeftOp && "Evaluating an empty numeric expression"); + llvm::Optional LeftOpValue = LeftOp->getValue(); // Variable is undefined. - if (!LeftOp) + if (!LeftOpValue) return llvm::None; - return EvalBinop(*LeftOp, RightOp); + return EvalBinop(*LeftOpValue, RightOp); } StringRef FileCheckNumExpr::getUndefVarName() const { @@ -121,24 +122,46 @@ return C; } -static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp + RightOp; -} -static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { - return LeftOp - RightOp; -} +bool FileCheckPattern::parseNumericVariable(StringRef &Expr, StringRef &Name, + bool IsDefinition, + const SourceMgr &SM) const { + bool IsPseudo; + unsigned TrailIdx; + + if (parseVariable(Expr, IsPseudo, TrailIdx)) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid variable name"); + return true; + } + Name = Expr.substr(0, TrailIdx); + Expr = Expr.substr(TrailIdx); -FileCheckNumExpr * -FileCheckPattern::parseNumericExpression(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 + "'"); - return nullptr; + return true; + } + if (IsDefinition && IsPseudo) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "definition of pseudo variable '" + Name + "' unsupported"); + return true; + } + + if (IsDefinition) { + // Detect collisions between pattern and numeric variable when the latter + // is created later than the former. + if (Context->DefinedVariableTable.find(Name) != + Context->DefinedVariableTable.end()) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "pattern variable with name '" + Name + + "' already exists"); + return true; + } + + return false; } - // This method is indirectly called from ParsePattern for all numeric + // This method is indirectly called from parsePattern for all numeric // variable definitions and uses in the order in which they appear in the // CHECK pattern. For each definition, the pointer to the class instance of // the corresponding numeric variable definition is stored in @@ -148,19 +171,45 @@ if (VarTableIter == Context->GlobalNumericVariableTable.end()) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, "using undefined numeric variable '" + Name + "'"); - return nullptr; + return true; } - FileCheckNumericVariable *LeftOp = VarTableIter->second; + FileCheckNumericVariable *NumericVariable = VarTableIter->second; + if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "numeric variable '" + NumericVariable->getName() + + "' defined on the same line"); + return true; + } + + return false; +} + +static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { + return LeftOp + RightOp; +} + +static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { + return LeftOp - RightOp; +} + +FileCheckNumExpr *FileCheckPattern::parseBinop(StringRef &Expr, + const SourceMgr &SM) const { + StringRef Name; + if (parseNumericVariable(Expr, Name, false, SM)) { + // Error reporting done in parseNumericVariable. + return nullptr; + } + FileCheckNumericVariable *LeftOp = + Context->GlobalNumericVariableTable.find(Name)->second; // Check if this is a supported operation and select a function to perform // it. - Trailer = Trailer.ltrim(SpaceChars); - if (Trailer.empty()) { + Expr = Expr.ltrim(SpaceChars); + if (Expr.empty()) return Context->makeNumExpr(add, LeftOp, 0); - } - SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data()); - char Operator = popFront(Trailer); + SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); + char Operator = popFront(Expr); binop_eval_t EvalBinop; switch (Operator) { case '+': @@ -177,35 +226,76 @@ } // Parse right operand. - Trailer = Trailer.ltrim(SpaceChars); - if (Trailer.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, + Expr = Expr.ltrim(SpaceChars); + if (Expr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, "missing operand in numeric expression"); return nullptr; } uint64_t RightOp; - if (Trailer.consumeInteger(10, RightOp)) { - SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, - "invalid offset in numeric expression '" + Trailer + "'"); + if (Expr.consumeInteger(10, RightOp)) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "invalid offset in numeric expression '" + Expr + "'"); return nullptr; } - Trailer = Trailer.ltrim(SpaceChars); - if (!Trailer.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, + Expr = Expr.ltrim(SpaceChars); + if (!Expr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, "unexpected characters at end of numeric expression '" + - Trailer + "'"); + Expr + "'"); return nullptr; } return Context->makeNumExpr(EvalBinop, LeftOp, RightOp); } -bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, - SourceMgr &SM, unsigned LineNumber, +FileCheckNumExpr *FileCheckPattern::parseNumericSubstitution( + StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, + const SourceMgr &SM) const { + + // Parse numeric variable definition. + DefinedNumericVariable = nullptr; + size_t DefEnd = Expr.find(':'); + if (DefEnd != StringRef::npos) { + StringRef DefExpr = Expr.substr(0, DefEnd); + StringRef UseExpr = Expr = Expr.substr(DefEnd + 1); + + DefExpr = DefExpr.ltrim(SpaceChars); + StringRef Name; + if (parseNumericVariable(DefExpr, Name, true /*IsDefinition*/, SM)) + // Invalid variable definition. Error reporting done in parsing function. + return nullptr; + + DefinedNumericVariable = + Context->makeNumericVariable(this->LineNumber, Name); + + DefExpr = DefExpr.ltrim(SpaceChars); + if (!DefExpr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(DefExpr.data()), + SourceMgr::DK_Error, + "invalid numeric variable definition"); + return nullptr; + } + UseExpr = UseExpr.ltrim(SpaceChars); + if (!UseExpr.empty()) { + SM.PrintMessage( + SMLoc::getFromPointer(UseExpr.data()), SourceMgr::DK_Error, + "unexpected string after variable definition: '" + UseExpr + "'"); + return nullptr; + } + return Context->makeNumExpr(add, nullptr, 0); + } + + // Parse numeric expression itself. + Expr = Expr.ltrim(SpaceChars); + return parseBinop(Expr, SM); +} + +bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, + SourceMgr &SM, const FileCheckRequest &Req) { bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot; - this->LineNumber = LineNumber; PatternLoc = SMLoc::getFromPointer(PatternStr.data()); // Create fake @LINE pseudo variable definition. @@ -288,14 +378,14 @@ continue; } - // Pattern and numeric expression matches. Pattern expressions come in two + // Pattern and numeric substitutions. Pattern substitutions 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 - // "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common - // errors. + // regex) and assigns it to the pattern variable 'foo'. The latter + // substitutes foo's value. Numeric substitutions start with a '#' sign + // after the double brackets and also have the definition and substitution + // forms. Both pattern and numeric variable names 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("[[")) { StringRef UnparsedPatternStr = PatternStr.substr(2); // Find the closing bracket pair ending the match. End is going to be an @@ -317,68 +407,84 @@ // of the first unparsed character. PatternStr = UnparsedPatternStr.substr(End + 2); - size_t VarEndIdx = MatchStr.find(":"); - if (IsNumExpr) - MatchStr = MatchStr.ltrim(SpaceChars); - else { + bool IsVarDef = false; + StringRef DefName; + StringRef SubstStr; + StringRef MatchRegexp; + size_t SubstInsertIdx = RegExStr.size(); + + // Parse pattern variable or legacy numeric expression. + if (!IsNumExpr) { + size_t VarEndIdx = MatchStr.find(":"); size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t"); if (SpacePos != StringRef::npos) { SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data() + SpacePos), SourceMgr::DK_Error, "unexpected whitespace"); return true; } - } - // Get the regex name (e.g. "foo") and verify it is well formed. - bool IsPseudo; - unsigned TrailIdx; - if (parseVariable(MatchStr, IsPseudo, TrailIdx)) { - SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), - SourceMgr::DK_Error, "invalid variable name"); - return true; - } - - size_t SubstInsertIdx = RegExStr.size(); - FileCheckNumExpr *NumExpr; - - StringRef Name = MatchStr.substr(0, TrailIdx); - StringRef Trailer = MatchStr.substr(TrailIdx); - bool IsVarDef = (VarEndIdx != StringRef::npos); - - if (IsVarDef) { - if (IsPseudo || !Trailer.consume_front(":")) { + // Get the regex name (e.g. "foo") and verify it is well formed. + bool IsPseudo; + unsigned TrailIdx; + if (parseVariable(MatchStr, IsPseudo, TrailIdx)) { SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), - SourceMgr::DK_Error, - "invalid name in pattern variable definition"); + SourceMgr::DK_Error, "invalid variable name"); return true; } - // Detect collisions between pattern and numeric variables when the - // former is created later than the latter. - if (Context->GlobalNumericVariableTable.find(Name) != - Context->GlobalNumericVariableTable.end()) { - SM.PrintMessage( - SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error, - "numeric variable with name '" + Name + "' already exists"); - return true; - } + StringRef Name = MatchStr.substr(0, TrailIdx); + StringRef Trailer = MatchStr.substr(TrailIdx); + IsVarDef = (VarEndIdx != StringRef::npos); + IsNumExpr = IsPseudo; + + if (IsVarDef) { + if ((IsPseudo || !Trailer.consume_front(":"))) { + SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), + SourceMgr::DK_Error, + "invalid name in pattern variable definition"); + return true; + } + + // Detect collisions between pattern and numeric variable when the + // former is created later than the latter. + if (Context->GlobalNumericVariableTable.find(Name) != + Context->GlobalNumericVariableTable.end()) { + SM.PrintMessage( + SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error, + "numeric variable with name '" + Name + "' already exists"); + return true; + } + DefName = Name; + MatchRegexp = Trailer; + } else + SubstStr = MatchStr; } - if (IsNumExpr || (!IsVarDef && IsPseudo)) { - NumExpr = parseNumericExpression(Name, IsPseudo, Trailer, SM); + // Parse numeric substitution. + FileCheckNumExpr *NumExpr; + FileCheckNumericVariable *DefinedNumericVariable; + if (IsNumExpr) { + NumExpr = + parseNumericSubstitution(MatchStr, DefinedNumericVariable, SM); if (NumExpr == nullptr) return true; - IsNumExpr = true; + if (DefinedNumericVariable) { + IsVarDef = true; + DefName = DefinedNumericVariable->getName(); + MatchRegexp = StringRef("[0-9]+"); + } else + SubstStr = MatchStr; } // Handle variable use: [[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()) { - unsigned CaptureParen = VariableDefs[Name]; + // same line by emitting a backreference. Numeric expressions do not + // support using a numeric variable defined on the same line. + if (!IsNumExpr && VariableDefs.find(SubstStr) != VariableDefs.end()) { + unsigned CaptureParen = VariableDefs[SubstStr]; if (CaptureParen < 1 || CaptureParen > 9) { - SM.PrintMessage(SMLoc::getFromPointer(Name.data()), + SM.PrintMessage(SMLoc::getFromPointer(SubstStr.data()), SourceMgr::DK_Error, "Can't back-reference more than 9 variables"); return true; @@ -388,21 +494,39 @@ // Handle use of pattern variables ([[]]) defined in previous // CHECK pattern or use of a numeric expression. FileCheckPatternSubstitution Substitution = - IsNumExpr ? FileCheckPatternSubstitution(Context, MatchStr, + IsNumExpr ? FileCheckPatternSubstitution(Context, SubstStr, NumExpr, SubstInsertIdx) - : FileCheckPatternSubstitution(Context, MatchStr, + : FileCheckPatternSubstitution(Context, SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } continue; } - // Handle [[foo:.*]]. - VariableDefs[Name] = CurParen; + // Handle variable definition: [[:(...)]] and [[#(...):(...)]]. + if (IsNumExpr) { + struct FileCheckNumExprMatch NumExprDef = {DefinedNumericVariable, + CurParen}; + NumericVariableDefs[DefName] = NumExprDef; + // This store is done here rather than in match() to allow + // parseNumericVariable() to get the pointer to the class instance + // of the right variable definition corresponding to a given numeric + // variable use. + Context->GlobalNumericVariableTable[DefName] = DefinedNumericVariable; + } else { + VariableDefs[DefName] = CurParen; + // Mark pattern variable as defined to detect collisions between + // pattern and numeric variable in parseNumericVariable and + // 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 loose the ability to + // detect the use of an undefined variable in match(). + Context->DefinedVariableTable[DefName] = true; + } RegExStr += '('; ++CurParen; - if (AddRegExToRegEx(Trailer, CurParen, SM)) + if (AddRegExToRegEx(MatchRegexp, CurParen, SM)) return true; RegExStr += ')'; @@ -445,7 +569,8 @@ RegExStr += Backref; } -size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const { +size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen, + const SourceMgr &SM) const { // If this is the EOF pattern, match it immediately. if (CheckTy == Check::CheckEOF) { MatchLen = 0; @@ -502,6 +627,24 @@ MatchInfo[VariableDef.second]; } + // If this defines any numeric variables, remember their values. + for (const auto &NumericVariableDef : NumericVariableDefs) { + assert(NumericVariableDef.getValue().CaptureParen < MatchInfo.size() && + "Internal paren error"); + unsigned CaptureParen = NumericVariableDef.getValue().CaptureParen; + FileCheckNumericVariable *DefinedNumericVariable = + NumericVariableDef.getValue().DefinedNumericVariable; + + StringRef MatchedValue = MatchInfo[CaptureParen]; + uint64_t Val; + if (MatchedValue.getAsInteger(10, Val)) { + SM.PrintMessage(SMLoc::getFromPointer(MatchedValue.data()), + SourceMgr::DK_Error, "Unable to represent numeric value"); + } + if (DefinedNumericVariable->setValue(Val)) + assert(false && "Numeric variable redefined"); + } + // Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after // the required preceding newline, which is consumed by the pattern in the // case of CHECK-EMPTY but not CHECK-NEXT. @@ -649,10 +792,11 @@ return NumExprs.back().get(); } +template FileCheckNumericVariable * -FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) { +FileCheckPatternContext::makeNumericVariable(Types... args) { NumericVariables.push_back( - llvm::make_unique(Name, Value)); + llvm::make_unique(args...)); return NumericVariables.back().get(); } @@ -934,9 +1078,9 @@ SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); ImplicitNegativeChecks.push_back( - FileCheckPattern(Check::CheckNot, &PatternContext)); - ImplicitNegativeChecks.back().ParsePattern(PatternInBuffer, - "IMPLICIT-CHECK", SM, 0, Req); + FileCheckPattern(Check::CheckNot, &PatternContext, 0)); + ImplicitNegativeChecks.back().parsePattern(PatternInBuffer, + "IMPLICIT-CHECK", SM, Req); } std::vector DagNotMatches = ImplicitNegativeChecks; @@ -997,8 +1141,8 @@ SMLoc PatternLoc = SMLoc::getFromPointer(Buffer.data()); // Parse the pattern. - FileCheckPattern P(CheckTy, &PatternContext); - if (P.ParsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, LineNumber, Req)) + FileCheckPattern P(CheckTy, &PatternContext, LineNumber); + if (P.parsePattern(Buffer.substr(0, EOL), UsedPrefix, SM, Req)) return true; // Verify that CHECK-LABEL lines do not define or use variables @@ -1042,7 +1186,7 @@ // prefix as a filler for the error message. if (!DagNotMatches.empty()) { CheckStrings.emplace_back( - FileCheckPattern(Check::CheckEOF, &PatternContext), + FileCheckPattern(Check::CheckEOF, &PatternContext, LineNumber + 1), *Req.CheckPrefixes.begin(), SMLoc::getFromPointer(Buffer.data())); std::swap(DagNotMatches, CheckStrings.back().DagNotStrings); } @@ -1216,7 +1360,7 @@ StringRef MatchBuffer = Buffer.substr(LastMatchEnd); size_t CurrentMatchLen; // get a match at current start point - size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen); + size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen, SM); if (i == 1) FirstMatchPos = LastPos + MatchPos; @@ -1337,7 +1481,7 @@ assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); size_t MatchLen = 0; - size_t Pos = Pat->match(Buffer, MatchLen); + size_t Pos = Pat->match(Buffer, MatchLen, SM); if (Pos == StringRef::npos) { PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, @@ -1397,7 +1541,7 @@ // CHECK-DAG group. for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) { StringRef MatchBuffer = Buffer.substr(MatchPos); - size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen); + size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen, SM); // With a group of CHECK-DAGs, a single mismatching means the match on // that group of CHECK-DAGs fails immediately. if (MatchPosBuf == StringRef::npos) { @@ -1555,13 +1699,17 @@ StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer(); SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc()); + // Dummy pattern to call parseNumericVariable. + FileCheckPattern P(Check::CheckPlain, this, 0); + SmallVector CmdlineDefsDiagVec; CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/, false /*KeepEmpty*/); for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) { unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size(); StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart); - if (CmdlineDef.find('=') == StringRef::npos) { + size_t EqIdx = CmdlineDef.find('='); + if (EqIdx == StringRef::npos) { SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()), SourceMgr::DK_Error, "Missing equal sign in global definition"); @@ -1571,16 +1719,14 @@ // Numeric variable definition. if (CmdlineDef[0] == '#') { - bool IsPseudo; - unsigned TrailIdx; - size_t EqIdx = CmdlineDef.find('='); - StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1); - if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) || - IsPseudo || TrailIdx != CmdlineName.size() || CmdlineName.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()), - SourceMgr::DK_Error, - "invalid name in numeric variable definition '" + - CmdlineName + "'"); + StringRef Expr = CmdlineDef.substr(1, EqIdx - 1); + StringRef CmdlineName; + SMLoc CmdlineNameLoc = SMLoc::getFromPointer(Expr.data()); + if (P.parseNumericVariable(Expr, CmdlineName, true /*IsDefinition*/, + SM) || + !Expr.empty()) { + SM.PrintMessage(CmdlineNameLoc, SourceMgr::DK_Error, + "invalid variable name"); ErrorFound = true; continue; } @@ -1606,10 +1752,12 @@ ErrorFound = true; continue; } - auto DefinedNumericVariable = makeNumericVariable(CmdlineName, Val); + auto DefinedNumericVariable = makeNumericVariable(0, CmdlineName); + DefinedNumericVariable->setValue(Val); // Record this variable definition. - GlobalNumericVariableTable[CmdlineName] = DefinedNumericVariable; + GlobalNumericVariableTable[DefinedNumericVariable->getName()] = + DefinedNumericVariable; } else { // Pattern variable definition. std::pair CmdlineNameVal = CmdlineDef.split('='); @@ -1641,7 +1789,7 @@ // 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(). + // variable in match(). DefinedVariableTable[Name] = true; } } diff --git a/llvm/test/FileCheck/numeric-defines-diagnostics.txt b/llvm/test/FileCheck/numeric-defines-diagnostics.txt --- a/llvm/test/FileCheck/numeric-defines-diagnostics.txt +++ b/llvm/test/FileCheck/numeric-defines-diagnostics.txt @@ -4,7 +4,7 @@ RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT -NUMERRCLIFMT: Global defines:1:20: error: invalid name in numeric variable definition '10VALUE' +NUMERRCLIFMT: Global defines:1:20: error: invalid variable name NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 NUMERRCLIFMT-NEXT: {{^ \^$}} @@ -12,7 +12,7 @@ RUN: not FileCheck -D#@VALUE=10 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIPSEUDO -NUMERRCLIPSEUDO: Global defines:1:20: error: invalid name in numeric variable definition '@VALUE' +NUMERRCLIPSEUDO: Global defines:1:20: error: invalid pseudo numeric variable NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10 NUMERRCLIPSEUDO-NEXT: {{^ \^$}} @@ -20,7 +20,7 @@ RUN: not FileCheck -D#VALUE+2=10 --input-file %s %s 2>&1 \ RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLITRAIL -NUMERRCLITRAIL: Global defines:1:20: error: invalid name in numeric variable definition 'VALUE+2' +NUMERRCLITRAIL: Global defines:1:20: error: invalid variable name NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10 NUMERRCLITRAIL-NEXT: {{^ \^$}} 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 @@ -1,19 +1,34 @@ -RUN: FileCheck -D#VAR1=11 --input-file %s %s +RUN: FileCheck -input-file %s %s ; We use CHECK-NEXT directives to force a match on all lines with digits. -; Numeric expressions using variables defined on the command-line without -; spaces +; Numeric variable definition without spaces. +DEF NO SPC +11 +CHECK-LABEL: DEF NO SPC +CHECK-NEXT: [[#VAR1:]] + +; Numeric variable definition in alternate spacing. +DEF ALT SPC +11 +11 +11 +CHECK-LABEL: DEF ALT SPC +CHECK-NEXT: [[# VAR1a:]] +CHECK-NEXT: [[# VAR1b :]] +CHECK-NEXT: [[# VAR1c : ]] + +; Numeric expressions using variables defined on other lines without spaces. USE NO SPC 11 12 10 -CHECK-LABEL: USE NO SPC +CHECK-LABEL: USE CHECK-NEXT: [[#VAR1]] CHECK-NEXT: [[#VAR1+1]] CHECK-NEXT: [[#VAR1-1]] -; Numeric expressions using variables defined on the command-line in alternate +; Numeric expressions using variables defined on other lines in alternate. ; spacing USE ALT SPC 11 @@ -39,7 +54,7 @@ CHECK-NEXT: [[# VAR1 - 1 ]] ; Numeric expressions using variables defined on the command-line and an -; immediate interpreted as an unsigned value +; immediate interpreted as an unsigned value. ; Note: 9223372036854775819 = 0x8000000000000000 + 11 ; 9223372036854775808 = 0x8000000000000000 USE UNSIGNED IMM @@ -47,7 +62,7 @@ CHECK-LABEL: USE UNSIGNED IMM CHECK-NEXT: [[#VAR1+9223372036854775808]] -; Numeric expression using undefined variable +; Numeric expression using undefined variable. RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \ RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s @@ -59,37 +74,51 @@ UNDEF-USE-MSG-NEXT: {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}} UNDEF-USE-MSG-NEXT: {{^ \^$}} -; Numeric expression with unsupported operator -RUN: not FileCheck -D#VAR1=11 --check-prefixes CHECK,INVAL-OP --input-file %s %s 2>&1 \ -RUN: | FileCheck --strict-whitespace --check-prefix INVAL-OP-MSG %s +; Numeric expression with unsupported operator. +RUN: not FileCheck -D#NUMVAR=10 -check-prefix INVAL-OP -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix INVAL-OP-MSG %s INVALID OPERATOR -VAR1*2: 22 +NUMVAR*2: 22 INVAL-OP-LABEL: INVALID OPERATOR -INVAL-OP-NEXT: VAR1*2: [[#VAR1*2]] -INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: unsupported numeric operation '*' -INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: VAR1*2: {{\[\[#VAR1\*2\]\]}} -INVAL-OP-MSG-NEXT: {{^ \^$}} +INVAL-OP-NEXT: NUMVAR*2: [[#NUMVAR*2]] +INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:35: error: unsupported numeric operation '*' +INVAL-OP-MSG-NEXT: {{I}}NVAL-OP-NEXT: NUMVAR*2: {{\[\[#NUMVAR\*2\]\]}} +INVAL-OP-MSG-NEXT: {{^ \^$}} ; Name conflict between Numeric variable definition and pattern 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 -RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 -DNUMVAR=foobar --check-prefix CONFLICT --input-file %s %s 2>&1 \ -RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-PAT-CONFLICT %s -RUN: not FileCheck -D#VAR1=11 -DPATVAR=foobar -D#PATVAR=42 --check-prefix CONFLICT --input-file %s %s 2>&1 \ -RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-NUM-CONFLICT %s +; definition whether from the command-line or input text. +RUN: not FileCheck -check-prefixes CONFLICT,CONFLICT1,CONFLICT2 -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix INPUT-PAT-CONFLICT %s +RUN: not FileCheck -D#NUMVAR=42 -check-prefixes CONFLICT,CONFLICT2 -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix INPUT-PAT-CONFLICT %s +RUN: not FileCheck -D#NUMVAR=42 -DNUMVAR=foobar -check-prefix CONFLICT -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix CLI-PAT-CONFLICT %s +RUN: not FileCheck -check-prefixes CONFLICT,CONFLICT3,CONFLICT4 -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix INPUT-NUM-CONFLICT %s +RUN: not FileCheck -DPATVAR=foobar -check-prefixes CONFLICT,CONFLICT4 -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix INPUT-NUM-CONFLICT %s +RUN: not FileCheck -DPATVAR=foobar -D#PATVAR=42 -check-prefix CONFLICT -input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace -check-prefix CLI-NUM-CONFLICT %s PATVAR NUMVAR CONFLICT +redef1 42 foobar +redef2 42 CONFLICT-LABEL: PATVAR NUMVAR CONFLICT -CONFLICT1-NEXT: [[NUMVAR:foo.*]] -CLI-INPUT-PAT-CONFLICT: numeric-expression.txt:[[#@LINE-1]]:19: error: numeric variable with name 'NUMVAR' already exists -CLI-INPUT-PAT-CONFLICT-NEXT: {{C}}ONFLICT1-NEXT: {{\[\[NUMVAR:foo\.\*\]\]}} -CLI-INPUT-PAT-CONFLICT-NEXT: {{^ \^$}} -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-NEXT: Global define #3: #PATVAR=42 -CLI-CLI-NUM-CONFLICT-NEXT: {{^ \^$}} +CONFLICT1-NEXT: redef1 [[#NUMVAR:]] +CONFLICT2: [[NUMVAR:foo.*]] +CONFLICT3: [[PATVAR:foo.*]] +CONFLICT4: redef2 [[#PATVAR:]] +INPUT-PAT-CONFLICT: numeric-expression.txt:[[#@LINE-3]]:14: error: numeric variable with name 'NUMVAR' already exists +INPUT-PAT-CONFLICT-NEXT: {{C}}ONFLICT2: {{\[\[NUMVAR:foo\.\*\]\]}} +INPUT-PAT-CONFLICT-NEXT: {{^ \^$}} +CLI-PAT-CONFLICT: Global defines:2:19: error: numeric variable with name 'NUMVAR' already exists +CLI-PAT-CONFLICT-NEXT: Global define #2: NUMVAR=foobar +CLI-PAT-CONFLICT-NEXT: {{^ \^$}} +INPUT-NUM-CONFLICT: numeric-expression.txt:[[#@LINE-7]]:22: error: pattern variable with name 'PATVAR' already exists +INPUT-NUM-CONFLICT-NEXT: CONFLICT4: redef2 {{\[\[#PATVAR:\]\]}} +INPUT-NUM-CONFLICT-NEXT: {{^ \^$}} +CLI-NUM-CONFLICT: Global defines:2:20: error: pattern variable with name 'PATVAR' already exists +CLI-NUM-CONFLICT-NEXT: Global define #2: #PATVAR=42 +CLI-NUM-CONFLICT-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/var-scope.txt b/llvm/test/FileCheck/var-scope.txt --- a/llvm/test/FileCheck/var-scope.txt +++ b/llvm/test/FileCheck/var-scope.txt @@ -1,19 +1,22 @@ ; Test that variables not starting with dollar sign get undefined after a ; CHECK-LABEL directive iff --enable-var-scope is used. -RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s -RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s -RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \ +; Reference run: variable are remain defined at all time when not using +; --enable-var-scope option. +RUN: FileCheck --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s + +RUN: FileCheck --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s +RUN: not FileCheck --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \ RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL %s -RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \ +RUN: not FileCheck --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \ RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCNUM %s -RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \ +RUN: not FileCheck --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \ RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL,ERRUNDEFLOCNUM %s local1 global1 -CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM]] -CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM]] +CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM:]] +CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM:]] local2 global2 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 @@ -1,6 +1,6 @@ -; RUN: FileCheck -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s -; RUN: FileCheck -v -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix V %s -; RUN: FileCheck -vv -D#NUMVAR=42 -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s +; RUN: FileCheck -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s +; RUN: FileCheck -v -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix V %s +; RUN: FileCheck -vv -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefixes V,VV %s foo bar @@ -29,36 +29,33 @@ VV-NEXT: {{^}}bar{{$}} VV-NEXT: {{^}}^{{$}} -NUMVAR:42 +NUMVAR=42 NUMVAR - 1:41 -CHECK: NUMVAR:[[#NUMVAR]] +CHECK: NUMVAR=[[#NUMVAR:]] CHECK-NOT: [[#NUMVAR + 1]] CHECK-NEXT: NUMVAR - 1:[[#NUMVAR - 1]] V: verbose.txt:[[#@LINE-4]]:8: remark: {{C}}HECK: expected string found in input -V-NEXT: {{C}}HECK: {{NUMVAR:[[][[]#NUMVAR[]][]]$}} +V-NEXT: {{C}}HECK: {{NUMVAR=[[][[]#NUMVAR:[]][]]$}} V-NEXT: {{^ \^$}} 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: {{^}}NUMVAR:42{{$}} +V-NEXT: {{^}}NUMVAR=42{{$}} V-NEXT: {{^}}^~~~~~~~~{{$}} -V-NEXT: verbose.txt:[[#@LINE-12]]:13: remark: {{C}}HECK-NEXT: expected string found in input +V-NEXT: verbose.txt:[[#@LINE-9]]:13: remark: {{C}}HECK-NEXT: expected string found in input V-NEXT: {{C}}HECK-NEXT: {{NUMVAR - 1:[[][[]#NUMVAR - 1[]][]]$}} V-NEXT: {{^ \^$}} -V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here +V-NEXT: verbose.txt:[[#@LINE-15]]: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-18]]:1: note: with numeric expression "NUMVAR - 1" equal to "41" V-NEXT: {{^}}NUMVAR - 1:41{{$}} V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} -VV-NEXT: verbose.txt:[[#@LINE-23]]:12: remark: {{C}}HECK-NOT: excluded string not found in input +VV-NEXT: verbose.txt:[[#@LINE-20]]:12: remark: {{C}}HECK-NOT: excluded string not found in input VV-NEXT: {{C}}HECK-NOT: {{[[][[]#NUMVAR [+] 1[]][]]$}} -VV-NEXT: {{^ \^$}} -VV-NEXT: verbose.txt:[[#@LINE-28]]:1: note: scanning from here +VV-NEXT: {{^ \^$}} +VV-NEXT: verbose.txt:[[#@LINE-25]]:1: note: scanning from here VV-NEXT: {{^}}NUMVAR - 1:41{{$}} VV-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 @@ -15,12 +15,16 @@ class FileCheckTest : public ::testing::Test {}; TEST_F(FileCheckTest, NumericVariable) { - FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42); + // Undefined variable: getValue and clearValue fails, setValue works. + FileCheckNumericVariable FooVar = FileCheckNumericVariable(1, "FOO"); EXPECT_EQ("FOO", FooVar.getName()); - - // Defined variable: getValue returns a value, setValue fails and value - // remains unchanged. llvm::Optional Value = FooVar.getValue(); + EXPECT_FALSE(Value); + EXPECT_TRUE(FooVar.clearValue()); + EXPECT_FALSE(FooVar.setValue(42)); + + // Defined variable: getValue returns value set, setValue fails. + Value = FooVar.getValue(); EXPECT_TRUE(Value); EXPECT_EQ(42U, *Value); EXPECT_TRUE(FooVar.setValue(43)); @@ -33,12 +37,6 @@ Value = FooVar.getValue(); EXPECT_FALSE(Value); EXPECT_TRUE(FooVar.clearValue()); - - // Undefined variable: setValue works, getValue returns value set. - EXPECT_FALSE(FooVar.setValue(43)); - Value = FooVar.getValue(); - EXPECT_TRUE(Value); - EXPECT_EQ(43U, *Value); } uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } @@ -149,76 +147,187 @@ return StrBufferRef; } -class ExprTester { +class PatternTester { private: + unsigned LineNumber = 1; SourceMgr SM; FileCheckRequest Req; FileCheckPatternContext Context; - FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context); + FileCheckPattern P = + FileCheckPattern(Check::CheckPlain, &Context, LineNumber++); public: - ExprTester() { + PatternTester() { std::vector GlobalDefines; GlobalDefines.emplace_back(std::string("#FOO=42")); + GlobalDefines.emplace_back(std::string("BAR=BAZ")); Context.defineCmdlineVariables(GlobalDefines, SM); - // Call ParsePattern to have @LINE defined. - P.ParsePattern("N/A", "CHECK", SM, 1, Req); + // Call parsePattern to have @LINE defined. + P.parsePattern("N/A", "CHECK", SM, Req); + // parsePattern does not expect to be called twice for the same line and + // will set FixedStr and RegExStr incorrectly if it is. Therefore prepare + // a pattern for a different line. + initNextPattern(); + } + + void initNextPattern() { + P = FileCheckPattern(Check::CheckPlain, &Context, LineNumber++); + } + +public: + bool parseNumVarExpect(StringRef Expr, bool isDefinition) { + StringRef ExprBufferRef = bufferize(SM, Expr); + StringRef Name; + return P.parseNumericVariable(ExprBufferRef, Name, isDefinition, SM); } - bool parseExpect(std::string &VarName, std::string &Trailer) { - bool IsPseudo = VarName[0] == '@'; - std::string NameTrailer = VarName + Trailer; - 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) == - nullptr; + bool parseSubstExpect(StringRef Expr) { + StringRef ExprBufferRef = bufferize(SM, Expr); + FileCheckNumericVariable *DefinedNumericVariable; + return P.parseNumericSubstitution(ExprBufferRef, DefinedNumericVariable, + SM) == nullptr; + } + + bool parsePatternExpect(StringRef Pattern) { + StringRef PatBufferRef = bufferize(SM, Pattern); + return P.parsePattern(PatBufferRef, "CHECK", SM, Req); + } + + bool matchExpect(StringRef Buffer) { + StringRef BufferRef = bufferize(SM, Buffer); + size_t MatchLen; + return P.match(BufferRef, MatchLen, SM); } }; -TEST_F(FileCheckTest, ParseExpr) { - ExprTester Tester; +TEST_F(FileCheckTest, ParseNumericVariable) { + PatternTester Tester; - // @LINE with offset. - std::string VarName = "@LINE"; - std::string Trailer = "+3"; - EXPECT_FALSE(Tester.parseExpect(VarName, Trailer)); + // Invalid variable name. + EXPECT_TRUE(Tester.parseNumVarExpect("42INVALID", false /*isDefinition*/)); - // @LINE only. - Trailer = ""; - EXPECT_FALSE(Tester.parseExpect(VarName, Trailer)); + // Invalid pseudo variable. + EXPECT_TRUE(Tester.parseNumVarExpect("@FOO", false /*isDefinition*/)); - // Defined variable. - VarName = "FOO"; - EXPECT_FALSE(Tester.parseExpect(VarName, Trailer)); + // Valid pseudo variable. + EXPECT_FALSE(Tester.parseNumVarExpect("@LINE", false /*isDefinition*/)); + + // Invalid definition of pseudo. + EXPECT_TRUE(Tester.parseNumVarExpect("@LINE", true /*isDefinition*/)); + + // Conflict with pattern variable. + EXPECT_TRUE(Tester.parseNumVarExpect("BAR", true /*isDefinition*/)); // Undefined variable. - VarName = "UNDEF"; - EXPECT_TRUE(Tester.parseExpect(VarName, Trailer)); + EXPECT_TRUE(Tester.parseNumVarExpect("UNDEF", false /*isDefinition*/)); + + // Use variable defined on same line. + EXPECT_FALSE(Tester.parseSubstExpect("LINE1VAR:")); + EXPECT_TRUE(Tester.parseNumVarExpect("LINE1VAR", false /*isDefinition*/)); + + // Defined variable. + EXPECT_FALSE(Tester.parseNumVarExpect("FOO", false /*isDefinition*/)); +} - // Wrong Pseudovar. - VarName = "@FOO"; - EXPECT_TRUE(Tester.parseExpect(VarName, Trailer)); +TEST_F(FileCheckTest, ParseExpr) { + PatternTester Tester; + + // Variable definition. + + // Definition of invalid variable. + EXPECT_TRUE(Tester.parseSubstExpect("10VAR:")); + EXPECT_TRUE(Tester.parseSubstExpect("@FOO:")); + EXPECT_TRUE(Tester.parseSubstExpect("@LINE:")); + + // Garbage after name of variable being defined. + EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:")); + + // Variable defined to numeric expression. + EXPECT_TRUE(Tester.parseSubstExpect("VAR1: FOO")); + + // Acceptable variable definition. + EXPECT_FALSE(Tester.parseSubstExpect("VAR1:")); + EXPECT_FALSE(Tester.parseSubstExpect(" VAR2:")); + EXPECT_FALSE(Tester.parseSubstExpect("VAR3 :")); + EXPECT_FALSE(Tester.parseSubstExpect("VAR3: ")); + + // Numeric expression. + + // Unacceptable variable. + EXPECT_TRUE(Tester.parseSubstExpect("10VAR")); + EXPECT_TRUE(Tester.parseSubstExpect("@FOO")); + EXPECT_TRUE(Tester.parseSubstExpect("UNDEF")); + + // Only valid variable. + EXPECT_FALSE(Tester.parseSubstExpect("@LINE")); + EXPECT_FALSE(Tester.parseSubstExpect("FOO")); // Unsupported operator. - VarName = "@LINE"; - Trailer = "/2"; - EXPECT_TRUE(Tester.parseExpect(VarName, Trailer)); + EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2")); // Missing offset operand. - VarName = "@LINE"; - Trailer = "+"; - EXPECT_TRUE(Tester.parseExpect(VarName, Trailer)); + EXPECT_TRUE(Tester.parseSubstExpect("@LINE+")); // Cannot parse offset operand. - VarName = "@LINE"; - Trailer = "+x"; - EXPECT_TRUE(Tester.parseExpect(VarName, Trailer)); + EXPECT_TRUE(Tester.parseSubstExpect("@LINE+x")); // Unexpected string at end of numeric expression. - VarName = "@LINE"; - Trailer = "+5x"; - EXPECT_TRUE(Tester.parseExpect(VarName, Trailer)); + EXPECT_TRUE(Tester.parseSubstExpect("@LINE+5x")); + + // Valid expression. + EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5")); + EXPECT_FALSE(Tester.parseSubstExpect("FOO+4")); +} + +TEST_F(FileCheckTest, ParsePattern) { + PatternTester Tester; + + // Space in pattern variable expression. + EXPECT_TRUE(Tester.parsePatternExpect("[[ BAR]]")); + + // Invalid variable name. + EXPECT_TRUE(Tester.parsePatternExpect("[[42INVALID]]")); + + // Invalid pattern variable definition. + EXPECT_TRUE(Tester.parsePatternExpect("[[@PAT:]]")); + EXPECT_TRUE(Tester.parsePatternExpect("[[PAT+2:]]")); + + // Collision with numeric variable. + EXPECT_TRUE(Tester.parsePatternExpect("[[FOO:]]")); + + // Valid use of pattern variable. + EXPECT_FALSE(Tester.parsePatternExpect("[[BAR]]")); + + // Valid pattern variable definition. + EXPECT_FALSE(Tester.parsePatternExpect("[[PAT:[0-9]+]]")); + + // Invalid numeric expressions. + EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]")); + EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]")); + EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]")); + EXPECT_TRUE(Tester.parsePatternExpect("[[#2+@LINE]]")); + EXPECT_TRUE(Tester.parsePatternExpect("[[#YUP:@LINE]]")); + + // Valid numeric expressions and numeric variable definition. + EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE+2]]")); + EXPECT_FALSE(Tester.parsePatternExpect("[[#NUMVAR:]]")); +} + +TEST_F(FileCheckTest, Match) { + PatternTester Tester; + + // Check matching a definition only matches a number. + Tester.parsePatternExpect("[[#NUMVAR:]]"); + EXPECT_TRUE(Tester.matchExpect("FAIL")); + EXPECT_FALSE(Tester.matchExpect("18")); + + // Check matching the variable defined matches the correct number only + Tester.initNextPattern(); + Tester.parsePatternExpect("[[#NUMVAR]] [[#NUMVAR+2]]"); + EXPECT_TRUE(Tester.matchExpect("19 21")); + EXPECT_TRUE(Tester.matchExpect("18 21")); + EXPECT_FALSE(Tester.matchExpect("18 20")); } TEST_F(FileCheckTest, Substitution) { @@ -236,7 +345,7 @@ // Substitutions of defined pseudo and non-pseudo numeric variables return // the right value. FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42); - FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10); + FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 10); FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0); FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3); FileCheckPatternSubstitution SubstitutionLine = @@ -257,7 +366,7 @@ EXPECT_FALSE(SubstitutionN.getResult()); // Substitution of defined pattern variable returns the right value. - FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context); + FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1); PatternSubstitution = FileCheckPatternSubstitution(&Context, "FOO", 42); Value = PatternSubstitution.getResult(); EXPECT_TRUE(Value); @@ -284,16 +393,16 @@ UndefVar = Substitution.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(); 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(); EXPECT_EQ("@LINE", UndefVar); @@ -358,9 +467,10 @@ StringRef EmptyVarStr = "EmptyVar"; StringRef UnknownVarStr = "UnknownVar"; llvm::Optional LocalVar = Cxt.getPatternVarValue(LocalVarStr); - FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt); + FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); + FileCheckNumericVariable *DefinedNumericVariable; FileCheckNumExpr *NumExpr = - P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM); + P.parseNumericSubstitution(LocalNumVarRef, DefinedNumericVariable, SM); llvm::Optional EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); llvm::Optional UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); EXPECT_TRUE(LocalVar); @@ -382,9 +492,9 @@ // variable clearing due to --enable-var-scope happens after numeric // expressions are linked to the numeric variables they use. EXPECT_FALSE(NumExpr->eval()); - P = FileCheckPattern(Check::CheckPlain, &Cxt); + P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); NumExpr = - P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM); + P.parseNumericSubstitution(LocalNumVarRef, DefinedNumericVariable, SM); EXPECT_FALSE(NumExpr); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_FALSE(EmptyVar); @@ -399,9 +509,9 @@ llvm::Optional GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); EXPECT_TRUE(GlobalVar); EXPECT_EQ(*GlobalVar, "BAR"); - P = FileCheckPattern(Check::CheckPlain, &Cxt); + P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); NumExpr = - P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM); + P.parseNumericSubstitution(GlobalNumVarRef, DefinedNumericVariable, SM); EXPECT_TRUE(NumExpr); NumExprVal = NumExpr->eval(); EXPECT_TRUE(NumExprVal); @@ -411,9 +521,9 @@ Cxt.clearLocalVars(); GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); EXPECT_TRUE(GlobalVar); - P = FileCheckPattern(Check::CheckPlain, &Cxt); + P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); NumExpr = - P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM); + P.parseNumericSubstitution(GlobalNumVarRef, DefinedNumericVariable, SM); EXPECT_TRUE(NumExpr); NumExprVal = NumExpr->eval(); EXPECT_TRUE(NumExprVal);