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 @@ -105,12 +105,12 @@ Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be used in ``CHECK:`` lines. -.. option:: -D#= +.. option:: -D#= Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating - ```` that can be used in ``CHECK:`` lines. See section - ``FileCheck Numeric Variables and Expressions`` for details on the format - and meaning of ````. + ```` that can be used in ``CHECK:`` lines. See section + ``FileCheck Numeric Variables and Expressions`` for details on supported + numeric expressions. .. option:: -version @@ -614,11 +614,19 @@ due to ``7`` being unequal to ``5 + 1``. +A numeric variable can also be defined to the result of a numeric expression, +in which case the numeric expression constraint is checked and if verified the +variable is assigned to the value. The unified syntax for both defining numeric +variables and checking a numeric expression is thus +``[[#%,: ]]`` with each element as +described previously. + 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. +a numeric variable with a non-empty numeric expression constraint defined on +the same line. FileCheck Pseudo Numeric Variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 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 @@ -96,6 +96,12 @@ /// Name of the numeric variable. StringRef Name; + /// Pointer to numeric expression defining this numeric variable. Null for + /// pseudo variable whose value is known at parse time (e.g. @LINE pseudo + /// variable) or cleared local variable. If numeric expression is empty + /// NumExpr points to a FileCheckNumExpr with a null AST. + FileCheckNumExpr *NumExpr; + /// Value of numeric variable, if defined, or None otherwise. llvm::Optional Value; @@ -104,21 +110,32 @@ unsigned DefLineNumber; public: - /// Constructor for a variable \p Name defined at line \p DefLineNumber. - FileCheckNumericVariable(StringRef Name, unsigned DefLineNumber) - : Name(Name), Value(llvm::None), DefLineNumber(DefLineNumber) {} + /// Constructor for a variable \p Name defined at line \p DefLineNumber to + /// the numeric expression represented by NumExpr. + FileCheckNumericVariable(StringRef Name, FileCheckNumExpr *NumExpr, + unsigned DefLineNumber) + : Name(Name), NumExpr(NumExpr), 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) - : Name(Name), Value(Value) {} + explicit FileCheckNumericVariable(StringRef Name, uint64_t Value) + : Name(Name), NumExpr(nullptr), Value(Value) {} /// \returns name of that numeric variable. StringRef getName() const { return Name; } + /// \returns numeric expression associated with this numeric variable. + FileCheckNumExpr *getNumExpr() const { return NumExpr; } + /// Evaluates and returns the value of the expression represented by this - /// AST. Therefore, \returns this variable's value. - llvm::Optional eval() const { return Value; } + /// AST. Therefore, \returns this variable's value or the value of its + /// associated numeric expression, if any. + llvm::Optional eval() const; + + /// \returns whether this variable's value is known at match time, when + /// performing the substitutions. + bool isMatchTimeKnown() const; /// Appends numeric variable's name to UndefVarNames if undefined. void appendUndefVarNames(std::vector &UndefVarNames) const; @@ -412,13 +429,6 @@ /// character that is part of the variable name. Otherwise, only /// \returns true. static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx); - /// 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, unless \p AcceptFail is true and the error is in - /// parsing the variable name. Otherwise, sets \p Name to the name of the - /// parsed numeric variable. - bool parseNumericVariable(StringRef &Expr, StringRef &Name, bool IsDefinition, - bool AcceptFail, const SourceMgr &SM) const; /// Parses \p Expr for a numeric expression. Parameter \p Legacy indicates /// whether Expr should be a legacy numeric expression. \p returns the class /// representing the AST of numeric expression or nullptr if parsing fails @@ -484,6 +494,13 @@ size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM); enum AllowedOperand { LegacyVar, LegacyLiteral, Any }; + /// 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, unless \p AcceptFail is true and the error is in + /// parsing the variable name. Otherwise, sets \p Name to the name of the + /// parsed numeric variable. + bool parseNumericVariable(StringRef &Expr, StringRef &Name, bool IsDefinition, + bool AcceptFail, const SourceMgr &SM) const; /// Parses \p Expr for use of a numeric operand. Accepts both literal values /// and numeric variables, depending on the value of \p AllowedOperandFlag. /// \returns the class representing that operand in the AST of the numeric 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 @@ -24,6 +24,23 @@ using namespace llvm; +llvm::Optional FileCheckNumericVariable::eval() const { + if (Value) + return Value; + + if (NumExpr == nullptr || NumExpr->getAST() == nullptr) + return llvm::None; + + return NumExpr->getAST()->eval(); +} + +bool FileCheckNumericVariable::isMatchTimeKnown() const { + if (Value) + return true; + + return NumExpr != nullptr && NumExpr->getAST() != nullptr; +} + void FileCheckNumericVariable::appendUndefVarNames( std::vector &UndefVarNames) const { if (!Value) @@ -41,6 +58,7 @@ if (!Value) return true; Value = llvm::None; + NumExpr = nullptr; return false; } @@ -187,7 +205,8 @@ } FileCheckNumericVariable *NumericVariable = VarTableIter->second.get(); - if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber) { + if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber && + !NumericVariable->isMatchTimeKnown()) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, "numeric variable '" + NumericVariable->getName() + "' defined on the same line"); @@ -280,42 +299,19 @@ StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable, bool Legacy, const SourceMgr &SM) const { std::shared_ptr NumExprAST; + StringRef DefExpr = StringRef(); - // Parse numeric variable definition. DefinedNumericVariable = nullptr; + // Save variable definition expression if any. 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*/, - false /*AcceptFail*/, SM)) { - // Invalid variable definition. Error reporting done in parsing function. - return nullptr; - } - - DefinedNumericVariable = - new FileCheckNumericVariable(Name, this->LineNumber); + DefExpr = Expr.substr(0, DefEnd); + Expr = Expr.substr(DefEnd + 1); + } - 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; - } - } else { - // Parse numeric expression itself. - Expr = Expr.ltrim(SpaceChars); + // Parse numeric expression itself. + Expr = Expr.ltrim(SpaceChars); + if (!Expr.empty()) { // First operand in legacy numeric expression is the @LINE pseudo variable. enum AllowedOperand AO = Legacy ? LegacyVar : Any; NumExprAST = parseNumericOperand(Expr, AO, SM); @@ -336,7 +332,31 @@ } } - return Context->makeNumExpr(NumExprAST); + FileCheckNumExpr *NumExpr = Context->makeNumExpr(NumExprAST); + + // Parse numeric variable definition. + if (!DefExpr.empty()) { + DefExpr = DefExpr.ltrim(SpaceChars); + StringRef Name; + if (parseNumericVariable(DefExpr, Name, true /*IsDefinition*/, + false /*AcceptFail*/, SM)) { + // Invalid variable definition. Error reporting done in parsing function. + return nullptr; + } + + DefinedNumericVariable = + new FileCheckNumericVariable(Name, NumExpr, this->LineNumber); + + DefExpr = DefExpr.ltrim(SpaceChars); + if (!DefExpr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(DefExpr.data()), + SourceMgr::DK_Error, + "invalid numeric variable definition"); + return nullptr; + } + } + + return NumExpr; } bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, @@ -430,10 +450,11 @@ // 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 also have the definition and substitution forms. - // 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. + // the double brackets and also have the definition and substitution forms + // and the two forms can be combined to set a variable to the evaluation of + // a numeric expression. 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. if (PatternStr.startswith("[[")) { StringRef UnparsedPatternStr = PatternStr.substr(2); // Find the closing bracket pair ending the match. End is going to be an @@ -456,10 +477,11 @@ PatternStr = UnparsedPatternStr.substr(End + 2); bool IsVarDef; + bool SubstNeeded; bool Legacy = false; StringRef DefName; StringRef SubstStr; - StringRef MatchRegexp; + StringRef MatchRegexp = StringRef(); size_t SubstInsertIdx = RegExStr.size(); FileCheckNumericVariable *DefinedNumericVariable; FileCheckNumExpr *NumExpr; @@ -487,6 +509,7 @@ StringRef Trailer = MatchStr.substr(TrailIdx); IsVarDef = (VarEndIdx != StringRef::npos); Legacy = IsNumExpr = IsPseudo; + SubstNeeded = !IsVarDef; if (IsVarDef) { if ((IsPseudo || !Trailer.consume_front(":"))) { @@ -518,16 +541,53 @@ if (NumExpr == nullptr) return true; IsNumExpr = true; - if (DefinedNumericVariable != nullptr) { - IsVarDef = true; + IsVarDef = DefinedNumericVariable != nullptr; + SubstNeeded = NumExpr->getAST() != nullptr; + if (IsVarDef) DefName = DefinedNumericVariable->getName(); - MatchRegexp = StringRef("[0-9]+"); - } else + if (SubstNeeded) SubstStr = MatchStr; + else + MatchRegexp = StringRef("[0-9]+"); + } + + // Handle variable definition: [[:(...)]] and [[#(...):(...)]]. + if (IsVarDef) { + RegExStr += '('; + ++SubstInsertIdx; + + 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] = + std::shared_ptr(DefinedNumericVariable); + } else { + VariableDefs[DefName] = CurParen; + // Mark pattern variable as defined to detect collision between + // pattern and numeric variables 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 use of undefined variable in match(). + Context->DefinedVariableTable[DefName] = true; + } + + ++CurParen; } + if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM)) + return true; + + if (IsVarDef) + RegExStr += ')'; + // Handle variable use: [[foo]] and [[#]]. - if (!IsVarDef) { + if (SubstNeeded) { // Handle use of pattern variables that were defined earlier on the // same line by emitting a backreference. Numeric expressions do not // support using a numeric variable defined on the same line. @@ -550,37 +610,7 @@ SubstInsertIdx); Substitutions.push_back(Substitution); } - continue; } - - // 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] = - std::shared_ptr(DefinedNumericVariable); - } else { - VariableDefs[DefName] = CurParen; - // Mark pattern variable as defined to detect collision 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 use of undefined variable in match(). - Context->DefinedVariableTable[DefName] = true; - } - RegExStr += '('; - ++CurParen; - - if (AddRegExToRegEx(MatchRegexp, CurParen, SM)) - return true; - - RegExStr += ')'; } // Handle fixed string matches. @@ -1730,9 +1760,37 @@ std::string CmdlineDefsDiag; StringRef Prefix1 = "Global define #"; StringRef Prefix2 = ": "; - for (StringRef CmdlineDef : CmdlineDefines) - CmdlineDefsDiag += - (Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str(); + StringRef Suffix1 = " (parsed as: [["; + StringRef Suffix3 = "]])"; + SmallVector, 4> CmdlineDefsIndices; + for (StringRef CmdlineDef : CmdlineDefines) { + StringRef DefNo = Twine(++I).str(); + size_t EqIdx = CmdlineDef.find('='); + if (EqIdx == StringRef::npos) { + llvm::outs() << "Missing equal sign in global definition '" << CmdlineDef + << "'"; + CmdlineDefsIndices.push_back(std::make_pair(0, 0)); + ErrorFound = true; + continue; + } + // Numeric variable definition. + if (CmdlineDef[0] == '#') { + // Append a copy command-line definition adapted to use the same format + // as in the input file to be able to reuse ParseNumericExpression. + CmdlineDefsDiag += + (Prefix1 + DefNo + Prefix2 + CmdlineDef + Suffix1).str(); + std::string Suffix2 = CmdlineDef; + Suffix2[EqIdx] = ':'; + CmdlineDefsIndices.push_back( + std::make_pair(CmdlineDefsDiag.size(), Suffix2.size())); + CmdlineDefsDiag += (Suffix2 + Suffix3 + "\n").str(); + } else { + CmdlineDefsDiag += (Prefix1 + DefNo + Prefix2).str(); + CmdlineDefsIndices.push_back( + std::make_pair(CmdlineDefsDiag.size(), CmdlineDef.size())); + CmdlineDefsDiag += (CmdlineDef + "\n").str(); + } + } // Create a buffer with fake command line content in order to display // parsing diagnostic with location information and point to the @@ -1742,66 +1800,41 @@ StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer(); SM.AddNewSourceBuffer(std::move(CmdLineDefsDiagBuffer), SMLoc()); - // Dummy pattern to call parseNumericVariable. + // Dummy pattern to call parseNumericExpression. 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); - size_t EqIdx = CmdlineDef.find('='); - if (EqIdx == StringRef::npos) { - SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()), - SourceMgr::DK_Error, - "Missing equal sign in global definition"); - ErrorFound = true; + for (std::pair CmdlineDefIndices : CmdlineDefsIndices) { + StringRef CmdlineDef = CmdlineDefsDiagRef.substr(CmdlineDefIndices.first, + CmdlineDefIndices.second); + // Error already handled in previous loop, skip this. + if (CmdlineDef.empty()) continue; - } // Numeric variable definition. if (CmdlineDef[0] == '#') { - StringRef Expr = CmdlineDef.substr(1, EqIdx - 1); - StringRef CmdlineName; - SMLoc CmdlineNameLoc = SMLoc::getFromPointer(Expr.data()); - if (P.parseNumericVariable(Expr, CmdlineName, true /*IsDefinition*/, - false /*AcceptFail*/, SM) || - !Expr.empty()) { - SM.PrintMessage(CmdlineNameLoc, SourceMgr::DK_Error, - "invalid variable name"); - ErrorFound = true; - continue; - } - - // Detect collisions between pattern 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"); + // Now parse to both check syntax is correct and create the necessary + // class instance. + StringRef CmdlineDefExpr = CmdlineDef.substr(1); + FileCheckNumericVariable *DefinedNumericVariable; + FileCheckNumExpr *NumExpr = P.parseNumericExpression( + CmdlineDefExpr, DefinedNumericVariable, false, SM); + if (NumExpr == nullptr || DefinedNumericVariable == nullptr) { ErrorFound = true; continue; } - - StringRef CmdlineVal = CmdlineDef.substr(EqIdx + 1); - uint64_t Val; - if (CmdlineVal.getAsInteger(10, Val)) { - SM.PrintMessage(SMLoc::getFromPointer(CmdlineVal.data()), + llvm::Optional Value = NumExpr->getAST()->eval(); + if (!Value) { + SM.PrintMessage(SMLoc::getFromPointer(CmdlineDefExpr.data()), SourceMgr::DK_Error, - "invalid value in numeric variable definition '" + - CmdlineVal + "'"); + "unable to represent numeric value"); ErrorFound = true; continue; } - auto DefinedNumericVariable = - std::make_shared(CmdlineName, (unsigned)0); - DefinedNumericVariable->setValue(Val); + DefinedNumericVariable->setValue(*Value); // Record this variable definition. GlobalNumericVariableTable[DefinedNumericVariable->getName()] = - DefinedNumericVariable; + std::shared_ptr(DefinedNumericVariable); } else { // Pattern variable definition. std::pair CmdlineNameVal = CmdlineDef.split('='); 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,30 +4,22 @@ 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 variable name -NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 -NUMERRCLIFMT-NEXT: {{^ \^$}} +NUMERRCLIFMT: Global defines:1:46: error: invalid variable name +NUMERRCLIFMT-NEXT: Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}}) +NUMERRCLIFMT-NEXT: {{^ \^$}} ; Invalid definition of pseudo variable. 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 pseudo numeric variable -NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10 -NUMERRCLIPSEUDO-NEXT: {{^ \^$}} +NUMERRCLIPSEUDO: Global defines:1:45: error: invalid pseudo numeric variable +NUMERRCLIPSEUDO-NEXT: Global define #1: #@VALUE=10 (parsed as: {{\[\[#@VALUE:10\]\]}}) +NUMERRCLIPSEUDO-NEXT: {{^ \^$}} ; Invalid definition of an expression. 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 variable name -NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10 -NUMERRCLITRAIL-NEXT: {{^ \^$}} - -; Invalid value: numeric expression instead of literal. -RUN: not FileCheck -D#VALUE1=3 -D#VALUE2='VALUE1 + 2' --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIEXPR - -NUMERRCLIEXPR: Global defines:2:27: error: invalid value in numeric variable definition 'VALUE1 + 2' -NUMERRCLIEXPR-NEXT: Global define #2: #VALUE2=VALUE1 + 2 -NUMERRCLIEXPR-NEXT: {{^ \^$}} +NUMERRCLITRAIL: Global defines:1:51: error: invalid numeric variable definition +NUMERRCLITRAIL-NEXT: Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}}) +NUMERRCLITRAIL-NEXT: {{^ \^$}} 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 @@ -1,22 +1,22 @@ ; Test functionality of -D# option: numeric variables are defined to the right ; value and CHECK directives using them match as expected given the value set. -RUN: FileCheck -D#NUMVAL=12 --check-prefix CHECKNUM --input-file %s %s -RUN: not FileCheck -D#NUMVAL=8 --check-prefix CHECKNUM --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG -RUN: not FileCheck -D#NUMVAL=12 --check-prefix NUMNOT --input-file %s %s 2>&1 \ -RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG -RUN: FileCheck -D#NUMVAL=8 --check-prefixes NUMNOT --input-file %s %s +RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefix CHECKNUM -input-file %s %s +RUN: not FileCheck -D#NUMVAL2=8 -check-prefix CHECKNUM -input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG +RUN: not FileCheck -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \ +RUN: | FileCheck %s --strict-whitespace -check-prefix NOT-NUMERRMSG +RUN: FileCheck -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s -Numeric value = 12 -CHECKNUM: Numeric value = [[#NUMVAL]] -NUMNOT-NOT: Numeric value = [[#NUMVAL]] +Numeric value #2 = 12 +CHECKNUM: Numeric value #2 = [[#NUMVAL2]] +NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]] 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 numeric expression "NUMVAL2" 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 numeric expression "NUMVAL2" 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 @@ -62,6 +62,20 @@ CHECK-LABEL: USE UNSIGNED IMM CHECK-NEXT: [[#VAR1+9223372036854775808]] +; Numeric expression using a variable defined from a numeric expression +DEF EXPR GOOD MATCH +42 +41 43 +; CHECK-LABEL: DEF EXPR GOOD MATCH +; CHECK-NEXT: [[# VAR42:VAR1+31]] +; CHECK-NEXT: [[# VAR41: VAR42-1]] [[# VAR41 + 2]] + +; Empty numeric expression +EMPTY NUM EXPR +foo 104 bar +; CHECK-LABEL: EMPTY NUM EXPR +; CHECK-NEXT: foo [[#]] bar + ; 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 @@ -119,6 +133,17 @@ 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: {{^ \^$}} +CLI-NUM-CONFLICT: Global defines:2:45: error: pattern variable with name 'PATVAR' already exists +CLI-NUM-CONFLICT-NEXT: Global define #2: #PATVAR=42 (parsed as: {{\[\[#PATVAR:42\]\]}}) +CLI-NUM-CONFLICT-NEXT: {{^ \^$}} + +; Verify that when variable is set to an expression the expression is still +; checked +RUN: not FileCheck -check-prefix DEF-EXPR-FAIL -input-file %s %s + +DEF EXPR WRONG MATCH +20 +43 +DEF-EXPR-FAIL-LABEL: DEF EXPR WRONG MATCH +DEF-EXPR-FAIL-NEXT: [[# VAR20:]] +DEF-EXPR-FAIL-NEXT: [[# VAR42: VAR20+22]] 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 @@ -31,7 +31,9 @@ TEST_F(FileCheckTest, NumericVariable) { // Undefined variable: eval and clearValue fails, appendUndefVarNames returns // the variable and setValue works. - FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1U); + auto NumVarExpr = FileCheckNumExpr(nullptr); + FileCheckNumericVariable FooVar = + FileCheckNumericVariable("FOO", &NumVarExpr, 1U); EXPECT_EQ("FOO", FooVar.getName()); llvm::Optional Value = FooVar.eval(); EXPECT_FALSE(Value); @@ -72,8 +74,13 @@ uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } TEST_F(FileCheckTest, Binop) { - auto FooVar = std::make_shared("FOO", (uint64_t)42); - auto BarVar = std::make_shared("BAR", (uint64_t)18); + auto DefNumExpr = FileCheckNumExpr(nullptr); + auto FooVar = + std::make_shared("FOO", &DefNumExpr, 1); + FooVar->setValue(42); + auto BarVar = + std::make_shared("BAR", &DefNumExpr, 2); + BarVar->setValue(18); FileCheckASTBinop Binop = FileCheckASTBinop(doAdd, FooVar, BarVar); // Defined variable: eval returns right value, no undefined variable @@ -218,13 +225,6 @@ } public: - bool parseNumVarExpect(StringRef Expr, bool isDefinition) { - StringRef ExprBufferRef = bufferize(SM, Expr); - StringRef Name; - return P.parseNumericVariable(ExprBufferRef, Name, isDefinition, - false /*AcceptFail*/, SM); - } - bool parseExprExpect(StringRef Expr) { StringRef ExprBufferRef = bufferize(SM, Expr); FileCheckNumericVariable *DefinedNumericVariable; @@ -244,35 +244,6 @@ } }; -TEST_F(FileCheckTest, ParseNumericVariable) { - PatternTester Tester; - - // Invalid variable name. - EXPECT_TRUE(Tester.parseNumVarExpect("42INVALID", false /*isDefinition*/)); - - // Invalid pseudo variable. - EXPECT_TRUE(Tester.parseNumVarExpect("@FOO", false /*isDefinition*/)); - - // 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. - EXPECT_TRUE(Tester.parseNumVarExpect("UNDEF", false /*isDefinition*/)); - - // Use variable defined on same line. - EXPECT_FALSE(Tester.parseExprExpect("LINE1VAR:")); - EXPECT_TRUE(Tester.parseNumVarExpect("LINE1VAR", false /*isDefinition*/)); - - // Defined variable. - EXPECT_FALSE(Tester.parseNumVarExpect("FOO", false /*isDefinition*/)); -} - TEST_F(FileCheckTest, ParseExpr) { PatternTester Tester; @@ -283,12 +254,12 @@ EXPECT_TRUE(Tester.parseExprExpect("@FOO:")); EXPECT_TRUE(Tester.parseExprExpect("@LINE:")); + // Conflict with pattern variable. + EXPECT_TRUE(Tester.parseExprExpect("BAR:")); + // Garbage after name of variable being defined. EXPECT_TRUE(Tester.parseExprExpect("VAR GARBAGE:")); - // Variable defined to numeric expression. - EXPECT_TRUE(Tester.parseExprExpect("VAR1: FOO")); - // Acceptable variable definition. EXPECT_FALSE(Tester.parseExprExpect("VAR1:")); EXPECT_FALSE(Tester.parseExprExpect(" VAR2:")); @@ -306,6 +277,10 @@ EXPECT_FALSE(Tester.parseExprExpect("@LINE")); EXPECT_FALSE(Tester.parseExprExpect("FOO")); + // Use variable defined on same line. + EXPECT_FALSE(Tester.parseExprExpect("LINE1VAR:")); + EXPECT_TRUE(Tester.parseExprExpect("LINE1VAR")); + // Unsupported operator. EXPECT_TRUE(Tester.parseExprExpect("@LINE/2")); @@ -352,7 +327,6 @@ EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]")); EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]")); EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]")); - EXPECT_TRUE(Tester.parsePatternExpect("[[#YUP:@LINE]]")); // Valid numeric expressions and numeric variable definition. EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]")); @@ -396,9 +370,12 @@ // Substitution of defined numeric variable returns the right value, // getUndefVarNames does not return any variable. + auto DefNumExpr = FileCheckNumExpr(nullptr); auto LineVar = - std::make_shared("@LINE", (uint64_t)42); - auto NVar = std::make_shared("N", (uint64_t)10); + std::make_shared("@LINE", &DefNumExpr, 1); + auto NVar = std::make_shared("N", &DefNumExpr, 1); + LineVar->setValue(42); + NVar->setValue(10); FileCheckNumExpr NumExprLine = FileCheckNumExpr(LineVar); FileCheckNumExpr NumExprN = FileCheckNumExpr(NVar); FileCheckPatternSubstitution SubstitutionLine =