Reinstate "FileCheck [5/12]: Introduce regular numeric variables"

This reinstates r360578 (git e47362c1ec1ea31b626336cc05822035601c3e57),
reverted in r360653 (git 004393681c25e34e921adccc69ae6378090dee54),
with a fix for the list added in FileCheck.rst to build without error.

Copyright:
    - Linaro (changes up to diff 183612 of revision D55940)
    - GraphCore (changes in later versions of revision D55940 and
                 in new revision created off D55940)

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

Subscribers: hiraditya, llvm-commits, probinson, dblaikie, grimar,
arichardson, tra, rnk, kristina, hfinkel, rogfer01, JonChesterfield

Tags: #llvm

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

llvm-svn: 360665
diff --git a/llvm/unittests/Support/FileCheckTest.cpp b/llvm/unittests/Support/FileCheckTest.cpp
index 2e9caa2..80120ba 100644
--- a/llvm/unittests/Support/FileCheckTest.cpp
+++ b/llvm/unittests/Support/FileCheckTest.cpp
@@ -14,6 +14,57 @@
 
 class FileCheckTest : public ::testing::Test {};
 
+TEST_F(FileCheckTest, NumericVariable) {
+  FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
+  EXPECT_EQ("FOO", FooVar.getName());
+
+  // Defined variable: getValue returns a value, setValue fails and value
+  // remains unchanged.
+  llvm::Optional<uint64_t> Value = FooVar.getValue();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ(42U, *Value);
+  EXPECT_TRUE(FooVar.setValue(43));
+  Value = FooVar.getValue();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ(42U, *Value);
+
+  // Clearing variable: getValue fails, clearValue again fails.
+  EXPECT_FALSE(FooVar.clearValue());
+  Value = FooVar.getValue();
+  EXPECT_FALSE(Value);
+  EXPECT_TRUE(FooVar.clearValue());
+
+  // Undefined variable: setValue works, getValue returns value set.
+  EXPECT_FALSE(FooVar.setValue(43));
+  Value = FooVar.getValue();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ(43U, *Value);
+}
+
+uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
+
+TEST_F(FileCheckTest, NumExpr) {
+  FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 42);
+  FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &FooVar, 18);
+
+  // Defined variable: eval returns right value, no undefined variable
+  // returned.
+  llvm::Optional<uint64_t> Value = NumExpr.eval();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ(60U, *Value);
+  StringRef UndefVar = NumExpr.getUndefVarName();
+  EXPECT_EQ("", UndefVar);
+
+  // Undefined variable: eval fails, undefined variable returned. We call
+  // getUndefVarName first to check that it can be called without calling
+  // eval() first.
+  FooVar.clearValue();
+  UndefVar = NumExpr.getUndefVarName();
+  EXPECT_EQ("FOO", UndefVar);
+  Value = NumExpr.eval();
+  EXPECT_FALSE(Value);
+}
+
 TEST_F(FileCheckTest, ValidVarNameStart) {
   EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('a'));
   EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('G'));
@@ -90,22 +141,38 @@
   EXPECT_EQ(TrailIdx, VarName.size() - 1);
 }
 
+static StringRef bufferize(SourceMgr &SM, StringRef Str) {
+  std::unique_ptr<MemoryBuffer> Buffer =
+      MemoryBuffer::getMemBufferCopy(Str, "TestBuffer");
+  StringRef StrBufferRef = Buffer->getBuffer();
+  SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
+  return StrBufferRef;
+}
+
 class ExprTester {
 private:
   SourceMgr SM;
+  FileCheckRequest Req;
   FileCheckPatternContext Context;
   FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
 
 public:
+  ExprTester() {
+    std::vector<std::string> GlobalDefines;
+    GlobalDefines.emplace_back(std::string("#FOO=42"));
+    Context.defineCmdlineVariables(GlobalDefines, SM);
+    // Call ParsePattern to have @LINE defined.
+    P.ParsePattern("N/A", "CHECK", SM, 1, Req);
+  }
+
   bool parseExpect(std::string &VarName, std::string &Trailer) {
+    bool IsPseudo = VarName[0] == '@';
     std::string NameTrailer = VarName + Trailer;
-    std::unique_ptr<MemoryBuffer> Buffer =
-        MemoryBuffer::getMemBufferCopy(NameTrailer, "TestBuffer");
-    StringRef NameTrailerRef = Buffer->getBuffer();
-    SM.AddNewSourceBuffer(std::move(Buffer), SMLoc());
+    StringRef NameTrailerRef = bufferize(SM, NameTrailer);
     StringRef VarNameRef = NameTrailerRef.substr(0, VarName.size());
     StringRef TrailerRef = NameTrailerRef.substr(VarName.size());
-    return P.parseNumericExpression(VarNameRef, TrailerRef, SM) == nullptr;
+    return P.parseNumericExpression(VarNameRef, IsPseudo, TrailerRef, SM) ==
+           nullptr;
   }
 };
 
