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,6 +105,11 @@ Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be used in ``CHECK:`` lines. +.. option:: -D#= + + Sets a filecheck numeric variable ``VAR`` to ```` that can be used + in ``CHECK:`` lines. + .. option:: -version Show the version number of this program. @@ -560,8 +565,52 @@ This makes it easier to ensure that individual tests are not affected by variables set in preceding tests. -FileCheck Numeric Expressions -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +FileCheck Numeric Variables and Expressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:program:`FileCheck` also allows to check for numeric values satisfying a +numeric expression constraint based on numeric variables. This allows to +capture numeric relations between two numbers in a text to check, such as the +the need for consecutive registers to be used. + +The syntax to check a numeric expression constraint is +``[[#]]`` where: + +*```` is the name of a numeric variable defined on the command line. + +*```` is an optional numeric operation to perform on the value of +````. Currently supported numeric operations are ``+`` and ``-``. + +*```` is the immediate value that constitutes the second operand of +the numeric operation . It must be present if ```` is present, +absent otherwise. + +For example: + +.. code-block:: llvm + + ; CHECK: add r[[#REG]], r[[#REG]], r[[#REG+1]] + +The above example would match the lines: + +.. code-block:: llvm + + add r5, r5, r6 + +but would not match the lines: + +.. code-block:: llvm + + add r5, r5, r7 + +due to ``7`` being unequal to ``5 + 1``. + +In the same way as for pattern variables, ``--enable-var-scope`` only consider +global numeric variables that start with ``$`` and undefined local variables at +the beginning of each CHECK-LABEL block. + +FileCheck Pseudo Numeric Variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Sometimes there's a need to verify output which contains line numbers of the match file, e.g. when testing compiler diagnostics. This introduces a certain @@ -569,11 +618,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 allows using ``[[#@LINE]]``, -``[[#@LINE+]]``, ``[[#@LINE-]]`` numeric expressions in -patterns, with arbitrary number of space between each elements of the -expression. These expressions expand to a number of the line where a pattern -is located (with an optional integer offset). +To support this case, FileCheck understands the ``@LINE`` pseudo numeric +variable which evaluate 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 @@ -40,20 +40,87 @@ // Numeric expression handling code. //===----------------------------------------------------------------------===// +class FileCheckASTBinop; + /// Class representing a numeric expression. class FileCheckNumExpr { private: - /// Value of the numeric expression.. + /// Pointer to AST of the numeric expression. + std::shared_ptr AST; + +public: + /// Generic constructor for a numeric expression whose equality constraint is + /// represented by \p AST. + FileCheckNumExpr(std::shared_ptr AST) : AST(AST) {} + + /// Return pointer to AST of the numeric expression. Pointer is guaranteed to + /// be valid as long as this object is. + FileCheckASTBinop *getAST() const { return AST.get(); } +}; + +/// Class representing a numeric variable with a given value in the AST of a +/// numeric expression. +class FileCheckNumExprVar { +private: + /// Name of the numeric variable. + StringRef Name; + + /// Whether variable is defined and thus Value is set. + bool Defined; + + /// Value of numeric variable if defined. uint64_t Value; public: - /// Constructor for a numeric expression with a known value at parse time, - /// e.g. the implicit numeric expression defining the @LINE numeric pseudo - /// variable. - explicit FileCheckNumExpr(uint64_t Value) : Value(Value) {} + /// Constructor for numeric variable \p Name with a known \p Value at parse + /// time (e.g. the @LINE numeric variable). + explicit FileCheckNumExprVar(StringRef Name, uint64_t Value) + : Name(Name), Defined(true), Value(Value) {} + + /// Return name of that numeric variable. + StringRef getName() const { return Name; } + + /// Return value of this numeric variable. + llvm::Optional getValue() const; - /// Return the value being matched against. - uint64_t getValue() const { return Value; } + /// Set value of this numeric variable if not defined. Return whether + /// variable was already defined. + bool setValue(uint64_t Value); + + /// Clear value of this numeric variable. Return whether value was already + /// undefined. + bool clearValue(); +}; + +/// Type of functions evaluating a given binary operation. +using binop_eval_t = uint64_t (*)(uint64_t, uint64_t); + +/// Class representing a single binary operation in the AST of a numeric +/// expression. +class FileCheckASTBinop { +private: + /// Left operand. + std::shared_ptr Opl; + + /// Right operand. + uint64_t Opr; + + /// Pointer to function that can evaluate this binary operation. + binop_eval_t EvalBinop; + +public: + FileCheckASTBinop(binop_eval_t EvalBinop, + std::shared_ptr OperandLeft, + uint64_t OperandRight) + : Opl(OperandLeft), Opr(OperandRight), EvalBinop(EvalBinop) {} + + /// Evaluate the value of the binary operation represented by this AST. Uses + /// EvalBinop to perform the binary operation. + llvm::Optional eval() const; + + /// Return the name of the undefined variable used in this substitution if + /// any. + llvm::Optional getUndefVarName() const; }; class FileCheckPatternContext; @@ -173,6 +240,17 @@ /// back-references are used for uses after any the other definition. StringMap GlobalVariableTable; + /// Map of all pattern variables defined so far. Used at parse time to detect + /// a name conflict between a numeric variable and a pattern variable when + /// the former is defined on a later line than the latter. + StringMap DefinedVariableTable; + + /// When matching a given pattern, this holds the pointers to the classes + /// representing the AST of all numeric variables defined in previous + /// patterns along with their values. In a pattern, only the last definition + /// for a given variable is recorded in this table. + StringMap> GlobalNumericVariableTable; + public: /// Return the value of pattern variable \p VarName or nothing if no such /// variable has been defined. @@ -204,12 +282,12 @@ /// Entries in this vector represent uses of a pattern variable or a numeric /// expression in the pattern that need to be substituted in the regexp - /// pattern at match time, e.g. "foo[[bar]]baz[[#@LINE+1]]". In this case, - /// the RegExStr will contain "foobaz" and we'll get two entries in this - /// vector that tells us to insert the value of pattern variable "bar" at - /// offset 3 and the value of numeric expression "@LINE+1" at offset 6. Uses - /// are represented by a FileCheckPatternSubst class to abstract whether it - /// is a pattern variable or a numeric expression. + /// pattern at match time, e.g. "foo[[bar]]baz[[#N+1]]". In this case, the + /// RegExStr will contain "foobaz" and we'll get two entries in this vector + /// that tells us to insert the value of pattern variable "bar" at offset 3 + /// and the value of numeric expression "N+1" at offset 6. Uses are + /// represented by a FileCheckPatternSubst class to abstract whether it is a + /// pattern variable or a numeric expression. std::vector Substs; /// Maps names of pattern variables defined in a pattern to the parenthesized @@ -223,8 +301,12 @@ /// iterating over values. std::map VariableDefs; - /// Pointer to a class instance holding a table with the values of live - /// variables at the start of any given CHECK line. + /// Pointer to a class instance holding the global state shared by all + /// patterns: + /// - tables with the values of live pattern and numeric variables at + /// the start of any given CHECK line; + /// - table holding whether a pattern variable has been defined at any given + /// point during the parsing phase. FileCheckPatternContext *Context; Check::FileCheckType CheckTy; @@ -246,7 +328,7 @@ static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx); std::shared_ptr - parseNumericExpression(StringRef Name, StringRef Trailer, + parseNumericExpression(StringRef Name, bool IsPseudo, StringRef Trailer, const SourceMgr &SM) const; bool ParsePattern(StringRef PatternStr, StringRef Prefix, SourceMgr &SM, unsigned LineNumber, const FileCheckRequest &Req); 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,13 +24,61 @@ using namespace llvm; +/// Return value of this numeric variable. +llvm::Optional FileCheckNumExprVar::getValue() const { + if (!Defined) + return llvm::None; + return Value; +} + +/// Set value of this numeric variable if not defined. Return whether the +/// variable was already defined. +bool FileCheckNumExprVar::setValue(uint64_t Value) { + if (Defined) + return true; + this->Value = Value; + this->Defined = true; + return false; +} + +/// Clear value of this numeric variable. Return whether value was already +/// undefined. +bool FileCheckNumExprVar::clearValue() { + if (!Defined) + return true; + Defined = false; + return false; +} + +/// Evaluate the value of the binary operation represented by this AST. Uses +/// EvalBinop to perform the binary operation on the values of recursively +/// evaluating the left and right operands. +llvm::Optional FileCheckASTBinop::eval() const { + llvm::Optional OptOpl = Opl->getValue(); + // Variable has been undefined. + if (!OptOpl.hasValue()) + return llvm::None; + return EvalBinop(OptOpl.getValue(), Opr); +} + +/// Return the name of the undefined variable used in this substitution if any. +llvm::Optional FileCheckASTBinop::getUndefVarName() const { + if (!Opl->getValue().hasValue()) + return Opl->getName(); + return llvm::None; +} + /// Return the result of the substitution represented by this class instance or /// nothing if substitution failed. For a numeric variable we substitute it by -/// its value. For a pattern variable we simply replace it by the text its -/// definition matched. +/// its value if known at match time or a suitable wildcard pattern otherwise. +/// For a pattern variable we simply replace it by the text its definition +/// matched. llvm::Optional FileCheckPatternSubst::getSubstitute() const { if (IsNumExpr) { - return utostr(NumExpr->getValue()); + llvm::Optional EvaluatedValue = NumExpr->getAST()->eval(); + if (!EvaluatedValue.hasValue()) + return llvm::None; + return utostr(EvaluatedValue.getValue()); } else { // Look up the value and escape it so that we can put it into the // regex. @@ -44,15 +92,16 @@ /// Return the name of the undefined variable used in this substitution if any. llvm::Optional FileCheckPatternSubst::getUndefVarName() const { - // Parsing guarantees only @LINE is ever referenced and it is not undefined - // by ClearLocalVars. if (IsNumExpr) - return llvm::None; - - if (!Context->getPatternVarValue(SubstStr).hasValue()) - return SubstStr; + // Although use of undefined numeric variable is tested at parse time, + // numeric variable can get undefined later by ClearLocalVariables. + return NumExpr->getAST()->getUndefVarName(); + else { + if (!Context->getPatternVarValue(SubstStr).hasValue()) + return SubstStr; - return llvm::None; + return llvm::None; + } } // Verify that the string in \p Str or at the start of \p Str (if \p @@ -104,35 +153,72 @@ return c; } -/// Parse a numeric expression involving pseudo variable \p Name with the -/// string corresponding to the operation being performed in \p Trailer. -/// Return the class representing the numeric expression or nullptr if -/// parsing fails in which case errors are reported on \p SM. +static uint64_t doAdd(uint64_t Opl, uint64_t Opr) { return Opl + Opr; } +static uint64_t doSub(uint64_t Opl, uint64_t Opr) { return Opl - Opr; } + +/// Parse \p Expr for a numeric expression. Return the class representing the +/// AST of numeric expression or nullptr if parsing fails in which case errors +/// are reported on \p SM. std::shared_ptr -FileCheckPattern::parseNumericExpression(StringRef Name, StringRef Trailer, +FileCheckPattern::parseNumericExpression(StringRef Name, bool IsPseudo, + StringRef Trailer, const SourceMgr &SM) const { - if (!Name.equals("@LINE")) { + std::shared_ptr NumExprAST; + + if (IsPseudo && !Name.equals("@LINE")) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, "Invalid pseudo numeric variable '" + Name + "'"); return nullptr; } - // Check if this is a supported operation and select function to perform it. + // This method is indirectly called from ParsePattern for all numeric + // variable definition and uses in the order in which they appear in the + // CHECK pattern. For each definition, the pointer to the corresponding AST + // class instance is stored in GlobalNumericVariableTable. Therefore the + // pointer we get below is for the AST class instance corresponding to the + // last definition of the variable before this use. + auto git = Context->GlobalNumericVariableTable.find(Name); + if (git == Context->GlobalNumericVariableTable.end()) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "Using undefined numeric variable '" + Name + "'"); + return nullptr; + } + + std::shared_ptr Opl = git->second; + + // Check if this is a supported operation and selection function to perform + // it. SkipWhitespace(Trailer); - if (Trailer.empty()) - return std::make_shared(LineNumber); + if (Trailer.empty()) { + NumExprAST = std::make_shared(doAdd, Opl, 0); + return std::make_shared(NumExprAST); + } SMLoc oploc = SMLoc::getFromPointer(Trailer.data()); char Operator = next(Trailer); + binop_eval_t EvalBinop; + switch (Operator) { + case '+': + EvalBinop = doAdd; + break; + case '-': + EvalBinop = doSub; + break; + default: + SM.PrintMessage(oploc, SourceMgr::DK_Error, + std::string("Unsupported numeric operation '") + Operator + + "'"); + return nullptr; + } // Parse right operand. SkipWhitespace(Trailer); if (Trailer.empty()) { SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, - "Missing operand in numeric expression '" + Trailer + "'"); + "Missing operand in numeric expression"); return nullptr; } - uint64_t Offset; - if (Trailer.consumeInteger(10, Offset)) { + uint64_t Opr; + if (Trailer.consumeInteger(10, Opr)) { SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error, "Invalid offset in numeric expression '" + Trailer + "'"); return nullptr; @@ -145,21 +231,8 @@ return nullptr; } - uint64_t Value; - switch (Operator) { - case '+': - Value = LineNumber + Offset; - break; - case '-': - Value = LineNumber - Offset; - break; - default: - SM.PrintMessage(oploc, SourceMgr::DK_Error, - std::string("Unsupported numeric operation '") + Operator + - "'"); - return nullptr; - } - return std::make_shared(Value); + NumExprAST = std::make_shared(EvalBinop, Opl, Opr); + return std::make_shared(NumExprAST); } /// Parses the given string into the Pattern. @@ -176,6 +249,13 @@ this->LineNumber = LineNumber; PatternLoc = SMLoc::getFromPointer(PatternStr.data()); + // Create fake @LINE pseudo variable definition. + StringRef LinePseudo = "@LINE"; + uint64_t LineNumber64 = LineNumber; + auto LinePseudoVar = + std::make_shared(LinePseudo, LineNumber64); + Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar; + if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines)) // Ignore trailing whitespace. while (!PatternStr.empty() && @@ -304,25 +384,37 @@ StringRef Trailer = MatchStr.substr(TrailIdx); bool IsVarDef = (DefSepIdx != StringRef::npos); - if (IsVarDef && (IsPseudo || !Trailer.consume_front(":"))) { - SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()), - SourceMgr::DK_Error, - "Invalid name in pattern variable definition"); - return true; + 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 collision 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; + } } - if (!IsVarDef && IsPseudo) { - NumExpr = parseNumericExpression(Name, Trailer, SM); + if (IsNumExpr || (!IsVarDef && IsPseudo)) { + NumExpr = parseNumericExpression(Name, IsPseudo, Trailer, SM); if (NumExpr == nullptr) return true; + IsNumExpr = true; } - // Handle [[foo]]. + // 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 (NumExpr == nullptr && - VariableDefs.find(Name) != VariableDefs.end()) { + if (!IsNumExpr && VariableDefs.find(Name) != VariableDefs.end()) { unsigned CaptureParen = VariableDefs[Name]; if (CaptureParen < 1 || CaptureParen > 9) { SM.PrintMessage(SMLoc::getFromPointer(Name.data()), @@ -335,7 +427,7 @@ // Handle use of pattern variables ([[]]) defined in previous // CHECK pattern or use of a numeric expression. FileCheckPatternSubst Subst = - NumExpr != nullptr + IsNumExpr ? FileCheckPatternSubst(Context, MatchStr, NumExpr, SubstInsertIdx) : FileCheckPatternSubst(Context, MatchStr, SubstInsertIdx); @@ -346,6 +438,12 @@ // Handle [[foo:.*]]. VariableDefs[Name] = CurParen; + // Mark pattern variable as defined to detect collision between pattern + // and numeric variable in DefineCmdlineVariables when the latter is + // created later than the former. We cannot reuse GlobalVariableTable for + // that by populating it with an empty string since we would then loose + // the ability to detect use of undefined variable in Match(). + Context->DefinedVariableTable[Name] = true; RegExStr += '('; ++CurParen; @@ -1512,26 +1610,99 @@ bool ErrorFound = false; for (const auto &CmdlineDef : CmdlineDefines) { - std::string Prefix = "-D"; - std::string DiagCmdline = Prefix + CmdlineDef; - std::unique_ptr CmdLine = - MemoryBuffer::getMemBufferCopy(DiagCmdline, "command line"); - StringRef CmdlineDefRef = CmdLine->getBuffer().substr(Prefix.size()); - SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + // Numeric variable definition. + if (CmdlineDef[0] == '#') { + // Create a buffer with fake command line content in order to display + // parsing diagnostic with location information and point to the + // command-line option with invalid syntax. + std::string Prefix = "-D"; + std::string DiagCmdline = Prefix + CmdlineDef; + std::unique_ptr CmdLine = + MemoryBuffer::getMemBufferCopy(DiagCmdline, "command line"); + StringRef DiagCmdlineDefRef = CmdLine->getBuffer().substr(Prefix.size()); + SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + + // Parse numeric variable definition. + bool IsPseudo; + unsigned TrailIdx; + size_t EqIdx = CmdlineDef.find('='); + StringRef CmdlineName = DiagCmdlineDefRef.substr(1, EqIdx - 1); + if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) || + IsPseudo || TrailIdx != CmdlineName.size()) { + SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()), + SourceMgr::DK_Error, + "Invalid name in numeric variable definition '" + + CmdlineName + "'"); + ErrorFound = true; + continue; + } - bool IsPseudo; - unsigned TrailIdx; - std::pair CmdlineNameVal = CmdlineDefRef.split('='); - StringRef Name = CmdlineNameVal.first; - if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) || IsPseudo || - TrailIdx != Name.size()) { - SM.PrintMessage(SMLoc::getFromPointer(CmdlineDefRef.data()), - SourceMgr::DK_Error, - "Invalid name for variable definition '" + Name + "'"); - ErrorFound = true; - continue; + // Detect collision between pattern and numeric variable 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"); + ErrorFound = true; + continue; + } + + StringRef CmdlineVal = DiagCmdlineDefRef.substr(EqIdx + 1); + uint64_t Val; + if (CmdlineVal.getAsInteger(10, Val)) { + SM.PrintMessage(SMLoc::getFromPointer(CmdlineVal.data()), + SourceMgr::DK_Error, + "Invalid value in numeric variable definition '" + + CmdlineVal + "'"); + ErrorFound = true; + continue; + } + std::shared_ptr NumVarDef = + std::make_shared(CmdlineName, Val); + + // Record this variable definition. + GlobalNumericVariableTable[CmdlineName] = NumVarDef; + } else { + // Pattern variable definition + std::string Prefix = "-D"; + std::string DiagCmdline = Prefix + CmdlineDef; + std::unique_ptr CmdLine = + MemoryBuffer::getMemBufferCopy(DiagCmdline, "command line"); + StringRef CmdlineDefRef = CmdLine->getBuffer().substr(Prefix.size()); + SM.AddNewSourceBuffer(std::move(CmdLine), SMLoc()); + + bool IsPseudo; + unsigned TrailIdx; + std::pair CmdlineNameVal = CmdlineDefRef.split('='); + StringRef Name = CmdlineNameVal.first; + if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) || + IsPseudo || TrailIdx != Name.size()) { + SM.PrintMessage( + SMLoc::getFromPointer(CmdlineDefRef.data()), SourceMgr::DK_Error, + "Invalid name in pattern variable definition '" + Name + "'"); + ErrorFound = true; + continue; + } + + // Detect collision between pattern and numeric variable when the former + // is created later than the latter. + if (GlobalNumericVariableTable.find(Name) != + GlobalNumericVariableTable.end()) { + SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "Numeric variable with name '" + Name + + "' already exists"); + ErrorFound = true; + continue; + } + GlobalVariableTable.insert(CmdlineNameVal); + // Mark pattern variable as defined to detect collision between pattern + // and numeric variable in DefineCmdlineVariables when the latter is + // created later than the former. We cannot reuse GlobalVariableTable for + // that by populating it with an empty string since we would then loose + // the ability to detect use of undefined variable in Match(). + DefinedVariableTable[Name] = true; } - GlobalVariableTable.insert(CmdlineNameVal); } return ErrorFound; @@ -1544,6 +1715,13 @@ for (const auto &Var : GlobalVariableTable) if (Var.first()[0] != '$') LocalPatternVars.push_back(Var.first()); + // Numeric expression substitution read the value of variable directly, not + // via GlobalNUmericVariableTable. Therefore we clear local variable by + // clearing their value which will lead to an numeric expression substitution + // failure. + for (const auto &Var : GlobalNumericVariableTable) + if (Var.first()[0] != '$') + Var.getValue()->clearValue(); for (const auto &Var : LocalPatternVars) GlobalVariableTable.erase(Var); @@ -1585,7 +1763,10 @@ ++j; } - if (Req.EnableVarScope && Context != nullptr) + // Do not clear the first region as it's the one before the first + // CHECK-LABEL and it would clear variable defined on the command-line + // before they get used. + if (i != 0 && Req.EnableVarScope && Context != nullptr) Context->clearLocalVars(); for (; i != j; ++i) { diff --git a/llvm/test/FileCheck/defines.txt b/llvm/test/FileCheck/defines.txt --- a/llvm/test/FileCheck/defines.txt +++ b/llvm/test/FileCheck/defines.txt @@ -11,6 +11,16 @@ ; RUN: not FileCheck -D10VALUE=10 -input-file %s %s 2>&1 | FileCheck %s --strict-whitespace -check-prefix ERRCLIFMT ; RUN: not FileCheck -D@VALUE=10 -input-file %s %s 2>&1 | FileCheck %s --strict-whitespace -check-prefix ERRCLIPSEUDO ; RUN: not FileCheck -D'VALUE + 2=10' -input-file %s %s 2>&1 | FileCheck %s --strict-whitespace -check-prefix ERRCLITRAIL + +; 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 | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG +; RUN: not FileCheck -D#NUMVAL=12 -check-prefix NUMNOT -input-file %s %s 2>&1 | FileCheck %s --strict-whitespace -check-prefix NOT-NUMERRMSG +; RUN: FileCheck -D#NUMVAL=8 -check-prefixes NUMNOT -input-file %s %s +; RUN: not FileCheck -D#10VALUE=10 -input-file %s %s 2>&1 | FileCheck %s --strict-whitespace -check-prefix NUMERRCLIFMT +; RUN: not FileCheck -D#@VALUE=10 -input-file %s %s 2>&1 | FileCheck %s --strict-whitespace -check-prefix NUMERRCLIPSEUDO +; RUN: not FileCheck -D#'VALUE + 2=10' -input-file %s %s 2>&1 | FileCheck %s --strict-whitespace -check-prefix NUMERRCLITRAIL +; RUN: not FileCheck -D#VALUE1=3 -D#VALUE2='VALUE1 + 2' -input-file %s %s 2>&1 | FileCheck %s --strict-whitespace -check-prefix NUMERRCLIEXPR + Value = 10 ; CHECK: Value = [[VALUE]] ; NOT-NOT: Value = [[VALUE]] @@ -35,14 +45,43 @@ Empty value = @@ ; EMPTY: Empty value = @[[VALUE]]@ -; ERRCLIFMT: command line:1:3: error: Invalid name for variable definition '10VALUE' +; ERRCLIFMT: command line:1:3: error: Invalid name in pattern variable definition '10VALUE' ; ERRCLIFMT-NEXT: -D10VALUE=10 ; ERRCLIFMT-NEXT: {{^ \^$}} -; ERRCLIPSEUDO: command line:1:3: error: Invalid name for variable definition '@VALUE' +; ERRCLIPSEUDO: command line:1:3: error: Invalid name in pattern variable definition '@VALUE' ; ERRCLIPSEUDO-NEXT: -D@VALUE=10 ; ERRCLIPSEUDO-NEXT: {{^ \^$}} -; ERRCLITRAIL: command line:1:3: error: Invalid name for variable definition 'VALUE + 2' +; ERRCLITRAIL: command line:1:3: error: Invalid name in pattern variable definition 'VALUE + 2' ; ERRCLITRAIL-NEXT: -DVALUE + 2=10 ; ERRCLITRAIL-NEXT: {{^ \^$}} + +Numeric value #2 = 12 +; CHECKNUM: Numeric value #2 = [[#NUMVAL]] +; NUMNOT-NOT: Numeric value #2 = [[#NUMVAL]] + +; NUMERRMSG: defines.txt:[[#@LINE-3]]:13: 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:[[#@LINE-7]]:1: note: possible intended match here + +; NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:15: 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" + +; NUMERRCLIFMT: command line:1:4: error: Invalid name in numeric variable definition '10VALUE' +; NUMERRCLIFMT-NEXT: -D#10VALUE=10 +; NUMERRCLIFMT-NEXT: {{^ \^$}} + +; NUMERRCLIPSEUDO: command line:1:4: error: Invalid name in numeric variable definition '@VALUE' +; NUMERRCLIPSEUDO-NEXT: -D#@VALUE=10 +; NUMERRCLIPSEUDO-NEXT: {{^ \^$}} + +; NUMERRCLITRAIL: command line:1:4: error: Invalid name in numeric variable definition 'VALUE + 2' +; NUMERRCLITRAIL-NEXT: -D#VALUE + 2=10 +; NUMERRCLITRAIL-NEXT: {{^ \^$}} + +; NUMERRCLIEXPR: command line:1:11: error: Invalid value in numeric variable definition 'VALUE1 + 2' +; NUMERRCLIEXPR-NEXT: -D#VALUE2=VALUE1 + 2 +; NUMERRCLIEXPR-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt new file mode 100644 --- /dev/null +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -0,0 +1,95 @@ +; RUN: FileCheck -D#VAR1=11 -input-file %s %s +; RUN: not FileCheck -check-prefix UNDEF-USE -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix UNDEF-USE-MSG %s +; RUN: not FileCheck -D#VAR1=11 -check-prefixes CHECK,INVAL-OP -input-file %s %s 2>&1 | FileCheck --strict-whitespace -check-prefix INVAL-OP-MSG %s +; RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 -check-prefixes CONFLICT,CONFLICT1 -input-file %s %s 2>&1 | 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 | 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 | FileCheck --strict-whitespace -check-prefix CLI-CLI-NUM-CONFLICT %s + + +; We ensure we attemt to match all lines with digits by using CHECK-NEXT +; directives for the checks to ensure each line of input is covered. + + +; Numeric expressions using variables defined on the command-line without +; spaces +USE NO SPC +11 +12 +10 +; CHECK-LABEL: USE NO SPC +; CHECK-NEXT: [[#VAR1]] +; CHECK-NEXT: [[#VAR1+1]] +; CHECK-NEXT: [[#VAR1-1]] + + +; Numeric expressions using variables defined on the command-line in alternate +; spacing +USE ALT SPC +11 +11 +12 +12 +12 +12 +10 +10 +10 +10 +; CHECK-LABEL: USE ALT SPC +; CHECK-NEXT: [[# VAR1]] +; CHECK-NEXT: [[# VAR1 ]] +; CHECK-NEXT: [[# VAR1+1]] +; CHECK-NEXT: [[# VAR1 +1]] +; CHECK-NEXT: [[# VAR1 + 1]] +; CHECK-NEXT: [[# VAR1 + 1 ]] +; CHECK-NEXT: [[# VAR1-1]] +; CHECK-NEXT: [[# VAR1 -1]] +; CHECK-NEXT: [[# VAR1 - 1]] +; CHECK-NEXT: [[# VAR1 - 1 ]] + + +; Numeric expressions using variables defined on the command-line and an +; immediate interpreted as an unsigned value +; Note: 9223372036854775819 = 0x8000000000000000 + 11 +; 9223372036854775808 = 0x8000000000000000 +USE UNSIGNED IMM +9223372036854775819 +; CHECK-LABEL: USE UNSIGNED IMM +; CHECK-NEXT: [[#VAR1+9223372036854775808]] + + +; Numeric expression using undefined variable +UNDEF VAR USE +UNDEFVAR: 11 +; UNDEF-USE-LABEL: UNDEF VAR USE +; UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR]] +; UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:32: error: Using undefined numeric variable 'UNDEFVAR' +; UNDEF-USE-MSG-NEXT: ; {{U}}NDEF-USE-NEXT: UNDEFVAR: {{\[\[#UNDEFVAR\]\]}} +; UNDEF-USE-MSG-NEXT: {{^ \^$}} + + +; Numeric expression with unsupported operator +INVALID OPERATOR +VAR1*2: 22 +; INVAL-OP-LABEL: INVALID OPERATOR +; INVAL-OP-NEXT: VAR1*2: [[#VAR1*2]] +; INVAL-OP-MSG: numeric-expression.txt:[[#@LINE-1]]:33: error: Unsupported numeric operation '*' +; INVAL-OP-MSG-NEXT: ; {{I}}NVAL-OP-NEXT: VAR1*2: {{\[\[#VAR1\*2\]\]}} +; INVAL-OP-MSG-NEXT: {{^ \^$}} + + +; Name conflict between Numeric variable definition and pattern variable +; definition +PATVAR NUMVAR CONFLICT +foobar +; CONFLICT-LABEL: PATVAR NUMVAR CONFLICT +; CONFLICT1-NEXT: [[NUMVAR:foo.*]] +; CLI-INPUT-PAT-CONFLICT: numeric-expression.txt:[[#@LINE-1]]:21: 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: command line:1:3: error: Numeric variable with name 'NUMVAR' already exists +; CLI-CLI-PAT-CONFLICT-NEXT: -DNUMVAR=foobar +; CLI-CLI-PAT-CONFLICT-NEXT: {{^ \^$}} +; CLI-CLI-NUM-CONFLICT: command line:1:4: error: Pattern variable with name 'PATVAR' already exists +; CLI-CLI-NUM-CONFLICT-NEXT: -D#PATVAR=42 +; CLI-CLI-NUM-CONFLICT-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/regex-scope.txt b/llvm/test/FileCheck/regex-scope.txt deleted file mode 100644 --- a/llvm/test/FileCheck/regex-scope.txt +++ /dev/null @@ -1,23 +0,0 @@ -// RUN: FileCheck -input-file %s %s -// RUN: FileCheck -check-prefixes CHECK,GLOBAL -input-file %s %s -// RUN: FileCheck -check-prefixes CHECK,LOCAL -input-file %s %s -// RUN: FileCheck -check-prefixes CHECK,GLOBAL --enable-var-scope -input-file %s %s -// RUN: not FileCheck -check-prefixes CHECK,LOCAL --enable-var-scope -input-file %s %s - -local -global -; CHECK: [[LOCAL:loc.*]] -; CHECK: [[$GLOBAL:glo.*]] - -local2 -global2 -; CHECK: [[LOCAL]]2 -; CHECK: [[$GLOBAL]]2 - -barrier: -; CHECK-LABEL: barrier - -local3 -global3 -; LOCAL: [[LOCAL]]3 -; GLOBAL: [[$GLOBAL]]3 diff --git a/llvm/test/FileCheck/var-scope.txt b/llvm/test/FileCheck/var-scope.txt new file mode 100644 --- /dev/null +++ b/llvm/test/FileCheck/var-scope.txt @@ -0,0 +1,27 @@ +// RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' -input-file %s %s +// RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' -check-prefixes CHECK,GLOBAL -input-file %s %s +// RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' -check-prefixes CHECK,LOCAL3 -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 +// RUN: not FileCheck -D#LOCNUM=1 -D#$GLOBNUM=1 -check-prefixes CHECK,LOCAL2 --enable-var-scope -input-file %s %s +// RUN: not FileCheck -D#LOCNUM=1 -D#$GLOBNUM=1 -check-prefixes CHECK,LOCAL3 --enable-var-scope -input-file %s %s + +local1 +global1 +; CHECK: [[LOCAL:loc[^[:digit:]]*]][[#LOCNUM]] +; CHECK: [[$GLOBAL:glo[^[:digit:]]*]][[#$GLOBNUM]] + +local2 +global2 +; CHECK: [[LOCAL]][[#LOCNUM+1]] +; CHECK: [[$GLOBAL]][[#$GLOBNUM+1]] + +barrier: +; CHECK-LABEL: barrier + +local3 +global3 +; LOCAL1: [[LOCAL]]3 +; LOCAL2: local[[#LOCNUM+2]] +; LOCAL3: [[LOCAL]][[#LOCNUM+2]] +; GLOBAL: [[$GLOBAL]][[#$GLOBNUM+2]] 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 -input-file %s %s 2>&1 | FileCheck -check-prefix QUIET --allow-empty %s -; RUN: FileCheck -v -input-file %s %s 2>&1 | FileCheck -check-prefix V %s -; RUN: FileCheck -vv -input-file %s %s 2>&1 | FileCheck -check-prefixes V,VV %s +; 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 foo bar @@ -29,6 +29,39 @@ VV-NEXT: {{^}}bar{{$}} VV-NEXT: {{^}}^{{$}} +NUMVAR:42 +NUMVAR - 1:41 +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: {{^ \^$}} +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: {{^}}^~~~~~~~~{{$}} + +V-NEXT: verbose.txt:[[#@LINE-12]]: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: {{^}}NUMVAR - 1:41{{$}} +V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} +V-NEXT: verbose.txt:[[#@LINE-21]]: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: {{C}}HECK-NOT: {{[[][[]#NUMVAR [+] 1[]][]]$}} +VV-NEXT: {{^ \^$}} +VV-NEXT: verbose.txt:[[#@LINE-28]]:1: note: scanning from here +VV-NEXT: {{^}}NUMVAR - 1:41{{$}} +VV-NEXT: {{^}}^{{$}} + before empty after empty @@ -99,7 +132,7 @@ VV-NEXT: verbose.txt:[[@LINE-9]]:19: remark: implicit EOF: expected string found in input VV-NEXT: {{C}}HECK-NOT: {{[{][{]z[}][}]yx$}} -VV-NEXT: {{^ \^$}} +VV-NEXT: {{^ \^$}} VV-NEXT: verbose.txt:[[@LINE+13]]:1: note: found here VV-NOT: {{.}} VV: {{^\^$}}