bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / unreffun.cxx
blob353eee5f0b31027953d6f2e67d4f125b7f394b14
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #ifndef LO_CLANG_SHARED_PLUGINS
12 #include <cassert>
13 #include <stack>
14 #include <string>
16 #include "clang/AST/Attr.h"
17 #include "clang/Sema/SemaInternal.h" // warn_unused_function
19 #include "plugin.hxx"
21 namespace {
23 bool isFriendDecl(Decl const * decl) {
24 return decl->getFriendObjectKind() != Decl::FOK_None;
27 Decl const * getPreviousNonFriendDecl(Decl const * decl) {
28 for (;;) {
29 decl = decl->getPreviousDecl();
30 if (decl == nullptr || !isFriendDecl(decl)) {
31 return decl;
36 bool isSpecialMemberFunction(FunctionDecl const * decl) {
37 if (auto const ctor = dyn_cast<CXXConstructorDecl>(decl)) {
38 return ctor->isDefaultConstructor() || ctor->isCopyOrMoveConstructor();
40 if (isa<CXXDestructorDecl>(decl)) {
41 return true;
43 if (auto const meth = dyn_cast<CXXMethodDecl>(decl)) {
44 return meth->isCopyAssignmentOperator() || meth->isMoveAssignmentOperator();
46 return false;
49 class UnrefFun: public loplugin::FilteringPlugin<UnrefFun> {
50 public:
51 explicit UnrefFun(loplugin::InstantiationData const & data): FilteringPlugin(data) {}
53 void run() override
54 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
56 bool PreTraverseFriendDecl(FriendDecl * decl) {
57 friendFunction.push( dyn_cast_or_null<FunctionDecl>(decl->getFriendDecl()));
58 return true;
60 bool PostTraverseFriendDecl(FriendDecl *, bool ) {
61 friendFunction.pop();
62 return true;
64 bool TraverseFriendDecl(FriendDecl * decl) {
65 PreTraverseFriendDecl(decl);
66 auto const ret = RecursiveASTVisitor::TraverseFriendDecl(decl);
67 PostTraverseFriendDecl(decl, ret);
68 return ret;
71 bool VisitFunctionDecl(FunctionDecl const * decl);
73 private:
74 std::stack<FunctionDecl const *> friendFunction;
77 bool UnrefFun::VisitFunctionDecl(FunctionDecl const * decl) {
78 if (ignoreLocation(decl)) {
79 return true;
82 //TODO, filtering out any functions relating to (class) templates for now:
83 CXXRecordDecl const * r = dyn_cast<CXXRecordDecl>(decl->getDeclContext());
84 if (r != nullptr
85 && (r->getTemplateSpecializationKind() != TSK_Undeclared
86 || r->isDependentContext()))
88 return true;
90 if (!friendFunction.empty() && decl == friendFunction.top()) {
91 if (auto const lex = dyn_cast<CXXRecordDecl>(decl->getLexicalDeclContext())) {
92 if (lex->isDependentContext()) {
93 return true;
98 if (!(decl->isThisDeclarationADefinition() || isFriendDecl(decl)
99 || decl->isFunctionTemplateSpecialization()))
101 Decl const * prev = getPreviousNonFriendDecl(decl);
102 if (prev != nullptr/* && prev != decl->getPrimaryTemplate()*/) {
103 // Workaround for redeclarations that introduce visibility attributes
104 // (as is done with
106 // SAL_DLLPUBLIC_EXPORT GType lok_doc_view_get_type();
108 // in libreofficekit/source/gtk/lokdocview.cxx):
109 if (decl->getAttr<VisibilityAttr>() != nullptr
110 && prev->getAttr<VisibilityAttr>() == nullptr)
112 return true;
114 report(
115 DiagnosticsEngine::Warning,
116 "redundant function%0 redeclaration", decl->getLocation())
117 << ((decl->getTemplatedKind()
118 == FunctionDecl::TK_FunctionTemplate)
119 ? " template" : "")
120 << decl->getSourceRange();
121 report(
122 DiagnosticsEngine::Note, "previous declaration is here",
123 prev->getLocation())
124 << prev->getSourceRange();
125 return true;
129 FunctionDecl const * canon = decl->getCanonicalDecl();
130 //TODO: is that the first?
131 if (canon->isDeleted() || canon->isReferenced()
132 || !(canon->isDefined()
133 ? decl->isThisDeclarationADefinition() : decl->isFirstDecl())
134 || !compiler.getSourceManager().isInMainFile(canon->getLocation())
135 || isInUnoIncludeFile(canon)
136 || canon->isMain() || canon->isMSVCRTEntryPoint()
137 || (decl->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate
138 && (decl->getDescribedFunctionTemplate()->spec_begin()
139 != decl->getDescribedFunctionTemplate()->spec_end()))
140 || (compiler.getDiagnostics().getDiagnosticLevel(
141 diag::warn_unused_function, decl->getLocation())
142 < DiagnosticsEngine::Warning))
144 return true;
146 if (canon->isExplicitlyDefaulted() && isSpecialMemberFunction(canon)) {
147 // If a special member function is explicitly defaulted on the first declaration, assume
148 // that its presence is always due to some interface design consideration, not to explicitly
149 // request a definition that might be worth to flag as unused (and C++20 may extend
150 // defaultability beyond special member functions to comparison operators, therefore
151 // explicitly check here for special member functions only):
152 return true;
154 LinkageInfo info(canon->getLinkageAndVisibility());
155 if (info.getLinkage() == ExternalLinkage
156 && loplugin::hasCLanguageLinkageType(canon) && canon->isDefined()
157 && ((decl == canon && info.getVisibility() == DefaultVisibility)
158 || ((canon->hasAttr<ConstructorAttr>()
159 || canon->hasAttr<DestructorAttr>())
160 && info.getVisibility() == HiddenVisibility)))
162 return true;
164 auto loc = decl->getLocation();
165 if (compiler.getSourceManager().isMacroBodyExpansion(loc)
166 && (Lexer::getImmediateMacroName(
167 loc, compiler.getSourceManager(), compiler.getLangOpts())
168 == "MDDS_MTV_DEFINE_ELEMENT_CALLBACKS"))
170 return true;
172 report(
173 DiagnosticsEngine::Warning,
174 (canon->isDefined()
175 ? (canon->isExternallyVisible()
176 ? "Unreferenced externally visible function%0 definition"
177 : "Unreferenced externally invisible function%0 definition")
178 : "Unreferenced function%0 declaration"),
179 decl->getLocation())
180 << (decl->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate
181 ? " template" : "")
182 << decl->getSourceRange();
183 if (canon->isDefined() && !decl->isFirstDecl()) {
184 report(
185 DiagnosticsEngine::Note, "first declaration is here",
186 canon->getLocation())
187 << canon->getSourceRange();
189 return true;
192 loplugin::Plugin::Registration<UnrefFun> unreffun("unreffun");
196 #endif // LO_CLANG_SHARED_PLUGINS
198 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */