1 //===- unittests/AST/NamedDeclPrinterTest.cpp --- NamedDecl printer 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file contains tests for NamedDecl::printQualifiedName(). 10 // 11 // These tests have a coding convention: 12 // * declaration to be printed is named 'A' unless it should have some special 13 // name (e.g., 'operator+'); 14 // * additional helper declarations are 'Z', 'Y', 'X' and so on. 15 // 16 //===----------------------------------------------------------------------===// 17 18 #include "clang/AST/ASTContext.h" 19 #include "clang/AST/Decl.h" 20 #include "clang/AST/PrettyPrinter.h" 21 #include "clang/ASTMatchers/ASTMatchFinder.h" 22 #include "clang/Tooling/Tooling.h" 23 #include "llvm/ADT/SmallString.h" 24 #include "llvm/Support/raw_ostream.h" 25 #include "gtest/gtest.h" 26 27 using namespace clang; 28 using namespace ast_matchers; 29 using namespace tooling; 30 31 namespace { 32 33 class PrintMatch : public MatchFinder::MatchCallback { 34 SmallString<1024> Printed; 35 unsigned NumFoundDecls; 36 std::function<void(llvm::raw_ostream &OS, const NamedDecl *)> Printer; 37 38 public: 39 explicit PrintMatch( 40 std::function<void(llvm::raw_ostream &OS, const NamedDecl *)> Printer) 41 : NumFoundDecls(0), Printer(std::move(Printer)) {} 42 43 void run(const MatchFinder::MatchResult &Result) override { 44 const NamedDecl *ND = Result.Nodes.getNodeAs<NamedDecl>("id"); 45 if (!ND) 46 return; 47 NumFoundDecls++; 48 if (NumFoundDecls > 1) 49 return; 50 51 llvm::raw_svector_ostream Out(Printed); 52 Printer(Out, ND); 53 } 54 55 StringRef getPrinted() const { 56 return Printed; 57 } 58 59 unsigned getNumFoundDecls() const { 60 return NumFoundDecls; 61 } 62 }; 63 64 ::testing::AssertionResult PrintedDeclMatches( 65 StringRef Code, const std::vector<std::string> &Args, 66 const DeclarationMatcher &NodeMatch, StringRef ExpectedPrinted, 67 StringRef FileName, 68 std::function<void(llvm::raw_ostream &, const NamedDecl *)> Print) { 69 PrintMatch Printer(std::move(Print)); 70 MatchFinder Finder; 71 Finder.addMatcher(NodeMatch, &Printer); 72 std::unique_ptr<FrontendActionFactory> Factory = 73 newFrontendActionFactory(&Finder); 74 75 if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName)) 76 return testing::AssertionFailure() 77 << "Parsing error in \"" << Code.str() << "\""; 78 79 if (Printer.getNumFoundDecls() == 0) 80 return testing::AssertionFailure() 81 << "Matcher didn't find any named declarations"; 82 83 if (Printer.getNumFoundDecls() > 1) 84 return testing::AssertionFailure() 85 << "Matcher should match only one named declaration " 86 "(found " << Printer.getNumFoundDecls() << ")"; 87 88 if (Printer.getPrinted() != ExpectedPrinted) 89 return ::testing::AssertionFailure() 90 << "Expected \"" << ExpectedPrinted.str() << "\", " 91 "got \"" << Printer.getPrinted().str() << "\""; 92 93 return ::testing::AssertionSuccess(); 94 } 95 96 ::testing::AssertionResult 97 PrintedNamedDeclMatches(StringRef Code, const std::vector<std::string> &Args, 98 bool SuppressUnwrittenScope, 99 const DeclarationMatcher &NodeMatch, 100 StringRef ExpectedPrinted, StringRef FileName) { 101 return PrintedDeclMatches(Code, Args, NodeMatch, ExpectedPrinted, FileName, 102 [=](llvm::raw_ostream &Out, const NamedDecl *ND) { 103 auto Policy = 104 ND->getASTContext().getPrintingPolicy(); 105 Policy.SuppressUnwrittenScope = 106 SuppressUnwrittenScope; 107 ND->printQualifiedName(Out, Policy); 108 }); 109 } 110 111 ::testing::AssertionResult 112 PrintedNamedDeclCXX98Matches(StringRef Code, StringRef DeclName, 113 StringRef ExpectedPrinted) { 114 std::vector<std::string> Args(1, "-std=c++98"); 115 return PrintedNamedDeclMatches(Code, Args, 116 /*SuppressUnwrittenScope*/ false, 117 namedDecl(hasName(DeclName)).bind("id"), 118 ExpectedPrinted, "input.cc"); 119 } 120 121 ::testing::AssertionResult 122 PrintedWrittenNamedDeclCXX11Matches(StringRef Code, StringRef DeclName, 123 StringRef ExpectedPrinted) { 124 std::vector<std::string> Args(1, "-std=c++11"); 125 return PrintedNamedDeclMatches(Code, Args, 126 /*SuppressUnwrittenScope*/ true, 127 namedDecl(hasName(DeclName)).bind("id"), 128 ExpectedPrinted, "input.cc"); 129 } 130 131 ::testing::AssertionResult 132 PrintedWrittenPropertyDeclObjCMatches(StringRef Code, StringRef DeclName, 133 StringRef ExpectedPrinted) { 134 std::vector<std::string> Args{"-std=c++11", "-xobjective-c++"}; 135 return PrintedNamedDeclMatches(Code, Args, 136 /*SuppressUnwrittenScope*/ true, 137 objcPropertyDecl(hasName(DeclName)).bind("id"), 138 ExpectedPrinted, "input.m"); 139 } 140 141 ::testing::AssertionResult 142 PrintedNestedNameSpecifierMatches(StringRef Code, StringRef DeclName, 143 StringRef ExpectedPrinted) { 144 std::vector<std::string> Args{"-std=c++11"}; 145 return PrintedDeclMatches(Code, Args, namedDecl(hasName(DeclName)).bind("id"), 146 ExpectedPrinted, "input.cc", 147 [](llvm::raw_ostream &Out, const NamedDecl *D) { 148 D->printNestedNameSpecifier(Out); 149 }); 150 } 151 152 } // unnamed namespace 153 154 TEST(NamedDeclPrinter, TestNamespace1) { 155 ASSERT_TRUE(PrintedNamedDeclCXX98Matches( 156 "namespace { int A; }", 157 "A", 158 "(anonymous namespace)::A")); 159 } 160 161 TEST(NamedDeclPrinter, TestNamespace2) { 162 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 163 "inline namespace Z { namespace { int A; } }", 164 "A", 165 "A")); 166 } 167 168 TEST(NamedDeclPrinter, TestUnscopedUnnamedEnum) { 169 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 170 "enum { A };", 171 "A", 172 "A")); 173 } 174 175 TEST(NamedDeclPrinter, TestNamedEnum) { 176 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 177 "enum X { A };", 178 "A", 179 "A")); 180 } 181 182 TEST(NamedDeclPrinter, TestScopedNamedEnum) { 183 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 184 "enum class X { A };", 185 "A", 186 "X::A")); 187 } 188 189 TEST(NamedDeclPrinter, TestClassWithUnscopedUnnamedEnum) { 190 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 191 "class X { enum { A }; };", 192 "A", 193 "X::A")); 194 } 195 196 TEST(NamedDeclPrinter, TestClassWithUnscopedNamedEnum) { 197 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 198 "class X { enum Y { A }; };", 199 "A", 200 "X::A")); 201 } 202 203 TEST(NamedDeclPrinter, TestClassWithScopedNamedEnum) { 204 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 205 "class X { enum class Y { A }; };", 206 "A", 207 "X::Y::A")); 208 } 209 210 TEST(NamedDeclPrinter, TestLinkageInNamespace) { 211 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 212 "namespace X { extern \"C\" { int A; } }", 213 "A", 214 "X::A")); 215 } 216 217 TEST(NamedDeclPrinter, TestObjCClassExtension) { 218 const char *Code = 219 R"( 220 @interface Obj 221 @end 222 223 @interface Obj () 224 @property(nonatomic) int property; 225 @end 226 )"; 227 ASSERT_TRUE(PrintedWrittenPropertyDeclObjCMatches( 228 Code, 229 "property", 230 "Obj::property")); 231 } 232 233 TEST(NamedDeclPrinter, TestObjCClassExtensionWithGetter) { 234 const char *Code = 235 R"( 236 @interface Obj 237 @end 238 239 @interface Obj () 240 @property(nonatomic, getter=myPropertyGetter) int property; 241 @end 242 )"; 243 ASSERT_TRUE(PrintedWrittenPropertyDeclObjCMatches( 244 Code, 245 "property", 246 "Obj::property")); 247 } 248 249 TEST(NamedDeclPrinter, NestedNameSpecifierSimple) { 250 const char *Code = 251 R"( 252 namespace foo { namespace bar { void func(); } } 253 )"; 254 ASSERT_TRUE(PrintedNestedNameSpecifierMatches(Code, "func", "foo::bar::")); 255 } 256 257 TEST(NamedDeclPrinter, NestedNameSpecifierTemplateArgs) { 258 const char *Code = 259 R"( 260 template <class T> struct vector; 261 template <> struct vector<int> { int method(); }; 262 )"; 263 ASSERT_TRUE( 264 PrintedNestedNameSpecifierMatches(Code, "method", "vector<int>::")); 265 } 266