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