File indexing completed on 2024-05-12 05:40:58
0001 /* 0002 SPDX-FileCopyrightText: 2016 Sergio Martins <smartins@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "incorrect-emit.h" 0008 #include "AccessSpecifierManager.h" 0009 #include "ClazyContext.h" 0010 #include "HierarchyUtils.h" 0011 #include "Utils.h" 0012 0013 #include <clang/AST/DeclCXX.h> 0014 #include <clang/AST/Expr.h> 0015 #include <clang/AST/ExprCXX.h> 0016 #include <clang/AST/Stmt.h> 0017 #include <clang/Basic/IdentifierTable.h> 0018 #include <clang/Basic/LLVM.h> 0019 #include <clang/Basic/SourceManager.h> 0020 #include <clang/Lex/Token.h> 0021 #include <llvm/ADT/StringRef.h> 0022 #include <llvm/Support/Casting.h> 0023 0024 #include <utility> 0025 0026 namespace clang 0027 { 0028 class MacroInfo; 0029 } // namespace clang 0030 0031 using namespace clang; 0032 0033 IncorrectEmit::IncorrectEmit(const std::string &name, ClazyContext *context) 0034 : CheckBase(name, context, Option_CanIgnoreIncludes) 0035 { 0036 context->enableAccessSpecifierManager(); 0037 enablePreProcessorCallbacks(); 0038 m_emitLocations.reserve(30); // bootstrap it 0039 m_filesToIgnore = {"moc_", ".moc"}; 0040 } 0041 0042 void IncorrectEmit::VisitMacroExpands(const Token &MacroNameTok, const SourceRange &range, const MacroInfo *) 0043 { 0044 IdentifierInfo *ii = MacroNameTok.getIdentifierInfo(); 0045 if (ii && (ii->getName() == "emit" || ii->getName() == "Q_EMIT")) { 0046 m_emitLocations.push_back(range.getBegin()); 0047 } 0048 } 0049 0050 void IncorrectEmit::VisitStmt(Stmt *stmt) 0051 { 0052 auto *methodCall = dyn_cast<CXXMemberCallExpr>(stmt); 0053 if (!methodCall || !methodCall->getCalleeDecl()) { 0054 return; 0055 } 0056 0057 AccessSpecifierManager *accessSpecifierManager = m_context->accessSpecifierManager; 0058 auto *method = dyn_cast<CXXMethodDecl>(methodCall->getCalleeDecl()); 0059 if (!method || !accessSpecifierManager) { 0060 return; 0061 } 0062 0063 if (shouldIgnoreFile(stmt->getBeginLoc())) { 0064 return; 0065 } 0066 0067 if (Stmt *parent = clazy::parent(m_context->parentMap, methodCall)) { 0068 // Check if we're inside a chained call, such as: emit d_func()->mySignal() 0069 // We're not interested in the d_func() call, so skip it 0070 if (clazy::getFirstParentOfType<CXXMemberCallExpr>(m_context->parentMap, parent)) { 0071 return; 0072 } 0073 } 0074 0075 const QtAccessSpecifierType type = accessSpecifierManager->qtAccessSpecifierType(method); 0076 if (type == QtAccessSpecifier_Unknown) { 0077 return; 0078 } 0079 0080 const bool hasEmit = hasEmitKeyboard(methodCall); 0081 const std::string methodName = method->getQualifiedNameAsString(); 0082 const bool isSignal = type == QtAccessSpecifier_Signal; 0083 if (isSignal && !hasEmit) { 0084 emitWarning(stmt, "Missing emit keyword on signal call " + methodName); 0085 } else if (!isSignal && hasEmit) { 0086 emitWarning(stmt, "Emit keyword being used with non-signal " + methodName); 0087 } 0088 0089 if (isSignal) { 0090 checkCallSignalInsideCTOR(methodCall); 0091 } 0092 } 0093 0094 void IncorrectEmit::checkCallSignalInsideCTOR(CXXMemberCallExpr *callExpr) 0095 { 0096 if (!m_context->lastMethodDecl) { 0097 return; 0098 } 0099 0100 auto *ctorDecl = dyn_cast<CXXConstructorDecl>(m_context->lastMethodDecl); 0101 if (!ctorDecl) { 0102 return; 0103 } 0104 0105 Expr *implicitArg = callExpr->getImplicitObjectArgument(); 0106 if (!implicitArg || !isa<CXXThisExpr>(implicitArg)) { // emit other->sig() is ok 0107 return; 0108 } 0109 0110 if (clazy::getFirstParentOfType<LambdaExpr>(m_context->parentMap, callExpr) != nullptr) { 0111 return; // Emit is inside a lambda, it's fine 0112 } 0113 0114 emitWarning(callExpr->getBeginLoc(), "Emitting inside constructor probably has no effect"); 0115 } 0116 0117 bool IncorrectEmit::hasEmitKeyboard(CXXMemberCallExpr *call) const 0118 { 0119 SourceLocation callLoc = call->getBeginLoc(); 0120 if (callLoc.isMacroID()) { 0121 callLoc = sm().getFileLoc(callLoc); 0122 } 0123 0124 for (const SourceLocation &emitLoc : m_emitLocations) { 0125 // We cache the calculation of the next token because it uses the Lexer and hence expensive. 0126 auto it = m_locationCache.find(emitLoc.getRawEncoding()); 0127 0128 SourceLocation nextTokenLoc; 0129 if (it == m_locationCache.end()) { 0130 nextTokenLoc = Utils::locForNextToken(emitLoc, sm(), lo()); 0131 m_locationCache[emitLoc.getRawEncoding()] = nextTokenLoc; 0132 } else { 0133 nextTokenLoc = it->second; 0134 } 0135 0136 if (nextTokenLoc == callLoc) { 0137 return true; 0138 } 0139 } 0140 0141 return false; 0142 }