forked from OSchip/llvm-project
[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
This commit is contained in:
parent
2a0a26bd98
commit
9743123af8
|
@ -25,6 +25,20 @@
|
||||||
|
|
||||||
using namespace llvm;
|
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 {
|
Expected<StringRef> ExpressionFormat::getWildcardRegex() const {
|
||||||
switch (Value) {
|
switch (Value) {
|
||||||
case Kind::Unsigned:
|
case Kind::Unsigned:
|
||||||
|
@ -71,7 +85,7 @@ Expected<uint64_t> NumericVariableUse::eval() const {
|
||||||
if (Value)
|
if (Value)
|
||||||
return *Value;
|
return *Value;
|
||||||
|
|
||||||
return make_error<UndefVarError>(Name);
|
return make_error<UndefVarError>(getExpressionStr());
|
||||||
}
|
}
|
||||||
|
|
||||||
Expected<uint64_t> BinaryOperation::eval() const {
|
Expected<uint64_t> BinaryOperation::eval() const {
|
||||||
|
@ -92,18 +106,31 @@ Expected<uint64_t> BinaryOperation::eval() const {
|
||||||
return EvalBinop(*LeftOp, *RightOp);
|
return EvalBinop(*LeftOp, *RightOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExpressionFormat BinaryOperation::getImplicitFormat() const {
|
Expected<ExpressionFormat>
|
||||||
ExpressionFormat LeftFormat = LeftOperand->getImplicitFormat();
|
BinaryOperation::getImplicitFormat(const SourceMgr &SM) const {
|
||||||
ExpressionFormat RightFormat = RightOperand->getImplicitFormat();
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
ExpressionFormat Format =
|
if (*LeftFormat != ExpressionFormat::Kind::NoFormat &&
|
||||||
LeftFormat != ExpressionFormat::Kind::NoFormat ? LeftFormat : RightFormat;
|
*RightFormat != ExpressionFormat::Kind::NoFormat &&
|
||||||
if (LeftFormat != ExpressionFormat::Kind::NoFormat &&
|
*LeftFormat != *RightFormat)
|
||||||
RightFormat != ExpressionFormat::Kind::NoFormat &&
|
return ErrorDiagnostic::get(
|
||||||
LeftFormat != RightFormat)
|
SM, getExpressionStr(),
|
||||||
Format = ExpressionFormat(ExpressionFormat::Kind::Conflict);
|
"implicit format conflict between '" + LeftOperand->getExpressionStr() +
|
||||||
|
"' (" + LeftFormat->toString() + ") and '" +
|
||||||
|
RightOperand->getExpressionStr() + "' (" + RightFormat->toString() +
|
||||||
|
"), need an explicit format specifier");
|
||||||
|
|
||||||
return Format;
|
return *LeftFormat != ExpressionFormat::Kind::NoFormat ? *LeftFormat
|
||||||
|
: *RightFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expected<std::string> NumericSubstitution::getResult() const {
|
Expected<std::string> NumericSubstitution::getResult() const {
|
||||||
|
@ -262,9 +289,12 @@ Expected<std::unique_ptr<ExpressionAST>> Pattern::parseNumericOperand(
|
||||||
|
|
||||||
// Otherwise, parse it as a literal.
|
// Otherwise, parse it as a literal.
|
||||||
uint64_t LiteralValue;
|
uint64_t LiteralValue;
|
||||||
|
StringRef OperandExpr = Expr;
|
||||||
if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
|
if (!Expr.consumeInteger((AO == AllowedOperand::LegacyLiteral) ? 10 : 0,
|
||||||
LiteralValue))
|
LiteralValue)) {
|
||||||
return std::make_unique<ExpressionLiteral>(LiteralValue);
|
return std::make_unique<ExpressionLiteral>(
|
||||||
|
OperandExpr.drop_back(Expr.size()), LiteralValue);
|
||||||
|
}
|
||||||
|
|
||||||
return ErrorDiagnostic::get(SM, Expr,
|
return ErrorDiagnostic::get(SM, Expr,
|
||||||
"invalid operand format '" + Expr + "'");
|
"invalid operand format '" + Expr + "'");
|
||||||
|
@ -279,17 +309,18 @@ static uint64_t sub(uint64_t LeftOp, uint64_t RightOp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Expected<std::unique_ptr<ExpressionAST>>
|
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,
|
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
|
||||||
FileCheckPatternContext *Context, const SourceMgr &SM) {
|
FileCheckPatternContext *Context, const SourceMgr &SM) {
|
||||||
Expr = Expr.ltrim(SpaceChars);
|
RemainingExpr = RemainingExpr.ltrim(SpaceChars);
|
||||||
if (Expr.empty())
|
if (RemainingExpr.empty())
|
||||||
return std::move(LeftOp);
|
return std::move(LeftOp);
|
||||||
|
|
||||||
// Check if this is a supported operation and select a function to perform
|
// Check if this is a supported operation and select a function to perform
|
||||||
// it.
|
// it.
|
||||||
SMLoc OpLoc = SMLoc::getFromPointer(Expr.data());
|
SMLoc OpLoc = SMLoc::getFromPointer(RemainingExpr.data());
|
||||||
char Operator = popFront(Expr);
|
char Operator = popFront(RemainingExpr);
|
||||||
binop_eval_t EvalBinop;
|
binop_eval_t EvalBinop;
|
||||||
switch (Operator) {
|
switch (Operator) {
|
||||||
case '+':
|
case '+':
|
||||||
|
@ -304,19 +335,20 @@ Pattern::parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse right operand.
|
// Parse right operand.
|
||||||
Expr = Expr.ltrim(SpaceChars);
|
RemainingExpr = RemainingExpr.ltrim(SpaceChars);
|
||||||
if (Expr.empty())
|
if (RemainingExpr.empty())
|
||||||
return ErrorDiagnostic::get(SM, Expr, "missing operand in expression");
|
return ErrorDiagnostic::get(SM, RemainingExpr,
|
||||||
|
"missing operand in expression");
|
||||||
// The second operand in a legacy @LINE expression is always a literal.
|
// The second operand in a legacy @LINE expression is always a literal.
|
||||||
AllowedOperand AO =
|
AllowedOperand AO =
|
||||||
IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
|
IsLegacyLineExpr ? AllowedOperand::LegacyLiteral : AllowedOperand::Any;
|
||||||
Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
|
Expected<std::unique_ptr<ExpressionAST>> RightOpResult =
|
||||||
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
|
parseNumericOperand(RemainingExpr, AO, LineNumber, Context, SM);
|
||||||
if (!RightOpResult)
|
if (!RightOpResult)
|
||||||
return RightOpResult;
|
return RightOpResult;
|
||||||
|
|
||||||
Expr = Expr.ltrim(SpaceChars);
|
Expr = Expr.drop_back(RemainingExpr.size());
|
||||||
return std::make_unique<BinaryOperation>(EvalBinop, std::move(LeftOp),
|
return std::make_unique<BinaryOperation>(Expr, EvalBinop, std::move(LeftOp),
|
||||||
std::move(*RightOpResult));
|
std::move(*RightOpResult));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,8 +402,9 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
|
||||||
|
|
||||||
// Parse the expression itself.
|
// Parse the expression itself.
|
||||||
Expr = Expr.ltrim(SpaceChars);
|
Expr = Expr.ltrim(SpaceChars);
|
||||||
StringRef UseExpr = Expr;
|
|
||||||
if (!Expr.empty()) {
|
if (!Expr.empty()) {
|
||||||
|
Expr = Expr.rtrim(SpaceChars);
|
||||||
|
StringRef OuterBinOpExpr = Expr;
|
||||||
// The first operand in a legacy @LINE expression is always the @LINE
|
// The first operand in a legacy @LINE expression is always the @LINE
|
||||||
// pseudo variable.
|
// pseudo variable.
|
||||||
AllowedOperand AO =
|
AllowedOperand AO =
|
||||||
|
@ -379,8 +412,8 @@ Expected<std::unique_ptr<Expression>> Pattern::parseNumericSubstitutionBlock(
|
||||||
Expected<std::unique_ptr<ExpressionAST>> ParseResult =
|
Expected<std::unique_ptr<ExpressionAST>> ParseResult =
|
||||||
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
|
parseNumericOperand(Expr, AO, LineNumber, Context, SM);
|
||||||
while (ParseResult && !Expr.empty()) {
|
while (ParseResult && !Expr.empty()) {
|
||||||
ParseResult = parseBinop(Expr, std::move(*ParseResult), IsLegacyLineExpr,
|
ParseResult = parseBinop(OuterBinOpExpr, Expr, std::move(*ParseResult),
|
||||||
LineNumber, Context, SM);
|
IsLegacyLineExpr, LineNumber, Context, SM);
|
||||||
// Legacy @LINE expressions only allow 2 operands.
|
// Legacy @LINE expressions only allow 2 operands.
|
||||||
if (ParseResult && IsLegacyLineExpr && !Expr.empty())
|
if (ParseResult && IsLegacyLineExpr && !Expr.empty())
|
||||||
return ErrorDiagnostic::get(
|
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
|
// otherwise (ii) its implicit format, if any, otherwise (iii) the default
|
||||||
// format (unsigned). Error out in case of conflicting implicit format
|
// format (unsigned). Error out in case of conflicting implicit format
|
||||||
// without explicit format.
|
// without explicit format.
|
||||||
ExpressionFormat Format,
|
ExpressionFormat Format;
|
||||||
ImplicitFormat = ExpressionASTPointer
|
if (ExplicitFormat)
|
||||||
? ExpressionASTPointer->getImplicitFormat()
|
|
||||||
: ExpressionFormat(ExpressionFormat::Kind::NoFormat);
|
|
||||||
if (bool(ExplicitFormat))
|
|
||||||
Format = ExplicitFormat;
|
Format = ExplicitFormat;
|
||||||
else if (ImplicitFormat == ExpressionFormat::Kind::Conflict)
|
else if (ExpressionASTPointer) {
|
||||||
return ErrorDiagnostic::get(
|
Expected<ExpressionFormat> ImplicitFormat =
|
||||||
SM, UseExpr,
|
ExpressionASTPointer->getImplicitFormat(SM);
|
||||||
"variables with conflicting format specifier: need an explicit one");
|
if (!ImplicitFormat)
|
||||||
else if (bool(ImplicitFormat))
|
return ImplicitFormat.takeError();
|
||||||
Format = ImplicitFormat;
|
Format = *ImplicitFormat;
|
||||||
else
|
}
|
||||||
|
if (!Format)
|
||||||
Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
|
Format = ExpressionFormat(ExpressionFormat::Kind::Unsigned);
|
||||||
|
|
||||||
std::unique_ptr<Expression> ExpressionPointer =
|
std::unique_ptr<Expression> ExpressionPointer =
|
||||||
|
|
|
@ -39,9 +39,6 @@ struct ExpressionFormat {
|
||||||
/// Denote absence of format. Used for implicit format of literals and
|
/// Denote absence of format. Used for implicit format of literals and
|
||||||
/// empty expressions.
|
/// empty expressions.
|
||||||
NoFormat,
|
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.
|
/// Value is an unsigned integer and should be printed as a decimal number.
|
||||||
Unsigned,
|
Unsigned,
|
||||||
/// Value should be printed as an uppercase hex number.
|
/// Value should be printed as an uppercase hex number.
|
||||||
|
@ -55,9 +52,7 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Evaluates a format to true if it can be used in a match.
|
/// Evaluates a format to true if it can be used in a match.
|
||||||
explicit operator bool() const {
|
explicit operator bool() const { return Value != Kind::NoFormat; }
|
||||||
return Value != Kind::NoFormat && Value != Kind::Conflict;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Define format equality: formats are equal if neither is NoFormat and
|
/// Define format equality: formats are equal if neither is NoFormat and
|
||||||
/// their kinds are the same.
|
/// their kinds are the same.
|
||||||
|
@ -73,16 +68,19 @@ public:
|
||||||
|
|
||||||
bool operator!=(Kind OtherValue) const { return !(*this == OtherValue); }
|
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){};
|
ExpressionFormat() : Value(Kind::NoFormat){};
|
||||||
explicit ExpressionFormat(Kind Value) : Value(Value){};
|
explicit ExpressionFormat(Kind Value) : Value(Value){};
|
||||||
|
|
||||||
/// \returns a wildcard regular expression StringRef that matches any 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
|
/// in the format represented by this instance, or an error if the format is
|
||||||
/// NoFormat or Conflict.
|
/// NoFormat.
|
||||||
Expected<StringRef> getWildcardRegex() const;
|
Expected<StringRef> getWildcardRegex() const;
|
||||||
|
|
||||||
/// \returns the string representation of \p Value in the format represented
|
/// \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;
|
Expected<std::string> getMatchingString(uint64_t Value) const;
|
||||||
|
|
||||||
/// \returns the value corresponding to string representation \p StrVal
|
/// \returns the value corresponding to string representation \p StrVal
|
||||||
|
@ -95,17 +93,26 @@ public:
|
||||||
|
|
||||||
/// Base class representing the AST of a given expression.
|
/// Base class representing the AST of a given expression.
|
||||||
class ExpressionAST {
|
class ExpressionAST {
|
||||||
|
private:
|
||||||
|
StringRef ExpressionStr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ExpressionAST(StringRef ExpressionStr) : ExpressionStr(ExpressionStr) {}
|
||||||
|
|
||||||
virtual ~ExpressionAST() = default;
|
virtual ~ExpressionAST() = default;
|
||||||
|
|
||||||
|
StringRef getExpressionStr() const { return ExpressionStr; }
|
||||||
|
|
||||||
/// Evaluates and \returns the value of the expression represented by this
|
/// Evaluates and \returns the value of the expression represented by this
|
||||||
/// AST or an error if evaluation fails.
|
/// AST or an error if evaluation fails.
|
||||||
virtual Expected<uint64_t> eval() const = 0;
|
virtual Expected<uint64_t> eval() const = 0;
|
||||||
|
|
||||||
/// \returns either the implicit format of this AST, FormatConflict if
|
/// \returns either the implicit format of this AST, a diagnostic against
|
||||||
/// implicit formats of the AST's components conflict, or NoFormat if the AST
|
/// \p SM if implicit formats of the AST's components conflict, or NoFormat
|
||||||
/// has no implicit format (e.g. AST is made up of a single literal).
|
/// if the AST has no implicit format (e.g. AST is made up of a single
|
||||||
virtual ExpressionFormat getImplicitFormat() const {
|
/// literal).
|
||||||
|
virtual Expected<ExpressionFormat>
|
||||||
|
getImplicitFormat(const SourceMgr &SM) const {
|
||||||
return ExpressionFormat();
|
return ExpressionFormat();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -117,8 +124,10 @@ private:
|
||||||
uint64_t Value;
|
uint64_t Value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Constructs a literal with the specified value.
|
/// Constructs a literal with the specified value parsed from
|
||||||
ExpressionLiteral(uint64_t Val) : Value(Val) {}
|
/// \p ExpressionStr.
|
||||||
|
ExpressionLiteral(StringRef ExpressionStr, uint64_t Val)
|
||||||
|
: ExpressionAST(ExpressionStr), Value(Val) {}
|
||||||
|
|
||||||
/// \returns the literal's value.
|
/// \returns the literal's value.
|
||||||
Expected<uint64_t> eval() const override { return Value; }
|
Expected<uint64_t> eval() const override { return Value; }
|
||||||
|
@ -222,21 +231,18 @@ public:
|
||||||
/// expression.
|
/// expression.
|
||||||
class NumericVariableUse : public ExpressionAST {
|
class NumericVariableUse : public ExpressionAST {
|
||||||
private:
|
private:
|
||||||
/// Name of the numeric variable.
|
|
||||||
StringRef Name;
|
|
||||||
|
|
||||||
/// Pointer to the class instance for the variable this use is about.
|
/// Pointer to the class instance for the variable this use is about.
|
||||||
NumericVariable *Variable;
|
NumericVariable *Variable;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
NumericVariableUse(StringRef Name, NumericVariable *Variable)
|
NumericVariableUse(StringRef Name, NumericVariable *Variable)
|
||||||
: Name(Name), Variable(Variable) {}
|
: ExpressionAST(Name), Variable(Variable) {}
|
||||||
|
|
||||||
/// \returns the value of the variable referenced by this instance.
|
/// \returns the value of the variable referenced by this instance.
|
||||||
Expected<uint64_t> eval() const override;
|
Expected<uint64_t> eval() const override;
|
||||||
|
|
||||||
/// \returns implicit format of this numeric variable.
|
/// \returns implicit format of this numeric variable.
|
||||||
ExpressionFormat getImplicitFormat() const override {
|
Expected<ExpressionFormat>
|
||||||
|
getImplicitFormat(const SourceMgr &SM) const override {
|
||||||
return Variable->getImplicitFormat();
|
return Variable->getImplicitFormat();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -257,9 +263,10 @@ private:
|
||||||
binop_eval_t EvalBinop;
|
binop_eval_t EvalBinop;
|
||||||
|
|
||||||
public:
|
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)
|
std::unique_ptr<ExpressionAST> RightOp)
|
||||||
: EvalBinop(EvalBinop) {
|
: ExpressionAST(ExpressionStr), EvalBinop(EvalBinop) {
|
||||||
LeftOperand = std::move(LeftOp);
|
LeftOperand = std::move(LeftOp);
|
||||||
RightOperand = std::move(RightOp);
|
RightOperand = std::move(RightOp);
|
||||||
}
|
}
|
||||||
|
@ -270,10 +277,12 @@ public:
|
||||||
/// variable is used in one of the operands.
|
/// variable is used in one of the operands.
|
||||||
Expected<uint64_t> eval() const override;
|
Expected<uint64_t> eval() const override;
|
||||||
|
|
||||||
/// \returns the implicit format of this AST, if any, a format conflict if
|
/// \returns the implicit format of this AST, if any, a diagnostic against
|
||||||
/// the implicit formats of the AST's components conflict, or no format if
|
/// \p SM if the implicit formats of the AST's components conflict, or no
|
||||||
/// the AST has no implicit format (e.g. AST is made of a single literal).
|
/// format if the AST has no implicit format (e.g. AST is made of a single
|
||||||
ExpressionFormat getImplicitFormat() const override;
|
/// literal).
|
||||||
|
Expected<ExpressionFormat>
|
||||||
|
getImplicitFormat(const SourceMgr &SM) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileCheckPatternContext;
|
class FileCheckPatternContext;
|
||||||
|
@ -663,17 +672,20 @@ private:
|
||||||
parseNumericOperand(StringRef &Expr, AllowedOperand AO,
|
parseNumericOperand(StringRef &Expr, AllowedOperand AO,
|
||||||
Optional<size_t> LineNumber,
|
Optional<size_t> LineNumber,
|
||||||
FileCheckPatternContext *Context, const SourceMgr &SM);
|
FileCheckPatternContext *Context, const SourceMgr &SM);
|
||||||
/// Parses \p Expr for a binary operation at line \p LineNumber, or before
|
/// Parses and updates \p RemainingExpr for a binary operation at line
|
||||||
/// input is parsed if \p LineNumber is None. The left operand of this binary
|
/// \p LineNumber, or before input is parsed if \p LineNumber is None. The
|
||||||
/// operation is given in \p LeftOp and \p IsLegacyLineExpr indicates whether
|
/// left operand of this binary operation is given in \p LeftOp and \p Expr
|
||||||
/// we are parsing a legacy @LINE expression. Parameter \p Context points to
|
/// holds the string for the full expression, including the left operand.
|
||||||
/// the class instance holding the live string and numeric variables.
|
/// Parameter \p IsLegacyLineExpr indicates whether we are parsing a legacy
|
||||||
/// \returns the class representing the binary operation in the AST of the
|
/// @LINE expression. Parameter \p Context points to the class instance
|
||||||
/// expression, or an error holding a diagnostic against \p SM otherwise.
|
/// 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>>
|
static Expected<std::unique_ptr<ExpressionAST>>
|
||||||
parseBinop(StringRef &Expr, std::unique_ptr<ExpressionAST> LeftOp,
|
parseBinop(StringRef Expr, StringRef &RemainingExpr,
|
||||||
bool IsLegacyLineExpr, Optional<size_t> LineNumber,
|
std::unique_ptr<ExpressionAST> LeftOp, bool IsLegacyLineExpr,
|
||||||
FileCheckPatternContext *Context, const SourceMgr &SM);
|
Optional<size_t> LineNumber, FileCheckPatternContext *Context,
|
||||||
|
const SourceMgr &SM);
|
||||||
};
|
};
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -185,16 +185,27 @@ CHECK-NEXT: [[# %u, VAR3]]
|
||||||
|
|
||||||
; Conflicting implicit format.
|
; Conflicting implicit format.
|
||||||
RUN: %ProtectFileCheckOutput \
|
RUN: %ProtectFileCheckOutput \
|
||||||
RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT --input-file %s %s 2>&1 \
|
RUN: not FileCheck --check-prefixes CHECK,FMT-CONFLICT1 --input-file %s %s 2>&1 \
|
||||||
RUN: | FileCheck --strict-whitespace --check-prefix FMT-CONFLICT-MSG %s
|
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
|
VAR USE IMPL FMT CONFLICT
|
||||||
23
|
23
|
||||||
FMT-CONFLICT-LABEL: VAR USE IMPL FMT CONFLICT
|
FMT-CONFLICT1-LABEL: VAR USE IMPL FMT CONFLICT
|
||||||
FMT-CONFLICT-NEXT: [[#VAR1 + VAR2]]
|
FMT-CONFLICT1-NEXT: [[#VAR1 + VAR2]]
|
||||||
FMT-CONFLICT-MSG: numeric-expression.txt:[[#@LINE-1]]:23: error: variables with conflicting format specifier: need an explicit one
|
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-CONFLICT-MSG-NEXT: {{F}}MT-CONFLICT-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}}
|
FMT-CONFLICT1-MSG-NEXT: {{F}}MT-CONFLICT1-NEXT: {{\[\[#VAR1 \+ VAR2\]\]}}
|
||||||
FMT-CONFLICT-MSG-NEXT: {{^ \^$}}
|
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.
|
; Explicitly specified format can override conflicting implicit formats.
|
||||||
VAR USE IMPL OVERRIDE FMT CONFLICT
|
VAR USE IMPL OVERRIDE FMT CONFLICT
|
||||||
|
|
|
@ -28,18 +28,50 @@ static StringRef bufferize(SourceMgr &SM, StringRef Str) {
|
||||||
return StrBufferRef;
|
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>
|
template <typename ErrorT>
|
||||||
static void expectError(StringRef ExpectedMsg, Error Err) {
|
static void expectError(StringRef ExpectedMsg, Error Err) {
|
||||||
bool ErrorHandled = false;
|
expectSameErrors<ErrorT>({ExpectedMsg.str()}, std::move(Err));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
|
static void expectDiagnosticError(StringRef ExpectedMsg, Error Err) {
|
||||||
|
@ -179,15 +211,6 @@ TEST_F(FileCheckTest, NoFormatProperties) {
|
||||||
EXPECT_FALSE(bool(NoFormat));
|
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) {
|
TEST_F(FileCheckTest, FormatEqualityOperators) {
|
||||||
ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
|
ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
|
||||||
ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned);
|
ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned);
|
||||||
|
@ -202,11 +225,6 @@ TEST_F(FileCheckTest, FormatEqualityOperators) {
|
||||||
ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat);
|
ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat);
|
||||||
EXPECT_FALSE(NoFormat == NoFormat2);
|
EXPECT_FALSE(NoFormat == NoFormat2);
|
||||||
EXPECT_TRUE(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) {
|
TEST_F(FileCheckTest, FormatKindEqualityOperators) {
|
||||||
|
@ -215,43 +233,36 @@ TEST_F(FileCheckTest, FormatKindEqualityOperators) {
|
||||||
EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned);
|
EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned);
|
||||||
EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower);
|
EXPECT_FALSE(UnsignedFormat == ExpressionFormat::Kind::HexLower);
|
||||||
EXPECT_TRUE(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);
|
ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
|
||||||
EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);
|
EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);
|
||||||
EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
|
EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FileCheckTest, Literal) {
|
TEST_F(FileCheckTest, Literal) {
|
||||||
|
SourceMgr SM;
|
||||||
|
|
||||||
// Eval returns the literal's value.
|
// Eval returns the literal's value.
|
||||||
ExpressionLiteral Ten(10);
|
ExpressionLiteral Ten(bufferize(SM, "10"), 10);
|
||||||
Expected<uint64_t> Value = Ten.eval();
|
Expected<uint64_t> Value = Ten.eval();
|
||||||
ASSERT_THAT_EXPECTED(Value, Succeeded());
|
ASSERT_THAT_EXPECTED(Value, Succeeded());
|
||||||
EXPECT_EQ(10U, *Value);
|
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.
|
// 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();
|
Value = Max.eval();
|
||||||
ASSERT_THAT_EXPECTED(Value, Succeeded());
|
ASSERT_THAT_EXPECTED(Value, Succeeded());
|
||||||
EXPECT_EQ(std::numeric_limits<uint64_t>::max(), *Value);
|
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) {
|
TEST_F(FileCheckTest, Expression) {
|
||||||
|
SourceMgr SM;
|
||||||
|
|
||||||
std::unique_ptr<ExpressionLiteral> Ten =
|
std::unique_ptr<ExpressionLiteral> Ten =
|
||||||
std::make_unique<ExpressionLiteral>(10);
|
std::make_unique<ExpressionLiteral>(bufferize(SM, "10"), 10);
|
||||||
ExpressionLiteral *TenPtr = Ten.get();
|
ExpressionLiteral *TenPtr = Ten.get();
|
||||||
Expression Expr(std::move(Ten),
|
Expression Expr(std::move(Ten),
|
||||||
ExpressionFormat(ExpressionFormat::Kind::HexLower));
|
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; }
|
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
|
||||||
|
|
||||||
TEST_F(FileCheckTest, NumericVariable) {
|
TEST_F(FileCheckTest, NumericVariable) {
|
||||||
|
SourceMgr SM;
|
||||||
|
|
||||||
// Undefined variable: getValue and eval fail, error returned by eval holds
|
// Undefined variable: getValue and eval fail, error returned by eval holds
|
||||||
// the name of the undefined variable.
|
// the name of the undefined variable.
|
||||||
NumericVariable FooVar("FOO",
|
NumericVariable FooVar("FOO",
|
||||||
|
@ -282,7 +295,9 @@ TEST_F(FileCheckTest, NumericVariable) {
|
||||||
EXPECT_EQ("FOO", FooVar.getName());
|
EXPECT_EQ("FOO", FooVar.getName());
|
||||||
EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
|
EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
|
||||||
NumericVariableUse FooVarUse("FOO", &FooVar);
|
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());
|
EXPECT_FALSE(FooVar.getValue());
|
||||||
Expected<uint64_t> EvalResult = FooVarUse.eval();
|
Expected<uint64_t> EvalResult = FooVarUse.eval();
|
||||||
expectUndefErrors({"FOO"}, EvalResult.takeError());
|
expectUndefErrors({"FOO"}, EvalResult.takeError());
|
||||||
|
@ -306,24 +321,32 @@ TEST_F(FileCheckTest, NumericVariable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(FileCheckTest, Binop) {
|
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);
|
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
|
||||||
FooVar.setValue(42);
|
FooVar.setValue(42);
|
||||||
std::unique_ptr<NumericVariableUse> FooVarUse =
|
std::unique_ptr<NumericVariableUse> FooVarUse =
|
||||||
std::make_unique<NumericVariableUse>("FOO", &FooVar);
|
std::make_unique<NumericVariableUse>(FooStr, &FooVar);
|
||||||
NumericVariable BarVar("BAR",
|
StringRef BarStr = ExprStr.take_back(3);
|
||||||
|
NumericVariable BarVar(BarStr,
|
||||||
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
|
ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
|
||||||
BarVar.setValue(18);
|
BarVar.setValue(18);
|
||||||
std::unique_ptr<NumericVariableUse> BarVarUse =
|
std::unique_ptr<NumericVariableUse> BarVarUse =
|
||||||
std::make_unique<NumericVariableUse>("BAR", &BarVar);
|
std::make_unique<NumericVariableUse>(BarStr, &BarVar);
|
||||||
BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
|
BinaryOperation Binop(ExprStr, doAdd, std::move(FooVarUse),
|
||||||
|
std::move(BarVarUse));
|
||||||
|
|
||||||
// Defined variables: eval returns right value; implicit format is as
|
// Defined variables: eval returns right value; implicit format is as
|
||||||
// expected.
|
// expected.
|
||||||
Expected<uint64_t> Value = Binop.eval();
|
Expected<uint64_t> Value = Binop.eval();
|
||||||
ASSERT_THAT_EXPECTED(Value, Succeeded());
|
ASSERT_THAT_EXPECTED(Value, Succeeded());
|
||||||
EXPECT_EQ(60U, *Value);
|
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
|
// 1 undefined variable: eval fails, error contains name of undefined
|
||||||
// variable.
|
// variable.
|
||||||
|
@ -338,24 +361,76 @@ TEST_F(FileCheckTest, Binop) {
|
||||||
expectUndefErrors({"FOO", "BAR"}, Value.takeError());
|
expectUndefErrors({"FOO", "BAR"}, Value.takeError());
|
||||||
|
|
||||||
// Literal + Variable has format of variable.
|
// 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::unique_ptr<ExpressionLiteral> Eighteen =
|
||||||
std::make_unique<ExpressionLiteral>(18);
|
std::make_unique<ExpressionLiteral>(EighteenStr, 18);
|
||||||
Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen));
|
Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse),
|
||||||
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
|
std::move(Eighteen));
|
||||||
FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
|
ImplicitFormat = Binop.getImplicitFormat(SM);
|
||||||
Eighteen = std::make_unique<ExpressionLiteral>(18);
|
ASSERT_THAT_EXPECTED(ImplicitFormat, Succeeded());
|
||||||
Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse));
|
EXPECT_EQ(*ImplicitFormat, ExpressionFormat::Kind::Unsigned);
|
||||||
EXPECT_EQ(Binop.getImplicitFormat(), 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 different implicit format conflict.
|
// 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);
|
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::unique_ptr<NumericVariableUse> BazVarUse =
|
||||||
std::make_unique<NumericVariableUse>("BAZ", &BazVar);
|
std::make_unique<NumericVariableUse>(BazStr, &BazVar);
|
||||||
Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse));
|
Binop = BinaryOperation(ExprStr, doAdd, std::move(FooVarUse),
|
||||||
EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict);
|
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) {
|
TEST_F(FileCheckTest, ValidVarNameStart) {
|
||||||
|
@ -648,7 +723,8 @@ TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
|
||||||
|
|
||||||
// Implicit format conflict.
|
// Implicit format conflict.
|
||||||
expectDiagnosticError(
|
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());
|
Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue