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 @@ -288,10 +288,10 @@ /// 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 +323,28 @@ size_t InsertIdx); }; +/// Class to represent a parsing error, holding a diagnostic with location +/// information used when printing it. +class ParseErrorInfo : public ErrorInfo { +private: + SMDiagnostic Diagnostic; + +public: + static char ID; + + ParseErrorInfo(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.getSourceMgr()->PrintMessage( + OS, Diagnostic.getLoc(), Diagnostic.getKind(), Diagnostic.getMessage()); + } +}; + class FileCheckPattern { SMLoc PatternLoc; @@ -403,27 +425,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. @@ -478,15 +503,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 @@ -85,10 +85,13 @@ 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 make_error( + SM.GetMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error, + "empty variable name")); bool ParsedOneChar = false; unsigned I = 0; @@ -100,7 +103,9 @@ for (unsigned E = Str.size(); I != E; ++I) { if (!ParsedOneChar && !isValidVarNameStart(Str[I])) - return true; + return make_error( + SM.GetMessage(SMLoc::getFromPointer(Str.data()), SourceMgr::DK_Error, + "invalid variable name")); // Variable names are composed of alphanumeric characters and underscores. if (Str[I] != '_' && !isalnum(Str[I])) @@ -108,9 +113,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 +129,46 @@ return C; } -bool FileCheckPattern::parseNumericVariableDefinition( +char ParseErrorInfo::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 make_error( + SM.GetMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "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 make_error(SM.GetMessage( + SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "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 make_error( + SM.GetMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "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 +177,16 @@ // 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 make_error( + SM.GetMessage(SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "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 make_error(SM.GetMessage( + SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "numeric variable '" + Name + "' defined on the same line as used")); return NumericVariable; } @@ -201,13 +199,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 +224,37 @@ EvalBinop = sub; break; default: - SM.PrintMessage(OpLoc, SourceMgr::DK_Error, - Twine("unsupported numeric operation '") + Twine(Operator) + - "'"); - return nullptr; + return make_error(SM.GetMessage( + OpLoc, SourceMgr::DK_Error, + 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 make_error( + SM.GetMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "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 make_error( + SM.GetMessage(SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "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 make_error(SM.GetMessage( + SMLoc::getFromPointer(Expr.data()), SourceMgr::DK_Error, + "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 +262,24 @@ DefExpr = DefExpr.ltrim(SpaceChars); StringRef Name; - if (parseNumericVariableDefinition(DefExpr, Name, Context, SM)) { - // Invalid variable definition. Error reporting done in parsing function. - return nullptr; - } + Error ParseError = + parseNumericVariableDefinition(DefExpr, Name, Context, SM); + if (ParseError) + return std::move(ParseError); 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 make_error(SM.GetMessage( + SMLoc::getFromPointer(DefExpr.data()), SourceMgr::DK_Error, + "invalid numeric variable definition")); UseExpr = UseExpr.ltrim(SpaceChars); - if (!UseExpr.empty()) { - SM.PrintMessage( + if (!UseExpr.empty()) + return make_error(SM.GetMessage( SMLoc::getFromPointer(UseExpr.data()), SourceMgr::DK_Error, - "unexpected string after variable definition: '" + UseExpr + "'"); - return nullptr; - } + "unexpected string after variable definition: '" + UseExpr + "'")); return Context->makeNumExpr(add, nullptr, 0); } @@ -429,13 +420,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 +460,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 +508,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 @@ -1077,8 +1072,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) { @@ -1687,19 +1686,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 +1722,11 @@ 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), + make_error(SM.GetMessage( + SMLoc::getFromPointer(CmdlineDef.data()), SourceMgr::DK_Error, + "Missing equal sign in global definition"))); continue; } @@ -1735,37 +1735,42 @@ StringRef CmdlineName = CmdlineDef.substr(1, EqIdx - 1); StringRef VarName; SMLoc CmdlineNameLoc = SMLoc::getFromPointer(CmdlineName.data()); - bool ParseError = FileCheckPattern::parseNumericVariableDefinition( + Error ParseError = 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 (ParseError) { + Errs = joinErrors(std::move(Errs), std::move(ParseError)); + 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), + make_error(SM.GetMessage( + CmdlineNameLoc, SourceMgr::DK_Error, "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), + make_error(SM.GetMessage( + SMLoc::getFromPointer(VarName.data()), SourceMgr::DK_Error, + "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), + make_error(SM.GetMessage( + SMLoc::getFromPointer(CmdlineVal.data()), SourceMgr::DK_Error, + "invalid value in numeric variable definition '" + CmdlineVal + + "'"))); continue; } auto DefinedNumericVariable = makeNumericVariable(0, VarName); @@ -1779,26 +1784,36 @@ 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), + make_error(SM.GetMessage( + SMLoc::getFromPointer(OrigCmdlineName.data()), + SourceMgr::DK_Error, + "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), + make_error(SM.GetMessage( + SMLoc::getFromPointer(Name.data()), SourceMgr::DK_Error, + "numeric variable with name '" + Name + "' already exists"))); continue; } GlobalVariableTable.insert(CmdlineNameVal); @@ -1812,7 +1827,7 @@ } } - return ErrorFound; + return Errs; } void FileCheckPatternContext::clearLocalVars() { 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 @@ -75,80 +75,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_FALSE(static_cast(ParsedName)); - VarName = "$@"; - EXPECT_TRUE(FileCheckPattern::parseVariable(VarName, ParsedName, IsPseudo)); + VarName = bufferize(SM, "$@"); + ParsedName = FileCheckPattern::parseVariable(VarName, IsPseudo, SM); + EXPECT_FALSE(static_cast(ParsedName)); - 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 +174,8 @@ std::vector GlobalDefines; GlobalDefines.emplace_back(std::string("#FOO=42")); GlobalDefines.emplace_back(std::string("BAR=BAZ")); - Context.defineCmdlineVariables(GlobalDefines, SM); + EXPECT_FALSE( + static_cast(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 +191,15 @@ bool parseNumVarDefExpect(StringRef Expr) { StringRef ExprBufferRef = bufferize(SM, Expr); StringRef Name; - return FileCheckPattern::parseNumericVariableDefinition(ExprBufferRef, Name, - &Context, SM); + return static_cast(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 !P.parseNumericSubstitutionBlock(ExprBufferRef, + DefinedNumericVariable, SM); } bool parsePatternExpect(StringRef Pattern) { @@ -325,7 +337,8 @@ FileCheckPatternContext Context; std::vector GlobalDefines; GlobalDefines.emplace_back(std::string("FOO=BAR")); - Context.defineCmdlineVariables(GlobalDefines, SM); + EXPECT_FALSE( + static_cast(Context.defineCmdlineVariables(GlobalDefines, SM))); // Substitution of an undefined string variable fails. FileCheckStringSubstitution StringSubstitution = @@ -368,7 +381,8 @@ FileCheckPatternContext Context; std::vector GlobalDefines; GlobalDefines.emplace_back(std::string("FOO=BAR")); - Context.defineCmdlineVariables(GlobalDefines, SM); + EXPECT_FALSE( + static_cast(Context.defineCmdlineVariables(GlobalDefines, SM))); // getUndefVarName() on a string substitution with an undefined variable // returns that variable. @@ -406,51 +420,51 @@ // Missing equal sign. GlobalDefines.emplace_back(std::string("LocalVar")); - EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM)); + EXPECT_TRUE(static_cast(Cxt.defineCmdlineVariables(GlobalDefines, SM))); GlobalDefines.clear(); GlobalDefines.emplace_back(std::string("#LocalNumVar")); - EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM)); + EXPECT_TRUE(static_cast(Cxt.defineCmdlineVariables(GlobalDefines, SM))); // Empty variable name. GlobalDefines.clear(); GlobalDefines.emplace_back(std::string("=18")); - EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM)); + EXPECT_TRUE(static_cast(Cxt.defineCmdlineVariables(GlobalDefines, SM))); GlobalDefines.clear(); GlobalDefines.emplace_back(std::string("#=18")); - EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM)); + EXPECT_TRUE(static_cast(Cxt.defineCmdlineVariables(GlobalDefines, SM))); // Invalid variable name. GlobalDefines.clear(); GlobalDefines.emplace_back(std::string("18LocalVar=18")); - EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM)); + EXPECT_TRUE(static_cast(Cxt.defineCmdlineVariables(GlobalDefines, SM))); GlobalDefines.clear(); GlobalDefines.emplace_back(std::string("#18LocalNumVar=18")); - EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM)); + EXPECT_TRUE(static_cast(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(static_cast(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(static_cast(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(static_cast(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); + Error Err = Cxt.defineCmdlineVariables(GlobalDefines, SM); + EXPECT_FALSE(static_cast(Err)); // Check defined variables are present and undefined is absent. StringRef LocalVarStr = "LocalVar"; @@ -459,15 +473,15 @@ StringRef UnknownVarStr = "UnknownVar"; llvm::Optional 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); EXPECT_EQ(*LocalVar, "FOO"); - EXPECT_TRUE(NumExpr); - llvm::Optional NumExprVal = NumExpr->eval(); + EXPECT_TRUE(static_cast(NumExpr)); + llvm::Optional NumExprVal = (*NumExpr)->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 18U); EXPECT_TRUE(EmptyVar); @@ -482,19 +496,19 @@ // 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_FALSE((*NumExpr)->eval()); P = FileCheckPattern(Check::CheckPlain, &Cxt, 2); NumExpr = P.parseNumericSubstitutionBlock(LocalNumVarRef, DefinedNumericVariable, SM); - EXPECT_FALSE(NumExpr); + EXPECT_FALSE(static_cast(NumExpr)); EmptyVar = Cxt.getPatternVarValue(EmptyVarStr); EXPECT_FALSE(EmptyVar); // Redefine global variables and check variables are defined again. GlobalDefines.emplace_back(std::string("$GlobalVar=BAR")); GlobalDefines.emplace_back(std::string("#$GlobalNumVar=36")); - GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM); - EXPECT_FALSE(GotError); + Err = Cxt.defineCmdlineVariables(GlobalDefines, SM); + EXPECT_FALSE(static_cast(Err)); StringRef GlobalVarStr = "$GlobalVar"; StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar"); llvm::Optional GlobalVar = Cxt.getPatternVarValue(GlobalVarStr); @@ -503,8 +517,8 @@ P = FileCheckPattern(Check::CheckPlain, &Cxt, 3); NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef, DefinedNumericVariable, SM); - EXPECT_TRUE(NumExpr); - NumExprVal = NumExpr->eval(); + EXPECT_TRUE(static_cast(NumExpr)); + NumExprVal = (*NumExpr)->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 36U); @@ -515,8 +529,8 @@ P = FileCheckPattern(Check::CheckPlain, &Cxt, 4); NumExpr = P.parseNumericSubstitutionBlock(GlobalNumVarRef, DefinedNumericVariable, SM); - EXPECT_TRUE(NumExpr); - NumExprVal = NumExpr->eval(); + EXPECT_TRUE(static_cast(NumExpr)); + NumExprVal = (*NumExpr)->eval(); EXPECT_TRUE(NumExprVal); EXPECT_EQ(*NumExprVal, 36U); }