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
This commit is contained in:
Stephan Bergmann 2016-06-13 19:22:12 +02:00
parent ab7e112bcf
commit 8110dd24d1

View File

@ -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;
}