@@ -121,6 +188,14 @@
   Trailer = "";
   EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
 
+  // Defined variable.
+  VarName = "FOO";
+  EXPECT_FALSE(Tester.parseExpect(VarName, Trailer));
+
+  // Undefined variable.
+  VarName = "UNDEF";
+  EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
+
   // Wrong Pseudovar.
   VarName = "@FOO";
   EXPECT_TRUE(Tester.parseExpect(VarName, Trailer));
@@ -153,19 +228,38 @@
   GlobalDefines.emplace_back(std::string("FOO=BAR"));
   Context.defineCmdlineVariables(GlobalDefines, SM);
 
-  FileCheckPatternSubstitution Substitution =
+  // Substitution of undefined pattern variable fails.
+  FileCheckPatternSubstitution PatternSubstitution =
       FileCheckPatternSubstitution(&Context, "VAR404", 42);
-  EXPECT_FALSE(Substitution.getResult());
+  EXPECT_FALSE(PatternSubstitution.getResult());
 
-  FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
-  Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
-  llvm::Optional<std::string> Value = Substitution.getResult();
+  // Substitutions of defined pseudo and non-pseudo numeric variables return
+  // the right value.
+  FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
+  FileCheckNumericVariable NVar = FileCheckNumericVariable("@N", 10);
+  FileCheckNumExpr NumExprLine = FileCheckNumExpr(doAdd, &LineVar, 0);
+  FileCheckNumExpr NumExprN = FileCheckNumExpr(doAdd, &NVar, 3);
+  FileCheckPatternSubstitution SubstitutionLine =
+      FileCheckPatternSubstitution(&Context, "@LINE", &NumExprLine, 12);
+  FileCheckPatternSubstitution SubstitutionN =
+      FileCheckPatternSubstitution(&Context, "N", &NumExprN, 30);
+  llvm::Optional<std::string> Value = SubstitutionLine.getResult();
   EXPECT_TRUE(Value);
   EXPECT_EQ("42", *Value);
+  Value = SubstitutionN.getResult();
+  EXPECT_TRUE(Value);
+  EXPECT_EQ("13", *Value);
 
+  // Substitution of undefined numeric variable fails.
+  LineVar.clearValue();
+  EXPECT_FALSE(SubstitutionLine.getResult());
+  NVar.clearValue();
+  EXPECT_FALSE(SubstitutionN.getResult());
+
+  // Substitution of defined pattern variable returns the right value.
   FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context);
-  Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
-  Value = Substitution.getResult();
+  PatternSubstitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
+  Value = PatternSubstitution.getResult();
   EXPECT_TRUE(Value);
   EXPECT_EQ("BAR", *Value);
 }
@@ -177,19 +271,32 @@
   GlobalDefines.emplace_back(std::string("FOO=BAR"));
   Context.defineCmdlineVariables(GlobalDefines, SM);
 
+  // getUndefVarName() on a pattern variable substitution with an undefined
+  // variable returns that variable.
   FileCheckPatternSubstitution Substitution =
       FileCheckPatternSubstitution(&Context, "VAR404", 42);
   StringRef UndefVar = Substitution.getUndefVarName();
   EXPECT_EQ("VAR404", UndefVar);
 
-  FileCheckNumExpr NumExpr = FileCheckNumExpr(42);
+  // getUndefVarName() on a pattern variable substitution with a defined
+  // variable returns an empty string.
+  Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
+  UndefVar = Substitution.getUndefVarName();
+  EXPECT_EQ("", UndefVar);
+
+  // getUndefVarName() on a numeric expression substitution with a defined
+  // variable returns an empty string.
+  FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 42);
+  FileCheckNumExpr NumExpr = FileCheckNumExpr(doAdd, &LineVar, 0);
   Substitution = FileCheckPatternSubstitution(&Context, "@LINE", &NumExpr, 12);
   UndefVar = Substitution.getUndefVarName();
   EXPECT_EQ("", UndefVar);
 
-  Substitution = FileCheckPatternSubstitution(&Context, "FOO", 42);
+  // getUndefVarName() on a numeric expression substitution with an undefined
+  // variable returns that variable.
+  LineVar.clearValue();
   UndefVar = Substitution.getUndefVarName();
