File indexing completed on 2024-11-24 05:05:12

0001 /*
0002     SPDX-FileCopyrightText: 2018 Sergio Martins <smartins@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "wrong-qevent-cast.h"
0008 #include "ClazyContext.h"
0009 #include "HierarchyUtils.h"
0010 #include "StringUtils.h"
0011 #include "TypeUtils.h"
0012 #include "Utils.h"
0013 #include "clazy_stl.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/ParentMap.h>
0020 #include <clang/AST/Stmt.h>
0021 #include <clang/AST/Type.h>
0022 #include <clang/Basic/LLVM.h>
0023 #include <llvm/ADT/APSInt.h>
0024 #include <llvm/ADT/StringRef.h>
0025 #include <llvm/Support/Casting.h>
0026 
0027 #include <unordered_map>
0028 #include <utility>
0029 #include <vector>
0030 
0031 using namespace clang;
0032 
0033 using ClassNameList = std::vector<StringRef>;
0034 
0035 enum QtUnregularlyNamedEventTypes {
0036     DragEnter = 60,
0037     DragLeave = 62,
0038     OrientationChange = 208,
0039     ActionAdded = 114,
0040     ActionRemoved = 115,
0041     ActionChanged = 99,
0042     ChildAdded = 68,
0043     ChildRemoved = 71,
0044     ChildPolished = 69,
0045     MouseButtonPress = 2,
0046     MouseButtonRelease = 3,
0047     MouseButtonDblClick = 4,
0048     MouseMove = 5,
0049     NonClientAreaMouseMove = 173,
0050     NonClientAreaMouseButtonPress = 174,
0051     NonClientAreaMouseButtonRelease = 175,
0052     NonClientAreaMouseButtonDblClick = 176,
0053     FocusIn = 8,
0054     FocusOut = 9,
0055     FocusAboutToChange = 23,
0056     Gesture = 198,
0057     GestureOverride = 202,
0058     HoverEnter = 127,
0059     HoverLeave = 128,
0060     HoverMove = 129,
0061     TabletEnterProximity = 171,
0062     TabletLeaveProximity = 172,
0063     TabletPress = 92,
0064     TabletMove = 87,
0065     TabletRelease = 93,
0066     ToolTip = 110,
0067     Wheel = 31,
0068     KeyPress = 6,
0069     KeyRelease = 7,
0070     ShortcutOverride = 51,
0071     DragMove = 61,
0072     GraphicsSceneMouseMove = 155,
0073     GraphicsSceneMousePress = 156,
0074     GraphicsSceneMouseRelease = 157,
0075     GraphicsSceneMouseDoubleClick = 158,
0076     GraphicsSceneContextMenu = 159,
0077     GraphicsSceneHoverEnter = 160,
0078     GraphicsSceneHoverMove = 161,
0079     GraphicsSceneHoverLeave = 162,
0080     GraphicsSceneHelp = 163,
0081     GraphicsSceneDragEnter = 164,
0082     GraphicsSceneDragMove = 165,
0083     GraphicsSceneDragLeave = 166,
0084     GraphicsSceneDrop = 167,
0085     GraphicsSceneWheel = 168,
0086     GraphicsSceneResize = 181,
0087     TouchBegin = 194,
0088     TouchEnd = 196,
0089     TouchCancel = 209,
0090     TouchUpdate = 195,
0091     NativeGesture = 197,
0092     MetaCall = 43,
0093     WhatsThis = 111,
0094     ContextMenu = 82,
0095     QueryWhatsThis = 123
0096     // StatusTip = 112 not irregular, but qtbase casts it to QHelpEvent for some reason, needs investigation
0097 };
0098 
0099 WrongQEventCast::WrongQEventCast(const std::string &name, ClazyContext *context)
0100     : CheckBase(name, context)
0101 {
0102 }
0103 
0104 static bool eventTypeMatchesClass(QtUnregularlyNamedEventTypes eventType, const std::string &eventTypeStr, StringRef className)
0105 {
0106     // In the simplest case, the class is "Q" + eventType + "Event"
0107     std::string expectedClassName = std::string("Q") + eventTypeStr + std::string("Event");
0108     if (expectedClassName == className) {
0109         return true;
0110     }
0111 
0112     // Otherwise it's unregular and we need a map:
0113 
0114     static std::unordered_map<QtUnregularlyNamedEventTypes, ClassNameList, std::hash<int>> map = {
0115         {ActionAdded, {"QActionEvent"}},
0116         {ActionRemoved, {"QActionEvent"}},
0117         {ActionChanged, {"QActionEvent"}},
0118         {ChildAdded, {"QChildEvent"}},
0119         {ChildRemoved, {"QChildEvent"}},
0120         {ChildPolished, {"QChildEvent"}},
0121         {MetaCall, {"QDBusSpyCallEvent", "QDBusCallDeliveryEvent"}},
0122         {DragEnter, {"QDragEnterEvent", "QDragMoveEvent", "QDropEvent"}},
0123         {DragLeave, {"QDragLeaveEvent", "QDragMoveEvent", "QDropEvent"}},
0124         {DragMove, {"QDragMoveEvent", "QDropEvent"}},
0125         {FocusIn, {"QFocusEvent"}},
0126         {FocusOut, {"QFocusEvent"}},
0127         {FocusAboutToChange, {"QFocusEvent"}},
0128         {Gesture, {"QGestureEvent"}},
0129         {GestureOverride, {"QGestureEvent"}},
0130         {GraphicsSceneContextMenu, {"QGraphicsSceneEvent"}},
0131         {GraphicsSceneHoverEnter, {"QGraphicsSceneHoverEvent", "QGraphicsSceneEvent"}},
0132         {GraphicsSceneHoverMove, {"QGraphicsSceneHoverEvent", "QGraphicsSceneEvent"}},
0133         {GraphicsSceneHoverLeave, {"QGraphicsSceneHoverEvent", "QGraphicsSceneEvent"}},
0134         {GraphicsSceneHelp, {"QGraphicsSceneEvent"}},
0135         {GraphicsSceneDragEnter, {"QGraphicsSceneDragDropEvent", "QGraphicsSceneEvent"}},
0136         {GraphicsSceneDragMove, {"QGraphicsSceneDragDropEvent", "QGraphicsSceneEvent"}},
0137         {GraphicsSceneDragLeave, {"QGraphicsSceneDragDropEvent", "QGraphicsSceneEvent"}},
0138         {GraphicsSceneDrop, {"QGraphicsSceneDragDropEvent", "QGraphicsSceneEvent"}},
0139         {GraphicsSceneWheel, {"QGraphicsSceneEvent"}},
0140         {GraphicsSceneResize, {"QGraphicsSceneEvent"}},
0141         {GraphicsSceneMouseMove, {"QGraphicsSceneMouseEvent"}},
0142         {GraphicsSceneMousePress, {"QGraphicsSceneMouseEvent"}},
0143         {GraphicsSceneMouseRelease, {"QGraphicsSceneMouseEvent"}},
0144         {GraphicsSceneMouseDoubleClick, {"QGraphicsSceneMouseEvent"}},
0145         //{ StatusTip, {"QStatusTipEvent" } },
0146         {ToolTip, {"QHelpEvent"}},
0147         {WhatsThis, {"QHelpEvent"}},
0148         {QueryWhatsThis, {"QHelpEvent"}},
0149         {HoverEnter, {"QHoverEvent", "QInputEvent"}},
0150         {HoverLeave, {"QHoverEvent", "QInputEvent"}},
0151         {HoverMove, {"QHoverEvent", "QInputEvent"}},
0152         {KeyPress, {"QKeyEvent", "QInputEvent"}},
0153         {KeyRelease, {"QKeyEvent", "QInputEvent"}},
0154         {ShortcutOverride, {"QKeyEvent", "QInputEvent"}},
0155         {MouseButtonPress, {"QMouseEvent"}},
0156         {MouseButtonRelease, {"QMouseEvent"}},
0157         {MouseButtonDblClick, {"QMouseEvent"}},
0158         {MouseMove, {"QMouseEvent"}},
0159         {NonClientAreaMouseMove, {"QMouseEvent"}},
0160         {NonClientAreaMouseButtonPress, {"QMouseEvent"}},
0161         {NonClientAreaMouseButtonRelease, {"QMouseEvent"}},
0162         {NonClientAreaMouseButtonRelease, {"QMouseEvent"}},
0163         {NonClientAreaMouseButtonDblClick, {"QMouseEvent"}},
0164         {NativeGesture, {"QInputEvent"}},
0165         {OrientationChange, {"QScreenOrientationChangeEvent"}},
0166         {TabletEnterProximity, {"QTabletEvent", "QInputEvent"}},
0167         {TabletLeaveProximity, {"QTabletEvent", "QInputEvent"}},
0168         {TabletPress, {"QTabletEvent", "QInputEvent"}},
0169         {TabletMove, {"QTabletEvent", "QInputEvent"}},
0170         {TabletRelease, {"QTabletEvent", "QInputEvent"}},
0171         {TouchBegin, {"QTouchEvent", "QInputEvent"}},
0172         {TouchCancel, {"QTouchEvent", "QInputEvent"}},
0173         {TouchEnd, {"QTouchEvent", "QInputEvent"}},
0174         {TouchUpdate, {"QTouchEvent", "QInputEvent"}},
0175         {Wheel, {"QInputEvent"}},
0176         {ContextMenu, {"QInputEvent"}}};
0177 
0178     auto it = map.find(eventType);
0179     if (it == map.cend()) {
0180         return false;
0181     }
0182 
0183     const ClassNameList &classes = it->second;
0184     const bool found = clazy::find(classes, className) != classes.cend();
0185 
0186     return found;
0187 }
0188 
0189 // TODO: Use iterators
0190 CaseStmt *getCaseStatement(clang::ParentMap *pmap, Stmt *stmt, DeclRefExpr *event)
0191 {
0192     Stmt *s = pmap->getParent(stmt);
0193 
0194     while (s) {
0195         if (auto *ifStmt = dyn_cast<IfStmt>(s)) {
0196             // if there's we're inside an if statement then skip, to avoid false-positives
0197             auto *declRef = clazy::getFirstChildOfType2<DeclRefExpr>(ifStmt->getCond());
0198             if (declRef && declRef->getDecl() == event->getDecl()) {
0199                 return nullptr;
0200             }
0201         }
0202 
0203         if (auto *caseStmt = dyn_cast<CaseStmt>(s)) {
0204             auto *switchStmt = clazy::getSwitchFromCase(pmap, caseStmt);
0205             if (switchStmt) {
0206                 auto *declRef = clazy::getFirstChildOfType2<DeclRefExpr>(switchStmt->getCond());
0207                 // Does this switch refer to the same QEvent ?
0208                 if (declRef && declRef->getDecl() == event->getDecl()) {
0209                     return caseStmt;
0210                 }
0211             }
0212         }
0213 
0214         s = pmap->getParent(s);
0215     }
0216 
0217     return nullptr;
0218 }
0219 
0220 void WrongQEventCast::VisitStmt(clang::Stmt *stmt)
0221 {
0222     auto *cast = dyn_cast<CXXStaticCastExpr>(stmt);
0223     if (!cast) {
0224         return;
0225     }
0226 
0227     Expr *e = cast->getSubExpr();
0228 
0229     QualType t = e ? e->getType() : QualType();
0230     QualType pointeeType = t.isNull() ? QualType() : clazy::pointeeQualType(t);
0231     CXXRecordDecl *rec = pointeeType.isNull() ? nullptr : pointeeType->getAsCXXRecordDecl();
0232 
0233     if (!rec || clazy::name(rec) != "QEvent") {
0234         return;
0235     }
0236 
0237     CXXRecordDecl *castTo = Utils::namedCastOuterDecl(cast);
0238     if (!castTo) {
0239         return;
0240     }
0241 
0242     auto *declref = clazy::getFirstChildOfType2<DeclRefExpr>(cast->getSubExpr());
0243     if (!declref) {
0244         return;
0245     }
0246 
0247     auto *caseStmt = getCaseStatement(m_context->parentMap, stmt, declref);
0248     if (!caseStmt) {
0249         return;
0250     }
0251 
0252     auto *caseValue = clazy::getFirstChildOfType2<DeclRefExpr>(caseStmt->getLHS());
0253     if (!caseValue) {
0254         return;
0255     }
0256 
0257     auto *enumeratorDecl = dyn_cast<EnumConstantDecl>(caseValue->getDecl());
0258     if (!enumeratorDecl) {
0259         return;
0260     }
0261 
0262     auto enumeratorVal = static_cast<QtUnregularlyNamedEventTypes>(enumeratorDecl->getInitVal().getExtValue());
0263 
0264     std::string eventTypeStr = enumeratorDecl->getNameAsString();
0265     StringRef castToName = clazy::name(castTo);
0266 
0267     if (eventTypeMatchesClass(enumeratorVal, eventTypeStr, castToName)) {
0268         return;
0269     }
0270 
0271     emitWarning(stmt, std::string("Cast from a QEvent::") + eventTypeStr + " event to " + std::string(castToName) + " looks suspicious.");
0272 }