loplugin:unusedfields improve finding callee
which makes absolutely no difference to the results, but anyhow, would be a shame to waste the work Change-Id: I4576528f30986a5ce522c76fdf21873f0ce23f0a
This commit is contained in:
@@ -83,15 +83,49 @@ public:
|
|||||||
};
|
};
|
||||||
class CalleeWrapper
|
class CalleeWrapper
|
||||||
{
|
{
|
||||||
const FunctionDecl * m_calleeFunctionDecl;
|
const FunctionDecl * m_calleeFunctionDecl = nullptr;
|
||||||
const CXXConstructorDecl * m_cxxConstructorDecl;
|
const CXXConstructorDecl * m_cxxConstructorDecl = nullptr;
|
||||||
|
const FunctionProtoType * m_functionPrototype = nullptr;
|
||||||
public:
|
public:
|
||||||
CalleeWrapper(const FunctionDecl * calleeFunctionDecl) : m_calleeFunctionDecl(calleeFunctionDecl), m_cxxConstructorDecl(nullptr) {}
|
explicit CalleeWrapper(const FunctionDecl * calleeFunctionDecl) : m_calleeFunctionDecl(calleeFunctionDecl) {}
|
||||||
CalleeWrapper(const CXXConstructExpr * cxxConstructExpr) : m_calleeFunctionDecl(nullptr), m_cxxConstructorDecl(cxxConstructExpr->getConstructor()) {}
|
explicit CalleeWrapper(const CXXConstructExpr * cxxConstructExpr) : m_cxxConstructorDecl(cxxConstructExpr->getConstructor()) {}
|
||||||
unsigned getNumParams () const
|
explicit CalleeWrapper(const FunctionProtoType * functionPrototype) : m_functionPrototype(functionPrototype) {}
|
||||||
{ return m_calleeFunctionDecl ? m_calleeFunctionDecl->getNumParams() : m_cxxConstructorDecl->getNumParams(); }
|
unsigned getNumParams() const
|
||||||
const ParmVarDecl * getParamDecl (unsigned i) const
|
{
|
||||||
{ return m_calleeFunctionDecl ? m_calleeFunctionDecl->getParamDecl(i) : m_cxxConstructorDecl->getParamDecl(i); }
|
if (m_calleeFunctionDecl)
|
||||||
|
return m_calleeFunctionDecl->getNumParams();
|
||||||
|
else if (m_cxxConstructorDecl)
|
||||||
|
return m_cxxConstructorDecl->getNumParams();
|
||||||
|
else if (m_functionPrototype->param_type_begin() == m_functionPrototype->param_type_end())
|
||||||
|
// FunctionProtoType will assert if we call getParamTypes() and it has no params
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return m_functionPrototype->getParamTypes().size();
|
||||||
|
}
|
||||||
|
const QualType getParamType(unsigned i) const
|
||||||
|
{
|
||||||
|
if (m_calleeFunctionDecl)
|
||||||
|
return m_calleeFunctionDecl->getParamDecl(i)->getType();
|
||||||
|
else if (m_cxxConstructorDecl)
|
||||||
|
return m_cxxConstructorDecl->getParamDecl(i)->getType();
|
||||||
|
else
|
||||||
|
return m_functionPrototype->getParamTypes()[i];
|
||||||
|
}
|
||||||
|
std::string getNameAsString() const
|
||||||
|
{
|
||||||
|
if (m_calleeFunctionDecl)
|
||||||
|
return m_calleeFunctionDecl->getNameAsString();
|
||||||
|
else if (m_cxxConstructorDecl)
|
||||||
|
return m_cxxConstructorDecl->getNameAsString();
|
||||||
|
else
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
CXXMethodDecl const * getAsCXXMethodDecl() const
|
||||||
|
{
|
||||||
|
if (m_calleeFunctionDecl)
|
||||||
|
return dyn_cast<CXXMethodDecl>(m_calleeFunctionDecl);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class UnusedFields:
|
class UnusedFields:
|
||||||
@@ -122,6 +156,7 @@ private:
|
|||||||
bool isSomeKindOfZero(const Expr* arg);
|
bool isSomeKindOfZero(const Expr* arg);
|
||||||
bool IsPassedByNonConst(const FieldDecl* fieldDecl, const Stmt * child, CallerWrapper callExpr,
|
bool IsPassedByNonConst(const FieldDecl* fieldDecl, const Stmt * child, CallerWrapper callExpr,
|
||||||
CalleeWrapper calleeFunctionDecl);
|
CalleeWrapper calleeFunctionDecl);
|
||||||
|
llvm::Optional<CalleeWrapper> getCallee(CallExpr const *);
|
||||||
|
|
||||||
RecordDecl * insideMoveOrCopyDeclParent;
|
RecordDecl * insideMoveOrCopyDeclParent;
|
||||||
RecordDecl * insideStreamOutputOperator;
|
RecordDecl * insideStreamOutputOperator;
|
||||||
@@ -492,12 +527,12 @@ void UnusedFields::checkWriteOnly(const FieldDecl* fieldDecl, const Expr* member
|
|||||||
else if (auto callExpr = dyn_cast<CallExpr>(parent))
|
else if (auto callExpr = dyn_cast<CallExpr>(parent))
|
||||||
{
|
{
|
||||||
// check for calls to ReadXXX() type methods and the operator>>= methods on Any.
|
// check for calls to ReadXXX() type methods and the operator>>= methods on Any.
|
||||||
const FunctionDecl * calleeFunctionDecl = callExpr->getDirectCallee();
|
auto callee = getCallee(callExpr);
|
||||||
if (calleeFunctionDecl)
|
if (callee)
|
||||||
{
|
{
|
||||||
// FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute
|
// FIXME perhaps a better solution here would be some kind of SAL_PARAM_WRITEONLY attribute
|
||||||
// which we could scatter around.
|
// which we could scatter around.
|
||||||
std::string name = calleeFunctionDecl->getNameAsString();
|
std::string name = callee->getNameAsString();
|
||||||
std::transform(name.begin(), name.end(), name.begin(), easytolower);
|
std::transform(name.begin(), name.end(), name.begin(), easytolower);
|
||||||
if (startswith(name, "read"))
|
if (startswith(name, "read"))
|
||||||
// this is a write-only call
|
// this is a write-only call
|
||||||
@@ -657,17 +692,17 @@ void UnusedFields::checkReadOnly(const FieldDecl* fieldDecl, const Expr* memberE
|
|||||||
}
|
}
|
||||||
else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
|
else if (auto operatorCallExpr = dyn_cast<CXXOperatorCallExpr>(parent))
|
||||||
{
|
{
|
||||||
const FunctionDecl * calleeFunctionDecl = operatorCallExpr->getDirectCallee();
|
auto callee = getCallee(operatorCallExpr);
|
||||||
if (calleeFunctionDecl)
|
if (callee)
|
||||||
{
|
{
|
||||||
// if calling a non-const operator on the field
|
// if calling a non-const operator on the field
|
||||||
auto calleeMethodDecl = dyn_cast<CXXMethodDecl>(calleeFunctionDecl);
|
auto calleeMethodDecl = callee->getAsCXXMethodDecl();
|
||||||
if (calleeMethodDecl
|
if (calleeMethodDecl
|
||||||
&& operatorCallExpr->getArg(0) == child && !calleeMethodDecl->isConst())
|
&& operatorCallExpr->getArg(0) == child && !calleeMethodDecl->isConst())
|
||||||
{
|
{
|
||||||
bPotentiallyWrittenTo = true;
|
bPotentiallyWrittenTo = true;
|
||||||
}
|
}
|
||||||
else if (IsPassedByNonConst(fieldDecl, child, operatorCallExpr, calleeFunctionDecl))
|
else if (IsPassedByNonConst(fieldDecl, child, operatorCallExpr, *callee))
|
||||||
{
|
{
|
||||||
bPotentiallyWrittenTo = true;
|
bPotentiallyWrittenTo = true;
|
||||||
}
|
}
|
||||||
@@ -692,7 +727,7 @@ void UnusedFields::checkReadOnly(const FieldDecl* fieldDecl, const Expr* memberE
|
|||||||
bPotentiallyWrittenTo = true;
|
bPotentiallyWrittenTo = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (IsPassedByNonConst(fieldDecl, child, cxxMemberCallExpr, calleeMethodDecl))
|
if (IsPassedByNonConst(fieldDecl, child, cxxMemberCallExpr, CalleeWrapper(calleeMethodDecl)))
|
||||||
bPotentiallyWrittenTo = true;
|
bPotentiallyWrittenTo = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -701,15 +736,15 @@ void UnusedFields::checkReadOnly(const FieldDecl* fieldDecl, const Expr* memberE
|
|||||||
}
|
}
|
||||||
else if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(parent))
|
else if (auto cxxConstructExpr = dyn_cast<CXXConstructExpr>(parent))
|
||||||
{
|
{
|
||||||
if (IsPassedByNonConst(fieldDecl, child, cxxConstructExpr, cxxConstructExpr))
|
if (IsPassedByNonConst(fieldDecl, child, cxxConstructExpr, CalleeWrapper(cxxConstructExpr)))
|
||||||
bPotentiallyWrittenTo = true;
|
bPotentiallyWrittenTo = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (auto callExpr = dyn_cast<CallExpr>(parent))
|
else if (auto callExpr = dyn_cast<CallExpr>(parent))
|
||||||
{
|
{
|
||||||
const FunctionDecl * calleeFunctionDecl = callExpr->getDirectCallee();
|
auto callee = getCallee(callExpr);
|
||||||
if (calleeFunctionDecl) {
|
if (callee) {
|
||||||
if (IsPassedByNonConst(fieldDecl, child, callExpr, calleeFunctionDecl))
|
if (IsPassedByNonConst(fieldDecl, child, callExpr, *callee))
|
||||||
bPotentiallyWrittenTo = true;
|
bPotentiallyWrittenTo = true;
|
||||||
} else
|
} else
|
||||||
bPotentiallyWrittenTo = true; // conservative, could improve
|
bPotentiallyWrittenTo = true; // conservative, could improve
|
||||||
@@ -807,14 +842,14 @@ bool UnusedFields::IsPassedByNonConst(const FieldDecl* fieldDecl, const Stmt * c
|
|||||||
{
|
{
|
||||||
for (unsigned i = 0; i < len; ++i)
|
for (unsigned i = 0; i < len; ++i)
|
||||||
if (callExpr.getArg(i) == child)
|
if (callExpr.getArg(i) == child)
|
||||||
if (loplugin::TypeCheck(calleeFunctionDecl.getParamDecl(i)->getType()).Pointer().NonConst())
|
if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)).Pointer().NonConst())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < len; ++i)
|
for (unsigned i = 0; i < len; ++i)
|
||||||
if (callExpr.getArg(i) == child)
|
if (callExpr.getArg(i) == child)
|
||||||
if (loplugin::TypeCheck(calleeFunctionDecl.getParamDecl(i)->getType()).LvalueReference().NonConst())
|
if (loplugin::TypeCheck(calleeFunctionDecl.getParamType(i)).LvalueReference().NonConst())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -920,6 +955,49 @@ void UnusedFields::checkTouchedFromOutside(const FieldDecl* fieldDecl, const Exp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Optional<CalleeWrapper> UnusedFields::getCallee(CallExpr const * callExpr)
|
||||||
|
{
|
||||||
|
FunctionDecl const * functionDecl = callExpr->getDirectCallee();
|
||||||
|
if (functionDecl)
|
||||||
|
return CalleeWrapper(functionDecl);
|
||||||
|
|
||||||
|
// Extract the functionprototype from a type
|
||||||
|
Type const * calleeType = callExpr->getCallee()->getType().getTypePtr();
|
||||||
|
if (auto pointerType = calleeType->getUnqualifiedDesugaredType()->getAs<PointerType>()) {
|
||||||
|
if (auto prototype = pointerType->getPointeeType()->getUnqualifiedDesugaredType()->getAs<FunctionProtoType>()) {
|
||||||
|
return CalleeWrapper(prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Optional<CalleeWrapper> ret;
|
||||||
|
auto callee = callExpr->getCallee()->IgnoreParenImpCasts();
|
||||||
|
if (isa<CXXDependentScopeMemberExpr>(callee)) // template stuff
|
||||||
|
return ret;
|
||||||
|
if (isa<UnresolvedLookupExpr>(callee)) // template stuff
|
||||||
|
return ret;
|
||||||
|
if (isa<UnresolvedMemberExpr>(callee)) // template stuff
|
||||||
|
return ret;
|
||||||
|
calleeType = calleeType->getUnqualifiedDesugaredType();
|
||||||
|
if (isa<TemplateSpecializationType>(calleeType)) // template stuff
|
||||||
|
return ret;
|
||||||
|
if (auto builtinType = dyn_cast<BuiltinType>(calleeType)) {
|
||||||
|
if (builtinType->getKind() == BuiltinType::Kind::Dependent) // template stuff
|
||||||
|
return ret;
|
||||||
|
if (builtinType->getKind() == BuiltinType::Kind::BoundMember) // template stuff
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (isa<TemplateTypeParmType>(calleeType)) // template stuff
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
callExpr->dump();
|
||||||
|
callExpr->getCallee()->getType()->dump();
|
||||||
|
report(
|
||||||
|
DiagnosticsEngine::Warning, "can't get callee",
|
||||||
|
callExpr->getExprLoc())
|
||||||
|
<< callExpr->getSourceRange();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
loplugin::Plugin::Registration< UnusedFields > X("unusedfields", false);
|
loplugin::Plugin::Registration< UnusedFields > X("unusedfields", false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user