xref: /llvm-project/clang/unittests/AST/NamedDeclPrinterTest.cpp (revision 9cfec72ffeec242783b70e792c50bd163dcf9dbb)
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:
PrintMatch(std::function<void (llvm::raw_ostream & OS,const NamedDecl *)> Printer)40   explicit PrintMatch(
41       std::function<void(llvm::raw_ostream &OS, const NamedDecl *)> Printer)
42       : NumFoundDecls(0), Printer(std::move(Printer)) {}
43 
run(const MatchFinder::MatchResult & Result)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 
getPrinted() const56   StringRef getPrinted() const {
57     return Printed;
58   }
59 
getNumFoundDecls() const60   unsigned getNumFoundDecls() const {
61     return NumFoundDecls;
62   }
63 };
64 
PrintedDeclMatches(StringRef Code,const std::vector<std::string> & Args,const DeclarationMatcher & NodeMatch,StringRef ExpectedPrinted,StringRef FileName,std::function<void (llvm::raw_ostream &,const NamedDecl *)> Print)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
PrintedNamedDeclMatches(StringRef Code,const std::vector<std::string> & Args,bool SuppressUnwrittenScope,const DeclarationMatcher & NodeMatch,StringRef ExpectedPrinted,StringRef FileName)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
PrintedNamedDeclCXX98Matches(StringRef Code,StringRef DeclName,StringRef ExpectedPrinted)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
PrintedWrittenNamedDeclCXX11Matches(StringRef Code,StringRef DeclName,StringRef ExpectedPrinted)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
PrintedWrittenPropertyDeclObjCMatches(StringRef Code,StringRef DeclName,StringRef ExpectedPrinted)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
PrintedNestedNameSpecifierMatches(StringRef Code,StringRef DeclName,StringRef ExpectedPrinted)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 
TEST(NamedDeclPrinter,TestNamespace1)135 TEST(NamedDeclPrinter, TestNamespace1) {
136   ASSERT_TRUE(PrintedNamedDeclCXX98Matches(
137     "namespace { int A; }",
138     "A",
139     "(anonymous namespace)::A"));
140 }
141 
TEST(NamedDeclPrinter,TestNamespace2)142 TEST(NamedDeclPrinter, TestNamespace2) {
143   ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
144     "inline namespace Z { namespace { int A; } }",
145     "A",
146     "A"));
147 }
148 
TEST(NamedDeclPrinter,TestUnscopedUnnamedEnum)149 TEST(NamedDeclPrinter, TestUnscopedUnnamedEnum) {
150   ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
151     "enum { A };",
152     "A",
153     "A"));
154 }
155 
TEST(NamedDeclPrinter,TestNamedEnum)156 TEST(NamedDeclPrinter, TestNamedEnum) {
157   ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
158     "enum X { A };",
159     "A",
160     "A"));
161 }
162 
TEST(NamedDeclPrinter,TestScopedNamedEnum)163 TEST(NamedDeclPrinter, TestScopedNamedEnum) {
164   ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
165     "enum class X { A };",
166     "A",
167     "X::A"));
168 }
169 
TEST(NamedDeclPrinter,TestClassWithUnscopedUnnamedEnum)170 TEST(NamedDeclPrinter, TestClassWithUnscopedUnnamedEnum) {
171   ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
172     "class X { enum { A }; };",
173     "A",
174     "X::A"));
175 }
176 
TEST(NamedDeclPrinter,TestClassWithUnscopedNamedEnum)177 TEST(NamedDeclPrinter, TestClassWithUnscopedNamedEnum) {
178   ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
179     "class X { enum Y { A }; };",
180     "A",
181     "X::A"));
182 }
183 
TEST(NamedDeclPrinter,TestClassWithScopedNamedEnum)184 TEST(NamedDeclPrinter, TestClassWithScopedNamedEnum) {
185   ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
186     "class X { enum class Y { A }; };",
187     "A",
188     "X::Y::A"));
189 }
190 
TEST(NamedDeclPrinter,TestLinkageInNamespace)191 TEST(NamedDeclPrinter, TestLinkageInNamespace) {
192   ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
193     "namespace X { extern \"C\" { int A; } }",
194     "A",
195     "X::A"));
196 }
197 
TEST(NamedDeclPrinter,TestObjCClassExtension)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 
TEST(NamedDeclPrinter,TestInstanceObjCClassExtension)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 
TEST(NamedDeclPrinter,TestObjCClassExtensionWithGetter)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 
TEST(NamedDeclPrinter,NestedNameSpecifierSimple)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 
TEST(NamedDeclPrinter,NestedNameSpecifierTemplateArgs)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