1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
9 #ifndef LO_CLANG_SHARED_PLUGINS
10
11 #include <algorithm>
12 #include <cassert>
13
14 #include "clang/Sema/SemaDiagnostic.h"
15
16 #include "check.hxx"
17 #include "plugin.hxx"
18
19 namespace
20 {
derivesFromTestFixture(CXXRecordDecl const * decl)21 bool derivesFromTestFixture(CXXRecordDecl const* decl)
22 {
23 static auto const pred = [](CXXBaseSpecifier const& spec) {
24 if (auto const t = spec.getType()->getAs<RecordType>())
25 { // (may be a template parameter)
26 return derivesFromTestFixture(dyn_cast<CXXRecordDecl>(t->getDecl()));
27 }
28 return false;
29 };
30 return loplugin::DeclCheck(decl).Class("TestFixture").Namespace("CppUnit").GlobalNamespace()
31 || std::any_of(decl->bases_begin(), decl->bases_end(), pred)
32 || std::any_of(decl->vbases_begin(), decl->vbases_end(), pred);
33 }
34
35 class External : public loplugin::FilteringPlugin<External>
36 {
37 public:
External(loplugin::InstantiationData const & data)38 explicit External(loplugin::InstantiationData const& data)
39 : FilteringPlugin(data)
40 {
41 }
42
run()43 void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
44
VisitTagDecl(TagDecl const * decl)45 bool VisitTagDecl(TagDecl const* decl)
46 {
47 /*TODO:*/
48 return true; // in general, moving classes or enumerations into an unnamed namespace can break ADL
49 if (isa<ClassTemplateSpecializationDecl>(decl))
50 {
51 return true;
52 }
53 if (!decl->isThisDeclarationADefinition())
54 {
55 return true;
56 }
57 if (isa<CXXRecordDecl>(decl->getDeclContext()))
58 {
59 return true;
60 }
61 if (!compiler.getLangOpts().CPlusPlus)
62 {
63 return true;
64 }
65 if (auto const d = dyn_cast<CXXRecordDecl>(decl))
66 {
67 if (d->getDescribedClassTemplate() != nullptr)
68 {
69 return true;
70 }
71 if (auto const attr = d->getAttr<VisibilityAttr>())
72 {
73 if (attr->getVisibility() == VisibilityAttr::Default)
74 {
75 // If the class definition has explicit default visibility, then assume that it
76 // needs to be present (e.g., a backwards-compatibility stub like in
77 // cppuhelper/source/compat.cxx):
78 return true;
79 }
80 }
81 if (derivesFromTestFixture(d))
82 {
83 // The names of CppUnit tests (that can be specified with CPPUNIT_TEST_NAME) are
84 // tied to the fully-qualified names of classes derived from CppUnit::TestFixture,
85 // so avoid unnamed namespaces in those classes' names:
86 return true;
87 }
88 }
89 return handleDeclaration(decl);
90 }
91
VisitFunctionDecl(FunctionDecl const * decl)92 bool VisitFunctionDecl(FunctionDecl const* decl)
93 {
94 if (isa<CXXMethodDecl>(decl))
95 {
96 return true;
97 }
98 if (decl->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
99 {
100 return true;
101 }
102 if (!decl->isThisDeclarationADefinition())
103 {
104 return true;
105 }
106 if (decl->isMain() || decl->isMSVCRTEntryPoint())
107 {
108 return true;
109 }
110 if (loplugin::hasCLanguageLinkageType(decl)
111 && loplugin::DeclCheck(decl).Function("_DllMainCRTStartup").GlobalNamespace())
112 {
113 return true;
114 }
115 // If the function definition is explicit marked SAL_DLLPUBLIC_EXPORT or similar, then
116 // assume that it needs to be present (e.g., only called via dlopen, or a backwards-
117 // compatibility stub like in sal/osl/all/compat.cxx):
118 if (auto const attr = decl->getAttr<VisibilityAttr>())
119 {
120 if (attr->getVisibility() == VisibilityAttr::Default)
121 {
122 return true;
123 }
124 }
125 else if (decl->hasAttr<DLLExportAttr>())
126 {
127 return true;
128 }
129 auto const canon = decl->getCanonicalDecl();
130 if (loplugin::hasCLanguageLinkageType(canon)
131 && (canon->hasAttr<ConstructorAttr>() || canon->hasAttr<DestructorAttr>()))
132 {
133 return true;
134 }
135 if (compiler.getDiagnostics().getDiagnosticLevel(diag::warn_unused_function,
136 decl->getLocation())
137 < DiagnosticsEngine::Warning)
138 {
139 // Don't warn about e.g.
140 //
141 // G_DEFINE_TYPE (GLOAction, g_lo_action, G_TYPE_OBJECT);
142 //
143 // in vcl/unx/gtk/gloactiongroup.cxx (which expands to non-static g_lo_action_get_type
144 // function definition), which is already wrapped in
145 //
146 // #pragma GCC diagnostic ignored "-Wunused-function"
147 return true;
148 }
149 return handleDeclaration(decl);
150 }
151
VisitVarDecl(VarDecl const * decl)152 bool VisitVarDecl(VarDecl const* decl)
153 {
154 if (decl->isStaticDataMember())
155 {
156 return true;
157 }
158 if (isa<VarTemplateSpecializationDecl>(decl))
159 {
160 return true;
161 }
162 if (!decl->isThisDeclarationADefinition())
163 {
164 return true;
165 }
166 if (loplugin::DeclCheck(decl).Var("_pRawDllMain").GlobalNamespace())
167 {
168 return true;
169 }
170 return handleDeclaration(decl);
171 }
172
VisitClassTemplateDecl(ClassTemplateDecl const * decl)173 bool VisitClassTemplateDecl(ClassTemplateDecl const* decl)
174 {
175 /*TODO:*/
176 return true; // in general, moving classes or enumerations into an unnamed namespace can break ADL
177 if (!decl->isThisDeclarationADefinition())
178 {
179 return true;
180 }
181 if (isa<CXXRecordDecl>(decl->getDeclContext()))
182 {
183 return true;
184 }
185 return handleDeclaration(decl);
186 }
187
VisitFunctionTemplateDecl(FunctionTemplateDecl const * decl)188 bool VisitFunctionTemplateDecl(FunctionTemplateDecl const* decl)
189 {
190 if (!decl->isThisDeclarationADefinition())
191 {
192 return true;
193 }
194 if (isa<CXXRecordDecl>(decl->getDeclContext()))
195 {
196 return true;
197 }
198 return handleDeclaration(decl);
199 }
200
VisitVarTemplateDecl(VarTemplateDecl const * decl)201 bool VisitVarTemplateDecl(VarTemplateDecl const* decl)
202 {
203 if (!decl->isThisDeclarationADefinition())
204 {
205 return true;
206 }
207 return handleDeclaration(decl);
208 }
209
210 private:
reportSpecializations(T specializations)211 template <typename T> void reportSpecializations(T specializations)
212 {
213 for (auto const d : specializations)
214 {
215 auto const k = d->getTemplateSpecializationKind();
216 if (isTemplateExplicitInstantiationOrSpecialization(k))
217 {
218 report(DiagnosticsEngine::Note,
219 "explicit %select{instantiation|specialization}0 is here", d->getLocation())
220 << (k == TSK_ExplicitSpecialization) << d->getSourceRange();
221 }
222 }
223 }
224
handleDeclaration(NamedDecl const * decl)225 bool handleDeclaration(NamedDecl const* decl)
226 {
227 if (ignoreLocation(decl))
228 {
229 return true;
230 }
231 if (decl->getLinkageInternal() < ModuleLinkage)
232 {
233 return true;
234 }
235 //TODO: in some cases getLinkageInternal() appears to report ExternalLinkage instead of
236 // UniqueExternalLinkage:
237 if (decl->isInAnonymousNamespace())
238 {
239 return true;
240 }
241 for (Decl const* d = decl; d != nullptr; d = d->getPreviousDecl())
242 {
243 if (!compiler.getSourceManager().isInMainFile(d->getLocation()))
244 {
245 return true;
246 }
247 }
248 if (compiler.getSourceManager().isMacroBodyExpansion(decl->getLocation()))
249 {
250 if (Lexer::getImmediateMacroName(decl->getLocation(), compiler.getSourceManager(),
251 compiler.getLangOpts())
252 == "MDDS_MTV_DEFINE_ELEMENT_CALLBACKS")
253 {
254 // Even wrapping in an unnamed namespace or sneaking "static" into the macro
255 // wouldn't help, as then some of the functions it defines would be flagged as
256 // unused:
257 return true;
258 }
259 }
260 else if (compiler.getSourceManager().isMacroArgExpansion(decl->getLocation()))
261 {
262 if (Lexer::getImmediateMacroName(decl->getLocation(), compiler.getSourceManager(),
263 compiler.getLangOpts())
264 == "DEFINE_GUID")
265 {
266 // Windows, guiddef.h:
267 return true;
268 }
269 }
270 TypedefNameDecl const* typedefed = nullptr;
271 if (auto const d = dyn_cast<TagDecl>(decl))
272 {
273 typedefed = d->getTypedefNameForAnonDecl();
274 }
275 bool canStatic;
276 if (auto const d = dyn_cast<CXXRecordDecl>(decl))
277 {
278 canStatic = d->isUnion() && d->isAnonymousStructOrUnion();
279 }
280 else
281 {
282 canStatic = isa<FunctionDecl>(decl) || isa<VarDecl>(decl)
283 || isa<FunctionTemplateDecl>(decl) || isa<VarTemplateDecl>(decl);
284 }
285 auto const canUnnamed = compiler.getLangOpts().CPlusPlus
286 && !(isa<FunctionDecl>(decl) || isa<FunctionTemplateDecl>(decl));
287 // in general, moving functions into an unnamed namespace can break ADL
288 assert(canStatic || canUnnamed);
289 report(
290 DiagnosticsEngine::Warning,
291 ("externally available%select{| typedef'ed}0 entity %1 is not previously declared in an"
292 " included file (if it is only used in this translation unit,"
293 " %select{|make it static}2%select{| or }3%select{|put it in an unnamed namespace}4;"
294 " otherwise, provide a declaration of it in an included file)"),
295 decl->getLocation())
296 << (typedefed != nullptr) << (typedefed == nullptr ? decl : typedefed) << canStatic
297 << (canStatic && canUnnamed) << canUnnamed << decl->getSourceRange();
298 for (auto d = decl->getPreviousDecl(); d != nullptr; d = d->getPreviousDecl())
299 {
300 report(DiagnosticsEngine::Note, "previous declaration is here", d->getLocation())
301 << d->getSourceRange();
302 }
303 //TODO: Class template specializations can be in the enclosing namespace, so no need to
304 // list them here (as they won't need to be put into the unnamed namespace too, unlike for
305 // specializations of function and variable templates); and explicit function template
306 // specializations cannot have storage-class specifiers, so as we only suggest to make
307 // function templates static (but not to move them into an unnamed namespace), no need to
308 // list function template specializations here, either:
309 if (auto const d = dyn_cast<VarTemplateDecl>(decl))
310 {
311 reportSpecializations(d->specializations());
312 }
313 return true;
314 }
315 };
316
317 loplugin::Plugin::Registration<External> external("external");
318
319 } // namespace
320
321 #endif // LO_CLANG_SHARED_PLUGINS
322
323 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
324