void RuleOfTwoSoft::VisitStmt(Stmt *s) { CXXOperatorCallExpr *op = dyn_cast<CXXOperatorCallExpr>(s); if (op) { FunctionDecl *func = op->getDirectCallee(); if (func && func->getNameAsString() == "operator=") { CXXMethodDecl *method = dyn_cast<CXXMethodDecl>(func); if (method && method->getParent()) { CXXRecordDecl *record = method->getParent(); const bool hasCopyCtor = record->hasNonTrivialCopyConstructor(); const bool hasCopyAssignOp = record->hasNonTrivialCopyAssignment(); if (hasCopyCtor && !hasCopyAssignOp && !isBlacklisted(record)) { string msg = "Using assign operator but class " + record->getQualifiedNameAsString() + " has copy-ctor but no assign operator"; emitWarning(s->getLocStart(), msg); } } } } else if (CXXConstructExpr *ctorExpr = dyn_cast<CXXConstructExpr>(s)) { CXXConstructorDecl *ctorDecl = ctorExpr->getConstructor(); CXXRecordDecl *record = ctorDecl->getParent(); if (ctorDecl->isCopyConstructor() && record) { const bool hasCopyCtor = record->hasNonTrivialCopyConstructor(); const bool hasCopyAssignOp = record->hasNonTrivialCopyAssignment(); if (!hasCopyCtor && hasCopyAssignOp && !isBlacklisted(record)) { string msg = "Using copy-ctor but class " + record->getQualifiedNameAsString() + " has a trivial copy-ctor but non trivial assign operator"; emitWarning(s->getLocStart(), msg); } } } }
SourceLocation VirtualCallsFromCTOR::containsVirtualCall(clang::CXXRecordDecl *classDecl, clang::Stmt *stmt, std::vector<Stmt*> &processedStmts) { if (stmt == nullptr) return {}; // already processed ? we don't want recurring calls if (std::find(processedStmts.cbegin(), processedStmts.cend(), stmt) != processedStmts.cend()) return {}; processedStmts.push_back(stmt); std::vector<CXXMemberCallExpr*> memberCalls; Utils::getChilds2<CXXMemberCallExpr>(stmt, memberCalls); for (CXXMemberCallExpr *callExpr : memberCalls) { CXXMethodDecl *memberDecl = callExpr->getMethodDecl(); if (memberDecl == nullptr || dyn_cast<CXXThisExpr>(callExpr->getImplicitObjectArgument()) == nullptr) continue; if (memberDecl->getParent() == classDecl) { if (memberDecl->isPure()) { return callExpr->getLocStart(); } else { if (containsVirtualCall(classDecl, memberDecl->getBody(), processedStmts).isValid()) return callExpr->getLocStart(); } } } return {}; }
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); } }
/// Starting at a given context (a Decl or DeclContext), look for a /// code context that is not a closure (a lambda, block, etc.). template <class T> static Decl *getNonClosureContext(T *D) { if (getKind(D) == Decl::CXXMethod) { CXXMethodDecl *MD = cast<CXXMethodDecl>(D); if (MD->getOverloadedOperator() == OO_Call && MD->getParent()->isLambda()) return getNonClosureContext(MD->getParent()->getParent()); return MD; } else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { return FD; } else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { return MD; } else if (BlockDecl *BD = dyn_cast<BlockDecl>(D)) { return getNonClosureContext(BD->getParent()); } else if (CapturedDecl *CD = dyn_cast<CapturedDecl>(D)) { return getNonClosureContext(CD->getParent()); } else { return 0; } }
void IsEmptyVSCount::VisitStmt(clang::Stmt *stmt) { ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(stmt); if (!cast || cast->getCastKind() != clang::CK_IntegralToBoolean) return; CXXMemberCallExpr *memberCall = dyn_cast<CXXMemberCallExpr>(*(cast->child_begin())); CXXMethodDecl *method = memberCall ? memberCall->getMethodDecl() : nullptr; if (!StringUtils::functionIsOneOf(method, {"size", "count", "length"})) return; if (!StringUtils::classIsOneOf(method->getParent(), QtUtils::qtContainers())) return; emitWarning(stmt->getLocStart(), "use isEmpty() instead"); }
// 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 Parser::ParseLexedMethodDeclaration(LateParsedMethodDeclaration &LM) { // If this is a member template, introduce the template parameter scope. ParseScope TemplateScope(this, Scope::TemplateParamScope, LM.TemplateScope); TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); if (LM.TemplateScope) { Actions.ActOnReenterTemplateScope(getCurScope(), LM.Method); ++CurTemplateDepthTracker; } // Start the delayed C++ method declaration Actions.ActOnStartDelayedCXXMethodDeclaration(getCurScope(), LM.Method); // Introduce the parameters into scope and parse their default // arguments. ParseScope PrototypeScope(this, Scope::FunctionPrototypeScope | Scope::FunctionDeclarationScope | Scope::DeclScope); for (unsigned I = 0, N = LM.DefaultArgs.size(); I != N; ++I) { auto Param = LM.DefaultArgs[I].Param; // Introduce the parameter into scope. Actions.ActOnDelayedCXXMethodParameter(getCurScope(), Param); if (CachedTokens *Toks = LM.DefaultArgs[I].Toks) { // Mark the end of the default argument so that we know when to stop when // we parse it later on. Token LastDefaultArgToken = Toks->back(); Token DefArgEnd; DefArgEnd.startToken(); DefArgEnd.setKind(tok::eof); DefArgEnd.setLocation(LastDefaultArgToken.getLocation().getLocWithOffset( LastDefaultArgToken.getLength())); DefArgEnd.setEofData(Param); Toks->push_back(DefArgEnd); // Parse the default argument from its saved token stream. Toks->push_back(Tok); // So that the current token doesn't get lost PP.EnterTokenStream(&Toks->front(), Toks->size(), true, false); // Consume the previously-pushed token. ConsumeAnyToken(); // Consume the '='. assert(Tok.is(tok::equal) && "Default argument not starting with '='"); SourceLocation EqualLoc = ConsumeToken(); // The argument isn't actually potentially evaluated unless it is // used. EnterExpressionEvaluationContext Eval(Actions, Sema::PotentiallyEvaluatedIfUsed, Param); ExprResult DefArgResult; if (getLangOpts().CPlusPlus11 && Tok.is(tok::l_brace)) { Diag(Tok, diag::warn_cxx98_compat_generalized_initializer_lists); DefArgResult = ParseBraceInitializer(); } else DefArgResult = ParseAssignmentExpression(); DefArgResult = Actions.CorrectDelayedTyposInExpr(DefArgResult); if (DefArgResult.isInvalid()) { Actions.ActOnParamDefaultArgumentError(Param, EqualLoc); } else { if (Tok.isNot(tok::eof) || Tok.getEofData() != Param) { // The last two tokens are the terminator and the saved value of // Tok; the last token in the default argument is the one before // those. assert(Toks->size() >= 3 && "expected a token in default arg"); Diag(Tok.getLocation(), diag::err_default_arg_unparsed) << SourceRange(Tok.getLocation(), (*Toks)[Toks->size() - 3].getLocation()); } Actions.ActOnParamDefaultArgument(Param, EqualLoc, DefArgResult.get()); } // There could be leftover tokens (e.g. because of an error). // Skip through until we reach the 'end of default argument' token. while (Tok.isNot(tok::eof)) ConsumeAnyToken(); if (Tok.is(tok::eof) && Tok.getEofData() == Param) ConsumeAnyToken(); delete Toks; LM.DefaultArgs[I].Toks = nullptr; } } // Parse a delayed exception-specification, if there is one. if (CachedTokens *Toks = LM.ExceptionSpecTokens) { // Add the 'stop' token. Token LastExceptionSpecToken = Toks->back(); Token ExceptionSpecEnd; ExceptionSpecEnd.startToken(); ExceptionSpecEnd.setKind(tok::eof); ExceptionSpecEnd.setLocation( LastExceptionSpecToken.getLocation().getLocWithOffset( LastExceptionSpecToken.getLength())); ExceptionSpecEnd.setEofData(LM.Method); Toks->push_back(ExceptionSpecEnd); // Parse the default argument from its saved token stream. Toks->push_back(Tok); // So that the current token doesn't get lost PP.EnterTokenStream(&Toks->front(), Toks->size(), true, false); // Consume the previously-pushed token. ConsumeAnyToken(); // C++11 [expr.prim.general]p3: // If a declaration declares a member function or member function // template of a class X, the expression this is a prvalue of type // "pointer to cv-qualifier-seq X" between the optional cv-qualifer-seq // and the end of the function-definition, member-declarator, or // declarator. CXXMethodDecl *Method; if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(LM.Method)) Method = cast<CXXMethodDecl>(FunTmpl->getTemplatedDecl()); else Method = cast<CXXMethodDecl>(LM.Method); Sema::CXXThisScopeRAII ThisScope(Actions, Method->getParent(), Method->getTypeQualifiers(), getLangOpts().CPlusPlus11); // Parse the exception-specification. SourceRange SpecificationRange; SmallVector<ParsedType, 4> DynamicExceptions; SmallVector<SourceRange, 4> DynamicExceptionRanges; ExprResult NoexceptExpr; CachedTokens *ExceptionSpecTokens; ExceptionSpecificationType EST = tryParseExceptionSpecification(/*Delayed=*/false, SpecificationRange, DynamicExceptions, DynamicExceptionRanges, NoexceptExpr, ExceptionSpecTokens); if (Tok.isNot(tok::eof) || Tok.getEofData() != LM.Method) Diag(Tok.getLocation(), diag::err_except_spec_unparsed); // Attach the exception-specification to the method. Actions.actOnDelayedExceptionSpecification(LM.Method, EST, SpecificationRange, DynamicExceptions, DynamicExceptionRanges, NoexceptExpr.isUsable()? NoexceptExpr.get() : nullptr); // There could be leftover tokens (e.g. because of an error). // Skip through until we reach the original token position. while (Tok.isNot(tok::eof)) ConsumeAnyToken(); // Clean up the remaining EOF token. if (Tok.is(tok::eof) && Tok.getEofData() == LM.Method) ConsumeAnyToken(); delete Toks; LM.ExceptionSpecTokens = nullptr; } PrototypeScope.Exit(); // Finish the delayed C++ method declaration. Actions.ActOnFinishDelayedCXXMethodDeclaration(getCurScope(), LM.Method); }