view unittests/Support/FileCheckTest.cpp @ 148:63bd29f05246

merged
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Wed, 14 Aug 2019 19:46:37 +0900
parents c2174574ed3a
children
line wrap: on
line source

//===- llvm/unittest/Support/FileCheckTest.cpp - FileCheck tests --===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/Support/FileCheck.h"
#include "gtest/gtest.h"
#include <unordered_set>

using namespace llvm;
namespace {

class FileCheckTest : public ::testing::Test {};

TEST_F(FileCheckTest, Literal) {
  // Eval returns the literal's value.
  FileCheckExpressionLiteral Ten(10);
  Expected<uint64_t> Value = Ten.eval();
  ASSERT_TRUE(bool(Value));
  EXPECT_EQ(10U, *Value);

  // Max value can be correctly represented.
  FileCheckExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
  Value = Max.eval();
  ASSERT_TRUE(bool(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;
}

static void
expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
                  Error Err) {
  handleAllErrors(std::move(Err), [&](const FileCheckUndefVarError &E) {
    ExpectedUndefVarNames.erase(E.getVarName());
  });
  EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
}

// Return whether Err contains any FileCheckUndefVarError whose associated name
// is not ExpectedUndefVarName.
static void expectUndefError(const Twine &ExpectedUndefVarName, Error Err) {
  expectUndefErrors({ExpectedUndefVarName.str()}, std::move(Err));
}

uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }

TEST_F(FileCheckTest, NumericVariable) {
  // Undefined variable: isValueKnownAtMatchTime returns false, getValue and
  // eval fail, error returned by eval holds the name of the undefined
  // variable.
  FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1);
  EXPECT_EQ("FOO", FooVar.getName());
  EXPECT_FALSE(FooVar.isValueKnownAtMatchTime());
  FileCheckNumericVariableUse FooVarUse =
      FileCheckNumericVariableUse("FOO", &FooVar);
  EXPECT_FALSE(FooVar.getValue());
  Expected<uint64_t> EvalResult = FooVarUse.eval();
  ASSERT_FALSE(EvalResult);
  expectUndefError("FOO", EvalResult.takeError());

  FooVar.setValue(42);

  // Defined variable: isValueKnownAtMatchTime returns true, getValue and eval
  // return value set.
  EXPECT_TRUE(FooVar.isValueKnownAtMatchTime());
  Optional<uint64_t> Value = FooVar.getValue();
  ASSERT_TRUE(bool(Value));
  EXPECT_EQ(42U, *Value);
  EvalResult = FooVarUse.eval();
  ASSERT_TRUE(bool(EvalResult));
  EXPECT_EQ(42U, *EvalResult);

  // Variable defined by numeric expression: isValueKnownAtMatchTime
  // returns true, getValue and eval return value of expression, setValue
  // clears expression.
  std::unique_ptr<FileCheckNumericVariableUse> FooVarUsePtr =
      llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
  std::unique_ptr<FileCheckExpressionLiteral> One =
      llvm::make_unique<FileCheckExpressionLiteral>(1);
  FileCheckASTBinop Binop =
      FileCheckASTBinop(doAdd, std::move(FooVarUsePtr), std::move(One));
  FileCheckNumericVariable FoobarExprVar =
      FileCheckNumericVariable("FOOBAR", 2, &Binop);
  EXPECT_TRUE(FoobarExprVar.isValueKnownAtMatchTime());
  ASSERT_FALSE(FoobarExprVar.getValue());
  FileCheckNumericVariableUse FoobarExprVarUse =
      FileCheckNumericVariableUse("FOOBAR", &FoobarExprVar);
  EvalResult = FoobarExprVarUse.eval();
  ASSERT_TRUE(bool(EvalResult));
  EXPECT_EQ(43U, *EvalResult);
  EXPECT_TRUE(FoobarExprVar.getExpressionAST());
  FoobarExprVar.setValue(43);
  EXPECT_FALSE(FoobarExprVar.getExpressionAST());
  FoobarExprVar = FileCheckNumericVariable("FOOBAR", 2, &Binop);
  EXPECT_TRUE(FoobarExprVar.getExpressionAST());

