| //===--- 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<CXXMemberCallExpr>("MemberCall"); |
| assert(MemberCall); |
| |
| if (const auto *Arrow = |
| Result.Nodes.getNodeAs<CXXOperatorCallExpr>("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<MemberExpr>(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 |