File indexing completed on 2025-02-16 05:11:19

0001 /*
0002     SPDX-FileCopyrightText: 2015 Klarälvdalens Datakonsult AB a KDAB Group company info@kdab.com
0003     SPDX-FileContributor: SĂ©rgio Martins <sergio.martins@kdab.com>
0004 
0005     SPDX-FileCopyrightText: 2015 Sergio Martins <smartins@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "qgetenv.h"
0011 #include "FixItUtils.h"
0012 #include "StringUtils.h"
0013 #include "Utils.h"
0014 
0015 #include <clang/AST/Decl.h>
0016 #include <clang/AST/DeclCXX.h>
0017 #include <clang/AST/Expr.h>
0018 #include <clang/AST/ExprCXX.h>
0019 #include <clang/AST/Stmt.h>
0020 #include <clang/Basic/Diagnostic.h>
0021 #include <clang/Basic/LLVM.h>
0022 #include <llvm/ADT/StringRef.h>
0023 #include <llvm/Support/Casting.h>
0024 
0025 #include <vector>
0026 
0027 class ClazyContext;
0028 
0029 using namespace clang;
0030 
0031 QGetEnv::QGetEnv(const std::string &name, ClazyContext *context)
0032     : CheckBase(name, context, Option_CanIgnoreIncludes)
0033 {
0034 }
0035 
0036 void QGetEnv::VisitStmt(clang::Stmt *stmt)
0037 {
0038     // Lets check only in function calls. Otherwise there are too many false positives, it's common
0039     // to implicit cast to bool when checking pointers for validity, like if (ptr)
0040 
0041     auto *memberCall = dyn_cast<CXXMemberCallExpr>(stmt);
0042     if (!memberCall) {
0043         return;
0044     }
0045 
0046     CXXMethodDecl *method = memberCall->getMethodDecl();
0047     if (!method) {
0048         return;
0049     }
0050 
0051     if (CXXRecordDecl *record = method->getParent(); !record || clazy::name(record) != "QByteArray") {
0052         return;
0053     }
0054 
0055     std::vector<CallExpr *> calls = Utils::callListForChain(memberCall);
0056     if (calls.size() != 2) {
0057         return;
0058     }
0059 
0060     CallExpr *qgetEnvCall = calls.back();
0061     if (FunctionDecl *func = qgetEnvCall->getDirectCallee(); !func || clazy::name(func) != "qgetenv") {
0062         return;
0063     }
0064 
0065     StringRef methodname = clazy::name(method);
0066     std::string errorMsg;
0067     std::string replacement;
0068     bool shouldIncludeOkParameter = false;
0069     bool changesToBaseAutodetection = false;
0070     if (methodname == "isEmpty") {
0071         errorMsg = "qgetenv().isEmpty() allocates.";
0072         replacement = "qEnvironmentVariableIsEmpty";
0073     } else if (methodname == "isNull") {
0074         errorMsg = "qgetenv().isNull() allocates.";
0075         replacement = "qEnvironmentVariableIsSet";
0076     } else if (methodname == "toInt") {
0077         errorMsg = "qgetenv().toInt() is slow.";
0078         replacement = "qEnvironmentVariableIntValue";
0079         for (unsigned int i = 0; i < memberCall->getNumArgs(); ++i) {
0080             auto *arg = memberCall->getArg(i);
0081             if (i == 0 && !isa<CXXDefaultArgExpr>(arg)) {
0082                 if (!isa<CastExpr>(arg) || !isa<CXXNullPtrLiteralExpr>(dyn_cast<CastExpr>(arg)->getSubExpr())) {
0083                     shouldIncludeOkParameter = true;
0084                 }
0085             } else if (i == 1) {
0086                 if (auto *intLiteral = dyn_cast<IntegerLiteral>(arg)) {
0087                     if (intLiteral->getValue() != 0) {
0088                         return; // Custom base is specified - ignore this case
0089                     }
0090                 } else if (isa<CXXDefaultArgExpr>(arg)) {
0091                     changesToBaseAutodetection = true;
0092                 } else {
0093                     return; // If the base is neither the 0 literal or the default, skip checking it
0094                 }
0095             }
0096         }
0097     } else {
0098         return; // Some different method on QByteArray, nothing to warn about
0099     }
0100 
0101     std::string getEnvArgStr = Lexer::getSourceText(CharSourceRange::getTokenRange(qgetEnvCall->getArg(0)->getSourceRange()), sm(), lo()).str();
0102     if (shouldIncludeOkParameter) {
0103         getEnvArgStr += ", " + Lexer::getSourceText(CharSourceRange::getTokenRange(memberCall->getArg(0)->getSourceRange()), sm(), lo()).str();
0104     }
0105 
0106     errorMsg += " Use " + replacement + "() instead";
0107     if (changesToBaseAutodetection) {
0108         errorMsg += ". This uses internally a base of 0, supporting decimal, hex and octal values";
0109     }
0110     emitWarning(memberCall->getBeginLoc(), errorMsg, {FixItHint::CreateReplacement(stmt->getSourceRange(), replacement + "(" + getEnvArgStr + ")")});
0111 }