[Libreoffice-commits] core.git: compilerplugins/clang

Stephan Bergmann sbergman at redhat.com
Mon Jun 13 17:27:30 UTC 2016


 compilerplugins/clang/passstuffbyref.cxx |  130 +++++++++++++++++++++++++++----
 1 file changed, 114 insertions(+), 16 deletions(-)

New commits:
commit 8110dd24d11229b6518c8b2cd5289c20589e8258
Author: Stephan Bergmann <sbergman at redhat.com>
Date:   Mon Jun 13 19:22:12 2016 +0200

    Fix loplugin:passstuffbyref to not warn when ref param is bound to ref
    
    cf. d150eab88ee26d5c05a6d662a2c13c6adea8ad78 "loplugin:passstuffbyref: For now
    disable 'pass parm by value' warnings".  At least all the other changes in
    4d49c9601c9b3e26a336e08e057d299895683480 "Let loplugin:passstuffbyref also look
    at fn defn not preceded by any decl" were OK but the one reverted with
    b3e939971f56d53e60448a954a616ec295544098 "coverity#1362680 Pointer to local
    outside scope".
    
    Change-Id: I022125fbcb592e7da3c288c0fd09079dd2e87928

diff --git a/compilerplugins/clang/passstuffbyref.cxx b/compilerplugins/clang/passstuffbyref.cxx
index 2768b81..d245152 100644
--- a/compilerplugins/clang/passstuffbyref.cxx
+++ b/compilerplugins/clang/passstuffbyref.cxx
@@ -35,25 +35,107 @@ public:
 
     virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
 
+    // When warning about function params of primitive type that could be passed
+    // by value instead of by reference, make sure not to warn if the paremeter
+    // is ever bound to a reference; on the one hand, this needs scaffolding in
+    // all Traverse*Decl functions (indirectly) derived from FunctionDecl; and
+    // on the other hand, use a hack of ignoring just the DeclRefExprs nested in
+    // LValueToRValue ImplicitCastExprs when determining whether a param is
+    // bound to a reference:
+    bool TraverseFunctionDecl(FunctionDecl * decl);
+    bool TraverseCXXMethodDecl(CXXMethodDecl * decl);
+    bool TraverseCXXConstructorDecl(CXXConstructorDecl * decl);
+    bool TraverseCXXDestructorDecl(CXXDestructorDecl * decl);
+    bool TraverseCXXConversionDecl(CXXConversionDecl * decl);
     bool VisitFunctionDecl(const FunctionDecl * decl);
+    bool TraverseImplicitCastExpr(ImplicitCastExpr * expr);
+    bool VisitDeclRefExpr(const DeclRefExpr * expr);
+
     bool VisitCallExpr(const CallExpr * ) { if (mbInsideFunctionDecl) mbFoundDisqualifier = true; return true; }
     bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr * ) { if (mbInsideFunctionDecl) mbFoundDisqualifier = true; return true; }
     bool VisitDeclStmt(const DeclStmt * ) { if (mbInsideFunctionDecl) mbFoundDisqualifier = true; return true; }
     bool VisitLambdaExpr(const LambdaExpr * expr);
 
 private:
+    template<typename T> bool traverseAnyFunctionDecl(
+        T * decl, bool (RecursiveASTVisitor<PassStuffByRef>::* fn)(T *));
     void checkParams(const FunctionDecl * functionDecl);
     void checkReturnValue(const FunctionDecl * functionDecl, const CXXMethodDecl * methodDecl);
     bool isFat(QualType type);
     bool isPrimitiveConstRef(QualType type);
     bool mbInsideFunctionDecl;
     bool mbFoundDisqualifier;
+
+    struct FDecl {
+        std::set<ParmVarDecl const *> parms;
+        bool check = false;
+    };
+    std::vector<FDecl> functionDecls_;
 };
 
 bool startswith(const std::string& rStr, const char* pSubStr) {
     return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
 }
 