  // Clearing variable: getValue and eval fail. Error returned by eval holds
  // the name of the cleared variable.
  FooVar.clearValue();
  FoobarExprVar.clearValue();
  EXPECT_FALSE(FoobarExprVar.getExpressionAST());
  EXPECT_FALSE(FooVar.getValue());
  EXPECT_FALSE(FoobarExprVar.getValue());
  EvalResult = FooVarUse.eval();
  ASSERT_FALSE(EvalResult);
  expectUndefError("FOO", EvalResult.takeError());
  EvalResult = FoobarExprVarUse.eval();
  ASSERT_FALSE(EvalResult);
  expectUndefError("FOOBAR", EvalResult.takeError());
}

TEST_F(FileCheckTest, Binop) {
  FileCheckNumericVariable FooVar = FileCheckNumericVariable("FOO", 1);
  FooVar.setValue(42);
  std::unique_ptr<FileCheckNumericVariableUse> FooVarUse =
      llvm::make_unique<FileCheckNumericVariableUse>("FOO", &FooVar);
  FileCheckNumericVariable BarVar = FileCheckNumericVariable("BAR", 2);
  BarVar.setValue(18);
  std::unique_ptr<FileCheckNumericVariableUse> BarVarUse =
      llvm::make_unique<FileCheckNumericVariableUse>("BAR", &BarVar);
  FileCheckASTBinop Binop =
      FileCheckASTBinop(doAdd, std::move(FooVarUse), std::move(BarVarUse));

  // Defined variable: eval returns right value.
  Expected<uint64_t> Value = Binop.eval();
  ASSERT_TRUE(bool(Value));
  EXPECT_EQ(60U, *Value);

  // 1 undefined variable: eval fails, error contains name of undefined
  // variable.
  FooVar.clearValue();
  Value = Binop.eval();
  ASSERT_FALSE(Value);
  expectUndefError("FOO", Value.takeError());

  // 2 undefined variables: eval fails, error contains names of all undefined
  // variables.
  BarVar.clearValue();
  Value = Binop.eval();
  ASSERT_FALSE(Value);
  expectUndefErrors({"FOO", "BAR"}, Value.takeError());
}

TEST_F(FileCheckTest, ValidVarNameStart) {
  EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('a'));
  EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('G'));
  EXPECT_TRUE(FileCheckPattern::isValidVarNameStart('_'));
  EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('2'));
  EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('$'));
  EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('@'));
  EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('+'));
  EXPECT_FALSE(FileCheckPattern::isValidVarNameStart('-'));
  EXPECT_FALSE(FileCheckPattern::isValidVarNameStart(':'));
}

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;
}

