Skip to content

Commit 31c81d7

Browse files
Thomas Preud'hommememfrob
authored andcommitted
[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
1 parent 38349eb commit 31c81d7

File tree

4 files changed

+276
-146
lines changed

4 files changed

+276
-146
lines changed

llvm/lib/Support/FileCheck.cpp

Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@
2525

2626
using namespace llvm;
2727

28+
StringRef ExpressionFormat::toString() const {
29+
switch (Value) {
30+
case Kind::NoFormat:
31+
return StringRef("<none>");
32+
case Kind::Unsigned:
33+
return StringRef("%u");
34+
case Kind::HexUpper:
35+
return StringRef("%X");
36+
case Kind::HexLower:
37+
return StringRef("%x");
38+
}
39+
llvm_unreachable("unknown expression format");
40+
}
41+
2842
Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
2943
switch (Value) {
3044
case Kind::Unsigned:
@@ -71,7 +85,7 @@ Expected<uint64_t> NumericVariableUse::eval() const {
7185
if (Value)
7286
return *Value;
7387

74-
return make_error<UndefVarError>(Name);
88+
return make_error<UndefVarError>(getExpressionStr());
7589
}
7690

7791
Expected<uint64_t> BinaryOperation::eval() const {
@@ -92,18 +106,31 @@ Expected<uint64_t> BinaryOperation::eval() const {
92106
return EvalBinop(*LeftOp, *RightOp);
93107
}
94108

95-
ExpressionFormat BinaryOperation::getImplicitFormat() const {
96-
ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat();
97-
ExpressionFormat RightFormat = RightOperand->getImplicitFormat();
98-
99-
ExpressionFormat Format =
100-
LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat;
101-
if (LeftFormat != ExpressionFormat::Kind::NoFormat &&
102-
RightFormat != ExpressionFormat::Kind::NoFormat &&
103-
LeftFormat != RightFormat)
104-
Format = ExpressionFormat(ExpressionFormat::Kind::Conflict);
109+
Expected<ExpressionFormat>
110+
BinaryOperation::getImplicitFormat(const SourceMgr &SM) const {
111+
Expected<ExpressionFormat> LeftFormat = LeftOperand->getImplicitFormat(SM);
112+
Expected<ExpressionFormat> RightFormat = RightOperand->getImplicitFormat(SM);
113+
if (!LeftFormat || !RightFormat) {
114+
Error Err = Error::success();
115+
if (!LeftFormat)
116+
Err = joinErrors(std::move(Err), LeftFormat.takeError());
117+
if (!RightFormat)
118+
Err = joinErrors(std::move(Err), RightFormat.takeError());
119+
return std::move(Err);
120+
}
105121

106-
return Format;
122+
if (*LeftFormat != ExpressionFormat::Kind::NoFormat &&
123+
*RightFormat != ExpressionFormat::Kind::NoFormat &&
124+
*LeftFormat != *RightFormat)
125+
return ErrorDiagnostic::get(
126+
SM, getExpressionStr(),
127+
"implicit format conflict between '" + LeftOperand->getExpressionStr() +
128+
"' (" + LeftFormat->toString() + ") and '" +
129+
RightOperand->getExpressionStr() + "' (" + RightFormat->toString() +
130+
"), need an explicit format specifier");
131+
132+
return *LeftFormat != ExpressionFormat::Kind::NoFormat ? *LeftFormat
133+
: *RightFormat;
107134
}
108135

109136
Expected<std::string> NumericSubstitution::getResult() const {
@@ -262,9 +289,12 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
262289

263290
// Otherwise, parse it as a literal.
264291
uint64_t LiteralValue;
292+
StringRef OperandExpr = Expr;
265293
if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
266-
LiteralValue))
267-
return std::make_unique<ExpressionLiteral>(LiteralValue);
294+
LiteralValue)) {
295+
return std::make_unique<ExpressionLiteral>(
296+
OperandExpr.drop_back(Expr.size()), LiteralValue);
297+
}
268298

269299
return ErrorDiagnostic::get(SM, Expr,
270300
"invalid operand format '" + Expr + "'");
@@ -279,17 +309,18 @@ static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
279309
}
280310

281311
Expected<std::unique_ptr<ExpressionAST>>
282-
Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
312+
Pattern::parseBinop(StringRef Expr, StringRef &RemainingExpr,
313+
std::unique_ptr<ExpressionAST> LeftOp,
283314
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
284315
FileCheckPatternContext *Context, const SourceMgr &SM) {
285-
Expr = Expr.ltrim(SpaceChars);
286-
if (Expr.empty())
316+
RemainingExpr = RemainingExpr.ltrim(SpaceChars);
317+
if (RemainingExpr.empty())
287318
return std::move(LeftOp);
288319

289320
// Check if this is a supported operation and select a function to perform
290321
// it.
291-
SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
292-
char Operator = popFront(Expr);
322+
SMLoc OpLoc = SMLoc::getFromPointer(RemainingExpr.data());
323+
char Operator = popFront(RemainingExpr);
293324
binop_eval_t EvalBinop;
294325
switch (Operator) {
295326
case '+':
@@ -304,19 +335,20 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
304335
}
305336

306337
// Parse right operand.
307-
Expr = Expr.ltrim(SpaceChars);
308-
if (Expr.empty())
309-
return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
338+
RemainingExpr = RemainingExpr.ltrim(SpaceChars);
339+
if (RemainingExpr.empty())
340+
return ErrorDiagnostic::get(SM, RemainingExpr,
341+
"missing operand in expression");
310342
// The second operand in a legacy @LINE expression is always a literal.
311343
AllowedOperand AO =
312344
IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
313345
Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
314-
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
346+
parseNumericOperand(RemainingExpr, AO, LineNumber, Context, SM);
315347
if (!RightOpResult)
316348
return RightOpResult;
317349

318-
Expr = Expr.ltrim(SpaceChars);
319-
return std::make_unique<BinaryOperation>(EvalBinop, std::move(LeftOp),
350+
Expr = Expr.drop_back(RemainingExpr.size());
351+
return std::make_unique<BinaryOperation>(Expr, EvalBinop, std::move(LeftOp),
320352
std::move(*RightOpResult));
321353
}
322354

@@ -370,17 +402,18 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
370402

371403
// Parse the expression itself.
372404
Expr = Expr.ltrim(SpaceChars);
373-
StringRef UseExpr = Expr;
374405
if (!Expr.empty()) {
406+
Expr = Expr.rtrim(SpaceChars);
407+
StringRef OuterBinOpExpr = Expr;
375408
// The first operand in a legacy @LINE expression is always the @LINE
376409
// pseudo variable.
377410
AllowedOperand AO =
378411
IsLegacyLineExpr ? AllowedOperand::LineVar : AllowedOperand::Any;
379412
Expected<std::unique_ptr<ExpressionAST>> ParseResult =
380413
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
381414
while (ParseResult && !Expr.empty()) {
382-
ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr,
383-
LineNumber, Context, SM);
415+
ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult),
416+
IsLegacyLineExpr, LineNumber, Context, SM);
384417
// Legacy @LINE expressions only allow 2 operands.
385418
if (ParseResult && IsLegacyLineExpr && !Expr.empty())
386419
return ErrorDiagnostic::get(
@@ -396,19 +429,17 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
396429
// otherwise (ii) its implicit format, if any, otherwise (iii) the default
397430
// format (unsigned). Error out in case of conflicting implicit format
398431
// without explicit format.
399-
ExpressionFormat Format,
400-
ImplicitFormat = ExpressionASTPointer
401-
? ExpressionASTPointer->getImplicitFormat()
402-
: ExpressionFormat(ExpressionFormat::Kind::NoFormat);
403-
if (bool(ExplicitFormat))
432+
ExpressionFormat Format;
433+
if (ExplicitFormat)
404434
Format = ExplicitFormat;
405-
else if (ImplicitFormat == ExpressionFormat::Kind::Conflict)
406-
return ErrorDiagnostic::get(
407-
SM, UseExpr,
408-
"variables with conflicting format specifier: need an explicit one");
409-
else if (bool(ImplicitFormat))
410-
Format = ImplicitFormat;
411-
else
435+
else if (ExpressionASTPointer) {
436+
Expected<ExpressionFormat> ImplicitFormat =
437+
ExpressionASTPointer->getImplicitFormat(SM);
438+
if (!ImplicitFormat)
439+
return ImplicitFormat.takeError();
440+
Format = *ImplicitFormat;
441+
}
442+
if (!Format)
412443
Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
413444

414445
std::unique_ptr<Expression> ExpressionPointer =

llvm/lib/Support/FileCheckImpl.h

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ struct ExpressionFormat {
3939
/// Denote absence of format. Used for implicit format of literals and
4040
/// empty expressions.
4141
NoFormat,
42-
/// Used when there are several conflicting implicit formats in an
43-
/// expression.
44-
Conflict,
4542
/// Value is an unsigned integer and should be printed as a decimal number.
4643
Unsigned,
4744
/// Value should be printed as an uppercase hex number.
@@ -55,9 +52,7 @@ struct ExpressionFormat {
5552

5653
public:
5754
/// Evaluates a format to true if it can be used in a match.
58-
explicit operator bool() const {
59-
return Value != Kind::NoFormat && Value != Kind::Conflict;
60-
}
55+
explicit operator bool() const { return Value != Kind::NoFormat; }
6156

6257
/// Define format equality: formats are equal if neither is NoFormat and
6358
/// their kinds are the same.
@@ -73,16 +68,19 @@ struct ExpressionFormat {
7368

7469
bool operator!=(Kind OtherValue) const { return !(*this == OtherValue); }
7570

71+
/// \returns the format specifier corresponding to this format as a string.
72+
StringRef toString() const;
73+
7674
ExpressionFormat() : Value(Kind::NoFormat){};
7775
explicit ExpressionFormat(Kind Value) : Value(Value){};
7876

7977
/// \returns a wildcard regular expression StringRef that matches any value
8078
/// in the format represented by this instance, or an error if the format is
81-
/// NoFormat or Conflict.
79+
/// NoFormat.
8280
Expected<StringRef> getWildcardRegex() const;
8381

8482
/// \returns the string representation of \p Value in the format represented
85-
/// by this instance, or an error if the format is NoFormat or Conflict.
83+
/// by this instance, or an error if the format is NoFormat.
8684
Expected<std::string> getMatchingString(uint64_t Value) const;
8785

8886
/// \returns the value corresponding to string representation \p StrVal
@@ -95,17 +93,26 @@ struct ExpressionFormat {
9593

9694
/// Base class representing the AST of a given expression.
9795
class ExpressionAST {
96+
private:
97+
StringRef ExpressionStr;
98+
9899
public:
100+
ExpressionAST(StringRef ExpressionStr) : ExpressionStr(ExpressionStr) {}
101+
99102
virtual ~ExpressionAST() = default;
100103

104+
StringRef getExpressionStr() const { return ExpressionStr; }
105+
101106
/// Evaluates and \returns the value of the expression represented by this
102107
/// AST or an error if evaluation fails.
103108
virtual Expected<uint64_t> eval() const = 0;
104109

105-
/// \returns either the implicit format of this AST, FormatConflict if
106-
/// implicit formats of the AST's components conflict, or NoFormat if the AST
107-
/// has no implicit format (e.g. AST is made up of a single literal).
108-
virtual ExpressionFormat getImplicitFormat() const {
110+
/// \returns either the implicit format of this AST, a diagnostic against
111+
/// \p SM if implicit formats of the AST's components conflict, or NoFormat
112+
/// if the AST has no implicit format (e.g. AST is made up of a single
113+
/// literal).
114+
virtual Expected<ExpressionFormat>
115+
getImplicitFormat(const SourceMgr &SM) const {
109116
return ExpressionFormat();
110117
}
111118
};
@@ -117,8 +124,10 @@ class ExpressionLiteral : public ExpressionAST {
117124
uint64_t Value;
118125

119126
public:
120-
/// Constructs a literal with the specified value.
121-
ExpressionLiteral(uint64_t Val) : Value(Val) {}
127+
/// Constructs a literal with the specified value parsed from
128+
/// \p ExpressionStr.
129+
ExpressionLiteral(StringRef ExpressionStr, uint64_t Val)
130+
: ExpressionAST(ExpressionStr), Value(Val) {}
122131

123132
/// \returns the literal's value.
124133
Expected<uint64_t> eval() const override { return Value; }
@@ -222,21 +231,18 @@ class NumericVariable {
222231
/// expression.
223232
class NumericVariableUse : public ExpressionAST {
224233
private:
225-
/// Name of the numeric variable.
226-
StringRef Name;
227-
228234
/// Pointer to the class instance for the variable this use is about.
229235
NumericVariable *Variable;
230236

231237
public:
232238
NumericVariableUse(StringRef Name, NumericVariable *Variable)
233-
: Name(Name), Variable(Variable) {}
234-
239+
: ExpressionAST(Name), Variable(Variable) {}
235240
/// \returns the value of the variable referenced by this instance.
236241
Expected<uint64_t> eval() const override;
237242

238243
/// \returns implicit format of this numeric variable.
239-
ExpressionFormat getImplicitFormat() const override {
244+
Expected<ExpressionFormat>
245+
getImplicitFormat(const SourceMgr &SM) const override {
240246
return Variable->getImplicitFormat();
241247
}
242248
};
@@ -257,9 +263,10 @@ class BinaryOperation : public ExpressionAST {
257263
binop_eval_t EvalBinop;
258264

259265
public:
260-
BinaryOperation(binop_eval_t EvalBinop, std::unique_ptr<ExpressionAST> LeftOp,
266+
BinaryOperation(StringRef ExpressionStr, binop_eval_t EvalBinop,
267+
std::unique_ptr<ExpressionAST> LeftOp,
261268
std::unique_ptr<ExpressionAST> RightOp)
262-
: EvalBinop(EvalBinop) {
269+
: ExpressionAST(ExpressionStr), EvalBinop(EvalBinop) {
263270
LeftOperand = std::move(LeftOp);
264271
RightOperand = std::move(RightOp);
265272
}
@@ -270,10 +277,12 @@ class BinaryOperation : public ExpressionAST {
270277
/// variable is used in one of the operands.
271278
Expected<uint64_t> eval() const override;
272279

273-
/// \returns the implicit format of this AST, if any, a format conflict if
274-
/// the implicit formats of the AST's components conflict, or no format if
275-
/// the AST has no implicit format (e.g. AST is made of a single literal).
276-
ExpressionFormat getImplicitFormat() const override;
280+
/// \returns the implicit format of this AST, if any, a diagnostic against
281+
/// \p SM if the implicit formats of the AST's components conflict, or no
282+
/// format if the AST has no implicit format (e.g. AST is made of a single
283+
/// literal).
284+
Expected<ExpressionFormat>
285+
getImplicitFormat(const SourceMgr &SM) const override;
277286
};
278287

279288
class FileCheckPatternContext;
@@ -663,17 +672,20 @@ class Pattern {
663672
parseNumericOperand(StringRef &Expr, AllowedOperand AO,
664673
Optional<size_t> LineNumber,
665674
FileCheckPatternContext *Context, const SourceMgr &SM);
666-
/// Parses \p Expr for a binary operation at line \p LineNumber, or before
667-
/// input is parsed if \p LineNumber is None. The left operand of this binary
668-
/// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether
669-
/// we are parsing a legacy @LINE expression. Parameter \p Context points to
670-
/// the class instance holding the live string and numeric variables.
671-
/// \returns the class representing the binary operation in the AST of the
672-
/// expression, or an error holding a diagnostic against \p SM otherwise.
675+
/// Parses and updates \p RemainingExpr for a binary operation at line
676+
/// \p LineNumber, or before input is parsed if \p LineNumber is None. The
677+
/// left operand of this binary operation is given in \p LeftOp and \p Expr
678+
/// holds the string for the full expression, including the left operand.
679+
/// Parameter \p IsLegacyLineExpr indicates whether we are parsing a legacy
680+
/// @LINE expression. Parameter \p Context points to the class instance
681+
/// holding the live string and numeric variables. \returns the class
682+
/// representing the binary operation in the AST of the expression, or an
683+
/// error holding a diagnostic against \p SM otherwise.
673684
static Expected<std::unique_ptr<ExpressionAST>>
674-
parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
675-
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
676-
FileCheckPatternContext *Context, const SourceMgr &SM);
685+
parseBinop(StringRef Expr, StringRef &RemainingExpr,
686+
std::unique_ptr<ExpressionAST> LeftOp, bool IsLegacyLineExpr,
687+
Optional<size_t> LineNumber, FileCheckPatternContext *Context,
688+
const SourceMgr &SM);
677689
};
678690

679691
//===----------------------------------------------------------------------===//

llvm/test/FileCheck/numeric-expression.txt

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -185,16 +185,27 @@ CHECK-NEXT: [[# %u, VAR3]]
185185

186186
; Conflicting implicit format.
187187
RUN: %ProtectFileCheckOutput \
188-
RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT --input-file %s %s 2>&1 \
189-
RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT-MSG %s
188+
RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT1 --input-file %s %s 2>&1 \
189+
RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT1-MSG %s
190+
RUN: %ProtectFileCheckOutput \
191+
RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT2 --input-file %s %s 2>&1 \
192+
RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT2-MSG %s
190193

191194
VAR USE IMPL FMT CONFLICT
192195
23
193-
FMT-CONFLICT-LABEL: VAR USE IMPL FMT CONFLICT
194-
FMT-CONFLICT-NEXT: [[#VAR1 + VAR2]]
195-
FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:23: error: variables with conflicting format specifier: need an explicit one
196-
FMT-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}}
197-
FMT-CONFLICT-MSG-NEXT: {{^ \^$}}
196+
FMT-CONFLICT1-LABEL: VAR USE IMPL FMT CONFLICT
197+
FMT-CONFLICT1-NEXT: [[#VAR1 + VAR2]]
198+
FMT-CONFLICT1-MSG: numeric-expression.txt:[[#@LINE-1]]:24: error: implicit format conflict between 'VAR1' (%u) and 'VAR2' (%x), need an explicit format specifier
199+
FMT-CONFLICT1-MSG-NEXT: {{F}}MT-CONFLICT1-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}}
200+
FMT-CONFLICT1-MSG-NEXT: {{^ \^$}}
201+
202+
VAR USE IMPL FMT CONFLICT COMPLEX
203+
34
204+
FMT-CONFLICT2-LABEL: VAR USE IMPL FMT CONFLICT
205+
FMT-CONFLICT2-NEXT: [[#VAR1 + VAR1a + VAR2]]
206+
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
207+
FMT-CONFLICT2-MSG-NEXT: {{F}}MT-CONFLICT2-NEXT: {{\[\[#VAR1 \+ VAR1a \+ VAR2\]\]}}
208+
FMT-CONFLICT2-MSG-NEXT: {{^ \^$}}
198209

199210
; Explicitly specified format can override conflicting implicit formats.
200211
VAR USE IMPL OVERRIDE FMT CONFLICT

0 commit comments

Comments
 (0)