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