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 }