mirror of
https://github.com/intel/llvm.git
synced 2026-01-26 12:26:52 +08:00
FileCheck: Return parse error w/ Error & Expected
Summary: Make use of Error and Expected to bubble up diagnostics and force checking of errors in the callers. Reviewers: jhenderson, jdenny, probinson, arichardson Subscribers: hiraditya, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D63125 llvm-svn: 363900
This commit is contained in:
@@ -87,6 +87,30 @@ public:
|
||||
/// Type of functions evaluating a given binary operation.
|
||||
using binop_eval_t = uint64_t (*)(uint64_t, uint64_t);
|
||||
|
||||
/// Class to represent an undefined variable error which prints that variable's
|
||||
/// name between quotes when printed.
|
||||
class FileCheckUndefVarError : public ErrorInfo<FileCheckUndefVarError> {
|
||||
private:
|
||||
StringRef VarName;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
FileCheckUndefVarError(StringRef VarName) : VarName(VarName) {}
|
||||
|
||||
StringRef getVarName() const { return VarName; }
|
||||
|
||||
std::error_code convertToErrorCode() const override {
|
||||
return inconvertibleErrorCode();
|
||||
}
|
||||
|
||||
/// Print name of variable associated with this error.
|
||||
void log(raw_ostream &OS) const override {
|
||||
OS << "\"";
|
||||
OS.write_escaped(VarName) << "\"";
|
||||
}
|
||||
};
|
||||
|
||||
/// Class representing a numeric expression consisting of either a single
|
||||
/// numeric variable or a binary operation between a numeric variable and an
|
||||
/// immediate.
|
||||
@@ -107,13 +131,9 @@ public:
|
||||
: 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.
|
||||
Optional<uint64_t> eval() const;
|
||||
|
||||
/// \returns the name of the undefined variable used in this expression if
|
||||
/// any or an empty string otherwise.
|
||||
StringRef getUndefVarName() const;
|
||||
/// perform the binary operation it consists of. \returns an error if the
|
||||
/// numeric variable used is undefined, or the expression value otherwise.
|
||||
Expected<uint64_t> eval() const;
|
||||
};
|
||||
|
||||
class FileCheckPatternContext;
|
||||
@@ -151,12 +171,8 @@ public:
|
||||
size_t getIndex() const { return InsertIdx; }
|
||||
|
||||
/// \returns a string containing the result of the substitution represented
|
||||
/// by this class instance or None if substitution failed.
|
||||
virtual Optional<std::string> getResult() const = 0;
|
||||
|
||||
/// \returns the name of the variable used in this substitution if undefined,
|
||||
/// or an empty string otherwise.
|
||||
virtual StringRef getUndefVarName() const = 0;
|
||||
/// by this class instance or an error if substitution failed.
|
||||
virtual Expected<std::string> getResult() const = 0;
|
||||
};
|
||||
|
||||
class FileCheckStringSubstitution : public FileCheckSubstitution {
|
||||
@@ -166,12 +182,8 @@ public:
|
||||
: FileCheckSubstitution(Context, VarName, InsertIdx) {}
|
||||
|
||||
/// \returns the text that the string variable in this substitution matched
|
||||
/// when defined, or None if the variable is undefined.
|
||||
Optional<std::string> getResult() const override;
|
||||
|
||||
/// \returns the name of the string variable used in this substitution if
|
||||
/// undefined, or an empty string otherwise.
|
||||
StringRef getUndefVarName() const override;
|
||||
/// when defined, or an error if the variable is undefined.
|
||||
Expected<std::string> getResult() const override;
|
||||
};
|
||||
|
||||
class FileCheckNumericSubstitution : public FileCheckSubstitution {
|
||||
@@ -186,12 +198,8 @@ public:
|
||||
: FileCheckSubstitution(Context, Expr, InsertIdx), NumExpr(NumExpr) {}
|
||||
|
||||
/// \returns a string containing the result of evaluating the numeric
|
||||
/// expression in this substitution, or None if evaluation failed.
|
||||
Optional<std::string> getResult() const override;
|
||||
|
||||
/// \returns the name of the numeric variable used in this substitution if
|
||||
/// undefined, or an empty string otherwise.
|
||||
StringRef getUndefVarName() const override;
|
||||
/// expression in this substitution, or an error if evaluation failed.
|
||||
Expected<std::string> getResult() const override;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
@@ -282,16 +290,16 @@ private:
|
||||
std::vector<std::unique_ptr<FileCheckSubstitution>> Substitutions;
|
||||
|
||||
public:
|
||||
/// \returns the value of string variable \p VarName or None if no such
|
||||
/// \returns the value of string variable \p VarName or an error if no such
|
||||
/// variable has been defined.
|
||||
Optional<StringRef> getPatternVarValue(StringRef VarName);
|
||||
Expected<StringRef> getPatternVarValue(StringRef VarName);
|
||||
|
||||
/// Defines string 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);
|
||||
/// \p CmdlineDefines. \returns an error list containing diagnostics against
|
||||
/// \p SM for all definition parsing failures, if any, or Success otherwise.
|
||||
Error defineCmdlineVariables(std::vector<std::string> &CmdlineDefines,
|
||||
SourceMgr &SM);
|
||||
|
||||
/// Undefines local variables (variables whose name does not start with a '$'
|
||||
/// sign), i.e. removes them from GlobalVariableTable and from
|
||||
@@ -323,6 +331,48 @@ private:
|
||||
size_t InsertIdx);
|
||||
};
|
||||
|
||||
/// Class to represent an error holding a diagnostic with location information
|
||||
/// used when printing it.
|
||||
class FileCheckErrorDiagnostic : public ErrorInfo<FileCheckErrorDiagnostic> {
|
||||
private:
|
||||
SMDiagnostic Diagnostic;
|
||||
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
FileCheckErrorDiagnostic(SMDiagnostic &&Diag) : Diagnostic(Diag) {}
|
||||
|
||||
std::error_code convertToErrorCode() const override {
|
||||
return inconvertibleErrorCode();
|
||||
}
|
||||
|
||||
/// Print diagnostic associated with this error when printing the error.
|
||||
void log(raw_ostream &OS) const override { Diagnostic.print(nullptr, OS); }
|
||||
|
||||
static Error get(const SourceMgr &SM, SMLoc Loc, const Twine &ErrMsg) {
|
||||
return make_error<FileCheckErrorDiagnostic>(
|
||||
SM.GetMessage(Loc, SourceMgr::DK_Error, ErrMsg));
|
||||
}
|
||||
|
||||
static Error get(const SourceMgr &SM, StringRef Buffer, const Twine &ErrMsg) {
|
||||
return get(SM, SMLoc::getFromPointer(Buffer.data()), ErrMsg);
|
||||
}
|
||||
};
|
||||
|
||||
class FileCheckNotFoundError : public ErrorInfo<FileCheckNotFoundError> {
|
||||
public:
|
||||
static char ID;
|
||||
|
||||
std::error_code convertToErrorCode() const override {
|
||||
return inconvertibleErrorCode();
|
||||
}
|
||||
|
||||
/// Print diagnostic associated with this error when printing the error.
|
||||
void log(raw_ostream &OS) const override {
|
||||
OS << "String not found in input";
|
||||
}
|
||||
};
|
||||
|
||||
class FileCheckPattern {
|
||||
SMLoc PatternLoc;
|
||||
|
||||
@@ -403,27 +453,30 @@ public:
|
||||
|
||||
/// \returns whether \p C is a valid first character for a variable name.
|
||||
static bool isValidVarNameStart(char C);
|
||||
/// Parses the string at the start of \p Str for a variable name and \returns
|
||||
/// whether the variable name is ill-formed. If parsing succeeded, sets
|
||||
/// \p IsPseudo to indicate if it is a pseudo variable, sets \p Name to the
|
||||
/// parsed variable name and strips \p Str from the variable name.
|
||||
static bool parseVariable(StringRef &Str, StringRef &Name, bool &IsPseudo);
|
||||
/// Parses \p Expr for the definition of a numeric variable, returning an
|
||||
/// error if \p Context already holds a string variable with the same name.
|
||||
/// \returns whether parsing fails, in which case errors are reported on
|
||||
/// \p SM. Otherwise, sets \p Name to the name of the parsed numeric
|
||||
/// variable.
|
||||
static bool parseNumericVariableDefinition(StringRef &Expr, StringRef &Name,
|
||||
FileCheckPatternContext *Context,
|
||||
const SourceMgr &SM);
|
||||
/// Parses the string at the start of \p Str for a variable name. \returns
|
||||
/// an error holding a diagnostic against \p SM if parsing fail, or the
|
||||
/// name of the variable otherwise. In the latter case, sets \p IsPseudo to
|
||||
/// indicate if it is a pseudo variable and strips \p Str from the variable
|
||||
/// name.
|
||||
static Expected<StringRef> parseVariable(StringRef &Str, bool &IsPseudo,
|
||||
const SourceMgr &SM);
|
||||
/// Parses \p Expr for the name of a numeric variable to be defined. \returns
|
||||
/// an error holding a diagnostic against \p SM should defining such a
|
||||
/// variable be invalid, or Success otherwise. In the latter case, sets
|
||||
/// \p Name to the name of the parsed numeric variable name.
|
||||
static Error parseNumericVariableDefinition(StringRef &Expr, StringRef &Name,
|
||||
FileCheckPatternContext *Context,
|
||||
const SourceMgr &SM);
|
||||
/// Parses \p Expr for a numeric substitution block. \returns the class
|
||||
/// representing the AST of the numeric expression whose value must be
|
||||
/// substituted, or nullptr if parsing fails, in which case errors are
|
||||
/// reported on \p SM. Sets \p DefinedNumericVariable to point to the class
|
||||
/// representing the numeric variable defined in this numeric substitution
|
||||
/// block, or nullptr if this block does not define any variable.
|
||||
FileCheckNumExpr *parseNumericSubstitutionBlock(
|
||||
StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable,
|
||||
/// substituted, 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.
|
||||
Expected<FileCheckNumExpr *> parseNumericSubstitutionBlock(
|
||||
StringRef Expr,
|
||||
Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
|
||||
const SourceMgr &SM) const;
|
||||
/// Parses the pattern in \p PatternStr and initializes this FileCheckPattern
|
||||
/// instance accordingly.
|
||||
@@ -436,9 +489,9 @@ public:
|
||||
const FileCheckRequest &Req);
|
||||
/// Matches the pattern string against the input buffer \p Buffer
|
||||
///
|
||||
/// \returns the position that is matched or npos if there is no match. If
|
||||
/// there is a match, updates \p MatchLen with the size of the matched
|
||||
/// string.
|
||||
/// \returns the position that is matched or an error indicating why matching
|
||||
/// failed. If there is a match, updates \p MatchLen with the size of the
|
||||
/// matched string.
|
||||
///
|
||||
/// The GlobalVariableTable StringMap in the FileCheckPatternContext class
|
||||
/// instance provides the current values of FileCheck string variables and
|
||||
@@ -446,7 +499,8 @@ public:
|
||||
/// GlobalNumericVariableTable StringMap in the same class provides the
|
||||
/// current values of FileCheck numeric variables and is updated if this
|
||||
/// match defines new numeric values.
|
||||
size_t match(StringRef Buffer, size_t &MatchLen, const SourceMgr &SM) const;
|
||||
Expected<size_t> match(StringRef Buffer, size_t &MatchLen,
|
||||
const SourceMgr &SM) const;
|
||||
/// Prints the value of successful substitutions or the name of the undefined
|
||||
/// string or numeric variable preventing a successful substitution.
|
||||
void printSubstitutions(const SourceMgr &SM, StringRef Buffer,
|
||||
@@ -478,15 +532,15 @@ private:
|
||||
size_t FindRegexVarEnd(StringRef Str, SourceMgr &SM);
|
||||
|
||||
/// Parses \p Expr for the use of a numeric variable. \returns the pointer to
|
||||
/// the class instance representing that variable if successful, or nullptr
|
||||
/// otherwise, in which case errors are reported on \p SM.
|
||||
FileCheckNumericVariable *parseNumericVariableUse(StringRef &Expr,
|
||||
const SourceMgr &SM) const;
|
||||
/// the class instance representing that variable if successful, or an error
|
||||
/// holding a diagnostic against \p SM otherwise.
|
||||
Expected<FileCheckNumericVariable *>
|
||||
parseNumericVariableUse(StringRef &Expr, const SourceMgr &SM) const;
|
||||
/// Parses \p Expr for a binary operation.
|
||||
/// \returns the class representing the binary operation of the numeric
|
||||
/// expression, or nullptr if parsing fails, in which case errors are
|
||||
/// reported on \p SM.
|
||||
FileCheckNumExpr *parseBinop(StringRef &Expr, const SourceMgr &SM) const;
|
||||
/// expression, or an error holding a diagnostic against \p SM otherwise.
|
||||
Expected<FileCheckNumExpr *> parseBinop(StringRef &Expr,
|
||||
const SourceMgr &SM) const;
|
||||
};
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -38,57 +38,39 @@ bool FileCheckNumericVariable::clearValue() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<uint64_t> FileCheckNumExpr::eval() const {
|
||||
Expected<uint64_t> FileCheckNumExpr::eval() const {
|
||||
assert(LeftOp && "Evaluating an empty numeric expression");
|
||||
Optional<uint64_t> LeftOpValue = LeftOp->getValue();
|
||||
// Variable is undefined.
|
||||
if (!LeftOpValue)
|
||||
return None;
|
||||
return make_error<FileCheckUndefVarError>(LeftOp->getName());
|
||||
return EvalBinop(*LeftOpValue, RightOp);
|
||||
}
|
||||
|
||||
StringRef FileCheckNumExpr::getUndefVarName() const {
|
||||
if (!LeftOp->getValue())
|
||||
return LeftOp->getName();
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
Optional<std::string> FileCheckNumericSubstitution::getResult() const {
|
||||
Optional<uint64_t> EvaluatedValue = NumExpr->eval();
|
||||
Expected<std::string> FileCheckNumericSubstitution::getResult() const {
|
||||
Expected<uint64_t> EvaluatedValue = NumExpr->eval();
|
||||
if (!EvaluatedValue)
|
||||
return None;
|
||||
return EvaluatedValue.takeError();
|
||||
return utostr(*EvaluatedValue);
|
||||
}
|
||||
|
||||
Optional<std::string> FileCheckStringSubstitution::getResult() const {
|
||||
Expected<std::string> FileCheckStringSubstitution::getResult() const {
|
||||
// Look up the value and escape it so that we can put it into the regex.
|
||||
Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
|
||||
Expected<StringRef> VarVal = Context->getPatternVarValue(FromStr);
|
||||
if (!VarVal)
|
||||
return None;
|
||||
return VarVal.takeError();
|
||||
return Regex::escape(*VarVal);
|
||||
}
|
||||
|
||||
StringRef FileCheckNumericSubstitution::getUndefVarName() const {
|
||||
// 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();
|
||||
}
|
||||
|
||||
StringRef FileCheckStringSubstitution::getUndefVarName() const {
|
||||
if (!Context->getPatternVarValue(FromStr))
|
||||
return FromStr;
|
||||
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
bool FileCheckPattern::isValidVarNameStart(char C) {
|
||||
return C == '_' || isalpha(C);
|
||||
}
|
||||
|
||||
bool FileCheckPattern::parseVariable(StringRef &Str, StringRef &Name,
|
||||
bool &IsPseudo) {
|
||||
Expected<StringRef> FileCheckPattern::parseVariable(StringRef &Str,
|
||||
bool &IsPseudo,
|
||||
const SourceMgr &SM) {
|
||||
if (Str.empty())
|
||||
return true;
|
||||
return FileCheckErrorDiagnostic::get(SM, Str, "empty variable name");
|
||||
|
||||
bool ParsedOneChar = false;
|
||||
unsigned I = 0;
|
||||
@@ -100,7 +82,7 @@ bool FileCheckPattern::parseVariable(StringRef &Str, StringRef &Name,
|
||||
|
||||
for (unsigned E = Str.size(); I != E; ++I) {
|
||||
if (!ParsedOneChar && !isValidVarNameStart(Str[I]))
|
||||
return true;
|
||||
return FileCheckErrorDiagnostic::get(SM, Str, "invalid variable name");
|
||||
|
||||
// Variable names are composed of alphanumeric characters and underscores.
|
||||
if (Str[I] != '_' && !isalnum(Str[I]))
|
||||
@@ -108,9 +90,9 @@ bool FileCheckPattern::parseVariable(StringRef &Str, StringRef &Name,
|
||||
ParsedOneChar = true;
|
||||
}
|
||||
|
||||
Name = Str.take_front(I);
|
||||
StringRef Name = Str.take_front(I);
|
||||
Str = Str.substr(I);
|
||||
return false;
|
||||
return Name;
|
||||
}
|
||||
|
||||
// StringRef holding all characters considered as horizontal whitespaces by
|
||||
@@ -124,50 +106,45 @@ static char popFront(StringRef &S) {
|
||||
return C;
|
||||
}
|
||||
|
||||
bool FileCheckPattern::parseNumericVariableDefinition(
|
||||
char FileCheckUndefVarError::ID = 0;
|
||||
char FileCheckErrorDiagnostic::ID = 0;
|
||||
char FileCheckNotFoundError::ID = 0;
|
||||
|
||||
Error FileCheckPattern::parseNumericVariableDefinition(
|
||||
StringRef &Expr, StringRef &Name, FileCheckPatternContext *Context,
|
||||
const SourceMgr &SM) {
|
||||
bool IsPseudo;
|
||||
if (parseVariable(Expr, Name, IsPseudo)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
|
||||
"invalid variable name");
|
||||
return true;
|
||||
}
|
||||
Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM);
|
||||
if (!ParseVarResult)
|
||||
return ParseVarResult.takeError();
|
||||
Name = *ParseVarResult;
|
||||
|
||||
if (IsPseudo) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"definition of pseudo numeric variable unsupported");
|
||||
return true;
|
||||
}
|
||||
if (IsPseudo)
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, Name, "definition of pseudo numeric variable unsupported");
|
||||
|
||||
// Detect collisions between string and numeric variables when the latter
|
||||
// is created later than the former.
|
||||
if (Context->DefinedVariableTable.find(Name) !=
|
||||
Context->DefinedVariableTable.end()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"string variable with name '" + Name + "' already exists");
|
||||
return true;
|
||||
}
|
||||
Context->DefinedVariableTable.end())
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, Name, "string variable with name '" + Name + "' already exists");
|
||||
|
||||
return false;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
FileCheckNumericVariable *
|
||||
Expected<FileCheckNumericVariable *>
|
||||
FileCheckPattern::parseNumericVariableUse(StringRef &Expr,
|
||||
const SourceMgr &SM) const {
|
||||
bool IsPseudo;
|
||||
StringRef Name;
|
||||
if (parseVariable(Expr, Name, IsPseudo)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
|
||||
"invalid variable name");
|
||||
return nullptr;
|
||||
}
|
||||
Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM);
|
||||
if (!ParseVarResult)
|
||||
return ParseVarResult.takeError();
|
||||
StringRef Name = *ParseVarResult;
|
||||
|
||||
if (IsPseudo && !Name.equals("@LINE")) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"invalid pseudo numeric variable '" + Name + "'");
|
||||
return nullptr;
|
||||
}
|
||||
if (IsPseudo && !Name.equals("@LINE"))
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, Name, "invalid pseudo numeric variable '" + Name + "'");
|
||||
|
||||
// This method is indirectly called from parsePattern for all numeric
|
||||
// variable definitions and uses in the order in which they appear in the
|
||||
@@ -176,19 +153,15 @@ FileCheckPattern::parseNumericVariableUse(StringRef &Expr,
|
||||
// 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;
|
||||
}
|
||||
if (VarTableIter == Context->GlobalNumericVariableTable.end())
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, Name, "using undefined numeric variable '" + Name + "'");
|
||||
|
||||
FileCheckNumericVariable *NumericVariable = VarTableIter->second;
|
||||
if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error,
|
||||
"numeric variable '" + Name +
|
||||
"' defined on the same line as used");
|
||||
return nullptr;
|
||||
}
|
||||
if (!IsPseudo && NumericVariable->getDefLineNumber() == LineNumber)
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, Name,
|
||||
"numeric variable '" + Name + "' defined on the same line as used");
|
||||
|
||||
return NumericVariable;
|
||||
}
|
||||
@@ -201,13 +174,14 @@ static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
|
||||
return LeftOp - RightOp;
|
||||
}
|
||||
|
||||
FileCheckNumExpr *FileCheckPattern::parseBinop(StringRef &Expr,
|
||||
const SourceMgr &SM) const {
|
||||
FileCheckNumericVariable *LeftOp = parseNumericVariableUse(Expr, SM);
|
||||
if (!LeftOp) {
|
||||
// Error reporting done in parseNumericVariableUse().
|
||||
return nullptr;
|
||||
Expected<FileCheckNumExpr *>
|
||||
FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const {
|
||||
Expected<FileCheckNumericVariable *> LeftParseResult =
|
||||
parseNumericVariableUse(Expr, SM);
|
||||
if (!LeftParseResult) {
|
||||
return LeftParseResult.takeError();
|
||||
}
|
||||
FileCheckNumericVariable *LeftOp = *LeftParseResult;
|
||||
|
||||
// Check if this is a supported operation and select a function to perform
|
||||
// it.
|
||||
@@ -225,41 +199,35 @@ FileCheckNumExpr *FileCheckPattern::parseBinop(StringRef &Expr,
|
||||
EvalBinop = sub;
|
||||
break;
|
||||
default:
|
||||
SM.PrintMessage(OpLoc, SourceMgr::DK_Error,
|
||||
Twine("unsupported numeric operation '") + Twine(Operator) +
|
||||
"'");
|
||||
return nullptr;
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, OpLoc,
|
||||
Twine("unsupported numeric operation '") + Twine(Operator) + "'");
|
||||
}
|
||||
|
||||
// Parse right operand.
|
||||
Expr = Expr.ltrim(SpaceChars);
|
||||
if (Expr.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
|
||||
"missing operand in numeric expression");
|
||||
return nullptr;
|
||||
}
|
||||
if (Expr.empty())
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, Expr, "missing operand in numeric expression");
|
||||
uint64_t RightOp;
|
||||
if (Expr.consumeInteger(10, RightOp)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
|
||||
"invalid offset in numeric expression '" + Expr + "'");
|
||||
return nullptr;
|
||||
}
|
||||
if (Expr.consumeInteger(10, RightOp))
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, Expr, "invalid offset in numeric expression '" + Expr + "'");
|
||||
Expr = Expr.ltrim(SpaceChars);
|
||||
if (!Expr.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error,
|
||||
"unexpected characters at end of numeric expression '" +
|
||||
Expr + "'");
|
||||
return nullptr;
|
||||
}
|
||||
if (!Expr.empty())
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, Expr,
|
||||
"unexpected characters at end of numeric expression '" + Expr + "'");
|
||||
|
||||
return Context->makeNumExpr(EvalBinop, LeftOp, RightOp);
|
||||
}
|
||||
|
||||
FileCheckNumExpr *FileCheckPattern::parseNumericSubstitutionBlock(
|
||||
StringRef Expr, FileCheckNumericVariable *&DefinedNumericVariable,
|
||||
Expected<FileCheckNumExpr *> FileCheckPattern::parseNumericSubstitutionBlock(
|
||||
StringRef Expr,
|
||||
Optional<FileCheckNumericVariable *> &DefinedNumericVariable,
|
||||
const SourceMgr &SM) const {
|
||||
// Parse the numeric variable definition.
|
||||
DefinedNumericVariable = nullptr;
|
||||
DefinedNumericVariable = None;
|
||||
size_t DefEnd = Expr.find(':');
|
||||
if (DefEnd != StringRef::npos) {
|
||||
StringRef DefExpr = Expr.substr(0, DefEnd);
|
||||
@@ -267,28 +235,23 @@ FileCheckNumExpr *FileCheckPattern::parseNumericSubstitutionBlock(
|
||||
|
||||
DefExpr = DefExpr.ltrim(SpaceChars);
|
||||
StringRef Name;
|
||||
if (parseNumericVariableDefinition(DefExpr, Name, Context, SM)) {
|
||||
// Invalid variable definition. Error reporting done in parsing function.
|
||||
return nullptr;
|
||||
}
|
||||
Error ErrorDiagnostic =
|
||||
parseNumericVariableDefinition(DefExpr, Name, Context, SM);
|
||||
if (ErrorDiagnostic)
|
||||
return std::move(ErrorDiagnostic);
|
||||
|
||||
DefinedNumericVariable =
|
||||
Context->makeNumericVariable(this->LineNumber, Name);
|
||||
|
||||
DefExpr = DefExpr.ltrim(SpaceChars);
|
||||
if (!DefExpr.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(DefExpr.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"invalid numeric variable definition");
|
||||
return nullptr;
|
||||
}
|
||||
if (!DefExpr.empty())
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, DefExpr, "invalid numeric variable definition");
|
||||
UseExpr = UseExpr.ltrim(SpaceChars);
|
||||
if (!UseExpr.empty()) {
|
||||
SM.PrintMessage(
|
||||
SMLoc::getFromPointer(UseExpr.data()), SourceMgr::DK_Error,
|
||||
if (!UseExpr.empty())
|
||||
return FileCheckErrorDiagnostic::get(
|
||||
SM, UseExpr,
|
||||
"unexpected string after variable definition: '" + UseExpr + "'");
|
||||
return nullptr;
|
||||
}
|
||||
return Context->makeNumExpr(add, nullptr, 0);
|
||||
}
|
||||
|
||||
@@ -429,13 +392,14 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
|
||||
|
||||
// Get the name (e.g. "foo") and verify it is well formed.
|
||||
bool IsPseudo;
|
||||
StringRef Name;
|
||||
StringRef OrigMatchStr = MatchStr;
|
||||
if (parseVariable(MatchStr, Name, IsPseudo)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(MatchStr.data()),
|
||||
SourceMgr::DK_Error, "invalid variable name");
|
||||
Expected<StringRef> ParseVarResult =
|
||||
parseVariable(MatchStr, IsPseudo, SM);
|
||||
if (!ParseVarResult) {
|
||||
logAllUnhandledErrors(ParseVarResult.takeError(), errs());
|
||||
return true;
|
||||
}
|
||||
StringRef Name = *ParseVarResult;
|
||||
|
||||
IsDefinition = (VarEndIdx != StringRef::npos);
|
||||
if (IsDefinition) {
|
||||
@@ -468,15 +432,18 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
|
||||
|
||||
// Parse numeric substitution block.
|
||||
FileCheckNumExpr *NumExpr;
|
||||
FileCheckNumericVariable *DefinedNumericVariable;
|
||||
Optional<FileCheckNumericVariable *> DefinedNumericVariable;
|
||||
if (IsNumBlock) {
|
||||
NumExpr =
|
||||
Expected<FileCheckNumExpr *> ParseResult =
|
||||
parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM);
|
||||
if (NumExpr == nullptr)
|
||||
if (!ParseResult) {
|
||||
logAllUnhandledErrors(ParseResult.takeError(), errs());
|
||||
return true;
|
||||
}
|
||||
NumExpr = *ParseResult;
|
||||
if (DefinedNumericVariable) {
|
||||
IsDefinition = true;
|
||||
DefName = DefinedNumericVariable->getName();
|
||||
DefName = (*DefinedNumericVariable)->getName();
|
||||
MatchRegexp = StringRef("[0-9]+");
|
||||
} else
|
||||
SubstStr = MatchStr;
|
||||
@@ -513,13 +480,13 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix,
|
||||
// Handle variable definitions: [[<def>:(...)]] and
|
||||
// [[#(...)<def>:(...)]].
|
||||
if (IsNumBlock) {
|
||||
FileCheckNumExprMatch NumExprDef = {DefinedNumericVariable, CurParen};
|
||||
FileCheckNumExprMatch NumExprDef = {*DefinedNumericVariable, CurParen};
|
||||
NumericVariableDefs[DefName] = NumExprDef;
|
||||
// This store is done here rather than in match() to allow
|
||||
// parseNumericVariableUse() to get the pointer to the class instance
|
||||
// of the right variable definition corresponding to a given numeric
|
||||
// variable use.
|
||||
Context->GlobalNumericVariableTable[DefName] = DefinedNumericVariable;
|
||||
Context->GlobalNumericVariableTable[DefName] = *DefinedNumericVariable;
|
||||
} else {
|
||||
VariableDefs[DefName] = CurParen;
|
||||
// Mark the string variable as defined to detect collisions between
|
||||
@@ -576,8 +543,8 @@ void FileCheckPattern::AddBackrefToRegEx(unsigned BackrefNum) {
|
||||
RegExStr += Backref;
|
||||
}
|
||||
|
||||
size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen,
|
||||
const SourceMgr &SM) const {
|
||||
Expected<size_t> FileCheckPattern::match(StringRef Buffer, size_t &MatchLen,
|
||||
const SourceMgr &SM) const {
|
||||
// If this is the EOF pattern, match it immediately.
|
||||
if (CheckTy == Check::CheckEOF) {
|
||||
MatchLen = 0;
|
||||
@@ -587,7 +554,10 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen,
|
||||
// If this is a fixed string pattern, just match it now.
|
||||
if (!FixedStr.empty()) {
|
||||
MatchLen = FixedStr.size();
|
||||
return Buffer.find(FixedStr);
|
||||
size_t Pos = Buffer.find(FixedStr);
|
||||
if (Pos == StringRef::npos)
|
||||
return make_error<FileCheckNotFoundError>();
|
||||
return Pos;
|
||||
}
|
||||
|
||||
// Regex match.
|
||||
@@ -605,9 +575,9 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen,
|
||||
// handled by back-references.
|
||||
for (const auto &Substitution : Substitutions) {
|
||||
// Substitute and check for failure (e.g. use of undefined variable).
|
||||
Optional<std::string> Value = Substitution->getResult();
|
||||
Expected<std::string> Value = Substitution->getResult();
|
||||
if (!Value)
|
||||
return StringRef::npos;
|
||||
return Value.takeError();
|
||||
|
||||
// Plop it into the regex at the adjusted offset.
|
||||
TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,
|
||||
@@ -621,7 +591,7 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen,
|
||||
|
||||
SmallVector<StringRef, 4> MatchInfo;
|
||||
if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo))
|
||||
return StringRef::npos;
|
||||
return make_error<FileCheckNotFoundError>();
|
||||
|
||||
// Successful regex match.
|
||||
assert(!MatchInfo.empty() && "Didn't get any match");
|
||||
@@ -645,12 +615,11 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen,
|
||||
|
||||
StringRef MatchedValue = MatchInfo[CaptureParenGroup];
|
||||
uint64_t Val;
|
||||
if (MatchedValue.getAsInteger(10, Val)) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(MatchedValue.data()),
|
||||
SourceMgr::DK_Error, "Unable to represent numeric value");
|
||||
}
|
||||
if (MatchedValue.getAsInteger(10, Val))
|
||||
return FileCheckErrorDiagnostic::get(SM, MatchedValue,
|
||||
"Unable to represent numeric value");
|
||||
if (DefinedNumericVariable->setValue(Val))
|
||||
assert(false && "Numeric variable redefined");
|
||||
llvm_unreachable("Numeric variable redefined");
|
||||
}
|
||||
|
||||
// Like CHECK-NEXT, CHECK-EMPTY's match range is considered to start after
|
||||
@@ -685,16 +654,26 @@ void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
|
||||
for (const auto &Substitution : Substitutions) {
|
||||
SmallString<256> Msg;
|
||||
raw_svector_ostream OS(Msg);
|
||||
Optional<std::string> MatchedValue = Substitution->getResult();
|
||||
Expected<std::string> MatchedValue = Substitution->getResult();
|
||||
|
||||
// Substitution failed or is not known at match time, print the undefined
|
||||
// variable it uses.
|
||||
if (!MatchedValue) {
|
||||
StringRef UndefVarName = Substitution->getUndefVarName();
|
||||
if (UndefVarName.empty())
|
||||
continue;
|
||||
OS << "uses undefined variable \"";
|
||||
OS.write_escaped(UndefVarName) << "\"";
|
||||
bool UndefSeen = false;
|
||||
handleAllErrors(MatchedValue.takeError(),
|
||||
[](const FileCheckNotFoundError &E) {},
|
||||
// Handled in PrintNoMatch()
|
||||
[](const FileCheckErrorDiagnostic &E) {},
|
||||
[&](const FileCheckUndefVarError &E) {
|
||||
if (!UndefSeen) {
|
||||
OS << "uses undefined variable ";
|
||||
UndefSeen = true;
|
||||
}
|
||||
E.log(OS);
|
||||
},
|
||||
[](const ErrorInfoBase &E) {
|
||||
llvm_unreachable("Unexpected error");
|
||||
});
|
||||
} else {
|
||||
// Substitution succeeded. Print substituted value.
|
||||
OS << "with \"";
|
||||
@@ -777,11 +756,11 @@ void FileCheckPattern::printFuzzyMatch(
|
||||
}
|
||||
}
|
||||
|
||||
Optional<StringRef>
|
||||
Expected<StringRef>
|
||||
FileCheckPatternContext::getPatternVarValue(StringRef VarName) {
|
||||
auto VarIter = GlobalVariableTable.find(VarName);
|
||||
if (VarIter == GlobalVariableTable.end())
|
||||
return None;
|
||||
return make_error<FileCheckUndefVarError>(VarName);
|
||||
|
||||
return VarIter->second;
|
||||
}
|
||||
@@ -1077,8 +1056,12 @@ FindFirstMatchingPrefix(Regex &PrefixRE, StringRef &Buffer,
|
||||
|
||||
bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE,
|
||||
std::vector<FileCheckString> &CheckStrings) {
|
||||
if (PatternContext.defineCmdlineVariables(Req.GlobalDefines, SM))
|
||||
Error DefineError =
|
||||
PatternContext.defineCmdlineVariables(Req.GlobalDefines, SM);
|
||||
if (DefineError) {
|
||||
logAllUnhandledErrors(std::move(DefineError), errs());
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<FileCheckPattern> ImplicitNegativeChecks;
|
||||
for (const auto &PatternString : Req.ImplicitCheckNot) {
|
||||
@@ -1276,11 +1259,14 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
|
||||
StringRef Prefix, SMLoc Loc,
|
||||
const FileCheckPattern &Pat, int MatchedCount,
|
||||
StringRef Buffer, bool VerboseVerbose,
|
||||
std::vector<FileCheckDiag> *Diags) {
|
||||
std::vector<FileCheckDiag> *Diags, Error MatchErrors) {
|
||||
assert(MatchErrors && "Called on successful match");
|
||||
bool PrintDiag = true;
|
||||
if (!ExpectedMatch) {
|
||||
if (!VerboseVerbose)
|
||||
if (!VerboseVerbose) {
|
||||
consumeError(std::move(MatchErrors));
|
||||
return;
|
||||
}
|
||||
// Due to their verbosity, we don't print verbose diagnostics here if we're
|
||||
// gathering them for a different rendering, but we always print other
|
||||
// diagnostics.
|
||||
@@ -1294,8 +1280,19 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
|
||||
ExpectedMatch ? FileCheckDiag::MatchNoneButExpected
|
||||
: FileCheckDiag::MatchNoneAndExcluded,
|
||||
SM, Loc, Pat.getCheckTy(), Buffer, 0, Buffer.size(), Diags);
|
||||
if (!PrintDiag)
|
||||
if (!PrintDiag) {
|
||||
consumeError(std::move(MatchErrors));
|
||||
return;
|
||||
}
|
||||
|
||||
MatchErrors =
|
||||
handleErrors(std::move(MatchErrors),
|
||||
[](const FileCheckErrorDiagnostic &E) { E.log(errs()); });
|
||||
|
||||
// No problem matching the string per se.
|
||||
if (!MatchErrors)
|
||||
return;
|
||||
consumeError(std::move(MatchErrors));
|
||||
|
||||
// Print "not found" diagnostic.
|
||||
std::string Message = formatv("{0}: {1} string not found in input",
|
||||
@@ -1320,9 +1317,10 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
|
||||
static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM,
|
||||
const FileCheckString &CheckStr, int MatchedCount,
|
||||
StringRef Buffer, bool VerboseVerbose,
|
||||
std::vector<FileCheckDiag> *Diags) {
|
||||
std::vector<FileCheckDiag> *Diags, Error MatchErrors) {
|
||||
PrintNoMatch(ExpectedMatch, SM, CheckStr.Prefix, CheckStr.Loc, CheckStr.Pat,
|
||||
MatchedCount, Buffer, VerboseVerbose, Diags);
|
||||
MatchedCount, Buffer, VerboseVerbose, Diags,
|
||||
std::move(MatchErrors));
|
||||
}
|
||||
|
||||
/// Counts the number of newlines in the specified range.
|
||||
@@ -1376,17 +1374,19 @@ size_t FileCheckString::Check(const SourceMgr &SM, StringRef Buffer,
|
||||
StringRef MatchBuffer = Buffer.substr(LastMatchEnd);
|
||||
size_t CurrentMatchLen;
|
||||
// get a match at current start point
|
||||
size_t MatchPos = Pat.match(MatchBuffer, CurrentMatchLen, SM);
|
||||
if (i == 1)
|
||||
FirstMatchPos = LastPos + MatchPos;
|
||||
Expected<size_t> MatchResult = Pat.match(MatchBuffer, CurrentMatchLen, SM);
|
||||
|
||||
// report
|
||||
if (MatchPos == StringRef::npos) {
|
||||
PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags);
|
||||
if (!MatchResult) {
|
||||
PrintNoMatch(true, SM, *this, i, MatchBuffer, Req.VerboseVerbose, Diags,
|
||||
MatchResult.takeError());
|
||||
return StringRef::npos;
|
||||
}
|
||||
size_t MatchPos = *MatchResult;
|
||||
PrintMatch(true, SM, *this, i, MatchBuffer, MatchPos, CurrentMatchLen, Req,
|
||||
Diags);
|
||||
if (i == 1)
|
||||
FirstMatchPos = LastPos + MatchPos;
|
||||
|
||||
// move start point after the match
|
||||
LastMatchEnd += MatchPos + CurrentMatchLen;
|
||||
@@ -1497,13 +1497,14 @@ bool FileCheckString::CheckNot(
|
||||
assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!");
|
||||
|
||||
size_t MatchLen = 0;
|
||||
size_t Pos = Pat->match(Buffer, MatchLen, SM);
|
||||
Expected<size_t> MatchResult = Pat->match(Buffer, MatchLen, SM);
|
||||
|
||||
if (Pos == StringRef::npos) {
|
||||
if (!MatchResult) {
|
||||
PrintNoMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer,
|
||||
Req.VerboseVerbose, Diags);
|
||||
Req.VerboseVerbose, Diags, MatchResult.takeError());
|
||||
continue;
|
||||
}
|
||||
size_t Pos = *MatchResult;
|
||||
|
||||
PrintMatch(false, SM, Prefix, Pat->getLoc(), *Pat, 1, Buffer, Pos, MatchLen,
|
||||
Req, Diags);
|
||||
@@ -1557,14 +1558,15 @@ FileCheckString::CheckDag(const SourceMgr &SM, StringRef Buffer,
|
||||
// CHECK-DAG group.
|
||||
for (auto MI = MatchRanges.begin(), ME = MatchRanges.end(); true; ++MI) {
|
||||
StringRef MatchBuffer = Buffer.substr(MatchPos);
|
||||
size_t MatchPosBuf = Pat.match(MatchBuffer, MatchLen, SM);
|
||||
Expected<size_t> MatchResult = Pat.match(MatchBuffer, MatchLen, SM);
|
||||
// With a group of CHECK-DAGs, a single mismatching means the match on
|
||||
// that group of CHECK-DAGs fails immediately.
|
||||
if (MatchPosBuf == StringRef::npos) {
|
||||
if (!MatchResult) {
|
||||
PrintNoMatch(true, SM, Prefix, Pat.getLoc(), Pat, 1, MatchBuffer,
|
||||
Req.VerboseVerbose, Diags);
|
||||
Req.VerboseVerbose, Diags, MatchResult.takeError());
|
||||
return StringRef::npos;
|
||||
}
|
||||
size_t MatchPosBuf = *MatchResult;
|
||||
// Re-calc it as the offset relative to the start of the original string.
|
||||
MatchPos += MatchPosBuf;
|
||||
if (Req.VerboseVerbose)
|
||||
@@ -1687,19 +1689,19 @@ Regex FileCheck::buildCheckPrefixRegex() {
|
||||
return Regex(PrefixRegexStr);
|
||||
}
|
||||
|
||||
bool FileCheckPatternContext::defineCmdlineVariables(
|
||||
Error FileCheckPatternContext::defineCmdlineVariables(
|
||||
std::vector<std::string> &CmdlineDefines, SourceMgr &SM) {
|
||||
assert(GlobalVariableTable.empty() && GlobalNumericVariableTable.empty() &&
|
||||
"Overriding defined variable with command-line variable definitions");
|
||||
|
||||
if (CmdlineDefines.empty())
|
||||
return false;
|
||||
return Error::success();
|
||||
|
||||
// Create a string representing the vector of command-line definitions. Each
|
||||
// definition is on its own line and prefixed with a definition number to
|
||||
// clarify which definition a given diagnostic corresponds to.
|
||||
unsigned I = 0;
|
||||
bool ErrorFound = false;
|
||||
Error Errs = Error::success();
|
||||
std::string CmdlineDefsDiag;
|
||||
StringRef Prefix1 = "Global define #";
|
||||
StringRef Prefix2 = ": ";
|
||||
@@ -1723,10 +1725,10 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
||||
StringRef CmdlineDef = CmdlineDefDiag.substr(DefStart);
|
||||
size_t EqIdx = CmdlineDef.find('=');
|
||||
if (EqIdx == StringRef::npos) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(CmdlineDef.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"Missing equal sign in global definition");
|
||||
ErrorFound = true;
|
||||
Errs = joinErrors(
|
||||
std::move(Errs),
|
||||
FileCheckErrorDiagnostic::get(
|
||||
SM, CmdlineDef, "missing equal sign in global definition"));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1735,37 +1737,40 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
||||
StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1);
|
||||
StringRef VarName;
|
||||
SMLoc CmdlineNameLoc = SMLoc::getFromPointer(CmdlineName.data());
|
||||
bool ParseError = FileCheckPattern::parseNumericVariableDefinition(
|
||||
Error ErrorDiagnostic = FileCheckPattern::parseNumericVariableDefinition(
|
||||
CmdlineName, VarName, this, SM);
|
||||
// Check that CmdlineName starts with a valid numeric variable to be
|
||||
// defined and that it is not followed that anything. That second check
|
||||
// detects cases like "FOO+2" in a "FOO+2=10" definition.
|
||||
if (ParseError || !CmdlineName.empty()) {
|
||||
if (!ParseError)
|
||||
SM.PrintMessage(CmdlineNameLoc, SourceMgr::DK_Error,
|
||||
"invalid variable name");
|
||||
ErrorFound = true;
|
||||
if (ErrorDiagnostic) {
|
||||
Errs = joinErrors(std::move(Errs), std::move(ErrorDiagnostic));
|
||||
continue;
|
||||
}
|
||||
// Check that CmdlineName is only composed of the parsed numeric
|
||||
// variable. This catches cases like "FOO+2" in a "FOO+2=10" definition.
|
||||
if (!CmdlineName.empty()) {
|
||||
Errs = joinErrors(std::move(Errs),
|
||||
FileCheckErrorDiagnostic::get(
|
||||
SM, CmdlineNameLoc, "invalid variable name"));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect collisions between string and numeric variables when the latter
|
||||
// is created later than the former.
|
||||
if (DefinedVariableTable.find(VarName) != DefinedVariableTable.end()) {
|
||||
SM.PrintMessage(
|
||||
SMLoc::getFromPointer(VarName.data()), SourceMgr::DK_Error,
|
||||
"string variable with name '" + VarName + "' already exists");
|
||||
ErrorFound = true;
|
||||
Errs = joinErrors(
|
||||
std::move(Errs),
|
||||
FileCheckErrorDiagnostic::get(SM, VarName,
|
||||
"string variable with name '" +
|
||||
VarName + "' already exists"));
|
||||
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;
|
||||
Errs = joinErrors(std::move(Errs),
|
||||
FileCheckErrorDiagnostic::get(
|
||||
SM, CmdlineVal,
|
||||
"invalid value in numeric variable definition '" +
|
||||
CmdlineVal + "'"));
|
||||
continue;
|
||||
}
|
||||
auto DefinedNumericVariable = makeNumericVariable(0, VarName);
|
||||
@@ -1779,26 +1784,34 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
||||
std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('=');
|
||||
StringRef CmdlineName = CmdlineNameVal.first;
|
||||
StringRef OrigCmdlineName = CmdlineName;
|
||||
StringRef Name;
|
||||
bool IsPseudo;
|
||||
if (FileCheckPattern::parseVariable(CmdlineName, Name, IsPseudo) ||
|
||||
IsPseudo || !CmdlineName.empty()) {
|
||||
SM.PrintMessage(SMLoc::getFromPointer(OrigCmdlineName.data()),
|
||||
SourceMgr::DK_Error,
|
||||
"invalid name in string variable definition '" +
|
||||
OrigCmdlineName + "'");
|
||||
ErrorFound = true;
|
||||
Expected<StringRef> ParseVarResult =
|
||||
FileCheckPattern::parseVariable(CmdlineName, IsPseudo, SM);
|
||||
if (!ParseVarResult) {
|
||||
Errs = joinErrors(std::move(Errs), ParseVarResult.takeError());
|
||||
continue;
|
||||
}
|
||||
// Check that CmdlineName does not denote a pseudo variable is only
|
||||
// composed of the parsed numeric variable. This catches cases like
|
||||
// "FOO+2" in a "FOO+2=10" definition.
|
||||
if (IsPseudo || !CmdlineName.empty()) {
|
||||
Errs = joinErrors(std::move(Errs),
|
||||
FileCheckErrorDiagnostic::get(
|
||||
SM, OrigCmdlineName,
|
||||
"invalid name in string variable definition '" +
|
||||
OrigCmdlineName + "'"));
|
||||
continue;
|
||||
}
|
||||
StringRef Name = *ParseVarResult;
|
||||
|
||||
// Detect collisions between string 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;
|
||||
Errs = joinErrors(std::move(Errs), FileCheckErrorDiagnostic::get(
|
||||
SM, Name,
|
||||
"numeric variable with name '" +
|
||||
Name + "' already exists"));
|
||||
continue;
|
||||
}
|
||||
GlobalVariableTable.insert(CmdlineNameVal);
|
||||
@@ -1812,7 +1825,7 @@ bool FileCheckPatternContext::defineCmdlineVariables(
|
||||
}
|
||||
}
|
||||
|
||||
return ErrorFound;
|
||||
return Errs;
|
||||
}
|
||||
|
||||
void FileCheckPatternContext::clearLocalVars() {
|
||||
|
||||
@@ -128,3 +128,15 @@ INPUT-NUM-CONFLICT-NEXT: {{^ \^$}}
|
||||
CLI-NUM-CONFLICT: Global defines:2:20: error: string variable with name 'STRVAR' already exists
|
||||
CLI-NUM-CONFLICT-NEXT: Global define #2: #STRVAR=42
|
||||
CLI-NUM-CONFLICT-NEXT: {{^ \^$}}
|
||||
|
||||
; Numeric variable definition with too big value.
|
||||
RUN: not FileCheck --check-prefix BIGVAL --input-file %s %s 2>&1 \
|
||||
RUN: | FileCheck --strict-whitespace --check-prefix BIGVAL-MSG %s
|
||||
|
||||
BIG VALUE
|
||||
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-NEXT: {{N}}UMVAR: 10000000000000000000000
|
||||
BIGVAL-MSG-NEXT: {{^ \^$}}
|
||||
|
||||
@@ -28,7 +28,7 @@ ERRCLIVAR2: Missing variable name in command-line definition '-D='
|
||||
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 in string variable definition '10VALUE'
|
||||
ERRCLIFMT: Global defines:1:19: error: invalid variable name
|
||||
ERRCLIFMT-NEXT: Global define #1: 10VALUE=10
|
||||
ERRCLIFMT-NEXT: {{^ \^$}}
|
||||
|
||||
|
||||
@@ -41,26 +41,28 @@ TEST_F(FileCheckTest, NumericVariable) {
|
||||
|
||||
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
|
||||
|
||||
static void expectUndefError(const Twine &ExpectedStr, Error Err) {
|
||||
handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) {
|
||||
EXPECT_EQ(ExpectedStr.str(), E.getVarName());
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
// Defined variable: eval returns right value.
|
||||
Expected<uint64_t> Value = NumExpr.eval();
|
||||
EXPECT_TRUE(static_cast<bool>(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);
|
||||
Error EvalError = NumExpr.eval().takeError();
|
||||
EXPECT_TRUE(errorToBool(std::move(EvalError)));
|
||||
expectUndefError("FOO", std::move(EvalError));
|
||||
}
|
||||
|
||||
TEST_F(FileCheckTest, ValidVarNameStart) {
|
||||
@@ -75,72 +77,6 @@ TEST_F(FileCheckTest, ValidVarNameStart) {
|
||||
EXPECT_FALSE(FileCheckPattern::isValidVarNameStart(':'));
|
||||
}
|
||||
|
||||
TEST_F(FileCheckTest, ParseVar) {
|
||||
StringRef OrigVarName = "GoodVar42";
|
||||
StringRef VarName = OrigVarName;
|
||||
StringRef ParsedName;
|
||||
bool IsPseudo = true;
|
||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
EXPECT_EQ(ParsedName, OrigVarName);
|
||||
EXPECT_TRUE(VarName.empty());
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = OrigVarName = "$GoodGlobalVar";
|
||||
IsPseudo = true;
|
||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
EXPECT_EQ(ParsedName, OrigVarName);
|
||||
EXPECT_TRUE(VarName.empty());
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = OrigVarName = "@GoodPseudoVar";
|
||||
IsPseudo = true;
|
||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
EXPECT_EQ(ParsedName, OrigVarName);
|
||||
EXPECT_TRUE(VarName.empty());
|
||||
EXPECT_TRUE(IsPseudo);
|
||||
|
||||
VarName = "42BadVar";
|
||||
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
|
||||
VarName = "$@";
|
||||
EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
|
||||
VarName = OrigVarName = "B@dVar";
|
||||
IsPseudo = true;
|
||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
EXPECT_EQ(VarName, OrigVarName.substr(1));
|
||||
EXPECT_EQ(ParsedName, "B");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = OrigVarName = "B$dVar";
|
||||
IsPseudo = true;
|
||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
EXPECT_EQ(VarName, OrigVarName.substr(1));
|
||||
EXPECT_EQ(ParsedName, "B");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = "BadVar+";
|
||||
IsPseudo = true;
|
||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
EXPECT_EQ(VarName, "+");
|
||||
EXPECT_EQ(ParsedName, "BadVar");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = "BadVar-";
|
||||
IsPseudo = true;
|
||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
EXPECT_EQ(VarName, "-");
|
||||
EXPECT_EQ(ParsedName, "BadVar");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = "BadVar:";
|
||||
IsPseudo = true;
|
||||
EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo));
|
||||
EXPECT_EQ(VarName, ":");
|
||||
EXPECT_EQ(ParsedName, "BadVar");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
}
|
||||
|
||||
static StringRef bufferize(SourceMgr &SM, StringRef Str) {
|
||||
std::unique_ptr<MemoryBuffer> Buffer =
|
||||
MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
|
||||
@@ -149,6 +85,83 @@ static StringRef bufferize(SourceMgr &SM, StringRef Str) {
|
||||
return StrBufferRef;
|
||||
}
|
||||
|
||||
TEST_F(FileCheckTest, ParseVar) {
|
||||
SourceMgr SM;
|
||||
StringRef OrigVarName = bufferize(SM, "GoodVar42");
|
||||
StringRef VarName = OrigVarName;
|
||||
bool IsPseudo = true;
|
||||
Expected<StringRef> ParsedName =
|
||||
FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(static_cast<bool>(ParsedName));
|
||||
EXPECT_EQ(*ParsedName, OrigVarName);
|
||||
EXPECT_TRUE(VarName.empty());
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar");
|
||||
IsPseudo = true;
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(static_cast<bool>(ParsedName));
|
||||
EXPECT_EQ(*ParsedName, OrigVarName);
|
||||
EXPECT_TRUE(VarName.empty());
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar");
|
||||
IsPseudo = true;
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(static_cast<bool>(ParsedName));
|
||||
EXPECT_EQ(*ParsedName, OrigVarName);
|
||||
EXPECT_TRUE(VarName.empty());
|
||||
EXPECT_TRUE(IsPseudo);
|
||||
|
||||
VarName = bufferize(SM, "42BadVar");
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(errorToBool(ParsedName.takeError()));
|
||||
|
||||
VarName = bufferize(SM, "$@");
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(errorToBool(ParsedName.takeError()));
|
||||
|
||||
VarName = OrigVarName = bufferize(SM, "B@dVar");
|
||||
IsPseudo = true;
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(static_cast<bool>(ParsedName));
|
||||
EXPECT_EQ(VarName, OrigVarName.substr(1));
|
||||
EXPECT_EQ(*ParsedName, "B");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = OrigVarName = bufferize(SM, "B$dVar");
|
||||
IsPseudo = true;
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(static_cast<bool>(ParsedName));
|
||||
EXPECT_EQ(VarName, OrigVarName.substr(1));
|
||||
EXPECT_EQ(*ParsedName, "B");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = bufferize(SM, "BadVar+");
|
||||
IsPseudo = true;
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(static_cast<bool>(ParsedName));
|
||||
EXPECT_EQ(VarName, "+");
|
||||
EXPECT_EQ(*ParsedName, "BadVar");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = bufferize(SM, "BadVar-");
|
||||
IsPseudo = true;
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(static_cast<bool>(ParsedName));
|
||||
EXPECT_EQ(VarName, "-");
|
||||
EXPECT_EQ(*ParsedName, "BadVar");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
|
||||
VarName = bufferize(SM, "BadVar:");
|
||||
IsPseudo = true;
|
||||
ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM);
|
||||
EXPECT_TRUE(static_cast<bool>(ParsedName));
|
||||
EXPECT_EQ(VarName, ":");
|
||||
EXPECT_EQ(*ParsedName, "BadVar");
|
||||
EXPECT_FALSE(IsPseudo);
|
||||
}
|
||||
|
||||
class PatternTester {
|
||||
private:
|
||||
size_t LineNumber = 1;
|
||||
@@ -163,7 +176,8 @@ public:
|
||||
std::vector<std::string> GlobalDefines;
|
||||
GlobalDefines.emplace_back(std::string("#FOO=42"));
|
||||
GlobalDefines.emplace_back(std::string("BAR=BAZ"));
|
||||
Context.defineCmdlineVariables(GlobalDefines, SM);
|
||||
EXPECT_FALSE(
|
||||
errorToBool(Context.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
// Call parsePattern to have @LINE defined.
|
||||
P.parsePattern("N/A", "CHECK", SM, Req);
|
||||
// parsePattern does not expect to be called twice for the same line and
|
||||
@@ -179,15 +193,16 @@ public:
|
||||
bool parseNumVarDefExpect(StringRef Expr) {
|
||||
StringRef ExprBufferRef = bufferize(SM, Expr);
|
||||
StringRef Name;
|
||||
return FileCheckPattern::parseNumericVariableDefinition(ExprBufferRef, Name,
|
||||
&Context, SM);
|
||||
return errorToBool(FileCheckPattern::parseNumericVariableDefinition(
|
||||
ExprBufferRef, Name, &Context, SM));
|
||||
}
|
||||
|
||||
bool parseSubstExpect(StringRef Expr) {
|
||||
StringRef ExprBufferRef = bufferize(SM, Expr);
|
||||
FileCheckNumericVariable *DefinedNumericVariable;
|
||||
return P.parseNumericSubstitutionBlock(
|
||||
ExprBufferRef, DefinedNumericVariable, SM) == nullptr;
|
||||
Optional<FileCheckNumericVariable *> DefinedNumericVariable;
|
||||
return errorToBool(P.parseNumericSubstitutionBlock(
|
||||
ExprBufferRef, DefinedNumericVariable, SM)
|
||||
.takeError());
|
||||
}
|
||||
|
||||
bool parsePatternExpect(StringRef Pattern) {
|
||||
@@ -198,7 +213,7 @@ public:
|
||||
bool matchExpect(StringRef Buffer) {
|
||||
StringRef BufferRef = bufferize(SM, Buffer);
|
||||
size_t MatchLen;
|
||||
return P.match(BufferRef, MatchLen, SM);
|
||||
return errorToBool(P.match(BufferRef, MatchLen, SM).takeError());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -325,12 +340,15 @@ TEST_F(FileCheckTest, Substitution) {
|
||||
FileCheckPatternContext Context;
|
||||
std::vector<std::string> GlobalDefines;
|
||||
GlobalDefines.emplace_back(std::string("FOO=BAR"));
|
||||
Context.defineCmdlineVariables(GlobalDefines, SM);
|
||||
EXPECT_FALSE(errorToBool(Context.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
|
||||
// Substitution of an undefined string variable fails.
|
||||
// Substitution of an undefined string variable fails and error holds that
|
||||
// variable's name.
|
||||
FileCheckStringSubstitution StringSubstitution =
|
||||
FileCheckStringSubstitution(&Context, "VAR404", 42);
|
||||
EXPECT_FALSE(StringSubstitution.getResult());
|
||||
Expected<std::string> SubstValue = StringSubstitution.getResult();
|
||||
EXPECT_FALSE(static_cast<bool>(SubstValue));
|
||||
expectUndefError("VAR404", SubstValue.takeError());
|
||||
|
||||
// Substitutions of defined pseudo and non-pseudo numeric variables return
|
||||
// the right value.
|
||||
@@ -342,63 +360,31 @@ TEST_F(FileCheckTest, Substitution) {
|
||||
FileCheckNumericSubstitution(&Context, "@LINE", &NumExprLine, 12);
|
||||
FileCheckNumericSubstitution SubstitutionN =
|
||||
FileCheckNumericSubstitution(&Context, "N", &NumExprN, 30);
|
||||
llvm::Optional<std::string> Value = SubstitutionLine.getResult();
|
||||
EXPECT_TRUE(Value);
|
||||
Expected<std::string> Value = SubstitutionLine.getResult();
|
||||
EXPECT_TRUE(static_cast<bool>(Value));
|
||||
EXPECT_EQ("42", *Value);
|
||||
Value = SubstitutionN.getResult();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_TRUE(static_cast<bool>(Value));
|
||||
EXPECT_EQ("13", *Value);
|
||||
|
||||
// Substitution of an undefined numeric variable fails.
|
||||
LineVar.clearValue();
|
||||
EXPECT_FALSE(SubstitutionLine.getResult());
|
||||
SubstValue = SubstitutionLine.getResult().takeError();
|
||||
EXPECT_FALSE(static_cast<bool>(SubstValue));
|
||||
expectUndefError("@LINE", SubstValue.takeError());
|
||||
NVar.clearValue();
|
||||
EXPECT_FALSE(SubstitutionN.getResult());
|
||||
SubstValue = SubstitutionN.getResult().takeError();
|
||||
EXPECT_FALSE(static_cast<bool>(SubstValue));
|
||||
expectUndefError("N", SubstValue.takeError());
|
||||
|
||||
// Substitution of a defined string variable returns the right value.
|
||||
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1);
|
||||
StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
|
||||
Value = StringSubstitution.getResult();
|
||||
EXPECT_TRUE(Value);
|
||||
EXPECT_TRUE(static_cast<bool>(Value));
|
||||
EXPECT_EQ("BAR", *Value);
|
||||
}
|
||||
|
||||
TEST_F(FileCheckTest, UndefVars) {
|
||||
SourceMgr SM;
|
||||
FileCheckPatternContext Context;
|
||||
std::vector<std::string> GlobalDefines;
|
||||
GlobalDefines.emplace_back(std::string("FOO=BAR"));
|
||||
Context.defineCmdlineVariables(GlobalDefines, SM);
|
||||
|
||||
// getUndefVarName() on a string substitution with an undefined variable
|
||||
// returns that variable.
|
||||
FileCheckStringSubstitution StringSubstitution =
|
||||
FileCheckStringSubstitution(&Context, "VAR404", 42);
|
||||
StringRef UndefVar = StringSubstitution.getUndefVarName();
|
||||
EXPECT_EQ("VAR404", UndefVar);
|
||||
|
||||
// getUndefVarName() on a string substitution with a defined variable returns
|
||||
// an empty string.
|
||||
StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
|
||||
UndefVar = StringSubstitution.getUndefVarName();
|
||||
EXPECT_EQ("", UndefVar);
|
||||
|
||||
// getUndefVarName() on a numeric substitution with a defined variable
|
||||
// returns an empty string.
|
||||
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
|
||||
FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0);
|
||||
FileCheckNumericSubstitution NumericSubstitution =
|
||||
FileCheckNumericSubstitution(&Context, "@LINE", &NumExpr, 12);
|
||||
UndefVar = NumericSubstitution.getUndefVarName();
|
||||
EXPECT_EQ("", UndefVar);
|
||||
|
||||
// getUndefVarName() on a numeric substitution with an undefined variable
|
||||
// returns that variable.
|
||||
LineVar.clearValue();
|
||||
UndefVar = NumericSubstitution.getUndefVarName();
|
||||
EXPECT_EQ("@LINE", UndefVar);
|
||||
}
|
||||
|
||||
TEST_F(FileCheckTest, FileCheckContext) {
|
||||
FileCheckPatternContext Cxt = FileCheckPatternContext();
|
||||
std::vector<std::string> GlobalDefines;
|
||||
@@ -406,118 +392,115 @@ TEST_F(FileCheckTest, FileCheckContext) {
|
||||
|
||||
// Missing equal sign.
|
||||
GlobalDefines.emplace_back(std::string("LocalVar"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("#LocalNumVar"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
|
||||
// Empty variable name.
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("=18"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("#=18"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
|
||||
// Invalid variable name.
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("18LocalVar=18"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
GlobalDefines.clear();
|
||||
GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
|
||||
EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
|
||||
EXPECT_TRUE(errorToBool(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));
|
||||
EXPECT_TRUE(errorToBool(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));
|
||||
EXPECT_TRUE(errorToBool(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));
|
||||
EXPECT_TRUE(errorToBool(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);
|
||||
EXPECT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
|
||||
// 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);
|
||||
Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
|
||||
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
|
||||
FileCheckNumericVariable *DefinedNumericVariable;
|
||||
FileCheckNumExpr *NumExpr = P.parseNumericSubstitutionBlock(
|
||||
Optional<FileCheckNumericVariable *> DefinedNumericVariable;
|
||||
Expected<FileCheckNumExpr *> NumExpr = P.parseNumericSubstitutionBlock(
|
||||
LocalNumVarRef, DefinedNumericVariable, SM);
|
||||
llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
||||
llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
|
||||
EXPECT_TRUE(LocalVar);
|
||||
Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
||||
Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
|
||||
EXPECT_TRUE(static_cast<bool>(LocalVar));
|
||||
EXPECT_EQ(*LocalVar, "FOO");
|
||||
EXPECT_TRUE(NumExpr);
|
||||
llvm::Optional<uint64_t> NumExprVal = NumExpr->eval();
|
||||
EXPECT_TRUE(NumExprVal);
|
||||
EXPECT_TRUE(static_cast<bool>(NumExpr));
|
||||
Expected<uint64_t> NumExprVal = (*NumExpr)->eval();
|
||||
EXPECT_TRUE(static_cast<bool>(NumExprVal));
|
||||
EXPECT_EQ(*NumExprVal, 18U);
|
||||
EXPECT_TRUE(EmptyVar);
|
||||
EXPECT_TRUE(static_cast<bool>(EmptyVar));
|
||||
EXPECT_EQ(*EmptyVar, "");
|
||||
EXPECT_FALSE(UnknownVar);
|
||||
EXPECT_TRUE(errorToBool(UnknownVar.takeError()));
|
||||
|
||||
// Clear local variables and check they become absent.
|
||||
Cxt.clearLocalVars();
|
||||
LocalVar = Cxt.getPatternVarValue(LocalVarStr);
|
||||
EXPECT_FALSE(LocalVar);
|
||||
EXPECT_TRUE(errorToBool(LocalVar.takeError()));
|
||||
// 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());
|
||||
EXPECT_TRUE(errorToBool((*NumExpr)->eval().takeError()));
|
||||
P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
|
||||
NumExpr = P.parseNumericSubstitutionBlock(LocalNumVarRef,
|
||||
DefinedNumericVariable, SM);
|
||||
EXPECT_FALSE(NumExpr);
|
||||
EXPECT_TRUE(errorToBool(NumExpr.takeError()));
|
||||
EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
|
||||
EXPECT_FALSE(EmptyVar);
|
||||
EXPECT_TRUE(errorToBool(EmptyVar.takeError()));
|
||||
|
||||
// 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);
|
||||
EXPECT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
|
||||
StringRef GlobalVarStr = "$GlobalVar";
|
||||
StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
|
||||
llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
||||
EXPECT_TRUE(GlobalVar);
|
||||
Expected<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
||||
EXPECT_TRUE(static_cast<bool>(GlobalVar));
|
||||
EXPECT_EQ(*GlobalVar, "BAR");
|
||||
P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
|
||||
NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
|
||||
DefinedNumericVariable, SM);
|
||||
EXPECT_TRUE(NumExpr);
|
||||
NumExprVal = NumExpr->eval();
|
||||
EXPECT_TRUE(NumExprVal);
|
||||
EXPECT_TRUE(static_cast<bool>(NumExpr));
|
||||
NumExprVal = (*NumExpr)->eval();
|
||||
EXPECT_TRUE(static_cast<bool>(NumExprVal));
|
||||
EXPECT_EQ(*NumExprVal, 36U);
|
||||
|
||||
// Clear local variables and check global variables remain defined.
|
||||
Cxt.clearLocalVars();
|
||||
GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
|
||||
EXPECT_TRUE(GlobalVar);
|
||||
EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError()));
|
||||
P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
|
||||
NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef,
|
||||
DefinedNumericVariable, SM);
|
||||
EXPECT_TRUE(NumExpr);
|
||||
NumExprVal = NumExpr->eval();
|
||||
EXPECT_TRUE(NumExprVal);
|
||||
EXPECT_TRUE(static_cast<bool>(NumExpr));
|
||||
NumExprVal = (*NumExpr)->eval();
|
||||
EXPECT_TRUE(static_cast<bool>(NumExprVal));
|
||||
EXPECT_EQ(*NumExprVal, 36U);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user