void QGetEnv::VisitStmt(clang::Stmt *stmt) { // Lets check only in function calls. Otherwise there are too many false positives, it's common // to implicit cast to bool when checking pointers for validity, like if (ptr) CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(stmt); if (!memberCall) return; CXXMethodDecl *method = memberCall->getMethodDecl(); if (!method) return; CXXRecordDecl *record = method->getParent(); if (!record || record->getNameAsString() != "QByteArray") { return; } std::vector<CallExpr *> calls = Utils::callListForChain(memberCall); if (calls.size() != 2) return; CallExpr *qgetEnvCall = calls.back(); FunctionDecl *func = qgetEnvCall->getDirectCallee(); if (!func || func->getNameAsString() != "qgetenv") return; string methodname = method->getNameAsString(); string errorMsg; std::string replacement; if (methodname == "isEmpty") { errorMsg = "qgetenv().isEmpty() allocates."; replacement = "qEnvironmentVariableIsEmpty"; } else if (methodname == "isNull") { errorMsg = "qgetenv().isNull() allocates."; replacement = "qEnvironmentVariableIsSet"; } else if (methodname == "toInt") { errorMsg = "qgetenv().toInt() is slow."; replacement = "qEnvironmentVariableIntValue"; } if (!errorMsg.empty()) { std::vector<FixItHint> fixits; if (isFixitEnabled(FixitAll)) { const bool success = FixItUtils::transformTwoCallsIntoOne(m_ci, qgetEnvCall, memberCall, replacement, fixits); if (!success) { queueManualFixitWarning(memberCall->getLocStart(), FixitAll); } } errorMsg += " Use " + replacement + "() instead"; emitWarning(memberCall->getLocStart(), errorMsg.c_str(), fixits); } }
// Catches cases like: s.append(s2.mid(1, 1)); bool StringRefCandidates::processCase2(CallExpr *call) { CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(call); CXXOperatorCallExpr *operatorCall = dyn_cast<CXXOperatorCallExpr>(call); CXXMethodDecl *method = nullptr; if (memberCall) { method = memberCall->getMethodDecl(); } else if (operatorCall && operatorCall->getCalleeDecl()) { Decl *decl = operatorCall->getCalleeDecl(); method = dyn_cast<CXXMethodDecl>(decl); } if (!isMethodReceivingQStringRef(method)) return false; Expr *firstArgument = call->getNumArgs() > 0 ? call->getArg(0) : nullptr; MaterializeTemporaryExpr *temp = firstArgument ? dyn_cast<MaterializeTemporaryExpr>(firstArgument) : nullptr; if (!temp) { Expr *secondArgument = call->getNumArgs() > 1 ? call->getArg(1) : nullptr; temp = secondArgument ? dyn_cast<MaterializeTemporaryExpr>(secondArgument) : nullptr; if (!temp) // For the CXXOperatorCallExpr it's in the second argument return false; } CallExpr *innerCall = HierarchyUtils::getFirstChildOfType2<CallExpr>(temp); CXXMemberCallExpr *innerMemberCall = innerCall ? dyn_cast<CXXMemberCallExpr>(innerCall) : nullptr; if (!innerMemberCall) return false; CXXMethodDecl *innerMethod = innerMemberCall->getMethodDecl(); if (!isInterestingFirstMethod(innerMethod)) return false; std::vector<FixItHint> fixits; if (isFixitEnabled(FixitUseQStringRef)) { fixits = fixit(innerMemberCall); } emitWarning(call->getLocStart(), "Use " + innerMethod->getNameAsString() + "Ref() instead", fixits); return true; }
// Catch existing reserves bool ReserveCandidates::registerReserveStatement(Stmt *stm) { auto memberCall = dyn_cast<CXXMemberCallExpr>(stm); if (!memberCall) return false; CXXMethodDecl *methodDecl = memberCall->getMethodDecl(); if (!methodDecl || methodDecl->getNameAsString() != "reserve") return false; CXXRecordDecl *decl = methodDecl->getParent(); if (!QtUtils::isAReserveClass(decl)) return false; ValueDecl *valueDecl = Utils::valueDeclForMemberCall(memberCall); if (!valueDecl) return false; if (!clazy_std::contains(m_foundReserves, valueDecl)) m_foundReserves.push_back(valueDecl); return true; }
void RecordInfo::DetermineTracingMethods() { if (determined_trace_methods_) return; determined_trace_methods_ = true; if (Config::IsGCBase(name_)) return; CXXMethodDecl* trace = nullptr; CXXMethodDecl* trace_impl = nullptr; CXXMethodDecl* trace_after_dispatch = nullptr; bool has_adjust_and_mark = false; bool has_is_heap_object_alive = false; for (Decl* decl : record_->decls()) { CXXMethodDecl* method = dyn_cast<CXXMethodDecl>(decl); if (!method) { if (FunctionTemplateDecl* func_template = dyn_cast<FunctionTemplateDecl>(decl)) method = dyn_cast<CXXMethodDecl>(func_template->getTemplatedDecl()); } if (!method) continue; switch (Config::GetTraceMethodType(method)) { case Config::TRACE_METHOD: trace = method; break; case Config::TRACE_AFTER_DISPATCH_METHOD: trace_after_dispatch = method; break; case Config::TRACE_IMPL_METHOD: trace_impl = method; break; case Config::TRACE_AFTER_DISPATCH_IMPL_METHOD: break; case Config::NOT_TRACE_METHOD: if (method->getNameAsString() == kFinalizeName) { finalize_dispatch_method_ = method; } else if (method->getNameAsString() == kAdjustAndMarkName) { has_adjust_and_mark = true; } else if (method->getNameAsString() == kIsHeapObjectAliveName) { has_is_heap_object_alive = true; } break; } } // Record if class defines the two GCMixin methods. has_gc_mixin_methods_ = has_adjust_and_mark && has_is_heap_object_alive ? kTrue : kFalse; if (trace_after_dispatch) { trace_method_ = trace_after_dispatch; trace_dispatch_method_ = trace_impl ? trace_impl : trace; } else { // TODO: Can we never have a dispatch method called trace without the same // class defining a traceAfterDispatch method? trace_method_ = trace; trace_dispatch_method_ = nullptr; } if (trace_dispatch_method_ && finalize_dispatch_method_) return; // If this class does not define dispatching methods inherit them. for (Bases::iterator it = GetBases().begin(); it != GetBases().end(); ++it) { // TODO: Does it make sense to inherit multiple dispatch methods? if (CXXMethodDecl* dispatch = it->second.info()->GetTraceDispatchMethod()) { assert(!trace_dispatch_method_ && "Multiple trace dispatching methods"); trace_dispatch_method_ = dispatch; } if (CXXMethodDecl* dispatch = it->second.info()->GetFinalizeDispatchMethod()) { assert(!finalize_dispatch_method_ && "Multiple finalize dispatching methods"); finalize_dispatch_method_ = dispatch; } } }