d1a2b80b9d
...as discussed in the mail thread starting at <https://lists.freedesktop.org/archives/libreoffice/2020-November/086234.html> "Bump --enable-compiler-plugins Clang baseline?" (and now picked up again at <https://lists.freedesktop.org/archives/libreoffice/2022-February/088459.html> "Re: Bump --enable-compiler-plugins Clang baseline?"), and clean up compilerplugins/clang/ accordingly Change-Id: I5e81c6fdcc363aeefd6227606225b526fdf7ac16 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/129989 Tested-by: Jenkins Reviewed-by: Stephan Bergmann <sbergman@redhat.com>
226 lines
7.2 KiB
C++
226 lines
7.2 KiB
C++
/* -*- 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"
|
|
#include "compat.hxx"
|
|
#include "functionaddress.hxx"
|
|
#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
|
|
{
|
|
class ReturnConstant : public loplugin::FunctionAddress<loplugin::FilteringPlugin<ReturnConstant>>
|
|
{
|
|
public:
|
|
explicit ReturnConstant(loplugin::InstantiationData const& data)
|
|
: FunctionAddress(data)
|
|
{
|
|
}
|
|
|
|
void run() override
|
|
{
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
|
|
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?",
|
|
functionDecl->getBeginLoc())
|
|
<< pair.second << functionDecl->getSourceRange();
|
|
if (functionDecl != functionDecl->getCanonicalDecl())
|
|
report(DiagnosticsEngine::Note, "decl here",
|
|
functionDecl->getCanonicalDecl()->getBeginLoc())
|
|
<< functionDecl->getCanonicalDecl()->getSourceRange();
|
|
}
|
|
}
|
|
|
|
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;
|
|
std::vector<std::pair<FunctionDecl const*, std::string>> problemFunctions;
|
|
};
|
|
|
|
bool ReturnConstant::TraverseCXXMethodDecl(CXXMethodDecl* functionDecl)
|
|
{
|
|
if (ignoreLocation(functionDecl))
|
|
return true;
|
|
if (isInUnoIncludeFile(functionDecl))
|
|
return true;
|
|
|
|
if (!functionDecl->hasBody())
|
|
return true;
|
|
if (!functionDecl->isThisDeclarationADefinition())
|
|
return true;
|
|
if (functionDecl->isConstexpr())
|
|
return true;
|
|
if (functionDecl->getReturnType()->isVoidType())
|
|
return true;
|
|
if (functionDecl->isVirtual())
|
|
return true;
|
|
// static with inline body will be optimised at compile-time to a constant anyway
|
|
if (functionDecl->isStatic()
|
|
&& (functionDecl->hasInlineBody() || functionDecl->isInlineSpecified()))
|
|
return true;
|
|
// this catches some stuff in templates
|
|
if (functionDecl->hasAttr<OverrideAttr>())
|
|
return true;
|
|
|
|
// include/unotools/localedatawrapper.hxx
|
|
if (functionDecl->getIdentifier() && functionDecl->getName() == "getCurrZeroChar")
|
|
return true;
|
|
// sc/inc/stlalgorithm.hxx
|
|
if (loplugin::DeclCheck(functionDecl->getParent())
|
|
.Class("AlignedAllocator")
|
|
.Namespace("sc")
|
|
.GlobalNamespace())
|
|
return true;
|
|
|
|
switch (functionDecl->getOverloadedOperator())
|
|
{
|
|
case OO_Delete:
|
|
case OO_EqualEqual:
|
|
case OO_Call:
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// gtk signals and slots stuff
|
|
if (loplugin::TypeCheck(functionDecl->getReturnType()).Typedef("gboolean"))
|
|
return true;
|
|
|
|
// ignore LINK macro stuff
|
|
if (compiler.getSourceManager().isMacroBodyExpansion(functionDecl->getBeginLoc())
|
|
|| compiler.getSourceManager().isMacroArgExpansion(functionDecl->getBeginLoc()))
|
|
{
|
|
StringRef name{ Lexer::getImmediateMacroName(
|
|
functionDecl->getBeginLoc(), compiler.getSourceManager(), compiler.getLangOpts()) };
|
|
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)
|
|
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())
|
|
{
|
|
problemFunctions.push_back({ functionDecl, *rContext.values.begin() });
|
|
}
|
|
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;
|
|
if (compat::EvaluateAsInt(arg, x1, compiler.getASTContext()))
|
|
{
|
|
return compat::toString(x1, 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: */
|