diff options
Diffstat (limited to 'llvm/lib/Support/FileCheck.cpp')
-rw-r--r-- | llvm/lib/Support/FileCheck.cpp | 208 |
1 files changed, 124 insertions, 84 deletions
diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp index 5ec126f934e..9fb4d798849 100644 --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -35,17 +35,33 @@ void FileCheckNumericVariable::clearValue() { Value = None; } -Expected<uint64_t> FileCheckExpression::eval() const { - assert(LeftOp && "Evaluating an empty expression"); - Optional<uint64_t> LeftOpValue = LeftOp->getValue(); - // Variable is undefined. - if (!LeftOpValue) - return make_error<FileCheckUndefVarError>(LeftOp->getName()); - return EvalBinop(*LeftOpValue, RightOp); +Expected<uint64_t> FileCheckNumericVariableUse::eval() const { + Optional<uint64_t> Value = NumericVariable->getValue(); + if (Value) + return *Value; + return make_error<FileCheckUndefVarError>(Name); +} + +Expected<uint64_t> FileCheckASTBinop::eval() const { + Expected<uint64_t> LeftOp = LeftOperand->eval(); + Expected<uint64_t> RightOp = RightOperand->eval(); + + // Bubble up any error (e.g. undefined variables) in the recursive + // evaluation. + if (!LeftOp || !RightOp) { + Error Err = Error::success(); + if (!LeftOp) + Err = joinErrors(std::move(Err), LeftOp.takeError()); + if (!RightOp) + Err = joinErrors(std::move(Err), RightOp.takeError()); + return std::move(Err); + } + + return EvalBinop(*LeftOp, *RightOp); } Expected<std::string> FileCheckNumericSubstitution::getResult() const { - Expected<uint64_t> EvaluatedValue = Expression->eval(); + Expected<uint64_t> EvaluatedValue = ExpressionAST->eval(); if (!EvaluatedValue) return EvaluatedValue.takeError(); return utostr(*EvaluatedValue); @@ -63,15 +79,14 @@ bool FileCheckPattern::isValidVarNameStart(char C) { return C == '_' || isalpha(C); } -Expected<StringRef> FileCheckPattern::parseVariable(StringRef &Str, - bool &IsPseudo, - const SourceMgr &SM) { +Expected<FileCheckPattern::VariableProperties> +FileCheckPattern::parseVariable(StringRef &Str, const SourceMgr &SM) { if (Str.empty()) return FileCheckErrorDiagnostic::get(SM, Str, "empty variable name"); bool ParsedOneChar = false; unsigned I = 0; - IsPseudo = Str[0] == '@'; + bool IsPseudo = Str[0] == '@'; // Global vars start with '$'. if (Str[0] == '$' || IsPseudo) @@ -89,7 +104,7 @@ Expected<StringRef> FileCheckPattern::parseVariable(StringRef &Str, StringRef Name = Str.take_front(I); Str = Str.substr(I); - return Name; + return VariableProperties {Name, IsPseudo}; } // StringRef holding all characters considered as horizontal whitespaces by @@ -111,13 +126,12 @@ Expected<FileCheckNumericVariable *> FileCheckPattern::parseNumericVariableDefinition( StringRef &Expr, FileCheckPatternContext *Context, size_t LineNumber, const SourceMgr &SM) { - bool IsPseudo; - Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM); + Expected<VariableProperties> ParseVarResult = parseVariable(Expr, SM); if (!ParseVarResult) return ParseVarResult.takeError(); - StringRef Name = *ParseVarResult; + StringRef Name = ParseVarResult->Name; - if (IsPseudo) + if (ParseVarResult->IsPseudo) return FileCheckErrorDiagnostic::get( SM, Name, "definition of pseudo numeric variable unsupported"); @@ -143,15 +157,9 @@ FileCheckPattern::parseNumericVariableDefinition( return DefinedNumericVariable; } -Expected<FileCheckNumericVariable *> -FileCheckPattern::parseNumericVariableUse(StringRef &Expr, +Expected<std::unique_ptr<FileCheckNumericVariableUse>> +FileCheckPattern::parseNumericVariableUse(StringRef Name, bool IsPseudo, const SourceMgr &SM) const { - bool IsPseudo; - Expected<StringRef> ParseVarResult = parseVariable(Expr, IsPseudo, SM); - if (!ParseVarResult) - return ParseVarResult.takeError(); - StringRef Name = *ParseVarResult; - if (IsPseudo && !Name.equals("@LINE")) return FileCheckErrorDiagnostic::get( SM, Name, "invalid pseudo numeric variable '" + Name + "'"); @@ -178,7 +186,32 @@ FileCheckPattern::parseNumericVariableUse(StringRef &Expr, SM, Name, "numeric variable '" + Name + "' defined on the same line as used"); - return NumericVariable; + return llvm::make_unique<FileCheckNumericVariableUse>(Name, NumericVariable); +} + +Expected<std::unique_ptr<FileCheckExpressionAST>> +FileCheckPattern::parseNumericOperand(StringRef &Expr, AllowedOperand AO, + const SourceMgr &SM) const { + if (AO == AllowedOperand::LineVar || AO == AllowedOperand::Any) { + // Try to parse as a numeric variable use. + Expected<FileCheckPattern::VariableProperties> ParseVarResult = + parseVariable(Expr, SM); + if (ParseVarResult) + return parseNumericVariableUse(ParseVarResult->Name, + ParseVarResult->IsPseudo, SM); + if (AO == AllowedOperand::LineVar) + return ParseVarResult.takeError(); + // Ignore the error and retry parsing as a literal. + consumeError(ParseVarResult.takeError()); + } + + // Otherwise, parse it as a literal. + uint64_t LiteralValue; + if (!Expr.consumeInteger(/*Radix=*/10, LiteralValue)) + return llvm::make_unique<FileCheckExpressionLiteral>(LiteralValue); + + return FileCheckErrorDiagnostic::get(SM, Expr, + "invalid operand format '" + Expr + "'"); } static uint64_t add(uint64_t LeftOp, uint64_t RightOp) { @@ -189,20 +222,16 @@ static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) { return LeftOp - RightOp; } -Expected<FileCheckExpression *> -FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const { - Expected<FileCheckNumericVariable *> LeftParseResult = - parseNumericVariableUse(Expr, SM); - if (!LeftParseResult) { - return LeftParseResult.takeError(); - } - FileCheckNumericVariable *LeftOp = *LeftParseResult; +Expected<std::unique_ptr<FileCheckExpressionAST>> +FileCheckPattern::parseBinop(StringRef &Expr, + std::unique_ptr<FileCheckExpressionAST> LeftOp, + bool IsLegacyLineExpr, const SourceMgr &SM) const { + Expr = Expr.ltrim(SpaceChars); + if (Expr.empty()) + return std::move(LeftOp); // Check if this is a supported operation and select a function to perform // it. - Expr = Expr.ltrim(SpaceChars); - if (Expr.empty()) - return Context->makeExpression(add, LeftOp, 0); SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); char Operator = popFront(Expr); binop_eval_t EvalBinop; @@ -223,22 +252,24 @@ FileCheckPattern::parseBinop(StringRef &Expr, const SourceMgr &SM) const { if (Expr.empty()) return FileCheckErrorDiagnostic::get(SM, Expr, "missing operand in expression"); - uint64_t RightOp; - if (Expr.consumeInteger(10, RightOp)) - return FileCheckErrorDiagnostic::get( - SM, Expr, "invalid offset in expression '" + Expr + "'"); - Expr = Expr.ltrim(SpaceChars); - if (!Expr.empty()) - return FileCheckErrorDiagnostic::get( - SM, Expr, "unexpected characters at end of expression '" + Expr + "'"); + // The second operand in a legacy @LINE expression is always a literal. + AllowedOperand AO = + IsLegacyLineExpr ? AllowedOperand::Literal : AllowedOperand::Any; + Expected<std::unique_ptr<FileCheckExpressionAST>> RightOpResult = + parseNumericOperand(Expr, AO, SM); + if (!RightOpResult) + return RightOpResult; - return Context->makeExpression(EvalBinop, LeftOp, RightOp); + Expr = Expr.ltrim(SpaceChars); + return llvm::make_unique<FileCheckASTBinop>(EvalBinop, std::move(LeftOp), + std::move(*RightOpResult)); } -Expected<FileCheckExpression *> FileCheckPattern::parseNumericSubstitutionBlock( +Expected<std::unique_ptr<FileCheckExpressionAST>> +FileCheckPattern::parseNumericSubstitutionBlock( StringRef Expr, Optional<FileCheckNumericVariable *> &DefinedNumericVariable, - const SourceMgr &SM) const { + bool IsLegacyLineExpr, const SourceMgr &SM) const { // Parse the numeric variable definition. DefinedNumericVariable = None; size_t DefEnd = Expr.find(':'); @@ -259,12 +290,29 @@ Expected<FileCheckExpression *> FileCheckPattern::parseNumericSubstitutionBlock( return ParseResult.takeError(); DefinedNumericVariable = *ParseResult; - return Context->makeExpression(add, nullptr, 0); + return nullptr; } // Parse the expression itself. Expr = Expr.ltrim(SpaceChars); - return parseBinop(Expr, SM); + // The first operand in a legacy @LINE expression is always the @LINE pseudo + // variable. + AllowedOperand AO = + IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any; + Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult = + parseNumericOperand(Expr, AO, SM); + while (ParseResult && !Expr.empty()) { + ParseResult = + parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, SM); + // Legacy @LINE expressions only allow 2 operands. + if (ParseResult && IsLegacyLineExpr && !Expr.empty()) + return FileCheckErrorDiagnostic::get( + SM, Expr, + "unexpected characters at end of expression '" + Expr + "'"); + } + if (!ParseResult) + return ParseResult; + return std::move(*ParseResult); } bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, @@ -375,12 +423,15 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, PatternStr = UnparsedPatternStr.substr(End + 2); bool IsDefinition = false; + // Whether the substitution block is a legacy use of @LINE with string + // substitution block syntax. + bool IsLegacyLineExpr = false; StringRef DefName; StringRef SubstStr; StringRef MatchRegexp; size_t SubstInsertIdx = RegExStr.size(); - // Parse string variable or legacy expression. + // Parse string variable or legacy @LINE expression. if (!IsNumBlock) { size_t VarEndIdx = MatchStr.find(":"); size_t SpacePos = MatchStr.substr(0, VarEndIdx).find_first_of(" \t"); @@ -391,15 +442,15 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, } // Get the name (e.g. "foo") and verify it is well formed. - bool IsPseudo; StringRef OrigMatchStr = MatchStr; - Expected<StringRef> ParseVarResult = - parseVariable(MatchStr, IsPseudo, SM); + Expected<FileCheckPattern::VariableProperties> ParseVarResult = + parseVariable(MatchStr, SM); if (!ParseVarResult) { logAllUnhandledErrors(ParseVarResult.takeError(), errs()); return true; } - StringRef Name = *ParseVarResult; + StringRef Name = ParseVarResult->Name; + bool IsPseudo = ParseVarResult->IsPseudo; IsDefinition = (VarEndIdx != StringRef::npos); if (IsDefinition) { @@ -424,23 +475,24 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, } else { if (IsPseudo) { MatchStr = OrigMatchStr; - IsNumBlock = true; + IsLegacyLineExpr = IsNumBlock = true; } else SubstStr = Name; } } // Parse numeric substitution block. - FileCheckExpression *Expression; + std::unique_ptr<FileCheckExpressionAST> ExpressionAST; Optional<FileCheckNumericVariable *> DefinedNumericVariable; if (IsNumBlock) { - Expected<FileCheckExpression *> ParseResult = - parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, SM); + Expected<std::unique_ptr<FileCheckExpressionAST>> ParseResult = + parseNumericSubstitutionBlock(MatchStr, DefinedNumericVariable, + IsLegacyLineExpr, SM); if (!ParseResult) { logAllUnhandledErrors(ParseResult.takeError(), errs()); return true; } - Expression = *ParseResult; + ExpressionAST = std::move(*ParseResult); if (DefinedNumericVariable) { IsDefinition = true; DefName = (*DefinedNumericVariable)->getName(); @@ -468,8 +520,8 @@ bool FileCheckPattern::parsePattern(StringRef PatternStr, StringRef Prefix, // previous CHECK patterns, and substitution of expressions. FileCheckSubstitution *Substitution = IsNumBlock - ? Context->makeNumericSubstitution(SubstStr, Expression, - SubstInsertIdx) + ? Context->makeNumericSubstitution( + SubstStr, std::move(ExpressionAST), SubstInsertIdx) : Context->makeStringSubstitution(SubstStr, SubstInsertIdx); Substitutions.push_back(Substitution); } @@ -660,7 +712,7 @@ void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer, Expected<std::string> MatchedValue = Substitution->getResult(); // Substitution failed or is not known at match time, print the undefined - // variable it uses. + // variables it uses. if (!MatchedValue) { bool UndefSeen = false; handleAllErrors(MatchedValue.takeError(), @@ -669,13 +721,11 @@ void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer, [](const FileCheckErrorDiagnostic &E) {}, [&](const FileCheckUndefVarError &E) { if (!UndefSeen) { - OS << "uses undefined variable "; + OS << "uses undefined variable(s):"; UndefSeen = true; } + OS << " "; E.log(OS); - }, - [](const ErrorInfoBase &E) { - llvm_unreachable("Unexpected error"); }); } else { // Substitution succeeded. Print substituted value. @@ -768,15 +818,6 @@ FileCheckPatternContext::getPatternVarValue(StringRef VarName) { return VarIter->second; } -FileCheckExpression * -FileCheckPatternContext::makeExpression(binop_eval_t EvalBinop, - FileCheckNumericVariable *OperandLeft, - uint64_t OperandRight) { - Expressions.push_back(llvm::make_unique<FileCheckExpression>( - EvalBinop, OperandLeft, OperandRight)); - return Expressions.back().get(); -} - template <class... Types> FileCheckNumericVariable * FileCheckPatternContext::makeNumericVariable(Types... args) { @@ -794,10 +835,10 @@ FileCheckPatternContext::makeStringSubstitution(StringRef VarName, } FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( - StringRef ExpressionStr, FileCheckExpression *Expression, - size_t InsertIdx) { + StringRef ExpressionStr, + std::unique_ptr<FileCheckExpressionAST> ExpressionAST, size_t InsertIdx) { Substitutions.push_back(llvm::make_unique<FileCheckNumericSubstitution>( - this, ExpressionStr, Expression, InsertIdx)); + this, ExpressionStr, std::move(ExpressionAST), InsertIdx)); return Substitutions.back().get(); } @@ -1777,9 +1818,8 @@ Error FileCheckPatternContext::defineCmdlineVariables( std::pair<StringRef, StringRef> CmdlineNameVal = CmdlineDef.split('='); StringRef CmdlineName = CmdlineNameVal.first; StringRef OrigCmdlineName = CmdlineName; - bool IsPseudo; - Expected<StringRef> ParseVarResult = - FileCheckPattern::parseVariable(CmdlineName, IsPseudo, SM); + Expected<FileCheckPattern::VariableProperties> ParseVarResult = + FileCheckPattern::parseVariable(CmdlineName, SM); if (!ParseVarResult) { Errs = joinErrors(std::move(Errs), ParseVarResult.takeError()); continue; @@ -1787,7 +1827,7 @@ Error FileCheckPatternContext::defineCmdlineVariables( // 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()) { + if (ParseVarResult->IsPseudo || !CmdlineName.empty()) { Errs = joinErrors(std::move(Errs), FileCheckErrorDiagnostic::get( SM, OrigCmdlineName, @@ -1795,7 +1835,7 @@ Error FileCheckPatternContext::defineCmdlineVariables( OrigCmdlineName + "'")); continue; } - StringRef Name = *ParseVarResult; + StringRef Name = ParseVarResult->Name; // Detect collisions between string and numeric variables when the former // is created later than the latter. |