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 @@ -619,11 +619,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 string 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 @@ -80,6 +80,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. + std::shared_ptr NumExprAST; + /// Value of numeric variable, if defined, or None otherwise. llvm::Optional Value; @@ -88,21 +94,29 @@ 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 a variable \p Name defined at line \p DefLineNumber to + /// the numeric expression represented by NumExpr. + FileCheckNumericVariable(size_t DefLineNumber, StringRef Name, + std::shared_ptr NumExprAST) + : Name(Name), NumExprAST(NumExprAST), 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), DefLineNumber(0) {} + : Name(Name), NumExprAST(nullptr), Value(Value), DefLineNumber(0) {} /// \returns name of that numeric variable. StringRef getName() const { return Name; } /// 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; @@ -430,13 +444,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 substitution block. Parameter \p Legacy /// indicates whether Expr should be a legacy numeric substitution block. /// Sets \p NumExprAST to point to the class instance representing the @@ -502,6 +509,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 (NumExprAST == nullptr) + return llvm::None; + + return NumExprAST->eval(); +} + +bool FileCheckNumericVariable::isMatchTimeKnown() const { + if (Value) + return true; + + return NumExprAST != nullptr; +} + void FileCheckNumericVariable::appendUndefVarNames( std::vector &UndefVarNames) const { if (!Value) @@ -41,6 +58,7 @@ if (!Value) return true; Value = llvm::None; + NumExprAST = nullptr; return false; } @@ -188,7 +206,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"); @@ -282,41 +301,19 @@ StringRef Expr, std::shared_ptr &NumExprAST, FileCheckNumericVariable *&DefinedNumericVariable, bool Legacy, const SourceMgr &SM) const { - // Parse numeric variable definition. + StringRef DefExpr = StringRef(); + 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 true; - } - - DefExpr = DefExpr.ltrim(SpaceChars); - if (!DefExpr.empty()) { - SM.PrintMessage(SMLoc::getFromPointer(DefExpr.data()), - SourceMgr::DK_Error, - "invalid numeric variable definition"); - return true; - } - UseExpr = UseExpr.ltrim(SpaceChars); - if (!UseExpr.empty()) { - SM.PrintMessage( - SMLoc::getFromPointer(UseExpr.data()), SourceMgr::DK_Error, - "unexpected string after variable definition: '" + UseExpr + "'"); - return true; - } + DefExpr = Expr.substr(0, DefEnd); + Expr = Expr.substr(DefEnd + 1); + } - DefinedNumericVariable = - new FileCheckNumericVariable(this->LineNumber, Name); - } 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); @@ -337,6 +334,28 @@ } } + // Parse numeric variable definition. + if (DefEnd != StringRef::npos) { + 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 true; + } + + DefExpr = DefExpr.ltrim(SpaceChars); + if (!DefExpr.empty()) { + SM.PrintMessage(SMLoc::getFromPointer(DefExpr.data()), + SourceMgr::DK_Error, + "invalid numeric variable definition"); + return true; + } + + DefinedNumericVariable = + new FileCheckNumericVariable(this->LineNumber, Name, NumExprAST); + } + return false; } @@ -427,14 +446,16 @@ continue; } - // String and numeric substitution blocks. String substitution blocks come + // String and numeric substitution blocks. Pattern 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 also have the definition and - // substitution forms. Both string 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. + // substitution forms and the two forms can be combined to set a variable + // to the evaluation of a numeric expression. Both string 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 @@ -455,10 +476,11 @@ PatternStr = UnparsedPatternStr.substr(End + 2); bool IsVarDef = false; + bool SubstNeeded = false; bool Legacy = false; StringRef DefName; StringRef SubstStr; - StringRef MatchRegexp; + StringRef MatchRegexp = StringRef(); size_t SubstInsertIdx = RegExStr.size(); // Parse string variable or legacy numeric expression. @@ -484,6 +506,7 @@ StringRef Trailer = MatchStr.substr(TrailIdx); IsVarDef = (VarEndIdx != StringRef::npos); Legacy = IsNumBlock = IsPseudo; + SubstNeeded = !IsVarDef; if (IsVarDef) { if ((IsPseudo || !Trailer.consume_front(":"))) { @@ -515,16 +538,53 @@ if (parseNumericSubstitutionBlock(MatchStr, NumExprAST, DefinedNumericVariable, Legacy, SM)) return true; - if (DefinedNumericVariable) { - IsVarDef = true; + IsVarDef = DefinedNumericVariable != nullptr; + SubstNeeded = NumExprAST != 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 (IsNumBlock) { + 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 string variable as defined to detect collisions between + // string 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; + } + + ++CurParen; + } + + if (!MatchRegexp.empty() && AddRegExToRegEx(MatchRegexp, CurParen, SM)) + return true; + + if (IsVarDef) + RegExStr += ')'; + // Handle substitutions: [[foo]] and [[#]]. - if (!IsVarDef) { + if (SubstNeeded) { // Handle substitution of string variables that were defined earlier on // the same line by emitting a backreference. Numeric expressions do // not support substituting a numeric variable defined on the same @@ -548,38 +608,7 @@ : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } - continue; - } - - // Handle variable definitions: [[:(...)]] and - // [[#(...):(...)]]. - if (IsNumBlock) { - 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 string variable as defined to detect collisions between - // string 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(MatchRegexp, CurParen, SM)) - return true; - - RegExStr += ')'; } // Handle fixed string matches. @@ -1734,9 +1763,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 @@ -1746,66 +1803,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 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, - "string 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); + std::shared_ptr NumExprAST; + FileCheckNumericVariable *DefinedNumericVariable; + if (P.parseNumericSubstitutionBlock(CmdlineDefExpr, NumExprAST, + DefinedNumericVariable, false, SM)) { 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 = NumExprAST->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(0, CmdlineName); - DefinedNumericVariable->setValue(Val); + DefinedNumericVariable->setValue(*Value); // Record this variable definition. GlobalNumericVariableTable[DefinedNumericVariable->getName()] = - DefinedNumericVariable; + std::shared_ptr(DefinedNumericVariable); } else { // String 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 "NUMVAL" equal to "8" +NUMERRMSG: defines.txt:1:1: note: with "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 "NUMVAL" equal to "12" +NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with "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: string 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: string 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: string 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,7 @@ TEST_F(FileCheckTest, NumericVariable) { // Undefined variable: eval and clearValue fails, appendUndefVarNames returns // the variable and setValue works. - FileCheckNumericVariable FooVar = FileCheckNumericVariable(1, "FOO"); + FileCheckNumericVariable FooVar = FileCheckNumericVariable(1, "FOO", nullptr); EXPECT_EQ("FOO", FooVar.getName()); llvm::Optional Value = FooVar.eval(); EXPECT_FALSE(Value); @@ -72,8 +72,10 @@ uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } TEST_F(FileCheckTest, Binop) { - auto FooVar = std::make_shared("FOO", 42); - auto BarVar = std::make_shared("BAR", 18); + auto FooVar = std::make_shared(1, "FOO", nullptr); + FooVar->setValue(42); + auto BarVar = std::make_shared(2, "BAR", nullptr); + BarVar->setValue(18); FileCheckASTBinop Binop = FileCheckASTBinop(doAdd, FooVar, BarVar); // Defined variable: eval returns right value, no undefined variable @@ -218,13 +220,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 parseSubstExpect(StringRef Expr) { StringRef ExprBufferRef = bufferize(SM, Expr); std::shared_ptr NumExprAST; @@ -245,35 +240,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.parseSubstExpect("LINE1VAR:")); - EXPECT_TRUE(Tester.parseNumVarExpect("LINE1VAR", false /*isDefinition*/)); - - // Defined variable. - EXPECT_FALSE(Tester.parseNumVarExpect("FOO", false /*isDefinition*/)); -} - TEST_F(FileCheckTest, ParseExpr) { PatternTester Tester; @@ -284,12 +250,12 @@ EXPECT_TRUE(Tester.parseSubstExpect("@FOO:")); EXPECT_TRUE(Tester.parseSubstExpect("@LINE:")); + // Conflict with pattern variable. + EXPECT_TRUE(Tester.parseSubstExpect("BAR:")); + // 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:")); @@ -307,6 +273,10 @@ EXPECT_FALSE(Tester.parseSubstExpect("@LINE")); EXPECT_FALSE(Tester.parseSubstExpect("FOO")); + // Use variable defined on same line. + EXPECT_FALSE(Tester.parseSubstExpect("LINE1VAR:")); + EXPECT_TRUE(Tester.parseSubstExpect("LINE1VAR")); + // Unsupported operator. EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2")); @@ -353,7 +323,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]]")); @@ -397,8 +366,11 @@ // Substitution of defined pseudo and non-pseudo numeric variable returns the // right value, getUndefVarNames does not return any variable. - auto LineVar = std::make_shared("@LINE", 42); - auto NVar = std::make_shared("N", 10); + auto LineVar = + std::make_shared(1, "@LINE", nullptr); + auto NVar = std::make_shared(1, "N", nullptr); + LineVar->setValue(42); + NVar->setValue(10); FileCheckNumericSubstitution SubstitutionLine = FileCheckNumericSubstitution(&Context, "@LINE", LineVar, 12); FileCheckNumericSubstitution SubstitutionN =