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
28using namespace clang;
29using namespace ast_matchers;
30using namespace tooling;
31
32namespace {
33
34class PrintMatch : public MatchFinder::MatchCallback {
35 SmallString<1024> Printed;
36 unsigned NumFoundDecls;
37 std::function<void(llvm::raw_ostream &OS, const NamedDecl *)> Printer;
38
39public:
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: "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 Printer: [Print](llvm::raw_ostream &Out, const ASTContext *Context,
73 const NamedDecl *ND,
74 PrintingPolicyAdjuster PolicyAdjuster) { Print(Out, ND); });
75}
76
77::testing::AssertionResult
78PrintedNamedDeclMatches(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 Print: [=](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
93PrintedNamedDeclCXX98Matches(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 NodeMatch: namedDecl(hasName(Name: DeclName)).bind(ID: "id"),
99 ExpectedPrinted, FileName: "input.cc");
100}
101
102::testing::AssertionResult
103PrintedWrittenNamedDeclCXX11Matches(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 NodeMatch: namedDecl(hasName(Name: DeclName)).bind(ID: "id"),
109 ExpectedPrinted, FileName: "input.cc");
110}
111
112::testing::AssertionResult
113PrintedWrittenPropertyDeclObjCMatches(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 NodeMatch: objcPropertyDecl(hasName(Name: DeclName)).bind(ID: "id"),
119 ExpectedPrinted, FileName: "input.m");
120}
121
122::testing::AssertionResult
123PrintedNestedNameSpecifierMatches(StringRef Code, StringRef DeclName,
124 StringRef ExpectedPrinted) {
125 std::vector<std::string> Args{"-std=c++11"};
126 return PrintedDeclMatches(Code, Args, NodeMatch: namedDecl(hasName(Name: DeclName)).bind(ID: "id"),
127 ExpectedPrinted, FileName: "input.cc",
128 Print: [](llvm::raw_ostream &Out, const NamedDecl *D) {
129 D->printNestedNameSpecifier(OS&: Out);
130 });
131}
132
133} // unnamed namespace
134
135TEST(NamedDeclPrinter, TestNamespace1) {
136 ASSERT_TRUE(PrintedNamedDeclCXX98Matches(
137 "namespace { int A; }",
138 "A",
139 "(anonymous namespace)::A"));
140}
141
142TEST(NamedDeclPrinter, TestNamespace2) {
143 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
144 "inline namespace Z { namespace { int A; } }",
145 "A",
146 "A"));
147}
148
149TEST(NamedDeclPrinter, TestUnscopedUnnamedEnum) {
150 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
151 "enum { A };",
152 "A",
153 "A"));
154}
155
156TEST(NamedDeclPrinter, TestNamedEnum) {
157 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
158 "enum X { A };",
159 "A",
160 "A"));
161}
162
163TEST(NamedDeclPrinter, TestScopedNamedEnum) {
164 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
165 "enum class X { A };",
166 "A",
167 "X::A"));
168}
169
170TEST(NamedDeclPrinter, TestClassWithUnscopedUnnamedEnum) {
171 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
172 "class X { enum { A }; };",
173 "A",
174 "X::A"));
175}
176
177TEST(NamedDeclPrinter, TestClassWithUnscopedNamedEnum) {
178 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
179 "class X { enum Y { A }; };",
180 "A",
181 "X::A"));
182}
183
184TEST(NamedDeclPrinter, TestClassWithScopedNamedEnum) {
185 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
186 "class X { enum class Y { A }; };",
187 "A",
188 "X::Y::A"));
189}
190
191TEST(NamedDeclPrinter, TestLinkageInNamespace) {
192 ASSERT_TRUE(PrintedWrittenNamedDeclCXX11Matches(
193 "namespace X { extern \"C\" { int A; } }",
194 "A",
195 "X::A"));
196}
197
198TEST(NamedDeclPrinter, TestObjCClassExtension) {
199 const char *Code =
200R"(
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
214TEST(NamedDeclPrinter, TestInstanceObjCClassExtension) {
215 const char *Code =
216R"(
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
235TEST(NamedDeclPrinter, TestObjCClassExtensionWithGetter) {
236 const char *Code =
237R"(
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
251TEST(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
259TEST(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

source code of clang/unittests/AST/NamedDeclPrinterTest.cpp