[llvm] 9743123 - [FileCheck] Better diagnostic for format conflict

Thomas Preud'homme via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 15 05:46:48 PDT 2020


Author: Thomas Preud'homme
Date: 2020-04-15T13:46:45+01:00
New Revision: 9743123af817447b527700d3e455b36f1b3f14e3

URL: https://github.com/llvm/llvm-project/commit/9743123af817447b527700d3e455b36f1b3f14e3
DIFF: https://github.com/llvm/llvm-project/commit/9743123af817447b527700d3e455b36f1b3f14e3.diff

LOG: [FileCheck] Better diagnostic for format conflict

Summary:
Improve error message in case of conflict between several implicit
format to mention the operand that conflict.

Reviewers: jhenderson, jdenny, probinson, grimar, arichardson, rnk

Reviewed By: jdenny

Subscribers: hiraditya, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D77741

Added: 
    

Modified: 
    llvm/lib/Support/FileCheck.cpp
    llvm/lib/Support/FileCheckImpl.h
    llvm/test/FileCheck/numeric-expression.txt
    llvm/unittests/Support/FileCheckTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp
index 23e1ece2113e..08c0e8f70c95 100644
--- a/llvm/lib/Support/FileCheck.cpp
+++ b/llvm/lib/Support/FileCheck.cpp
@@ -25,6 +25,20 @@
 
 using namespace llvm;
 
+StringRef ExpressionFormat::toString() const {
+  switch (Value) {
+  case Kind::NoFormat:
+    return StringRef("<none>");
+  case Kind::Unsigned:
+    return StringRef("%u");
+  case Kind::HexUpper:
+    return StringRef("%X");
+  case Kind::HexLower:
+    return StringRef("%x");
+  }
+  llvm_unreachable("unknown expression format");
+}
+
 Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
   switch (Value) {
   case Kind::Unsigned:
@@ -71,7 +85,7 @@ Expected<uint64_t> NumericVariableUse::eval() const {
   if (Value)
     return *Value;
 
-  return make_error<UndefVarError>(Name);
+  return make_error<UndefVarError>(getExpressionStr());
 }
 
 Expected<uint64_t> BinaryOperation::eval() const {
@@ -92,18 +106,31 @@ Expected<uint64_t> BinaryOperation::eval() const {
   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<ExpressionFormat>
+BinaryOperation::getImplicitFormat(const SourceMgr &SM) const {
+  Expected<ExpressionFormat> LeftFormat = LeftOperand->getImplicitFormat(SM);
+  Expected<ExpressionFormat> 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<std::string> NumericSubstitution::getResult() const {
@@ -262,9 +289,12 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
 
   // 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<ExpressionLiteral>(LiteralValue);
+                           LiteralValue)) {
+    return std::make_unique<ExpressionLiteral>(
+        OperandExpr.drop_back(Expr.size()), LiteralValue);
+  }
 
   return ErrorDiagnostic::get(SM, Expr,
                               "invalid operand format '" + Expr + "'");
@@ -279,17 +309,18 @@ static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
 }
 
 Expected<std::unique_ptr<ExpressionAST>>
-Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
+Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
+                    std::unique_ptr<ExpressionAST> LeftOp,
                     bool IsLegacyLineExpr, Optional<size_t> 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 +335,20 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
   }
 
   // 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<std::unique_ptr<ExpressionAST>> 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<BinaryOperation>(EvalBinop, std::move(LeftOp),
+  Expr = Expr.drop_back(RemainingExpr.size());
+  return std::make_unique<BinaryOperation>(Expr, EvalBinop, std::move(LeftOp),
                                            std::move(*RightOpResult));
 }
 
@@ -370,8 +402,9 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
 
   // 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 +412,8 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
     Expected<std::unique_ptr<ExpressionAST>> 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 +429,17 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
   // 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<ExpressionFormat> ImplicitFormat =
+        ExpressionASTPointer->getImplicitFormat(SM);
+    if (!ImplicitFormat)
+      return ImplicitFormat.takeError();
+    Format = *ImplicitFormat;
+  }
+  if (!Format)
     Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
 
   std::unique_ptr<Expression> ExpressionPointer =

