bump product version to 6.4.0.3
[LibreOffice.git] / compilerplugins / clang / returnconstant.cxx
blob70119b07bed7cff1e59feb85d7fd9449c888f6f4
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 compat::getBeginLoc(functionDecl))
48 << pair.second << functionDecl->getSourceRange();
49 if (functionDecl != functionDecl->getCanonicalDecl())
50 report(DiagnosticsEngine::Note, "decl here",
51 compat::getBeginLoc(functionDecl->getCanonicalDecl()))
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 std::string aImmediateMacro = "";
122 if (compiler.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(functionDecl))
123 || compiler.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(functionDecl)))
125 StringRef name{ Lexer::getImmediateMacroName(compat::getBeginLoc(functionDecl),
126 compiler.getSourceManager(),
127 compiler.getLangOpts()) };
128 aImmediateMacro = name;
129 if (name.find("IMPL_LINK") != StringRef::npos
130 || name.find("IMPL_STATIC_LINK") != StringRef::npos
131 || name.find("DECL_LINK") != StringRef::npos
132 || name.find("SFX_IMPL_POS_CHILDWINDOW_WITHID") != StringRef::npos)
133 return true;
136 m_functionStack.emplace_back();
137 bool ret = RecursiveASTVisitor<ReturnConstant>::TraverseCXXMethodDecl(functionDecl);
138 Context& rContext = m_functionStack.back();
139 if (!rContext.ignore && rContext.values.size() == 1
140 && rContext.values.find("unknown") == rContext.values.end())
142 problemFunctions.push_back({ functionDecl, *rContext.values.begin() });
144 m_functionStack.pop_back();
145 return ret;
148 bool ReturnConstant::VisitReturnStmt(ReturnStmt const* returnStmt)
150 if (ignoreLocation(returnStmt))
151 return true;
152 if (m_functionStack.empty())
153 return true;
154 Context& rContext = m_functionStack.back();
156 if (!returnStmt->getRetValue())
157 return true;
158 if (returnStmt->getRetValue()->isTypeDependent())
160 rContext.ignore = true;
161 return true;
163 if (const UnaryOperator* unaryOp = dyn_cast<UnaryOperator>(returnStmt->getRetValue()))
165 if (unaryOp->getOpcode() == UO_AddrOf)
167 rContext.ignore = true;
168 return true;
171 rContext.values.insert(getExprValue(returnStmt->getRetValue()));
172 return true;
175 std::string ReturnConstant::getExprValue(Expr const* arg)
177 arg = arg->IgnoreParenCasts();
178 if (isa<CXXDefaultArgExpr>(arg))
180 arg = dyn_cast<CXXDefaultArgExpr>(arg)->getExpr();
182 arg = arg->IgnoreParenCasts();
183 // ignore this, it seems to trigger an infinite recursion
184 if (isa<UnaryExprOrTypeTraitExpr>(arg))
186 return "unknown";
188 APSInt x1;
189 if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
191 return x1.toString(10);
193 if (isa<CXXNullPtrLiteralExpr>(arg))
195 return "0";
197 if (isa<MaterializeTemporaryExpr>(arg))
199 const CXXBindTemporaryExpr* strippedArg
200 = dyn_cast_or_null<CXXBindTemporaryExpr>(arg->IgnoreParenCasts());
201 if (strippedArg && strippedArg->getSubExpr())
203 auto temp = dyn_cast<CXXTemporaryObjectExpr>(strippedArg->getSubExpr());
204 if (temp->getNumArgs() == 0)
206 if (loplugin::TypeCheck(temp->getType())
207 .Class("OUString")
208 .Namespace("rtl")
209 .GlobalNamespace())
211 return "\"\"";
213 if (loplugin::TypeCheck(temp->getType())
214 .Class("OString")
215 .Namespace("rtl")
216 .GlobalNamespace())
218 return "\"\"";
223 return "unknown";
226 loplugin::Plugin::Registration<ReturnConstant> X("returnconstant", false);
229 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */