[FileCheck] Introduce substitution subclasses

Summary:
With now a clear distinction between string and numeric substitutions,
this patch introduces separate classes to represent them with a parent
class implementing the common interface. Diagnostics in
printSubstitutions() are also adapted to not require knowing which
substitution is being looked at since it does not hinder clarity and
makes the implementation simpler.

Reviewers: jhenderson, jdenny, probinson, arichardson

Subscribers: llvm-commits, probinson, arichardson, hiraditya

Tags: #llvm

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

llvm-svn: 361446
This commit is contained in:
Thomas Preud'homme 2019-05-23 00:10:29 +00:00
parent 1a944d27b2
commit f3b9bb3d69
6 changed files with 124 additions and 80 deletions

View File

@ -116,37 +116,20 @@ protected:
/// evaluate their value. /// evaluate their value.
FileCheckPatternContext *Context; FileCheckPatternContext *Context;
/// Whether this represents a numeric expression substitution.
bool IsNumSubst;
/// The string that needs to be substituted for something else. For a /// The string that needs to be substituted for something else. For a
/// string variable this is its name, otherwise this is the whole numeric /// string variable this is its name, otherwise this is the whole numeric
/// expression. /// expression.
StringRef FromStr; StringRef FromStr;
/// If this is a numeric expression substitution, this is the pointer to the
/// class representing the numeric expression whose value is to be
/// substituted.
FileCheckNumExpr *NumExpr = nullptr;
// Index in RegExStr of where to do the substitution. // Index in RegExStr of where to do the substitution.
size_t InsertIdx; size_t InsertIdx;
public: public:
/// Constructor for a pattern variable substitution. FileCheckSubstitution(FileCheckPatternContext *Context, StringRef VarName,
FileCheckSubstitution(FileCheckPatternContext *Context, size_t InsertIdx)
StringRef VarName, size_t InsertIdx) : Context(Context), FromStr(VarName), InsertIdx(InsertIdx) {}
: Context(Context), IsNumSubst(false), FromStr(VarName),
InsertIdx(InsertIdx) {}
/// Constructor for a numeric expression substitution. virtual ~FileCheckSubstitution() = default;
FileCheckSubstitution(FileCheckPatternContext *Context, StringRef Expr,
FileCheckNumExpr *NumExpr, size_t InsertIdx)
: Context(Context), IsNumSubst(true), FromStr(Expr), NumExpr(NumExpr),
InsertIdx(InsertIdx) {}
/// \returns whether this is a numeric expression substitution.
bool isNumSubst() const { return IsNumSubst; }
/// \returns the string to be substituted for something else. /// \returns the string to be substituted for something else.
StringRef getFromString() const { return FromStr; } StringRef getFromString() const { return FromStr; }
@ -154,15 +137,48 @@ public:
/// \returns the index where the substitution is to be performed in RegExStr. /// \returns the index where the substitution is to be performed in RegExStr.
size_t getIndex() const { return InsertIdx; } size_t getIndex() const { return InsertIdx; }
/// \returns the result of the substitution represented by this class /// \returns a string containing the result of the substitution represented
/// instance or None if substitution failed. Numeric expressions are /// by this class instance or None if substitution failed.
/// substituted by their values. String variables are simply replaced by the virtual llvm::Optional<std::string> getResult() const = 0;
/// text their definition matched.
llvm::Optional<std::string> getResult() const;
/// \returns the name of the undefined variable used in this substitution, if /// \returns the name of the variable used in this substitution if undefined,
/// any, or an empty string otherwise. /// or an empty string otherwise.
StringRef getUndefVarName() const; virtual StringRef getUndefVarName() const = 0;
};
class FileCheckStringSubstitution : public FileCheckSubstitution {
public:
FileCheckStringSubstitution(FileCheckPatternContext *Context,
StringRef VarName, size_t InsertIdx)
: FileCheckSubstitution(Context, VarName, InsertIdx) {}
/// \returns the text that the string variable in this substitution matched
/// when defined, or None if the variable is undefined.
llvm::Optional<std::string> getResult() const override;
/// \returns the name of the string variable used in this substitution if
/// undefined, or an empty string otherwise.
StringRef getUndefVarName() const override;
};
class FileCheckNumericSubstitution : public FileCheckSubstitution {
private:
/// Pointer to the class representing the numeric expression whose value is
/// to be substituted.
FileCheckNumExpr *NumExpr;
public:
FileCheckNumericSubstitution(FileCheckPatternContext *Context, StringRef Expr,
FileCheckNumExpr *NumExpr, size_t InsertIdx)
: FileCheckSubstitution(Context, Expr, InsertIdx), NumExpr(NumExpr) {}
/// \returns a string containing the result of evaluating the numeric
/// expression in this substitution, or None if evaluation failed.
llvm::Optional<std::string> getResult() const override;
/// \returns the name of the numeric variable used in this substitution if
/// undefined, or an empty string otherwise.
StringRef getUndefVarName() const override;
}; };
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
@ -245,6 +261,10 @@ private:
/// automatically free them once they are guaranteed to no longer be used. /// automatically free them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<FileCheckNumericVariable>> NumericVariables; std::vector<std::unique_ptr<FileCheckNumericVariable>> NumericVariables;
/// Vector holding pointers to all substitutions. Used to automatically free
/// them once they are guaranteed to no longer be used.
std::vector<std::unique_ptr<FileCheckSubstitution>> Substitutions;
public: public:
/// \returns the value of string variable \p VarName or None if no such /// \returns the value of string variable \p VarName or None if no such
/// variable has been defined. /// variable has been defined.
@ -273,6 +293,17 @@ private:
/// Makes a new numeric variable and registers it for destruction when the /// Makes a new numeric variable and registers it for destruction when the
/// context is destroyed. /// context is destroyed.
FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value); FileCheckNumericVariable *makeNumericVariable(StringRef Name, uint64_t Value);
/// Makes a new string substitution and registers it for destruction when the
/// context is destroyed.
FileCheckSubstitution *makeStringSubstitution(StringRef VarName,
size_t InsertIdx);
/// Makes a new numeric substitution and registers it for destruction when
/// the context is destroyed.
FileCheckSubstitution *makeNumericSubstitution(StringRef Expr,
FileCheckNumExpr *NumExpr,
size_t InsertIdx);
}; };
class FileCheckPattern { class FileCheckPattern {
@ -292,7 +323,7 @@ class FileCheckPattern {
/// RegExStr will contain "foobaz" and we'll get two entries in this vector /// RegExStr will contain "foobaz" and we'll get two entries in this vector
/// that tells us to insert the value of string variable "bar" at offset 3 /// that tells us to insert the value of string variable "bar" at offset 3
/// and the value of numeric expression "N+1" at offset 6. /// and the value of numeric expression "N+1" at offset 6.
std::vector<FileCheckSubstitution> Substitutions; std::vector<FileCheckSubstitution *> Substitutions;
/// Maps names of string variables defined in a pattern to the parenthesized /// Maps names of string variables defined in a pattern to the parenthesized
/// capture numbers of their last definition. /// capture numbers of their last definition.

View File

@ -52,14 +52,14 @@ StringRef FileCheckNumExpr::getUndefVarName() const {
return StringRef(); return StringRef();
} }
llvm::Optional<std::string> FileCheckSubstitution::getResult() const { llvm::Optional<std::string> FileCheckNumericSubstitution::getResult() const {
if (IsNumSubst) { llvm::Optional<uint64_t> EvaluatedValue = NumExpr->eval();
llvm::Optional<uint64_t> EvaluatedValue = NumExpr->eval(); if (!EvaluatedValue)
if (!EvaluatedValue) return llvm::None;
return llvm::None; return utostr(*EvaluatedValue);
return utostr(*EvaluatedValue); }
}
llvm::Optional<std::string> FileCheckStringSubstitution::getResult() const {
// Look up the value and escape it so that we can put it into the regex. // Look up the value and escape it so that we can put it into the regex.
llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr); llvm::Optional<StringRef> VarVal = Context->getPatternVarValue(FromStr);
if (!VarVal) if (!VarVal)
@ -67,12 +67,13 @@ llvm::Optional<std::string> FileCheckSubstitution::getResult() const {
return Regex::escape(*VarVal); return Regex::escape(*VarVal);
} }
StringRef FileCheckSubstitution::getUndefVarName() const { StringRef FileCheckNumericSubstitution::getUndefVarName() const {
if (IsNumSubst) // Although a use of an undefined numeric variable is detected at parse
// Although a use of an undefined numeric variable is detected at parse // time, a numeric variable can be undefined later by ClearLocalVariables.
// time, a numeric variable can be undefined later by ClearLocalVariables. return NumExpr->getUndefVarName();
return NumExpr->getUndefVarName(); }
StringRef FileCheckStringSubstitution::getUndefVarName() const {
if (!Context->getPatternVarValue(FromStr)) if (!Context->getPatternVarValue(FromStr))
return FromStr; return FromStr;
@ -385,11 +386,11 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix,
} else { } else {
// Handle substitution of string variables ([[<var>]]) defined in // Handle substitution of string variables ([[<var>]]) defined in
// previous CHECK patterns, and substitution of numeric expressions. // previous CHECK patterns, and substitution of numeric expressions.
FileCheckSubstitution Substitution = FileCheckSubstitution *Substitution =
IsNumBlock ? FileCheckSubstitution(Context, MatchStr, NumExpr, IsNumBlock
SubstInsertIdx) ? Context->makeNumericSubstitution(MatchStr, NumExpr,
: FileCheckSubstitution(Context, MatchStr, SubstInsertIdx)
SubstInsertIdx); : Context->makeStringSubstitution(MatchStr, SubstInsertIdx);
Substitutions.push_back(Substitution); Substitutions.push_back(Substitution);
} }
continue; continue;
@ -471,12 +472,12 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const {
// handled by back-references. // handled by back-references.
for (const auto &Substitution : Substitutions) { for (const auto &Substitution : Substitutions) {
// Substitute and check for failure (e.g. use of undefined variable). // Substitute and check for failure (e.g. use of undefined variable).
llvm::Optional<std::string> Value = Substitution.getResult(); llvm::Optional<std::string> Value = Substitution->getResult();
if (!Value) if (!Value)
return StringRef::npos; return StringRef::npos;
// Plop it into the regex at the adjusted offset. // Plop it into the regex at the adjusted offset.
TmpStr.insert(TmpStr.begin() + Substitution.getIndex() + InsertOffset, TmpStr.insert(TmpStr.begin() + Substitution->getIndex() + InsertOffset,
Value->begin(), Value->end()); Value->begin(), Value->end());
InsertOffset += Value->size(); InsertOffset += Value->size();
} }
@ -532,24 +533,20 @@ void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer,
for (const auto &Substitution : Substitutions) { for (const auto &Substitution : Substitutions) {
SmallString<256> Msg; SmallString<256> Msg;
raw_svector_ostream OS(Msg); raw_svector_ostream OS(Msg);
bool IsNumSubst = Substitution.isNumSubst(); llvm::Optional<std::string> MatchedValue = Substitution->getResult();
llvm::Optional<std::string> MatchedValue = Substitution.getResult();
// Substitution failed or is not known at match time, print the undefined // Substitution failed or is not known at match time, print the undefined
// variable it uses. // variable it uses.
if (!MatchedValue) { if (!MatchedValue) {
StringRef UndefVarName = Substitution.getUndefVarName(); StringRef UndefVarName = Substitution->getUndefVarName();
if (UndefVarName.empty()) if (UndefVarName.empty())
continue; continue;
OS << "uses undefined variable \""; OS << "uses undefined variable \"";
OS.write_escaped(UndefVarName) << "\""; OS.write_escaped(UndefVarName) << "\"";
} else { } else {
// Substitution succeeded. Print substituted value. // Substitution succeeded. Print substituted value.
if (IsNumSubst) OS << "with \"";
OS << "with numeric expression \""; OS.write_escaped(Substitution->getFromString()) << "\" equal to \"";
else
OS << "with string variable \"";
OS.write_escaped(Substitution.getFromString()) << "\" equal to \"";
OS.write_escaped(*MatchedValue) << "\""; OS.write_escaped(*MatchedValue) << "\"";
} }
@ -653,6 +650,21 @@ FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) {
return NumericVariables.back().get(); return NumericVariables.back().get();
} }
FileCheckSubstitution *
FileCheckPatternContext::makeStringSubstitution(StringRef VarName,
size_t InsertIdx) {
Substitutions.push_back(
llvm::make_unique<FileCheckStringSubstitution>(this, VarName, InsertIdx));
return Substitutions.back().get();
}
FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution(
StringRef Expr, FileCheckNumExpr *NumExpr, size_t InsertIdx) {
Substitutions.push_back(llvm::make_unique<FileCheckNumericSubstitution>(
this, Expr, NumExpr, InsertIdx));
return Substitutions.back().get();
}
size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) { size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) {
// Offset keeps track of the current offset within the input Str // Offset keeps track of the current offset within the input Str
size_t Offset = 0; size_t Offset = 0;

View File

@ -14,9 +14,9 @@ NUMNOT-NOT: Numeric value = [[#NUMVAL]]
NUMERRMSG: defines.txt:[[#@LINE-3]]:11: error: CHECKNUM: expected string not found in input NUMERRMSG: defines.txt:[[#@LINE-3]]:11: error: CHECKNUM: expected string not found in input
NUMERRMSG: defines.txt:1:1: note: scanning from here NUMERRMSG: defines.txt:1:1: note: scanning from here
NUMERRMSG: defines.txt:1:1: note: with numeric expression "NUMVAL" equal to "8" NUMERRMSG: defines.txt:1:1: note: with "NUMVAL" equal to "8"
NUMERRMSG: defines.txt:[[#@LINE-7]]:1: note: possible intended match here NUMERRMSG: defines.txt:[[#@LINE-7]]:1: note: possible intended match here
NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:13: error: {{NUMNOT}}-NOT: excluded string found in input NOT-NUMERRMSG: defines.txt:[[#@LINE-7]]:13: error: {{NUMNOT}}-NOT: excluded string found in input
NOT-NUMERRMSG: defines.txt:[[#@LINE-10]]:1: note: found here NOT-NUMERRMSG: defines.txt:[[#@LINE-10]]:1: note: found here
NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with numeric expression "NUMVAL" equal to "12" NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with "NUMVAL" equal to "12"

View File

@ -15,12 +15,12 @@ NOT-NOT: Value = [[VALUE]]
ERRMSG: defines.txt:[[@LINE-3]]:8: error: CHECK: expected string not found in input ERRMSG: defines.txt:[[@LINE-3]]:8: error: CHECK: expected string not found in input
ERRMSG: defines.txt:1:1: note: scanning from here ERRMSG: defines.txt:1:1: note: scanning from here
ERRMSG: defines.txt:1:1: note: with string variable "VALUE" equal to "20" ERRMSG: defines.txt:1:1: note: with "VALUE" equal to "20"
ERRMSG: defines.txt:[[@LINE-7]]:1: note: possible intended match here ERRMSG: defines.txt:[[@LINE-7]]:1: note: possible intended match here
NOT-ERRMSG: defines.txt:[[@LINE-7]]:10: error: {{NOT}}-NOT: excluded string found in input NOT-ERRMSG: defines.txt:[[@LINE-7]]:10: error: {{NOT}}-NOT: excluded string found in input
NOT-ERRMSG: defines.txt:[[@LINE-10]]:1: note: found here NOT-ERRMSG: defines.txt:[[@LINE-10]]:1: note: found here
NOT-ERRMSG: defines.txt:[[@LINE-11]]:1: note: with string variable "VALUE" equal to "10" NOT-ERRMSG: defines.txt:[[@LINE-11]]:1: note: with "VALUE" equal to "10"
; Definition of string variable to an empty string. ; Definition of string variable to an empty string.
RUN: FileCheck -DVALUE= --check-prefix EMPTY --input-file %s %s 2>&1 RUN: FileCheck -DVALUE= --check-prefix EMPTY --input-file %s %s 2>&1

View File

@ -41,7 +41,7 @@ V-NEXT: {{^ \^$}}
V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here
V-NEXT: {{^}}NUMVAR:42{{$}} V-NEXT: {{^}}NUMVAR:42{{$}}
V-NEXT: {{^}}^~~~~~~~~{{$}} V-NEXT: {{^}}^~~~~~~~~{{$}}
V-NEXT: verbose.txt:[[#@LINE-12]]:1: note: with numeric expression "NUMVAR" equal to "42" V-NEXT: verbose.txt:[[#@LINE-12]]:1: note: with "NUMVAR" equal to "42"
V-NEXT: {{^}}NUMVAR:42{{$}} V-NEXT: {{^}}NUMVAR:42{{$}}
V-NEXT: {{^}}^~~~~~~~~{{$}} V-NEXT: {{^}}^~~~~~~~~{{$}}
@ -51,7 +51,7 @@ V-NEXT: {{^ \^$}}
V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here
V-NEXT: {{^}}NUMVAR - 1:41{{$}} V-NEXT: {{^}}NUMVAR - 1:41{{$}}
V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}
V-NEXT: verbose.txt:[[#@LINE-21]]:1: note: with numeric expression "NUMVAR - 1" equal to "41" V-NEXT: verbose.txt:[[#@LINE-21]]:1: note: with "NUMVAR - 1" equal to "41"
V-NEXT: {{^}}NUMVAR - 1:41{{$}} V-NEXT: {{^}}NUMVAR - 1:41{{$}}
V-NEXT: {{^}}^~~~~~~~~~~~~{{$}} V-NEXT: {{^}}^~~~~~~~~~~~~{{$}}

View File

@ -229,9 +229,9 @@ TEST_F(FileCheckTest, Substitution) {
Context.defineCmdlineVariables(GlobalDefines, SM); Context.defineCmdlineVariables(GlobalDefines, SM);
// Substitution of an undefined string variable fails. // Substitution of an undefined string variable fails.
FileCheckSubstitution Substitution = FileCheckStringSubstitution StringSubstitution =
FileCheckSubstitution(&Context, "VAR404", 42); FileCheckStringSubstitution(&Context, "VAR404", 42);
EXPECT_FALSE(Substitution.getResult()); EXPECT_FALSE(StringSubstitution.getResult());
// Substitutions of defined pseudo and non-pseudo numeric variables return // Substitutions of defined pseudo and non-pseudo numeric variables return
// the right value. // the right value.
@ -239,10 +239,10 @@ TEST_F(FileCheckTest, Substitution) {
FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10); FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10);
FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0); FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0);
FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3); FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3);
FileCheckSubstitution SubstitutionLine = FileCheckNumericSubstitution SubstitutionLine =
FileCheckSubstitution(&Context, "@LINE", &NumExprLine, 12); FileCheckNumericSubstitution(&Context, "@LINE", &NumExprLine, 12);
FileCheckSubstitution SubstitutionN = FileCheckNumericSubstitution SubstitutionN =
FileCheckSubstitution(&Context, "N", &NumExprN, 30); FileCheckNumericSubstitution(&Context, "N", &NumExprN, 30);
llvm::Optional<std::string> Value = SubstitutionLine.getResult(); llvm::Optional<std::string> Value = SubstitutionLine.getResult();
EXPECT_TRUE(Value); EXPECT_TRUE(Value);
EXPECT_EQ("42", *Value); EXPECT_EQ("42", *Value);
@ -258,8 +258,8 @@ TEST_F(FileCheckTest, Substitution) {
// Substitution of a defined string variable returns the right value. // Substitution of a defined string variable returns the right value.
FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context); FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
Substitution = FileCheckSubstitution(&Context, "FOO", 42); StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
Value = Substitution.getResult(); Value = StringSubstitution.getResult();
EXPECT_TRUE(Value); EXPECT_TRUE(Value);
EXPECT_EQ("BAR", *Value); EXPECT_EQ("BAR", *Value);
} }
@ -273,29 +273,30 @@ TEST_F(FileCheckTest, UndefVars) {
// getUndefVarName() on a string substitution with an undefined variable // getUndefVarName() on a string substitution with an undefined variable
// returns that variable. // returns that variable.
FileCheckSubstitution Substitution = FileCheckStringSubstitution StringSubstitution =
FileCheckSubstitution(&Context, "VAR404", 42); FileCheckStringSubstitution(&Context, "VAR404", 42);
StringRef UndefVar = Substitution.getUndefVarName(); StringRef UndefVar = StringSubstitution.getUndefVarName();
EXPECT_EQ("VAR404", UndefVar); EXPECT_EQ("VAR404", UndefVar);
// getUndefVarName() on a string substitution with a defined variable returns // getUndefVarName() on a string substitution with a defined variable returns
// an empty string. // an empty string.
Substitution = FileCheckSubstitution(&Context, "FOO", 42); StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
UndefVar = Substitution.getUndefVarName(); UndefVar = StringSubstitution.getUndefVarName();
EXPECT_EQ("", UndefVar); EXPECT_EQ("", UndefVar);
// getUndefVarName() on a numeric substitution with a defined variable // getUndefVarName() on a numeric substitution with a defined variable
// returns an empty string. // returns an empty string.
FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42); FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0); FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0);
Substitution = FileCheckSubstitution(&Context, "@LINE", &NumExpr, 12); FileCheckNumericSubstitution NumericSubstitution =
UndefVar = Substitution.getUndefVarName(); FileCheckNumericSubstitution(&Context, "@LINE", &NumExpr, 12);
UndefVar = NumericSubstitution.getUndefVarName();
EXPECT_EQ("", UndefVar); EXPECT_EQ("", UndefVar);
// getUndefVarName() on a numeric substitution with an undefined variable // getUndefVarName() on a numeric substitution with an undefined variable
// returns that variable. // returns that variable.
LineVar.clearValue(); LineVar.clearValue();
UndefVar = Substitution.getUndefVarName(); UndefVar = NumericSubstitution.getUndefVarName();
EXPECT_EQ("@LINE", UndefVar); EXPECT_EQ("@LINE", UndefVar);
} }