brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.3 KiB · a930638 Raw
360 lines · cpp
1// unittests/ASTMatchers/ASTMatchersInternalTest.cpp - AST matcher unit tests //2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ASTMatchersTest.h"10#include "clang/AST/PrettyPrinter.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "clang/ASTMatchers/ASTMatchers.h"13#include "clang/Tooling/Tooling.h"14#include "llvm/TargetParser/Host.h"15#include "llvm/TargetParser/Triple.h"16#include "llvm/Testing/Support/SupportHelpers.h"17#include "gtest/gtest.h"18 19namespace clang {20namespace ast_matchers {21using internal::DynTypedMatcher;22 23#if GTEST_HAS_DEATH_TEST24TEST(HasNameDeathTest, DiesOnEmptyName) {25  ASSERT_DEBUG_DEATH({26    DeclarationMatcher HasEmptyName = recordDecl(hasName(""));27    EXPECT_TRUE(notMatches("class X {};", HasEmptyName));28  }, "");29}30 31TEST(HasNameDeathTest, DiesOnEmptyPattern) {32  ASSERT_DEBUG_DEATH({33      DeclarationMatcher HasEmptyName = recordDecl(matchesName(""));34      EXPECT_TRUE(notMatches("class X {};", HasEmptyName));35    }, "");36}37 38// FIXME Re-enable these tests without breaking standalone builds.39#if 040// FIXME: Figure out why back traces aren't being generated on clang builds on41// windows.42#if ENABLE_BACKTRACES && (!defined(_MSC_VER) || !defined(__clang__))43 44AST_MATCHER(Decl, causeCrash) {45  abort();46  return true;47}48 49TEST(MatcherCrashDeathTest, CrashOnMatcherDump) {50  llvm::EnablePrettyStackTrace();51  auto Matcher = testing::HasSubstr(52      "ASTMatcher: Matching '<unknown>' against:\n\tFunctionDecl foo : "53      "<input.cc:1:1, col:10>");54  ASSERT_DEATH(matches("void foo();", functionDecl(causeCrash())), Matcher);55}56 57template <typename MatcherT>58static void crashTestNodeDump(MatcherT Matcher,59                              ArrayRef<StringRef> MatchedNodes,60                              StringRef Against, StringRef Code) {61  llvm::EnablePrettyStackTrace();62  MatchFinder Finder;63 64  struct CrashCallback : public MatchFinder::MatchCallback {65    void run(const MatchFinder::MatchResult &Result) override { abort(); }66    std::optional<TraversalKind> getCheckTraversalKind() const override {67      return TK_IgnoreUnlessSpelledInSource;68    }69    StringRef getID() const override { return "CrashTester"; }70  } Callback;71  Finder.addMatcher(std::move(Matcher), &Callback);72  if (MatchedNodes.empty()) {73    ASSERT_DEATH(tooling::runToolOnCode(74                     newFrontendActionFactory(&Finder)->create(), Code),75                 testing::HasSubstr(76                     ("ASTMatcher: Processing 'CrashTester' against:\n\t" +77                      Against + "\nNo bound nodes")78                         .str()));79  } else {80    std::vector<testing::PolymorphicMatcher<81        testing::internal::HasSubstrMatcher<std::string>>>82        Matchers;83    Matchers.reserve(MatchedNodes.size());84    for (auto Node : MatchedNodes) {85      Matchers.push_back(testing::HasSubstr(Node.str()));86    }87    auto CrashMatcher = testing::AllOf(88        testing::HasSubstr(89            ("ASTMatcher: Processing 'CrashTester' against:\n\t" + Against +90             "\n--- Bound Nodes Begin ---")91                .str()),92        testing::HasSubstr("--- Bound Nodes End ---"),93        testing::AllOfArray(Matchers));94 95    ASSERT_DEATH(tooling::runToolOnCode(96                     newFrontendActionFactory(&Finder)->create(), Code),97                 CrashMatcher);98  }99}100TEST(MatcherCrashDeathTest, CrashOnCallbackDump) {101  crashTestNodeDump(forStmt(), {}, "ForStmt : <input.cc:1:14, col:21>",102                    "void foo() { for(;;); }");103  crashTestNodeDump(104      forStmt(hasLoopInit(declStmt(hasSingleDecl(105                                       varDecl(hasType(qualType().bind("QT")),106                                               hasType(type().bind("T")),107                                               hasInitializer(108                                                   integerLiteral().bind("IL")))109                                           .bind("VD")))110                              .bind("DS")))111          .bind("FS"),112      {"FS - { ForStmt : <input.cc:3:5, line:4:5> }",113       "DS - { DeclStmt : <input.cc:3:10, col:19> }",114       "IL - { IntegerLiteral : <input.cc:3:18> }", "QT - { QualType : int }",115       "T - { BuiltinType : int }",116       "VD - { VarDecl I : <input.cc:3:10, col:18> }"},117      "ForStmt : <input.cc:3:5, line:4:5>",118      R"cpp(119  void foo() {120    for (int I = 0; I < 5; ++I) {121    }122  }123  )cpp");124  crashTestNodeDump(125      cxxRecordDecl(hasMethod(cxxMethodDecl(hasName("operator+")).bind("Op+")))126          .bind("Unnamed"),127      {"Unnamed - { CXXRecordDecl (anonymous) : <input.cc:1:1, col:36> }",128       "Op+ - { CXXMethodDecl (anonymous struct)::operator+ : <input.cc:1:10, "129       "col:29> }"},130      "CXXRecordDecl (anonymous) : <input.cc:1:1, col:36>",131      "struct { int operator+(int) const; } Unnamed;");132  crashTestNodeDump(133      cxxRecordDecl(hasMethod(cxxConstructorDecl(isDefaulted()).bind("Ctor")),134                    hasMethod(cxxDestructorDecl(isDefaulted()).bind("Dtor"))),135      {"Ctor - { CXXConstructorDecl Foo::Foo : <input.cc:1:14, col:28> }",136       "Dtor - { CXXDestructorDecl Foo::~Foo : <input.cc:1:31, col:46> }"},137      "CXXRecordDecl Foo : <input.cc:1:1, col:49>",138      "struct Foo { Foo() = default; ~Foo() = default; };");139}140#endif // ENABLE_BACKTRACES141#endif142#endif143 144TEST(ConstructVariadic, MismatchedTypes_Regression) {145  EXPECT_TRUE(146      matches("const int a = 0;", internal::DynTypedMatcher::constructVariadic(147                                      internal::DynTypedMatcher::VO_AnyOf,148                                      ASTNodeKind::getFromNodeKind<QualType>(),149                                      {isConstQualified(), arrayType()})150                                      .convertTo<QualType>()));151}152 153// For testing AST_MATCHER_P().154AST_MATCHER_P(Decl, just, internal::Matcher<Decl>, AMatcher) {155  // Make sure all special variables are used: node, match_finder,156  // bound_nodes_builder, and the parameter named 'AMatcher'.157  return AMatcher.matches(Node, Finder, Builder);158}159 160TEST(AstMatcherPMacro, Works) {161  DeclarationMatcher HasClassB = just(has(recordDecl(hasName("B")).bind("b")));162 163  EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",164      HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));165 166  EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",167      HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("a")));168 169  EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",170      HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));171}172 173AST_POLYMORPHIC_MATCHER_P(polymorphicHas,174                          AST_POLYMORPHIC_SUPPORTED_TYPES(Decl, Stmt),175                          internal::Matcher<Decl>, AMatcher) {176  return Finder->matchesChildOf(177      Node, AMatcher, Builder,178      ASTMatchFinder::BK_First);179}180 181TEST(AstPolymorphicMatcherPMacro, Works) {182  DeclarationMatcher HasClassB =183      polymorphicHas(recordDecl(hasName("B")).bind("b"));184 185  EXPECT_TRUE(matchAndVerifyResultTrue("class A { class B {}; };",186      HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));187 188  EXPECT_TRUE(matchAndVerifyResultFalse("class A { class B {}; };",189      HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("a")));190 191  EXPECT_TRUE(matchAndVerifyResultFalse("class A { class C {}; };",192      HasClassB, std::make_unique<VerifyIdIsBoundTo<Decl>>("b")));193 194  StatementMatcher StatementHasClassB =195      polymorphicHas(recordDecl(hasName("B")));196 197  EXPECT_TRUE(matches("void x() { class B {}; }", StatementHasClassB));198}199 200TEST(MatchFinder, CheckProfiling) {201  MatchFinder::MatchFinderOptions Options;202  llvm::StringMap<llvm::TimeRecord> Records;203  Options.CheckProfiling.emplace(Records);204  MatchFinder Finder(std::move(Options));205 206  struct NamedCallback : public MatchFinder::MatchCallback {207    void run(const MatchFinder::MatchResult &Result) override {}208    StringRef getID() const override { return "MyID"; }209  } Callback;210  Finder.addMatcher(decl(), &Callback);211  std::unique_ptr<FrontendActionFactory> Factory(212      newFrontendActionFactory(&Finder));213  ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));214 215  EXPECT_EQ(1u, Records.size());216  EXPECT_EQ("MyID", Records.begin()->getKey());217}218 219class VerifyStartOfTranslationUnit : public MatchFinder::MatchCallback {220public:221  VerifyStartOfTranslationUnit() : Called(false) {}222  void run(const MatchFinder::MatchResult &Result) override {223    EXPECT_TRUE(Called);224  }225  void onStartOfTranslationUnit() override { Called = true; }226  bool Called;227};228 229TEST(MatchFinder, InterceptsStartOfTranslationUnit) {230  MatchFinder Finder;231  VerifyStartOfTranslationUnit VerifyCallback;232  Finder.addMatcher(decl(), &VerifyCallback);233  std::unique_ptr<FrontendActionFactory> Factory(234      newFrontendActionFactory(&Finder));235  ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));236  EXPECT_TRUE(VerifyCallback.Called);237 238  VerifyCallback.Called = false;239  std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));240  ASSERT_TRUE(AST.get());241  Finder.matchAST(AST->getASTContext());242  EXPECT_TRUE(VerifyCallback.Called);243}244 245class VerifyEndOfTranslationUnit : public MatchFinder::MatchCallback {246public:247  VerifyEndOfTranslationUnit() : Called(false) {}248  void run(const MatchFinder::MatchResult &Result) override {249    EXPECT_FALSE(Called);250  }251  void onEndOfTranslationUnit() override { Called = true; }252  bool Called;253};254 255TEST(MatchFinder, InterceptsEndOfTranslationUnit) {256  MatchFinder Finder;257  VerifyEndOfTranslationUnit VerifyCallback;258  Finder.addMatcher(decl(), &VerifyCallback);259  std::unique_ptr<FrontendActionFactory> Factory(260      newFrontendActionFactory(&Finder));261  ASSERT_TRUE(tooling::runToolOnCode(Factory->create(), "int x;"));262  EXPECT_TRUE(VerifyCallback.Called);263 264  VerifyCallback.Called = false;265  std::unique_ptr<ASTUnit> AST(tooling::buildASTFromCode("int x;"));266  ASSERT_TRUE(AST.get());267  Finder.matchAST(AST->getASTContext());268  EXPECT_TRUE(VerifyCallback.Called);269}270 271TEST(Matcher, matchOverEntireASTContext) {272  std::unique_ptr<ASTUnit> AST =273      clang::tooling::buildASTFromCode("struct { int *foo; };");274  ASSERT_TRUE(AST.get());275  auto PT = selectFirst<PointerType>(276      "x", match(pointerType().bind("x"), AST->getASTContext()));277  EXPECT_NE(nullptr, PT);278}279 280TEST(DynTypedMatcherTest, TraversalKindForwardsToImpl) {281  auto M = DynTypedMatcher(decl());282  EXPECT_FALSE(M.getTraversalKind());283 284  M = DynTypedMatcher(traverse(TK_AsIs, decl()));285  EXPECT_THAT(M.getTraversalKind(), llvm::ValueIs(TK_AsIs));286}287 288TEST(DynTypedMatcherTest, ConstructWithTraversalKindSetsTK) {289  auto M = DynTypedMatcher(decl()).withTraversalKind(TK_AsIs);290  EXPECT_THAT(M.getTraversalKind(), llvm::ValueIs(TK_AsIs));291}292 293TEST(DynTypedMatcherTest, ConstructWithTraversalKindOverridesNestedTK) {294  auto M = DynTypedMatcher(decl()).withTraversalKind(TK_AsIs).withTraversalKind(295      TK_IgnoreUnlessSpelledInSource);296  EXPECT_THAT(M.getTraversalKind(),297              llvm::ValueIs(TK_IgnoreUnlessSpelledInSource));298}299 300TEST(IsInlineMatcher, IsInline) {301  EXPECT_TRUE(matches("void g(); inline void f();",302                      functionDecl(isInline(), hasName("f"))));303  EXPECT_TRUE(matches("namespace n { inline namespace m {} }",304                      namespaceDecl(isInline(), hasName("m"))));305  EXPECT_TRUE(matches("inline int Foo = 5;",306                      varDecl(isInline(), hasName("Foo")), {Lang_CXX17}));307}308 309// FIXME: Figure out how to specify paths so the following tests pass on310// Windows.311#ifndef _WIN32312 313TEST(Matcher, IsExpansionInMainFileMatcher) {314  EXPECT_TRUE(matches("class X {};",315                      recordDecl(hasName("X"), isExpansionInMainFile())));316  EXPECT_TRUE(notMatches("", recordDecl(isExpansionInMainFile())));317  FileContentMappings M;318  M.push_back(std::make_pair("/other", "class X {};"));319  EXPECT_TRUE(matchesConditionally("#include <other>\n",320                                   recordDecl(isExpansionInMainFile()), false,321                                   {"-isystem/"}, M));322}323 324TEST(Matcher, IsExpansionInSystemHeader) {325  FileContentMappings M;326  M.push_back(std::make_pair("/other", "class X {};"));327  EXPECT_TRUE(matchesConditionally("#include \"other\"\n",328                                   recordDecl(isExpansionInSystemHeader()),329                                   true, {"-isystem/"}, M));330  EXPECT_TRUE(matchesConditionally("#include \"other\"\n",331                                   recordDecl(isExpansionInSystemHeader()),332                                   false, {"-I/"}, M));333  EXPECT_TRUE(notMatches("class X {};",334                         recordDecl(isExpansionInSystemHeader())));335  EXPECT_TRUE(notMatches("", recordDecl(isExpansionInSystemHeader())));336}337 338TEST(Matcher, IsExpansionInFileMatching) {339  FileContentMappings M;340  M.push_back(std::make_pair("/foo", "class A {};"));341  M.push_back(std::make_pair("/bar", "class B {};"));342  EXPECT_TRUE(matchesConditionally(343      "#include <foo>\n"344      "#include <bar>\n"345      "class X {};",346      recordDecl(isExpansionInFileMatching("b.*"), hasName("B")), true,347      {"-isystem/"}, M));348  EXPECT_TRUE(matchesConditionally(349      "#include <foo>\n"350      "#include <bar>\n"351      "class X {};",352      recordDecl(isExpansionInFileMatching("f.*"), hasName("X")), false,353      {"-isystem/"}, M));354}355 356#endif // _WIN32357 358} // end namespace ast_matchers359} // end namespace clang360