File indexing completed on 2024-05-12 05:41:04
0001 /* 0002 SPDX-FileCopyrightText: 2020 Jesper K. Pedersen <jesper.pedersen@kdab.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "use-chrono-in-qtimer.h" 0008 #include "ClazyContext.h" 0009 #include "FixItUtils.h" 0010 #include "HierarchyUtils.h" 0011 #include "PreProcessorVisitor.h" 0012 #include <clang/AST/AST.h> 0013 0014 using namespace clang; 0015 0016 UseChronoInQTimer::UseChronoInQTimer(const std::string &name, ClazyContext *context) 0017 : CheckBase(name, context, Option_CanIgnoreIncludes) 0018 { 0019 context->enablePreprocessorVisitor(); 0020 } 0021 0022 static int unpackValue(clang::Expr *expr) 0023 { 0024 auto *value = dyn_cast<IntegerLiteral>(expr); 0025 if (value) { 0026 return static_cast<int>(*value->getValue().getRawData()); 0027 } 0028 0029 auto *binaryOp = dyn_cast<BinaryOperator>(expr); 0030 if (!binaryOp) { 0031 return -1; 0032 } 0033 0034 int left = unpackValue(binaryOp->getLHS()); 0035 int right = unpackValue(binaryOp->getRHS()); 0036 if (left == -1 || right == -1) { 0037 return -1; 0038 } 0039 0040 if (binaryOp->getOpcode() == BO_Mul) { 0041 return left * right; 0042 } 0043 0044 if (binaryOp->getOpcode() == BO_Div) { 0045 return left / right; 0046 } 0047 0048 return -1; 0049 } 0050 0051 void UseChronoInQTimer::warn(const clang::Stmt *stmt, int value) 0052 { 0053 if (value == 0) { 0054 return; // ignore zero times; 0055 } 0056 0057 std::string suggestion; 0058 if (value % (1000 * 3600) == 0) { 0059 suggestion = std::to_string(value / 1000 / 3600) + "h"; 0060 } else if (value % (1000 * 60) == 0) { 0061 suggestion = std::to_string(value / 1000 / 60) + "min"; 0062 } else if (value % 1000 == 0) { 0063 suggestion = std::to_string(value / 1000) + "s"; 0064 } else { 0065 suggestion = std::to_string(value) + "ms"; 0066 } 0067 0068 std::vector<FixItHint> fixits; 0069 fixits.push_back(FixItHint::CreateReplacement(stmt->getSourceRange(), suggestion)); 0070 0071 if (!m_hasInsertedInclude && !m_context->preprocessorVisitor->hasInclude("chrono", true)) { 0072 fixits.push_back(clazy::createInsertion(m_context->preprocessorVisitor->endOfIncludeSection(), 0073 "\n" 0074 "#include <chrono>\n\n" 0075 "using namespace std::chrono_literals;")); 0076 } 0077 m_hasInsertedInclude = true; 0078 0079 emitWarning(stmt->getBeginLoc(), "make code more robust: use " + suggestion + " instead.", fixits); 0080 } 0081 0082 static std::string functionName(CallExpr *callExpr) 0083 { 0084 auto *memberCall = clazy::getFirstChildOfType<MemberExpr>(callExpr); 0085 if (memberCall) { 0086 auto *methodDecl = dyn_cast<CXXMethodDecl>(memberCall->getMemberDecl()); 0087 if (!methodDecl) { 0088 return {}; 0089 } 0090 return methodDecl->getQualifiedNameAsString(); 0091 } 0092 0093 FunctionDecl *fdecl = callExpr->getDirectCallee(); 0094 if (fdecl) { 0095 return fdecl->getQualifiedNameAsString(); 0096 } 0097 0098 return {}; 0099 } 0100 0101 void UseChronoInQTimer::VisitStmt(clang::Stmt *stmt) 0102 { 0103 auto *callExpr = dyn_cast<CallExpr>(stmt); 0104 if (!callExpr) { 0105 return; 0106 } 0107 0108 if (callExpr->getNumArgs() == 0) { 0109 return; // start() doesn't take any arguments. 0110 } 0111 0112 const std::string name = functionName(callExpr); 0113 if (name != "QTimer::setInterval" && name != "QTimer::start" && name != "QTimer::singleShot") { 0114 return; 0115 } 0116 0117 const int value = unpackValue(callExpr->getArg(0)); 0118 if (value == -1) { 0119 return; 0120 } 0121 0122 warn(callExpr->getArg(0), value); 0123 }