+bool PassStuffByRef::TraverseFunctionDecl(FunctionDecl * decl) {
+    return traverseAnyFunctionDecl(
+        decl, &RecursiveASTVisitor::TraverseFunctionDecl);
+}
+
+bool PassStuffByRef::TraverseCXXMethodDecl(CXXMethodDecl * decl) {
+    return traverseAnyFunctionDecl(
+        decl, &RecursiveASTVisitor::TraverseCXXMethodDecl);
+}
+
+bool PassStuffByRef::TraverseCXXConstructorDecl(CXXConstructorDecl * decl) {
+    return traverseAnyFunctionDecl(
+        decl, &RecursiveASTVisitor::TraverseCXXConstructorDecl);
+}
+
+bool PassStuffByRef::TraverseCXXDestructorDecl(CXXDestructorDecl * decl) {
+    return traverseAnyFunctionDecl(
+        decl, &RecursiveASTVisitor::TraverseCXXDestructorDecl);
+}
+
+bool PassStuffByRef::TraverseCXXConversionDecl(CXXConversionDecl * decl) {
+    return traverseAnyFunctionDecl(
+        decl, &RecursiveASTVisitor::TraverseCXXConversionDecl);
+}
+
+template<typename T> bool PassStuffByRef::traverseAnyFunctionDecl(
+    T * decl, bool (RecursiveASTVisitor<PassStuffByRef>::* fn)(T *))
+{
+    if (ignoreLocation(decl)) {
+        return true;
+    }
+    if (decl->doesThisDeclarationHaveABody()) {
+        functionDecls_.emplace_back();
+    }
+    bool ret = (this->*fn)(decl);
+    if (decl->doesThisDeclarationHaveABody()) {
+        assert(!functionDecls_.empty());
+        if (functionDecls_.back().check) {
+            for (auto d: functionDecls_.back().parms) {
+                report(
+                    DiagnosticsEngine::Warning,
+                    ("passing primitive type param %0 by const &, rather pass"
+                     " by value"),
+                    d->getLocation())
+                    << d->getType() << d->getSourceRange();
+                auto can = decl->getCanonicalDecl();
+                if (can->getLocation() != decl->getLocation()) {
+                    report(
+                        DiagnosticsEngine::Note, "function is declared here:",
+                        can->getLocation())
+                        << can->getSourceRange();
+                }
+            }
+        }
+        functionDecls_.pop_back();
+    }
+    return ret;
+}
+
 bool PassStuffByRef::VisitFunctionDecl(const FunctionDecl * functionDecl) {
     if (ignoreLocation(functionDecl)) {
         return true;
@@ -75,9 +157,36 @@ bool PassStuffByRef::VisitFunctionDecl(const FunctionDecl * functionDecl) {
     return true;
 }
 
+bool PassStuffByRef::TraverseImplicitCastExpr(ImplicitCastExpr * expr) {
+    if (ignoreLocation(expr)) {
+        return true;
+    }
+    return
+        (expr->getCastKind() == CK_LValueToRValue
+         && (dyn_cast<DeclRefExpr>(expr->getSubExpr()->IgnoreParenImpCasts())
+             != nullptr))
+        || RecursiveASTVisitor::TraverseImplicitCastExpr(expr);
+}
+
+bool PassStuffByRef::VisitDeclRefExpr(const DeclRefExpr * expr) {
+    if (ignoreLocation(expr)) {
+        return true;
+    }
+    auto d = dyn_cast<ParmVarDecl>(expr->getDecl());
+    if (d == nullptr) {
+        return true;
+    }
+    for (auto & fd: functionDecls_) {
+        if (fd.parms.erase(d) == 1) {
+            break;
+        }
+    }
+    return true;
+}
+
 void PassStuffByRef::checkParams(const FunctionDecl * functionDecl) {
     // Only warn on the definition of the function:
-    if (!functionDecl->isThisDeclarationADefinition()) {
+    if (!functionDecl->doesThisDeclarationHaveABody()) {
         return;
     }
     unsigned n = functionDecl->getNumParams();
@@ -102,24 +211,13 @@ void PassStuffByRef::checkParams(const FunctionDecl * functionDecl) {
     if (startswith(aFunctionName, "slideshow::internal::ShapeAttributeLayer")) {
         return;
     }
+    assert(!functionDecls_.empty());
+    functionDecls_.back().check = true;
     for (unsigned i = 0; i != n; ++i) {
         const ParmVarDecl * pvDecl = functionDecl->getParamDecl(i);
         auto const t = pvDecl->getType();
         if (isPrimitiveConstRef(t)) {
-#if 0 //TODO: check that the parm is not bound to a reference
-            report(
-                DiagnosticsEngine::Warning,
-                ("passing primitive type param %0 by const &, rather pass by value"),
-                pvDecl->getLocation())
-                << t << pvDecl->getSourceRange();
-            auto can = functionDecl->getCanonicalDecl();
-            if (can->getLocation() != functionDecl->getLocation()) {
-                report(
-                    DiagnosticsEngine::Note, "function is declared here:",
-                    can->getLocation())
-                    << can->getSourceRange();
-            }
-#endif
+            functionDecls_.back().parms.insert(pvDecl);
         }
     }
 }
@@ -246,7 +344,7 @@ bool PassStuffByRef::isPrimitiveConstRef(QualType type) {
         return false;
     }
     // ignore double for now, some of our code seems to believe it is cheaper to pass by ref
-    const BuiltinType* builtinType = dyn_cast<BuiltinType>(pointeeQT);
+    const BuiltinType* builtinType = pointeeQT->getAs<BuiltinType>();
     return builtinType->getKind() != BuiltinType::Kind::Double;
 }
 


More information about the Libreoffice-commits mailing list