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:
parent
ab7e112bcf
commit
8110dd24d1
@ -35,25 +35,107 @@ public:
|
|||||||
|
|
||||||
virtual void run() override { TraverseDecl(compiler.getASTContext().getTranslationUnitDecl()); }
|
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 VisitFunctionDecl(const FunctionDecl * decl);
|
||||||
|
bool TraverseImplicitCastExpr(ImplicitCastExpr * expr);
|
||||||
|
bool VisitDeclRefExpr(const DeclRefExpr * expr);
|
||||||
|
|
||||||
bool VisitCallExpr(const CallExpr * ) { if (mbInsideFunctionDecl) mbFoundDisqualifier = true; return true; }
|
bool VisitCallExpr(const CallExpr * ) { if (mbInsideFunctionDecl) mbFoundDisqualifier = true; return true; }
|
||||||
bool VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr * ) { 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 VisitDeclStmt(const DeclStmt * ) { if (mbInsideFunctionDecl) mbFoundDisqualifier = true; return true; }
|
||||||
bool VisitLambdaExpr(const LambdaExpr * expr);
|
bool VisitLambdaExpr(const LambdaExpr * expr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template<typename T> bool traverseAnyFunctionDecl(
|
||||||
|
T * decl, bool (RecursiveASTVisitor<PassStuffByRef>::* fn)(T *));
|
||||||
void checkParams(const FunctionDecl * functionDecl);
|
void checkParams(const FunctionDecl * functionDecl);
|
||||||
void checkReturnValue(const FunctionDecl * functionDecl, const CXXMethodDecl * methodDecl);
|
void checkReturnValue(const FunctionDecl * functionDecl, const CXXMethodDecl * methodDecl);
|
||||||
bool isFat(QualType type);
|
bool isFat(QualType type);
|
||||||
bool isPrimitiveConstRef(QualType type);
|
bool isPrimitiveConstRef(QualType type);
|
||||||
bool mbInsideFunctionDecl;
|
bool mbInsideFunctionDecl;
|
||||||
bool mbFoundDisqualifier;
|
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) {
|
bool startswith(const std::string& rStr, const char* pSubStr) {
|
||||||
return rStr.compare(0, strlen(pSubStr), pSubStr) == 0;
|
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) {
|
bool PassStuffByRef::VisitFunctionDecl(const FunctionDecl * functionDecl) {
|
||||||
if (ignoreLocation(functionDecl)) {
|
if (ignoreLocation(functionDecl)) {
|
||||||
return true;
|
return true;
|
||||||
@ -75,9 +157,36 @@ bool PassStuffByRef::VisitFunctionDecl(const FunctionDecl * functionDecl) {
|
|||||||
return true;
|
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) {
|
void PassStuffByRef::checkParams(const FunctionDecl * functionDecl) {
|
||||||
// Only warn on the definition of the function:
|
// Only warn on the definition of the function:
|
||||||
if (!functionDecl->isThisDeclarationADefinition()) {
|
if (!functionDecl->doesThisDeclarationHaveABody()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
unsigned n = functionDecl->getNumParams();
|
unsigned n = functionDecl->getNumParams();
|
||||||
@ -102,24 +211,13 @@ void PassStuffByRef::checkParams(const FunctionDecl * functionDecl) {
|
|||||||
if (startswith(aFunctionName, "slideshow::internal::ShapeAttributeLayer")) {
|
if (startswith(aFunctionName, "slideshow::internal::ShapeAttributeLayer")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
assert(!functionDecls_.empty());
|
||||||
|
functionDecls_.back().check = true;
|
||||||
for (unsigned i = 0; i != n; ++i) {
|
for (unsigned i = 0; i != n; ++i) {
|
||||||
const ParmVarDecl * pvDecl = functionDecl->getParamDecl(i);
|
const ParmVarDecl * pvDecl = functionDecl->getParamDecl(i);
|
||||||
auto const t = pvDecl->getType();
|
auto const t = pvDecl->getType();
|
||||||
if (isPrimitiveConstRef(t)) {
|
if (isPrimitiveConstRef(t)) {
|
||||||
#if 0 //TODO: check that the parm is not bound to a reference
|
functionDecls_.back().parms.insert(pvDecl);
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,7 +344,7 @@ bool PassStuffByRef::isPrimitiveConstRef(QualType type) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// ignore double for now, some of our code seems to believe it is cheaper to pass by ref
|
// 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;
|
return builtinType->getKind() != BuiltinType::Kind::Double;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user