TEST_F(FileCheckTest, ParseVar) {
  SourceMgr SM;
  StringRef OrigVarName = bufferize(SM, "GoodVar42");
  StringRef VarName = OrigVarName;
  Expected<FileCheckPattern::VariableProperties> ParsedVarResult =
      FileCheckPattern::parseVariable(VarName, SM);
  ASSERT_TRUE(bool(ParsedVarResult));
  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
  EXPECT_TRUE(VarName.empty());
  EXPECT_FALSE(ParsedVarResult->IsPseudo);

  VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  ASSERT_TRUE(bool(ParsedVarResult));
  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
  EXPECT_TRUE(VarName.empty());
  EXPECT_FALSE(ParsedVarResult->IsPseudo);

  VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  ASSERT_TRUE(bool(ParsedVarResult));
  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
  EXPECT_TRUE(VarName.empty());
  EXPECT_TRUE(ParsedVarResult->IsPseudo);

  VarName = bufferize(SM, "42BadVar");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  EXPECT_TRUE(errorToBool(ParsedVarResult.takeError()));

  VarName = bufferize(SM, "$@");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  EXPECT_TRUE(errorToBool(ParsedVarResult.takeError()));

  VarName = OrigVarName = bufferize(SM, "B@dVar");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  ASSERT_TRUE(bool(ParsedVarResult));
  EXPECT_EQ(VarName, OrigVarName.substr(1));
  EXPECT_EQ(ParsedVarResult->Name, "B");
  EXPECT_FALSE(ParsedVarResult->IsPseudo);

  VarName = OrigVarName = bufferize(SM, "B$dVar");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  ASSERT_TRUE(bool(ParsedVarResult));
  EXPECT_EQ(VarName, OrigVarName.substr(1));
  EXPECT_EQ(ParsedVarResult->Name, "B");
  EXPECT_FALSE(ParsedVarResult->IsPseudo);

  VarName = bufferize(SM, "BadVar+");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  ASSERT_TRUE(bool(ParsedVarResult));
  EXPECT_EQ(VarName, "+");
  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
  EXPECT_FALSE(ParsedVarResult->IsPseudo);

  VarName = bufferize(SM, "BadVar-");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  ASSERT_TRUE(bool(ParsedVarResult));
  EXPECT_EQ(VarName, "-");
  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
  EXPECT_FALSE(ParsedVarResult->IsPseudo);

  VarName = bufferize(SM, "BadVar:");
  ParsedVarResult = FileCheckPattern::parseVariable(VarName, SM);
  ASSERT_TRUE(bool(ParsedVarResult));
  EXPECT_EQ(VarName, ":");
  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
  EXPECT_FALSE(ParsedVarResult->IsPseudo);
}

class PatternTester {
private:
  size_t LineNumber = 1;
  SourceMgr SM;
  FileCheckRequest Req;
  FileCheckPatternContext Context;
  FileCheckPattern P =
      FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);

public:
  PatternTester() {
    std::vector<std::string> GlobalDefines;
    GlobalDefines.emplace_back(std::string("#FOO=42"));
    GlobalDefines.emplace_back(std::string("BAR=BAZ"));
    // An ASSERT_FALSE would make more sense but cannot be used in a
    // constructor.
    EXPECT_FALSE(
        errorToBool(Context.defineCmdlineVariables(GlobalDefines, SM)));
    Context.createLineVariable();
    // Call parsePattern to have @LINE defined.
    P.parsePattern("N/A", "CHECK", SM, Req);
    // parsePattern does not expect to be called twice for the same line and
    // will set FixedStr and RegExStr incorrectly if it is. Therefore prepare
    // a pattern for a different line.
    initNextPattern();
  }

  void initNextPattern() {
    P = FileCheckPattern(Check::CheckPlain, &Context, LineNumber++);
  }

  bool parseSubstExpect(StringRef Expr) {
    StringRef ExprBufferRef = bufferize(SM, Expr);
    Optional<FileCheckNumericVariable *> DefinedNumericVariable;
    return errorToBool(
        P.parseNumericSubstitutionBlock(ExprBufferRef, DefinedNumericVariable,
                                        false, LineNumber - 1, &Context, SM)
            .takeError());
  }

  bool parsePatternExpect(StringRef Pattern) {
    StringRef PatBufferRef = bufferize(SM, Pattern);
    return P.parsePattern(PatBufferRef, "CHECK", SM, Req);
  }

  bool matchExpect(StringRef Buffer) {
    StringRef BufferRef = bufferize(SM, Buffer);
    size_t MatchLen;
    return errorToBool(P.match(BufferRef, MatchLen, SM).takeError());
  }
};

