From 94aa1e382019076b86ca5dab51d4125d006adf06 Mon Sep 17 00:00:00 2001 From: Thomas Preud'homme Date: Thu, 23 May 2019 00:10:29 +0000 Subject: [PATCH] [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 --- llvm/include/llvm/Support/FileCheck.h | 91 ++++++++++++++++-------- llvm/lib/Support/FileCheck.cpp | 66 ++++++++++------- llvm/test/FileCheck/numeric-defines.txt | 4 +- llvm/test/FileCheck/string-defines.txt | 4 +- llvm/test/FileCheck/verbose.txt | 4 +- llvm/unittests/Support/FileCheckTest.cpp | 35 ++++----- 6 files changed, 124 insertions(+), 80 deletions(-) diff --git a/llvm/include/llvm/Support/FileCheck.h b/llvm/include/llvm/Support/FileCheck.h index 2094880c681d0..087cc2d8bbd4c 100644 --- a/llvm/include/llvm/Support/FileCheck.h +++ b/llvm/include/llvm/Support/FileCheck.h @@ -116,37 +116,20 @@ class FileCheckSubstitution { /// evaluate their value. FileCheckPatternContext *Context; - /// Whether this represents a numeric expression substitution. - bool IsNumSubst; - /// The string that needs to be substituted for something else. For a /// string variable this is its name, otherwise this is the whole numeric /// expression. 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. size_t InsertIdx; public: - /// Constructor for a pattern variable substitution. - FileCheckSubstitution(FileCheckPatternContext *Context, - StringRef VarName, size_t InsertIdx) - : Context(Context), IsNumSubst(false), FromStr(VarName), - InsertIdx(InsertIdx) {} - - /// Constructor for a numeric expression substitution. - FileCheckSubstitution(FileCheckPatternContext *Context, StringRef Expr, - FileCheckNumExpr *NumExpr, size_t InsertIdx) - : Context(Context), IsNumSubst(true), FromStr(Expr), NumExpr(NumExpr), - InsertIdx(InsertIdx) {} + FileCheckSubstitution(FileCheckPatternContext *Context, StringRef VarName, + size_t InsertIdx) + : Context(Context), FromStr(VarName), InsertIdx(InsertIdx) {} - /// \returns whether this is a numeric expression substitution. - bool isNumSubst() const { return IsNumSubst; } + virtual ~FileCheckSubstitution() = default; /// \returns the string to be substituted for something else. StringRef getFromString() const { return FromStr; } @@ -154,15 +137,48 @@ class FileCheckSubstitution { /// \returns the index where the substitution is to be performed in RegExStr. size_t getIndex() const { return InsertIdx; } - /// \returns the result of the substitution represented by this class - /// instance or None if substitution failed. Numeric expressions are - /// substituted by their values. String variables are simply replaced by the - /// text their definition matched. - llvm::Optional getResult() const; + /// \returns a string containing the result of the substitution represented + /// by this class instance or None if substitution failed. + virtual llvm::Optional getResult() const = 0; - /// \returns the name of the undefined variable used in this substitution, if - /// any, or an empty string otherwise. - StringRef getUndefVarName() const; + /// \returns the name of the variable used in this substitution if undefined, + /// or an empty string otherwise. + 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 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 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 @@ class FileCheckPatternContext { /// automatically free them once they are guaranteed to no longer be used. std::vector> NumericVariables; + /// Vector holding pointers to all substitutions. Used to automatically free + /// them once they are guaranteed to no longer be used. + std::vector> Substitutions; + public: /// \returns the value of string variable \p VarName or None if no such /// variable has been defined. @@ -273,6 +293,17 @@ class FileCheckPatternContext { /// Makes a new numeric variable and registers it for destruction when the /// context is destroyed. 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 { @@ -292,7 +323,7 @@ class FileCheckPattern { /// 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 /// and the value of numeric expression "N+1" at offset 6. - std::vector Substitutions; + std::vector Substitutions; /// Maps names of string variables defined in a pattern to the parenthesized /// capture numbers of their last definition. diff --git a/llvm/lib/Support/FileCheck.cpp b/llvm/lib/Support/FileCheck.cpp index 5eb0c6481b0a4..a2b0f84e2c96f 100644 --- a/llvm/lib/Support/FileCheck.cpp +++ b/llvm/lib/Support/FileCheck.cpp @@ -52,14 +52,14 @@ StringRef FileCheckNumExpr::getUndefVarName() const { return StringRef(); } -llvm::Optional FileCheckSubstitution::getResult() const { - if (IsNumSubst) { - llvm::Optional EvaluatedValue = NumExpr->eval(); - if (!EvaluatedValue) - return llvm::None; - return utostr(*EvaluatedValue); - } +llvm::Optional FileCheckNumericSubstitution::getResult() const { + llvm::Optional EvaluatedValue = NumExpr->eval(); + if (!EvaluatedValue) + return llvm::None; + return utostr(*EvaluatedValue); +} +llvm::Optional FileCheckStringSubstitution::getResult() const { // Look up the value and escape it so that we can put it into the regex. llvm::Optional VarVal = Context->getPatternVarValue(FromStr); if (!VarVal) @@ -67,12 +67,13 @@ llvm::Optional FileCheckSubstitution::getResult() const { return Regex::escape(*VarVal); } -StringRef FileCheckSubstitution::getUndefVarName() const { - if (IsNumSubst) - // Although a use of an undefined numeric variable is detected at parse - // time, a numeric variable can be undefined later by ClearLocalVariables. - return NumExpr->getUndefVarName(); +StringRef FileCheckNumericSubstitution::getUndefVarName() const { + // Although a use of an undefined numeric variable is detected at parse + // time, a numeric variable can be undefined later by ClearLocalVariables. + return NumExpr->getUndefVarName(); +} +StringRef FileCheckStringSubstitution::getUndefVarName() const { if (!Context->getPatternVarValue(FromStr)) return FromStr; @@ -385,11 +386,11 @@ bool FileCheckPattern::ParsePattern(StringRef PatternStr, StringRef Prefix, } else { // Handle substitution of string variables ([[]]) defined in // previous CHECK patterns, and substitution of numeric expressions. - FileCheckSubstitution Substitution = - IsNumBlock ? FileCheckSubstitution(Context, MatchStr, NumExpr, - SubstInsertIdx) - : FileCheckSubstitution(Context, MatchStr, - SubstInsertIdx); + FileCheckSubstitution *Substitution = + IsNumBlock + ? Context->makeNumericSubstitution(MatchStr, NumExpr, + SubstInsertIdx) + : Context->makeStringSubstitution(MatchStr, SubstInsertIdx); Substitutions.push_back(Substitution); } continue; @@ -471,12 +472,12 @@ size_t FileCheckPattern::match(StringRef Buffer, size_t &MatchLen) const { // handled by back-references. for (const auto &Substitution : Substitutions) { // Substitute and check for failure (e.g. use of undefined variable). - llvm::Optional Value = Substitution.getResult(); + llvm::Optional Value = Substitution->getResult(); if (!Value) return StringRef::npos; // 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()); InsertOffset += Value->size(); } @@ -532,24 +533,20 @@ void FileCheckPattern::printSubstitutions(const SourceMgr &SM, StringRef Buffer, for (const auto &Substitution : Substitutions) { SmallString<256> Msg; raw_svector_ostream OS(Msg); - bool IsNumSubst = Substitution.isNumSubst(); - llvm::Optional MatchedValue = Substitution.getResult(); + llvm::Optional MatchedValue = Substitution->getResult(); // Substitution failed or is not known at match time, print the undefined // variable it uses. if (!MatchedValue) { - StringRef UndefVarName = Substitution.getUndefVarName(); + StringRef UndefVarName = Substitution->getUndefVarName(); if (UndefVarName.empty()) continue; OS << "uses undefined variable \""; OS.write_escaped(UndefVarName) << "\""; } else { // Substitution succeeded. Print substituted value. - if (IsNumSubst) - OS << "with numeric expression \""; - else - OS << "with string variable \""; - OS.write_escaped(Substitution.getFromString()) << "\" equal to \""; + OS << "with \""; + OS.write_escaped(Substitution->getFromString()) << "\" equal to \""; OS.write_escaped(*MatchedValue) << "\""; } @@ -653,6 +650,21 @@ FileCheckPatternContext::makeNumericVariable(StringRef Name, uint64_t Value) { return NumericVariables.back().get(); } +FileCheckSubstitution * +FileCheckPatternContext::makeStringSubstitution(StringRef VarName, + size_t InsertIdx) { + Substitutions.push_back( + llvm::make_unique(this, VarName, InsertIdx)); + return Substitutions.back().get(); +} + +FileCheckSubstitution *FileCheckPatternContext::makeNumericSubstitution( + StringRef Expr, FileCheckNumExpr *NumExpr, size_t InsertIdx) { + Substitutions.push_back(llvm::make_unique( + this, Expr, NumExpr, InsertIdx)); + return Substitutions.back().get(); +} + size_t FileCheckPattern::FindRegexVarEnd(StringRef Str, SourceMgr &SM) { // Offset keeps track of the current offset within the input Str size_t Offset = 0; diff --git a/llvm/test/FileCheck/numeric-defines.txt b/llvm/test/FileCheck/numeric-defines.txt index aeaeba2db1c9d..bbdd8e8c69ec9 100644 --- a/llvm/test/FileCheck/numeric-defines.txt +++ b/llvm/test/FileCheck/numeric-defines.txt @@ -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: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 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-11]]:1: note: with numeric expression "NUMVAL" equal to "12" +NOT-NUMERRMSG: defines.txt:[[#@LINE-11]]:1: note: with "NUMVAL" equal to "12" diff --git a/llvm/test/FileCheck/string-defines.txt b/llvm/test/FileCheck/string-defines.txt index 4c7366c02de40..a8514741ddd8a 100644 --- a/llvm/test/FileCheck/string-defines.txt +++ b/llvm/test/FileCheck/string-defines.txt @@ -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: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 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-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. RUN: FileCheck -DVALUE= --check-prefix EMPTY --input-file %s %s 2>&1 diff --git a/llvm/test/FileCheck/verbose.txt b/llvm/test/FileCheck/verbose.txt index 244fff4e824ce..4d44c77515963 100644 --- a/llvm/test/FileCheck/verbose.txt +++ b/llvm/test/FileCheck/verbose.txt @@ -41,7 +41,7 @@ V-NEXT: {{^ \^$}} V-NEXT: verbose.txt:[[#@LINE-9]]:1: note: found here V-NEXT: {{^}}NUMVAR:42{{$}} 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: {{^}}^~~~~~~~~{{$}} @@ -51,7 +51,7 @@ V-NEXT: {{^ \^$}} V-NEXT: verbose.txt:[[#@LINE-18]]:1: note: found here V-NEXT: {{^}}NUMVAR - 1:41{{$}} 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: {{^}}^~~~~~~~~~~~~{{$}} diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp index 5e157b216e524..dba6b5976946b 100644 --- a/llvm/unittests/Support/FileCheckTest.cpp +++ b/llvm/unittests/Support/FileCheckTest.cpp @@ -229,9 +229,9 @@ TEST_F(FileCheckTest, Substitution) { Context.defineCmdlineVariables(GlobalDefines, SM); // Substitution of an undefined string variable fails. - FileCheckSubstitution Substitution = - FileCheckSubstitution(&Context, "VAR404", 42); - EXPECT_FALSE(Substitution.getResult()); + FileCheckStringSubstitution StringSubstitution = + FileCheckStringSubstitution(&Context, "VAR404", 42); + EXPECT_FALSE(StringSubstitution.getResult()); // Substitutions of defined pseudo and non-pseudo numeric variables return // the right value. @@ -239,10 +239,10 @@ TEST_F(FileCheckTest, Substitution) { FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10); FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0); FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3); - FileCheckSubstitution SubstitutionLine = - FileCheckSubstitution(&Context, "@LINE", &NumExprLine, 12); - FileCheckSubstitution SubstitutionN = - FileCheckSubstitution(&Context, "N", &NumExprN, 30); + FileCheckNumericSubstitution SubstitutionLine = + FileCheckNumericSubstitution(&Context, "@LINE", &NumExprLine, 12); + FileCheckNumericSubstitution SubstitutionN = + FileCheckNumericSubstitution(&Context, "N", &NumExprN, 30); llvm::Optional Value = SubstitutionLine.getResult(); EXPECT_TRUE(Value); EXPECT_EQ("42", *Value); @@ -258,8 +258,8 @@ TEST_F(FileCheckTest, Substitution) { // Substitution of a defined string variable returns the right value. FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context); - Substitution = FileCheckSubstitution(&Context, "FOO", 42); - Value = Substitution.getResult(); + StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); + Value = StringSubstitution.getResult(); EXPECT_TRUE(Value); EXPECT_EQ("BAR", *Value); } @@ -273,29 +273,30 @@ TEST_F(FileCheckTest, UndefVars) { // getUndefVarName() on a string substitution with an undefined variable // returns that variable. - FileCheckSubstitution Substitution = - FileCheckSubstitution(&Context, "VAR404", 42); - StringRef UndefVar = Substitution.getUndefVarName(); + FileCheckStringSubstitution StringSubstitution = + FileCheckStringSubstitution(&Context, "VAR404", 42); + StringRef UndefVar = StringSubstitution.getUndefVarName(); EXPECT_EQ("VAR404", UndefVar); // getUndefVarName() on a string substitution with a defined variable returns // an empty string. - Substitution = FileCheckSubstitution(&Context, "FOO", 42); - UndefVar = Substitution.getUndefVarName(); + StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42); + UndefVar = StringSubstitution.getUndefVarName(); EXPECT_EQ("", UndefVar); // getUndefVarName() on a numeric substitution with a defined variable // returns an empty string. FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42); FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0); - Substitution = FileCheckSubstitution(&Context, "@LINE", &NumExpr, 12); - UndefVar = Substitution.getUndefVarName(); + FileCheckNumericSubstitution NumericSubstitution = + FileCheckNumericSubstitution(&Context, "@LINE", &NumExpr, 12); + UndefVar = NumericSubstitution.getUndefVarName(); EXPECT_EQ("", UndefVar); // getUndefVarName() on a numeric substitution with an undefined variable // returns that variable. LineVar.clearValue(); - UndefVar = Substitution.getUndefVarName(); + UndefVar = NumericSubstitution.getUndefVarName(); EXPECT_EQ("@LINE", UndefVar); }