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 }