diff options
author | Noel Grandin <noel.grandin@collabora.co.uk> | 2017-12-22 14:23:16 +0200 |
---|---|---|
committer | Noel Grandin <noel.grandin@collabora.co.uk> | 2017-12-23 08:04:54 +0100 |
commit | 2a1fb4401da16f6a18c0bd05fe4b460a3048f9b5 (patch) | |
tree | e659812bc29bb01db62cde81f3d218758b26e07c /compilerplugins/clang/passstuffbyref.cxx | |
parent | 7f42b0f96a2798ae99aa65b84b0db3b2af2b282b (diff) |
loplugin:passstuffbyref improved returns
improve the detection of stuff we can return by const &, instead of by
copying
Change-Id: I479ae89d0413125a8295cc3cddbc0017ed61ed69
Reviewed-on: https://gerrit.libreoffice.org/46915
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
Diffstat (limited to 'compilerplugins/clang/passstuffbyref.cxx')
-rw-r--r-- | compilerplugins/clang/passstuffbyref.cxx | 142 |
1 files changed, 97 insertions, 45 deletions
diff --git a/compilerplugins/clang/passstuffbyref.cxx b/compilerplugins/clang/passstuffbyref.cxx index 8c522efb5dbe..5901a587594e 100644 --- a/compilerplugins/clang/passstuffbyref.cxx +++ b/compilerplugins/clang/passstuffbyref.cxx @@ -9,6 +9,7 @@ #include <string> #include <set> +#include <iostream> #include "check.hxx" #include "plugin.hxx" @@ -211,7 +212,6 @@ static bool startswith(const std::string& rStr, const char* pSubStr) { } void PassStuffByRef::checkReturnValue(const FunctionDecl * functionDecl, const CXXMethodDecl * methodDecl) { - if (methodDecl && (methodDecl->isVirtual() || methodDecl->hasAttr<OverrideAttr>())) { return; } @@ -233,6 +233,7 @@ void PassStuffByRef::checkReturnValue(const FunctionDecl * functionDecl, const C if (isInUnoIncludeFile(functionDecl)) { return; } + loplugin::DeclCheck dc(functionDecl); // function is passed as parameter to another function if (dc.Function("ImplColMonoFnc").Class("GDIMetaFile").GlobalNamespace() @@ -274,21 +275,22 @@ void PassStuffByRef::checkReturnValue(const FunctionDecl * functionDecl, const C if (mbFoundReturnValueDisqualifier) return; - report( - DiagnosticsEngine::Warning, - "rather return %0 from function %1 by const& than by value, to avoid unnecessary copying", + report( DiagnosticsEngine::Warning, + "rather return %0 by const& than by value, to avoid unnecessary copying", functionDecl->getSourceRange().getBegin()) - << type.getAsString() << functionDecl->getQualifiedNameAsString() << functionDecl->getSourceRange(); + << type.getAsString() << functionDecl->getSourceRange(); // display the location of the class member declaration so I don't have to search for it by hand - if (functionDecl->getSourceRange().getBegin() != functionDecl->getCanonicalDecl()->getSourceRange().getBegin()) + auto canonicalDecl = functionDecl->getCanonicalDecl(); + if (functionDecl != canonicalDecl) { - report( - DiagnosticsEngine::Note, + report( DiagnosticsEngine::Note, "decl here", - functionDecl->getCanonicalDecl()->getSourceRange().getBegin()) - << functionDecl->getCanonicalDecl()->getSourceRange(); + canonicalDecl->getSourceRange().getBegin()) + << canonicalDecl->getSourceRange(); } + + //functionDecl->dump(); } bool PassStuffByRef::VisitReturnStmt(const ReturnStmt * returnStmt) @@ -297,53 +299,103 @@ bool PassStuffByRef::VisitReturnStmt(const ReturnStmt * returnStmt) return true; const Expr* expr = dyn_cast<Expr>(*returnStmt->child_begin())->IgnoreParenCasts(); - if (isReturnExprDisqualified(expr)) { + if (isReturnExprDisqualified(expr)) mbFoundReturnValueDisqualifier = true; - return true; - } + return true; } +/** + * Does a return expression disqualify this method from doing return by const & ? + */ bool PassStuffByRef::isReturnExprDisqualified(const Expr* expr) { - if (isa<ExprWithCleanups>(expr)) { - return true; - } - if (const CXXConstructExpr* constructExpr = dyn_cast<CXXConstructExpr>(expr)) + while (true) { - if (constructExpr->getNumArgs()==1 - && constructExpr->getConstructor()->isCopyOrMoveConstructor()) + expr = expr->IgnoreParens(); + if (auto implicitCast = dyn_cast<ImplicitCastExpr>(expr)) { + expr = implicitCast->getSubExpr(); + continue; + } + if (auto exprWithCleanups = dyn_cast<ExprWithCleanups>(expr)) { + expr = exprWithCleanups->getSubExpr(); + continue; + } + if (auto constructExpr = dyn_cast<CXXConstructExpr>(expr)) { - expr = constructExpr->getArg(0)->IgnoreParenCasts(); + if (constructExpr->getNumArgs()==1 + && constructExpr->getConstructor()->isCopyOrMoveConstructor()) + { + expr = constructExpr->getArg(0); + continue; + } + else + return true; } - } - if (isa<CXXConstructExpr>(expr)) { - return true; - } - if (const ArraySubscriptExpr* childExpr = dyn_cast<ArraySubscriptExpr>(expr)) { - expr = childExpr->getLHS(); - } - if (const MemberExpr* memberExpr = dyn_cast<MemberExpr>(expr)) { - expr = memberExpr->getBase(); - } - if (const DeclRefExpr* declRef = dyn_cast<DeclRefExpr>(expr)) { - const VarDecl* varDecl = dyn_cast<VarDecl>(declRef->getDecl()); - if (varDecl) { - if (varDecl->getStorageDuration() == SD_Automatic - || varDecl->getStorageDuration() == SD_FullExpression ) { + if (isa<MaterializeTemporaryExpr>(expr)) { + return true; + } + if (isa<CXXBindTemporaryExpr>(expr)) { + return true; + } + expr = expr->IgnoreParenCasts(); + if (auto childExpr = dyn_cast<ArraySubscriptExpr>(expr)) { + expr = childExpr->getLHS(); + continue; + } + if (auto memberExpr = dyn_cast<MemberExpr>(expr)) { + expr = memberExpr->getBase(); + continue; + } + if (auto declRef = dyn_cast<DeclRefExpr>(expr)) { + // a param might be a temporary + if (isa<ParmVarDecl>(declRef->getDecl())) + return true; + const VarDecl* varDecl = dyn_cast<VarDecl>(declRef->getDecl()); + if (varDecl) { + if (varDecl->getStorageDuration() == SD_Thread + || varDecl->getStorageDuration() == SD_Static ) { + return false; + } return true; } - return false; } + if (auto condOper = dyn_cast<ConditionalOperator>(expr)) { + return isReturnExprDisqualified(condOper->getTrueExpr()) + || isReturnExprDisqualified(condOper->getFalseExpr()); + } + if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(expr)) { + // TODO could improve this, but sometimes it means we're returning a copy of a temporary. + // Same logic as CXXOperatorCallExpr::isAssignmentOp(), which our supported clang + // doesn't have yet. + auto Opc = operatorCallExpr->getOperator(); + if (Opc == OO_Equal || Opc == OO_StarEqual || + Opc == OO_SlashEqual || Opc == OO_PercentEqual || + Opc == OO_PlusEqual || Opc == OO_MinusEqual || + Opc == OO_LessLessEqual || Opc == OO_GreaterGreaterEqual || + Opc == OO_AmpEqual || Opc == OO_CaretEqual || + Opc == OO_PipeEqual) + return true; + if (Opc == OO_Subscript) + { + if (isReturnExprDisqualified(operatorCallExpr->getArg(0))) + return true; + // otherwise fall through to the checking below + } + } + if (auto memberCallExpr = dyn_cast<CXXMemberCallExpr>(expr)) { + if (isReturnExprDisqualified(memberCallExpr->getImplicitObjectArgument())) + return true; + // otherwise fall through to the checking in CallExpr + } + if (auto callExpr = dyn_cast<CallExpr>(expr)) { + FunctionDecl const * calleeFunctionDecl = callExpr->getDirectCallee(); + if (!calleeFunctionDecl) + return true; + return !loplugin::TypeCheck(calleeFunctionDecl->getReturnType()).LvalueReference(); + } + return false; } - if (const ConditionalOperator* condOper = dyn_cast<ConditionalOperator>(expr)) { - return isReturnExprDisqualified(condOper->getTrueExpr()) - || isReturnExprDisqualified(condOper->getFalseExpr()); - } - if (const CallExpr* callExpr = dyn_cast<CallExpr>(expr)) { - return !loplugin::TypeCheck(callExpr->getType()).Const().LvalueReference(); - } - return true; } bool PassStuffByRef::VisitVarDecl(const VarDecl * varDecl) @@ -388,7 +440,7 @@ bool PassStuffByRef::isPrimitiveConstRef(QualType type) { } -loplugin::Plugin::Registration< PassStuffByRef > X("passstuffbyref"); +loplugin::Plugin::Registration< PassStuffByRef > X("passstuffbyref", false); } |