summaryrefslogtreecommitdiff
path: root/compilerplugins/clang/unusedfields.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'compilerplugins/clang/unusedfields.cxx')
-rw-r--r--compilerplugins/clang/unusedfields.cxx97
1 files changed, 79 insertions, 18 deletions
diff --git a/compilerplugins/clang/unusedfields.cxx b/compilerplugins/clang/unusedfields.cxx
index 5ba6ac3c1cd6..bcb2db202cec 100644
--- a/compilerplugins/clang/unusedfields.cxx
+++ b/compilerplugins/clang/unusedfields.cxx
@@ -159,9 +159,10 @@ public:
private:
MyFieldInfo niceName(const FieldDecl*);
void checkTouchedFromOutside(const FieldDecl* fieldDecl, const Expr* memberExpr);
- void checkWriteOnly(const FieldDecl* fieldDecl, const Expr* memberExpr);
- void checkReadOnly(const FieldDecl* fieldDecl, const Expr* memberExpr);
+ void checkIfReadFrom(const FieldDecl* fieldDecl, const Expr* memberExpr);
+ void checkIfWrittenTo(const FieldDecl* fieldDecl, const Expr* memberExpr);
bool isSomeKindOfZero(const Expr* arg);
+ bool checkForWriteWhenUsingCollectionType(const CXXMethodDecl * calleeMethodDecl);
bool IsPassedByNonConst(const FieldDecl* fieldDecl, const Stmt * child, CallerWrapper callExpr,
CalleeWrapper calleeFunctionDecl);
llvm::Optional<CalleeWrapper> getCallee(CallExpr const *);
@@ -453,14 +454,14 @@ bool UnusedFields::VisitMemberExpr( const MemberExpr* memberExpr )
checkTouchedFromOutside(fieldDecl, memberExpr);
- checkWriteOnly(fieldDecl, memberExpr);
+ checkIfReadFrom(fieldDecl, memberExpr);
- checkReadOnly(fieldDecl, memberExpr);
+ checkIfWrittenTo(fieldDecl, memberExpr);
return true;
}
-void UnusedFields::checkWriteOnly(const FieldDecl* fieldDecl, const Expr* memberExpr)
+void UnusedFields::checkIfReadFrom(const FieldDecl* fieldDecl, const Expr* memberExpr)
{
if (insideMoveOrCopyOrCloneDeclParent || insideStreamOutputOperator)
{
@@ -656,15 +657,10 @@ void UnusedFields::checkWriteOnly(const FieldDecl* fieldDecl, const Expr* member
if (bPotentiallyReadFrom)
{
readFromSet.insert(fieldInfo);
- if (fieldInfo.fieldName == "nNextElementNumber")
- {
- parent->dump();
- memberExpr->dump();
- }
}
}
-void UnusedFields::checkReadOnly(const FieldDecl* fieldDecl, const Expr* memberExpr)
+void UnusedFields::checkIfWrittenTo(const FieldDecl* fieldDecl, const Expr* memberExpr)
{
if (insideMoveOrCopyOrCloneDeclParent)
{
@@ -749,10 +745,10 @@ void UnusedFields::checkReadOnly(const FieldDecl* fieldDecl, const Expr* memberE
{
// if calling a non-const operator on the field
auto calleeMethodDecl = callee->getAsCXXMethodDecl();
- if (calleeMethodDecl
- && operatorCallExpr->getArg(0) == child && !calleeMethodDecl->isConst())
+ if (calleeMethodDecl && operatorCallExpr->getArg(0) == child)
{
- bPotentiallyWrittenTo = true;
+ if (!calleeMethodDecl->isConst())
+ bPotentiallyWrittenTo = checkForWriteWhenUsingCollectionType(calleeMethodDecl);
}
else if (IsPassedByNonConst(fieldDecl, child, operatorCallExpr, *callee))
{
@@ -773,13 +769,13 @@ void UnusedFields::checkReadOnly(const FieldDecl* fieldDecl, const Expr* memberE
if (tmp->isBoundMemberFunction(compiler.getASTContext())) {
tmp = dyn_cast<MemberExpr>(tmp)->getBase();
}
- if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp
- && !calleeMethodDecl->isConst())
+ if (cxxMemberCallExpr->getImplicitObjectArgument() == tmp)
{
- bPotentiallyWrittenTo = true;
+ if (!calleeMethodDecl->isConst())
+ bPotentiallyWrittenTo = checkForWriteWhenUsingCollectionType(calleeMethodDecl);
break;
}
- if (IsPassedByNonConst(fieldDecl, child, cxxMemberCallExpr, CalleeWrapper(calleeMethodDecl)))
+ else if (IsPassedByNonConst(fieldDecl, child, cxxMemberCallExpr, CalleeWrapper(calleeMethodDecl)))
bPotentiallyWrittenTo = true;
}
else
@@ -888,6 +884,71 @@ void UnusedFields::checkReadOnly(const FieldDecl* fieldDecl, const Expr* memberE
}
}
+// return true if this not a collection type, or if it is a collection type, and we might be writing to it
+bool UnusedFields::checkForWriteWhenUsingCollectionType(const CXXMethodDecl * calleeMethodDecl)
+{
+ auto const tc = loplugin::TypeCheck(calleeMethodDecl->getParent());
+ bool listLike = false, setLike = false, mapLike = false, cssSequence = false;
+ if (tc.Class("deque").StdNamespace()
+ || tc.Class("list").StdNamespace()
+ || tc.Class("queue").StdNamespace()
+ || tc.Class("vector").StdNamespace())
+ {
+ listLike = true;
+ }
+ else if (tc.Class("set").StdNamespace()
+ || tc.Class("unordered_set").StdNamespace())
+ {
+ setLike = true;
+ }
+ else if (tc.Class("map").StdNamespace()
+ || tc.Class("unordered_map").StdNamespace())
+ {
+ mapLike = true;
+ }
+ else if (tc.Class("Sequence").Namespace("uno").Namespace("star").Namespace("sun").Namespace("com").GlobalNamespace())
+ {
+ cssSequence = true;
+ }
+ else
+ return true;
+
+ if (calleeMethodDecl->isOverloadedOperator())
+ {
+ auto oo = calleeMethodDecl->getOverloadedOperator();
+ if (oo == OO_Equal)
+ return true;
+ // This is operator[]. We only care about things that add elements to the collection.
+ // if nothing modifies the size of the collection, then nothing useful
+ // is stored in it.
+ if (listLike)
+ return false;
+ return true;
+ }
+
+ auto name = calleeMethodDecl->getName();
+ if (listLike || setLike || mapLike)
+ {
+ if (name == "reserve" || name == "shrink_to_fit" || name == "clear"
+ || name == "erase" || name == "pop_back" || name == "pop_front"
+ || name == "front" || name == "back" || name == "data"
+ || name == "remove" || name == "remove_if"
+ || name == "unique" || name == "sort"
+ || name == "begin" || name == "end"
+ || name == "rbegin" || name == "rend"
+ || name == "at" || name == "find" || name == "equal_range"
+ || name == "lower_bound" || name == "upper_bound")
+ return false;
+ }
+ if (cssSequence)
+ {
+ if (name == "getArray" || name == "begin" || name == "end")
+ return false;
+ }
+
+ return true;
+}
+
bool UnusedFields::IsPassedByNonConst(const FieldDecl* fieldDecl, const Stmt * child, CallerWrapper callExpr,
CalleeWrapper calleeFunctionDecl)
{