blob: 5f36c3976fc695739021285641f9c0621f53c8fa [file] [log] [blame]
//===--- 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