TEST_F(FileCheckTest, ParseExpr) {
  PatternTester Tester;

  // Variable definition.

  // Definition of invalid variable.
  EXPECT_TRUE(Tester.parseSubstExpect("10VAR:"));
  EXPECT_TRUE(Tester.parseSubstExpect("@FOO:"));
  EXPECT_TRUE(Tester.parseSubstExpect("@LINE:"));

  // Conflict with pattern variable.
  EXPECT_TRUE(Tester.parseSubstExpect("BAR:"));

  // Garbage after name of variable being defined.
  EXPECT_TRUE(Tester.parseSubstExpect("VAR GARBAGE:"));

  // Acceptable variable definition.
  EXPECT_FALSE(Tester.parseSubstExpect("VAR1:"));
  EXPECT_FALSE(Tester.parseSubstExpect("  VAR2:"));
  EXPECT_FALSE(Tester.parseSubstExpect("VAR3  :"));
  EXPECT_FALSE(Tester.parseSubstExpect("VAR3:  "));
  EXPECT_FALSE(Tester.parsePatternExpect("[[#FOOBAR: FOO+1]]"));

  // Numeric expression.

  // Unacceptable variable.
  EXPECT_TRUE(Tester.parseSubstExpect("10VAR"));
  EXPECT_TRUE(Tester.parseSubstExpect("@FOO"));

  // Only valid variable.
  EXPECT_FALSE(Tester.parseSubstExpect("@LINE"));
  EXPECT_FALSE(Tester.parseSubstExpect("FOO"));
  EXPECT_FALSE(Tester.parseSubstExpect("UNDEF"));

  // Valid empty expression.
  EXPECT_FALSE(Tester.parseSubstExpect(""));

  // Valid use of variable defined on the same line from expression. Note that
  // the same pattern object is used for the parsePatternExpect and
  // parseSubstExpect since no initNextPattern is called, thus appearing as
  // being on the same line from the pattern's point of view.
  ASSERT_FALSE(Tester.parsePatternExpect("[[#LINE1VAR:FOO+1]]"));
  EXPECT_FALSE(Tester.parseSubstExpect("LINE1VAR"));

  // Invalid use of variable defined on same line from input. As above, the
  // absence of a call to initNextPattern makes it appear to be on the same
  // line from the pattern's point of view.
  ASSERT_FALSE(Tester.parsePatternExpect("[[#LINE2VAR:]]"));
  EXPECT_TRUE(Tester.parseSubstExpect("LINE2VAR"));

  // Unsupported operator.
  EXPECT_TRUE(Tester.parseSubstExpect("@LINE/2"));

  // Missing offset operand.
  EXPECT_TRUE(Tester.parseSubstExpect("@LINE+"));

  // Valid expression.
  EXPECT_FALSE(Tester.parseSubstExpect("@LINE+5"));
  EXPECT_FALSE(Tester.parseSubstExpect("FOO+4"));
  EXPECT_FALSE(Tester.parseSubstExpect("FOOBAR"));
  Tester.initNextPattern();
  EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+FOO]]"));
  EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO+3-FOO]]"));
}

TEST_F(FileCheckTest, ParsePattern) {
  PatternTester Tester;

  // Space in pattern variable expression.
  EXPECT_TRUE(Tester.parsePatternExpect("[[ BAR]]"));

  // Invalid variable name.
  EXPECT_TRUE(Tester.parsePatternExpect("[[42INVALID]]"));

  // Invalid pattern variable definition.
  EXPECT_TRUE(Tester.parsePatternExpect("[[@PAT:]]"));
  EXPECT_TRUE(Tester.parsePatternExpect("[[PAT+2:]]"));

  // Collision with numeric variable.
  EXPECT_TRUE(Tester.parsePatternExpect("[[FOO:]]"));

  // Valid use of pattern variable.
  EXPECT_FALSE(Tester.parsePatternExpect("[[BAR]]"));

  // Valid pattern variable definition.
  EXPECT_FALSE(Tester.parsePatternExpect("[[PAT:[0-9]+]]"));

  // Invalid numeric expressions.
  EXPECT_TRUE(Tester.parsePatternExpect("[[#42INVALID]]"));
  EXPECT_TRUE(Tester.parsePatternExpect("[[#@FOO]]"));
  EXPECT_TRUE(Tester.parsePatternExpect("[[#@LINE/2]]"));

  // Valid numeric expressions and numeric variable definition.
  EXPECT_FALSE(Tester.parsePatternExpect("[[#FOO]]"));
  EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE+2]]"));
  EXPECT_FALSE(Tester.parsePatternExpect("[[#NUMVAR:]]"));
}

TEST_F(FileCheckTest, Match) {
  PatternTester Tester;

  // Check matching an empty expression only matches a number.
  Tester.parsePatternExpect("[[#]]");
  EXPECT_TRUE(Tester.matchExpect("FAIL"));
  EXPECT_FALSE(Tester.matchExpect("18"));

  // Check matching a definition only matches a number.
  Tester.initNextPattern();
  Tester.parsePatternExpect("[[#NUMVAR:]]");
  EXPECT_TRUE(Tester.matchExpect("FAIL"));
  EXPECT_TRUE(Tester.matchExpect(""));
  EXPECT_FALSE(Tester.matchExpect("18"));

  // Check matching the variable defined matches the correct number only
  Tester.initNextPattern();
  Tester.parsePatternExpect("[[#NUMVAR]] [[#NUMVAR+2]]");
  EXPECT_TRUE(Tester.matchExpect("19 21"));
  EXPECT_TRUE(Tester.matchExpect("18 21"));
  EXPECT_FALSE(Tester.matchExpect("18 20"));

  // Check matching a numeric expression using @LINE after match failure uses
  // the correct value for @LINE.
  Tester.initNextPattern();
  EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]"));
  // Ok, @LINE is 5 now.
  EXPECT_FALSE(Tester.matchExpect("5"));
  Tester.initNextPattern();
  // @LINE is now 6, match with substitution failure.
  EXPECT_FALSE(Tester.parsePatternExpect("[[#UNKNOWN]]"));
  EXPECT_TRUE(Tester.matchExpect("FOO"));
  Tester.initNextPattern();
  // Check that @LINE is 7 as expected.
  EXPECT_FALSE(Tester.parsePatternExpect("[[#@LINE]]"));
  EXPECT_FALSE(Tester.matchExpect("7"));
}

TEST_F(FileCheckTest, Substitution) {
  SourceMgr SM;
  FileCheckPatternContext Context;
  std::vector<std::string> GlobalDefines;
  GlobalDefines.emplace_back(std::string("FOO=BAR"));
  EXPECT_FALSE(errorToBool(Context.defineCmdlineVariables(GlobalDefines, SM)));

  // Substitution of an undefined string variable fails and error holds that
  // variable's name.
  FileCheckStringSubstitution StringSubstitution =
      FileCheckStringSubstitution(&Context, "VAR404", 42);
  Expected<std::string> SubstValue = StringSubstitution.getResult();
  ASSERT_FALSE(bool(SubstValue));
  expectUndefError("VAR404", SubstValue.takeError());

  // Substitutions of defined pseudo and non-pseudo numeric variables return
  // the right value.
  FileCheckNumericVariable LineVar = FileCheckNumericVariable("@LINE", 1);
  FileCheckNumericVariable NVar = FileCheckNumericVariable("N", 1);
  LineVar.setValue(42);
  NVar.setValue(10);
  auto LineVarUse =
      llvm::make_unique<FileCheckNumericVariableUse>("@LINE", &LineVar);
  auto NVarUse = llvm::make_unique<FileCheckNumericVariableUse>("N", &NVar);
  FileCheckNumericSubstitution SubstitutionLine = FileCheckNumericSubstitution(
      &Context, "@LINE", std::move(LineVarUse), 12);
  FileCheckNumericSubstitution SubstitutionN =
      FileCheckNumericSubstitution(&Context, "N", std::move(NVarUse), 30);
  SubstValue = SubstitutionLine.getResult();
  ASSERT_TRUE(bool(SubstValue));
  EXPECT_EQ("42", *SubstValue);
  SubstValue = SubstitutionN.getResult();
  ASSERT_TRUE(bool(SubstValue));
  EXPECT_EQ("10", *SubstValue);

  // Substitution of an undefined numeric variable fails, error holds name of
  // undefined variable.
  LineVar.clearValue();
  SubstValue = SubstitutionLine.getResult();
  ASSERT_FALSE(bool(SubstValue));
  expectUndefError("@LINE", SubstValue.takeError());
  NVar.clearValue();
  SubstValue = SubstitutionN.getResult();
  ASSERT_FALSE(bool(SubstValue));
  expectUndefError("N", SubstValue.takeError());

  // Substitution of a defined string variable returns the right value.
  FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Context, 1);
  StringSubstitution = FileCheckStringSubstitution(&Context, "FOO", 42);
  SubstValue = StringSubstitution.getResult();
  ASSERT_TRUE(bool(SubstValue));
  EXPECT_EQ("BAR", *SubstValue);
}

