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 }