//===--- AmbiguousSmartptrResetCallCheck.cpp - clang-tidy -----------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "AmbiguousSmartptrResetCallCheck.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Lex/Lexer.h" using namespace clang::ast_matchers; namespace clang::tidy::readability { namespace { AST_MATCHER(CXXMethodDecl, hasOnlyDefaultParameters) { for (const auto *Param : Node.parameters()) { if (!Param->hasDefaultArg()) return false; } return true; } const auto DefaultSmartPointers = "::std::shared_ptr;::std::unique_ptr;" "::boost::shared_ptr"; } // namespace AmbiguousSmartptrResetCallCheck::AmbiguousSmartptrResetCallCheck( StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), SmartPointers(utils::options::parseStringList( Options.get("SmartPointers", DefaultSmartPointers))) {} void AmbiguousSmartptrResetCallCheck::storeOptions( ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "SmartPointers", utils::options::serializeStringList(SmartPointers)); } void AmbiguousSmartptrResetCallCheck::registerMatchers(MatchFinder *Finder) { const auto IsSmartptr = hasAnyName(SmartPointers); const auto ResetMethod = cxxMethodDecl(hasName("reset"), hasOnlyDefaultParameters()); const auto TypeWithReset = anyOf(cxxRecordDecl( anyOf(hasMethod(ResetMethod), isDerivedFrom(cxxRecordDecl(hasMethod(ResetMethod))))), classTemplateSpecializationDecl( hasSpecializedTemplate(classTemplateDecl(has(ResetMethod))))); const auto SmartptrWithReset = expr(hasType(hasUnqualifiedDesugaredType( recordType(hasDeclaration(classTemplateSpecializationDecl( IsSmartptr, hasTemplateArgument( 0, templateArgument(refersToType(hasUnqualifiedDesugaredType( recordType(hasDeclaration(TypeWithReset)))))))))))); Finder->addMatcher( cxxMemberCallExpr( callee(ResetMethod), unless(hasAnyArgument(expr(unless(cxxDefaultArgExpr())))), anyOf(on(cxxOperatorCallExpr(hasOverloadedOperatorName("->"), hasArgument(0, SmartptrWithReset)) .bind("ArrowOp")), on(SmartptrWithReset))) .bind("MemberCall"), this); } void AmbiguousSmartptrResetCallCheck::check( const MatchFinder::MatchResult &Result) { const auto *MemberCall = Result.Nodes.getNodeAs("MemberCall"); assert(MemberCall); if (const auto *Arrow = Result.Nodes.getNodeAs("ArrowOp")) { const CharSourceRange SmartptrSourceRange = Lexer::getAsCharRange(Arrow->getArg(0)->getSourceRange(), *Result.SourceManager, getLangOpts()); diag(MemberCall->getBeginLoc(), "ambiguous call to 'reset()' on a pointee of a smart pointer, prefer " "more explicit approach"); diag(MemberCall->getBeginLoc(), "consider dereferencing smart pointer to call 'reset' method " "of the pointee here", DiagnosticIDs::Note) << FixItHint::CreateInsertion(SmartptrSourceRange.getBegin(), "(*") << FixItHint::CreateInsertion(SmartptrSourceRange.getEnd(), ")") << FixItHint::CreateReplacement( CharSourceRange::getCharRange( Arrow->getOperatorLoc(), Arrow->getOperatorLoc().getLocWithOffset(2)), "."); } else { const auto *Member = cast(MemberCall->getCallee()); assert(Member); diag(MemberCall->getBeginLoc(), "ambiguous call to 'reset()' on a smart pointer with pointee that " "also has a 'reset()' method, prefer more explicit approach"); diag(MemberCall->getBeginLoc(), "consider assigning the pointer to 'nullptr' here", DiagnosticIDs::Note) << FixItHint::CreateReplacement( SourceRange(Member->getOperatorLoc(), Member->getOperatorLoc()), " =") << FixItHint::CreateReplacement( SourceRange(Member->getMemberLoc(), MemberCall->getEndLoc()), " nullptr"); } } } // namespace clang::tidy::readability