2018-07-25 06:55:35 -05:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "plugin.hxx"
|
|
|
|
#include "check.hxx"
|
2018-08-16 04:54:14 -05:00
|
|
|
#include "compat.hxx"
|
|
|
|
#include "functionaddress.hxx"
|
2018-07-25 06:55:35 -05:00
|
|
|
#include <iostream>
|
|
|
|
#include <set>
|
|
|
|
#include <string>
|
|
|
|
|
|
|
|
/*
|
|
|
|
Look for member functions that merely return a compile-time constant, or they are empty, and can thus
|
|
|
|
be either removed, or converted into a constant.
|
|
|
|
|
|
|
|
This mostly tends to happen as a side-effect of other cleanups.
|
|
|
|
*/
|
|
|
|
namespace
|
|
|
|
{
|
2019-11-11 08:55:49 -06:00
|
|
|
class ReturnConstant : public loplugin::FunctionAddress<loplugin::FilteringPlugin<ReturnConstant>>
|
2018-07-25 06:55:35 -05:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit ReturnConstant(loplugin::InstantiationData const& data)
|
2019-11-11 08:55:49 -06:00
|
|
|
: FunctionAddress(data)
|
2018-07-25 06:55:35 -05:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void run() override
|
|
|
|
{
|
|
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
2018-08-16 04:54:14 -05:00
|
|
|
|
|
|
|
for (auto& pair : problemFunctions)
|
|
|
|
{
|
|
|
|
auto functionDecl = pair.first;
|
|
|
|
auto canonicalDecl = functionDecl->getCanonicalDecl();
|
|
|
|
if (getFunctionsWithAddressTaken().find(canonicalDecl)
|
|
|
|
!= getFunctionsWithAddressTaken().end())
|
|
|
|
continue;
|
|
|
|
report(DiagnosticsEngine::Warning,
|
|
|
|
"Method only returns a single constant value %0, does it make sense?",
|
|
|
|
compat::getBeginLoc(functionDecl))
|
|
|
|
<< pair.second << functionDecl->getSourceRange();
|
|
|
|
if (functionDecl != functionDecl->getCanonicalDecl())
|
|
|
|
report(DiagnosticsEngine::Note, "decl here",
|
|
|
|
compat::getBeginLoc(functionDecl->getCanonicalDecl()))
|
|
|
|
<< functionDecl->getCanonicalDecl()->getSourceRange();
|
|
|
|
}
|
2018-07-25 06:55:35 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool TraverseCXXMethodDecl(CXXMethodDecl*);
|
|
|
|
bool VisitReturnStmt(ReturnStmt const*);
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::string getExprValue(Expr const* arg);
|
|
|
|
|
|
|
|
struct Context
|
|
|
|
{
|
|
|
|
bool ignore = false;
|
|
|
|
std::set<std::string> values;
|
|
|
|
};
|
|
|
|
std::vector<Context> m_functionStack;
|
2018-08-16 04:54:14 -05:00
|
|
|
std::vector<std::pair<FunctionDecl const*, std::string>> problemFunctions;
|
2018-07-25 06:55:35 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
bool ReturnConstant::TraverseCXXMethodDecl(CXXMethodDecl* functionDecl)
|
|
|
|
{
|
|
|
|
if (ignoreLocation(functionDecl))
|
|
|
|
return true;
|
2018-08-16 04:54:14 -05:00
|
|
|
if (isInUnoIncludeFile(functionDecl))
|
|
|
|
return true;
|
|
|
|
|
2018-07-25 06:55:35 -05:00
|
|
|
if (!functionDecl->hasBody())
|
|
|
|
return true;
|
|
|
|
if (!functionDecl->isThisDeclarationADefinition())
|
|
|
|
return true;
|
|
|
|
if (functionDecl->isConstexpr())
|
|
|
|
return true;
|
2018-08-16 04:54:14 -05:00
|
|
|
if (functionDecl->getReturnType()->isVoidType())
|
2018-07-25 06:55:35 -05:00
|
|
|
return true;
|
2018-08-16 04:54:14 -05:00
|
|
|
if (functionDecl->isVirtual())
|
2018-07-25 06:55:35 -05:00
|
|
|
return true;
|
2018-08-16 04:54:14 -05:00
|
|
|
// static with inline body will be optimised at compile-time to a constant anyway
|
|
|
|
if (functionDecl->isStatic()
|
|
|
|
&& (functionDecl->hasInlineBody() || functionDecl->isInlineSpecified()))
|
2018-07-25 06:55:35 -05:00
|
|
|
return true;
|
2018-08-16 04:54:14 -05:00
|
|
|
// this catches some stuff in templates
|
|
|
|
if (functionDecl->hasAttr<OverrideAttr>())
|
2018-07-25 06:55:35 -05:00
|
|
|
return true;
|
|
|
|
|
2018-08-16 04:54:14 -05:00
|
|
|
// include/unotools/localedatawrapper.hxx
|
|
|
|
if (functionDecl->getIdentifier() && functionDecl->getName() == "getCurrZeroChar")
|
2018-07-25 06:55:35 -05:00
|
|
|
return true;
|
2018-08-16 04:54:14 -05:00
|
|
|
// sc/inc/stlalgorithm.hxx
|
|
|
|
if (loplugin::DeclCheck(functionDecl->getParent())
|
|
|
|
.Class("AlignedAllocator")
|
|
|
|
.Namespace("sc")
|
|
|
|
.GlobalNamespace())
|
2018-07-25 06:55:35 -05:00
|
|
|
return true;
|
|
|
|
|
|
|
|
switch (functionDecl->getOverloadedOperator())
|
|
|
|
{
|
|
|
|
case OO_Delete:
|
|
|
|
case OO_EqualEqual:
|
|
|
|
case OO_Call:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2018-08-16 04:54:14 -05:00
|
|
|
// gtk signals and slots stuff
|
|
|
|
if (loplugin::TypeCheck(functionDecl->getReturnType()).Typedef("gboolean"))
|
2018-07-25 06:55:35 -05:00
|
|
|
return true;
|
|
|
|
|
|
|
|
// ignore LINK macro stuff
|
2018-08-16 04:54:14 -05:00
|
|
|
if (compiler.getSourceManager().isMacroBodyExpansion(compat::getBeginLoc(functionDecl))
|
|
|
|
|| compiler.getSourceManager().isMacroArgExpansion(compat::getBeginLoc(functionDecl)))
|
2018-07-25 06:55:35 -05:00
|
|
|
{
|
2018-08-10 05:35:21 -05:00
|
|
|
StringRef name{ Lexer::getImmediateMacroName(compat::getBeginLoc(functionDecl),
|
|
|
|
compiler.getSourceManager(),
|
|
|
|
compiler.getLangOpts()) };
|
2018-08-16 04:54:14 -05:00
|
|
|
if (name.find("IMPL_LINK") != StringRef::npos
|
|
|
|
|| name.find("IMPL_STATIC_LINK") != StringRef::npos
|
|
|
|
|| name.find("DECL_LINK") != StringRef::npos
|
|
|
|
|| name.find("SFX_IMPL_POS_CHILDWINDOW_WITHID") != StringRef::npos)
|
2018-07-25 06:55:35 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_functionStack.emplace_back();
|
|
|
|
bool ret = RecursiveASTVisitor<ReturnConstant>::TraverseCXXMethodDecl(functionDecl);
|
|
|
|
Context& rContext = m_functionStack.back();
|
|
|
|
if (!rContext.ignore && rContext.values.size() == 1
|
|
|
|
&& rContext.values.find("unknown") == rContext.values.end())
|
|
|
|
{
|
2018-08-16 04:54:14 -05:00
|
|
|
problemFunctions.push_back({ functionDecl, *rContext.values.begin() });
|
2018-07-25 06:55:35 -05:00
|
|
|
}
|
|
|
|
m_functionStack.pop_back();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ReturnConstant::VisitReturnStmt(ReturnStmt const* returnStmt)
|
|
|
|
{
|
|
|
|
if (ignoreLocation(returnStmt))
|
|
|
|
return true;
|
|
|
|
if (m_functionStack.empty())
|
|
|
|
return true;
|
|
|
|
Context& rContext = m_functionStack.back();
|
|
|
|
|
|
|
|
if (!returnStmt->getRetValue())
|
|
|
|
return true;
|
|
|
|
if (returnStmt->getRetValue()->isTypeDependent())
|
|
|
|
{
|
|
|
|
rContext.ignore = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (const UnaryOperator* unaryOp = dyn_cast<UnaryOperator>(returnStmt->getRetValue()))
|
|
|
|
{
|
|
|
|
if (unaryOp->getOpcode() == UO_AddrOf)
|
|
|
|
{
|
|
|
|
rContext.ignore = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rContext.values.insert(getExprValue(returnStmt->getRetValue()));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string ReturnConstant::getExprValue(Expr const* arg)
|
|
|
|
{
|
|
|
|
arg = arg->IgnoreParenCasts();
|
|
|
|
if (isa<CXXDefaultArgExpr>(arg))
|
|
|
|
{
|
|
|
|
arg = dyn_cast<CXXDefaultArgExpr>(arg)->getExpr();
|
|
|
|
}
|
|
|
|
arg = arg->IgnoreParenCasts();
|
|
|
|
// ignore this, it seems to trigger an infinite recursion
|
|
|
|
if (isa<UnaryExprOrTypeTraitExpr>(arg))
|
|
|
|
{
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
APSInt x1;
|
2018-11-27 04:56:12 -06:00
|
|
|
if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
|
2018-07-25 06:55:35 -05:00
|
|
|
{
|
|
|
|
return x1.toString(10);
|
|
|
|
}
|
|
|
|
if (isa<CXXNullPtrLiteralExpr>(arg))
|
|
|
|
{
|
|
|
|
return "0";
|
|
|
|
}
|
|
|
|
if (isa<MaterializeTemporaryExpr>(arg))
|
|
|
|
{
|
|
|
|
const CXXBindTemporaryExpr* strippedArg
|
|
|
|
= dyn_cast_or_null<CXXBindTemporaryExpr>(arg->IgnoreParenCasts());
|
|
|
|
if (strippedArg && strippedArg->getSubExpr())
|
|
|
|
{
|
|
|
|
auto temp = dyn_cast<CXXTemporaryObjectExpr>(strippedArg->getSubExpr());
|
|
|
|
if (temp->getNumArgs() == 0)
|
|
|
|
{
|
|
|
|
if (loplugin::TypeCheck(temp->getType())
|
|
|
|
.Class("OUString")
|
|
|
|
.Namespace("rtl")
|
|
|
|
.GlobalNamespace())
|
|
|
|
{
|
|
|
|
return "\"\"";
|
|
|
|
}
|
|
|
|
if (loplugin::TypeCheck(temp->getType())
|
|
|
|
.Class("OString")
|
|
|
|
.Namespace("rtl")
|
|
|
|
.GlobalNamespace())
|
|
|
|
{
|
|
|
|
return "\"\"";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
|
|
|
|
loplugin::Plugin::Registration<ReturnConstant> X("returnconstant", false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|