aboutsummaryrefslogtreecommitdiffstats
path: root/src/checks/level0/qstring-arg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/checks/level0/qstring-arg.cpp')
-rw-r--r--src/checks/level0/qstring-arg.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/src/checks/level0/qstring-arg.cpp b/src/checks/level0/qstring-arg.cpp
new file mode 100644
index 00000000..e66e0eb4
--- /dev/null
+++ b/src/checks/level0/qstring-arg.cpp
@@ -0,0 +1,181 @@
+/*
+ This file is part of the clazy static checker.
+
+ 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 "qstring-arg.h"
+#include "Utils.h"
+#include "StringUtils.h"
+#include "HierarchyUtils.h"
+
+#include <clang/AST/AST.h>
+#include <vector>
+
+using namespace clang;
+using namespace std;
+
+QStringArg::QStringArg(const std::string &name, ClazyContext *context)
+ : CheckBase(name, context, Option_CanIgnoreIncludes)
+{
+ m_filesToIgnore = { "qstring.h" };
+}
+
+static string variableNameFromArg(Expr *arg)
+{
+ vector<DeclRefExpr*> declRefs;
+ clazy::getChilds<DeclRefExpr>(arg, declRefs);
+ if (declRefs.size() == 1) {
+ ValueDecl *decl = declRefs.at(0)->getDecl();
+ return decl ? decl->getNameAsString() : string();
+ }
+
+ return {};
+}
+
+static CXXMethodDecl* isArgMethod(FunctionDecl *func)
+{
+ if (!func)
+ return nullptr;
+
+ auto method = dyn_cast<CXXMethodDecl>(func);
+ if (!method || clazy::name(method) != "arg")
+ return nullptr;
+
+ CXXRecordDecl *record = method->getParent();
+ if (!record || clazy::name(record) != "QString")
+ return nullptr;
+
+ return method;
+}
+
+static bool isArgFuncWithOnlyQString(CallExpr *callExpr)
+{
+ if (!callExpr)
+ return false;
+
+ CXXMethodDecl *method = isArgMethod(callExpr->getDirectCallee());
+ if (!method)
+ return false;
+
+ ParmVarDecl *secondParam = method->getParamDecl(1);
+ if (clazy::classNameFor(secondParam) == "QString")
+ return true;
+
+ ParmVarDecl *firstParam = method->getParamDecl(0);
+ if (clazy::classNameFor(firstParam) != "QString")
+ return false;
+
+ // This is a arg(QString, int, QChar) call, it's good if the second parameter is a default param
+ return isa<CXXDefaultArgExpr>(callExpr->getArg(1));
+}
+
+bool QStringArg::checkMultiArgWarningCase(const vector<clang::CallExpr *> &calls)
+{
+ const int size = calls.size();
+ for (int i = 1; i < size; ++i) {
+ auto call = calls.at(i);
+ if (calls.at(i - 1)->getNumArgs() + call->getNumArgs() <= 9) {
+ emitWarning(getLocEnd(call), "Use multi-arg instead");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QStringArg::checkForMultiArgOpportunities(CXXMemberCallExpr *memberCall)
+{
+ if (!isArgFuncWithOnlyQString(memberCall))
+ return;
+
+ if (getLocStart(memberCall).isMacroID()) {
+ auto macroName = Lexer::getImmediateMacroName(getLocStart(memberCall), sm(), lo());
+ if (macroName == "QT_REQUIRE_VERSION") // bug #391851
+ return;
+ }
+
+ vector<clang::CallExpr *> callExprs = Utils::callListForChain(memberCall);
+ vector<clang::CallExpr *> argCalls;
+ for (auto call : callExprs) {
+ if (!clazy::contains(m_alreadyProcessedChainedCalls, call) && isArgFuncWithOnlyQString(call)) {
+ argCalls.push_back(call);
+ m_alreadyProcessedChainedCalls.push_back(call);
+ } else {
+ if (checkMultiArgWarningCase(argCalls))
+ return;
+ argCalls.clear();
+ }
+ }
+
+ checkMultiArgWarningCase(argCalls);
+}
+
+void QStringArg::VisitStmt(clang::Stmt *stmt)
+{
+ auto memberCall = dyn_cast<CXXMemberCallExpr>(stmt);
+ if (!memberCall)
+ return;
+
+ if (shouldIgnoreFile(getLocStart(stmt)))
+ return;
+
+ checkForMultiArgOpportunities(memberCall);
+
+ if (!isOptionSet("fillChar-overloads"))
+ return;
+
+ CXXMethodDecl *method = isArgMethod(memberCall->getDirectCallee());
+ if (!method)
+ return;
+
+ if (clazy::simpleArgTypeName(method, method->getNumParams() - 1, lo()) == "QChar") {
+ // The second arg wasn't passed, so this is a safe and unambiguous use, like .arg(1)
+ if (isa<CXXDefaultArgExpr>(memberCall->getArg(1)))
+ return;
+
+ ParmVarDecl *p = method->getParamDecl(2);
+ if (p && clazy::name(p) == "base") {
+ // User went through the trouble specifying a base, lets allow it if it's a literal.
+ vector<IntegerLiteral*> literals;
+ clazy::getChilds<IntegerLiteral>(memberCall->getArg(2), literals);
+ if (!literals.empty())
+ return;
+
+ string variableName = clazy::toLower(variableNameFromArg(memberCall->getArg(2)));
+ if (clazy::contains(variableName, "base"))
+ return;
+ }
+
+ p = method->getParamDecl(1);
+ if (p && clazy::name(p) == "fieldWidth") {
+ // He specified a literal, so he knows what he's doing, otherwise he would have put it directly in the string
+ vector<IntegerLiteral*> literals;
+ clazy::getChilds<IntegerLiteral>(memberCall->getArg(1), literals);
+ if (!literals.empty())
+ return;
+
+ // the variable is named "width", user knows what he's doing
+ string variableName = clazy::toLower(variableNameFromArg(memberCall->getArg(1)));
+ if (clazy::contains(variableName, "width"))
+ return;
+ }
+
+ emitWarning(getLocStart(stmt), "Using QString::arg() with fillChar overload");
+ }
+}