diff  --git a/llvm/lib/Support/FileCheckImpl.h b/llvm/lib/Support/FileCheckImpl.h
index 2620e9e39cc7..e65cfeb26959 100644
--- a/llvm/lib/Support/FileCheckImpl.h
+++ b/llvm/lib/Support/FileCheckImpl.h
@@ -39,9 +39,6 @@ struct ExpressionFormat {
     /// 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.
@@ -55,9 +52,7 @@ struct ExpressionFormat {
 
 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.
@@ -73,16 +68,19 @@ struct ExpressionFormat {
 
   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<StringRef> 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<std::string> getMatchingString(uint64_t Value) const;
 
   /// \returns the value corresponding to string representation \p StrVal
@@ -95,17 +93,26 @@ struct ExpressionFormat {
 
 /// 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<uint64_t> 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<ExpressionFormat>
+  getImplicitFormat(const SourceMgr &SM) const {
     return ExpressionFormat();
   }
 };
@@ -117,8 +124,10 @@ class ExpressionLiteral : public ExpressionAST {
   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<uint64_t> eval() const override { return Value; }
@@ -222,21 +231,18 @@ class NumericVariable {
 /// 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<uint64_t> eval() const override;
 
   /// \returns implicit format of this numeric variable.
-  ExpressionFormat getImplicitFormat() const override {
+  Expected<ExpressionFormat>
+  getImplicitFormat(const SourceMgr &SM) const override {
     return Variable->getImplicitFormat();
   }
 };
@@ -257,9 +263,10 @@ class BinaryOperation : public ExpressionAST {
   binop_eval_t EvalBinop;
 
 public:
-  BinaryOperation(binop_eval_t EvalBinop, std::unique_ptr<ExpressionAST> LeftOp,
+  BinaryOperation(StringRef ExpressionStr, binop_eval_t EvalBinop,
+                  std::unique_ptr<ExpressionAST> LeftOp,
                   std::unique_ptr<ExpressionAST> RightOp)
-      : EvalBinop(EvalBinop) {
+      : ExpressionAST(ExpressionStr), EvalBinop(EvalBinop) {
     LeftOperand = std::move(LeftOp);
     RightOperand = std::move(RightOp);
   }
@@ -270,10 +277,12 @@ class BinaryOperation : public ExpressionAST {
   /// variable is used in one of the operands.
   Expected<uint64_t> 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<ExpressionFormat>
+  getImplicitFormat(const SourceMgr &SM) const override;
 };
 
 class FileCheckPatternContext;
@@ -663,17 +672,20 @@ class Pattern {
   parseNumericOperand(StringRef &Expr, AllowedOperand AO,
                       Optional<size_t> 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<std::unique_ptr<ExpressionAST>>
-  parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
-             bool IsLegacyLineExpr, Optional<size_t> LineNumber,
-             FileCheckPatternContext *Context, const SourceMgr &SM);
+  parseBinop(StringRef Expr, StringRef &RemainingExpr,
+             std::unique_ptr<ExpressionAST> LeftOp, bool IsLegacyLineExpr,
+             Optional<size_t> LineNumber, FileCheckPatternContext *Context,
+             const SourceMgr &SM);
 };
 
 //===----------------------------------------------------------------------===//

diff  --git a/llvm/test/FileCheck/numeric-expression.txt b/llvm/test/FileCheck/numeric-expression.txt
index 8396b6e2de11..b81a9da82c63 100644
--- a/llvm/test/FileCheck/numeric-expression.txt
+++ b/llvm/test/FileCheck/numeric-expression.txt
@@ -185,16 +185,27 @@ CHECK-NEXT: [[# %u, VAR3]]
 
 ; Conflicting implicit format.
 RUN: %ProtectFileCheckOutput \
-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: %ProtectFileCheckOutput \
+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
index a451e0beaf90..634172826ab2 100644
--- a/llvm/unittests/Support/FileCheckTest.cpp
+++ b/llvm/unittests/Support/FileCheckTest.cpp
@@ -28,18 +28,50 @@ static StringRef bufferize(SourceMgr &SM, StringRef Str) {
   return StrBufferRef;
 }
 
+static std::string toString(const std::unordered_set<std::string> &Set) {
+  bool First = true;
+  std::string Str;
+  for (StringRef S : Set) {
+    Str += Twine(First ? "{" + S : ", " + S).str();
+    First = false;
+  }
+  Str += '}';
+  return Str;
+}
+
+template <typename ErrorT>
+static void expectSameErrors(std::unordered_set<std::string> ExpectedMsgs,
+                             Error Err) {
+  auto AnyErrorMsgMatch = [&ExpectedMsgs](std::string &&ErrorMsg) -> bool {
+    for (auto ExpectedMsgItr = ExpectedMsgs.begin(),
+              ExpectedMsgEnd = ExpectedMsgs.end();
+         ExpectedMsgItr != ExpectedMsgEnd; ++ExpectedMsgItr) {
+      if (ErrorMsg.find(*ExpectedMsgItr) != std::string::npos) {
+        ExpectedMsgs.erase(ExpectedMsgItr);
+        return true;
+      }
+    }
+    return false;
+  };
+
+  Error RemainingErrors = std::move(Err);
+  do {
+    RemainingErrors =
+        handleErrors(std::move(RemainingErrors), [&](const ErrorT &E) {
+          EXPECT_TRUE(AnyErrorMsgMatch(E.message()))
+              << "Unexpected error message:" << std::endl
+              << E.message();
+        });
+  } while (RemainingErrors && !ExpectedMsgs.empty());
+  EXPECT_THAT_ERROR(std::move(RemainingErrors), Succeeded());
+  EXPECT_TRUE(ExpectedMsgs.empty())
+      << "Error message(s) not found:" << std::endl
+      << toString(ExpectedMsgs);
+}
+
 template <typename ErrorT>
 static void expectError(StringRef ExpectedMsg, Error Err) {
-  bool ErrorHandled = false;
-  EXPECT_THAT_ERROR(handleErrors(std::move(Err),
-                                 [&](const ErrorT &E) {
-                                   EXPECT_NE(
-                                       E.message().find(ExpectedMsg.str()),
-                                       std::string::npos);
-                                   ErrorHandled = true;
-                                 }),
-                    Succeeded());
-  EXPECT_TRUE(ErrorHandled);
+  expectSameErrors<ErrorT>({ExpectedMsg.str()}, std::move(Err));
 }
 
 static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
@@ -179,15 +211,6 @@ TEST_F(FileCheckTest, NoFormatProperties) {
   EXPECT_FALSE(bool(NoFormat));
 }
 
-TEST_F(FileCheckTest, ConflictFormatProperties) {
-  ExpressionFormat ConflictFormat(ExpressionFormat::Kind::Conflict);
-  expectError<StringError>("trying to match value with invalid format",
-                           ConflictFormat.getWildcardRegex().takeError());
-  expectError<StringError>("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 +225,6 @@ TEST_F(FileCheckTest, FormatEqualityOperators) {
   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,43 +233,36 @@ TEST_F(FileCheckTest, FormatKindEqualityOperators) {
   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<uint64_t> Value = Ten.eval();
   ASSERT_THAT_EXPECTED(Value, Succeeded());
   EXPECT_EQ(10U, *Value);
-  EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat);
+  Expected<ExpressionFormat> 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<uint64_t>::max());
+  uint64_t MaxUint64 = std::numeric_limits<uint64_t>::max();
+  ExpressionLiteral Max(bufferize(SM, std::to_string(MaxUint64)), MaxUint64);
   Value = Max.eval();
   ASSERT_THAT_EXPECTED(Value, Succeeded());
   EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *Value);
 }
 
-static std::string toString(const std::unordered_set<std::string> &Set) {
-  bool First = true;
-  std::string Str;
-  for (StringRef S : Set) {
-    Str += Twine(First ? "{" + S : ", " + S).str();
-    First = false;
-  }
-  Str += '}';
-  return Str;
-}
-
 TEST_F(FileCheckTest, Expression) {
+  SourceMgr SM;
+
   std::unique_ptr<ExpressionLiteral> Ten =
-      std::make_unique<ExpressionLiteral>(10);
+      std::make_unique<ExpressionLiteral>(bufferize(SM, "10"), 10);
   ExpressionLiteral *TenPtr = Ten.get();
   Expression Expr(std::move(Ten),
                   ExpressionFormat(ExpressionFormat::Kind::HexLower));
@@ -275,6 +286,8 @@ expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
 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 +295,9 @@ TEST_F(FileCheckTest, NumericVariable) {
   EXPECT_EQ("FOO", FooVar.getName());
   EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
   NumericVariableUse FooVarUse("FOO", &FooVar);
-  EXPECT_EQ(FooVarUse.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
+  Expected<ExpressionFormat> ImplicitFormat = FooVarUse.getImplicitFormat(SM);
+  ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
+  EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);
   EXPECT_FALSE(FooVar.getValue());
   Expected<uint64_t> EvalResult = FooVarUse.eval();
   expectUndefErrors({"FOO"}, EvalResult.takeError());
@@ -306,24 +321,32 @@ TEST_F(FileCheckTest, NumericVariable) {
 }
 
 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<NumericVariableUse> FooVarUse =
-      std::make_unique<NumericVariableUse>("FOO", &FooVar);
-  NumericVariable BarVar("BAR",
+      std::make_unique<NumericVariableUse>(FooStr, &FooVar);
+  StringRef BarStr = ExprStr.take_back(3);
+  NumericVariable BarVar(BarStr,
                          ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
   BarVar.setValue(18);
   std::unique_ptr<NumericVariableUse> BarVarUse =
-      std::make_unique<NumericVariableUse>("BAR", &BarVar);
-  BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
+      std::make_unique<NumericVariableUse>(BarStr, &BarVar);
+  BinaryOperation Binop(ExprStr, doAdd, std::move(FooVarUse),
+                        std::move(BarVarUse));
 
   // Defined variables: eval returns right value; implicit format is as
   // expected.
   Expected<uint64_t> Value = Binop.eval();
   ASSERT_THAT_EXPECTED(Value, Succeeded());
   EXPECT_EQ(60U, *Value);
-  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
+  Expected<ExpressionFormat> 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 +361,76 @@ TEST_F(FileCheckTest, Binop) {
   expectUndefErrors({"FOO", "BAR"}, Value.takeError());
 
   // Literal + Variable has format of variable.
-  FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
+  ExprStr = bufferize(SM, "FOO+18");
+  FooStr = ExprStr.take_front(3);
+  StringRef EighteenStr = ExprStr.take_back(2);
+  FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
   std::unique_ptr<ExpressionLiteral> Eighteen =
-      std::make_unique<ExpressionLiteral>(18);
-  Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen));
-  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
-  FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
-  Eighteen = std::make_unique<ExpressionLiteral>(18);
-  Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse));
-  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
+      std::make_unique<ExpressionLiteral>(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<NumericVariableUse>(FooStr, &FooVar);
+  Eighteen = std::make_unique<ExpressionLiteral>(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 
diff erent 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<NumericVariableUse>("BAZ", &FooVar);
+  FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
   std::unique_ptr<NumericVariableUse> BazVarUse =
-      std::make_unique<NumericVariableUse>("BAZ", &BazVar);
-  Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse));
-  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict);
+      std::make_unique<NumericVariableUse>(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());
+
+  // All variable conflicts are reported.
+  ExprStr = bufferize(SM, "(FOO+BAZ)+(FOO+QUUX)");
+  StringRef Paren1ExprStr = ExprStr.substr(1, 7);
+  FooStr = Paren1ExprStr.take_front(3);
+  BazStr = Paren1ExprStr.take_back(3);
+  StringRef Paren2ExprStr = ExprStr.substr(ExprStr.rfind('(') + 1, 8);
+  StringRef FooStr2 = Paren2ExprStr.take_front(3);
+  StringRef QuuxStr = Paren2ExprStr.take_back(4);
+  FooVarUse = std::make_unique<NumericVariableUse>(FooStr, &FooVar);
+  BazVarUse = std::make_unique<NumericVariableUse>(BazStr, &BazVar);
+  std::unique_ptr<NumericVariableUse> FooVarUse2 =
+      std::make_unique<NumericVariableUse>(FooStr2, &FooVar);
+  NumericVariable QuuxVar(
+      QuuxStr, ExpressionFormat(ExpressionFormat::Kind::HexLower), 4);
+  std::unique_ptr<NumericVariableUse> QuuxVarUse =
+      std::make_unique<NumericVariableUse>(QuuxStr, &QuuxVar);
+  std::unique_ptr<BinaryOperation> Binop1 = std::make_unique<BinaryOperation>(
+      ExprStr.take_front(9), doAdd, std::move(FooVarUse), std::move(BazVarUse));
+  std::unique_ptr<BinaryOperation> Binop2 = std::make_unique<BinaryOperation>(
+      ExprStr.take_back(10), doAdd, std::move(FooVarUse2),
+      std::move(QuuxVarUse));
+  std::unique_ptr<BinaryOperation> OuterBinop =
+      std::make_unique<BinaryOperation>(ExprStr, doAdd, std::move(Binop1),
+                                        std::move(Binop2));
+  ImplicitFormat = OuterBinop->getImplicitFormat(SM);
+  expectSameErrors<ErrorDiagnostic>(
+      {"implicit format conflict between 'FOO' (%u) and 'BAZ' (%x), "
+       "need an explicit format specifier",
+       "implicit format conflict between 'FOO' (%u) and 'QUUX' (%x), "
+       "need an explicit format specifier"},
+      ImplicitFormat.takeError());
 }
 
 TEST_F(FileCheckTest, ValidVarNameStart) {
@@ -648,7 +723,8 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
 
   // 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());
 }
 


        


More information about the llvm-commits mailing list