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, 116 Args, 117 /*SuppressUnwrittenScope*/ false, 118 namedDecl(hasName(DeclName)).bind("id"), 119 ExpectedPrinted, 120 "input.cc"); 121 } 122 123 ::testing::AssertionResult 124 PrintedWrittenNamedDeclCXX11Matches(StringRef Code, StringRef DeclName, 125 StringRef ExpectedPrinted) { 126 std::vector<std::string> Args(1, "-std=c++11"); 127 return PrintedNamedDeclMatches(Code, 128 Args, 129 /*SuppressUnwrittenScope*/ true, 130 namedDecl(hasName(DeclName)).bind("id"), 131 ExpectedPrinted, 132 "input.cc"); 133 } 134 135 ::testing::AssertionResult 136 PrintedWrittenPropertyDeclObjCMatches(StringRef Code, StringRef DeclName, 137 StringRef ExpectedPrinted) { 138 std::vector<std::string> Args{"-std=c++11", "-xobjective-c++"}; 139 return PrintedNamedDeclMatches(Code, 140 Args, 141 /*SuppressUnwrittenScope*/ true, 142 objcPropertyDecl(hasName(DeclName)).bind("id"), 143 ExpectedPrinted, 144 "input.m"); 145 } 146 147 ::testing::AssertionResult 148 PrintedNestedNameSpecifierMatches(StringRef Code, StringRef DeclName, 149 StringRef ExpectedPrinted) { 150 std::vector<std::string> Args{"-std=c++11"}; 151 return PrintedDeclMatches(Code, Args, namedDecl(hasName(DeclName)).bind("id"), 152 ExpectedPrinted, "input.cc", 153 [](llvm::raw_ostream &Out, const NamedDecl *D) { 154 D->printNestedNameSpecifier(Out); 155 }); 156 } 157 158 } // unnamed namespace 159 160 TEST(NamedDeclPrinter, TestNamespace1) { 161 ASSERT_TRUE(PrintedNamedDeclCXX98Matches( 162 "namespace { int A; }", 163 "A", 164 "(anonymous namespace)::A")); 165 } 166 167 TEST(NamedDeclPrinter, TestNamespace2) { 168 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 169 "inline namespace Z { namespace { int A; } }", 170 "A", 171 "A")); 172 } 173 174 TEST(NamedDeclPrinter, TestUnscopedUnnamedEnum) { 175 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 176 "enum { A };", 177 "A", 178 "A")); 179 } 180 181 TEST(NamedDeclPrinter, TestNamedEnum) { 182 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 183 "enum X { A };", 184 "A", 185 "A")); 186 } 187 188 TEST(NamedDeclPrinter, TestScopedNamedEnum) { 189 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 190 "enum class X { A };", 191 "A", 192 "X::A")); 193 } 194 195 TEST(NamedDeclPrinter, TestClassWithUnscopedUnnamedEnum) { 196 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 197 "class X { enum { A }; };", 198 "A", 199 "X::A")); 200 } 201 202 TEST(NamedDeclPrinter, TestClassWithUnscopedNamedEnum) { 203 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 204 "class X { enum Y { A }; };", 205 "A", 206 "X::A")); 207 } 208 209 TEST(NamedDeclPrinter, TestClassWithScopedNamedEnum) { 210 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 211 "class X { enum class Y { A }; };", 212 "A", 213 "X::Y::A")); 214 } 215 216 TEST(NamedDeclPrinter, TestLinkageInNamespace) { 217 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches( 218 "namespace X { extern \"C\" { int A; } }", 219 "A", 220 "X::A")); 221 } 222 223 TEST(NamedDeclPrinter, TestObjCClassExtension) { 224 const char *Code = 225 R"( 226 @interface Obj 227 @end 228 229 @interface Obj () 230 @property(nonatomic) int property; 231 @end 232 )"; 233 ASSERT_TRUE(PrintedWrittenPropertyDeclObjCMatches( 234 Code, 235 "property", 236 "Obj::property")); 237 } 238 239 TEST(NamedDeclPrinter, TestObjCClassExtensionWithGetter) { 240 const char *Code = 241 R"( 242 @interface Obj 243 @end 244 245 @interface Obj () 246 @property(nonatomic, getter=myPropertyGetter) int property; 247 @end 248 )"; 249 ASSERT_TRUE(PrintedWrittenPropertyDeclObjCMatches( 250 Code, 251 "property", 252 "Obj::property")); 253 } 254 255 TEST(NamedDeclPrinter, NestedNameSpecifierSimple) { 256 const char *Code = 257 R"( 258 namespace foo { namespace bar { void func(); } } 259 )"; 260 ASSERT_TRUE(PrintedNestedNameSpecifierMatches(Code, "func", "foo::bar::")); 261 } 262 263 TEST(NamedDeclPrinter, NestedNameSpecifierTemplateArgs) { 264 const char *Code = 265 R"( 266 template <class T> struct vector; 267 template <> struct vector<int> { int method(); }; 268 )"; 269 ASSERT_TRUE( 270 PrintedNestedNameSpecifierMatches(Code, "method", "vector<int>::")); 271 } 272