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