aboutsummaryrefslogtreecommitdiffstats
path: root/src/checks/level3/detaching-member.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/checks/level3/detaching-member.cpp')
-rw-r--r--src/checks/level3/detaching-member.cpp138
1 files changed, 138 insertions, 0 deletions
diff --git a/src/checks/level3/detaching-member.cpp b/src/checks/level3/detaching-member.cpp
new file mode 100644
index 00000000..39101fa9
--- /dev/null
+++ b/src/checks/level3/detaching-member.cpp
@@ -0,0 +1,138 @@
+/*
+ This file is part of the clazy static checker.
+
+ Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
+ Author: SĂ©rgio Martins <sergio.martins@kdab.com>
+
+ Copyright (C) 2015 Sergio Martins <smartins@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "detaching-member.h"
+#include "ClazyContext.h"
+#include "Utils.h"
+#include "HierarchyUtils.h"
+#include "StringUtils.h"
+
+#include <clang/AST/DeclCXX.h>
+#include <clang/AST/Expr.h>
+#include <clang/AST/ExprCXX.h>
+
+using namespace clang;
+using namespace std;
+
+DetachingMember::DetachingMember(const std::string &name, ClazyContext *context)
+ : DetachingBase(name, context, Option_CanIgnoreIncludes)
+{
+ m_filesToIgnore = { "qstring.h" };
+}
+
+void DetachingMember::VisitStmt(clang::Stmt *stm)
+{
+ auto callExpr = dyn_cast<CallExpr>(stm);
+ if (!callExpr)
+ return;
+
+ auto memberCall = dyn_cast<CXXMemberCallExpr>(callExpr);
+ auto operatorExpr = dyn_cast<CXXOperatorCallExpr>(callExpr);
+ if (!memberCall && !operatorExpr)
+ return;
+
+ if (shouldIgnoreFile(getLocStart(stm)))
+ return;
+
+ CXXMethodDecl *method = nullptr;
+ ValueDecl *memberDecl = nullptr;
+ if (operatorExpr) {
+ FunctionDecl *func = operatorExpr->getDirectCallee();
+ method = func ? dyn_cast<CXXMethodDecl>(func) : nullptr;
+ if (!method || clazy::name(method) != "operator[]")
+ return;
+
+ auto memberExpr = clazy::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, operatorExpr);
+ CXXMethodDecl *parentMemberDecl = memberExpr ? memberExpr->getMethodDecl() : nullptr;
+ if (parentMemberDecl && !parentMemberDecl->isConst()) {
+ // Don't warn for s.m_listOfValues[0].nonConstMethod();
+ // However do warn for: s.m_listOfPointers[0]->nonConstMethod(); because it compiles with .at()
+ QualType qt = operatorExpr->getType();
+ const Type *t = qt.getTypePtrOrNull();
+ if (t && !t->isPointerType())
+ return;
+ }
+
+ memberDecl = Utils::valueDeclForOperatorCall(operatorExpr);
+ } else {
+ method = memberCall->getMethodDecl();
+ memberDecl = Utils::valueDeclForMemberCall(memberCall);
+ }
+
+ if (!method || !memberDecl || !Utils::isMemberVariable(memberDecl) || !isDetachingMethod(method, DetachingMethodWithConstCounterPart) || method->isConst())
+ return;
+
+ // Catch cases like m_foo[0] = .. , which is fine
+
+ auto parentUnaryOp = clazy::getFirstParentOfType<UnaryOperator>(m_context->parentMap, callExpr);
+ if (parentUnaryOp) {
+ // m_foo[0]++ is OK
+ return;
+ }
+
+ auto parentOp = clazy::getFirstParentOfType<CXXOperatorCallExpr>(m_context->parentMap, clazy::parent(m_context->parentMap, callExpr));
+ if (parentOp) {
+ FunctionDecl *parentFunc = parentOp->getDirectCallee();
+ const string parentFuncName = parentFunc ? parentFunc->getNameAsString() : "";
+ if (clazy::startsWith(parentFuncName, "operator")) {
+ // m_foo[0] = ... is OK
+ return;
+ }
+ }
+
+ auto parentBinaryOp = clazy::getFirstParentOfType<BinaryOperator>(m_context->parentMap, callExpr);
+ if (parentBinaryOp && parentBinaryOp->isAssignmentOp()) {
+ // m_foo[0] += .. is OK
+ Expr *lhs = parentBinaryOp->getLHS();
+ if (callExpr == lhs || clazy::isChildOf(callExpr, lhs))
+ return;
+ }
+
+ const bool returnsNonConstIterator = clazy::endsWith(memberCall ? memberCall->getType().getAsString() : "", "::iterator");
+ if (returnsNonConstIterator) {
+ // If we're calling begin()/end() as arguments to a function taking non-const iterators it's fine
+ // Such as qSort(list.begin(), list.end());
+ auto parentCall = clazy::getFirstParentOfType<CallExpr>(m_context->parentMap, clazy::parent(m_context->parentMap, memberCall));
+ FunctionDecl *parentFunc = parentCall ? parentCall->getDirectCallee() : nullptr;
+ if (parentFunc && parentFunc->getNumParams() == parentCall->getNumArgs()) {
+ int i = 0;
+ for (auto argExpr : parentCall->arguments()) {
+ if (CXXMemberCallExpr *expr2 = clazy::getFirstChildOfType<CXXMemberCallExpr>(argExpr)) {
+ if (expr2 == memberCall) {
+ // Success, we found which arg
+ ParmVarDecl *parm = parentFunc->getParamDecl(i);
+ if (parm->getType().getAsString() == memberCall->getType().getAsString()) {
+ return;
+ } else {
+ break;
+ }
+ }
+ }
+ ++i;
+ }
+ }
+ }
+
+ emitWarning(getLocStart(stm), "Potential detachment due to calling " + method->getQualifiedNameAsString() + "()");
+}