-  EXPECT_EQ("", UndefVar);
+  EXPECT_EQ("@LINE", UndefVar);
 }
 
 TEST_F(FileCheckTest, FileCheckContext) {
@@ -197,36 +304,71 @@
   std::vector<std::string> GlobalDefines;
   SourceMgr SM;
 
-  // Missing equal sign
+  // Missing equal sign.
   GlobalDefines.emplace_back(std::string("LocalVar"));
   EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#LocalNumVar"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
 
-  // Empty variable
+  // Empty variable name.
   GlobalDefines.clear();
   GlobalDefines.emplace_back(std::string("=18"));
   EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#=18"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
 
-  // Invalid variable
+  // Invalid variable name.
   GlobalDefines.clear();
   GlobalDefines.emplace_back(std::string("18LocalVar=18"));
   EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+
+  // Name conflict between pattern and numeric variable.
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("LocalVar=18"));
+  GlobalDefines.emplace_back(std::string("#LocalVar=36"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  Cxt = FileCheckPatternContext();
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
+  GlobalDefines.emplace_back(std::string("LocalNumVar=36"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
+  Cxt = FileCheckPatternContext();
+
+  // Invalid numeric value for numeric variable.
+  GlobalDefines.clear();
+  GlobalDefines.emplace_back(std::string("#LocalNumVar=x"));
+  EXPECT_TRUE(Cxt.defineCmdlineVariables(GlobalDefines, SM));
 
   // Define local variables from command-line.
   GlobalDefines.clear();
   GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
   GlobalDefines.emplace_back(std::string("EmptyVar="));
+  GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
   bool GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
   EXPECT_FALSE(GotError);
 
   // Check defined variables are present and undefined is absent.
   StringRef LocalVarStr = "LocalVar";
+  StringRef LocalNumVarRef = bufferize(SM, "LocalNumVar");
   StringRef EmptyVarStr = "EmptyVar";
   StringRef UnknownVarStr = "UnknownVar";
   llvm::Optional<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
+  FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt);
+  FileCheckNumExpr *NumExpr =
+      P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM);
   llvm::Optional<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
   llvm::Optional<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
   EXPECT_TRUE(LocalVar);
   EXPECT_EQ(*LocalVar, "FOO");
+  EXPECT_TRUE(NumExpr);
+  llvm::Optional<uint64_t> NumExprVal = NumExpr->eval();
+  EXPECT_TRUE(NumExprVal);
+  EXPECT_EQ(*NumExprVal, 18U);
   EXPECT_TRUE(EmptyVar);
   EXPECT_EQ(*EmptyVar, "");
   EXPECT_FALSE(UnknownVar);
@@ -235,21 +377,46 @@
   Cxt.clearLocalVars();
   LocalVar = Cxt.getPatternVarValue(LocalVarStr);
   EXPECT_FALSE(LocalVar);
+  // Check a numeric expression's evaluation fails if called after clearing of
+  // local variables, if it was created before. This is important because local
+  // variable clearing due to --enable-var-scope happens after numeric
+  // expressions are linked to the numeric variables they use.
+  EXPECT_FALSE(NumExpr->eval());
+  P = FileCheckPattern(Check::CheckPlain, &Cxt);
+  NumExpr =
+      P.parseNumericExpression(LocalNumVarRef, false /*IsPseudo*/, "", SM);
+  EXPECT_FALSE(NumExpr);
   EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
   EXPECT_FALSE(EmptyVar);
 
   // Redefine global variables and check variables are defined again.
   GlobalDefines.emplace_back(std::string("$GlobalVar=BAR"));
+  GlobalDefines.emplace_back(std::string("#$GlobalNumVar=36"));
   GotError = Cxt.defineCmdlineVariables(GlobalDefines, SM);
   EXPECT_FALSE(GotError);
   StringRef GlobalVarStr = "$GlobalVar";
+  StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
   llvm::Optional<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
   EXPECT_TRUE(GlobalVar);
   EXPECT_EQ(*GlobalVar, "BAR");
+  P = FileCheckPattern(Check::CheckPlain, &Cxt);
+  NumExpr =
+      P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
+  EXPECT_TRUE(NumExpr);
+  NumExprVal = NumExpr->eval();
+  EXPECT_TRUE(NumExprVal);
+  EXPECT_EQ(*NumExprVal, 36U);
 
   // Clear local variables and check global variables remain defined.
   Cxt.clearLocalVars();
   GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
   EXPECT_TRUE(GlobalVar);
+  P = FileCheckPattern(Check::CheckPlain, &Cxt);
+  NumExpr =
+      P.parseNumericExpression(GlobalNumVarRef, false /*IsPseudo*/, "", SM);
+  EXPECT_TRUE(NumExpr);
+  NumExprVal = NumExpr->eval();
+  EXPECT_TRUE(NumExprVal);
+  EXPECT_EQ(*NumExprVal, 36U);
 }
 } // namespace