11#include "clang/Frontend/CompilerInstance.h"
12#include "clang/Lex/Lexer.h"
13#include "clang/Lex/Preprocessor.h"
22 if (
const auto *RD = llvm::dyn_cast<CXXRecordDecl>(&Node)) {
23 if (
const auto *Destructor = RD->getDestructor())
24 return Destructor->isDeleted() || Destructor->getAccess() != AS_public;
37 const SourceManager &SM,
38 const LangOptions &Lang) {
39 const StringRef WrittenName = Lexer::getSourceText(
40 CharSourceRange::getTokenRange(
41 NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
43 if (NewExpr->isArray())
44 return (WrittenName +
"[]").str();
45 return WrittenName.str();
49 StringRef MakeSmartPtrFunctionName)
51 Inserter(Options.getLocalOrGlobal(
"IncludeStyle",
52 utils::IncludeSorter::IS_LLVM),
53 areDiagsSelfContained()),
54 MakeSmartPtrFunctionHeader(
55 Options.get(
"MakeSmartPtrFunctionHeader",
"<memory>")),
56 MakeSmartPtrFunctionName(
57 Options.get(
"MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
58 IgnoreMacros(Options.get(
"IgnoreMacros", true)),
59 IgnoreDefaultInitialization(
60 Options.get(
"IgnoreDefaultInitialization", true)) {}
63 Options.store(Opts,
"IncludeStyle", Inserter.getStyle());
64 Options.store(Opts,
"MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
65 Options.store(Opts,
"MakeSmartPtrFunction", MakeSmartPtrFunctionName);
66 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
67 Options.store(Opts,
"IgnoreDefaultInitialization",
68 IgnoreDefaultInitialization);
72 const LangOptions &LangOpts)
const {
73 return LangOpts.CPlusPlus11;
78 Preprocessor *ModuleExpanderPP) {
79 Inserter.registerPreprocessor(PP);
85 const auto CanCallCtor = unless(has(ignoringImpCasts(
86 cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
91 const auto CanCallDtor = unless(hasType(hasUnqualifiedDesugaredType(
92 pointerType(pointee(hasUnqualifiedDesugaredType(
93 recordType(hasDeclaration(hasInaccessibleDtor()))))))));
95 auto IsPlacement = hasAnyPlacementArg(anything());
100 anyOf(hasParent(cxxBindTemporaryExpr()),
104 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
106 CanCallCtor, unless(IsPlacement))
108 unless(isInTemplateInstantiation()))
116 unless(isInTemplateInstantiation()),
118 0, cxxNewExpr(CanCallCtor, CanCallDtor, unless(IsPlacement))
120 callee(cxxMethodDecl(hasName(
"reset"))),
122 on(ignoringImplicit(anyOf(
134 SourceManager &SM = *Result.SourceManager;
135 const auto *Construct =
137 const auto *DVar = Result.Nodes.getNodeAs<VarDecl>(
DirectVar);
138 const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
ResetCall);
139 const auto *Type = Result.Nodes.getNodeAs<QualType>(
PointerType);
140 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(
NewExpression);
143 if (New->getType()->getPointeeType()->getContainedAutoType())
154 const bool Initializes = New->hasInitializer() ||
156 New->getAllocatedType(), *Result.Context);
157 if (!Initializes && IgnoreDefaultInitialization)
160 checkConstruct(SM, Result.Context, Construct, DVar, Type, New);
162 checkReset(SM, Result.Context, Reset, New);
165void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,
166 const CXXConstructExpr *Construct,
168 const QualType *Type,
169 const CXXNewExpr *New) {
170 const SourceLocation ConstructCallStart = Construct->getExprLoc();
171 const bool InMacro = ConstructCallStart.isMacroID();
173 if (InMacro && IgnoreMacros)
176 bool Invalid =
false;
177 const StringRef ExprStr = Lexer::getSourceText(
178 CharSourceRange::getCharRange(
179 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
180 SM, getLangOpts(), &Invalid);
184 auto Diag = diag(ConstructCallStart,
"use %0 instead")
185 << MakeSmartPtrFunctionName;
191 if (!replaceNew(Diag, New, SM, Ctx))
195 const size_t LAngle = ExprStr.find(
'<');
196 SourceLocation ConstructCallEnd;
197 if (LAngle == StringRef::npos) {
200 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
201 Diag << FixItHint::CreateInsertion(
202 ConstructCallEnd,
"<" +
getNewExprName(New, SM, getLangOpts()) +
">");
204 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
207 std::string FinalMakeSmartPtrFunctionName = MakeSmartPtrFunctionName.str();
209 FinalMakeSmartPtrFunctionName =
210 ExprStr.str() +
" = " + MakeSmartPtrFunctionName.str();
212 Diag << FixItHint::CreateReplacement(
213 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
214 FinalMakeSmartPtrFunctionName);
218 if (Construct->isListInitialization()) {
219 const SourceRange BraceRange = Construct->getParenOrBraceRange();
220 Diag << FixItHint::CreateReplacement(
221 CharSourceRange::getCharRange(
222 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
224 Diag << FixItHint::CreateReplacement(
225 CharSourceRange::getCharRange(BraceRange.getEnd(),
226 BraceRange.getEnd().getLocWithOffset(1)),
230 insertHeader(Diag, SM.getFileID(ConstructCallStart));
233void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
234 const CXXMemberCallExpr *Reset,
235 const CXXNewExpr *New) {
236 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
237 const SourceLocation OperatorLoc = Expr->getOperatorLoc();
238 const SourceLocation ResetCallStart = Reset->getExprLoc();
239 const SourceLocation ExprStart = Expr->getBeginLoc();
240 const SourceLocation ExprEnd =
241 Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());
243 const bool InMacro = ExprStart.isMacroID();
245 if (InMacro && IgnoreMacros)
251 if (OperatorLoc.isInvalid())
254 auto Diag = diag(ResetCallStart,
"use %0 instead")
255 << MakeSmartPtrFunctionName;
261 if (!replaceNew(Diag, New, SM, Ctx))
264 Diag << FixItHint::CreateReplacement(
265 CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
266 (llvm::Twine(
" = ") + MakeSmartPtrFunctionName +
"<" +
271 Diag << FixItHint::CreateInsertion(ExprStart,
"*");
273 insertHeader(Diag, SM.getFileID(OperatorLoc));
276bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
277 const CXXNewExpr *New, SourceManager &SM,
279 const auto SkipParensParents = [&](
const Expr *E) {
280 const TraversalKindScope RAII(*Ctx, TK_AsIs);
282 for (
const Expr *OldE =
nullptr; E != OldE;) {
284 for (
const auto &Node : Ctx->getParents(*E)) {
285 if (
const Expr *Parent = Node.get<ParenExpr>()) {
294 const SourceRange NewRange = SkipParensParents(New)->getSourceRange();
295 const SourceLocation NewStart = NewRange.getBegin();
296 const SourceLocation NewEnd = NewRange.getEnd();
299 if (NewStart.isInvalid() || NewEnd.isInvalid())
302 std::string ArraySizeExpr;
303 if (
const auto *ArraySize = New->getArraySize().value_or(
nullptr)) {
304 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
305 ArraySize->getSourceRange()),
315 const auto HasListInitializedArgument = [](
const CXXConstructExpr *CE) {
316 for (
const auto *Arg : CE->arguments()) {
317 Arg = Arg->IgnoreImplicit();
319 if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
323 if (
const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
327 if (CEArg->isElidable()) {
328 if (
const auto *TempExp = CEArg->getArg(0)) {
329 if (
const auto *UnwrappedCE =
330 dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
334 if (CEArg->isStdInitListInitialization())
340 switch (New->getInitializationStyle()) {
341 case CXXNewInitializationStyle::None: {
342 if (ArraySizeExpr.empty()) {
343 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
347 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
352 case CXXNewInitializationStyle::Parens: {
370 if (
const auto *CE = New->getConstructExpr();
371 CE && HasListInitializedArgument(CE))
374 if (ArraySizeExpr.empty()) {
375 const SourceRange InitRange = New->getDirectInitRange();
376 Diag << FixItHint::CreateRemoval(
377 SourceRange(NewStart, InitRange.getBegin()));
378 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
383 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
388 case CXXNewInitializationStyle::Braces: {
390 SourceRange InitRange;
391 if (
const auto *NewConstruct = New->getConstructExpr()) {
392 if (NewConstruct->isStdInitListInitialization() ||
393 HasListInitializedArgument(NewConstruct)) {
416 InitRange = SourceRange(
417 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
418 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
429 if (
const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl();
430 RD && llvm::any_of(RD->ctors(), [](
const CXXConstructorDecl *Ctor) {
431 return Ctor->isCopyOrMoveConstructor() &&
432 (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
437 InitRange = SourceRange(
438 New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
439 New->getInitializer()->getSourceRange().getEnd());
441 Diag << FixItHint::CreateRemoval(
442 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
443 Diag << FixItHint::CreateRemoval(
444 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
451void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
452 if (MakeSmartPtrFunctionHeader.empty())
454 Diag << Inserter.createIncludeInsertion(FD, MakeSmartPtrFunctionHeader);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static constexpr char PointerType[]
virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const =0
Returns matcher that match with different smart pointer types.
MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context, StringRef MakeSmartPtrFunctionName)
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override
Returns whether the C++ version is compatible with current check.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) final
AST_MATCHER(BinaryOperator, isRelationalOperator)
static std::string getNewExprName(const CXXNewExpr *NewExpr, const SourceManager &SM, const LangOptions &Lang)
static constexpr char ResetCall[]
static constexpr char NewExpression[]
static constexpr char DirectVar[]
static constexpr char ConstructorCall[]
bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context)
Returns true if Type is trivially default constructible.
llvm::StringMap< ClangTidyValue > OptionMap