127 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "AmbiguousSmartptrResetCallCheck.h"10#include "../utils/OptionsUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "clang/Lex/Lexer.h"15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::readability {19 20namespace {21 22AST_MATCHER(CXXMethodDecl, hasOnlyDefaultParameters) {23 for (const auto *Param : Node.parameters()) {24 if (!Param->hasDefaultArg())25 return false;26 }27 28 return true;29}30 31const auto DefaultSmartPointers = "::std::shared_ptr;::std::unique_ptr;"32 "::boost::shared_ptr";33} // namespace34 35AmbiguousSmartptrResetCallCheck::AmbiguousSmartptrResetCallCheck(36 StringRef Name, ClangTidyContext *Context)37 : ClangTidyCheck(Name, Context),38 SmartPointers(utils::options::parseStringList(39 Options.get("SmartPointers", DefaultSmartPointers))) {}40 41void AmbiguousSmartptrResetCallCheck::storeOptions(42 ClangTidyOptions::OptionMap &Opts) {43 Options.store(Opts, "SmartPointers",44 utils::options::serializeStringList(SmartPointers));45}46 47void AmbiguousSmartptrResetCallCheck::registerMatchers(MatchFinder *Finder) {48 const auto IsSmartptr = hasAnyName(SmartPointers);49 50 const auto ResetMethod =51 cxxMethodDecl(hasName("reset"), hasOnlyDefaultParameters());52 53 const auto TypeWithReset =54 anyOf(cxxRecordDecl(55 anyOf(hasMethod(ResetMethod),56 isDerivedFrom(cxxRecordDecl(hasMethod(ResetMethod))))),57 classTemplateSpecializationDecl(58 hasSpecializedTemplate(classTemplateDecl(has(ResetMethod)))));59 60 const auto SmartptrWithReset = expr(hasType(hasUnqualifiedDesugaredType(61 recordType(hasDeclaration(classTemplateSpecializationDecl(62 IsSmartptr,63 hasTemplateArgument(64 0, templateArgument(refersToType(hasUnqualifiedDesugaredType(65 recordType(hasDeclaration(TypeWithReset))))))))))));66 67 Finder->addMatcher(68 cxxMemberCallExpr(69 callee(ResetMethod),70 unless(hasAnyArgument(expr(unless(cxxDefaultArgExpr())))),71 anyOf(on(cxxOperatorCallExpr(hasOverloadedOperatorName("->"),72 hasArgument(0, SmartptrWithReset))73 .bind("ArrowOp")),74 on(SmartptrWithReset)))75 .bind("MemberCall"),76 this);77}78 79void AmbiguousSmartptrResetCallCheck::check(80 const MatchFinder::MatchResult &Result) {81 const auto *MemberCall =82 Result.Nodes.getNodeAs<CXXMemberCallExpr>("MemberCall");83 assert(MemberCall);84 85 if (const auto *Arrow =86 Result.Nodes.getNodeAs<CXXOperatorCallExpr>("ArrowOp")) {87 const CharSourceRange SmartptrSourceRange =88 Lexer::getAsCharRange(Arrow->getArg(0)->getSourceRange(),89 *Result.SourceManager, getLangOpts());90 91 diag(MemberCall->getBeginLoc(),92 "ambiguous call to 'reset()' on a pointee of a smart pointer, prefer "93 "more explicit approach");94 95 diag(MemberCall->getBeginLoc(),96 "consider dereferencing smart pointer to call 'reset' method "97 "of the pointee here",98 DiagnosticIDs::Note)99 << FixItHint::CreateInsertion(SmartptrSourceRange.getBegin(), "(*")100 << FixItHint::CreateInsertion(SmartptrSourceRange.getEnd(), ")")101 << FixItHint::CreateReplacement(102 CharSourceRange::getCharRange(103 Arrow->getOperatorLoc(),104 Arrow->getOperatorLoc().getLocWithOffset(2)),105 ".");106 } else {107 const auto *Member = cast<MemberExpr>(MemberCall->getCallee());108 assert(Member);109 110 diag(MemberCall->getBeginLoc(),111 "ambiguous call to 'reset()' on a smart pointer with pointee that "112 "also has a 'reset()' method, prefer more explicit approach");113 114 diag(MemberCall->getBeginLoc(),115 "consider assigning the pointer to 'nullptr' here",116 DiagnosticIDs::Note)117 << FixItHint::CreateReplacement(118 SourceRange(Member->getOperatorLoc(), Member->getOperatorLoc()),119 " =")120 << FixItHint::CreateReplacement(121 SourceRange(Member->getMemberLoc(), MemberCall->getEndLoc()),122 " nullptr");123 }124}125 126} // namespace clang::tidy::readability127