File indexing completed on 2024-05-12 05:40:57
0001 /* 0002 SPDX-FileCopyrightText: 2016-2017 Sergio Martins <smartins@kde.org> 0003 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "unused-non-trivial-variable.h" 0009 #include "ContextUtils.h" 0010 #include "HierarchyUtils.h" 0011 #include "QtUtils.h" 0012 #include "StringUtils.h" 0013 #include "TypeUtils.h" 0014 #include "clazy_stl.h" 0015 0016 #include <clang/AST/Decl.h> 0017 #include <clang/AST/DeclCXX.h> 0018 #include <clang/AST/Expr.h> 0019 #include <clang/AST/Stmt.h> 0020 #include <clang/AST/Type.h> 0021 #include <clang/Basic/LLVM.h> 0022 #include <clang/Basic/SourceLocation.h> 0023 #include <clang/Basic/SourceManager.h> 0024 #include <llvm/ADT/StringRef.h> 0025 #include <llvm/Support/Casting.h> 0026 0027 #include <stdlib.h> 0028 #include <string> 0029 #include <vector> 0030 0031 class ClazyContext; 0032 0033 using namespace clang; 0034 0035 UnusedNonTrivialVariable::UnusedNonTrivialVariable(const std::string &name, ClazyContext *context) 0036 : CheckBase(name, context, Option_CanIgnoreIncludes) 0037 { 0038 const char *user_blacklist = getenv("CLAZY_UNUSED_NON_TRIVIAL_VARIABLE_BLACKLIST"); 0039 const char *user_whitelist = getenv("CLAZY_UNUSED_NON_TRIVIAL_VARIABLE_WHITELIST"); 0040 0041 if (user_blacklist) { 0042 m_userBlacklist = clazy::splitString(user_blacklist, ','); 0043 } 0044 0045 if (user_whitelist) { 0046 m_userWhitelist = clazy::splitString(user_whitelist, ','); 0047 } 0048 } 0049 0050 void UnusedNonTrivialVariable::VisitStmt(clang::Stmt *stmt) 0051 { 0052 auto *declStmt = dyn_cast<DeclStmt>(stmt); 0053 if (!declStmt) { 0054 return; 0055 } 0056 0057 for (auto *decl : declStmt->decls()) { 0058 handleVarDecl(dyn_cast<VarDecl>(decl)); 0059 } 0060 } 0061 0062 bool UnusedNonTrivialVariable::isUninterestingType(const CXXRecordDecl *record) const 0063 { 0064 static const std::vector<StringRef> blacklist = { 0065 "QMutexLocker", 0066 "QDebugStateSaver", 0067 "QTextBlockFormat", 0068 "QWriteLocker", 0069 "QSignalBlocker", 0070 "QReadLocker", 0071 "PRNGLocker", 0072 "QDBusWriteLocker", 0073 "QDBusBlockingCallWatcher", 0074 "QBoolBlocker", 0075 "QOrderedMutexLocker", 0076 "QTextLine", 0077 "QScopedScopeLevelCounter", 0078 }; 0079 0080 // Check some obvious candidates first 0081 StringRef typeName = clazy::name(record); 0082 bool any = clazy::any_of(blacklist, [typeName](StringRef container) { 0083 return container == typeName; 0084 }); 0085 0086 if (any) { 0087 return true; 0088 } 0089 0090 static const std::vector<StringRef> blacklistedTemplates = { 0091 "QScopedPointer", 0092 "QSetValueOnDestroy", 0093 "QScopedValueRollback", 0094 "QScopeGuard", 0095 }; 0096 StringRef className = clazy::name(record); 0097 for (StringRef templateName : blacklistedTemplates) { 0098 if (clazy::startsWith(static_cast<std::string>(className), static_cast<std::string>(templateName))) { 0099 return true; 0100 } 0101 } 0102 0103 // Now check the user's blacklist, set by env-variable 0104 any = clazy::any_of(m_userBlacklist, [typeName](const std::string &container) { 0105 return container == typeName; 0106 }); 0107 0108 return any; 0109 } 0110 0111 bool UnusedNonTrivialVariable::isInterestingType(QualType t) const 0112 { 0113 // TODO Remove QColor in Qt6 0114 static const std::vector<StringRef> nonTrivialTypes = { 0115 "QColor", 0116 "QVariant", 0117 "QFont", 0118 "QUrl", 0119 "QIcon", 0120 "QImage", 0121 "QPixmap", 0122 "QPicture", 0123 "QBitmap", 0124 "QBrush", 0125 "QPen", 0126 "QBuffer", 0127 "QCache", 0128 "QDateTime", 0129 "QDir", 0130 "QEvent", 0131 "QFileInfo", 0132 "QFontInfo", 0133 "QFontMetrics", 0134 "QJSValue", 0135 "QLocale", 0136 "QRegularExpression", 0137 "QRegExp", 0138 "QUrlQuery", 0139 "QStorageInfo", 0140 "QPersistentModelIndex", 0141 "QJsonArray", 0142 "QJsonValue", 0143 "QJsonDocument", 0144 "QMimeType", 0145 "QBitArray", 0146 "QCollator", 0147 "QByteArrayList", 0148 "QCollatorSortKey", 0149 "QCursor", 0150 "QPalette", 0151 "QPainterPath", 0152 "QRegion", 0153 "QFontInfo", 0154 "QTextCursor", 0155 "QStaticText", 0156 "QFontMetricsF", 0157 "QTextFrameFormat", 0158 "QTextImageFormat", 0159 "QNetworkCookie", 0160 "QNetworkRequest", 0161 "QNetworkConfiguration", 0162 "QHostAddress", 0163 "QSqlQuery", 0164 "QSqlRecord", 0165 "QSqlField", 0166 "QLine", 0167 "QLineF", 0168 "QRect", 0169 "QRectF", 0170 "QDomNode", 0171 }; 0172 0173 CXXRecordDecl *record = clazy::typeAsRecord(t); 0174 if (!record) { 0175 return false; 0176 } 0177 0178 if (isOptionSet("no-whitelist")) { 0179 // Will cause too many false-positives, like RAII classes. Use suppressing comments to silence them. 0180 return !isUninterestingType(record); 0181 } 0182 0183 if (clazy::isQtContainer(record)) { 0184 return true; 0185 } 0186 0187 StringRef typeName = clazy::name(record); 0188 bool any = clazy::any_of(nonTrivialTypes, [typeName](StringRef container) { 0189 return container == typeName; 0190 }); 0191 0192 if (any) { 0193 return true; 0194 } 0195 0196 return clazy::any_of(m_userWhitelist, [typeName](const std::string &container) { 0197 return container == typeName; 0198 }); 0199 } 0200 0201 void UnusedNonTrivialVariable::handleVarDecl(VarDecl *varDecl) 0202 { 0203 if (!varDecl || !isInterestingType(varDecl->getType())) { 0204 return; 0205 } 0206 0207 auto *currentFunc = clazy::firstContextOfType<FunctionDecl>(varDecl->getDeclContext()); 0208 Stmt *body = currentFunc ? currentFunc->getBody() : nullptr; 0209 if (!body) { 0210 return; 0211 } 0212 0213 SourceLocation locStart = varDecl->getBeginLoc(); 0214 locStart = sm().getExpansionLoc(locStart); 0215 auto declRefs = clazy::getStatements<DeclRefExpr>(body, &sm(), locStart); 0216 0217 auto pred = [varDecl](DeclRefExpr *declRef) { 0218 return declRef->getDecl() == varDecl; 0219 }; 0220 0221 if (!clazy::any_of(declRefs, pred)) { 0222 emitWarning(locStart, "unused " + clazy::simpleTypeName(varDecl->getType(), lo())); 0223 } 0224 }