[llvm] r360665 - Reinstate "FileCheck [5/12]: Introduce regular numeric variables"
Thomas Preud'homme via llvm-commits
llvm-commits at lists.llvm.org
Tue May 14 04:58:30 PDT 2019
Author: thopre
Date: Tue May 14 04:58:30 2019
New Revision: 360665
URL: http://llvm.org/viewvc/llvm-project?rev=360665&view=rev
Log:
Reinstate "FileCheck [5/12]: Introduce regular numeric variables"
This reinstates r360578 (git e47362c1ec1ea31b626336cc05822035601c3e57),
reverted in r360653 (git 004393681c25e34e921adccc69ae6378090dee54),
with a fix for the list added in FileCheck.rst to build without error.
Copyright:
- Linaro (changes up to diff 183612 of revision D55940)
- GraphCore (changes in later versions of revision D55940 and
in new revision created off D55940)
Reviewers: jhenderson, chandlerc, jdenny, probinson, grimar,
arichardson, rnk
Subscribers: hiraditya, llvm-commits, probinson, dblaikie, grimar,
arichardson, tra, rnk, kristina, hfinkel, rogfer01, JonChesterfield
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D60385
Added:
llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt
llvm/trunk/test/FileCheck/numeric-defines.txt
llvm/trunk/test/FileCheck/numeric-expression.txt
llvm/trunk/test/FileCheck/var-scope.txt
Removed:
llvm/trunk/test/FileCheck/regex-scope.txt
Modified:
llvm/trunk/docs/CommandGuide/FileCheck.rst
llvm/trunk/include/llvm/Support/FileCheck.h
llvm/trunk/lib/Support/FileCheck.cpp
llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt
llvm/trunk/test/FileCheck/verbose.txt
llvm/trunk/unittests/Support/FileCheckTest.cpp
Modified: llvm/trunk/docs/CommandGuide/FileCheck.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/CommandGuide/FileCheck.rst?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/docs/CommandGuide/FileCheck.rst (original)
+++ llvm/trunk/docs/CommandGuide/FileCheck.rst Tue May 14 04:58:30 2019
@@ -105,6 +105,11 @@ and from the command line.
Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
used in ``CHECK:`` lines.
+.. option:: -D#<NUMVAR>=<VALUE>
+
+ Sets a filecheck numeric variable ``NUMVAR`` to ``<VALUE>`` that can be used
+ in ``CHECK:`` lines.
+
.. option:: -version
Show the version number of this program.
@@ -560,8 +565,51 @@ CHECK-LABEL block. Global variables are
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 checking for numeric values that satisfy a
+numeric expression constraint based on numeric variables. This allows
+``CHECK:`` directives to verify a numeric relation between two numbers, such as
+the need for consecutive registers to be used.
+
+The syntax to check a numeric expression constraint is
+``[[#<NUMVAR><op><offset>]]`` where:
+
+* ``<NUMVAR>`` is the name of a numeric variable defined on the command line.
+
+* ``<op>`` is an optional numeric operation to perform on the value of
+ ``<NUMVAR>``. Currently supported numeric operations are ``+`` and ``-``.
+
+* ``<offset>`` is the immediate value that constitutes the second operand of
+ the numeric operation <op>. It must be present if ``<op>`` is present,
+ absent otherwise.
+
+For example:
+
+.. code-block:: llvm
+
+ ; CHECK: add r[[#REG]], r[[#REG]], r[[#REG+1]]
+
+The above example would match the line:
+
+.. code-block:: llvm
+
+ add r5, r5, r6
+
+but would not match the line:
+
+.. code-block:: llvm
+
+ add r5, r5, r7
+
+due to ``7`` being unequal to ``5 + 1``.
+
+The ``--enable-var-scope`` option has the same effect on numeric variables as
+on pattern variables.
+
+FileCheck Pseudo Numeric Variables
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes there's a need to verify output that contains line numbers of the
match file, e.g. when testing compiler diagnostics. This introduces a certain
@@ -569,11 +617,9 @@ fragility of the match file structure, a
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+<offset>]]`` and ``[[#@LINE-<offset>]]`` numeric expressions in
-patterns, with an arbitrary number of spaces between each element of the
-expression. These expressions expand to the 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 evaluates to the line number of the CHECK pattern where it is
+found.
This way match patterns can be put near the relevant test lines and include
relative line number references, for example:
Modified: llvm/trunk/include/llvm/Support/FileCheck.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Support/FileCheck.h?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/include/llvm/Support/FileCheck.h (original)
+++ llvm/trunk/include/llvm/Support/FileCheck.h Tue May 14 04:58:30 2019
@@ -40,20 +40,67 @@ struct FileCheckRequest {
// Numeric expression handling code.
//===----------------------------------------------------------------------===//
-/// Class representing a numeric expression.
+/// Class representing a numeric variable with a given value in a numeric
+/// expression.
+class FileCheckNumericVariable {
+private:
+ /// Name of the numeric variable.
+ StringRef Name;
+
+ /// Value of numeric variable, if defined, or None otherwise.
+ llvm::Optional<uint64_t> Value;
+
+public:
+ /// 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) {}
+
+ /// \returns name of that numeric variable.
+ StringRef getName() const { return Name; }
+
+ /// \returns value of this numeric variable.
+ llvm::Optional<uint64_t> getValue() const { return Value; }
+
+ /// Sets value of this numeric variable if not defined. \returns whether the
+ /// variable was already defined.
+ bool setValue(uint64_t Value);
+
+ /// Clears value of this numeric variable. \returns whether the variable 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 numeric expression consisting of either a single
+/// numeric variable or a binary operation between a numeric variable and an
+/// immediate.
class FileCheckNumExpr {
private:
- /// Value of the numeric expression.
- uint64_t Value;
+ /// Left operand.
+ FileCheckNumericVariable *LeftOp;
+
+ /// Right operand.
+ uint64_t RightOp;
+
+ /// Pointer to function that can evaluate this binary operation.
+ binop_eval_t EvalBinop;
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) {}
+ FileCheckNumExpr(binop_eval_t EvalBinop,
+ FileCheckNumericVariable *OperandLeft, uint64_t OperandRight)
+ : LeftOp(OperandLeft), RightOp(OperandRight), EvalBinop(EvalBinop) {}
+
+ /// Evaluates the value of this numeric expression, using EvalBinop to
+ /// perform the binary operation it consists of. \returns None if the numeric
+ /// variable used is undefined, or the expression value otherwise.
+ llvm::Optional<uint64_t> eval() const;
- /// \returns the value being matched against.
- uint64_t getValue() const { return Value; }
+ /// \returns the name of the undefined variable used in this expression if
+ /// any or an empty string otherwise.
+ StringRef getUndefVarName() const;
};
class FileCheckPatternContext;
@@ -105,9 +152,9 @@ public:
size_t getIndex() const { return InsertIdx; }
/// \returns the result of the substitution represented by this class
- /// instance or None if substitution failed. For a numeric expression we
- /// substitute it by its value. For a pattern variable we simply replace it
- /// by the text its definition matched.
+ /// instance or None if substitution failed. Numeric expressions are
+ /// substituted by their values. Pattern variables are simply replaced by the
+ /// text their definition matched.
llvm::Optional<std::string> getResult() const;
/// \returns the name of the undefined variable used in this substitution, if
@@ -175,19 +222,35 @@ private:
/// Back-references are used for uses after any the other definition.
StringMap<StringRef> 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<bool> DefinedVariableTable;
+
+ /// When matching a given pattern, this holds the pointers to the classes
+ /// representing the last definitions of numeric variables defined in
+ /// previous patterns. Earlier definition of the variables, if any, have
+ /// their own class instance not referenced by this table.
+ StringMap<FileCheckNumericVariable *> GlobalNumericVariableTable;
+
/// Vector holding pointers to all parsed numeric expressions. Used to
/// automatically free the numeric expressions once they are guaranteed to no
/// longer be used.
std::vector<std::unique_ptr<FileCheckNumExpr>> NumExprs;
+ /// Vector holding pointers to all parsed numeric variables. Used to
+ /// automatically free them once they are guaranteed to no longer be used.
+ std::vector<std::unique_ptr<FileCheckNumericVariable>> NumericVariables;
+
public:
/// \returns the value of pattern variable \p VarName or None if no such
/// variable has been defined.
llvm::Optional<StringRef> getPatternVarValue(StringRef VarName);
- /// Defines pattern variables from definitions given on the command line,
- /// passed as a vector of VAR=VAL strings in \p CmdlineDefines. Reports any
- /// error to \p SM and \returns whether an error occured.
+ /// Defines pattern and numeric variables from definitions given on the
+ /// command line, passed as a vector of [#]VAR=VAL strings in
+ /// \p CmdlineDefines. Reports any error to \p SM and \returns whether an
+ /// error occured.
bool defineCmdlineVariables(std::vector<std::string> &CmdlineDefines,
SourceMgr &SM);
@@ -198,7 +261,13 @@ public:
private:
/// Makes a new numeric expression instance and registers it for destruction
/// when the context is destroyed.
- template <class... Types> FileCheckNumExpr *makeNumExpr(Types... Args);
+ FileCheckNumExpr *makeNumExpr(binop_eval_t EvalBinop,
+ FileCheckNumericVariable *OperandLeft,
+ uint64_t OperandRight);
+
+ /// Makes a new numeric variable and registers it for destruction when the
+ /// context is destroyed.
+ FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value);
};
class FileCheckPattern {
@@ -214,12 +283,12 @@ class FileCheckPattern {
/// 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 FileCheckPatternSubstitution 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 FileCheckPatternSubstitution class to abstract whether
+ /// it is a pattern variable or a numeric expression.
std::vector<FileCheckPatternSubstitution> Substitutions;
/// Maps names of pattern variables defined in a pattern to the parenthesized
@@ -233,8 +302,12 @@ class FileCheckPattern {
/// iterating over values.
std::map<StringRef, unsigned> VariableDefs;
- /// Pointer to the class instance shared by all patterns 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:
+ /// - separate tables with the values of live pattern and numeric variables
+ /// respectively 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;
@@ -262,11 +335,13 @@ public:
/// character that is part of the variable name. Otherwise, only
/// \returns true.
static bool parseVariable(StringRef Str, bool &IsPseudo, unsigned &TrailIdx);
- /// Parses a numeric expression involving pseudo variable \p Name with the
- /// string corresponding to the operation being performed in \p Trailer.
- /// \returns the class representing the numeric expression or nullptr if
- /// parsing fails in which case errors are reported on \p SM.
- FileCheckNumExpr *parseNumericExpression(StringRef Name, StringRef Trailer,
+ /// Parses a numeric expression involving (pseudo if \p IsPseudo is true)
+ /// variable \p Name with the string corresponding to the operation being
+ /// performed in \p Trailer. \returns the class representing the numeric
+ /// expression or nullptr if parsing fails in which case errors are reported
+ /// on \p SM.
+ FileCheckNumExpr *parseNumericExpression(StringRef Name, bool IsPseudo,
+ StringRef Trailer,
const SourceMgr &SM) const;
/// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
/// instance accordingly.
Modified: llvm/trunk/lib/Support/FileCheck.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Support/FileCheck.cpp?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/lib/Support/FileCheck.cpp (original)
+++ llvm/trunk/lib/Support/FileCheck.cpp Tue May 14 04:58:30 2019
@@ -24,24 +24,54 @@
using namespace llvm;
+bool FileCheckNumericVariable::setValue(uint64_t NewValue) {
+ if (Value)
+ return true;
+ Value = NewValue;
+ return false;
+}
+
+bool FileCheckNumericVariable::clearValue() {
+ if (!Value)
+ return true;
+ Value = llvm::None;
+ return false;
+}
+
+llvm::Optional<uint64_t> FileCheckNumExpr::eval() const {
+ llvm::Optional<uint64_t> LeftOp = this->LeftOp->getValue();
+ // Variable is undefined.
+ if (!LeftOp)
+ return llvm::None;
+ return EvalBinop(*LeftOp, RightOp);
+}
+
+StringRef FileCheckNumExpr::getUndefVarName() const {
+ if (!LeftOp->getValue())
+ return LeftOp->getName();
+ return StringRef();
+}
+
llvm::Optional<std::string> FileCheckPatternSubstitution::getResult() const {
if (IsNumExpr) {
- return utostr(NumExpr->getValue());
- } else {
- // Look up the value and escape it so that we can put it into the
- // regex.
- llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
- if (!VarVal)
+ llvm::Optional<uint64_t> EvaluatedValue = NumExpr->eval();
+ if (!EvaluatedValue)
return llvm::None;
- return Regex::escape(*VarVal);
+ return utostr(*EvaluatedValue);
}
+
+ // Look up the value and escape it so that we can put it into the regex.
+ llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
+ if (!VarVal)
+ return llvm::None;
+ return Regex::escape(*VarVal);
}
StringRef FileCheckPatternSubstitution::getUndefVarName() const {
- // Parsing guarantees only @LINE is ever referenced and it is not undefined
- // by ClearLocalVars.
if (IsNumExpr)
- return StringRef();
+ // Although a use of an undefined numeric variable is detected at parse
+ // time, a numeric variable can be undefined later by ClearLocalVariables.
+ return NumExpr->getUndefVarName();
if (!Context->getPatternVarValue(FromStr))
return FromStr;
@@ -91,31 +121,70 @@ static char popFront(StringRef &S) {
return C;
}
+static uint64_t add(uint64_t LeftOp, uint64_t RightOp) {
+ return LeftOp + RightOp;
+}
+static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
+ return LeftOp - RightOp;
+}
+
FileCheckNumExpr *
-FileCheckPattern::parseNumericExpression(StringRef Name, StringRef Trailer,
+FileCheckPattern::parseNumericExpression(StringRef Name, bool IsPseudo,
+ StringRef Trailer,
const SourceMgr &SM) const {
- if (!Name.equals("@LINE")) {
+ 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 definitions and uses in the order in which they appear in the
+ // CHECK pattern. For each definition, the pointer to the class instance of
+ // the corresponding numeric variable definition is stored in
+ // GlobalNumericVariableTable. Therefore, the pointer we get below is for the
+ // class instance corresponding to the last definition of this variable use.
+ auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
+ if (VarTableIter == Context->GlobalNumericVariableTable.end()) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
+ "using undefined numeric variable '" + Name + "'");
+ return nullptr;
+ }
+
+ FileCheckNumericVariable *LeftOp = VarTableIter->second;
+
+ // Check if this is a supported operation and select a function to perform
+ // it.
Trailer = Trailer.ltrim(SpaceChars);
- if (Trailer.empty())
- return Context->makeNumExpr(LineNumber);
+ if (Trailer.empty()) {
+ return Context->makeNumExpr(add, LeftOp, 0);
+ }
SMLoc OpLoc = SMLoc::getFromPointer(Trailer.data());
char Operator = popFront(Trailer);
+ binop_eval_t EvalBinop;
+ switch (Operator) {
+ case '+':
+ EvalBinop = add;
+ break;
+ case '-':
+ EvalBinop = sub;
+ break;
+ default:
+ SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
+ Twine("unsupported numeric operation '") + Twine(Operator) +
+ "'");
+ return nullptr;
+ }
// Parse right operand.
Trailer = Trailer.ltrim(SpaceChars);
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 RightOp;
+ if (Trailer.consumeInteger(10, RightOp)) {
SM.PrintMessage(SMLoc::getFromPointer(Trailer.data()), SourceMgr::DK_Error,
"invalid offset in numeric expression '" + Trailer + "'");
return nullptr;
@@ -128,31 +197,24 @@ FileCheckPattern::parseNumericExpression
return nullptr;
}
- uint64_t Value;
- switch (Operator) {
- case '+':
- Value = LineNumber + Offset;
- break;
- case '-':
- Value = LineNumber - Offset;
- break;
- default:
- SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
- Twine("unsupported numeric operation '") + Twine(Operator) +
- "'");
- return nullptr;
- }
- return Context->makeNumExpr(Value);
+ return Context->makeNumExpr(EvalBinop, LeftOp, RightOp);
}
bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
- SourceMgr &SM, unsigned LineNumber,
- const FileCheckRequest &Req) {
+ SourceMgr &SM, unsigned LineNumber,
+ const FileCheckRequest &Req) {
bool MatchFullLinesHere = Req.MatchFullLines && CheckTy != Check::CheckNot;
this->LineNumber = LineNumber;
PatternLoc = SMLoc::getFromPointer(PatternStr.data());
+ // Create fake @LINE pseudo variable definition.
+ StringRef LinePseudo = "@LINE";
+ uint64_t LineNumber64 = LineNumber;
+ FileCheckNumericVariable *LinePseudoVar =
+ Context->makeNumericVariable(LinePseudo, LineNumber64);
+ Context->GlobalNumericVariableTable[LinePseudo] = LinePseudoVar;
+
if (!(Req.NoCanonicalizeWhiteSpace && Req.MatchFullLines))
// Ignore trailing whitespace.
while (!PatternStr.empty() &&
@@ -230,10 +292,10 @@ bool FileCheckPattern::ParsePattern(Stri
// forms: [[foo:.*]] and [[foo]]. The former matches .* (or some other
// regex) and assigns it to the FileCheck variable 'foo'. The latter
// substitutes foo's value. Numeric expressions start with a '#' sign after
- // the double brackets and only have the substitution form. Pattern
- // variables must satisfy the regular expression "[a-zA-Z_][0-9a-zA-Z_]*"
- // to be valid, as this helps catch some common errors. Numeric expressions
- // only support the @LINE pseudo numeric variable.
+ // the double brackets and only have the substitution form. Both pattern
+ // and numeric variables must satisfy the regular expression
+ // "[a-zA-Z_][0-9a-zA-Z_]*" to be valid, as this helps catch some common
+ // errors.
if (PatternStr.startswith("[[")) {
StringRef UnparsedPatternStr = PatternStr.substr(2);
// Find the closing bracket pair ending the match. End is going to be an
@@ -283,21 +345,33 @@ bool FileCheckPattern::ParsePattern(Stri
StringRef Trailer = MatchStr.substr(TrailIdx);
bool IsVarDef = (VarEndIdx != 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 collisions between pattern and numeric variables when the
+ // former is created later than the latter.
+ if (Context->GlobalNumericVariableTable.find(Name) !=
+ Context->GlobalNumericVariableTable.end()) {
+ SM.PrintMessage(
+ SMLoc::getFromPointer(MatchStr.data()), SourceMgr::DK_Error,
+ "numeric variable with name '" + Name + "' already exists");
+ return true;
+ }
}
- 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 [[#<foo expr>]].
if (!IsVarDef) {
// Handle use of pattern variables that were defined earlier on the
// same line by emitting a backreference.
@@ -566,12 +640,22 @@ FileCheckPatternContext::getPatternVarVa
return VarIter->second;
}
-template <class... Types>
-FileCheckNumExpr *FileCheckPatternContext::makeNumExpr(Types... Args) {
- NumExprs.emplace_back(new FileCheckNumExpr(Args...));
+FileCheckNumExpr *
+FileCheckPatternContext::makeNumExpr(binop_eval_t EvalBinop,
+ FileCheckNumericVariable *OperandLeft,
+ uint64_t OperandRight) {
+ NumExprs.push_back(llvm::make_unique<FileCheckNumExpr>(EvalBinop, OperandLeft,
+ OperandRight));
return NumExprs.back().get();
}
+FileCheckNumericVariable *
+FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) {
+ NumericVariables.push_back(
+ llvm::make_unique<FileCheckNumericVariable>(Name, Value));
+ return NumericVariables.back().get();
+}
+
size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
// Offset keeps track of the current offset within the input Str
size_t Offset = 0;
@@ -1445,7 +1529,7 @@ Regex llvm::FileCheck::buildCheckPrefixR
bool FileCheckPatternContext::defineCmdlineVariables(
std::vector<std::string> &CmdlineDefines, SourceMgr &SM) {
- assert(GlobalVariableTable.empty() &&
+ assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
"Overriding defined variable with command-line variable definitions");
if (CmdlineDefines.empty())
@@ -1463,6 +1547,9 @@ bool FileCheckPatternContext::defineCmdl
CmdlineDefsDiag +=
(Prefix1 + Twine(++I) + Prefix2 + CmdlineDef + "\n").str();
+ // Create a buffer with fake command line content in order to display
+ // parsing diagnostic with location information and point to the
+ // global definition with invalid syntax.
std::unique_ptr<MemoryBuffer> CmdLineDefsDiagBuffer =
MemoryBuffer::getMemBufferCopy(CmdlineDefsDiag, "Global defines");
StringRef CmdlineDefsDiagRef = CmdLineDefsDiagBuffer->getBuffer();
@@ -1472,27 +1559,91 @@ bool FileCheckPatternContext::defineCmdl
CmdlineDefsDiagRef.split(CmdlineDefsDiagVec, '\n', -1 /*MaxSplit*/,
false /*KeepEmpty*/);
for (StringRef CmdlineDefDiag : CmdlineDefsDiagVec) {
- unsigned NameStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
- if (CmdlineDefDiag.substr(NameStart).find('=') == StringRef::npos) {
- SM.PrintMessage(SMLoc::getFromPointer(CmdlineDefDiag.data()),
+ unsigned DefStart = CmdlineDefDiag.find(Prefix2) + Prefix2.size();
+ StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
+ if (CmdlineDef.find('=') == StringRef::npos) {
+ SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()),
SourceMgr::DK_Error,
"Missing equal sign in global definition");
ErrorFound = true;
continue;
}
- std::pair<StringRef, StringRef> CmdlineNameVal =
- CmdlineDefDiag.substr(NameStart).split('=');
- StringRef Name = CmdlineNameVal.first;
- bool IsPseudo;
- unsigned TrailIdx;
- if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) || IsPseudo ||
- TrailIdx != Name.size() || Name.empty()) {
- SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
- "invalid name for variable definition '" + Name + "'");
- ErrorFound = true;
- continue;
+
+ // Numeric variable definition.
+ if (CmdlineDef[0] == '#') {
+ bool IsPseudo;
+ unsigned TrailIdx;
+ size_t EqIdx = CmdlineDef.find('=');
+ StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
+ if (FileCheckPattern::parseVariable(CmdlineName, IsPseudo, TrailIdx) ||
+ IsPseudo || TrailIdx != CmdlineName.size() || CmdlineName.empty()) {
+ SM.PrintMessage(SMLoc::getFromPointer(CmdlineName.data()),
+ SourceMgr::DK_Error,
+ "invalid name in numeric variable definition '" +
+ CmdlineName + "'");
+ 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");
+ ErrorFound = true;
+ continue;
+ }
+
+ StringRef CmdlineVal = CmdlineDef.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;
+ }
+ auto DefinedNumericVariable = makeNumericVariable(CmdlineName, Val);
+
+ // Record this variable definition.
+ GlobalNumericVariableTable[CmdlineName] = DefinedNumericVariable;
+ } else {
+ // Pattern variable definition.
+ std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
+ StringRef Name = CmdlineNameVal.first;
+ bool IsPseudo;
+ unsigned TrailIdx;
+ if (FileCheckPattern::parseVariable(Name, IsPseudo, TrailIdx) ||
+ IsPseudo || TrailIdx != Name.size() || Name.empty()) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
+ "invalid name in pattern variable definition '" + Name +
+ "'");
+ ErrorFound = true;
+ continue;
+ }
+
+ // Detect collisions between pattern and numeric variables when the
+ // former is created later than the latter.
+ if (GlobalNumericVariableTable.find(Name) !=
+ GlobalNumericVariableTable.end()) {
+ SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
+ "numeric variable with name '" + Name +
+ "' already exists");
+ ErrorFound = true;
+ continue;
+ }
+ GlobalVariableTable.insert(CmdlineNameVal);
+ // Mark the pattern variable as defined to detect collisions between
+ // pattern and numeric variables in DefineCmdlineVariables when the
+ // latter is created later than the former. We cannot reuse
+ // GlobalVariableTable for that by populating it with an empty string
+ // since we would then lose the ability to detect the use of an undefined
+ // variable in Match().
+ DefinedVariableTable[Name] = true;
}
- GlobalVariableTable.insert(CmdlineNameVal);
}
return ErrorFound;
@@ -1504,8 +1655,22 @@ void FileCheckPatternContext::clearLocal
if (Var.first()[0] != '$')
LocalPatternVars.push_back(Var.first());
+ // Numeric expression substitution reads the value of a variable directly,
+ // not via GlobalNumericVariableTable. Therefore, we clear local variables by
+ // clearing their value which will lead to a numeric expression substitution
+ // failure. We also mark the variable for removal from
+ // GlobalNumericVariableTable since this is what defineCmdlineVariables
+ // checks to decide that no global variable has been defined.
+ for (const auto &Var : GlobalNumericVariableTable)
+ if (Var.first()[0] != '$') {
+ Var.getValue()->clearValue();
+ LocalNumericVars.push_back(Var.first());
+ }
+
for (const auto &Var : LocalPatternVars)
GlobalVariableTable.erase(Var);
+ for (const auto &Var : LocalNumericVars)
+ GlobalNumericVariableTable.erase(Var);
}
bool llvm::FileCheck::CheckInput(SourceMgr &SM, StringRef Buffer,
@@ -1538,7 +1703,10 @@ bool llvm::FileCheck::CheckInput(SourceM
++j;
}
- if (Req.EnableVarScope)
+ // Do not clear the first region as it's the one before the first
+ // CHECK-LABEL and it would clear variables defined on the command-line
+ // before they get used.
+ if (i != 0 && Req.EnableVarScope)
PatternContext.clearLocalVars();
for (; i != j; ++i) {
Added: llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt?rev=360665&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-defines-diagnostics.txt Tue May 14 04:58:30 2019
@@ -0,0 +1,33 @@
+; Test incorrect syntax for -D# option is correctly diagnosed.
+
+; Invalid variable name: starts with a digit.
+RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRCLIFMT
+
+NUMERRCLIFMT: Global defines:1:20: error: invalid name in numeric variable definition '10VALUE'
+NUMERRCLIFMT-NEXT: Global define #1: #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 name in numeric variable definition '@VALUE'
+NUMERRCLIPSEUDO-NEXT: Global define #1: #@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 name in numeric variable definition 'VALUE+2'
+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: {{^ \^$}}
Added: llvm/trunk/test/FileCheck/numeric-defines.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-defines.txt?rev=360665&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-defines.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-defines.txt Tue May 14 04:58:30 2019
@@ -0,0 +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
+
+Numeric value = 12
+CHECKNUM: Numeric value = [[#NUMVAL]]
+NUMNOT-NOT: Numeric value = [[#NUMVAL]]
+
+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:[[#@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"
Added: llvm/trunk/test/FileCheck/numeric-expression.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/numeric-expression.txt?rev=360665&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/numeric-expression.txt (added)
+++ llvm/trunk/test/FileCheck/numeric-expression.txt Tue May 14 04:58:30 2019
@@ -0,0 +1,95 @@
+RUN: FileCheck -D#VAR1=11 --input-file %s %s
+
+; We use CHECK-NEXT directives to force a match on all lines with digits.
+
+; Numeric expressions using variables defined on the command-line without
+; spaces
+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
+RUN: not FileCheck --check-prefix UNDEF-USE --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix UNDEF-USE-MSG %s
+
+UNDEF VAR USE
+UNDEFVAR: 11
+UNDEF-USE-LABEL: UNDEF VAR USE
+UNDEF-USE-NEXT: UNDEFVAR: [[#UNDEFVAR]]
+UNDEF-USE-MSG: numeric-expression.txt:[[#@LINE-1]]:30: 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
+RUN: not FileCheck -D#VAR1=11 --check-prefixes CHECK,INVAL-OP --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix INVAL-OP-MSG %s
+
+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]]:31: 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
+RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 --check-prefixes CONFLICT,CONFLICT1 --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix CLI-INPUT-PAT-CONFLICT %s
+RUN: not FileCheck -D#VAR1=11 -D#NUMVAR=42 -DNUMVAR=foobar --check-prefix CONFLICT --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-PAT-CONFLICT %s
+RUN: not FileCheck -D#VAR1=11 -DPATVAR=foobar -D#PATVAR=42 --check-prefix CONFLICT --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix CLI-CLI-NUM-CONFLICT %s
+
+PATVAR NUMVAR CONFLICT
+foobar
+CONFLICT-LABEL: PATVAR NUMVAR CONFLICT
+CONFLICT1-NEXT: [[NUMVAR:foo.*]]
+CLI-INPUT-PAT-CONFLICT: numeric-expression.txt:[[#@LINE-1]]:19: error: numeric variable with name 'NUMVAR' already exists
+CLI-INPUT-PAT-CONFLICT-NEXT: {{C}}ONFLICT1-NEXT: {{\[\[NUMVAR:foo\.\*\]\]}}
+CLI-INPUT-PAT-CONFLICT-NEXT: {{^ \^$}}
+CLI-CLI-PAT-CONFLICT: Global defines:3:19: error: numeric variable with name 'NUMVAR' already exists
+CLI-CLI-PAT-CONFLICT-NEXT: Global define #3: NUMVAR=foobar
+CLI-CLI-PAT-CONFLICT-NEXT: {{^ \^$}}
+CLI-CLI-NUM-CONFLICT: Global defines:3:20: error: pattern variable with name 'PATVAR' already exists
+CLI-CLI-NUM-CONFLICT-NEXT: Global define #3: #PATVAR=42
+CLI-CLI-NUM-CONFLICT-NEXT: {{^ \^$}}
Modified: llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt (original)
+++ llvm/trunk/test/FileCheck/pattern-defines-diagnostics.txt Tue May 14 04:58:30 2019
@@ -28,7 +28,7 @@ ERRCLIVAR2: Missing pattern variable nam
RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLIFMT
-ERRCLIFMT: Global defines:1:19: error: invalid name for variable definition '10VALUE'
+ERRCLIFMT: Global defines:1:19: error: invalid name in pattern variable definition '10VALUE'
ERRCLIFMT-NEXT: Global define #1: 10VALUE=10
ERRCLIFMT-NEXT: {{^ \^$}}
@@ -36,7 +36,7 @@ ERRCLIFMT-NEXT: {{^ \^$
RUN: not FileCheck -D at VALUE=10 --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLIPSEUDO
-ERRCLIPSEUDO: Global defines:1:19: error: invalid name for variable definition '@VALUE'
+ERRCLIPSEUDO: Global defines:1:19: error: invalid name in pattern variable definition '@VALUE'
ERRCLIPSEUDO-NEXT: Global define #1: @VALUE=10
ERRCLIPSEUDO-NEXT: {{^ \^$}}
@@ -44,6 +44,6 @@ ERRCLIPSEUDO-NEXT: {{^
RUN: not FileCheck -D'VALUE + 2=10' --input-file %s %s 2>&1 \
RUN: | FileCheck %s --strict-whitespace --check-prefix ERRCLITRAIL
-ERRCLITRAIL: Global defines:1:19: error: invalid name for variable definition 'VALUE + 2'
+ERRCLITRAIL: Global defines:1:19: error: invalid name in pattern variable definition 'VALUE + 2'
ERRCLITRAIL-NEXT: Global define #1: VALUE + 2=10
ERRCLITRAIL-NEXT: {{^ \^$}}
Removed: llvm/trunk/test/FileCheck/regex-scope.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/regex-scope.txt?rev=360664&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/regex-scope.txt (original)
+++ llvm/trunk/test/FileCheck/regex-scope.txt (removed)
@@ -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
Added: llvm/trunk/test/FileCheck/var-scope.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/var-scope.txt?rev=360665&view=auto
==============================================================================
--- llvm/trunk/test/FileCheck/var-scope.txt (added)
+++ llvm/trunk/test/FileCheck/var-scope.txt Tue May 14 04:58:30 2019
@@ -0,0 +1,35 @@
+; Test that variables not starting with dollar sign get undefined after a
+; CHECK-LABEL directive iff --enable-var-scope is used.
+
+RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,LOCAL3,GLOBAL --input-file %s %s
+RUN: FileCheck -D#LOCNUM=1 -D'#$GLOBNUM=1' --check-prefixes CHECK,GLOBAL --enable-var-scope --input-file %s %s
+RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL1 --enable-var-scope --input-file %s %s 2>&1 \
+RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL %s
+RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL2 --enable-var-scope --input-file %s %s 2>&1 \
+RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCNUM %s
+RUN: not FileCheck -D#LOCNUM=1 -D#'$GLOBNUM=1' --check-prefixes CHECK,LOCAL3 --enable-var-scope --input-file %s %s 2>&1 \
+RUN: | FileCheck --check-prefixes ERRUNDEF,ERRUNDEFLOCAL,ERRUNDEFLOCNUM %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]]
+
+ERRUNDEF: expected string not found in input
+ERRUNDEFLOCAL: uses undefined variable "LOCAL"
+ERRUNDEFLOCNUM: uses undefined variable "LOCNUM"
Modified: llvm/trunk/test/FileCheck/verbose.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/FileCheck/verbose.txt?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/test/FileCheck/verbose.txt (original)
+++ llvm/trunk/test/FileCheck/verbose.txt Tue May 14 04:58:30 2019
@@ -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: verbose.txt:[[@LINE-22]]:1: not
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 @@ V-NEXT: {{^}}^~~{{$}}
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: {{^\^$}}
Modified: llvm/trunk/unittests/Support/FileCheckTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/Support/FileCheckTest.cpp?rev=360665&r1=360664&r2=360665&view=diff
==============================================================================
--- llvm/trunk/unittests/Support/FileCheckTest.cpp (original)
+++ llvm/trunk/unittests/Support/FileCheckTest.cpp Tue May 14 04:58:30 2019
@@ -14,6 +14,57 @@ namespace {
class FileCheckTest : public ::testing::Test {};
+TEST_F(FileCheckTest, NumericVariable) {
+ FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
+ EXPECT_EQ("FOO", FooVar.getName());
+
+ // Defined variable: getValue returns a value, setValue fails and value
+ // remains unchanged.
+ llvm::Optional<uint64_t> Value = FooVar.getValue();
+ EXPECT_TRUE(Value);
+ EXPECT_EQ(42U, *Value);
+ EXPECT_TRUE(FooVar.setValue(43));
+ Value = FooVar.getValue();
+ EXPECT_TRUE(Value);
+ EXPECT_EQ(42U, *Value);
+
+ // Clearing variable: getValue fails, clearValue again fails.
+ EXPECT_FALSE(FooVar.clearValue());
+ Value = FooVar.getValue();
+ EXPECT_FALSE(Value);
+ EXPECT_TRUE(FooVar.clearValue());
+
+ // Undefined variable: setValue works, getValue returns value set.
+ EXPECT_FALSE(FooVar.setValue(43));
+ Value = FooVar.getValue();
+ EXPECT_TRUE(Value);
+ EXPECT_EQ(43U, *Value);
+}
+
+uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
+
+TEST_F(FileCheckTest, NumExpr) {
+ FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
+ FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &FooVar, 18);
+
+ // Defined variable: eval returns right value, no undefined variable
+ // returned.
+ llvm::Optional<uint64_t> Value = NumExpr.eval();
+ EXPECT_TRUE(Value);
+ EXPECT_EQ(60U, *Value);
+ StringRef UndefVar = NumExpr.getUndefVarName();
+ EXPECT_EQ("", UndefVar);
+
+ // Undefined variable: eval fails, undefined variable returned. We call
+ // getUndefVarName first to check that it can be called without calling
+ // eval() first.
+ FooVar.clearValue();
+ UndefVar = NumExpr.getUndefVarName();
+ EXPECT_EQ("FOO", UndefVar);
+ Value = NumExpr.eval();
+ EXPECT_FALSE(Value);
+}
+
TEST_F(FileCheckTest, ValidVarNameStart) {
EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('a'));
EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('G'));
@@ -90,22 +141,38 @@ TEST_F(FileCheckTest, ParseVar) {
EXPECT_EQ(TrailIdx, VarName.size() - 1);
}
+static StringRef bufferize(SourceMgr &SM, StringRef Str) {
+ std::unique_ptr<MemoryBuffer> Buffer =
+ MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
+ StringRef StrBufferRef = Buffer->getBuffer();
+ SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
+ return StrBufferRef;
+}
+
class ExprTester {
private:
SourceMgr SM;
+ FileCheckRequest Req;
FileCheckPatternContext Context;
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
public:
+ ExprTester() {
+ std::vector<std::string> GlobalDefines;
+ GlobalDefines.emplace_back(std::string("#FOO=42"));
+ Context.defineCmdlineVariables(GlobalDefines, SM);
+ // Call ParsePattern to have @LINE defined.
+ P.ParsePattern("N/A", "CHECK", SM, 1, Req);
+ }
+
bool parseExpect(std::string &VarName, std::string &Trailer) {
+ bool IsPseudo = VarName[0] == '@';
std::string NameTrailer = VarName + Trailer;
- std::unique_ptr<MemoryBuffer> Buffer =
- MemoryBuffer::getMemBufferCopy(NameTrailer, "TestBuffer");
- StringRef NameTrailerRef = Buffer->getBuffer();
- SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
+ StringRef NameTrailerRef = bufferize(SM, NameTrailer);
StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size());
StringRef TrailerRef = NameTrailerRef.substr(VarName.size());
- return P.parseNumericExpression(VarNameRef, TrailerRef, SM) == nullptr;
+ return P.parseNumericExpression(VarNameRef, IsPseudo, TrailerRef, SM) ==
+ nullptr;
}
};
@@ -121,6 +188,14 @@ TEST_F(FileCheckTest, ParseExpr) {
Trailer = "";
EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
+ // Defined variable.
+ VarName = "FOO";
+ EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
+
+ // Undefined variable.
+ VarName = "UNDEF";
+ EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
+
// Wrong Pseudovar.
VarName = "@FOO";
EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
@@ -153,19 +228,38 @@ TEST_F(FileCheckTest, Substitution) {
GlobalDefines.emplace_back(std::string("FOO=BAR"));
Context.defineCmdlineVariables(GlobalDefines, SM);
- FileCheckPatternSubstitution Substitution =
+ // Substitution of undefined pattern variable fails.
+ FileCheckPatternSubstitution PatternSubstitution =
FileCheckPatternSubstitution(&Context, "VAR404", 42);
- EXPECT_FALSE(Substitution.getResult());
+ EXPECT_FALSE(PatternSubstitution.getResult());
- FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
- Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
- llvm::Optional<std::string> Value = Substitution.getResult();
+ // Substitutions of defined pseudo and non-pseudo numeric variables return
+ // the right value.
+ FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
+ FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10);
+ FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0);
+ FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3);
+ FileCheckPatternSubstitution SubstitutionLine =
+ FileCheckPatternSubstitution(&Context, "@LINE", &NumExprLine, 12);
+ FileCheckPatternSubstitution SubstitutionN =
+ FileCheckPatternSubstitution(&Context, "N", &NumExprN, 30);
+ llvm::Optional<std::string> Value = SubstitutionLine.getResult();
EXPECT_TRUE(Value);
EXPECT_EQ("42", *Value);
+ Value = SubstitutionN.getResult();
+ EXPECT_TRUE(Value);
+ EXPECT_EQ("13", *Value);
+ // Substitution of undefined numeric variable fails.
+ LineVar.clearValue();
+ EXPECT_FALSE(SubstitutionLine.getResult());
+ NVar.clearValue();
+ EXPECT_FALSE(SubstitutionN.getResult());
+
+ // Substitution of defined pattern variable returns the right value.
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
- Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
- Value = Substitution.getResult();
+ PatternSubstitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
+ Value = PatternSubstitution.getResult();
EXPECT_TRUE(Value);
EXPECT_EQ("BAR", *Value);
}
@@ -177,19 +271,32 @@ TEST_F(FileCheckTest, UndefVars) {
GlobalDefines.emplace_back(std::string("FOO=BAR"));
Context.defineCmdlineVariables(GlobalDefines, SM);
+ // getUndefVarName() on a pattern variable substitution with an undefined
+ // variable returns that variable.
FileCheckPatternSubstitution Substitution =
FileCheckPatternSubstitution(&Context, "VAR404", 42);
StringRef UndefVar = Substitution.getUndefVarName();
EXPECT_EQ("VAR404", UndefVar);
- FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
- Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
+ // getUndefVarName() on a pattern variable substitution with a defined
+ // variable returns an empty string.
+ Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
UndefVar = Substitution.getUndefVarName();
EXPECT_EQ("", UndefVar);
- Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
+ // getUndefVarName() on a numeric expression substitution with a defined
+ // variable returns an empty string.
+ FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
+ FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0);
+ Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
UndefVar = Substitution.getUndefVarName();
EXPECT_EQ("", UndefVar);
+
+ // getUndefVarName() on a numeric expression substitution with an undefined
+ // variable returns that variable.
+ LineVar.clearValue();
+ UndefVar = Substitution.getUndefVarName();
+ EXPECT_EQ("@LINE", UndefVar);
}
TEST_F(FileCheckTest, FileCheckContext) {
@@ -197,36 +304,71 @@ TEST_F(FileCheckTest, FileCheckContext)
std::vector<std::string> GlobalDefines;
SourceMgr SM;
- // Missing equal sign
+ // Missing equal sign.
GlobalDefines.emplace_back(std::string("LocalVar"));
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+ GlobalDefines.clear();
+ GlobalDefines.emplace_back(std::string("#LocalNumVar"));
+ EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
- // Empty variable
+ // Empty variable name.
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("=18"));
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+ GlobalDefines.clear();
+ GlobalDefines.emplace_back(std::string("#=18"));
+ EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
- // Invalid variable
+ // Invalid variable name.
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("18LocalVar=18"));
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+ GlobalDefines.clear();
+ GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
+ EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+
+ // Name conflict between pattern and numeric variable.
+ GlobalDefines.clear();
+ GlobalDefines.emplace_back(std::string("LocalVar=18"));
+ GlobalDefines.emplace_back(std::string("#LocalVar=36"));
+ EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+ Cxt = FileCheckPatternContext();
+ GlobalDefines.clear();
+ GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
+ GlobalDefines.emplace_back(std::string("LocalNumVar=36"));
+ EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+ Cxt = FileCheckPatternContext();
+
+ // Invalid numeric value for numeric variable.
+ GlobalDefines.clear();
+ GlobalDefines.emplace_back(std::string("#LocalNumVar=x"));
+ EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
// Define local variables from command-line.
GlobalDefines.clear();
GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
GlobalDefines.emplace_back(std::string("EmptyVar="));
+ GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
bool GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
EXPECT_FALSE(GotError);
// Check defined variables are present and undefined is absent.
StringRef LocalVarStr = "LocalVar";
+ StringRef LocalNumVarRef = bufferize(SM, "LocalNumVar");
StringRef EmptyVarStr = "EmptyVar";
StringRef UnknownVarStr = "UnknownVar";
llvm::Optional<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
+ FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt);
+ FileCheckNumExpr *NumExpr =
+ P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM);
llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
EXPECT_TRUE(LocalVar);
EXPECT_EQ(*LocalVar, "FOO");
+ EXPECT_TRUE(NumExpr);
+ llvm::Optional<uint64_t> NumExprVal = NumExpr->eval();
+ EXPECT_TRUE(NumExprVal);
+ EXPECT_EQ(*NumExprVal, 18U);
EXPECT_TRUE(EmptyVar);
EXPECT_EQ(*EmptyVar, "");
EXPECT_FALSE(UnknownVar);
@@ -235,21 +377,46 @@ TEST_F(FileCheckTest, FileCheckContext)
Cxt.clearLocalVars();
LocalVar = Cxt.getPatternVarValue(LocalVarStr);
EXPECT_FALSE(LocalVar);
+ // Check a numeric expression's evaluation fails if called after clearing of
+ // local variables, if it was created before. This is important because local
+ // variable clearing due to --enable-var-scope happens after numeric
+ // expressions are linked to the numeric variables they use.
+ EXPECT_FALSE(NumExpr->eval());
+ P = FileCheckPattern(Check::CheckPlain, &Cxt);
+ NumExpr =
+ P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM);
+ EXPECT_FALSE(NumExpr);
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
EXPECT_FALSE(EmptyVar);
// Redefine global variables and check variables are defined again.
GlobalDefines.emplace_back(std::string("$GlobalVar=BAR"));
+ GlobalDefines.emplace_back(std::string("#$GlobalNumVar=36"));
GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
EXPECT_FALSE(GotError);
StringRef GlobalVarStr = "$GlobalVar";
+ StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
EXPECT_TRUE(GlobalVar);
EXPECT_EQ(*GlobalVar, "BAR");
+ P = FileCheckPattern(Check::CheckPlain, &Cxt);
+ NumExpr =
+ P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
+ EXPECT_TRUE(NumExpr);
+ NumExprVal = NumExpr->eval();
+ EXPECT_TRUE(NumExprVal);
+ EXPECT_EQ(*NumExprVal, 36U);
// Clear local variables and check global variables remain defined.
Cxt.clearLocalVars();
GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
EXPECT_TRUE(GlobalVar);
+ P = FileCheckPattern(Check::CheckPlain, &Cxt);
+ NumExpr =
+ P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
+ EXPECT_TRUE(NumExpr);
+ NumExprVal = NumExpr->eval();
+ EXPECT_TRUE(NumExprVal);
+ EXPECT_EQ(*NumExprVal, 36U);
}
} // namespace
More information about the llvm-commits
mailing list