update credits
[LibreOffice.git] / compilerplugins / clang / returnconstant.cxx
blob530044c9609dd8819c801a7e9ba0e2f4990a2566
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 #include "plugin.hxx"
11 #include "check.hxx"
12 #include "compat.hxx"
13 #include "functionaddress.hxx"
14 #include <iostream>
15 #include <set>
16 #include <string>
19 Look for member functions that merely return a compile-time constant, or they are empty, and can thus
20 be either removed, or converted into a constant.
22 This mostly tends to happen as a side-effect of other cleanups.
24 namespace
26 class ReturnConstant : public loplugin::FunctionAddress<loplugin::FilteringPlugin<ReturnConstant>>
28 public:
29 explicit ReturnConstant(loplugin::InstantiationData const& data)
30 : FunctionAddress(data)
34 void run() override
36 TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
38 for (auto& pair : problemFunctions)
40 auto functionDecl = pair.first;
41 auto canonicalDecl = functionDecl->getCanonicalDecl();
42 if (getFunctionsWithAddressTaken().find(canonicalDecl)
43 != getFunctionsWithAddressTaken().end())
44 continue;
45 report(DiagnosticsEngine::Warning,
46 "Method only returns a single constant value %0, does it make sense?",
47 functionDecl->getBeginLoc())
48 << pair.second << functionDecl->getSourceRange();
49 if (functionDecl != functionDecl->getCanonicalDecl())
50 report(DiagnosticsEngine::Note, "decl here",
51 functionDecl->getCanonicalDecl()->getBeginLoc())
52 << functionDecl->getCanonicalDecl()->getSourceRange();
56 bool TraverseCXXMethodDecl(CXXMethodDecl*);
57 bool VisitReturnStmt(ReturnStmt const*);
59 private:
60 std::string getExprValue(Expr const* arg);
62 struct Context
64 bool ignore = false;
65 std::set<std::string> values;
67 std::vector<Context> m_functionStack;
68 std::vector<std::pair<FunctionDecl const*, std::string>> problemFunctions;
71 bool ReturnConstant::TraverseCXXMethodDecl(CXXMethodDecl* functionDecl)
73 if (ignoreLocation(functionDecl))
74 return true;
75 if (isInUnoIncludeFile(functionDecl))
76 return true;
78 if (!functionDecl->hasBody())
79 return true;
80 if (!functionDecl->isThisDeclarationADefinition())
81 return true;
82 if (functionDecl->isConstexpr())
83 return true;
84 if (functionDecl->getReturnType()->isVoidType())
85 return true;
86 if (functionDecl->isVirtual())
87 return true;
88 // static with inline body will be optimised at compile-time to a constant anyway
89 if (functionDecl->isStatic()
90 && (functionDecl->hasInlineBody() || functionDecl->isInlineSpecified()))
91 return true;
92 // this catches some stuff in templates
93 if (functionDecl->hasAttr<OverrideAttr>())
94 return true;
96 // include/unotools/localedatawrapper.hxx
97 if (functionDecl->getIdentifier() && functionDecl->getName() == "getCurrZeroChar")
98 return true;
99 // sc/inc/stlalgorithm.hxx
100 if (loplugin::DeclCheck(functionDecl->getParent())
101 .Class("AlignedAllocator")
102 .Namespace("sc")
103 .GlobalNamespace())
104 return true;
106 switch (functionDecl->getOverloadedOperator())
108 case OO_Delete:
109 case OO_EqualEqual:
110 case OO_Call:
111 return true;
112 default:
113 break;
116 // gtk signals and slots stuff
117 if (loplugin::TypeCheck(functionDecl->getReturnType()).Typedef("gboolean"))
118 return true;
120 // ignore LINK macro stuff
121 if (compiler.getSourceManager().isMacroBodyExpansion(functionDecl->getBeginLoc())
122 || compiler.getSourceManager().isMacroArgExpansion(functionDecl->getBeginLoc()))
124 StringRef name{ Lexer::getImmediateMacroName(
125 functionDecl->getBeginLoc(), compiler.getSourceManager(), compiler.getLangOpts()) };
126 if (name.find("IMPL_LINK") != StringRef::npos
127 || name.find("IMPL_STATIC_LINK") != StringRef::npos
128 || name.find("DECL_LINK") != StringRef::npos
129 || name.find("SFX_IMPL_POS_CHILDWINDOW_WITHID") != StringRef::npos)
130 return true;
133 m_functionStack.emplace_back();
134 bool ret = RecursiveASTVisitor<ReturnConstant>::TraverseCXXMethodDecl(functionDecl);
135 Context& rContext = m_functionStack.back();
136 if (!rContext.ignore && rContext.values.size() == 1
137 && rContext.values.find("unknown") == rContext.values.end())
139 problemFunctions.push_back({ functionDecl, *rContext.values.begin() });
141 m_functionStack.pop_back();
142 return ret;
145 bool ReturnConstant::VisitReturnStmt(ReturnStmt const* returnStmt)
147 if (ignoreLocation(returnStmt))
148 return true;
149 if (m_functionStack.empty())
150 return true;
151 Context& rContext = m_functionStack.back();
153 if (!returnStmt->getRetValue())
154 return true;
155 if (returnStmt->getRetValue()->isTypeDependent())
157 rContext.ignore = true;
158 return true;
160 if (const UnaryOperator* unaryOp = dyn_cast<UnaryOperator>(returnStmt->getRetValue()))
162 if (unaryOp->getOpcode() == UO_AddrOf)
164 rContext.ignore = true;
165 return true;
168 rContext.values.insert(getExprValue(returnStmt->getRetValue()));
169 return true;
172 std::string ReturnConstant::getExprValue(Expr const* arg)
174 arg = arg->IgnoreParenCasts();
175 if (isa<CXXDefaultArgExpr>(arg))
177 arg = dyn_cast<CXXDefaultArgExpr>(arg)->getExpr();
179 arg = arg->IgnoreParenCasts();
180 // ignore this, it seems to trigger an infinite recursion
181 if (isa<UnaryExprOrTypeTraitExpr>(arg))
183 return "unknown";
185 APSInt x1;
186 if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
188 return compat::toString(x1, 10);
190 if (isa<CXXNullPtrLiteralExpr>(arg))
192 return "0";
194 if (isa<MaterializeTemporaryExpr>(arg))
196 const CXXBindTemporaryExpr* strippedArg
197 = dyn_cast_or_null<CXXBindTemporaryExpr>(arg->IgnoreParenCasts());
198 if (strippedArg && strippedArg->getSubExpr())
200 auto temp = dyn_cast<CXXTemporaryObjectExpr>(strippedArg->getSubExpr());
201 if (temp->getNumArgs() == 0)
203 if (loplugin::TypeCheck(temp->getType())
204 .Class("OUString")
205 .Namespace("rtl")
206 .GlobalNamespace())
208 return "\"\"";
210 if (loplugin::TypeCheck(temp->getType())
211 .Class("OString")
212 .Namespace("rtl")
213 .GlobalNamespace())
215 return "\"\"";
220 return "unknown";
223 loplugin::Plugin::Registration<ReturnConstant> X("returnconstant", false);
226 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */