forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			1002 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			1002 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C++
		
	
	
	
//===- 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 "../lib/Support/FileCheckImpl.h"
 | 
						|
#include "llvm/Support/Regex.h"
 | 
						|
#include "llvm/Testing/Support/Error.h"
 | 
						|
#include "gtest/gtest.h"
 | 
						|
#include <tuple>
 | 
						|
#include <unordered_set>
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class FileCheckTest : public ::testing::Test {};
 | 
						|
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
template <typename ErrorT>
 | 
						|
static void expectError(StringRef ExpectedMsg, Error Err) {
 | 
						|
  bool ErrorHandled = false;
 | 
						|
  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) {
 | 
						|
  expectError<ErrorDiagnostic>(ExpectedMsg, std::move(Err));
 | 
						|
}
 | 
						|
 | 
						|
struct ExpressionFormatParameterisedFixture
 | 
						|
    : public ::testing::TestWithParam<
 | 
						|
          std::tuple<ExpressionFormat::Kind, bool, bool>> {
 | 
						|
  void SetUp() { std::tie(Kind, AllowHex, AllowUpperHex) = GetParam(); }
 | 
						|
 | 
						|
  ExpressionFormat::Kind Kind;
 | 
						|
  bool AllowHex;
 | 
						|
  bool AllowUpperHex;
 | 
						|
};
 | 
						|
 | 
						|
