brintos

brintos / llvm-project-archived public Read only

0
0
Text · 22.6 KiB · 3487f24 Raw
581 lines · cpp
1//===--- WalkASTTest.cpp ------------------------------------------- C++-*-===//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#include "AnalysisInternal.h"9#include "clang-include-cleaner/Types.h"10#include "clang/AST/ASTContext.h"11#include "clang/AST/Decl.h"12#include "clang/AST/DeclBase.h"13#include "clang/Basic/Diagnostic.h"14#include "clang/Basic/DiagnosticOptions.h"15#include "clang/Basic/FileManager.h"16#include "clang/Basic/SourceLocation.h"17#include "clang/Frontend/TextDiagnostic.h"18#include "clang/Testing/TestAST.h"19#include "llvm/ADT/STLExtras.h"20#include "llvm/ADT/StringRef.h"21#include "llvm/Support/Error.h"22#include "llvm/Support/ScopedPrinter.h"23#include "llvm/Support/raw_ostream.h"24#include "llvm/Testing/Annotations/Annotations.h"25#include "gmock/gmock.h"26#include "gtest/gtest.h"27#include <cstddef>28#include <string>29#include <unordered_map>30#include <utility>31#include <vector>32 33namespace clang::include_cleaner {34namespace {35using testing::ElementsAre;36 37// Specifies a test of which symbols are referenced by a piece of code.38// Target should contain points annotated with the reference kind.39// Example:40//   Target:      int $explicit^foo();41//   Referencing: int x = ^foo();42// There must be exactly one referencing location marked.43// Returns target decls.44std::vector<Decl::Kind> testWalk(llvm::StringRef TargetCode,45                                 llvm::StringRef ReferencingCode) {46  llvm::Annotations Target(TargetCode);47  llvm::Annotations Referencing(ReferencingCode);48 49  TestInputs Inputs(Referencing.code());50  Inputs.ExtraFiles["target.h"] = Target.code().str();51  Inputs.ExtraArgs.push_back("-include");52  Inputs.ExtraArgs.push_back("target.h");53  Inputs.ExtraArgs.push_back("-std=c++20");54  TestAST AST(Inputs);55  const auto &SM = AST.sourceManager();56 57  // We're only going to record references from the nominated point,58  // to the target file.59  FileID ReferencingFile = SM.getMainFileID();60  SourceLocation ReferencingLoc =61      SM.getComposedLoc(ReferencingFile, Referencing.point());62  FileID TargetFile = SM.translateFile(63      llvm::cantFail(AST.fileManager().getFileRef("target.h")));64 65  std::vector<Decl::Kind> TargetDecls;66  // Perform the walk, and capture the offsets of the referenced targets.67  std::unordered_map<RefType, std::vector<size_t>> ReferencedOffsets;68  for (Decl *D : AST.context().getTranslationUnitDecl()->decls()) {69    if (ReferencingFile != SM.getDecomposedExpansionLoc(D->getLocation()).first)70      continue;71    walkAST(*D, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) {72      if (SM.getFileLoc(Loc) != ReferencingLoc)73        return;74      auto NDLoc = SM.getDecomposedLoc(SM.getFileLoc(ND.getLocation()));75      if (NDLoc.first != TargetFile)76        return;77      ReferencedOffsets[RT].push_back(NDLoc.second);78      TargetDecls.push_back(ND.getKind());79    });80  }81  for (auto &Entry : ReferencedOffsets)82    llvm::sort(Entry.second);83 84  // Compare results to the expected points.85  // For each difference, show the target point in context, like a diagnostic.86  std::string DiagBuf;87  llvm::raw_string_ostream DiagOS(DiagBuf);88  DiagnosticOptions DiagOpts;89  DiagOpts.ShowLevel = 0;90  DiagOpts.ShowNoteIncludeStack = 0;91  TextDiagnostic Diag(DiagOS, AST.context().getLangOpts(), DiagOpts);92  auto DiagnosePoint = [&](llvm::StringRef Message, unsigned Offset) {93    Diag.emitDiagnostic(94        FullSourceLoc(SM.getComposedLoc(TargetFile, Offset), SM),95        DiagnosticsEngine::Note, Message, {}, {});96  };97  for (auto RT : {RefType::Explicit, RefType::Implicit, RefType::Ambiguous}) {98    auto RTStr = llvm::to_string(RT);99    for (auto Expected : Target.points(RTStr))100      if (!llvm::is_contained(ReferencedOffsets[RT], Expected))101        DiagnosePoint("location not marked used with type " + RTStr, Expected);102    for (auto Actual : ReferencedOffsets[RT])103      if (!llvm::is_contained(Target.points(RTStr), Actual))104        DiagnosePoint("location unexpectedly used with type " + RTStr, Actual);105  }106 107  // If there were any differences, we print the entire referencing code once.108  if (!DiagBuf.empty())109    ADD_FAILURE() << DiagBuf << "\nfrom code:\n" << ReferencingCode;110  return TargetDecls;111}112 113TEST(WalkAST, DeclRef) {114  testWalk("int $explicit^x;", "int y = ^x;");115  testWalk("int $explicit^foo();", "int y = ^foo();");116  testWalk("namespace ns { int $explicit^x; }", "int y = ns::^x;");117  testWalk("struct S { static int x; };", "int y = S::^x;");118  // Canonical declaration only.119  testWalk("extern int $explicit^x; int x;", "int y = ^x;");120  // Return type of `foo` isn't used.121  testWalk("struct S{}; S $explicit^foo();", "auto bar() { return ^foo(); }");122}123 124TEST(WalkAST, TagType) {125  testWalk("struct $explicit^S {};", "^S *y;");126  testWalk("enum $explicit^E {};", "^E *y;");127  testWalk("struct $explicit^S { static int x; };", "int y = ^S::x;");128  // One explicit call from the TypeLoc in constructor spelling, another129  // implicit reference through the constructor call.130  testWalk("struct $explicit^$implicit^S { static int x; };", "auto y = ^S();");131}132 133TEST(WalkAST, ClassTemplates) {134  // Explicit instantiation and (partial) specialization references primary135  // template.136  EXPECT_THAT(testWalk("template<typename> struct $explicit^Foo{};",137                       "template struct ^Foo<int>;"),138              ElementsAre(Decl::CXXRecord));139  EXPECT_THAT(testWalk("template<typename> struct $explicit^Foo{};",140                       "template<> struct ^Foo<int> {};"),141              ElementsAre(Decl::CXXRecord));142  EXPECT_THAT(testWalk("template<typename> struct $explicit^Foo{};",143                       "template<typename T> struct ^Foo<T*> {};"),144              ElementsAre(Decl::CXXRecord));145 146  // Implicit instantiations references most relevant template.147  EXPECT_THAT(148      testWalk("template<typename> struct $explicit^Foo;", "^Foo<int> x();"),149      ElementsAre(Decl::Kind::ClassTemplate));150  EXPECT_THAT(151      testWalk("template<typename> struct $explicit^Foo {};", "^Foo<int> x;"),152      ElementsAre(Decl::CXXRecord));153  EXPECT_THAT(testWalk(R"cpp(154    template<typename> struct Foo {};155    template<> struct $explicit^Foo<int> {};)cpp",156                       "^Foo<int> x;"),157              ElementsAre(Decl::ClassTemplateSpecialization));158  EXPECT_THAT(testWalk(R"cpp(159    template<typename> struct Foo {};160    template<typename T> struct $explicit^Foo<T*> {};)cpp",161                       "^Foo<int *> x;"),162              ElementsAre(Decl::ClassTemplatePartialSpecialization));163  // Incomplete instantiations don't have a specific specialization associated.164  EXPECT_THAT(testWalk(R"cpp(165    template<typename> struct $explicit^Foo;166    template<typename T> struct Foo<T*>;)cpp",167                       "^Foo<int *> x();"),168              ElementsAre(Decl::Kind::ClassTemplate));169  EXPECT_THAT(testWalk(R"cpp(170    template<typename> struct $explicit^Foo {};171    template struct Foo<int>;)cpp",172                       "^Foo<int> x;"),173              ElementsAre(Decl::CXXRecord));174  // FIXME: This is broken due to175  // https://github.com/llvm/llvm-project/issues/42259.176  EXPECT_THAT(testWalk(R"cpp(177    template<typename T> struct $explicit^Foo { Foo(T); };178    template<> struct Foo<int> { Foo(int); };)cpp",179                       "^Foo x(3);"),180              ElementsAre(Decl::ClassTemplate));181}182TEST(WalkAST, VarTemplates) {183  // Explicit instantiation and (partial) specialization references primary184  // template.185  // FIXME: Explicit instantiations has wrong source location, they point at the186  // primary template location (hence we drop the reference).187  EXPECT_THAT(188      testWalk("template<typename T> T Foo = 0;", "template int ^Foo<int>;"),189      ElementsAre());190  EXPECT_THAT(testWalk("template<typename T> T $explicit^Foo = 0;",191                       "template<> int ^Foo<int> = 2;"),192              ElementsAre(Decl::Var));193  EXPECT_THAT(testWalk("template<typename T> T $explicit^Foo = 0;",194                       "template<typename T> T* ^Foo<T*> = 1;"),195              ElementsAre(Decl::Var));196 197  // Implicit instantiations references most relevant template.198  // FIXME: This points at implicit specialization, instead we should point to199  // pattern.200  EXPECT_THAT(testWalk(R"cpp(201    template <typename T> T $explicit^Foo = 0;)cpp",202                       "int z = ^Foo<int>;"),203              ElementsAre(Decl::VarTemplateSpecialization));204  EXPECT_THAT(testWalk(R"cpp(205    template<typename T> T Foo = 0;206    template<> int $explicit^Foo<int> = 1;)cpp",207                       "int x = ^Foo<int>;"),208              ElementsAre(Decl::VarTemplateSpecialization));209  // FIXME: This points at implicit specialization, instead we should point to210  // explicit partial specializaiton pattern.211  EXPECT_THAT(testWalk(R"cpp(212    template<typename T> T Foo = 0;213    template<typename T> T* $explicit^Foo<T*> = nullptr;)cpp",214                       "int *x = ^Foo<int *>;"),215              ElementsAre(Decl::VarTemplateSpecialization));216  // Implicit specializations through explicit instantiations has source217  // locations pointing at the primary template.218  EXPECT_THAT(testWalk(R"cpp(219    template<typename T> T $explicit^Foo = 0;220    template int Foo<int>;)cpp",221                       "int x = ^Foo<int>;"),222              ElementsAre(Decl::VarTemplateSpecialization));223}224TEST(WalkAST, FunctionTemplates) {225  // Explicit instantiation and (partial) specialization references primary226  // template.227  // FIXME: Explicit instantiations has wrong source location, they point at the228  // primary template location (hence we drop the reference).229  EXPECT_THAT(testWalk("template<typename T> void foo(T) {}",230                       "template void ^foo<int>(int);"),231              ElementsAre());232  EXPECT_THAT(testWalk("template<typename T> void $explicit^foo(T);",233                       "template<> void ^foo<int>(int);"),234              ElementsAre(Decl::FunctionTemplate));235 236  // Implicit instantiations references most relevant template.237  EXPECT_THAT(testWalk(R"cpp(238    template <typename T> void $explicit^foo() {})cpp",239                       "auto x = []{ ^foo<int>(); };"),240              ElementsAre(Decl::Function));241  EXPECT_THAT(testWalk(R"cpp(242    template<typename T> void foo() {}243    template<> void $explicit^foo<int>(){})cpp",244                       "auto x = []{ ^foo<int>(); };"),245              ElementsAre(Decl::Function));246  // The decl is actually the specialization, but explicit instantations point247  // at the primary template.248  EXPECT_THAT(testWalk(R"cpp(249    template<typename T> void $explicit^foo() {};250    template void foo<int>();)cpp",251                       "auto x = [] { ^foo<int>(); };"),252              ElementsAre(Decl::Function));253}254TEST(WalkAST, TemplateSpecializationsFromUsingDecl) {255  // Class templates256  testWalk(R"cpp(257namespace ns {258template<class T> class $explicit^Z {};      // primary template259template<class T> class $ambiguous^Z<T*> {};  // partial specialization260template<> class $ambiguous^Z<int> {};        // full specialization261}262  )cpp",263           "using ns::^Z;");264 265  // Var templates266  testWalk(R"cpp(267namespace ns {268template<class T> T $explicit^foo;      // primary template269template<class T> T $ambiguous^foo<T*>;  // partial specialization270template<> int* $ambiguous^foo<int>;     // full specialization271}272  )cpp",273           "using ns::^foo;");274  // Function templates, no partial template specializations.275  testWalk(R"cpp(276namespace ns {277template<class T> void $ambiguous^function(T);  // primary template278template<> void $ambiguous^function(int);       // full specialization279}280  )cpp",281           "using ns::^function;");282}283 284TEST(WalkAST, Alias) {285  testWalk(R"cpp(286    namespace ns { int x; }287    using ns::$explicit^x;288  )cpp",289           "int y = ^x;");290  testWalk("using $explicit^foo = int;", "^foo x;");291  testWalk("struct S {}; using $explicit^foo = S;", "^foo x;");292  testWalk(R"cpp(293    template<typename> struct Foo {};294    template<> struct Foo<int> {};295    namespace ns { using ::$explicit^Foo; })cpp",296           "ns::^Foo<int> x;");297  testWalk(R"cpp(298    template<typename> struct Foo {};299    namespace ns { using ::Foo; }300    template<> struct ns::$explicit^Foo<int> {};)cpp",301           "^Foo<int> x;");302  // AST doesn't have enough information to figure out whether specialization303  // happened through an exported type or not. So err towards attributing use to304  // the using-decl, specializations on the exported type should be rare and305  // they're not permitted on type-aliases.306  testWalk(R"cpp(307    template<typename> struct Foo {};308    namespace ns { using ::$explicit^Foo; }309    template<> struct ns::Foo<int> {};)cpp",310           "ns::^Foo<int> x;");311  testWalk(R"cpp(312    namespace ns { enum class foo { bar }; }313    using ns::foo;)cpp",314           "auto x = foo::^bar;");315  testWalk(R"cpp(316    namespace ns { enum foo { bar }; }317    using ns::foo::$explicit^bar;)cpp",318           "auto x = ^bar;");319}320 321TEST(WalkAST, Using) {322  // We should report unused overloads as ambiguous.323  testWalk(R"cpp(324    namespace ns {325      void $explicit^x(); void $ambiguous^x(int); void $ambiguous^x(char);326    })cpp",327           "using ns::^x; void foo() { x(); }");328  testWalk(R"cpp(329    namespace ns {330      void $ambiguous^x(); void $ambiguous^x(int); void $ambiguous^x(char);331    })cpp",332           "using ns::^x;");333  testWalk("namespace ns { struct S; } using ns::$explicit^S;", "^S *s;");334 335  testWalk(R"cpp(336    namespace ns {337      template<class T>338      class $explicit^Y {};339    })cpp",340           "using ns::^Y;");341  testWalk(R"cpp(342    namespace ns {343      class $explicit^Y {};344    })cpp",345           "using ns::^Y;");346  testWalk(R"cpp(347    namespace ns {348      template<class T>349      class Y {};350    }351    using ns::$explicit^Y;)cpp",352           "^Y<int> x;");353  testWalk("namespace ns { enum E {A}; } using enum ns::$explicit^E;",354           "auto x = ^A;");355}356 357TEST(WalkAST, Namespaces) {358  testWalk("namespace ns { void x(); }", "using namespace ^ns;");359}360 361TEST(WalkAST, TemplateNames) {362  testWalk("template<typename> struct $explicit^S {};", "^S<int> s;");363  // FIXME: Template decl has the wrong primary location for type-alias template364  // decls.365  testWalk(R"cpp(366      template <typename> struct S {};367      template <typename T> $explicit^using foo = S<T>;)cpp",368           "^foo<int> x;");369  testWalk(R"cpp(370      namespace ns {template <typename> struct S {}; }371      using ns::$explicit^S;)cpp",372           "^S<int> x;");373  testWalk(R"cpp(374      namespace ns {375        template <typename T> struct S { S(T);};376        template <typename T> S(T t) -> S<T>;377      }378      using ns::$explicit^S;)cpp",379           "^S x(123);");380  testWalk("template<typename> struct $explicit^S {};",381           R"cpp(382      template <template <typename> typename> struct X {};383      X<^S> x;)cpp");384  testWalk("template<typename T> struct $explicit^S { S(T); };", "^S s(42);");385}386 387TEST(WalkAST, NestedTypes) {388  testWalk(R"cpp(389      struct Base { typedef int $implicit^a; };390      struct Derived : public Base {};)cpp",391           "void fun() { Derived::^a x; }");392  testWalk(R"cpp(393      struct Base { using $implicit^a = int; };394      struct Derived : public Base {};)cpp",395           "void fun() { Derived::^a x; }");396  testWalk(R"cpp(397      struct ns { struct a {}; };398      struct Base : public ns { using ns::$implicit^a; };399      struct Derived : public Base {};)cpp",400           "void fun() { Derived::^a x; }");401  testWalk(R"cpp(402      struct Base { struct $implicit^a {}; };403      struct Derived : public Base {};)cpp",404           "void fun() { Derived::^a x; }");405  testWalk("struct Base { struct $implicit^a {}; };",406           "struct Derived : public Base { ^a x; };");407  testWalk(R"cpp(408      struct Base { struct $implicit^a {}; };409      struct Derived : public Base {};410      struct SoDerived : public Derived {};411      )cpp",412           "void fun() { SoDerived::Derived::^a x; }");413}414 415TEST(WalkAST, MemberExprs) {416  testWalk("struct S { static int f; };", "void foo() { S::^f; }");417  testWalk("struct B { static int f; }; struct S : B {};",418           "void foo() { S::^f; }");419  testWalk("struct B { static void f(); }; struct S : B {};",420           "void foo() { S::^f; }");421  testWalk("struct B { static void f(); }; ",422           "struct S : B { void foo() { ^f(); } };");423  testWalk("struct $implicit^S { void foo(); };", "void foo() { S{}.^foo(); }");424  testWalk(425      "struct S { void foo(); }; struct $implicit^X : S { using S::foo; };",426      "void foo() { X{}.^foo(); }");427  testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",428           "void fun(Derived d) { d.^a; }");429  testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",430           "void fun(Derived* d) { d->^a; }");431  testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",432           "void fun(Derived& d) { d.^a; }");433  testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",434           "void fun() { Derived().^a; }");435  testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",436           "Derived foo(); void fun() { foo().^a; }");437  testWalk("struct Base { int a; }; struct $implicit^Derived : public Base {};",438           "Derived& foo(); void fun() { foo().^a; }");439  testWalk(R"cpp(440      template <typename T>441      struct unique_ptr {442        T *operator->();443      };444      struct $implicit^Foo { int a; };)cpp",445           "void test(unique_ptr<Foo> &V) { V->^a; }");446  testWalk(R"cpp(447      template <typename T>448      struct $implicit^unique_ptr {449        void release();450      };451      struct Foo {};)cpp",452           "void test(unique_ptr<Foo> &V) { V.^release(); }");453  // Respect the sugar type (typedef, using-type).454  testWalk(R"cpp(455      namespace ns { struct Foo { int a; }; }456      using $implicit^Bar = ns::Foo;)cpp",457           "void test(Bar b) { b.^a; }");458  testWalk(R"cpp(459      namespace ns { struct Foo { int a; }; }460      using ns::$implicit^Foo;)cpp",461           "void test(Foo b) { b.^a; }");462  testWalk(R"cpp(463      namespace ns { struct Foo { int a; }; }464      namespace ns2 { using Bar = ns::Foo; }465      using ns2::$implicit^Bar;466      )cpp",467           "void test(Bar b) { b.^a; }");468  testWalk(R"cpp(469      namespace ns { template<typename> struct Foo { int a; }; }470      using ns::$implicit^Foo;)cpp",471           "void k(Foo<int> b) { b.^a; }");472  // Test the dependent-type case (CXXDependentScopeMemberExpr)473  testWalk("template<typename T> struct $implicit^Base { void method(); };",474           "template<typename T> void k(Base<T> t) { t.^method(); }");475  testWalk("template<typename T> struct $implicit^Base { void method(); };",476           "template<typename T> void k(Base<T>& t) { t.^method(); }");477  testWalk("template<typename T> struct $implicit^Base { void method(); };",478           "template<typename T> void k(Base<T>* t) { t->^method(); }");479}480 481TEST(WalkAST, ConstructExprs) {482  testWalk("struct $implicit^S {};", "S ^t;");483  testWalk("struct $implicit^S { S(); };", "S ^t;");484  testWalk("struct $implicit^S { S(int); };", "S ^t(42);");485  testWalk("struct $implicit^S { S(int); };", "S t = ^42;");486  testWalk("namespace ns { struct S{}; } using ns::$implicit^S;", "S ^t;");487}488 489TEST(WalkAST, Operator) {490  // Operator calls are marked as implicit references as they're ADL-used and491  // type should be providing them.492  testWalk(493      "struct string { friend int $implicit^operator+(string, string); }; ",494      "int k = string() ^+ string();");495  // Treat member operators as regular member expr calls.496  testWalk("struct $implicit^string {int operator+(string); }; ",497           "int k = string() ^+ string();");498  // Make sure usage is attributed to the alias.499  testWalk(500      "struct string {int operator+(string); }; using $implicit^foo = string;",501      "int k = foo() ^+ string();");502}503 504TEST(WalkAST, VarDecls) {505  // Definition uses declaration, not the other way around.506  testWalk("extern int $explicit^x;", "int ^x = 1;");507  testWalk("int x = 1;", "extern int ^x;");508}509 510TEST(WalkAST, Functions) {511  // Definition uses declaration, not the other way around.512  testWalk("void $explicit^foo();", "void ^foo() {}");513  testWalk("void foo() {}", "void ^foo();");514  testWalk("template <typename> void $explicit^foo();",515           "template <typename> void ^foo() {}");516 517  // Unresolved calls marks all the overloads.518  testWalk("void $ambiguous^foo(int); void $ambiguous^foo(char);",519           "template <typename T> void bar() { ^foo(T{}); }");520}521 522TEST(WalkAST, Enums) {523  testWalk("enum E { $explicit^A = 42 };", "int e = ^A;");524  testWalk("enum class $explicit^E : int;", "enum class ^E : int {};");525  testWalk("enum class E : int {};", "enum class ^E : int ;");526  testWalk("namespace ns { enum E { $explicit^A = 42 }; }", "int e = ns::^A;");527  testWalk("namespace ns { enum E { A = 42 }; } using ns::E::$explicit^A;",528           "int e = ^A;");529  testWalk("namespace ns { enum E { A = 42 }; } using enum ns::$explicit^E;",530           "int e = ^A;");531  testWalk(R"(namespace ns { enum E { A = 42 }; }532              struct S { using enum ns::E; };)",533           "int e = S::^A;");534  testWalk(R"(namespace ns { enum E { A = 42 }; }535              struct S { using ns::E::A; };)",536           "int e = S::^A;");537  testWalk(R"(namespace ns { enum E { $explicit^A = 42 }; })",538           "namespace z = ns; int e = z::^A;");539  testWalk(R"(enum E { $explicit^A = 42 };)", "int e = ::^A;");540}541 542TEST(WalkAST, InitializerList) {543  testWalk(R"cpp(544       namespace std {545        template <typename T> struct $implicit^initializer_list { const T *a, *b; };546       })cpp",547           R"cpp(548       const char* s = "";549       auto sx = ^{s};)cpp");550}551 552TEST(WalkAST, Concepts) {553  std::string Concept = "template<typename T> concept $explicit^Foo = true;";554  testWalk(Concept, "template<typename T>concept Bar = ^Foo<T> && true;");555  testWalk(Concept, "template<^Foo T>void func() {}");556  testWalk(Concept, "template<typename T> requires ^Foo<T> void func() {}");557  testWalk(Concept, "template<typename T> void func() requires ^Foo<T> {}");558  testWalk(Concept, "void func(^Foo auto x) {}");559  testWalk(Concept, "void func() { ^Foo auto x = 1; }");560}561 562TEST(WalkAST, FriendDecl) {563  testWalk("void $explicit^foo();", "struct Bar { friend void ^foo(); };");564  testWalk("struct $explicit^Foo {};", "struct Bar { friend struct ^Foo; };");565}566 567TEST(WalkAST, OperatorNewDelete) {568  testWalk("void* $ambiguous^operator new(decltype(sizeof(int)), void*);",569           "struct Bar { void foo() { Bar b; ^new (&b) Bar; } };");570  testWalk("struct A { static void $ambiguous^operator delete(void*); };",571           "void foo() { A a; ^delete &a; }");572}573 574TEST(WalkAST, CleanupAttr) {575  testWalk("void* $explicit^freep(void *p);",576           "void foo() { __attribute__((__cleanup__(^freep))) char* x = 0; }");577}578 579} // namespace580} // namespace clang::include_cleaner581