1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
//===--- 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
|