TEST_F(FileCheckTest, FileCheckContext) {
  FileCheckPatternContext Cxt = FileCheckPatternContext();
  std::vector<std::string> GlobalDefines;
  SourceMgr SM;

  // Missing equal sign.
  GlobalDefines.emplace_back(std::string("LocalVar"));
  EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
  GlobalDefines.clear();
  GlobalDefines.emplace_back(std::string("#LocalNumVar"));
  EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));

  // Empty variable name.
  GlobalDefines.clear();
  GlobalDefines.emplace_back(std::string("=18"));
  EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
  GlobalDefines.clear();
  GlobalDefines.emplace_back(std::string("#=18"));
  EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));

  // Invalid variable name.
  GlobalDefines.clear();
  GlobalDefines.emplace_back(std::string("18LocalVar=18"));
  EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
  GlobalDefines.clear();
  GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
  EXPECT_TRUE(errorToBool(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(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
  Cxt = FileCheckPatternContext();
  GlobalDefines.clear();
  GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
  GlobalDefines.emplace_back(std::string("LocalNumVar=36"));
  EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
  Cxt = FileCheckPatternContext();

  // Invalid numeric value for numeric variable.
  GlobalDefines.clear();
  GlobalDefines.emplace_back(std::string("#LocalNumVar=x"));
  EXPECT_TRUE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));

  // Define local variables from command-line.
  GlobalDefines.clear();
  // Clear local variables to remove dummy numeric variable x that
  // parseNumericSubstitutionBlock would have created and stored in
  // GlobalNumericVariableTable.
  Cxt.clearLocalVars();
  GlobalDefines.emplace_back(std::string("LocalVar=FOO"));
  GlobalDefines.emplace_back(std::string("EmptyVar="));
  GlobalDefines.emplace_back(std::string("#LocalNumVar1=18"));
  GlobalDefines.emplace_back(std::string("#LocalNumVar2=LocalNumVar1+2"));
  ASSERT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));

  // Check defined variables are present and undefined is absent.
  StringRef LocalVarStr = "LocalVar";
  StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
  StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
  StringRef EmptyVarStr = "EmptyVar";
  StringRef UnknownVarStr = "UnknownVar";
  Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
  FileCheckPattern P = FileCheckPattern(Check::CheckPlain, &Cxt, 1);
  Optional<FileCheckNumericVariable *> DefinedNumericVariable;
  Expected<std::unique_ptr<FileCheckExpressionAST>> ExpressionAST =
      P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
                                      /*IsLegacyLineExpr=*/false,
                                      /*LineNumber=*/1, &Cxt, SM);
  ASSERT_TRUE(bool(LocalVar));
  EXPECT_EQ(*LocalVar, "FOO");
  Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
  Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
  ASSERT_TRUE(bool(ExpressionAST));
  Expected<uint64_t> ExpressionVal = (*ExpressionAST)->eval();
  ASSERT_TRUE(bool(ExpressionVal));
  EXPECT_EQ(*ExpressionVal, 18U);
  ExpressionAST =
      P.parseNumericSubstitutionBlock(LocalNumVar2Ref, DefinedNumericVariable,
                                      /*IsLegacyLineExpr=*/false,
                                      /*LineNumber=*/1, &Cxt, SM);
  ASSERT_TRUE(bool(ExpressionAST));
  ExpressionVal = (*ExpressionAST)->eval();
  ASSERT_TRUE(bool(ExpressionVal));
  EXPECT_EQ(*ExpressionVal, 20U);
  ASSERT_TRUE(bool(EmptyVar));
  EXPECT_EQ(*EmptyVar, "");
  EXPECT_TRUE(errorToBool(UnknownVar.takeError()));

  // Clear local variables and check they become absent.
  Cxt.clearLocalVars();
  LocalVar = Cxt.getPatternVarValue(LocalVarStr);
  EXPECT_TRUE(errorToBool(LocalVar.takeError()));
  // 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_TRUE(errorToBool((*ExpressionAST)->eval().takeError()));
  P = FileCheckPattern(Check::CheckPlain, &Cxt, 2);
  ExpressionAST = P.parseNumericSubstitutionBlock(
      LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
      /*LineNumber=*/2, &Cxt, SM);
  ASSERT_TRUE(bool(ExpressionAST));
  ExpressionVal = (*ExpressionAST)->eval();
  EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
  ExpressionAST = P.parseNumericSubstitutionBlock(
      LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
      /*LineNumber=*/2, &Cxt, SM);
  ASSERT_TRUE(bool(ExpressionAST));
  ExpressionVal = (*ExpressionAST)->eval();
  EXPECT_TRUE(errorToBool(ExpressionVal.takeError()));
  EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
  EXPECT_TRUE(errorToBool(EmptyVar.takeError()));
  // Clear again because parseNumericSubstitutionBlock would have created a
  // dummy variable and stored it in GlobalNumericVariableTable.
  Cxt.clearLocalVars();

  // Redefine global variables and check variables are defined again.
  GlobalDefines.emplace_back(std::string("$GlobalVar=BAR"));
  GlobalDefines.emplace_back(std::string("#$GlobalNumVar=36"));
  ASSERT_FALSE(errorToBool(Cxt.defineCmdlineVariables(GlobalDefines, SM)));
  StringRef GlobalVarStr = "$GlobalVar";
  StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
  Expected<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
  ASSERT_TRUE(bool(GlobalVar));
  EXPECT_EQ(*GlobalVar, "BAR");
  P = FileCheckPattern(Check::CheckPlain, &Cxt, 3);
  ExpressionAST = P.parseNumericSubstitutionBlock(
      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
      /*LineNumber=*/3, &Cxt, SM);
  ASSERT_TRUE(bool(ExpressionAST));
  ExpressionVal = (*ExpressionAST)->eval();
  ASSERT_TRUE(bool(ExpressionVal));
  EXPECT_EQ(*ExpressionVal, 36U);

  // Clear local variables and check global variables remain defined.
  Cxt.clearLocalVars();
  EXPECT_FALSE(errorToBool(Cxt.getPatternVarValue(GlobalVarStr).takeError()));
  P = FileCheckPattern(Check::CheckPlain, &Cxt, 4);
  ExpressionAST = P.parseNumericSubstitutionBlock(
      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
      /*LineNumber=*/4, &Cxt, SM);
  ASSERT_TRUE(bool(ExpressionAST));
  ExpressionVal = (*ExpressionAST)->eval();
  ASSERT_TRUE(bool(ExpressionVal));
  EXPECT_EQ(*ExpressionVal, 36U);
}
} // namespace