diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -87,6 +87,30 @@ /// 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 { +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 @@ : 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 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 eval() const; }; class FileCheckPatternContext; @@ -151,12 +171,8 @@ 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 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 getResult() const = 0; }; class FileCheckStringSubstitution : public FileCheckSubstitution { @@ -166,12 +182,8 @@ : 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 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 getResult() const override; }; class FileCheckNumericSubstitution : public FileCheckSubstitution { @@ -186,12 +198,8 @@ : 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 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 getResult() const override; }; //===----------------------------------------------------------------------===// @@ -282,16 +290,16 @@ std::vector> 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 getPatternVarValue(StringRef VarName); + Expected 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 &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 &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 @@ size_t InsertIdx); }; +/// Class to represent an error holding a diagnostic with location information +/// used when printing it. +class FileCheckErrorDiagnostic : public ErrorInfo { +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( + 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 { +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 @@ /// \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 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 parseNumericSubstitutionBlock( + StringRef Expr, + Optional &DefinedNumericVariable, const SourceMgr &SM) const; /// Parses the pattern in \p PatternStr and initializes this FileCheckPattern /// instance accordingly. @@ -436,9 +489,9 @@ 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 @@ /// 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 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 @@ 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 + 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 parseBinop(StringRef &Expr, + const SourceMgr &SM) const; }; //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -38,57 +38,39 @@ return false; } -Optional FileCheckNumExpr::eval() const { +Expected FileCheckNumExpr::eval() const { assert(LeftOp && "Evaluating an empty numeric expression"); Optional LeftOpValue = LeftOp->getValue(); // Variable is undefined. if (!LeftOpValue) - return None; + return make_error(LeftOp->getName()); return EvalBinop(*LeftOpValue, RightOp); } -StringRef FileCheckNumExpr::getUndefVarName() const { - if (!LeftOp->getValue()) - return LeftOp->getName(); - return StringRef(); -} - -Optional FileCheckNumericSubstitution::getResult() const { - Optional EvaluatedValue = NumExpr->eval(); +Expected FileCheckNumericSubstitution::getResult() const { + Expected EvaluatedValue = NumExpr->eval(); if (!EvaluatedValue) - return None; + return EvaluatedValue.takeError(); return utostr(*EvaluatedValue); } -Optional FileCheckStringSubstitution::getResult() const { +Expected FileCheckStringSubstitution::getResult() const { // Look up the value and escape it so that we can put it into the regex. - Optional VarVal = Context->getPatternVarValue(FromStr); + Expected 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 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 @@ 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 @@ 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 @@ 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 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 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 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 @@ // 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 @@ 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 +FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const { + Expected 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 @@ 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 FileCheckPattern::parseNumericSubstitutionBlock( + StringRef Expr, + Optional &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 @@ 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 @@ // 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 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 @@ // Parse numeric substitution block. FileCheckNumExpr *NumExpr; - FileCheckNumericVariable *DefinedNumericVariable; + Optional DefinedNumericVariable; if (IsNumBlock) { - NumExpr = + Expected 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 @@ // Handle variable definitions: [[:(...)]] and // [[#(...):(...)]]. 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 @@ RegExStr += Backref; } -size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen, - const SourceMgr &SM) const { +Expected 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 @@ // 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(); + return Pos; } // Regex match. @@ -605,9 +575,9 @@ // handled by back-references. for (const auto &Substitution : Substitutions) { // Substitute and check for failure (e.g. use of undefined variable). - Optional Value = Substitution->getResult(); + Expected 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 @@ SmallVector MatchInfo; if (!Regex(RegExToMatch, Regex::Newline).match(Buffer, &MatchInfo)) - return StringRef::npos; + return make_error(); // Successful regex match. assert(!MatchInfo.empty() && "Didn't get any match"); @@ -645,10 +615,9 @@ 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 store numeric value"); if (DefinedNumericVariable->setValue(Val)) assert(false && "Numeric variable redefined"); } @@ -685,16 +654,24 @@ for (const auto &Substitution : Substitutions) { SmallString<256> Msg; raw_svector_ostream OS(Msg); - Optional MatchedValue = Substitution->getResult(); + Expected 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) { assert("Unexpected error"); }); } else { // Substitution succeeded. Print substituted value. OS << "with \""; @@ -777,11 +754,11 @@ } } -Optional +Expected FileCheckPatternContext::getPatternVarValue(StringRef VarName) { auto VarIter = GlobalVariableTable.find(VarName); if (VarIter == GlobalVariableTable.end()) - return None; + return make_error(VarName); return VarIter->second; } @@ -1077,8 +1054,12 @@ bool FileCheck::ReadCheckFile(SourceMgr &SM, StringRef Buffer, Regex &PrefixRE, std::vector &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 ImplicitNegativeChecks; for (const auto &PatternString : Req.ImplicitCheckNot) { @@ -1276,11 +1257,14 @@ StringRef Prefix, SMLoc Loc, const FileCheckPattern &Pat, int MatchedCount, StringRef Buffer, bool VerboseVerbose, - std::vector *Diags) { + std::vector *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 +1278,19 @@ 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 +1315,10 @@ static void PrintNoMatch(bool ExpectedMatch, const SourceMgr &SM, const FileCheckString &CheckStr, int MatchedCount, StringRef Buffer, bool VerboseVerbose, - std::vector *Diags) { + std::vector *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 +1372,19 @@ 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 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 +1495,14 @@ assert((Pat->getCheckTy() == Check::CheckNot) && "Expect CHECK-NOT!"); size_t MatchLen = 0; - size_t Pos = Pat->match(Buffer, MatchLen, SM); + Expected 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 +1556,15 @@ // 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 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 +1687,19 @@ return Regex(PrefixRegexStr); } -bool FileCheckPatternContext::defineCmdlineVariables( +Error FileCheckPatternContext::defineCmdlineVariables( std::vector &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 +1723,10 @@ 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 +1735,40 @@ 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 +1782,34 @@ std::pair 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 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 +1823,7 @@ } } - return ErrorFound; + return Errs; } void FileCheckPatternContext::clearLocalVars() { diff --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt --- a/llvm/test/FileCheck/numeric-expression.txt +++ b/llvm/test/FileCheck/numeric-expression.txt @@ -128,3 +128,15 @@ 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 store numeric value +BIGVAL-MSG-NEXT: {{N}}UMVAR: 10000000000000000000000 +BIGVAL-MSG-NEXT: {{^ \^$}} diff --git a/llvm/test/FileCheck/string-defines-diagnostics.txt b/llvm/test/FileCheck/string-defines-diagnostics.txt --- a/llvm/test/FileCheck/string-defines-diagnostics.txt +++ b/llvm/test/FileCheck/string-defines-diagnostics.txt @@ -28,7 +28,7 @@ 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: {{^ \^$}} diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -41,26 +41,28 @@ 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 Value = NumExpr.eval(); - EXPECT_TRUE(Value); + // Defined variable: eval returns right value. + Expected Value = NumExpr.eval(); + EXPECT_TRUE(static_cast(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,80 +77,91 @@ EXPECT_FALSE(FileCheckPattern::isValidVarNameStart(':')); } +static StringRef bufferize(SourceMgr &SM, StringRef Str) { + std::unique_ptr Buffer = + MemoryBuffer::getMemBufferCopy(Str, "TestBuffer"); + StringRef StrBufferRef = Buffer->getBuffer(); + SM.AddNewSourceBuffer(std::move(Buffer), SMLoc()); + return StrBufferRef; +} + TEST_F(FileCheckTest, ParseVar) { - StringRef OrigVarName = "GoodVar42"; + SourceMgr SM; + StringRef OrigVarName = bufferize(SM, "GoodVar42"); StringRef VarName = OrigVarName; - StringRef ParsedName; bool IsPseudo = true; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); - EXPECT_EQ(ParsedName, OrigVarName); + Expected ParsedName = + FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(static_cast(ParsedName)); + EXPECT_EQ(*ParsedName, OrigVarName); EXPECT_TRUE(VarName.empty()); EXPECT_FALSE(IsPseudo); - VarName = OrigVarName = "$GoodGlobalVar"; + VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar"); IsPseudo = true; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); - EXPECT_EQ(ParsedName, OrigVarName); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(static_cast(ParsedName)); + EXPECT_EQ(*ParsedName, OrigVarName); EXPECT_TRUE(VarName.empty()); EXPECT_FALSE(IsPseudo); - VarName = OrigVarName = "@GoodPseudoVar"; + VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar"); IsPseudo = true; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); - EXPECT_EQ(ParsedName, OrigVarName); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(static_cast(ParsedName)); + EXPECT_EQ(*ParsedName, OrigVarName); EXPECT_TRUE(VarName.empty()); EXPECT_TRUE(IsPseudo); - VarName = "42BadVar"; - EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + VarName = bufferize(SM, "42BadVar"); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(errorToBool(ParsedName.takeError())); - VarName = "$@"; - EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + VarName = bufferize(SM, "$@"); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(errorToBool(ParsedName.takeError())); - VarName = OrigVarName = "B@dVar"; + VarName = OrigVarName = bufferize(SM, "B@dVar"); IsPseudo = true; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(static_cast(ParsedName)); EXPECT_EQ(VarName, OrigVarName.substr(1)); - EXPECT_EQ(ParsedName, "B"); + EXPECT_EQ(*ParsedName, "B"); EXPECT_FALSE(IsPseudo); - VarName = OrigVarName = "B$dVar"; + VarName = OrigVarName = bufferize(SM, "B$dVar"); IsPseudo = true; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(static_cast(ParsedName)); EXPECT_EQ(VarName, OrigVarName.substr(1)); - EXPECT_EQ(ParsedName, "B"); + EXPECT_EQ(*ParsedName, "B"); EXPECT_FALSE(IsPseudo); - VarName = "BadVar+"; + VarName = bufferize(SM, "BadVar+"); IsPseudo = true; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(static_cast(ParsedName)); EXPECT_EQ(VarName, "+"); - EXPECT_EQ(ParsedName, "BadVar"); + EXPECT_EQ(*ParsedName, "BadVar"); EXPECT_FALSE(IsPseudo); - VarName = "BadVar-"; + VarName = bufferize(SM, "BadVar-"); IsPseudo = true; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(static_cast(ParsedName)); EXPECT_EQ(VarName, "-"); - EXPECT_EQ(ParsedName, "BadVar"); + EXPECT_EQ(*ParsedName, "BadVar"); EXPECT_FALSE(IsPseudo); - VarName = "BadVar:"; + VarName = bufferize(SM, "BadVar:"); IsPseudo = true; - EXPECT_FALSE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_TRUE(static_cast(ParsedName)); EXPECT_EQ(VarName, ":"); - EXPECT_EQ(ParsedName, "BadVar"); + EXPECT_EQ(*ParsedName, "BadVar"); EXPECT_FALSE(IsPseudo); } -static StringRef bufferize(SourceMgr &SM, StringRef Str) { - std::unique_ptr Buffer = - MemoryBuffer::getMemBufferCopy(Str, "TestBuffer"); - StringRef StrBufferRef = Buffer->getBuffer(); - SM.AddNewSourceBuffer(std::move(Buffer), SMLoc()); - return StrBufferRef; -} - class PatternTester { private: size_t LineNumber = 1; @@ -163,7 +176,8 @@ std::vector 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 @@ 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 DefinedNumericVariable; + return errorToBool(P.parseNumericSubstitutionBlock( + ExprBufferRef, DefinedNumericVariable, SM) + .takeError()); } bool parsePatternExpect(StringRef Pattern) { @@ -325,12 +340,15 @@ FileCheckPatternContext Context; std::vector 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 SubstValue = StringSubstitution.getResult(); + EXPECT_FALSE(static_cast(SubstValue)); + expectUndefError("VAR404", SubstValue.takeError()); // Substitutions of defined pseudo and non-pseudo numeric variables return // the right value. @@ -342,63 +360,31 @@ FileCheckNumericSubstitution(&Context, "@LINE", &NumExprLine, 12); FileCheckNumericSubstitution SubstitutionN = FileCheckNumericSubstitution(&Context, "N", &NumExprN, 30); - llvm::Optional Value = SubstitutionLine.getResult(); - EXPECT_TRUE(Value); + Expected Value = SubstitutionLine.getResult(); + EXPECT_TRUE(static_cast(Value)); EXPECT_EQ("42", *Value); Value = SubstitutionN.getResult(); - EXPECT_TRUE(Value); + EXPECT_TRUE(static_cast(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(SubstValue)); + expectUndefError("@LINE", SubstValue.takeError()); NVar.clearValue(); - EXPECT_FALSE(SubstitutionN.getResult()); + SubstValue = SubstitutionN.getResult().takeError(); + EXPECT_FALSE(static_cast(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(Value)); EXPECT_EQ("BAR", *Value); } -TEST_F(FileCheckTest, UndefVars) { - SourceMgr SM; - FileCheckPatternContext Context; - std::vector 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 GlobalDefines; @@ -406,118 +392,115 @@ // 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 LocalVar = Cxt.getPatternVarValue(LocalVarStr); + Expected LocalVar = Cxt.getPatternVarValue(LocalVarStr); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1); - FileCheckNumericVariable *DefinedNumericVariable; - FileCheckNumExpr *NumExpr = P.parseNumericSubstitutionBlock( + Optional DefinedNumericVariable; + Expected NumExpr = P.parseNumericSubstitutionBlock( LocalNumVarRef, DefinedNumericVariable, SM); - llvm::Optional EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); - llvm::Optional UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); - EXPECT_TRUE(LocalVar); + Expected EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); + Expected UnknownVar = Cxt.getPatternVarValue(UnknownVarStr); + EXPECT_TRUE(static_cast(LocalVar)); EXPECT_EQ(*LocalVar, "FOO"); - EXPECT_TRUE(NumExpr); - llvm::Optional NumExprVal = NumExpr->eval(); - EXPECT_TRUE(NumExprVal); + EXPECT_TRUE(static_cast(NumExpr)); + Expected NumExprVal = (*NumExpr)->eval(); + EXPECT_TRUE(static_cast(NumExprVal)); EXPECT_EQ(*NumExprVal, 18U); - EXPECT_TRUE(EmptyVar); + EXPECT_TRUE(static_cast(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 GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); - EXPECT_TRUE(GlobalVar); + Expected GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); + EXPECT_TRUE(static_cast(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(NumExpr)); + NumExprVal = (*NumExpr)->eval(); + EXPECT_TRUE(static_cast(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(NumExpr)); + NumExprVal = (*NumExpr)->eval(); + EXPECT_TRUE(static_cast(NumExprVal)); EXPECT_EQ(*NumExprVal, 36U); } } // namespace