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>
563 lines
24 KiB
C++
563 lines
24 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 <algorithm>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <unordered_map>
|
|
#include <iostream>
|
|
|
|
#include "config_clang.h"
|
|
|
|
#include "plugin.hxx"
|
|
#include "check.hxx"
|
|
#include "functionaddress.hxx"
|
|
|
|
#include "clang/AST/ParentMapContext.h"
|
|
|
|
/**
|
|
Find methods that can be declared const.
|
|
|
|
This analysis attempts to implement "logical const" as opposed to "technical const", which means
|
|
we ignore always-const nature of std::unique_ptr::operator->
|
|
|
|
This is not a sophisticated analysis. It deliberately skips all of the hard cases for now.
|
|
It is an exercise in getting the most benefit for the least effort.
|
|
*/
|
|
namespace
|
|
{
|
|
|
|
class ConstMethod:
|
|
public loplugin::FunctionAddress<loplugin::FilteringPlugin<ConstMethod>>
|
|
{
|
|
public:
|
|
explicit ConstMethod(loplugin::InstantiationData const & data): FunctionAddress(data) {}
|
|
|
|
virtual void run() override {
|
|
std::string fn(handler.getMainFileName());
|
|
loplugin::normalizeDotDotInFilePath(fn);
|
|
// things I'm not sure about
|
|
if (loplugin::hasPathnamePrefix(fn, SRCDIR "/svl/unx/source/svdde/ddedummy.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/svl/source/numbers/zformat.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/svl/source/numbers/zforscan.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/svl/source/numbers/zforlist.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/source/gdi/impgraph.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/source/image/ImplImage.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/source/filter/wmf/wmfwr.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/unx/generic/app/i18n_im.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/unx/generic/app/randrwrapper.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/unx/gtk3/gtkinst.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/unx/gtk3/gtkframe.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/skia/gdiimpl.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/vcl/qt5/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/package/source/xstor/owriteablestream.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/package/source/zippackage/ZipPackage.cxx")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/toolkit/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/canvas/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/accessibility/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/framework/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/basic/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/sfx2/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/xmloff/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/connectivity/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/editeng/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/scripting/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/ucb/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/svx/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/basctl/")
|
|
|| loplugin::hasPathnamePrefix(fn, SRCDIR "/chart2/")
|
|
)
|
|
return;
|
|
|
|
TraverseDecl(compiler.getASTContext().getTranslationUnitDecl());
|
|
|
|
for (const CXXMethodDecl *pMethodDecl : interestingMethodSet) {
|
|
if (methodCannotBeConstSet.find(pMethodDecl) != methodCannotBeConstSet.end())
|
|
continue;
|
|
auto canonicalDecl = pMethodDecl->getCanonicalDecl();
|
|
if (getFunctionsWithAddressTaken().find((FunctionDecl const *)canonicalDecl)
|
|
!= getFunctionsWithAddressTaken().end())
|
|
continue;
|
|
// things that I don't think should be logically const
|
|
std::string fqn = pMethodDecl->getQualifiedNameAsString();
|
|
if (fqn == "comphelper::EmbeddedObjectContainer::CommitImageSubStorage"
|
|
|| fqn == "SvtLinguConfig::SetProperty"
|
|
|| fqn == "SvtLinguConfig::ReplaceSetProperties"
|
|
|| fqn == "SystemWindow::UpdatePositionData"
|
|
|| fqn == "OutputDevice::SelectClipRegion"
|
|
|| fqn == "OutputDevice::BlendBitmap")
|
|
continue;
|
|
StringRef aFileName = getFilenameOfLocation(compiler.getSourceManager().getSpellingLoc(canonicalDecl->getBeginLoc()));
|
|
// leave the kit API alone
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/LibreOfficeKit/LibreOfficeKit.hxx"))
|
|
continue;
|
|
// don't feel like touching this right now
|
|
if (loplugin::isSamePathname(aFileName, SRCDIR "/include/vcl/weld.hxx"))
|
|
continue;
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
"this method can be const",
|
|
pMethodDecl->getBeginLoc())
|
|
<< pMethodDecl->getSourceRange();
|
|
if (canonicalDecl->getLocation() != pMethodDecl->getLocation()) {
|
|
report(
|
|
DiagnosticsEngine::Note,
|
|
"canonical method declaration here",
|
|
canonicalDecl->getBeginLoc())
|
|
<< canonicalDecl->getSourceRange();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TraverseCXXMethodDecl(CXXMethodDecl *);
|
|
bool TraverseCXXConversionDecl(CXXConversionDecl *);
|
|
bool VisitCXXMethodDecl(const CXXMethodDecl *);
|
|
bool VisitCXXThisExpr(const CXXThisExpr *);
|
|
|
|
private:
|
|
bool isPointerOrReferenceToConst(const QualType& qt);
|
|
bool isPointerOrReferenceToNonConst(const QualType& qt);
|
|
bool checkIfCanBeConst(const Stmt*, const CXXMethodDecl*);
|
|
|
|
std::unordered_set<const CXXMethodDecl*> interestingMethodSet;
|
|
std::unordered_set<const CXXMethodDecl*> methodCannotBeConstSet;
|
|
CXXMethodDecl const * currCXXMethodDecl;
|
|
};
|
|
|
|
bool ConstMethod::TraverseCXXMethodDecl(CXXMethodDecl * cxxMethodDecl)
|
|
{
|
|
currCXXMethodDecl = cxxMethodDecl;
|
|
bool rv = RecursiveASTVisitor<ConstMethod>::TraverseCXXMethodDecl(cxxMethodDecl);
|
|
currCXXMethodDecl = nullptr;
|
|
return rv;
|
|
}
|
|
|
|
bool ConstMethod::TraverseCXXConversionDecl(CXXConversionDecl * cxxConversionDecl)
|
|
{
|
|
currCXXMethodDecl = cxxConversionDecl;
|
|
bool rv = RecursiveASTVisitor<ConstMethod>::TraverseCXXConversionDecl(cxxConversionDecl);
|
|
currCXXMethodDecl = nullptr;
|
|
return rv;
|
|
}
|
|
|
|
bool ConstMethod::VisitCXXMethodDecl(const CXXMethodDecl * cxxMethodDecl)
|
|
{
|
|
if (ignoreLocation(cxxMethodDecl) || !cxxMethodDecl->isThisDeclarationADefinition()) {
|
|
return true;
|
|
}
|
|
if (cxxMethodDecl->isConst())
|
|
return true;
|
|
// ignore stuff that forms part of the stable URE interface
|
|
if (isInUnoIncludeFile(cxxMethodDecl)) {
|
|
return true;
|
|
}
|
|
// TODO ignore template stuff for now
|
|
if (cxxMethodDecl->getTemplatedKind() != FunctionDecl::TK_NonTemplate) {
|
|
return true;
|
|
}
|
|
if (cxxMethodDecl->isDeleted())
|
|
return true;
|
|
if (cxxMethodDecl->isStatic())
|
|
return true;
|
|
if (cxxMethodDecl->isOverloadedOperator())
|
|
return true;
|
|
if (isa<CXXConstructorDecl>(cxxMethodDecl))
|
|
return true;
|
|
if (isa<CXXDestructorDecl>(cxxMethodDecl))
|
|
return true;
|
|
if (cxxMethodDecl->getParent()->getDescribedClassTemplate() != nullptr ) {
|
|
return true;
|
|
}
|
|
// ignore virtual methods
|
|
if (cxxMethodDecl->isVirtual() ) {
|
|
return true;
|
|
}
|
|
// ignore macro expansions so we can ignore the IMPL_LINK macros from include/tools/link.hxx
|
|
// TODO make this more precise
|
|
if (cxxMethodDecl->getLocation().isMacroID())
|
|
return true;
|
|
|
|
if (!cxxMethodDecl->getIdentifier())
|
|
return true;
|
|
// if (cxxMethodDecl->getNumParams() > 0)
|
|
// return true;
|
|
// returning pointers or refs to non-const stuff, and then having the whole method
|
|
// be const doesn't seem like a good idea
|
|
auto tc = loplugin::TypeCheck(cxxMethodDecl->getReturnType());
|
|
if (tc.Pointer().NonConst())
|
|
return true;
|
|
if (tc.LvalueReference().NonConst())
|
|
return true;
|
|
// a Get method that returns void is probably doing something that has side-effects
|
|
if (tc.Void())
|
|
return true;
|
|
|
|
// StringRef name = cxxMethodDecl->getName();
|
|
// if (!name.startswith("get") && !name.startswith("Get")
|
|
// && !name.startswith("is") && !name.startswith("Is")
|
|
// && !name.startswith("has") && !name.startswith("Has"))
|
|
// return true;
|
|
|
|
// something lacking in my analysis here
|
|
if (loplugin::DeclCheck(cxxMethodDecl).Function("GetDescr").Class("SwRangeRedline").GlobalNamespace())
|
|
return true;
|
|
|
|
interestingMethodSet.insert(cxxMethodDecl);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ConstMethod::VisitCXXThisExpr( const CXXThisExpr* cxxThisExpr )
|
|
{
|
|
if (!currCXXMethodDecl)
|
|
return true;
|
|
if (ignoreLocation(cxxThisExpr))
|
|
return true;
|
|
// ignore stuff that forms part of the stable URE interface
|
|
if (isInUnoIncludeFile(cxxThisExpr->getBeginLoc()))
|
|
return true;
|
|
if (interestingMethodSet.find(currCXXMethodDecl) == interestingMethodSet.end())
|
|
return true;
|
|
// no need to check again if we have already eliminated this one
|
|
if (methodCannotBeConstSet.find(currCXXMethodDecl) != methodCannotBeConstSet.end())
|
|
return true;
|
|
if (!checkIfCanBeConst(cxxThisExpr, currCXXMethodDecl))
|
|
methodCannotBeConstSet.insert(currCXXMethodDecl);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Walk up from a statement that contains a CXXThisExpr, checking if the usage means that the
|
|
// related CXXMethodDecl can be const.
|
|
bool ConstMethod::checkIfCanBeConst(const Stmt* stmt, const CXXMethodDecl* cxxMethodDecl)
|
|
{
|
|
const Stmt* parent = getParentStmt( stmt );
|
|
if (!parent) {
|
|
auto parentsRange = compiler.getASTContext().getParents(*stmt);
|
|
if ( parentsRange.begin() == parentsRange.end())
|
|
return true;
|
|
auto varDecl = dyn_cast_or_null<VarDecl>(parentsRange.begin()->get<Decl>());
|
|
if (!varDecl)
|
|
{
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
"no parent?",
|
|
stmt->getBeginLoc())
|
|
<< stmt->getSourceRange();
|
|
return false;
|
|
}
|
|
return varDecl->getType()->isIntegralOrEnumerationType()
|
|
|| loplugin::TypeCheck(varDecl->getType()).Pointer().Const()
|
|
|| loplugin::TypeCheck(varDecl->getType()).LvalueReference().Const();
|
|
}
|
|
|
|
if (auto unaryOperator = dyn_cast<UnaryOperator>(parent)) {
|
|
UnaryOperator::Opcode op = unaryOperator->getOpcode();
|
|
if (op == UO_PreInc || op == UO_PostInc
|
|
|| op == UO_PreDec || op == UO_PostDec) {
|
|
return false;
|
|
}
|
|
if (op == UO_Deref || op == UO_AddrOf) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
}
|
|
return true;
|
|
} else if (auto binaryOp = dyn_cast<BinaryOperator>(parent)) {
|
|
BinaryOperator::Opcode op = binaryOp->getOpcode();
|
|
if (binaryOp->getRHS() == stmt) {
|
|
return true;
|
|
}
|
|
if (op == BO_Assign || op == BO_PtrMemD || op == BO_PtrMemI || op == BO_MulAssign
|
|
|| op == BO_DivAssign || op == BO_RemAssign || op == BO_AddAssign
|
|
|| op == BO_SubAssign || op == BO_ShlAssign || op == BO_ShrAssign
|
|
|| op == BO_AndAssign || op == BO_XorAssign || op == BO_OrAssign) {
|
|
return false;
|
|
}
|
|
// // for pointer arithmetic need to check parent
|
|
// if (binaryOp->getType()->isPointerType()) {
|
|
// return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
// }
|
|
return true;
|
|
} else if (auto constructExpr = dyn_cast<CXXConstructExpr>(parent)) {
|
|
const CXXConstructorDecl * constructorDecl = constructExpr->getConstructor();
|
|
for (unsigned i = 0; i < constructExpr->getNumArgs(); ++i) {
|
|
if (constructExpr->getArg(i) == stmt) {
|
|
return isPointerOrReferenceToConst(constructorDecl->getParamDecl(i)->getType());
|
|
}
|
|
}
|
|
return false; // TODO ??
|
|
} else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent)) {
|
|
const CXXMethodDecl* calleeMethodDecl = dyn_cast_or_null<CXXMethodDecl>(operatorCallExpr->getDirectCallee());
|
|
if (calleeMethodDecl) {
|
|
// unary operator
|
|
if (calleeMethodDecl->getNumParams() == 0) {
|
|
// some classes like std::unique_ptr do not do a very good job with their operator-> which is always const
|
|
if (operatorCallExpr->getOperator() == OO_Arrow || operatorCallExpr->getOperator() == OO_Star) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
}
|
|
return calleeMethodDecl->isConst();
|
|
}
|
|
// some classes like std::unique_ptr do not do a very good job with their operator[] which is always const
|
|
if (calleeMethodDecl->getNumParams() == 1 && operatorCallExpr->getArg(0) == stmt) {
|
|
if (operatorCallExpr->getOperator() == OO_Subscript) {
|
|
return false;
|
|
}
|
|
}
|
|
// binary operator
|
|
if (operatorCallExpr->getArg(0) == stmt) {
|
|
return calleeMethodDecl->isConst();
|
|
}
|
|
unsigned const n = std::min(
|
|
operatorCallExpr->getNumArgs(),
|
|
calleeMethodDecl->getNumParams());
|
|
for (unsigned i = 1; i < n; ++i)
|
|
if (operatorCallExpr->getArg(i) == stmt) {
|
|
return isPointerOrReferenceToConst(calleeMethodDecl->getParamDecl(i - 1)->getType());
|
|
}
|
|
} else {
|
|
const Expr* callee = operatorCallExpr->getCallee()->IgnoreParenImpCasts();
|
|
const DeclRefExpr* dr = dyn_cast<DeclRefExpr>(callee);
|
|
const FunctionDecl* calleeFunctionDecl = nullptr;
|
|
if (dr) {
|
|
calleeFunctionDecl = dyn_cast<FunctionDecl>(dr->getDecl());
|
|
}
|
|
if (calleeFunctionDecl) {
|
|
for (unsigned i = 0; i < operatorCallExpr->getNumArgs(); ++i) {
|
|
if (operatorCallExpr->getArg(i) == stmt) {
|
|
return isPointerOrReferenceToConst(calleeFunctionDecl->getParamDecl(i)->getType());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false; // TODO ???
|
|
} else if (auto callExpr = dyn_cast<CallExpr>(parent)) {
|
|
QualType functionType = callExpr->getCallee()->getType();
|
|
if (functionType->isFunctionPointerType()) {
|
|
functionType = functionType->getPointeeType();
|
|
}
|
|
if (const FunctionProtoType* prototype = functionType->getAs<FunctionProtoType>()) {
|
|
// TODO could do better
|
|
if (prototype->isVariadic()) {
|
|
return false;
|
|
}
|
|
if (callExpr->getCallee() == stmt) {
|
|
return true;
|
|
}
|
|
for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
|
|
if (callExpr->getArg(i) == stmt) {
|
|
return isPointerOrReferenceToConst(prototype->getParamType(i));
|
|
}
|
|
}
|
|
}
|
|
const FunctionDecl* calleeFunctionDecl = callExpr->getDirectCallee();
|
|
if (calleeFunctionDecl)
|
|
{
|
|
if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(parent)) {
|
|
const MemberExpr* memberExpr = dyn_cast<MemberExpr>(stmt);
|
|
if (memberExpr && memberCallExpr->getImplicitObjectArgument() == memberExpr->getBase())
|
|
{
|
|
const CXXMethodDecl* calleeMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
|
|
// some classes like std::unique_ptr do not do a very good job with their get() which is always const
|
|
if (calleeMethodDecl->getIdentifier() && calleeMethodDecl->getName() == "get") {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
}
|
|
// VclPtr<T>'s implicit conversion to T*
|
|
if (isa<CXXConversionDecl>(calleeMethodDecl)) {
|
|
if (loplugin::DeclCheck(calleeMethodDecl->getParent()).Class("OWeakObject").Namespace("cppu").GlobalNamespace())
|
|
return false;
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
}
|
|
return calleeMethodDecl->isConst();
|
|
}
|
|
}
|
|
// TODO could do better
|
|
if (calleeFunctionDecl->isVariadic()) {
|
|
return false;
|
|
}
|
|
if (callExpr->getCallee() == stmt) {
|
|
return true;
|
|
}
|
|
for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
|
|
if (i >= calleeFunctionDecl->getNumParams()) // can happen in template code
|
|
return false;
|
|
if (callExpr->getArg(i) == stmt) {
|
|
return isPointerOrReferenceToConst(calleeFunctionDecl->getParamDecl(i)->getType());
|
|
}
|
|
}
|
|
}
|
|
return false; // TODO ????
|
|
// } else if (auto callExpr = dyn_cast<ObjCMessageExpr>(parent)) {
|
|
// if (callExpr->getInstanceReceiver() == stmt) {
|
|
// return true;
|
|
// }
|
|
// if (auto const method = callExpr->getMethodDecl()) {
|
|
// // TODO could do better
|
|
// if (method->isVariadic()) {
|
|
// return false;
|
|
// }
|
|
// assert(method->param_size() == callExpr->getNumArgs());
|
|
// for (unsigned i = 0; i < callExpr->getNumArgs(); ++i) {
|
|
// if (callExpr->getArg(i) == stmt) {
|
|
// return isPointerOrReferenceToConst(
|
|
// method->param_begin()[i]->getType());
|
|
// }
|
|
// }
|
|
// }
|
|
// return false; // TODO ????
|
|
} else if (isa<CXXReinterpretCastExpr>(parent)) {
|
|
return false;
|
|
} else if (isa<ImplicitCastExpr>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (isa<CXXStaticCastExpr>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (isa<CXXDynamicCastExpr>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (isa<CXXFunctionalCastExpr>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (isa<CXXConstCastExpr>(parent)) {
|
|
return false;
|
|
} else if (isa<CStyleCastExpr>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
// } else if (isa<CastExpr>(parent)) { // all other cast expression subtypes
|
|
// if (auto e = dyn_cast<ExplicitCastExpr>(parent)) {
|
|
// if (loplugin::TypeCheck(e->getTypeAsWritten()).Void()) {
|
|
// if (auto const sub = dyn_cast<DeclRefExpr>(
|
|
// e->getSubExpr()->IgnoreParenImpCasts()))
|
|
// {
|
|
// if (sub->getDecl() == cxxMethodDecl) {
|
|
// return false;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
// return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (isa<MemberExpr>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (auto arraySubscriptExpr = dyn_cast<ArraySubscriptExpr>(parent)) {
|
|
if (arraySubscriptExpr->getIdx() == stmt)
|
|
return true;
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (isa<ParenExpr>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (auto declStmt = dyn_cast<DeclStmt>(parent)) {
|
|
for (Decl const * decl : declStmt->decls())
|
|
if (auto varDecl = dyn_cast<VarDecl>(decl)) {
|
|
if (varDecl->getInit() == stmt) {
|
|
auto tc = loplugin::TypeCheck(varDecl->getType());
|
|
if (tc.LvalueReference() && !tc.LvalueReference().Const())
|
|
return false;
|
|
if (tc.Pointer() && !tc.Pointer().Const())
|
|
return false;
|
|
return true;
|
|
}
|
|
}
|
|
// fall through
|
|
} else if (isa<ReturnStmt>(parent)) {
|
|
return !isPointerOrReferenceToNonConst(cxxMethodDecl->getReturnType());
|
|
} else if (isa<InitListExpr>(parent)) {
|
|
return false; // TODO could be improved
|
|
} else if (isa<IfStmt>(parent)) {
|
|
return true;
|
|
} else if (isa<WhileStmt>(parent)) {
|
|
return true;
|
|
} else if (isa<ForStmt>(parent)) {
|
|
return true;
|
|
} else if (isa<CompoundStmt>(parent)) {
|
|
return true;
|
|
} else if (isa<SwitchStmt>(parent)) {
|
|
return true;
|
|
} else if (isa<DoStmt>(parent)) {
|
|
return true;
|
|
} else if (isa<CXXDeleteExpr>(parent)) {
|
|
return false;
|
|
// } else if (isa<VAArgExpr>(parent)) {
|
|
// return false;
|
|
} else if (isa<CXXDependentScopeMemberExpr>(parent)) {
|
|
return false;
|
|
} else if (isa<MaterializeTemporaryExpr>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (auto conditionalExpr = dyn_cast<ConditionalOperator>(parent)) {
|
|
if (conditionalExpr->getCond() == stmt)
|
|
return true;
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
// } else if (isa<UnaryExprOrTypeTraitExpr>(parent)) {
|
|
// return false; // ???
|
|
} else if (isa<CXXNewExpr>(parent)) {
|
|
// for (auto pa : cxxNewExpr->placement_arguments())
|
|
// if (pa == stmt)
|
|
// return false;
|
|
return true; // because the Stmt must be a parameter to the expression, probably an array length
|
|
// } else if (auto lambdaExpr = dyn_cast<LambdaExpr>(parent)) {
|
|
//// for (auto it = lambdaExpr->capture_begin(); it != lambdaExpr->capture_end(); ++it)
|
|
//// {
|
|
//// if (it->capturesVariable() && it->getCapturedVar() == cxxMethodDecl)
|
|
//// return it->getCaptureKind() != LCK_ByRef;
|
|
//// }
|
|
// return true;
|
|
// } else if (isa<CXXTypeidExpr>(parent)) {
|
|
// return true;
|
|
} else if (isa<ParenListExpr>(parent)) {
|
|
return true;
|
|
} else if (isa<CXXUnresolvedConstructExpr>(parent)) {
|
|
return false;
|
|
// } else if (isa<UnresolvedMemberExpr>(parent)) {
|
|
// return false;
|
|
// } else if (isa<PackExpansionExpr>(parent)) {
|
|
// return false;
|
|
} else if (isa<ExprWithCleanups>(parent)) {
|
|
return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
// } else if (isa<CaseStmt>(parent)) {
|
|
// return true;
|
|
// } else if (isa<CXXPseudoDestructorExpr>(parent)) {
|
|
// return false;
|
|
// } else if (isa<CXXDependentScopeMemberExpr>(parent)) {
|
|
// return false;
|
|
// } else if (isa<ObjCIvarRefExpr>(parent)) {
|
|
// return checkIfCanBeConst(parent, cxxMethodDecl);
|
|
} else if (isa<CXXTemporaryObjectExpr>(parent)) {
|
|
return true;
|
|
} else if (isa<CXXBindTemporaryExpr>(parent)) {
|
|
return true;
|
|
}
|
|
if (parent)
|
|
parent->dump();
|
|
// if (cxxMethodDecl)
|
|
// cxxMethodDecl->dump();
|
|
report(
|
|
DiagnosticsEngine::Warning,
|
|
"oh dear, what can the matter be?",
|
|
parent->getBeginLoc())
|
|
<< parent->getSourceRange();
|
|
return false;
|
|
}
|
|
|
|
bool ConstMethod::isPointerOrReferenceToConst(const QualType& qt) {
|
|
auto const type = loplugin::TypeCheck(qt);
|
|
if (type.Pointer()) {
|
|
return bool(type.Pointer().Const());
|
|
} else if (type.LvalueReference()) {
|
|
return bool(type.LvalueReference().Const());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool ConstMethod::isPointerOrReferenceToNonConst(const QualType& qt) {
|
|
auto const type = loplugin::TypeCheck(qt);
|
|
if (type.Pointer()) {
|
|
return !bool(type.Pointer().Const());
|
|
} else if (type.LvalueReference()) {
|
|
return !bool(type.LvalueReference().Const());
|
|
}
|
|
return false;
|
|
}
|
|
|
|
loplugin::Plugin::Registration< ConstMethod > X("constmethod", false);
|
|
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|