Sets a filecheck pattern variable ``VAR`` with value ``VALUE`` that can be
used in ``CHECK:`` lines.
-.. option:: -D#<NUMVAR>=<NUMERIC EXPRESSION>
+.. option:: -D#<FMT>,<NUMVAR>=<NUMERIC EXPRESSION>
- Sets a filecheck numeric variable ``NUMVAR`` to the result of evaluating
- ``<NUMERIC EXPRESSION>`` that can be used in ``CHECK:`` lines. See section
+ Sets a filecheck numeric variable ``NUMVAR`` of matching format ``FMT`` to
+ the result of evaluating ``<NUMERIC EXPRESSION>`` that can be used in
+ ``CHECK:`` lines. See section
``FileCheck Numeric Variables and Expressions`` for details on supported
numeric expressions.
substitution. This allows ``CHECK:`` directives to verify a numeric relation
between two numbers, such as the need for consecutive registers to be used.
-The syntax to define a numeric variable is ``[[#<NUMVAR>:]]`` where
-``<NUMVAR>`` is the name of the numeric variable to define to the matching
-value.
+The syntax to define a numeric variable is ``[[#%<fmtspec>,<NUMVAR>:]]`` where:
+
+* ``%<fmtspec>`` is an optional scanf-style matching format specifier to
+ indicate what number format to match (e.g. hex number). Currently accepted
+ format specifiers are ``%u``, ``%x`` and ``%X``. If absent, the format
+ specifier defaults to ``%u``.
+
+* ``<NUMVAR>`` is the name of the numeric variable to define to the matching
+ value.
For example:
.. code-block:: llvm
- ; CHECK: mov r[[#REG:]], 42
+ ; CHECK: mov r[[#REG:]], 0x[[#%X,IMM:]]
+
+would match ``mov r5, 0xF0F0`` and set ``REG`` to the value ``5`` and ``IMM``
+to the value ``0xF0F0``.
-would match ``mov r5, 42`` and set ``REG`` to the value ``5``.
+The syntax of a numeric substitution is ``[[#%<fmtspec>,<expr>]]`` where:
-The syntax of a numeric substitution is ``[[#<expr>]]`` where ``<expr>`` is an
-expression. An expression is recursively defined as:
+* ``%<fmtspec>`` is the same matching format specifier as for defining numeric
+ variables but acting as a printf-style format to indicate how a numeric
+ expression value should be matched against. If absent, the format specifier
+ is inferred from the matching format of the numeric variable(s) used by the
+ expression constraint if any, and defaults to ``%u`` if no numeric variable
+ is used. In case of conflict between matching formats of several numeric
+ variables the format specifier is mandatory.
-* a numeric operand, or
-* an expression followed by an operator and a numeric operand.
+* ``<expr>`` is an expression. An expression is in turn recursively defined
+ as:
-A numeric operand is a previously defined numeric variable, or an integer
-literal. The supported operators are ``+`` and ``-``. Spaces are accepted
-before, after and between any of these elements.
+ * a numeric operand, or
+ * an expression followed by an operator and a numeric operand.
+
+ A numeric operand is a previously defined numeric variable, or an integer
+ literal. The supported operators are ``+`` and ``-``. Spaces are accepted
+ before, after and between any of these elements.
For example:
; CHECK: load r[[#REG:]], [r0]
; CHECK: load r[[#REG+1]], [r1]
+ ; CHECK: Loading from 0x[[#%x,ADDR:] to 0x[[#ADDR + 7]]
The above example would match the text:
load r5, [r0]
load r6, [r1]
+ Loading from 0xa0463440 to 0xa0463447
but would not match the text:
load r5, [r0]
load r7, [r1]
+ Loading from 0xa0463440 to 0xa0463443
-due to ``7`` being unequal to ``5 + 1``.
+Due to ``7`` being unequal to ``5 + 1`` and ``a0463443`` being unequal to
+``a0463440 + 7``.
The syntax also supports an empty expression, equivalent to writing {{[0-9]+}},
for cases where the input must contain a numeric value but the value itself
A numeric variable can also be defined to the result of a numeric expression,
in which case the numeric expression is checked and if verified the variable is
assigned to the value. The unified syntax for both defining numeric variables
-and checking a numeric expression is thus ``[[#<NUMVAR>: <expr>]]`` with each
-element as described previously.
+and checking a numeric expression is thus ``[[#%<fmtspec>,<NUMVAR>: <expr>]]``
+with each element as described previously. One can use this syntax to make a
+testcase more self-describing by using variables instead of values:
+
+.. code-block:: gas
+
+ ; CHECK: mov r[[#REG_OFFSET:]], 0x[[#%X,FIELD_OFFSET:12]]
+ ; CHECK-NEXT: load r[[#]], [r[[#REG_BASE:]], r[[#REG_OFFSET]]]
+
+which would match:
+
+.. code-block:: gas
+
+ mov r4, 0xC
+ load r6, [r5, r4]
The ``--enable-var-scope`` option has the same effect on numeric variables as
on string variables.
using namespace llvm;
+Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
+ switch (Value) {
+ case Kind::Unsigned:
+ return StringRef("[0-9]+");
+ case Kind::HexUpper:
+ return StringRef("[0-9A-F]+");
+ case Kind::HexLower:
+ return StringRef("[0-9a-f]+");
+ default:
+ return createStringError(std::errc::invalid_argument,
+ "trying to match value with invalid format");
+ }
+}
+
+Expected<std::string>
+ExpressionFormat::getMatchingString(uint64_t IntegerValue) const {
+ switch (Value) {
+ case Kind::Unsigned:
+ return utostr(IntegerValue);
+ case Kind::HexUpper:
+ return utohexstr(IntegerValue, /*LowerCase=*/false);
+ case Kind::HexLower:
+ return utohexstr(IntegerValue, /*LowerCase=*/true);
+ default:
+ return createStringError(std::errc::invalid_argument,
+ "trying to match value with invalid format");
+ }
+}
+
+Expected<uint64_t>
+ExpressionFormat::valueFromStringRepr(StringRef StrVal,
+ const SourceMgr &SM) const {
+ bool Hex = Value == Kind::HexUpper || Value == Kind::HexLower;
+ uint64_t IntegerValue;
+ if (StrVal.getAsInteger(Hex ? 16 : 10, IntegerValue))
+ return ErrorDiagnostic::get(SM, StrVal,
+ "unable to represent numeric value");
+
+ return IntegerValue;
+}
+
Expected<uint64_t> NumericVariableUse::eval() const {
Optional<uint64_t> Value = Variable->getValue();
if (Value)
return EvalBinop(*LeftOp, *RightOp);
}
+ExpressionFormat BinaryOperation::getImplicitFormat() const {
+ ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat();
+ ExpressionFormat RightFormat = RightOperand->getImplicitFormat();
+
+ ExpressionFormat Format =
+ LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat;
+ if (LeftFormat != ExpressionFormat::Kind::NoFormat &&
+ RightFormat != ExpressionFormat::Kind::NoFormat &&
+ LeftFormat != RightFormat)
+ Format = ExpressionFormat(ExpressionFormat::Kind::Conflict);
+
+ return Format;
+}
+
Expected<std::string> NumericSubstitution::getResult() const {
- Expected<uint64_t> EvaluatedValue = ExpressionASTPointer->eval();
+ assert(ExpressionPointer->getAST() != nullptr &&
+ "Substituting empty expression");
+ Expected<uint64_t> EvaluatedValue = ExpressionPointer->getAST()->eval();
if (!EvaluatedValue)
return EvaluatedValue.takeError();
- return utostr(*EvaluatedValue);
+ ExpressionFormat Format = ExpressionPointer->getFormat();
+ return Format.getMatchingString(*EvaluatedValue);
}
Expected<std::string> StringSubstitution::getResult() const {
Expected<NumericVariable *> Pattern::parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context,
- Optional<size_t> LineNumber, const SourceMgr &SM) {
+ Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
+ const SourceMgr &SM) {
Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM);
if (!ParseVarResult)
return ParseVarResult.takeError();
NumericVariable *DefinedNumericVariable;
auto VarTableIter = Context->GlobalNumericVariableTable.find(Name);
- if (VarTableIter != Context->GlobalNumericVariableTable.end())
+ if (VarTableIter != Context->GlobalNumericVariableTable.end()) {
DefinedNumericVariable = VarTableIter->second;
- else
- DefinedNumericVariable = Context->makeNumericVariable(Name, LineNumber);
+ if (DefinedNumericVariable->getImplicitFormat() != ImplicitFormat)
+ return ErrorDiagnostic::get(
+ SM, Expr, "format different from previous variable definition");
+ } else
+ DefinedNumericVariable =
+ Context->makeNumericVariable(Name, ImplicitFormat, LineNumber);
return DefinedNumericVariable;
}
if (VarTableIter != Context->GlobalNumericVariableTable.end())
NumericVariable = VarTableIter->second;
else {
- NumericVariable = Context->makeNumericVariable(Name);
+ NumericVariable = Context->makeNumericVariable(
+ Name, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
Context->GlobalNumericVariableTable[Name] = NumericVariable;
}
// Otherwise, parse it as a literal.
uint64_t LiteralValue;
- if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue))
+ if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
+ LiteralValue))
return std::make_unique<ExpressionLiteral>(LiteralValue);
return ErrorDiagnostic::get(SM, Expr,
return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
// The second operand in a legacy @LINE expression is always a literal.
AllowedOperand AO =
- IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any;
+ IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
if (!RightOpResult)
std::move(*RightOpResult));
}
-Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericSubstitutionBlock(
+Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM) {
std::unique_ptr<ExpressionAST> ExpressionASTPointer = nullptr;
StringRef DefExpr = StringRef();
DefinedNumericVariable = None;
+ ExpressionFormat ExplicitFormat = ExpressionFormat();
+
+ // Parse format specifier.
+ size_t FormatSpecEnd = Expr.find(',');
+ if (FormatSpecEnd != StringRef::npos) {
+ Expr = Expr.ltrim(SpaceChars);
+ if (!Expr.consume_front("%"))
+ return ErrorDiagnostic::get(
+ SM, Expr, "invalid matching format specification in expression");
+
+ // Check for unknown matching format specifier and set matching format in
+ // class instance representing this expression.
+ SMLoc fmtloc = SMLoc::getFromPointer(Expr.data());
+ switch (popFront(Expr)) {
+ case 'u':
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
+ break;
+ case 'x':
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexLower);
+ break;
+ case 'X':
+ ExplicitFormat = ExpressionFormat(ExpressionFormat::Kind::HexUpper);
+ break;
+ default:
+ return ErrorDiagnostic::get(SM, fmtloc,
+ "invalid format specifier in expression");
+ }
+
+ Expr = Expr.ltrim(SpaceChars);
+ if (!Expr.consume_front(","))
+ return ErrorDiagnostic::get(
+ SM, Expr, "invalid matching format specification in expression");
+ }
+
// Save variable definition expression if any.
size_t DefEnd = Expr.find(':');
if (DefEnd != StringRef::npos) {
// Parse the expression itself.
Expr = Expr.ltrim(SpaceChars);
+ StringRef UseExpr = Expr;
if (!Expr.empty()) {
// The first operand in a legacy @LINE expression is always the @LINE
// pseudo variable.
"unexpected characters at end of expression '" + Expr + "'");
}
if (!ParseResult)
- return ParseResult;
+ return ParseResult.takeError();
ExpressionASTPointer = std::move(*ParseResult);
}
+ // Select format of the expression, i.e. (i) its explicit format, if any,
+ // otherwise (ii) its implicit format, if any, otherwise (iii) the default
+ // format (unsigned). Error out in case of conflicting implicit format
+ // without explicit format.
+ ExpressionFormat Format,
+ ImplicitFormat = ExpressionASTPointer
+ ? ExpressionASTPointer->getImplicitFormat()
+ : ExpressionFormat(ExpressionFormat::Kind::NoFormat);
+ if (bool(ExplicitFormat))
+ Format = ExplicitFormat;
+ else if (ImplicitFormat == ExpressionFormat::Kind::Conflict)
+ return ErrorDiagnostic::get(
+ SM, UseExpr,
+ "variables with conflicting format specifier: need an explicit one");
+ else if (bool(ImplicitFormat))
+ Format = ImplicitFormat;
+ else
+ Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
+
+ std::unique_ptr<Expression> ExpressionPointer =
+ std::make_unique<Expression>(std::move(ExpressionASTPointer), Format);
+
// Parse the numeric variable definition.
if (DefEnd != StringRef::npos) {
DefExpr = DefExpr.ltrim(SpaceChars);
- Expected<NumericVariable *> ParseResult =
- parseNumericVariableDefinition(DefExpr, Context, LineNumber, SM);
+ Expected<NumericVariable *> ParseResult = parseNumericVariableDefinition(
+ DefExpr, Context, LineNumber, ExpressionPointer->getFormat(), SM);
if (!ParseResult)
return ParseResult.takeError();
DefinedNumericVariable = *ParseResult;
}
- return std::move(ExpressionASTPointer);
+ return std::move(ExpressionPointer);
}
bool Pattern::parsePattern(StringRef PatternStr, StringRef Prefix,
}
// Parse numeric substitution block.
- std::unique_ptr<ExpressionAST> ExpressionASTPointer;
+ std::unique_ptr<Expression> ExpressionPointer;
Optional<NumericVariable *> DefinedNumericVariable;
if (IsNumBlock) {
- Expected<std::unique_ptr<ExpressionAST>> ParseResult =
+ Expected<std::unique_ptr<Expression>> ParseResult =
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable,
IsLegacyLineExpr, LineNumber, Context,
SM);
logAllUnhandledErrors(ParseResult.takeError(), errs());
return true;
}
- ExpressionASTPointer = std::move(*ParseResult);
- SubstNeeded = ExpressionASTPointer != nullptr;
+ ExpressionPointer = std::move(*ParseResult);
+ SubstNeeded = ExpressionPointer->getAST() != nullptr;
if (DefinedNumericVariable) {
IsDefinition = true;
DefName = (*DefinedNumericVariable)->getName();
}
if (SubstNeeded)
SubstStr = MatchStr;
- else
- MatchRegexp = "[0-9]+";
+ else {
+ ExpressionFormat Format = ExpressionPointer->getFormat();
+ MatchRegexp = cantFail(Format.getWildcardRegex());
+ }
}
// Handle variable definition: [[<def>:(...)]] and [[#(...)<def>:(...)]].
Substitution *Substitution =
IsNumBlock
? Context->makeNumericSubstitution(
- SubstStr, std::move(ExpressionASTPointer),
- SubstInsertIdx)
+ SubstStr, std::move(ExpressionPointer), SubstInsertIdx)
: Context->makeStringSubstitution(SubstStr, SubstInsertIdx);
Substitutions.push_back(Substitution);
}
NumericVariableMatch.DefinedNumericVariable;
StringRef MatchedValue = MatchInfo[CaptureParenGroup];
- uint64_t Val;
- if (MatchedValue.getAsInteger(10, Val))
- return ErrorDiagnostic::get(SM, MatchedValue,
- "Unable to represent numeric value");
- DefinedNumericVariable->setValue(Val);
+ ExpressionFormat Format = DefinedNumericVariable->getImplicitFormat();
+ Expected<uint64_t> Value = Format.valueFromStringRepr(MatchedValue, SM);
+ if (!Value)
+ return Value.takeError();
+ DefinedNumericVariable->setValue(*Value);
}
// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
}
Substitution *FileCheckPatternContext::makeNumericSubstitution(
- StringRef ExpressionStr,
- std::unique_ptr<ExpressionAST> ExpressionASTPointer, size_t InsertIdx) {
+ StringRef ExpressionStr, std::unique_ptr<Expression> Expression,
+ size_t InsertIdx) {
Substitutions.push_back(std::make_unique<NumericSubstitution>(
- this, ExpressionStr, std::move(ExpressionASTPointer), InsertIdx));
+ this, ExpressionStr, std::move(Expression), InsertIdx));
return Substitutions.back().get();
}
void FileCheckPatternContext::createLineVariable() {
assert(!LineVariable && "@LINE pseudo numeric variable already created");
StringRef LineName = "@LINE";
- LineVariable = makeNumericVariable(LineName);
+ LineVariable = makeNumericVariable(
+ LineName, ExpressionFormat(ExpressionFormat::Kind::Unsigned));
GlobalNumericVariableTable[LineName] = LineVariable;
}
// to create the necessary class instance.
StringRef CmdlineDefExpr = CmdlineDef.substr(1);
Optional<NumericVariable *> DefinedNumericVariable;
- Expected<std::unique_ptr<ExpressionAST>> ExpressionASTResult =
+ Expected<std::unique_ptr<Expression>> ExpressionResult =
Pattern::parseNumericSubstitutionBlock(
CmdlineDefExpr, DefinedNumericVariable, false, None, this, SM);
- if (!ExpressionASTResult) {
- Errs = joinErrors(std::move(Errs), ExpressionASTResult.takeError());
+ if (!ExpressionResult) {
+ Errs = joinErrors(std::move(Errs), ExpressionResult.takeError());
continue;
}
- std::unique_ptr<ExpressionAST> ExpressionASTPointer =
- std::move(*ExpressionASTResult);
+ std::unique_ptr<Expression> Expression = std::move(*ExpressionResult);
// Now evaluate the expression whose value this variable should be set
// to, since the expression of a command-line variable definition should
// only use variables defined earlier on the command-line. If not, this
// is an error and we report it.
- Expected<uint64_t> Value = ExpressionASTPointer->eval();
+ Expected<uint64_t> Value = Expression->getAST()->eval();
if (!Value) {
Errs = joinErrors(std::move(Errs), Value.takeError());
continue;
// Numeric substitution handling code.
//===----------------------------------------------------------------------===//
+/// Type representing the format an expression value should be textualized into
+/// for matching. Used to represent both explicit format specifiers as well as
+/// implicit format from using numeric variables.
+struct ExpressionFormat {
+ enum class Kind {
+ /// Denote absence of format. Used for implicit format of literals and
+ /// empty expressions.
+ NoFormat,
+ /// Used when there are several conflicting implicit formats in an
+ /// expression.
+ Conflict,
+ /// Value is an unsigned integer and should be printed as a decimal number.
+ Unsigned,
+ /// Value should be printed as an uppercase hex number.
+ HexUpper,
+ /// Value should be printed as a lowercase hex number.
+ HexLower
+ };
+
+private:
+ Kind Value;
+
+public:
+ /// Evaluates a format to true if it can be used in a match.
+ explicit operator bool() const {
+ return Value != Kind::NoFormat && Value != Kind::Conflict;
+ }
+
+ /// Define format equality: formats are equal if neither is NoFormat and
+ /// their kinds are the same.
+ bool operator==(const ExpressionFormat &Other) const {
+ return Value != Kind::NoFormat && Value == Other.Value;
+ }
+
+ bool operator!=(const ExpressionFormat &other) const {
+ return !(*this == other);
+ }
+
+ bool operator==(Kind OtherValue) const { return Value == OtherValue; }
+
+ bool operator!=(Kind OtherValue) const { return !(*this == OtherValue); }
+
+ ExpressionFormat() : Value(Kind::NoFormat){};
+ explicit ExpressionFormat(Kind Value) : Value(Value){};
+
+ /// \returns a wildcard regular expression StringRef that matches any value
+ /// in the format represented by this instance, or an error if the format is
+ /// NoFormat or Conflict.
+ Expected<StringRef> getWildcardRegex() const;
+
+ /// \returns the string representation of \p Value in the format represented
+ /// by this instance, or an error if the format is NoFormat or Conflict.
+ Expected<std::string> getMatchingString(uint64_t Value) const;
+
+ /// \returns the value corresponding to string representation \p StrVal
+ /// according to the matching format represented by this instance or an error
+ /// with diagnostic against \p SM if \p StrVal does not correspond to a valid
+ /// and representable value.
+ Expected<uint64_t> valueFromStringRepr(StringRef StrVal,
+ const SourceMgr &SM) const;
+};
+
/// Base class representing the AST of a given expression.
class ExpressionAST {
public:
/// Evaluates and \returns the value of the expression represented by this
/// AST or an error if evaluation fails.
virtual Expected<uint64_t> eval() const = 0;
+
+ /// \returns either the implicit format of this AST, FormatConflict if
+ /// implicit formats of the AST's components conflict, or NoFormat if the AST
+ /// has no implicit format (e.g. AST is made up of a single literal).
+ virtual ExpressionFormat getImplicitFormat() const {
+ return ExpressionFormat();
+ }
};
/// Class representing an unsigned literal in the AST of an expression.
}
};
+/// Class representing an expression and its matching format.
+class Expression {
+private:
+ /// Pointer to AST of the expression.
+ std::unique_ptr<ExpressionAST> AST;
+
+ /// Format to use (e.g. hex upper case letters) when matching the value.
+ ExpressionFormat Format;
+
+public:
+ /// Generic constructor for an expression represented by the given \p AST and
+ /// whose matching format is \p Format.
+ Expression(std::unique_ptr<ExpressionAST> AST, ExpressionFormat Format)
+ : AST(std::move(AST)), Format(Format) {}
+
+ /// \returns pointer to AST of the expression. Pointer is guaranteed to be
+ /// valid as long as this object is.
+ ExpressionAST *getAST() const { return AST.get(); }
+
+ ExpressionFormat getFormat() const { return Format; }
+};
+
/// Class representing a numeric variable and its associated current value.
class NumericVariable {
private:
/// Name of the numeric variable.
StringRef Name;
+ /// Format to use for expressions using this variable without an explicit
+ /// format.
+ ExpressionFormat ImplicitFormat;
+
/// Value of numeric variable, if defined, or None otherwise.
Optional<uint64_t> Value;
Optional<size_t> DefLineNumber;
public:
- /// Constructor for a variable \p Name defined at line \p DefLineNumber or
- /// defined before input is parsed if \p DefLineNumber is None.
- explicit NumericVariable(StringRef Name,
+ /// Constructor for a variable \p Name with implicit format \p ImplicitFormat
+ /// defined at line \p DefLineNumber or defined before input is parsed if
+ /// \p DefLineNumber is None.
+ explicit NumericVariable(StringRef Name, ExpressionFormat ImplicitFormat,
Optional<size_t> DefLineNumber = None)
- : Name(Name), DefLineNumber(DefLineNumber) {}
+ : Name(Name), ImplicitFormat(ImplicitFormat),
+ DefLineNumber(DefLineNumber) {}
/// \returns name of this numeric variable.
StringRef getName() const { return Name; }
+ /// \returns implicit format of this numeric variable.
+ ExpressionFormat getImplicitFormat() const { return ImplicitFormat; }
+
/// \returns this variable's value.
Optional<uint64_t> getValue() const { return Value; }
/// \returns the value of the variable referenced by this instance.
Expected<uint64_t> eval() const override;
+
+ /// \returns implicit format of this numeric variable.
+ ExpressionFormat getImplicitFormat() const override {
+ return Variable->getImplicitFormat();
+ }
};
/// Type of functions evaluating a given binary operation.
/// \returns the expression value or an error if an undefined numeric
/// variable is used in one of the operands.
Expected<uint64_t> eval() const override;
+
+ /// \returns the implicit format of this AST, if any, a format conflict if
+ /// the implicit formats of the AST's components conflict, or no format if
+ /// the AST has no implicit format (e.g. AST is made of a single literal).
+ ExpressionFormat getImplicitFormat() const override;
};
class FileCheckPatternContext;
private:
/// Pointer to the class representing the expression whose value is to be
/// substituted.
- std::unique_ptr<ExpressionAST> ExpressionASTPointer;
+ std::unique_ptr<Expression> ExpressionPointer;
public:
- NumericSubstitution(FileCheckPatternContext *Context, StringRef Expr,
- std::unique_ptr<ExpressionAST> ExprAST, size_t InsertIdx)
- : Substitution(Context, Expr, InsertIdx) {
- ExpressionASTPointer = std::move(ExprAST);
- }
+ NumericSubstitution(FileCheckPatternContext *Context, StringRef ExpressionStr,
+ std::unique_ptr<Expression> ExpressionPointer,
+ size_t InsertIdx)
+ : Substitution(Context, ExpressionStr, InsertIdx),
+ ExpressionPointer(std::move(ExpressionPointer)) {}
/// \returns a string containing the result of evaluating the expression in
/// this substitution, or an error if evaluation failed.
/// automatically free them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<NumericVariable>> NumericVariables;
+ /// Vector holding pointers to all parsed expressions. Used to automatically
+ /// free the expressions once they are guaranteed to no longer be used.
+ std::vector<std::unique_ptr<Expression>> Expressions;
+
/// Vector holding pointers to all substitutions. Used to automatically free
/// them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<Substitution>> Substitutions;
/// Makes a new numeric substitution and registers it for destruction when
/// the context is destroyed.
- Substitution *
- makeNumericSubstitution(StringRef ExpressionStr,
- std::unique_ptr<ExpressionAST> ExpressionAST,
- size_t InsertIdx);
+ Substitution *makeNumericSubstitution(StringRef ExpressionStr,
+ std::unique_ptr<Expression> Expression,
+ size_t InsertIdx);
};
/// Class to represent an error holding a diagnostic with location information
std::map<StringRef, unsigned> VariableDefs;
/// Structure representing the definition of a numeric variable in a pattern.
- /// It holds the pointer to the class representing the numeric variable whose
- /// value is being defined and the number of the parenthesis group in
- /// RegExStr to capture that value.
+ /// It holds the pointer to the class instance holding the value and matching
+ /// format of the numeric variable whose value is being defined and the
+ /// number of the parenthesis group in RegExStr to capture that value.
struct NumericVariableMatch {
- /// Pointer to class representing the numeric variable whose value is being
- /// defined.
+ /// Pointer to class instance holding the value and matching format of the
+ /// numeric variable being defined.
NumericVariable *DefinedNumericVariable;
/// Number of the parenthesis group in RegExStr that captures the value of
/// \p IsLegacyLineExpr indicates whether \p Expr should be a legacy @LINE
/// expression and \p Context points to the class instance holding the live
/// string and numeric variables. \returns a pointer to the class instance
- /// representing the AST of the expression whose value must be substitued, or
- /// an error holding a diagnostic against \p SM if parsing fails. If
- /// substitution was successful, sets \p DefinedNumericVariable to point to
- /// the class representing the numeric variable defined in this numeric
- /// substitution block, or None if this block does not define any variable.
- static Expected<std::unique_ptr<ExpressionAST>> parseNumericSubstitutionBlock(
+ /// representing the expression whose value must be substitued, or an error
+ /// holding a diagnostic against \p SM if parsing fails. If substitution was
+ /// successful, sets \p DefinedNumericVariable to point to the class
+ /// representing the numeric variable defined in this numeric substitution
+ /// block, or None if this block does not define any variable.
+ static Expected<std::unique_ptr<Expression>> parseNumericSubstitutionBlock(
StringRef Expr, Optional<NumericVariable *> &DefinedNumericVariable,
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
/// should defining such a variable be invalid.
static Expected<NumericVariable *> parseNumericVariableDefinition(
StringRef &Expr, FileCheckPatternContext *Context,
- Optional<size_t> LineNumber, const SourceMgr &SM);
+ Optional<size_t> LineNumber, ExpressionFormat ImplicitFormat,
+ const SourceMgr &SM);
/// Parses \p Name as a (pseudo if \p IsPseudo is true) numeric variable use
/// at line \p LineNumber, or before input is parsed if \p LineNumber is
/// None. Parameter \p Context points to the class instance holding the live
static Expected<std::unique_ptr<NumericVariableUse>> parseNumericVariableUse(
StringRef Name, bool IsPseudo, Optional<size_t> LineNumber,
FileCheckPatternContext *Context, const SourceMgr &SM);
- enum class AllowedOperand { LineVar, Literal, Any };
+ enum class AllowedOperand { LineVar, LegacyLiteral, Any };
/// Parses \p Expr for use of a numeric operand at line \p LineNumber, or
/// before input is parsed if \p LineNumber is None. Accepts both literal
/// values and numeric variables, depending on the value of \p AO. Parameter
62 BAD12: [[#@LINE-1]] NOT HERE
63 ERR12: note: with "@LINE-1" equal to "61"
64
-65 CHECK: [[#@LINE]] CHECK
-66 CHECK: [[# @LINE]] CHECK
-67 CHECK: [[# @LINE ]] CHECK
+; RUN: %ProtectFileCheckOutput \
+; RUN: not FileCheck --check-prefix BAD13 --input-file %s %s 2>&1 \
+; RUN: | FileCheck --check-prefix ERR13 %s
68
-69 CHECK: [[#@LINE-1]]
-70 CHECK: [[# @LINE-1]] CHECK
-71 CHECK: [[# @LINE -1]] CHECK
-72 CHECK: [[# @LINE - 1]] CHECK
-73 CHECK: [[# @LINE - 1 ]] CHECK
+69 BAD13: [[@LINE-0xA]]
+70 ERR13: line-count.txt:[[#@LINE-1]]:20: error: unexpected characters at end of expression 'xA'
+71
+72 CHECK: [[#@LINE]] CHECK
+73 CHECK: [[# @LINE]] CHECK
+74 CHECK: [[# @LINE ]] CHECK
+75
+76 CHECK: [[#@LINE-1]]
+77 CHECK: [[# @LINE-1]] CHECK
+78 CHECK: [[# @LINE -1]] CHECK
+79 CHECK: [[# @LINE - 1]] CHECK
+80 CHECK: [[# @LINE - 1 ]] CHECK
; Invalid variable name: starts with a digit.
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D#10VALUE=10 --input-file %s %s 2>&1 \
-RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLIFMT
+RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLINAME
-NUMERRCLIFMT:Global defines:1:46: error: invalid variable name
-NUMERRCLIFMT-NEXT:Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}})
- NUMERRCLIFMT-NEXT: ^
+NUMERRCLINAME:Global defines:1:46: error: invalid variable name
+NUMERRCLINAME-NEXT:Global define #1: #10VALUE=10 (parsed as: {{\[\[#10VALUE:10\]\]}})
+ NUMERRCLINAME-NEXT: ^
; Invalid definition of pseudo variable.
RUN: %ProtectFileCheckOutput \
NUMERRCLITRAIL:Global defines:1:51: error: unexpected characters after numeric variable name
NUMERRCLITRAIL-NEXT:Global define #1: #VALUE+2=10 (parsed as: {{\[\[#VALUE\+2:10\]\]}})
NUMERRCLITRAIL-NEXT: ^
+
+; Invalid format for variable.
+RUN: not FileCheck -D#,VALUE=10 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix NUMERRCLIFMT
+
+NUMERRCLIFMT:Global defines:1:45: error: invalid matching format specification in expression
+NUMERRCLIFMT-NEXT:Global define #1: #,VALUE=10 (parsed as: {{\[\[#,VALUE:10\]\]}})
+ NUMERRCLIFMT-NEXT: ^
; 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#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' -check-prefixes CHECKNUM1,CHECKNUM2 -input-file %s %s
+; Tests with default format specifier.
+RUN: FileCheck -D#NUMVAL1=8 -D#NUMVAL2='NUMVAL1 + 4' --check-prefixes CHECKNUM1,CHECKNUM2 --input-file %s %s
RUN: %ProtectFileCheckOutput \
-RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix CHECKNUM1 -input-file %s %s 2>&1 \
-RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG1
+RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix CHECKNUM1 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG1
RUN: %ProtectFileCheckOutput \
-RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix CHECKNUM2 -input-file %s %s 2>&1 \
-RUN: | FileCheck %s --strict-whitespace -check-prefix NUMERRMSG2
+RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix CHECKNUM2 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG2
RUN: %ProtectFileCheckOutput \
-RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 -check-prefix NUMNOT -input-file %s %s 2>&1 \
-RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG1
+RUN: not FileCheck -D#NUMVAL1=8 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG1
RUN: %ProtectFileCheckOutput \
-RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 -check-prefix NUMNOT -input-file %s %s 2>&1 \
-RUN: | FileCheck %s --strict-whitespace -check-prefixes NOT-NUMERRMSG2
-RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 -check-prefixes NUMNOT -input-file %s %s
+RUN: not FileCheck -D#NUMVAL1=7 -D#NUMVAL2=12 --check-prefix NUMNOT1 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG2
+
+RUN: FileCheck -D#NUMVAL1=7 -D#NUMVAL2=8 --check-prefix NUMNOT1 --input-file %s %s
Numeric value #1 = 8
Numeric value #2 = 12
CHECKNUM1: Numeric value #1 = [[#NUMVAL1]]
CHECKNUM2: Numeric value #2 = [[#NUMVAL2]]
-NUMNOT-NOT: Numeric value #1 = [[#NUMVAL1]]
-NUMNOT-NOT: Numeric value #2 = [[#NUMVAL2]]
+NUMNOT1-NOT: Numeric value #1 = [[#NUMVAL1]]
+NUMNOT1-NOT: Numeric value #2 = [[#NUMVAL2]]
NUMERRMSG1: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM1: expected string not found in input
NUMERRMSG1: defines.txt:1:1: note: scanning from here
NUMERRMSG2: defines.txt:1:1: note: with "NUMVAL2" equal to "8"
NUMERRMSG2: defines.txt:[[#@LINE-14]]:1: note: possible intended match here
-NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
+NOT-NUMERRMSG1: defines.txt:[[#@LINE-13]]:14: error: {{NUMNOT1}}-NOT: excluded string found in input
NOT-NUMERRMSG1: defines.txt:[[#@LINE-18]]:1: note: found here
NOT-NUMERRMSG1: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL1" equal to "8"
-NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
+NOT-NUMERRMSG2: defines.txt:[[#@LINE-16]]:14: error: {{NUMNOT1}}-NOT: excluded string found in input
NOT-NUMERRMSG2: defines.txt:[[#@LINE-21]]:1: note: found here
NOT-NUMERRMSG2: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL2" equal to "12"
+
+; Tests with explicit format specifiers.
+RUN: FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=12 --check-prefix CHECKNUM3 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG3
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4=8 --check-prefix CHECKNUM4 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NUMERRMSG4
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#%X,NUMVAL3=8 -D#%X,NUMVAL4=8 --check-prefix NUMNOT2 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG3
+
+RUN: %ProtectFileCheckOutput \
+RUN: not FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=12 --check-prefix NUMNOT2 --input-file %s %s 2>&1 \
+RUN: | FileCheck %s --strict-whitespace --check-prefix NOT-NUMERRMSG4
+
+RUN: FileCheck -D#%X,NUMVAL3=7 -D#%X,NUMVAL4=8 --check-prefix NUMNOT2 --input-file %s %s
+
+; Test with explicit and default format specifiers.
+RUN: FileCheck -D#NUMVAL3=8 -D#%X,NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
+; Test with explicit and implicit format specifiers.
+RUN: FileCheck -D#%X,NUMVAL3=8 -D#NUMVAL4='NUMVAL3 + 4' --check-prefixes CHECKNUM3,CHECKNUM4 --input-file %s %s
+
+Numeric value #3 = 8
+Numeric value #4 = C
+CHECKNUM3: Numeric value #3 = [[#NUMVAL3]]
+CHECKNUM4: Numeric value #4 = [[#NUMVAL4]]
+NUMNOT2-NOT: Numeric value #3 = [[#NUMVAL3]]
+NUMNOT2-NOT: Numeric value #4 = [[#NUMVAL4]]
+
+NUMERRMSG3: defines.txt:[[#@LINE-5]]:12: error: CHECKNUM3: expected string not found in input
+NUMERRMSG3: defines.txt:1:1: note: scanning from here
+NUMERRMSG3: defines.txt:1:1: note: with "NUMVAL3" equal to "7"
+NUMERRMSG3: defines.txt:[[#@LINE-10]]:1: note: possible intended match here
+
+NUMERRMSG4: defines.txt:[[#@LINE-9]]:12: error: CHECKNUM4: expected string not found in input
+NUMERRMSG4: defines.txt:1:1: note: scanning from here
+NUMERRMSG4: defines.txt:1:1: note: with "NUMVAL4" equal to "8"
+NUMERRMSG4: defines.txt:[[#@LINE-14]]:1: note: possible intended match here
+
+NOT-NUMERRMSG3: defines.txt:[[#@LINE-13]]:14: error: {{NUMNOT2}}-NOT: excluded string found in input
+NOT-NUMERRMSG3: defines.txt:[[#@LINE-18]]:1: note: found here
+NOT-NUMERRMSG3: defines.txt:[[#@LINE-19]]:1: note: with "NUMVAL3" equal to "8"
+
+NOT-NUMERRMSG4: defines.txt:[[#@LINE-16]]:14: error: {{NUMNOT2}}-NOT: excluded string found in input
+NOT-NUMERRMSG4: defines.txt:[[#@LINE-21]]:1: note: found here
+NOT-NUMERRMSG4: defines.txt:[[#@LINE-22]]:1: note: with "NUMVAL4" equal to "C"
; We use CHECK-NEXT directives to force a match on all lines with digits.
-; Numeric variable definition without spaces.
-DEF NO SPC
+; Numeric variable definition with default matching format without spaces.
+DEF DEFAULT FMT NO SPC
10
-CHECK-LABEL: DEF NO SPC
+CHECK-LABEL: DEF DEFAULT FMT NO SPC
CHECK-NEXT: [[#VAR1:]]
-; Numeric variable definition with different spacing.
-DEF SPC
+; Numeric variable definition with default matching format with different
+; spacing.
+DEF DEFAULT FMT SPC
11
11
11
-CHECK-LABEL: DEF SPC
+CHECK-LABEL: DEF DEFAULT FMT SPC
CHECK-NEXT: [[# VAR1a:]]
CHECK-NEXT: [[# VAR1b :]]
CHECK-NEXT: [[# VAR1c : ]]
CHECK-LABEL: REDEF
CHECK-NEXT: [[#VAR1:]]
-; Numeric expressions using variables defined on other lines without spaces.
-USE NO SPC
+; Numeric variable definition with explicit matching format.
+DEF FMT
+c
+D
+CHECK-LABEL: DEF FMT
+CHECK-NEXT: [[#%x,VAR2:]]
+CHECK-NEXT: [[#%X,VAR3:]]
+
+; Numeric variable definition with explicit matching format with different
+; spacing.
+DEF FMT SPC
+c
+CHECK-LABEL: DEF FMT SPC
+CHECK-NEXT: [[# %x , VAR2a : ]]
+
+; Numeric variable definition with unsupported matching format.
+RUN: not FileCheck --check-prefixes ERR,INVALID-FMT-SPEC1 --input-file %s %s 2>&1 \
+RUN: | FileCheck --check-prefix INVALID-FMT-SPEC-MSG1 --strict-whitespace %s
+RUN: not FileCheck --check-prefixes ERR,INVALID-FMT-SPEC2 --input-file %s %s 2>&1 \
+RUN: | FileCheck --check-prefix INVALID-FMT-SPEC-MSG2 --strict-whitespace %s
+
+DEF INVALID FMT
+INVVAR1=a
+INVVAR2=11
+ERR-LABEL: DEF INVALID FMT
+INVALID-FMT-SPEC1-NEXT: INVVAR1=[[#%c,INVVAR1:]]
+INVALID-FMT-SPEC2-NEXT: INVVAR2=[[#%hhd,INVVAR2:]]
+INVALID-FMT-SPEC-MSG1: numeric-expression.txt:[[#@LINE-2]]:37: error: invalid format specifier in expression
+INVALID-FMT-SPEC-MSG1-NEXT: {{I}}NVALID-FMT-SPEC1-NEXT: INVVAR1={{\[\[#%c,INVVAR1:\]\]}}
+INVALID-FMT-SPEC-MSG1-NEXT: {{^}} ^{{$}}
+INVALID-FMT-SPEC-MSG2: numeric-expression.txt:[[#@LINE-4]]:37: error: invalid format specifier in expression
+INVALID-FMT-SPEC-MSG2-NEXT: {{I}}NVALID-FMT-SPEC2-NEXT: INVVAR2={{\[\[#%hhd,INVVAR2:\]\]}}
+INVALID-FMT-SPEC-MSG2-NEXT: {{^}} ^{{$}}
+
+; Numeric expressions in explicit matching format and default matching rule using
+; variables defined on other lines without spaces.
+USE DEF FMT IMPL MATCH
11
12
10
+c
+d
+b
+1a
+D
+E
+C
+1B
11
11
11
-CHECK-LABEL: USE
-CHECK-NEXT: [[#VAR1]]
-CHECK-NEXT: [[#VAR1+1]]
-CHECK-NEXT: [[#VAR1-1]]
-CHECK-NEXT: [[#VAR1a]]
-CHECK-NEXT: [[#VAR1b]]
-CHECK-NEXT: [[#VAR1c]]
+c
+c
+c
+c
+c
+CHECK-LABEL: USE DEF FMT IMPL MATCH
+CHECK-NEXT: [[#%u,VAR1]]
+CHECK-NEXT: [[#%u,VAR1+1]]
+CHECK-NEXT: [[#%u,VAR1-1]]
+CHECK-NEXT: [[#%x,VAR2]]
+CHECK-NEXT: [[#%x,VAR2+1]]
+CHECK-NEXT: [[#%x,VAR2-1]]
+CHECK-NEXT: [[#%x,VAR2+14]]
+CHECK-NEXT: [[#%X,VAR3]]
+CHECK-NEXT: [[#%X,VAR3+1]]
+CHECK-NEXT: [[#%X,VAR3-1]]
+CHECK-NEXT: [[#%X,VAR3+14]]
+CHECK-NEXT: [[#%u,VAR1a]]
+CHECK-NEXT: [[#%u,VAR1b]]
+CHECK-NEXT: [[#%u,VAR1c]]
+CHECK-NEXT: [[#%x,VAR2a]]
-; Numeric expressions using variables defined on other lines with different
-; spacing.
-USE SPC
+; Numeric expressions in explicit matching format and default matching rule using
+; variables defined on other lines with different spacing.
+USE EXPL FMT IMPL MATCH SPC
11
11
+11
+12
12
12
12
12
+12
+10
+10
10
10
10
10
-CHECK-LABEL: USE 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 ]]
+CHECK-LABEL: USE EXPL FMT IMPL MATCH SPC
+CHECK-NEXT: [[#%u, VAR1]]
+CHECK-NEXT: [[# %u, VAR1]]
+CHECK-NEXT: [[# %u, VAR1 ]]
+CHECK-NEXT: [[#%u, VAR1+1]]
+CHECK-NEXT: [[# %u, VAR1+1]]
+CHECK-NEXT: [[# %u , VAR1+1]]
+CHECK-NEXT: [[# %u , VAR1 +1]]
+CHECK-NEXT: [[# %u , VAR1 + 1]]
+CHECK-NEXT: [[# %u , VAR1 + 1 ]]
+CHECK-NEXT: [[#%u, VAR1-1]]
+CHECK-NEXT: [[# %u, VAR1-1]]
+CHECK-NEXT: [[# %u , VAR1-1]]
+CHECK-NEXT: [[# %u , VAR1 -1]]
+CHECK-NEXT: [[# %u , VAR1 - 1]]
+CHECK-NEXT: [[# %u , VAR1 - 1 ]]
+
+; Numeric expressions in implicit matching format and default matching rule using
+; variables defined on other lines.
+USE IMPL FMT IMPL MATCH
+11
+12
+10
+c
+d
+b
+1a
+D
+E
+C
+1B
+CHECK-LABEL: USE IMPL FMT IMPL MATCH
+CHECK-NEXT: [[#VAR1]]
+CHECK-NEXT: [[#VAR1+1]]
+CHECK-NEXT: [[#VAR1-1]]
+CHECK-NEXT: [[#VAR2]]
+CHECK-NEXT: [[#VAR2+1]]
+CHECK-NEXT: [[#VAR2-1]]
+CHECK-NEXT: [[#VAR2+14]]
+CHECK-NEXT: [[#VAR3]]
+CHECK-NEXT: [[#VAR3+1]]
+CHECK-NEXT: [[#VAR3-1]]
+CHECK-NEXT: [[#VAR3+14]]
; Numeric expressions using variables defined on other lines and an immediate
; interpreted as an unsigned value.
; Note: 9223372036854775819 = 0x8000000000000000 + 11
-; 9223372036854775808 = 0x8000000000000000
-USE UNSIGNED IMM
+USE IMPL FMT IMPL MATCH UNSIGNED IMM
9223372036854775819
-CHECK-LABEL: USE UNSIGNED IMM
-CHECK-NEXT: [[#VAR1+9223372036854775808]]
+CHECK-LABEL: USE IMPL FMT IMPL MATCH UNSIGNED IMM
+CHECK-NEXT: [[#VAR1+0x8000000000000000]]
+
+; Numeric expressions with matching format overriding the implicit format of
+; variables defined on other lines.
+USE CONV FMT IMPL MATCH
+b
+B
+12
+13
+CHECK-LABEL: USE CONV FMT IMPL MATCH
+CHECK-NEXT: [[# %x, VAR1]]
+CHECK-NEXT: [[# %X, VAR1]]
+CHECK-NEXT: [[# %u, VAR2]]
+CHECK-NEXT: [[# %u, VAR3]]
+
+; Conflicting implicit format.
+RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT-MSG %s
+
+VAR USE IMPL FMT CONFLICT
+23
+FMT-CONFLICT-LABEL: VAR USE IMPL FMT CONFLICT
+FMT-CONFLICT-NEXT: [[#VAR1 + VAR2]]
+FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:23: error: variables with conflicting format specifier: need an explicit one
+FMT-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}}
+FMT-CONFLICT-MSG-NEXT: {{^ \^$}}
+
+; Explicitly specified format can override conflicting implicit formats.
+VAR USE IMPL OVERRIDE FMT CONFLICT
+23
+CHECK-LABEL: VAR USE IMPL OVERRIDE FMT CONFLICT
+CHECK-NEXT: [[# %u, VAR1 + VAR2]]
; Numeric expressions using more than one variable defined on other lines.
USE MULTI VAR
31
42
CHECK-LABEL: USE MULTI VAR
-CHECK-NEXT: [[#VAR2:]]
-CHECK-NEXT: [[#VAR1+VAR2]]
+CHECK-NEXT: [[#VAR4:]]
+CHECK-NEXT: [[#VAR1+VAR4]]
; Numeric expression using a variable defined from a numeric expression.
DEF EXPR GOOD MATCH
; Empty numeric expression.
EMPTY NUM EXPR
foo 104 bar
-; CHECK-LABEL: EMPTY NUM EXPR
-; CHECK-NEXT: foo [[#]] bar
+CHECK-LABEL: EMPTY NUM EXPR
+CHECK-NEXT: foo [[#]] bar
; Numeric expression using undefined variables.
RUN: %ProtectFileCheckOutput \
NUMVAR: 10000000000000000000000
BIGVAL-LABEL: BIG VALUE
BIGVAL-NEXT: NUMVAR: [[#NUMVAR:]]
-BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: Unable to represent numeric value
+BIGVAL-MSG: numeric-expression.txt:[[#@LINE-3]]:9: error: unable to represent numeric value
BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000
BIGVAL-MSG-NEXT: {{^}} ^{{$}}
SAME-LINE-USE-MSG2: numeric-expression.txt:[[#@LINE-1]]:42: error: numeric variable 'VAR2' defined earlier in the same CHECK directive
SAME-LINE-USE-MSG2-NEXT: {{S}}AME-LINE-USE2-NEXT: {{\[\[#VAR2:VAR1\+1\]\] \[\[#VAR2\+1\]\]}}
SAME-LINE-USE-MSG2-NEXT: {{^}} ^{{$}}
+
+; Invalid change of format in variable redefinition.
+RUN: not FileCheck --check-prefix REDEF-NEW-FMT --input-file %s %s 2>&1 \
+RUN: | FileCheck --strict-whitespace --check-prefix REDEF-NEW-FMT-MSG %s
+
+VAR REDEF FMT CHANGE
+22
+DC
+REDEF-NEW-FMT-LABEL: VAR REDEF FMT CHANGE
+REDEF-NEW-FMT-NEXT: [[#VAR1:]]
+REDEF-NEW-FMT-NEXT: [[#%X,VAR1:]]
+REDEF-NEW-FMT-MSG: numeric-expression.txt:[[#@LINE-1]]:31: error: format different from previous variable definition
+REDEF-NEW-FMT-MSG-NEXT: {{R}}EDEF-NEW-FMT-NEXT: {{\[\[#%X,VAR1:\]\]}}
+REDEF-NEW-FMT-MSG-NEXT: {{^ \^$}}
; Invalid variable name: starts with a digit.
RUN: %ProtectFileCheckOutput \
RUN: not FileCheck -D10VALUE=10 --input-file %s %s 2>&1 \
-RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix ERRCLIFMT
+RUN: | FileCheck %s --strict-whitespace --match-full-lines --check-prefix ERRCLINAME
-ERRCLIFMT:Global defines:1:19: error: invalid variable name
-ERRCLIFMT-NEXT:Global define #1: 10VALUE=10
-ERRCLIFMT-NEXT: ^
+ERRCLINAME:Global defines:1:19: error: invalid variable name
+ERRCLINAME-NEXT:Global define #1: 10VALUE=10
+ERRCLINAME-NEXT: ^
; Invalid definition of pseudo variable.
RUN: %ProtectFileCheckOutput \
#include "llvm/Support/FileCheck.h"
#include "../lib/Support/FileCheckImpl.h"
+#include "llvm/Support/Regex.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
+#include <tuple>
#include <unordered_set>
using namespace llvm;
class FileCheckTest : public ::testing::Test {};
+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;
+}
+
+template <typename ErrorT>
+static void expectError(StringRef ExpectedMsg, Error Err) {
+ bool ErrorHandled = false;
+ EXPECT_THAT_ERROR(handleErrors(std::move(Err),
+ [&](const ErrorT &E) {
+ EXPECT_NE(
+ E.message().find(ExpectedMsg.str()),
+ std::string::npos);
+ ErrorHandled = true;
+ }),
+ Succeeded());
+ EXPECT_TRUE(ErrorHandled);
+}
+
+static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
+ expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));
+}
+
+struct ExpressionFormatParameterisedFixture
+ : public ::testing::TestWithParam<
+ std::tuple<ExpressionFormat::Kind, bool, bool>> {
+ void SetUp() { std::tie(Kind, AllowHex, AllowUpperHex) = GetParam(); }
+
+ ExpressionFormat::Kind Kind;
+ bool AllowHex;
+ bool AllowUpperHex;
+};
+
+TEST_P(ExpressionFormatParameterisedFixture, Format) {
+ SourceMgr SM;
+ ExpressionFormat Format(Kind);
+
+ Expected<StringRef> WildcardPattern = Format.getWildcardRegex();
+ ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());
+ Regex WildcardRegex(*WildcardPattern);
+ ASSERT_TRUE(WildcardRegex.isValid());
+ // Does not match empty string.
+ EXPECT_FALSE(WildcardRegex.match(""));
+ // Matches all decimal digits and matches several of them.
+ SmallVector<StringRef, 4> Matches;
+ StringRef DecimalDigits = "0123456789";
+ ASSERT_TRUE(WildcardRegex.match(DecimalDigits, &Matches));
+ EXPECT_EQ(Matches[0], DecimalDigits);
+ // Check non digits or digits with wrong casing are not matched.
+ if (AllowHex) {
+ StringRef HexOnlyDigits[] = {"abcdef", "ABCDEF"};
+ StringRef AcceptedHexOnlyDigits =
+ AllowUpperHex ? HexOnlyDigits[1] : HexOnlyDigits[0];
+ StringRef RefusedHexOnlyDigits =
+ AllowUpperHex ? HexOnlyDigits[0] : HexOnlyDigits[1];
+ ASSERT_TRUE(WildcardRegex.match(AcceptedHexOnlyDigits, &Matches));
+ EXPECT_EQ(Matches[0], AcceptedHexOnlyDigits);
+ EXPECT_FALSE(WildcardRegex.match(RefusedHexOnlyDigits));
+
+ EXPECT_FALSE(WildcardRegex.match("g"));
+ EXPECT_FALSE(WildcardRegex.match("G"));
+ } else {
+ EXPECT_FALSE(WildcardRegex.match("a"));
+ EXPECT_FALSE(WildcardRegex.match("A"));
+ }
+
+ Expected<std::string> MatchingString = Format.getMatchingString(0U);
+ ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
+ EXPECT_EQ(*MatchingString, "0");
+ MatchingString = Format.getMatchingString(9U);
+ ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
+ EXPECT_EQ(*MatchingString, "9");
+ Expected<std::string> TenMatchingString = Format.getMatchingString(10U);
+ ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded());
+ Expected<std::string> FifteenMatchingString = Format.getMatchingString(15U);
+ ASSERT_THAT_EXPECTED(FifteenMatchingString, Succeeded());
+ StringRef ExpectedTenMatchingString, ExpectedFifteenMatchingString;
+ if (AllowHex) {
+ if (AllowUpperHex) {
+ ExpectedTenMatchingString = "A";
+ ExpectedFifteenMatchingString = "F";
+ } else {
+ ExpectedTenMatchingString = "a";
+ ExpectedFifteenMatchingString = "f";
+ }
+ } else {
+ ExpectedTenMatchingString = "10";
+ ExpectedFifteenMatchingString = "15";
+ }
+ EXPECT_EQ(*TenMatchingString, ExpectedTenMatchingString);
+ EXPECT_EQ(*FifteenMatchingString, ExpectedFifteenMatchingString);
+
+ StringRef BufferizedValidValueStr = bufferize(SM, "0");
+ Expected<uint64_t> Val =
+ Format.valueFromStringRepr(BufferizedValidValueStr, SM);
+ ASSERT_THAT_EXPECTED(Val, Succeeded());
+ EXPECT_EQ(*Val, 0U);
+ BufferizedValidValueStr = bufferize(SM, "9");
+ Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM);
+ ASSERT_THAT_EXPECTED(Val, Succeeded());
+ EXPECT_EQ(*Val, 9U);
+ StringRef BufferizedTenStr, BufferizedInvalidTenStr, BufferizedFifteenStr;
+ StringRef TenStr, FifteenStr, InvalidTenStr;
+ if (AllowHex) {
+ if (AllowUpperHex) {
+ TenStr = "A";
+ FifteenStr = "F";
+ InvalidTenStr = "a";
+ } else {
+ TenStr = "a";
+ FifteenStr = "f";
+ InvalidTenStr = "A";
+ }
+ } else {
+ TenStr = "10";
+ FifteenStr = "15";
+ InvalidTenStr = "A";
+ }
+ BufferizedTenStr = bufferize(SM, TenStr);
+ Val = Format.valueFromStringRepr(BufferizedTenStr, SM);
+ ASSERT_THAT_EXPECTED(Val, Succeeded());
+ EXPECT_EQ(*Val, 10U);
+ BufferizedFifteenStr = bufferize(SM, FifteenStr);
+ Val = Format.valueFromStringRepr(BufferizedFifteenStr, SM);
+ ASSERT_THAT_EXPECTED(Val, Succeeded());
+ EXPECT_EQ(*Val, 15U);
+ // Wrong casing is not tested because valueFromStringRepr() relies on
+ // StringRef's getAsInteger() which does not allow to restrict casing.
+ BufferizedInvalidTenStr = bufferize(SM, InvalidTenStr);
+ expectDiagnosticError(
+ "unable to represent numeric value",
+ Format.valueFromStringRepr(bufferize(SM, "G"), SM).takeError());
+
+ // Check boolean operator.
+ EXPECT_TRUE(bool(Format));
+}
+
+INSTANTIATE_TEST_CASE_P(
+ AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture,
+ ::testing::Values(
+ std::make_tuple(ExpressionFormat::Kind::Unsigned, /*AllowHex=*/false,
+ /*AllowUpperHex=*/false),
+ std::make_tuple(ExpressionFormat::Kind::HexLower, /*AllowHex=*/true,
+ /*AllowUpperHex=*/false),
+ std::make_tuple(ExpressionFormat::Kind::HexUpper, /*AllowHex=*/true,
+ /*AllowUpperHex=*/true)), );
+
+TEST_F(FileCheckTest, NoFormatProperties) {
+ ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+ expectError<StringError>("trying to match value with invalid format",
+ NoFormat.getWildcardRegex().takeError());
+ expectError<StringError>("trying to match value with invalid format",
+ NoFormat.getMatchingString(18).takeError());
+ EXPECT_FALSE(bool(NoFormat));
+}
+
+TEST_F(FileCheckTest, ConflictFormatProperties) {
+ ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
+ expectError<StringError>("trying to match value with invalid format",
+ ConflictFormat.getWildcardRegex().takeError());
+ expectError<StringError>("trying to match value with invalid format",
+ ConflictFormat.getMatchingString(18).takeError());
+ EXPECT_FALSE(bool(ConflictFormat));
+}
+
+TEST_F(FileCheckTest, FormatEqualityOperators) {
+ ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
+ ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned);
+ EXPECT_TRUE(UnsignedFormat == UnsignedFormat2);
+ EXPECT_FALSE(UnsignedFormat != UnsignedFormat2);
+
+ ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower);
+ EXPECT_FALSE(UnsignedFormat == HexLowerFormat);
+ EXPECT_TRUE(UnsignedFormat != HexLowerFormat);
+
+ ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+ ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat);
+ EXPECT_FALSE(NoFormat == NoFormat2);
+ EXPECT_TRUE(NoFormat != NoFormat2);
+
+ ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
+ ExpressionFormat ConflictFormat2(ExpressionFormat::Kind::Conflict);
+ EXPECT_TRUE(ConflictFormat == ConflictFormat2);
+ EXPECT_FALSE(ConflictFormat != ConflictFormat2);
+}
+
+TEST_F(FileCheckTest, FormatKindEqualityOperators) {
+ ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
+ EXPECT_TRUE(UnsignedFormat == ExpressionFormat::Kind::Unsigned);
+ EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned);
+ EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower);
+ EXPECT_TRUE(UnsignedFormat != ExpressionFormat::Kind::HexLower);
+ ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
+ EXPECT_TRUE(ConflictFormat == ExpressionFormat::Kind::Conflict);
+ EXPECT_FALSE(ConflictFormat != ExpressionFormat::Kind::Conflict);
+ ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
+ EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);
+ EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
+}
+
TEST_F(FileCheckTest, Literal) {
// Eval returns the literal's value.
ExpressionLiteral Ten(10);
Expected<uint64_t> Value = Ten.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(10U, *Value);
+ EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat);
// Max value can be correctly represented.
ExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
return Str;
}
+TEST_F(FileCheckTest, Expression) {
+ std::unique_ptr<ExpressionLiteral> Ten =
+ std::make_unique<ExpressionLiteral>(10);
+ ExpressionLiteral *TenPtr = Ten.get();
+ Expression Expr(std::move(Ten),
+ ExpressionFormat(ExpressionFormat::Kind::HexLower));
+ EXPECT_EQ(Expr.getAST(), TenPtr);
+ EXPECT_EQ(Expr.getFormat(), ExpressionFormat::Kind::HexLower);
+}
+
static void
expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
Error Err) {
TEST_F(FileCheckTest, NumericVariable) {
// Undefined variable: getValue and eval fail, error returned by eval holds
// the name of the undefined variable.
- NumericVariable FooVar("FOO", 1);
+ NumericVariable FooVar("FOO",
+ ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
EXPECT_EQ("FOO", FooVar.getName());
+ EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
NumericVariableUse FooVarUse("FOO", &FooVar);
+ EXPECT_EQ(FooVarUse.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
EXPECT_FALSE(FooVar.getValue());
Expected<uint64_t> EvalResult = FooVarUse.eval();
expectUndefErrors({"FOO"}, EvalResult.takeError());
}
TEST_F(FileCheckTest, Binop) {
- NumericVariable FooVar("FOO", 1);
+ NumericVariable FooVar("FOO",
+ ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
FooVar.setValue(42);
std::unique_ptr<NumericVariableUse> FooVarUse =
std::make_unique<NumericVariableUse>("FOO", &FooVar);
- NumericVariable BarVar("BAR", 2);
+ NumericVariable BarVar("BAR",
+ ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
BarVar.setValue(18);
std::unique_ptr<NumericVariableUse> BarVarUse =
std::make_unique<NumericVariableUse>("BAR", &BarVar);
BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
- // Defined variable: eval returns right value.
+ // Defined variables: eval returns right value; implicit format is as
+ // expected.
Expected<uint64_t> Value = Binop.eval();
ASSERT_THAT_EXPECTED(Value, Succeeded());
EXPECT_EQ(60U, *Value);
+ EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
// 1 undefined variable: eval fails, error contains name of undefined
// variable.
BarVar.clearValue();
Value = Binop.eval();
expectUndefErrors({"FOO", "BAR"}, Value.takeError());
+
+ // Literal + Variable has format of variable.
+ FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
+ std::unique_ptr<ExpressionLiteral> Eighteen =
+ std::make_unique<ExpressionLiteral>(18);
+ Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen));
+ EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
+ FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
+ Eighteen = std::make_unique<ExpressionLiteral>(18);
+ Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse));
+ EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
+
+ // Variables with different implicit format conflict.
+ NumericVariable BazVar("BAZ",
+ ExpressionFormat(ExpressionFormat::Kind::HexLower), 3);
+ FooVarUse = std::make_unique<NumericVariableUse>("BAZ", &FooVar);
+ std::unique_ptr<NumericVariableUse> BazVarUse =
+ std::make_unique<NumericVariableUse>("BAZ", &BazVar);
+ Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse));
+ EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict);
}
TEST_F(FileCheckTest, ValidVarNameStart) {
EXPECT_FALSE(Pattern::isValidVarNameStart(':'));
}
-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;
-}
-
-template <typename ErrorT>
-static void expectError(StringRef ExpectedMsg, Error Err) {
- bool ErrorHandled = false;
- EXPECT_THAT_ERROR(handleErrors(std::move(Err),
- [&](const ErrorT &E) {
- EXPECT_NE(
- E.message().find(ExpectedMsg.str()),
- std::string::npos);
- ErrorHandled = true;
- }),
- Succeeded());
- EXPECT_TRUE(ErrorHandled);
-}
-
-static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
- expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));
-}
-
TEST_F(FileCheckTest, ParseVar) {
SourceMgr SM;
StringRef OrigVarName = bufferize(SM, "GoodVar42");
size_t getLineNumber() const { return LineNumber; }
- Expected<std::unique_ptr<ExpressionAST>>
+ Expected<std::unique_ptr<Expression>>
parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) {
StringRef ExprBufferRef = bufferize(SM, Expr);
Optional<NumericVariable *> DefinedNumericVariable;
expectDiagnosticError("unexpected characters after numeric variable name",
Tester.parseSubst("VAR GARBAGE:").takeError());
+ // Change of format.
+ expectDiagnosticError("format different from previous variable definition",
+ Tester.parseSubst("%X,FOO:").takeError());
+
+ // Invalid format.
+ expectDiagnosticError("invalid matching format specification in expression",
+ Tester.parseSubst("X,VAR1:").takeError());
+ expectDiagnosticError("invalid format specifier in expression",
+ Tester.parseSubst("%F,VAR1:").takeError());
+ expectDiagnosticError("invalid matching format specification in expression",
+ Tester.parseSubst("%X a,VAR1:").takeError());
+
+ // Acceptable variable definition.
EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR1:"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst(" VAR2:"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3 :"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3: "), Succeeded());
+
+ // Acceptable variable definition with format specifier. Use parsePattern for
+ // variables whose definition needs to be visible for later checks.
+ EXPECT_FALSE(Tester.parsePattern("[[#%u, VAR_UNSIGNED:]]"));
+ EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]"));
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded());
+
+ // Acceptable variable definition from a numeric expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded());
- // Numeric expression.
+ // Numeric expression. Switch to next line to make above valid definition
+ // available in expressions.
+ Tester.initNextPattern();
// Invalid variable name.
expectDiagnosticError("invalid operand format '%VAR'",
// Valid single operand expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
+ // Invalid format.
+ expectDiagnosticError("invalid matching format specification in expression",
+ Tester.parseSubst("X,FOO:").takeError());
+ expectDiagnosticError("invalid format specifier in expression",
+ Tester.parseSubst("%F,FOO").takeError());
+ expectDiagnosticError("invalid matching format specification in expression",
+ Tester.parseSubst("%X a,FOO").takeError());
+
// Valid expression with 2 or more operands.
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+3"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+0xC"), Succeeded());
EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO-3+FOO"), Succeeded());
expectDiagnosticError("unsupported operation '/'",
"invalid variable name",
Tester.parseSubst("2", /*IsLegacyNumExpr=*/true).takeError());
+ // Invalid hex literal in legacy @LINE expression.
+ expectDiagnosticError(
+ "unexpected characters at end of expression 'xC'",
+ Tester.parseSubst("@LINE+0xC", /*LegacyLineExpr=*/true).takeError());
+
+ // Valid expression with format specifier.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded());
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded());
+
// Valid legacy @LINE expression.
EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true),
Succeeded());
expectDiagnosticError(
"unexpected characters at end of expression '+2'",
Tester.parseSubst("@LINE+2+2", /*IsLegacyNumExpr=*/true).takeError());
+
+ // Valid expression with several variables when their implicit formats do not
+ // conflict.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+VAR_UNSIGNED"), Succeeded());
+
+ // Valid implicit format conflict in presence of explicit formats.
+ EXPECT_THAT_EXPECTED(Tester.parseSubst("%X,FOO+VAR_LOWER_HEX"), Succeeded());
+
+ // Implicit format conflict.
+ expectDiagnosticError(
+ "variables with conflicting format specifier: need an explicit one",
+ Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError());
}
TEST_F(FileCheckTest, ParsePattern) {
// Valid numeric substitution.
EXPECT_FALSE(Tester.parsePattern("[[#FOO]]"));
+
+ // Valid legacy @LINE expression.
+ EXPECT_FALSE(Tester.parsePattern("[[@LINE+2]]"));
+
+ // Invalid legacy @LINE expression with non decimal literal.
+ EXPECT_TRUE(Tester.parsePattern("[[@LINE+0x3]]"));
}
TEST_F(FileCheckTest, Match) {
expectNotFoundError(Tester.match("FAIL").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
- // Check matching a definition only matches a number.
+ // Check matching a definition only matches a number with the right format.
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
expectNotFoundError(Tester.match("FAIL").takeError());
expectNotFoundError(Tester.match("").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#%u,NUMVAR_UNSIGNED:]]");
+ expectNotFoundError(Tester.match("C").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#%x,NUMVAR_LOWER_HEX:]]");
+ expectNotFoundError(Tester.match("g").takeError());
+ expectNotFoundError(Tester.match("C").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("c"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#%X,NUMVAR_UPPER_HEX:]]");
+ expectNotFoundError(Tester.match("H").takeError());
+ expectNotFoundError(Tester.match("b").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("B"), Succeeded());
+
+ // Check matching expressions with no explicit format matches the values in
+ // the right format.
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#NUMVAR_UNSIGNED-5]]");
+ expectNotFoundError(Tester.match("f").takeError());
+ expectNotFoundError(Tester.match("F").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("15"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#NUMVAR_LOWER_HEX+1]]");
+ expectNotFoundError(Tester.match("13").takeError());
+ expectNotFoundError(Tester.match("D").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("d"), Succeeded());
+ Tester.initNextPattern();
+ Tester.parsePattern("[[#NUMVAR_UPPER_HEX+1]]");
+ expectNotFoundError(Tester.match("12").takeError());
+ expectNotFoundError(Tester.match("c").takeError());
+ EXPECT_THAT_EXPECTED(Tester.match("C"), Succeeded());
// Check matching an undefined variable returns a NotFound error.
Tester.initNextPattern();
expectNotFoundError(Tester.match("18 21").takeError());
EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded());
- // Check matching a numeric expression using @LINE after match failure uses
+ // Check matching a numeric expression using @LINE after a match failure uses
// the correct value for @LINE.
Tester.initNextPattern();
ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]"));
// Numeric substitution blocks constituted of defined numeric variables are
// substituted for the variable's value.
- NumericVariable NVar("N", 1);
+ NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),
+ 1);
NVar.setValue(10);
auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar);
- NumericSubstitution SubstitutionN(&Context, "N", std::move(NVarUse),
+ auto ExpressionN = std::make_unique<Expression>(
+ std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper));
+ NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN),
/*InsertIdx=*/30);
SubstValue = SubstitutionN.getResult();
ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
- EXPECT_EQ("10", *SubstValue);
+ EXPECT_EQ("A", *SubstValue);
// Substitution of an undefined numeric variable fails, error holds name of
// undefined variable.
GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
GlobalDefines.emplace_back(std::string("EmptyVar="));
GlobalDefines.emplace_back(std::string("#LocalNumVar1=18"));
- GlobalDefines.emplace_back(std::string("#LocalNumVar2=LocalNumVar1+2"));
+ GlobalDefines.emplace_back(std::string("#%x,LocalNumVar2=LocalNumVar1+2"));
+ GlobalDefines.emplace_back(std::string("#LocalNumVar3=0xc"));
ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
// Create @LINE pseudo numeric variable and check it is present by matching
StringRef LocalVarStr = "LocalVar";
StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
+ StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3");
StringRef EmptyVarStr = "EmptyVar";
StringRef UnknownVarStr = "UnknownVar";
Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
Optional<NumericVariable *> DefinedNumericVariable;
- Expected<std::unique_ptr<ExpressionAST>> ExpressionASTPointer =
+ Expected<std::unique_ptr<Expression>> ExpressionPointer =
P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, LineNumber,
&Cxt, SM);
EXPECT_EQ(*LocalVar, "FOO");
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
- ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
- Expected<uint64_t> ExpressionVal = (*ExpressionASTPointer)->eval();
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ Expected<uint64_t> ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 18U);
- ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar2Ref, DefinedNumericVariable,
/*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
- ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
- ExpressionVal = (*ExpressionASTPointer)->eval();
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 20U);
+ ExpressionPointer =
+ P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable,
+ /*IsLegacyLineExpr=*/false,
+ LineNumber, &Cxt, SM);
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
+ ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
+ EXPECT_EQ(*ExpressionVal, 12U);
ASSERT_THAT_EXPECTED(EmptyVar, Succeeded());
EXPECT_EQ(*EmptyVar, "");
expectUndefErrors({UnknownVarStr}, UnknownVar.takeError());
// 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.
- expectUndefErrors({"LocalNumVar2"},
- (*ExpressionASTPointer)->eval().takeError());
+ expectUndefErrors({"LocalNumVar3"},
+ (*ExpressionPointer)->getAST()->eval().takeError());
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
- ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
- ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
- ExpressionVal = (*ExpressionASTPointer)->eval();
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError());
- ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
- ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
- ExpressionVal = (*ExpressionASTPointer)->eval();
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError());
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
expectUndefErrors({"EmptyVar"}, EmptyVar.takeError());
ASSERT_THAT_EXPECTED(GlobalVar, Succeeded());
EXPECT_EQ(*GlobalVar, "BAR");
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
- ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
- ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
- ExpressionVal = (*ExpressionASTPointer)->eval();
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 36U);
Cxt.clearLocalVars();
EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded());
P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
- ExpressionASTPointer = P.parseNumericSubstitutionBlock(
+ ExpressionPointer = P.parseNumericSubstitutionBlock(
GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
LineNumber, &Cxt, SM);
- ASSERT_THAT_EXPECTED(ExpressionASTPointer, Succeeded());
- ExpressionVal = (*ExpressionASTPointer)->eval();
+ ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
+ ExpressionVal = (*ExpressionPointer)->getAST()->eval();
ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
EXPECT_EQ(*ExpressionVal, 36U);
}