TEST_P(ExpressionFormatParameterisedFixture, Format) {
 | 
						|
  SourceMgr SM;
 | 
						|
  ExpressionFormat Format(Kind);
 | 
						|
 | 
						|
  Expected<StringRef> WildcardPattern = Format.getWildcardRegex();
 | 
						|
  ASSERT_THAT_EXPECTED(WildcardPattern, Succeeded());
 | 
						|
  Regex WildcardRegex(*WildcardPattern);
 | 
						|
  ASSERT_TRUE(WildcardRegex.isValid());
 | 
						|
  // Does not match empty string.
 | 
						|
  EXPECT_FALSE(WildcardRegex.match(""));
 | 
						|
  // Matches all decimal digits and matches several of them.
 | 
						|
  SmallVector<StringRef, 4> Matches;
 | 
						|
  StringRef DecimalDigits = "0123456789";
 | 
						|
  ASSERT_TRUE(WildcardRegex.match(DecimalDigits, &Matches));
 | 
						|
  EXPECT_EQ(Matches[0], DecimalDigits);
 | 
						|
  // Check non digits or digits with wrong casing are not matched.
 | 
						|
  if (AllowHex) {
 | 
						|
    StringRef HexOnlyDigits[] = {"abcdef", "ABCDEF"};
 | 
						|
    StringRef AcceptedHexOnlyDigits =
 | 
						|
        AllowUpperHex ? HexOnlyDigits[1] : HexOnlyDigits[0];
 | 
						|
    StringRef RefusedHexOnlyDigits =
 | 
						|
        AllowUpperHex ? HexOnlyDigits[0] : HexOnlyDigits[1];
 | 
						|
    ASSERT_TRUE(WildcardRegex.match(AcceptedHexOnlyDigits, &Matches));
 | 
						|
    EXPECT_EQ(Matches[0], AcceptedHexOnlyDigits);
 | 
						|
    EXPECT_FALSE(WildcardRegex.match(RefusedHexOnlyDigits));
 | 
						|
 | 
						|
    EXPECT_FALSE(WildcardRegex.match("g"));
 | 
						|
    EXPECT_FALSE(WildcardRegex.match("G"));
 | 
						|
  } else {
 | 
						|
    EXPECT_FALSE(WildcardRegex.match("a"));
 | 
						|
    EXPECT_FALSE(WildcardRegex.match("A"));
 | 
						|
  }
 | 
						|
 | 
						|
  Expected<std::string> MatchingString = Format.getMatchingString(0U);
 | 
						|
  ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
 | 
						|
  EXPECT_EQ(*MatchingString, "0");
 | 
						|
  MatchingString = Format.getMatchingString(9U);
 | 
						|
  ASSERT_THAT_EXPECTED(MatchingString, Succeeded());
 | 
						|
  EXPECT_EQ(*MatchingString, "9");
 | 
						|
  Expected<std::string> TenMatchingString = Format.getMatchingString(10U);
 | 
						|
  ASSERT_THAT_EXPECTED(TenMatchingString, Succeeded());
 | 
						|
  Expected<std::string> FifteenMatchingString = Format.getMatchingString(15U);
 | 
						|
  ASSERT_THAT_EXPECTED(FifteenMatchingString, Succeeded());
 | 
						|
  StringRef ExpectedTenMatchingString, ExpectedFifteenMatchingString;
 | 
						|
  if (AllowHex) {
 | 
						|
    if (AllowUpperHex) {
 | 
						|
      ExpectedTenMatchingString = "A";
 | 
						|
      ExpectedFifteenMatchingString = "F";
 | 
						|
    } else {
 | 
						|
      ExpectedTenMatchingString = "a";
 | 
						|
      ExpectedFifteenMatchingString = "f";
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    ExpectedTenMatchingString = "10";
 | 
						|
    ExpectedFifteenMatchingString = "15";
 | 
						|
  }
 | 
						|
  EXPECT_EQ(*TenMatchingString, ExpectedTenMatchingString);
 | 
						|
  EXPECT_EQ(*FifteenMatchingString, ExpectedFifteenMatchingString);
 | 
						|
 | 
						|
  StringRef BufferizedValidValueStr = bufferize(SM, "0");
 | 
						|
  Expected<uint64_t> Val =
 | 
						|
      Format.valueFromStringRepr(BufferizedValidValueStr, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(Val, Succeeded());
 | 
						|
  EXPECT_EQ(*Val, 0U);
 | 
						|
  BufferizedValidValueStr = bufferize(SM, "9");
 | 
						|
  Val = Format.valueFromStringRepr(BufferizedValidValueStr, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(Val, Succeeded());
 | 
						|
  EXPECT_EQ(*Val, 9U);
 | 
						|
  StringRef BufferizedTenStr, BufferizedInvalidTenStr, BufferizedFifteenStr;
 | 
						|
  StringRef TenStr, FifteenStr, InvalidTenStr;
 | 
						|
  if (AllowHex) {
 | 
						|
    if (AllowUpperHex) {
 | 
						|
      TenStr = "A";
 | 
						|
      FifteenStr = "F";
 | 
						|
      InvalidTenStr = "a";
 | 
						|
    } else {
 | 
						|
      TenStr = "a";
 | 
						|
      FifteenStr = "f";
 | 
						|
      InvalidTenStr = "A";
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    TenStr = "10";
 | 
						|
    FifteenStr = "15";
 | 
						|
    InvalidTenStr = "A";
 | 
						|
  }
 | 
						|
  BufferizedTenStr = bufferize(SM, TenStr);
 | 
						|
  Val = Format.valueFromStringRepr(BufferizedTenStr, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(Val, Succeeded());
 | 
						|
  EXPECT_EQ(*Val, 10U);
 | 
						|
  BufferizedFifteenStr = bufferize(SM, FifteenStr);
 | 
						|
  Val = Format.valueFromStringRepr(BufferizedFifteenStr, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(Val, Succeeded());
 | 
						|
  EXPECT_EQ(*Val, 15U);
 | 
						|
  // Wrong casing is not tested because valueFromStringRepr() relies on
 | 
						|
  // StringRef's getAsInteger() which does not allow to restrict casing.
 | 
						|
  BufferizedInvalidTenStr = bufferize(SM, InvalidTenStr);
 | 
						|
  expectDiagnosticError(
 | 
						|
      "unable to represent numeric value",
 | 
						|
      Format.valueFromStringRepr(bufferize(SM, "G"), SM).takeError());
 | 
						|
 | 
						|
  // Check boolean operator.
 | 
						|
  EXPECT_TRUE(bool(Format));
 | 
						|
}
 | 
						|
 | 
						|
INSTANTIATE_TEST_CASE_P(
 | 
						|
    AllowedExplicitExpressionFormat, ExpressionFormatParameterisedFixture,
 | 
						|
    ::testing::Values(
 | 
						|
        std::make_tuple(ExpressionFormat::Kind::Unsigned, /*AllowHex=*/false,
 | 
						|
                        /*AllowUpperHex=*/false),
 | 
						|
        std::make_tuple(ExpressionFormat::Kind::HexLower, /*AllowHex=*/true,
 | 
						|
                        /*AllowUpperHex=*/false),
 | 
						|
        std::make_tuple(ExpressionFormat::Kind::HexUpper, /*AllowHex=*/true,
 | 
						|
                        /*AllowUpperHex=*/true)), );
 | 
						|
 | 
						|
TEST_F(FileCheckTest, NoFormatProperties) {
 | 
						|
  ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
 | 
						|
  expectError<StringError>("trying to match value with invalid format",
 | 
						|
                           NoFormat.getWildcardRegex().takeError());
 | 
						|
  expectError<StringError>("trying to match value with invalid format",
 | 
						|
                           NoFormat.getMatchingString(18).takeError());
 | 
						|
  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) {
 | 
						|
  ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
 | 
						|
  ExpressionFormat UnsignedFormat2(ExpressionFormat::Kind::Unsigned);
 | 
						|
  EXPECT_TRUE(UnsignedFormat == UnsignedFormat2);
 | 
						|
  EXPECT_FALSE(UnsignedFormat != UnsignedFormat2);
 | 
						|
 | 
						|
  ExpressionFormat HexLowerFormat(ExpressionFormat::Kind::HexLower);
 | 
						|
  EXPECT_FALSE(UnsignedFormat == HexLowerFormat);
 | 
						|
  EXPECT_TRUE(UnsignedFormat != HexLowerFormat);
 | 
						|
 | 
						|
  ExpressionFormat NoFormat(ExpressionFormat::Kind::NoFormat);
 | 
						|
  ExpressionFormat NoFormat2(ExpressionFormat::Kind::NoFormat);
 | 
						|
  EXPECT_FALSE(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) {
 | 
						|
  ExpressionFormat UnsignedFormat(ExpressionFormat::Kind::Unsigned);
 | 
						|
  EXPECT_TRUE(UnsignedFormat == ExpressionFormat::Kind::Unsigned);
 | 
						|
  EXPECT_FALSE(UnsignedFormat != ExpressionFormat::Kind::Unsigned);
 | 
						|
  EXPECT_FALSE(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);
 | 
						|
  EXPECT_TRUE(NoFormat == ExpressionFormat::Kind::NoFormat);
 | 
						|
  EXPECT_FALSE(NoFormat != ExpressionFormat::Kind::NoFormat);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(FileCheckTest, Literal) {
 | 
						|
  // Eval returns the literal's value.
 | 
						|
  ExpressionLiteral Ten(10);
 | 
						|
  Expected<uint64_t> Value = Ten.eval();
 | 
						|
  ASSERT_THAT_EXPECTED(Value, Succeeded());
 | 
						|
  EXPECT_EQ(10U, *Value);
 | 
						|
  EXPECT_EQ(Ten.getImplicitFormat(), ExpressionFormat::Kind::NoFormat);
 | 
						|
 | 
						|
  // Max value can be correctly represented.
 | 
						|
  ExpressionLiteral Max(std::numeric_limits<uint64_t>::max());
 | 
						|
  Value = Max.eval();
 | 
						|
  ASSERT_THAT_EXPECTED(Value, Succeeded());
 | 
						|
  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) {
 | 
						|
  std::unique_ptr<ExpressionLiteral> Ten =
 | 
						|
      std::make_unique<ExpressionLiteral>(10);
 | 
						|
  ExpressionLiteral *TenPtr = Ten.get();
 | 
						|
  Expression Expr(std::move(Ten),
 | 
						|
                  ExpressionFormat(ExpressionFormat::Kind::HexLower));
 | 
						|
  EXPECT_EQ(Expr.getAST(), TenPtr);
 | 
						|
  EXPECT_EQ(Expr.getFormat(), ExpressionFormat::Kind::HexLower);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
expectUndefErrors(std::unordered_set<std::string> ExpectedUndefVarNames,
 | 
						|
                  Error Err) {
 | 
						|
  EXPECT_THAT_ERROR(handleErrors(std::move(Err),
 | 
						|
                                 [&](const UndefVarError &E) {
 | 
						|
                                   EXPECT_EQ(ExpectedUndefVarNames.erase(
 | 
						|
                                                 std::string(E.getVarName())),
 | 
						|
                                             1U);
 | 
						|
                                 }),
 | 
						|
                    Succeeded());
 | 
						|
  EXPECT_TRUE(ExpectedUndefVarNames.empty()) << toString(ExpectedUndefVarNames);
 | 
						|
}
 | 
						|
 | 
						|
uint64_t doAdd(uint64_t OpL, uint64_t OpR) { return OpL + OpR; }
 | 
						|
 | 
						|
TEST_F(FileCheckTest, NumericVariable) {
 | 
						|
  // Undefined variable: getValue and eval fail, error returned by eval holds
 | 
						|
  // the name of the undefined variable.
 | 
						|
  NumericVariable FooVar("FOO",
 | 
						|
                         ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
 | 
						|
  EXPECT_EQ("FOO", FooVar.getName());
 | 
						|
  EXPECT_EQ(FooVar.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
 | 
						|
  NumericVariableUse FooVarUse("FOO", &FooVar);
 | 
						|
  EXPECT_EQ(FooVarUse.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
 | 
						|
  EXPECT_FALSE(FooVar.getValue());
 | 
						|
  Expected<uint64_t> EvalResult = FooVarUse.eval();
 | 
						|
  expectUndefErrors({"FOO"}, EvalResult.takeError());
 | 
						|
 | 
						|
  FooVar.setValue(42);
 | 
						|
 | 
						|
  // Defined variable: getValue and eval return value set.
 | 
						|
  Optional<uint64_t> Value = FooVar.getValue();
 | 
						|
  ASSERT_TRUE(Value);
 | 
						|
  EXPECT_EQ(42U, *Value);
 | 
						|
  EvalResult = FooVarUse.eval();
 | 
						|
  ASSERT_THAT_EXPECTED(EvalResult, Succeeded());
 | 
						|
  EXPECT_EQ(42U, *EvalResult);
 | 
						|
 | 
						|
  // Clearing variable: getValue and eval fail. Error returned by eval holds
 | 
						|
  // the name of the cleared variable.
 | 
						|
  FooVar.clearValue();
 | 
						|
  EXPECT_FALSE(FooVar.getValue());
 | 
						|
  EvalResult = FooVarUse.eval();
 | 
						|
  expectUndefErrors({"FOO"}, EvalResult.takeError());
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(FileCheckTest, Binop) {
 | 
						|
  NumericVariable FooVar("FOO",
 | 
						|
                         ExpressionFormat(ExpressionFormat::Kind::Unsigned), 1);
 | 
						|
  FooVar.setValue(42);
 | 
						|
  std::unique_ptr<NumericVariableUse> FooVarUse =
 | 
						|
      std::make_unique<NumericVariableUse>("FOO", &FooVar);
 | 
						|
  NumericVariable BarVar("BAR",
 | 
						|
                         ExpressionFormat(ExpressionFormat::Kind::Unsigned), 2);
 | 
						|
  BarVar.setValue(18);
 | 
						|
  std::unique_ptr<NumericVariableUse> BarVarUse =
 | 
						|
      std::make_unique<NumericVariableUse>("BAR", &BarVar);
 | 
						|
  BinaryOperation Binop(doAdd, std::move(FooVarUse), std::move(BarVarUse));
 | 
						|
 | 
						|
  // Defined variables: eval returns right value; implicit format is as
 | 
						|
  // expected.
 | 
						|
  Expected<uint64_t> Value = Binop.eval();
 | 
						|
  ASSERT_THAT_EXPECTED(Value, Succeeded());
 | 
						|
  EXPECT_EQ(60U, *Value);
 | 
						|
  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
 | 
						|
 | 
						|
  // 1 undefined variable: eval fails, error contains name of undefined
 | 
						|
  // variable.
 | 
						|
  FooVar.clearValue();
 | 
						|
  Value = Binop.eval();
 | 
						|
  expectUndefErrors({"FOO"}, Value.takeError());
 | 
						|
 | 
						|
  // 2 undefined variables: eval fails, error contains names of all undefined
 | 
						|
  // variables.
 | 
						|
  BarVar.clearValue();
 | 
						|
  Value = Binop.eval();
 | 
						|
  expectUndefErrors({"FOO", "BAR"}, Value.takeError());
 | 
						|
 | 
						|
  // Literal + Variable has format of variable.
 | 
						|
  FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
 | 
						|
  std::unique_ptr<ExpressionLiteral> Eighteen =
 | 
						|
      std::make_unique<ExpressionLiteral>(18);
 | 
						|
  Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(Eighteen));
 | 
						|
  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
 | 
						|
  FooVarUse = std::make_unique<NumericVariableUse>("FOO", &FooVar);
 | 
						|
  Eighteen = std::make_unique<ExpressionLiteral>(18);
 | 
						|
  Binop = BinaryOperation(doAdd, std::move(Eighteen), std::move(FooVarUse));
 | 
						|
  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Unsigned);
 | 
						|
 | 
						|
  // Variables with different implicit format conflict.
 | 
						|
  NumericVariable BazVar("BAZ",
 | 
						|
                         ExpressionFormat(ExpressionFormat::Kind::HexLower), 3);
 | 
						|
  FooVarUse = std::make_unique<NumericVariableUse>("BAZ", &FooVar);
 | 
						|
  std::unique_ptr<NumericVariableUse> BazVarUse =
 | 
						|
      std::make_unique<NumericVariableUse>("BAZ", &BazVar);
 | 
						|
  Binop = BinaryOperation(doAdd, std::move(FooVarUse), std::move(BazVarUse));
 | 
						|
  EXPECT_EQ(Binop.getImplicitFormat(), ExpressionFormat::Kind::Conflict);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(FileCheckTest, ValidVarNameStart) {
 | 
						|
  EXPECT_TRUE(Pattern::isValidVarNameStart('a'));
 | 
						|
  EXPECT_TRUE(Pattern::isValidVarNameStart('G'));
 | 
						|
  EXPECT_TRUE(Pattern::isValidVarNameStart('_'));
 | 
						|
  EXPECT_FALSE(Pattern::isValidVarNameStart('2'));
 | 
						|
  EXPECT_FALSE(Pattern::isValidVarNameStart('$'));
 | 
						|
  EXPECT_FALSE(Pattern::isValidVarNameStart('@'));
 | 
						|
  EXPECT_FALSE(Pattern::isValidVarNameStart('+'));
 | 
						|
  EXPECT_FALSE(Pattern::isValidVarNameStart('-'));
 | 
						|
  EXPECT_FALSE(Pattern::isValidVarNameStart(':'));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(FileCheckTest, ParseVar) {
 | 
						|
  SourceMgr SM;
 | 
						|
  StringRef OrigVarName = bufferize(SM, "GoodVar42");
 | 
						|
  StringRef VarName = OrigVarName;
 | 
						|
  Expected<Pattern::VariableProperties> ParsedVarResult =
 | 
						|
      Pattern::parseVariable(VarName, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
 | 
						|
  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
 | 
						|
  EXPECT_TRUE(VarName.empty());
 | 
						|
  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 | 
						|
 | 
						|
  VarName = OrigVarName = bufferize(SM, "$GoodGlobalVar");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
 | 
						|
  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
 | 
						|
  EXPECT_TRUE(VarName.empty());
 | 
						|
  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 | 
						|
 | 
						|
  VarName = OrigVarName = bufferize(SM, "@GoodPseudoVar");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
 | 
						|
  EXPECT_EQ(ParsedVarResult->Name, OrigVarName);
 | 
						|
  EXPECT_TRUE(VarName.empty());
 | 
						|
  EXPECT_TRUE(ParsedVarResult->IsPseudo);
 | 
						|
 | 
						|
  VarName = bufferize(SM, "42BadVar");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  expectDiagnosticError("invalid variable name", ParsedVarResult.takeError());
 | 
						|
 | 
						|
  VarName = bufferize(SM, "$@");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  expectDiagnosticError("invalid variable name", ParsedVarResult.takeError());
 | 
						|
 | 
						|
  VarName = OrigVarName = bufferize(SM, "B@dVar");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
 | 
						|
  EXPECT_EQ(VarName, OrigVarName.substr(1));
 | 
						|
  EXPECT_EQ(ParsedVarResult->Name, "B");
 | 
						|
  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 | 
						|
 | 
						|
  VarName = OrigVarName = bufferize(SM, "B$dVar");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
 | 
						|
  EXPECT_EQ(VarName, OrigVarName.substr(1));
 | 
						|
  EXPECT_EQ(ParsedVarResult->Name, "B");
 | 
						|
  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 | 
						|
 | 
						|
  VarName = bufferize(SM, "BadVar+");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
 | 
						|
  EXPECT_EQ(VarName, "+");
 | 
						|
  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
 | 
						|
  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 | 
						|
 | 
						|
  VarName = bufferize(SM, "BadVar-");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
 | 
						|
  EXPECT_EQ(VarName, "-");
 | 
						|
  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
 | 
						|
  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 | 
						|
 | 
						|
  VarName = bufferize(SM, "BadVar:");
 | 
						|
  ParsedVarResult = Pattern::parseVariable(VarName, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ParsedVarResult, Succeeded());
 | 
						|
  EXPECT_EQ(VarName, ":");
 | 
						|
  EXPECT_EQ(ParsedVarResult->Name, "BadVar");
 | 
						|
  EXPECT_FALSE(ParsedVarResult->IsPseudo);
 | 
						|
}
 | 
						|
 | 
						|
static void expectNotFoundError(Error Err) {
 | 
						|
  expectError<NotFoundError>("String not found in input", std::move(Err));
 | 
						|
}
 | 
						|
 | 
						|
class PatternTester {
 | 
						|
private:
 | 
						|
  size_t LineNumber = 1;
 | 
						|
  SourceMgr SM;
 | 
						|
  FileCheckRequest Req;
 | 
						|
  FileCheckPatternContext Context;
 | 
						|
  Pattern P{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_THAT_ERROR(Context.defineCmdlineVariables(GlobalDefines, SM),
 | 
						|
                      Succeeded());
 | 
						|
    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 = Pattern(Check::CheckPlain, &Context, ++LineNumber);
 | 
						|
  }
 | 
						|
 | 
						|
  size_t getLineNumber() const { return LineNumber; }
 | 
						|
 | 
						|
  Expected<std::unique_ptr<Expression>>
 | 
						|
  parseSubst(StringRef Expr, bool IsLegacyLineExpr = false) {
 | 
						|
    StringRef ExprBufferRef = bufferize(SM, Expr);
 | 
						|
    Optional<NumericVariable *> DefinedNumericVariable;
 | 
						|
    return P.parseNumericSubstitutionBlock(
 | 
						|
        ExprBufferRef, DefinedNumericVariable, IsLegacyLineExpr, LineNumber,
 | 
						|
        &Context, SM);
 | 
						|
  }
 | 
						|
 | 
						|
  bool parsePattern(StringRef Pattern) {
 | 
						|
    StringRef PatBufferRef = bufferize(SM, Pattern);
 | 
						|
    return P.parsePattern(PatBufferRef, "CHECK", SM, Req);
 | 
						|
  }
 | 
						|
 | 
						|
  Expected<size_t> match(StringRef Buffer) {
 | 
						|
    StringRef BufferRef = bufferize(SM, Buffer);
 | 
						|
    size_t MatchLen;
 | 
						|
    return P.match(BufferRef, MatchLen, SM);
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
TEST_F(FileCheckTest, ParseNumericSubstitutionBlock) {
 | 
						|
  PatternTester Tester;
 | 
						|
 | 
						|
  // Variable definition.
 | 
						|
 | 
						|
  expectDiagnosticError("invalid variable name",
 | 
						|
                        Tester.parseSubst("%VAR:").takeError());
 | 
						|
 | 
						|
  expectDiagnosticError("definition of pseudo numeric variable unsupported",
 | 
						|
                        Tester.parseSubst("@LINE:").takeError());
 | 
						|
 | 
						|
  expectDiagnosticError("string variable with name 'BAR' already exists",
 | 
						|
                        Tester.parseSubst("BAR:").takeError());
 | 
						|
 | 
						|
  expectDiagnosticError("unexpected characters after numeric variable name",
 | 
						|
                        Tester.parseSubst("VAR GARBAGE:").takeError());
 | 
						|
 | 
						|
  // Change of format.
 | 
						|
  expectDiagnosticError("format different from previous variable definition",
 | 
						|
                        Tester.parseSubst("%X,FOO:").takeError());
 | 
						|
 | 
						|
  // Invalid format.
 | 
						|
  expectDiagnosticError("invalid matching format specification in expression",
 | 
						|
                        Tester.parseSubst("X,VAR1:").takeError());
 | 
						|
  expectDiagnosticError("invalid format specifier in expression",
 | 
						|
                        Tester.parseSubst("%F,VAR1:").takeError());
 | 
						|
  expectDiagnosticError("invalid matching format specification in expression",
 | 
						|
                        Tester.parseSubst("%X a,VAR1:").takeError());
 | 
						|
 | 
						|
  // Acceptable variable definition.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR1:"), Succeeded());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("  VAR2:"), Succeeded());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3  :"), Succeeded());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("VAR3:  "), Succeeded());
 | 
						|
 | 
						|
  // Acceptable variable definition with format specifier. Use parsePattern for
 | 
						|
  // variables whose definition needs to be visible for later checks.
 | 
						|
  EXPECT_FALSE(Tester.parsePattern("[[#%u, VAR_UNSIGNED:]]"));
 | 
						|
  EXPECT_FALSE(Tester.parsePattern("[[#%x, VAR_LOWER_HEX:]]"));
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, VAR_UPPER_HEX:"), Succeeded());
 | 
						|
 | 
						|
  // Acceptable variable definition from a numeric expression.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("FOOBAR: FOO+1"), Succeeded());
 | 
						|
 | 
						|
  // Numeric expression. Switch to next line to make above valid definition
 | 
						|
  // available in expressions.
 | 
						|
  Tester.initNextPattern();
 | 
						|
 | 
						|
  // Invalid variable name.
 | 
						|
  expectDiagnosticError("invalid operand format '%VAR'",
 | 
						|
                        Tester.parseSubst("%VAR").takeError());
 | 
						|
 | 
						|
  expectDiagnosticError("invalid pseudo numeric variable '@FOO'",
 | 
						|
                        Tester.parseSubst("@FOO").takeError());
 | 
						|
 | 
						|
  // parsePattern() is used here instead of parseSubst() for the variable to be
 | 
						|
  // recorded in GlobalNumericVariableTable and thus appear defined to
 | 
						|
  // parseNumericVariableUse(). Note that the same pattern object is used for
 | 
						|
  // the parsePattern() and parseSubst() since no initNextPattern() is called,
 | 
						|
  // thus appearing as being on the same line from the pattern's point of view.
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("[[#SAME_LINE_VAR:]]"));
 | 
						|
  expectDiagnosticError("numeric variable 'SAME_LINE_VAR' defined earlier in "
 | 
						|
                        "the same CHECK directive",
 | 
						|
                        Tester.parseSubst("SAME_LINE_VAR").takeError());
 | 
						|
 | 
						|
  // Invalid use of variable defined on the same line from an expression not
 | 
						|
  // using any variable defined on the same line.
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("[[#SAME_LINE_EXPR_VAR:@LINE+1]]"));
 | 
						|
  expectDiagnosticError("numeric variable 'SAME_LINE_EXPR_VAR' defined earlier "
 | 
						|
                        "in the same CHECK directive",
 | 
						|
                        Tester.parseSubst("SAME_LINE_EXPR_VAR").takeError());
 | 
						|
 | 
						|
  // Valid use of undefined variable which creates the variable and record it
 | 
						|
  // in GlobalNumericVariableTable.
 | 
						|
  ASSERT_THAT_EXPECTED(Tester.parseSubst("UNDEF"), Succeeded());
 | 
						|
  EXPECT_TRUE(Tester.parsePattern("[[UNDEF:.*]]"));
 | 
						|
 | 
						|
  // Invalid literal.
 | 
						|
  expectDiagnosticError("unsupported operation 'U'",
 | 
						|
                        Tester.parseSubst("42U").takeError());
 | 
						|
 | 
						|
  // Valid empty expression.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst(""), Succeeded());
 | 
						|
 | 
						|
  // Valid single operand expression.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO"), Succeeded());
 | 
						|
 | 
						|
  // Invalid format.
 | 
						|
  expectDiagnosticError("invalid matching format specification in expression",
 | 
						|
                        Tester.parseSubst("X,FOO:").takeError());
 | 
						|
  expectDiagnosticError("invalid format specifier in expression",
 | 
						|
                        Tester.parseSubst("%F,FOO").takeError());
 | 
						|
  expectDiagnosticError("invalid matching format specification in expression",
 | 
						|
                        Tester.parseSubst("%X a,FOO").takeError());
 | 
						|
 | 
						|
  // Valid expression with 2 or more operands.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+3"), Succeeded());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+0xC"), Succeeded());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO-3+FOO"), Succeeded());
 | 
						|
 | 
						|
  expectDiagnosticError("unsupported operation '/'",
 | 
						|
                        Tester.parseSubst("@LINE/2").takeError());
 | 
						|
 | 
						|
  expectDiagnosticError("missing operand in expression",
 | 
						|
                        Tester.parseSubst("@LINE+").takeError());
 | 
						|
 | 
						|
  // Errors in RHS operand are bubbled up by parseBinop() to
 | 
						|
  // parseNumericSubstitutionBlock().
 | 
						|
  expectDiagnosticError("invalid operand format '%VAR'",
 | 
						|
                        Tester.parseSubst("@LINE+%VAR").takeError());
 | 
						|
 | 
						|
  // Invalid legacy @LINE expression with non literal rhs.
 | 
						|
  expectDiagnosticError(
 | 
						|
      "invalid operand format '@LINE'",
 | 
						|
      Tester.parseSubst("@LINE+@LINE", /*IsLegacyNumExpr=*/true).takeError());
 | 
						|
 | 
						|
  // Invalid legacy @LINE expression made of a single literal.
 | 
						|
  expectDiagnosticError(
 | 
						|
      "invalid variable name",
 | 
						|
      Tester.parseSubst("2", /*IsLegacyNumExpr=*/true).takeError());
 | 
						|
 | 
						|
  // Invalid hex literal in legacy @LINE expression.
 | 
						|
  expectDiagnosticError(
 | 
						|
      "unexpected characters at end of expression 'xC'",
 | 
						|
      Tester.parseSubst("@LINE+0xC", /*LegacyLineExpr=*/true).takeError());
 | 
						|
 | 
						|
  // Valid expression with format specifier.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("%u, FOO"), Succeeded());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("%x, FOO"), Succeeded());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("%X, FOO"), Succeeded());
 | 
						|
 | 
						|
  // Valid legacy @LINE expression.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("@LINE+2", /*IsLegacyNumExpr=*/true),
 | 
						|
                       Succeeded());
 | 
						|
 | 
						|
  // Invalid legacy @LINE expression with more than 2 operands.
 | 
						|
  expectDiagnosticError(
 | 
						|
      "unexpected characters at end of expression '+@LINE'",
 | 
						|
      Tester.parseSubst("@LINE+2+@LINE", /*IsLegacyNumExpr=*/true).takeError());
 | 
						|
  expectDiagnosticError(
 | 
						|
      "unexpected characters at end of expression '+2'",
 | 
						|
      Tester.parseSubst("@LINE+2+2", /*IsLegacyNumExpr=*/true).takeError());
 | 
						|
 | 
						|
  // Valid expression with several variables when their implicit formats do not
 | 
						|
  // conflict.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("FOO+VAR_UNSIGNED"), Succeeded());
 | 
						|
 | 
						|
  // Valid implicit format conflict in presence of explicit formats.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.parseSubst("%X,FOO+VAR_LOWER_HEX"), Succeeded());
 | 
						|
 | 
						|
  // Implicit format conflict.
 | 
						|
  expectDiagnosticError(
 | 
						|
      "variables with conflicting format specifier: need an explicit one",
 | 
						|
      Tester.parseSubst("FOO+VAR_LOWER_HEX").takeError());
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(FileCheckTest, ParsePattern) {
 | 
						|
  PatternTester Tester;
 | 
						|
 | 
						|
  // Invalid space in string substitution.
 | 
						|
  EXPECT_TRUE(Tester.parsePattern("[[ BAR]]"));
 | 
						|
 | 
						|
  // Invalid variable name in string substitution.
 | 
						|
  EXPECT_TRUE(Tester.parsePattern("[[42INVALID]]"));
 | 
						|
 | 
						|
  // Invalid string variable definition.
 | 
						|
  EXPECT_TRUE(Tester.parsePattern("[[@PAT:]]"));
 | 
						|
  EXPECT_TRUE(Tester.parsePattern("[[PAT+2:]]"));
 | 
						|
 | 
						|
  // Collision with numeric variable.
 | 
						|
  EXPECT_TRUE(Tester.parsePattern("[[FOO:]]"));
 | 
						|
 | 
						|
  // Valid use of string variable.
 | 
						|
  EXPECT_FALSE(Tester.parsePattern("[[BAR]]"));
 | 
						|
 | 
						|
  // Valid string variable definition.
 | 
						|
  EXPECT_FALSE(Tester.parsePattern("[[PAT:[0-9]+]]"));
 | 
						|
 | 
						|
  // Invalid numeric substitution.
 | 
						|
  EXPECT_TRUE(Tester.parsePattern("[[#42INVALID]]"));
 | 
						|
 | 
						|
  // Valid numeric substitution.
 | 
						|
  EXPECT_FALSE(Tester.parsePattern("[[#FOO]]"));
 | 
						|
 | 
						|
  // Valid legacy @LINE expression.
 | 
						|
  EXPECT_FALSE(Tester.parsePattern("[[@LINE+2]]"));
 | 
						|
 | 
						|
  // Invalid legacy @LINE expression with non decimal literal.
 | 
						|
  EXPECT_TRUE(Tester.parsePattern("[[@LINE+0x3]]"));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(FileCheckTest, Match) {
 | 
						|
  PatternTester Tester;
 | 
						|
 | 
						|
  // Check matching an empty expression only matches a number.
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("[[#]]"));
 | 
						|
  expectNotFoundError(Tester.match("FAIL").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
 | 
						|
 | 
						|
  // Check matching a definition only matches a number with the right format.
 | 
						|
  Tester.initNextPattern();
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR:]]"));
 | 
						|
  expectNotFoundError(Tester.match("FAIL").takeError());
 | 
						|
  expectNotFoundError(Tester.match("").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
 | 
						|
  Tester.initNextPattern();
 | 
						|
  Tester.parsePattern("[[#%u,NUMVAR_UNSIGNED:]]");
 | 
						|
  expectNotFoundError(Tester.match("C").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("20"), Succeeded());
 | 
						|
  Tester.initNextPattern();
 | 
						|
  Tester.parsePattern("[[#%x,NUMVAR_LOWER_HEX:]]");
 | 
						|
  expectNotFoundError(Tester.match("g").takeError());
 | 
						|
  expectNotFoundError(Tester.match("C").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("c"), Succeeded());
 | 
						|
  Tester.initNextPattern();
 | 
						|
  Tester.parsePattern("[[#%X,NUMVAR_UPPER_HEX:]]");
 | 
						|
  expectNotFoundError(Tester.match("H").takeError());
 | 
						|
  expectNotFoundError(Tester.match("b").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("B"), Succeeded());
 | 
						|
 | 
						|
  // Check matching expressions with no explicit format matches the values in
 | 
						|
  // the right format.
 | 
						|
  Tester.initNextPattern();
 | 
						|
  Tester.parsePattern("[[#NUMVAR_UNSIGNED-5]]");
 | 
						|
  expectNotFoundError(Tester.match("f").takeError());
 | 
						|
  expectNotFoundError(Tester.match("F").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("15"), Succeeded());
 | 
						|
  Tester.initNextPattern();
 | 
						|
  Tester.parsePattern("[[#NUMVAR_LOWER_HEX+1]]");
 | 
						|
  expectNotFoundError(Tester.match("13").takeError());
 | 
						|
  expectNotFoundError(Tester.match("D").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("d"), Succeeded());
 | 
						|
  Tester.initNextPattern();
 | 
						|
  Tester.parsePattern("[[#NUMVAR_UPPER_HEX+1]]");
 | 
						|
  expectNotFoundError(Tester.match("12").takeError());
 | 
						|
  expectNotFoundError(Tester.match("c").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("C"), Succeeded());
 | 
						|
 | 
						|
  // Check matching an undefined variable returns a NotFound error.
 | 
						|
  Tester.initNextPattern();
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("100"));
 | 
						|
  expectNotFoundError(Tester.match("101").takeError());
 | 
						|
 | 
						|
  // Check matching the defined variable matches the correct number only.
 | 
						|
  Tester.initNextPattern();
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR]]"));
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("18"), Succeeded());
 | 
						|
 | 
						|
  // Check matching several substitutions does not match them independently.
 | 
						|
  Tester.initNextPattern();
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("[[#NUMVAR]] [[#NUMVAR+2]]"));
 | 
						|
  expectNotFoundError(Tester.match("19 21").takeError());
 | 
						|
  expectNotFoundError(Tester.match("18 21").takeError());
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match("18 20"), Succeeded());
 | 
						|
 | 
						|
  // Check matching a numeric expression using @LINE after a match failure uses
 | 
						|
  // the correct value for @LINE.
 | 
						|
  Tester.initNextPattern();
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("[[#@LINE]]"));
 | 
						|
  // Ok, @LINE matches the current line number.
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match(std::to_string(Tester.getLineNumber())),
 | 
						|
                       Succeeded());
 | 
						|
  Tester.initNextPattern();
 | 
						|
  // Match with substitution failure.
 | 
						|
  ASSERT_FALSE(Tester.parsePattern("[[#UNKNOWN]]"));
 | 
						|
  expectUndefErrors({"UNKNOWN"}, Tester.match("FOO").takeError());
 | 
						|
  Tester.initNextPattern();
 | 
						|
  // Check that @LINE matches the later (given the calls to initNextPattern())
 | 
						|
  // line number.
 | 
						|
  EXPECT_FALSE(Tester.parsePattern("[[#@LINE]]"));
 | 
						|
  EXPECT_THAT_EXPECTED(Tester.match(std::to_string(Tester.getLineNumber())),
 | 
						|
                       Succeeded());
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(FileCheckTest, Substitution) {
 | 
						|
  SourceMgr SM;
 | 
						|
  FileCheckPatternContext Context;
 | 
						|
  std::vector<std::string> GlobalDefines;
 | 
						|
  GlobalDefines.emplace_back(std::string("FOO=BAR"));
 | 
						|
  EXPECT_THAT_ERROR(Context.defineCmdlineVariables(GlobalDefines, SM),
 | 
						|
                    Succeeded());
 | 
						|
 | 
						|
  // Substitution of an undefined string variable fails and error holds that
 | 
						|
  // variable's name.
 | 
						|
  StringSubstitution StringSubstitution(&Context, "VAR404", 42);
 | 
						|
  Expected<std::string> SubstValue = StringSubstitution.getResult();
 | 
						|
  expectUndefErrors({"VAR404"}, SubstValue.takeError());
 | 
						|
 | 
						|
  // Numeric substitution blocks constituted of defined numeric variables are
 | 
						|
  // substituted for the variable's value.
 | 
						|
  NumericVariable NVar("N", ExpressionFormat(ExpressionFormat::Kind::Unsigned),
 | 
						|
                       1);
 | 
						|
  NVar.setValue(10);
 | 
						|
  auto NVarUse = std::make_unique<NumericVariableUse>("N", &NVar);
 | 
						|
  auto ExpressionN = std::make_unique<Expression>(
 | 
						|
      std::move(NVarUse), ExpressionFormat(ExpressionFormat::Kind::HexUpper));
 | 
						|
  NumericSubstitution SubstitutionN(&Context, "N", std::move(ExpressionN),
 | 
						|
                                    /*InsertIdx=*/30);
 | 
						|
  SubstValue = SubstitutionN.getResult();
 | 
						|
  ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
 | 
						|
  EXPECT_EQ("A", *SubstValue);
 | 
						|
 | 
						|
  // Substitution of an undefined numeric variable fails, error holds name of
 | 
						|
  // undefined variable.
 | 
						|
  NVar.clearValue();
 | 
						|
  SubstValue = SubstitutionN.getResult();
 | 
						|
  expectUndefErrors({"N"}, SubstValue.takeError());
 | 
						|
 | 
						|
  // Substitution of a defined string variable returns the right value.
 | 
						|
  Pattern P(Check::CheckPlain, &Context, 1);
 | 
						|
  StringSubstitution = llvm::StringSubstitution(&Context, "FOO", 42);
 | 
						|
  SubstValue = StringSubstitution.getResult();
 | 
						|
  ASSERT_THAT_EXPECTED(SubstValue, Succeeded());
 | 
						|
  EXPECT_EQ("BAR", *SubstValue);
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(FileCheckTest, FileCheckContext) {
 | 
						|
  FileCheckPatternContext Cxt;
 | 
						|
  std::vector<std::string> GlobalDefines;
 | 
						|
  SourceMgr SM;
 | 
						|
 | 
						|
  // No definition.
 | 
						|
  EXPECT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
 | 
						|
 | 
						|
  // Missing equal sign.
 | 
						|
  GlobalDefines.emplace_back(std::string("LocalVar"));
 | 
						|
  expectDiagnosticError("missing equal sign in global definition",
 | 
						|
                        Cxt.defineCmdlineVariables(GlobalDefines, SM));
 | 
						|
  GlobalDefines.clear();
 | 
						|
  GlobalDefines.emplace_back(std::string("#LocalNumVar"));
 | 
						|
  expectDiagnosticError("missing equal sign in global definition",
 | 
						|
                        Cxt.defineCmdlineVariables(GlobalDefines, SM));
 | 
						|
 | 
						|
  // Empty variable name.
 | 
						|
  GlobalDefines.clear();
 | 
						|
  GlobalDefines.emplace_back(std::string("=18"));
 | 
						|
  expectDiagnosticError("empty variable name",
 | 
						|
                        Cxt.defineCmdlineVariables(GlobalDefines, SM));
 | 
						|
  GlobalDefines.clear();
 | 
						|
  GlobalDefines.emplace_back(std::string("#=18"));
 | 
						|
  expectDiagnosticError("empty variable name",
 | 
						|
                        Cxt.defineCmdlineVariables(GlobalDefines, SM));
 | 
						|
 | 
						|
  // Invalid variable name.
 | 
						|
  GlobalDefines.clear();
 | 
						|
  GlobalDefines.emplace_back(std::string("18LocalVar=18"));
 | 
						|
  expectDiagnosticError("invalid variable name",
 | 
						|
                        Cxt.defineCmdlineVariables(GlobalDefines, SM));
 | 
						|
  GlobalDefines.clear();
 | 
						|
  GlobalDefines.emplace_back(std::string("#18LocalNumVar=18"));
 | 
						|
  expectDiagnosticError("invalid variable name",
 | 
						|
                        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"));
 | 
						|
  expectDiagnosticError("string variable with name 'LocalVar' already exists",
 | 
						|
                        Cxt.defineCmdlineVariables(GlobalDefines, SM));
 | 
						|
  Cxt = FileCheckPatternContext();
 | 
						|
  GlobalDefines.clear();
 | 
						|
  GlobalDefines.emplace_back(std::string("#LocalNumVar=18"));
 | 
						|
  GlobalDefines.emplace_back(std::string("LocalNumVar=36"));
 | 
						|
  expectDiagnosticError(
 | 
						|
      "numeric variable with name 'LocalNumVar' already exists",
 | 
						|
      Cxt.defineCmdlineVariables(GlobalDefines, SM));
 | 
						|
  Cxt = FileCheckPatternContext();
 | 
						|
 | 
						|
  // Invalid numeric value for numeric variable.
 | 
						|
  GlobalDefines.clear();
 | 
						|
  GlobalDefines.emplace_back(std::string("#LocalNumVar=x"));
 | 
						|
  expectUndefErrors({"x"}, 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("#%x,LocalNumVar2=LocalNumVar1+2"));
 | 
						|
  GlobalDefines.emplace_back(std::string("#LocalNumVar3=0xc"));
 | 
						|
  ASSERT_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
 | 
						|
 | 
						|
  // Create @LINE pseudo numeric variable and check it is present by matching
 | 
						|
  // it.
 | 
						|
  size_t LineNumber = 1;
 | 
						|
  Pattern P(Check::CheckPlain, &Cxt, LineNumber);
 | 
						|
  FileCheckRequest Req;
 | 
						|
  Cxt.createLineVariable();
 | 
						|
  ASSERT_FALSE(P.parsePattern("[[@LINE]]", "CHECK", SM, Req));
 | 
						|
  size_t MatchLen;
 | 
						|
  ASSERT_THAT_EXPECTED(P.match("1", MatchLen, SM), Succeeded());
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
  // Recreating @LINE pseudo numeric variable fails.
 | 
						|
  EXPECT_DEATH(Cxt.createLineVariable(),
 | 
						|
               "@LINE pseudo numeric variable already created");
 | 
						|
#endif
 | 
						|
 | 
						|
  // Check defined variables are present and undefined ones are absent.
 | 
						|
  StringRef LocalVarStr = "LocalVar";
 | 
						|
  StringRef LocalNumVar1Ref = bufferize(SM, "LocalNumVar1");
 | 
						|
  StringRef LocalNumVar2Ref = bufferize(SM, "LocalNumVar2");
 | 
						|
  StringRef LocalNumVar3Ref = bufferize(SM, "LocalNumVar3");
 | 
						|
  StringRef EmptyVarStr = "EmptyVar";
 | 
						|
  StringRef UnknownVarStr = "UnknownVar";
 | 
						|
  Expected<StringRef> LocalVar = Cxt.getPatternVarValue(LocalVarStr);
 | 
						|
  P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
 | 
						|
  Optional<NumericVariable *> DefinedNumericVariable;
 | 
						|
  Expected<std::unique_ptr<Expression>> ExpressionPointer =
 | 
						|
      P.parseNumericSubstitutionBlock(LocalNumVar1Ref, DefinedNumericVariable,
 | 
						|
                                      /*IsLegacyLineExpr=*/false, LineNumber,
 | 
						|
                                      &Cxt, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(LocalVar, Succeeded());
 | 
						|
  EXPECT_EQ(*LocalVar, "FOO");
 | 
						|
  Expected<StringRef> EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
 | 
						|
  Expected<StringRef> UnknownVar = Cxt.getPatternVarValue(UnknownVarStr);
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
 | 
						|
  Expected<uint64_t> ExpressionVal = (*ExpressionPointer)->getAST()->eval();
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
 | 
						|
  EXPECT_EQ(*ExpressionVal, 18U);
 | 
						|
  ExpressionPointer = P.parseNumericSubstitutionBlock(
 | 
						|
      LocalNumVar2Ref, DefinedNumericVariable,
 | 
						|
      /*IsLegacyLineExpr=*/false, LineNumber, &Cxt, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
 | 
						|
  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
 | 
						|
  EXPECT_EQ(*ExpressionVal, 20U);
 | 
						|
  ExpressionPointer =
 | 
						|
      P.parseNumericSubstitutionBlock(LocalNumVar3Ref, DefinedNumericVariable,
 | 
						|
                                      /*IsLegacyLineExpr=*/false,
 | 
						|
                                      LineNumber, &Cxt, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
 | 
						|
  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
 | 
						|
  EXPECT_EQ(*ExpressionVal, 12U);
 | 
						|
  ASSERT_THAT_EXPECTED(EmptyVar, Succeeded());
 | 
						|
  EXPECT_EQ(*EmptyVar, "");
 | 
						|
  expectUndefErrors({std::string(UnknownVarStr)}, UnknownVar.takeError());
 | 
						|
 | 
						|
  // Clear local variables and check they become absent.
 | 
						|
  Cxt.clearLocalVars();
 | 
						|
  LocalVar = Cxt.getPatternVarValue(LocalVarStr);
 | 
						|
  expectUndefErrors({std::string(LocalVarStr)}, 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.
 | 
						|
  expectUndefErrors({"LocalNumVar3"},
 | 
						|
                    (*ExpressionPointer)->getAST()->eval().takeError());
 | 
						|
  P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
 | 
						|
  ExpressionPointer = P.parseNumericSubstitutionBlock(
 | 
						|
      LocalNumVar1Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
 | 
						|
      LineNumber, &Cxt, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
 | 
						|
  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
 | 
						|
  expectUndefErrors({"LocalNumVar1"}, ExpressionVal.takeError());
 | 
						|
  ExpressionPointer = P.parseNumericSubstitutionBlock(
 | 
						|
      LocalNumVar2Ref, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
 | 
						|
      LineNumber, &Cxt, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
 | 
						|
  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
 | 
						|
  expectUndefErrors({"LocalNumVar2"}, ExpressionVal.takeError());
 | 
						|
  EmptyVar = Cxt.getPatternVarValue(EmptyVarStr);
 | 
						|
  expectUndefErrors({"EmptyVar"}, 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_THAT_ERROR(Cxt.defineCmdlineVariables(GlobalDefines, SM), Succeeded());
 | 
						|
  StringRef GlobalVarStr = "$GlobalVar";
 | 
						|
  StringRef GlobalNumVarRef = bufferize(SM, "$GlobalNumVar");
 | 
						|
  Expected<StringRef> GlobalVar = Cxt.getPatternVarValue(GlobalVarStr);
 | 
						|
  ASSERT_THAT_EXPECTED(GlobalVar, Succeeded());
 | 
						|
  EXPECT_EQ(*GlobalVar, "BAR");
 | 
						|
  P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
 | 
						|
  ExpressionPointer = P.parseNumericSubstitutionBlock(
 | 
						|
      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
 | 
						|
      LineNumber, &Cxt, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
 | 
						|
  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
 | 
						|
  EXPECT_EQ(*ExpressionVal, 36U);
 | 
						|
 | 
						|
  // Clear local variables and check global variables remain defined.
 | 
						|
  Cxt.clearLocalVars();
 | 
						|
  EXPECT_THAT_EXPECTED(Cxt.getPatternVarValue(GlobalVarStr), Succeeded());
 | 
						|
  P = Pattern(Check::CheckPlain, &Cxt, ++LineNumber);
 | 
						|
  ExpressionPointer = P.parseNumericSubstitutionBlock(
 | 
						|
      GlobalNumVarRef, DefinedNumericVariable, /*IsLegacyLineExpr=*/false,
 | 
						|
      LineNumber, &Cxt, SM);
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionPointer, Succeeded());
 | 
						|
  ExpressionVal = (*ExpressionPointer)->getAST()->eval();
 | 
						|
  ASSERT_THAT_EXPECTED(ExpressionVal, Succeeded());
 | 
						|
  EXPECT_EQ(*ExpressionVal, 36U);
 | 
						|
}
 | 
						|
} // namespace
 |