tdf#130857 qt weld: Support "Insert Breaks" dialog
[LibreOffice.git] / compilerplugins / clang / unreffun.cxx
blobfcb6f04016b7843a3f116a9f7e3e340662c45489
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 "compat.hxx"
20 #include "plugin.hxx"
22 namespace {
24 bool isFriendDecl(Decl const * decl) {
25 return decl->getFriendObjectKind() != Decl::FOK_None;
28 Decl const * getPreviousNonFriendDecl(Decl const * decl) {
29 for (;;) {
30 decl = decl->getPreviousDecl();
31 if (decl == nullptr || !isFriendDecl(decl)) {
32 return decl;
37 bool isSpecialMemberFunction(FunctionDecl const * decl) {
38 if (auto const ctor = dyn_cast<CXXConstructorDecl>(decl)) {
39 return ctor->isDefaultConstructor() || ctor->isCopyOrMoveConstructor();
41 if (isa<CXXDestructorDecl>(decl)) {
42 return true;
44 if (auto const meth = dyn_cast<CXXMethodDecl>(decl)) {
45 return meth->isCopyAssignmentOperator() || meth->isMoveAssignmentOperator();
47 return false;
50 class UnrefFun: public loplugin::FilteringPlugin<UnrefFun> {
51 public:
52 explicit UnrefFun(loplugin::InstantiationData const & data): FilteringPlugin(data) {}
54 void run() override
55 { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
57 bool PreTraverseFriendDecl(FriendDecl * decl) {
58 friendFunction.push( dyn_cast_or_null<FunctionDecl>(decl->getFriendDecl()));
59 return true;
61 bool PostTraverseFriendDecl(FriendDecl *, bool ) {
62 friendFunction.pop();
63 return true;
65 bool TraverseFriendDecl(FriendDecl * decl) {
66 PreTraverseFriendDecl(decl);
67 auto const ret = RecursiveASTVisitor::TraverseFriendDecl(decl);
68 PostTraverseFriendDecl(decl, ret);
69 return ret;
72 bool VisitFunctionDecl(FunctionDecl const * decl);
74 private:
75 std::stack<FunctionDecl const *> friendFunction;
78 bool UnrefFun::VisitFunctionDecl(FunctionDecl const * decl) {
79 if (ignoreLocation(decl)) {
80 return true;
83 //TODO, filtering out any functions relating to (class) templates for now:
84 CXXRecordDecl const * r = dyn_cast<CXXRecordDecl>(decl->getDeclContext());
85 if (r != nullptr
86 && (r->getTemplateSpecializationKind() != TSK_Undeclared
87 || r->isDependentContext()))
89 return true;
91 if (!friendFunction.empty() && decl == friendFunction.top()) {
92 if (auto const lex = dyn_cast<CXXRecordDecl>(decl->getLexicalDeclContext())) {
93 if (lex->isDependentContext()) {
94 return true;
99 if (!(decl->isThisDeclarationADefinition() || isFriendDecl(decl)
100 || decl->isFunctionTemplateSpecialization()))
102 Decl const * prev = getPreviousNonFriendDecl(decl);
103 if (prev != nullptr/* && prev != decl->getPrimaryTemplate()*/) {
104 // Workaround for redeclarations that introduce visibility attributes
105 // (as is done with
107 // SAL_DLLPUBLIC_EXPORT GType lok_doc_view_get_type();
109 // in libreofficekit/source/gtk/lokdocview.cxx):
110 if (decl->getAttr<VisibilityAttr>() != nullptr
111 && prev->getAttr<VisibilityAttr>() == nullptr)
113 return true;
115 report(
116 DiagnosticsEngine::Warning,
117 "redundant function%0 redeclaration", decl->getLocation())
118 << ((decl->getTemplatedKind()
119 == FunctionDecl::TK_FunctionTemplate)
120 ? " template" : "")
121 << decl->getSourceRange();
122 report(
123 DiagnosticsEngine::Note, "previous declaration is here",
124 prev->getLocation())
125 << prev->getSourceRange();
126 return true;
130 FunctionDecl const * canon = decl->getCanonicalDecl();
131 //TODO: is that the first?
132 if (canon->isDeleted() || canon->isReferenced()
133 || !(canon->isDefined()
134 ? decl->isThisDeclarationADefinition() : decl->isFirstDecl())
135 || !compiler.getSourceManager().isInMainFile(canon->getLocation())
136 || isInUnoIncludeFile(canon)
137 || canon->isMain() || canon->isMSVCRTEntryPoint()
138 || (decl->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate
139 && (decl->getDescribedFunctionTemplate()->spec_begin()
140 != decl->getDescribedFunctionTemplate()->spec_end()))
141 || (compiler.getDiagnostics().getDiagnosticLevel(
142 diag::warn_unused_function, decl->getLocation())
143 < DiagnosticsEngine::Warning))
145 return true;
147 if (canon->isExplicitlyDefaulted() && isSpecialMemberFunction(canon)) {
148 // If a special member function is explicitly defaulted on the first declaration, assume
149 // that its presence is always due to some interface design consideration, not to explicitly
150 // request a definition that might be worth to flag as unused (and C++20 may extend
151 // defaultability beyond special member functions to comparison operators, therefore
152 // explicitly check here for special member functions only):
153 return true;
155 LinkageInfo info(canon->getLinkageAndVisibility());
156 if (info.getLinkage() == compat::Linkage::External
157 && loplugin::hasCLanguageLinkageType(canon) && canon->isDefined()
158 && ((decl == canon && info.getVisibility() == DefaultVisibility)
159 || ((canon->hasAttr<ConstructorAttr>()
160 || canon->hasAttr<DestructorAttr>())
161 && info.getVisibility() == HiddenVisibility)))
163 return true;
165 auto loc = decl->getLocation();
166 if (compiler.getSourceManager().isMacroBodyExpansion(loc)
167 && (Lexer::getImmediateMacroName(
168 loc, compiler.getSourceManager(), compiler.getLangOpts())
169 == "MDDS_MTV_DEFINE_ELEMENT_CALLBACKS"))
171 return true;
173 report(
174 DiagnosticsEngine::Warning,
175 (canon->isDefined()
176 ? (canon->isExternallyVisible()
177 ? "Unreferenced externally visible function%0 definition"
178 : "Unreferenced externally invisible function%0 definition")
179 : "Unreferenced function%0 declaration"),
180 decl->getLocation())
181 << (decl->getTemplatedKind() == FunctionDecl::TK_FunctionTemplate
182 ? " template" : "")
183 << decl->getSourceRange();
184 if (canon->isDefined() && !decl->isFirstDecl()) {
185 report(
186 DiagnosticsEngine::Note, "first declaration is here",
187 canon->getLocation())
188 << canon->getSourceRange();
190 return true;
193 loplugin::Plugin::Registration<UnrefFun> unreffun("unreffun");
197 #endif // LO_CLANG_SHARED_PLUGINS
199 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */