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 @@ -25,6 +25,21 @@ using namespace llvm; +StringRef ExpressionFormat::toString() const { + switch (Value) { + case Kind::NoFormat: + return StringRef(""); + case Kind::Unsigned: + return StringRef("%u"); + case Kind::HexUpper: + return StringRef("%X"); + case Kind::HexLower: + return StringRef("%x"); + default: + llvm_unreachable("unknown expression format"); + } +} + Expected ExpressionFormat::getWildcardRegex() const { switch (Value) { case Kind::Unsigned: @@ -71,7 +86,7 @@ if (Value) return *Value; - return make_error(Name); + return make_error(getExpressionStr()); } Expected BinaryOperation::eval() const { @@ -92,18 +107,31 @@ return EvalBinop(*LeftOp, *RightOp); } -ExpressionFormat BinaryOperation::getImplicitFormat() const { - ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat(); - ExpressionFormat RightFormat = RightOperand->getImplicitFormat(); - - ExpressionFormat Format = - LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat; - if (LeftFormat != ExpressionFormat::Kind::NoFormat && - RightFormat != ExpressionFormat::Kind::NoFormat && - LeftFormat != RightFormat) - Format = ExpressionFormat(ExpressionFormat::Kind::Conflict); +Expected +BinaryOperation::getImplicitFormat(const SourceMgr &SM) const { + Expected LeftFormat = LeftOperand->getImplicitFormat(SM); + Expected RightFormat = RightOperand->getImplicitFormat(SM); + if (!LeftFormat || !RightFormat) { + Error Err = Error::success(); + if (!LeftFormat) + Err = joinErrors(std::move(Err), LeftFormat.takeError()); + if (!RightFormat) + Err = joinErrors(std::move(Err), RightFormat.takeError()); + return std::move(Err); + } - return Format; + if (*LeftFormat != ExpressionFormat::Kind::NoFormat && + *RightFormat != ExpressionFormat::Kind::NoFormat && + *LeftFormat != *RightFormat) + return ErrorDiagnostic::get( + SM, getExpressionStr(), + "implicit format conflict between '" + LeftOperand->getExpressionStr() + + "' (" + LeftFormat->toString() + ") and '" + + RightOperand->getExpressionStr() + "' (" + RightFormat->toString() + + "), need an explicit format specifier"); + + return *LeftFormat != ExpressionFormat::Kind::NoFormat ? *LeftFormat + : *RightFormat; } Expected NumericSubstitution::getResult() const { @@ -262,9 +290,12 @@ // Otherwise, parse it as a literal. uint64_t LiteralValue; + StringRef OperandExpr = Expr; if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0, - LiteralValue)) - return std::make_unique(LiteralValue); + LiteralValue)) { + return std::make_unique( + OperandExpr.drop_back(Expr.size()), LiteralValue); + } return ErrorDiagnostic::get(SM, Expr, "invalid operand format '" + Expr + "'"); @@ -279,17 +310,18 @@ } Expected> -Pattern::parseBinop(StringRef &Expr, std::unique_ptr LeftOp, +Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr, + std::unique_ptr LeftOp, bool IsLegacyLineExpr, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM) { - Expr = Expr.ltrim(SpaceChars); - if (Expr.empty()) + RemainingExpr = RemainingExpr.ltrim(SpaceChars); + if (RemainingExpr.empty()) return std::move(LeftOp); // Check if this is a supported operation and select a function to perform // it. - SMLoc OpLoc = SMLoc::getFromPointer(Expr.data()); - char Operator = popFront(Expr); + SMLoc OpLoc = SMLoc::getFromPointer(RemainingExpr.data()); + char Operator = popFront(RemainingExpr); binop_eval_t EvalBinop; switch (Operator) { case '+': @@ -304,19 +336,20 @@ } // Parse right operand. - Expr = Expr.ltrim(SpaceChars); - if (Expr.empty()) - return ErrorDiagnostic::get(SM, Expr, "missing operand in expression"); + RemainingExpr = RemainingExpr.ltrim(SpaceChars); + if (RemainingExpr.empty()) + return ErrorDiagnostic::get(SM, RemainingExpr, + "missing operand in expression"); // The second operand in a legacy @LINE expression is always a literal. AllowedOperand AO = IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any; Expected> RightOpResult = - parseNumericOperand(Expr, AO, LineNumber, Context, SM); + parseNumericOperand(RemainingExpr, AO, LineNumber, Context, SM); if (!RightOpResult) return RightOpResult; - Expr = Expr.ltrim(SpaceChars); - return std::make_unique(EvalBinop, std::move(LeftOp), + Expr = Expr.drop_back(RemainingExpr.size()); + return std::make_unique(Expr, EvalBinop, std::move(LeftOp), std::move(*RightOpResult)); } @@ -370,8 +403,9 @@ // Parse the expression itself. Expr = Expr.ltrim(SpaceChars); - StringRef UseExpr = Expr; if (!Expr.empty()) { + Expr = Expr.rtrim(SpaceChars); + StringRef OuterBinOpExpr = Expr; // The first operand in a legacy @LINE expression is always the @LINE // pseudo variable. AllowedOperand AO = @@ -379,8 +413,8 @@ Expected> ParseResult = parseNumericOperand(Expr, AO, LineNumber, Context, SM); while (ParseResult && !Expr.empty()) { - ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr, - LineNumber, Context, SM); + ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult), + IsLegacyLineExpr, LineNumber, Context, SM); // Legacy @LINE expressions only allow 2 operands. if (ParseResult && IsLegacyLineExpr && !Expr.empty()) return ErrorDiagnostic::get( @@ -396,19 +430,17 @@ // otherwise (ii) its implicit format, if any, otherwise (iii) the default // format (unsigned). Error out in case of conflicting implicit format // without explicit format. - ExpressionFormat Format, - ImplicitFormat = ExpressionASTPointer - ? ExpressionASTPointer->getImplicitFormat() - : ExpressionFormat(ExpressionFormat::Kind::NoFormat); - if (bool(ExplicitFormat)) + ExpressionFormat Format; + if (ExplicitFormat) Format = ExplicitFormat; - else if (ImplicitFormat == ExpressionFormat::Kind::Conflict) - return ErrorDiagnostic::get( - SM, UseExpr, - "variables with conflicting format specifier: need an explicit one"); - else if (bool(ImplicitFormat)) - Format = ImplicitFormat; - else + else if (ExpressionASTPointer) { + Expected ImplicitFormat = + ExpressionASTPointer->getImplicitFormat(SM); + if (!ImplicitFormat) + return ImplicitFormat.takeError(); + Format = *ImplicitFormat; + } + if (!Format) Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned); std::unique_ptr ExpressionPointer = diff --git a/llvm/lib/Support/FileCheckImpl.h b/llvm/lib/Support/FileCheckImpl.h --- a/llvm/lib/Support/FileCheckImpl.h +++ b/llvm/lib/Support/FileCheckImpl.h @@ -38,9 +38,6 @@ /// Denote absence of format. Used for implicit format of literals and /// empty expressions. NoFormat, - /// Used when there are several conflicting implicit formats in an - /// expression. - Conflict, /// Value is an unsigned integer and should be printed as a decimal number. Unsigned, /// Value should be printed as an uppercase hex number. @@ -54,9 +51,7 @@ public: /// Evaluates a format to true if it can be used in a match. - explicit operator bool() const { - return Value != Kind::NoFormat && Value != Kind::Conflict; - } + explicit operator bool() const { return Value != Kind::NoFormat; } /// Define format equality: formats are equal if neither is NoFormat and /// their kinds are the same. @@ -72,16 +67,19 @@ bool operator!=(Kind OtherValue) const { return !(*this == OtherValue); } + /// \returns the format specifier corresponding to this format as a string. + StringRef toString() const; + ExpressionFormat() : Value(Kind::NoFormat){}; explicit ExpressionFormat(Kind Value) : Value(Value){}; /// \returns a wildcard regular expression StringRef that matches any value /// in the format represented by this instance, or an error if the format is - /// NoFormat or Conflict. + /// NoFormat. Expected getWildcardRegex() const; /// \returns the string representation of \p Value in the format represented - /// by this instance, or an error if the format is NoFormat or Conflict. + /// by this instance, or an error if the format is NoFormat. Expected getMatchingString(uint64_t Value) const; /// \returns the value corresponding to string representation \p StrVal @@ -94,17 +92,26 @@ /// Base class representing the AST of a given expression. class ExpressionAST { +private: + StringRef ExpressionStr; + public: + ExpressionAST(StringRef ExpressionStr) : ExpressionStr(ExpressionStr) {} + virtual ~ExpressionAST() = default; + StringRef getExpressionStr() const { return ExpressionStr; } + /// Evaluates and \returns the value of the expression represented by this /// AST or an error if evaluation fails. virtual Expected eval() const = 0; - /// \returns either the implicit format of this AST, FormatConflict if - /// implicit formats of the AST's components conflict, or NoFormat if the AST - /// has no implicit format (e.g. AST is made up of a single literal). - virtual ExpressionFormat getImplicitFormat() const { + /// \returns either the implicit format of this AST, a diagnostic against + /// \p SM if implicit formats of the AST's components conflict, or NoFormat + /// if the AST has no implicit format (e.g. AST is made up of a single + /// literal). + virtual Expected + getImplicitFormat(const SourceMgr &SM) const { return ExpressionFormat(); } }; @@ -116,8 +123,10 @@ uint64_t Value; public: - /// Constructs a literal with the specified value. - ExpressionLiteral(uint64_t Val) : Value(Val) {} + /// Constructs a literal with the specified value parsed from + /// \p ExpressionStr. + ExpressionLiteral(StringRef ExpressionStr, uint64_t Val) + : ExpressionAST(ExpressionStr), Value(Val) {} /// \returns the literal's value. Expected eval() const override { return Value; } @@ -221,21 +230,19 @@ /// expression. class NumericVariableUse : public ExpressionAST { private: - /// Name of the numeric variable. - StringRef Name; - /// Pointer to the class instance for the variable this use is about. NumericVariable *Variable; public: NumericVariableUse(StringRef Name, NumericVariable *Variable) - : Name(Name), Variable(Variable) {} + : ExpressionAST(Name), Variable(Variable) {} /// \returns the value of the variable referenced by this instance. Expected eval() const override; /// \returns implicit format of this numeric variable. - ExpressionFormat getImplicitFormat() const override { + Expected + getImplicitFormat(const SourceMgr &SM) const override { return Variable->getImplicitFormat(); } }; @@ -256,9 +263,10 @@ binop_eval_t EvalBinop; public: - BinaryOperation(binop_eval_t EvalBinop, std::unique_ptr LeftOp, + BinaryOperation(StringRef ExpressionStr, binop_eval_t EvalBinop, + std::unique_ptr LeftOp, std::unique_ptr RightOp) - : EvalBinop(EvalBinop) { + : ExpressionAST(ExpressionStr), EvalBinop(EvalBinop) { LeftOperand = std::move(LeftOp); RightOperand = std::move(RightOp); } @@ -269,10 +277,12 @@ /// variable is used in one of the operands. Expected eval() const override; - /// \returns the implicit format of this AST, if any, a format conflict if - /// the implicit formats of the AST's components conflict, or no format if - /// the AST has no implicit format (e.g. AST is made of a single literal). - ExpressionFormat getImplicitFormat() const override; + /// \returns the implicit format of this AST, if any, a diagnostic against + /// \p SM if the implicit formats of the AST's components conflict, or no + /// format if the AST has no implicit format (e.g. AST is made of a single + /// literal). + Expected + getImplicitFormat(const SourceMgr &SM) const override; }; class FileCheckPatternContext; @@ -662,17 +672,20 @@ parseNumericOperand(StringRef &Expr, AllowedOperand AO, Optional LineNumber, FileCheckPatternContext *Context, const SourceMgr &SM); - /// Parses \p Expr for a binary operation at line \p LineNumber, or before - /// input is parsed if \p LineNumber is None. The left operand of this binary - /// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether - /// we are parsing a legacy @LINE expression. Parameter \p Context points to - /// the class instance holding the live string and numeric variables. - /// \returns the class representing the binary operation in the AST of the - /// expression, or an error holding a diagnostic against \p SM otherwise. + /// Parses and updates \p RemainingExpr for a binary operation at line + /// \p LineNumber, or before input is parsed if \p LineNumber is None. The + /// left operand of this binary operation is given in \p LeftOp and \p Expr + /// holds the string for the full expression, including the left operand. + /// Parameter \p IsLegacyLineExpr indicates whether we are parsing a legacy + /// @LINE expression. Parameter \p Context points to the class instance + /// holding the live string and numeric variables. \returns the class + /// representing the binary operation in the AST of the expression, or an + /// error holding a diagnostic against \p SM otherwise. static Expected> - parseBinop(StringRef &Expr, std::unique_ptr LeftOp, - bool IsLegacyLineExpr, Optional LineNumber, - FileCheckPatternContext *Context, const SourceMgr &SM); + parseBinop(StringRef Expr, StringRef &RemainingExpr, + std::unique_ptr LeftOp, bool IsLegacyLineExpr, + Optional LineNumber, FileCheckPatternContext *Context, + const SourceMgr &SM); }; //===----------------------------------------------------------------------===// 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 @@ -182,16 +182,26 @@ CHECK-NEXT: [[# %u, VAR3]] ; Conflicting implicit format. -RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT --input-file %s %s 2>&1 \ -RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT-MSG %s +RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT1 --input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT1-MSG %s +RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT2 --input-file %s %s 2>&1 \ +RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT2-MSG %s VAR USE IMPL FMT CONFLICT 23 -FMT-CONFLICT-LABEL: VAR USE IMPL FMT CONFLICT -FMT-CONFLICT-NEXT: [[#VAR1 + VAR2]] -FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:23: error: variables with conflicting format specifier: need an explicit one -FMT-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}} -FMT-CONFLICT-MSG-NEXT: {{^ \^$}} +FMT-CONFLICT1-LABEL: VAR USE IMPL FMT CONFLICT +FMT-CONFLICT1-NEXT: [[#VAR1 + VAR2]] +FMT-CONFLICT1-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'VAR1' (%u) and 'VAR2' (%x), need an explicit format specifier +FMT-CONFLICT1-MSG-NEXT: {{F}}MT-CONFLICT1-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}} +FMT-CONFLICT1-MSG-NEXT: {{^ \^$}} + +VAR USE IMPL FMT CONFLICT COMPLEX +34 +FMT-CONFLICT2-LABEL: VAR USE IMPL FMT CONFLICT +FMT-CONFLICT2-NEXT: [[#VAR1 + VAR1a + VAR2]] +FMT-CONFLICT2-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'VAR1 + VAR1a' (%u) and 'VAR2' (%x), need an explicit format specifier +FMT-CONFLICT2-MSG-NEXT: {{F}}MT-CONFLICT2-NEXT: {{\[\[#VAR1 \+ VAR1a \+ VAR2\]\]}} +FMT-CONFLICT2-MSG-NEXT: {{^ \^$}} ; Explicitly specified format can override conflicting implicit formats. VAR USE IMPL OVERRIDE FMT CONFLICT 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 @@ -179,15 +179,6 @@ EXPECT_FALSE(bool(NoFormat)); } -TEST_F(FileCheckTest, ConflictFormatProperties) { - ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); - expectError("trying to match value with invalid format", - ConflictFormat.getWildcardRegex().takeError()); - expectError("trying to match value with invalid format", - ConflictFormat.getMatchingString(18).takeError()); - EXPECT_FALSE(bool(ConflictFormat)); -} - TEST_F(FileCheckTest, FormatEqualityOperators) { ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned); ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned); @@ -202,11 +193,6 @@ ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat); EXPECT_FALSE(NoFormat == NoFormat2); EXPECT_TRUE(NoFormat != NoFormat2); - - ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); - ExpressionFormat ConflictFormat2(ExpressionFormat::Kind::Conflict); - EXPECT_TRUE(ConflictFormat == ConflictFormat2); - EXPECT_FALSE(ConflictFormat != ConflictFormat2); } TEST_F(FileCheckTest, FormatKindEqualityOperators) { @@ -215,24 +201,26 @@ EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned); EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower); EXPECT_TRUE(UnsignedFormat != ExpressionFormat::Kind::HexLower); - ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict); - EXPECT_TRUE(ConflictFormat == ExpressionFormat::Kind::Conflict); - EXPECT_FALSE(ConflictFormat != ExpressionFormat::Kind::Conflict); ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat); EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat); EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat); } TEST_F(FileCheckTest, Literal) { + SourceMgr SM; + // Eval returns the literal's value. - ExpressionLiteral Ten(10); + ExpressionLiteral Ten(bufferize(SM, "10"), 10); Expected Value = Ten.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); EXPECT_EQ(10U, *Value); - EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat); + Expected ImplicitFormat = Ten.getImplicitFormat(SM); + ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); + EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::NoFormat); // Max value can be correctly represented. - ExpressionLiteral Max(std::numeric_limits::max()); + uint64_t MaxUint64 = std::numeric_limits::max(); + ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), MaxUint64); Value = Max.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); EXPECT_EQ(std::numeric_limits::max(), *Value); @@ -250,8 +238,10 @@ } TEST_F(FileCheckTest, Expression) { + SourceMgr SM; + std::unique_ptr Ten = - std::make_unique(10); + std::make_unique(bufferize(SM, "10"), 10); ExpressionLiteral *TenPtr = Ten.get(); Expression Expr(std::move(Ten), ExpressionFormat(ExpressionFormat::Kind::HexLower)); @@ -275,6 +265,8 @@ uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; } TEST_F(FileCheckTest, NumericVariable) { + SourceMgr SM; + // Undefined variable: getValue and eval fail, error returned by eval holds // the name of the undefined variable. NumericVariable FooVar("FOO", @@ -282,7 +274,9 @@ EXPECT_EQ("FOO", FooVar.getName()); EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); NumericVariableUse FooVarUse("FOO", &FooVar); - EXPECT_EQ(FooVarUse.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); + Expected ImplicitFormat = FooVarUse.getImplicitFormat(SM); + ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); + EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); EXPECT_FALSE(FooVar.getValue()); Expected EvalResult = FooVarUse.eval(); expectUndefErrors({"FOO"}, EvalResult.takeError()); @@ -306,24 +300,32 @@ } TEST_F(FileCheckTest, Binop) { - NumericVariable FooVar("FOO", + SourceMgr SM; + + StringRef ExprStr = bufferize(SM, "FOO+BAR"); + StringRef FooStr = ExprStr.take_front(3); + NumericVariable FooVar(FooStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1); FooVar.setValue(42); std::unique_ptr FooVarUse = - std::make_unique("FOO", &FooVar); - NumericVariable BarVar("BAR", + std::make_unique(FooStr, &FooVar); + StringRef BarStr = ExprStr.take_back(3); + NumericVariable BarVar(BarStr, ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2); BarVar.setValue(18); std::unique_ptr BarVarUse = - std::make_unique("BAR", &BarVar); - BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse)); + std::make_unique(BarStr, &BarVar); + BinaryOperation Binop(ExprStr, doAdd, std::move(FooVarUse), + std::move(BarVarUse)); // Defined variables: eval returns right value; implicit format is as // expected. Expected Value = Binop.eval(); ASSERT_THAT_EXPECTED(Value, Succeeded()); EXPECT_EQ(60U, *Value); - EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); + Expected ImplicitFormat = Binop.getImplicitFormat(SM); + ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); + EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); // 1 undefined variable: eval fails, error contains name of undefined // variable. @@ -338,24 +340,44 @@ expectUndefErrors({"FOO", "BAR"}, Value.takeError()); // Literal + Variable has format of variable. - FooVarUse = std::make_unique("FOO", &FooVar); + ExprStr = bufferize(SM, "FOO+18"); + FooStr = ExprStr.take_front(3); + StringRef EighteenStr = ExprStr.take_back(2); + FooVarUse = std::make_unique(FooStr, &FooVar); std::unique_ptr Eighteen = - std::make_unique(18); - Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen)); - EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); - FooVarUse = std::make_unique("FOO", &FooVar); - Eighteen = std::make_unique(18); - Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse)); - EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned); + std::make_unique(EighteenStr, 18); + Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse), + std::move(Eighteen)); + ImplicitFormat = Binop.getImplicitFormat(SM); + ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); + EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); + ExprStr = bufferize(SM, "18+FOO"); + FooStr = ExprStr.take_back(3); + EighteenStr = ExprStr.take_front(2); + FooVarUse = std::make_unique(FooStr, &FooVar); + Eighteen = std::make_unique(EighteenStr, 18); + Binop = BinaryOperation(ExprStr, doAdd, std::move(Eighteen), + std::move(FooVarUse)); + ImplicitFormat = Binop.getImplicitFormat(SM); + ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded()); + EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned); // Variables with different implicit format conflict. - NumericVariable BazVar("BAZ", + ExprStr = bufferize(SM, "FOO+BAZ"); + FooStr = ExprStr.take_front(3); + StringRef BazStr = ExprStr.take_back(3); + NumericVariable BazVar(BazStr, ExpressionFormat(ExpressionFormat::Kind::HexLower), 3); - FooVarUse = std::make_unique("BAZ", &FooVar); + FooVarUse = std::make_unique(FooStr, &FooVar); std::unique_ptr BazVarUse = - std::make_unique("BAZ", &BazVar); - Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse)); - EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict); + std::make_unique(BazStr, &BazVar); + Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse), + std::move(BazVarUse)); + ImplicitFormat = Binop.getImplicitFormat(SM); + expectDiagnosticError( + "implicit format conflict between 'FOO' (%u) and 'BAZ' (%x), " + "need an explicit format specifier", + ImplicitFormat.takeError()); } TEST_F(FileCheckTest, ValidVarNameStart) { @@ -648,7 +670,8 @@ // Implicit format conflict. expectDiagnosticError( - "variables with conflicting format specifier: need an explicit one", + "implicit format conflict between 'FOO' (%u) and " + "'VAR_LOWER_HEX' (%x), need an explicit format specifier", Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError()); }