11#include "clang/Frontend/CompilerInstance.h"
12#include "clang/Lex/Lexer.h"
13#include "clang/Lex/Preprocessor.h"
25 const SourceManager &SM,
26 const LangOptions &Lang) {
27 const StringRef WrittenName = Lexer::getSourceText(
28 CharSourceRange::getTokenRange(
29 NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
31 if (NewExpr->isArray())
32 return (WrittenName +
"[]").str();
33 return WrittenName.str();
37 StringRef MakeSmartPtrFunctionName)
39 Inserter(Options.getLocalOrGlobal(
"IncludeStyle",
40 utils::IncludeSorter::IS_LLVM),
41 areDiagsSelfContained()),
42 MakeSmartPtrFunctionHeader(
43 Options.get(
"MakeSmartPtrFunctionHeader",
"<memory>")),
44 MakeSmartPtrFunctionName(
45 Options.get(
"MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
46 IgnoreMacros(Options.get(
"IgnoreMacros", true)),
47 IgnoreDefaultInitialization(
48 Options.get(
"IgnoreDefaultInitialization", true)) {}
51 Options.store(Opts,
"IncludeStyle", Inserter.getStyle());
52 Options.store(Opts,
"MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
53 Options.store(Opts,
"MakeSmartPtrFunction", MakeSmartPtrFunctionName);
54 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
55 Options.store(Opts,
"IgnoreDefaultInitialization",
56 IgnoreDefaultInitialization);
60 const LangOptions &LangOpts)
const {
61 return LangOpts.CPlusPlus11;
66 Preprocessor *ModuleExpanderPP) {
67 Inserter.registerPreprocessor(PP);
73 auto CanCallCtor = unless(has(ignoringImpCasts(
74 cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
76 auto IsPlacement = hasAnyPlacementArg(anything());
81 anyOf(hasParent(cxxBindTemporaryExpr()),
85 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
87 CanCallCtor, unless(IsPlacement))
89 unless(isInTemplateInstantiation()))
97 unless(isInTemplateInstantiation()),
98 hasArgument(0, cxxNewExpr(CanCallCtor, unless(IsPlacement))
100 callee(cxxMethodDecl(hasName(
"reset"))),
102 on(ignoringImplicit(anyOf(
114 SourceManager &SM = *Result.SourceManager;
115 const auto *Construct =
117 const auto *DVar = Result.Nodes.getNodeAs<VarDecl>(
DirectVar);
118 const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
ResetCall);
119 const auto *Type = Result.Nodes.getNodeAs<QualType>(
PointerType);
120 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(
NewExpression);
123 if (New->getType()->getPointeeType()->getContainedAutoType())
134 const bool Initializes = New->hasInitializer() ||
136 New->getAllocatedType(), *Result.Context);
137 if (!Initializes && IgnoreDefaultInitialization)
140 checkConstruct(SM, Result.Context, Construct, DVar, Type, New);
142 checkReset(SM, Result.Context, Reset, New);
145void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,
146 const CXXConstructExpr *Construct,
148 const QualType *Type,
149 const CXXNewExpr *New) {
150 const SourceLocation ConstructCallStart = Construct->getExprLoc();
151 const bool InMacro = ConstructCallStart.isMacroID();
153 if (InMacro && IgnoreMacros)
156 bool Invalid =
false;
157 const StringRef ExprStr = Lexer::getSourceText(
158 CharSourceRange::getCharRange(
159 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
160 SM, getLangOpts(), &Invalid);
164 auto Diag = diag(ConstructCallStart,
"use %0 instead")
165 << MakeSmartPtrFunctionName;
171 if (!replaceNew(Diag, New, SM, Ctx))
175 const size_t LAngle = ExprStr.find(
'<');
176 SourceLocation ConstructCallEnd;
177 if (LAngle == StringRef::npos) {
180 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
181 Diag << FixItHint::CreateInsertion(
182 ConstructCallEnd,
"<" +
getNewExprName(New, SM, getLangOpts()) +
">");
184 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
187 std::string FinalMakeSmartPtrFunctionName = MakeSmartPtrFunctionName.str();
189 FinalMakeSmartPtrFunctionName =
190 ExprStr.str() +
" = " + MakeSmartPtrFunctionName.str();
192 Diag << FixItHint::CreateReplacement(
193 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
194 FinalMakeSmartPtrFunctionName);
198 if (Construct->isListInitialization()) {
199 const SourceRange BraceRange = Construct->getParenOrBraceRange();
200 Diag << FixItHint::CreateReplacement(
201 CharSourceRange::getCharRange(
202 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
204 Diag << FixItHint::CreateReplacement(
205 CharSourceRange::getCharRange(BraceRange.getEnd(),
206 BraceRange.getEnd().getLocWithOffset(1)),
210 insertHeader(Diag, SM.getFileID(ConstructCallStart));
213void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
214 const CXXMemberCallExpr *Reset,
215 const CXXNewExpr *New) {
216 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
217 const SourceLocation OperatorLoc = Expr->getOperatorLoc();
218 const SourceLocation ResetCallStart = Reset->getExprLoc();
219 const SourceLocation ExprStart = Expr->getBeginLoc();
220 const SourceLocation ExprEnd =
221 Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM, getLangOpts());
223 const bool InMacro = ExprStart.isMacroID();
225 if (InMacro && IgnoreMacros)
231 if (OperatorLoc.isInvalid())
234 auto Diag = diag(ResetCallStart,
"use %0 instead")
235 << MakeSmartPtrFunctionName;
241 if (!replaceNew(Diag, New, SM, Ctx))
244 Diag << FixItHint::CreateReplacement(
245 CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
246 (llvm::Twine(
" = ") + MakeSmartPtrFunctionName +
"<" +
251 Diag << FixItHint::CreateInsertion(ExprStart,
"*");
253 insertHeader(Diag, SM.getFileID(OperatorLoc));
256bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
257 const CXXNewExpr *New, SourceManager &SM,
259 auto SkipParensParents = [&](
const Expr *E) {
260 const TraversalKindScope RAII(*Ctx, TK_AsIs);
262 for (
const Expr *OldE =
nullptr; E != OldE;) {
264 for (
const auto &Node : Ctx->getParents(*E)) {
265 if (
const Expr *Parent = Node.get<ParenExpr>()) {
274 const SourceRange NewRange = SkipParensParents(New)->getSourceRange();
275 const SourceLocation NewStart = NewRange.getBegin();
276 const SourceLocation NewEnd = NewRange.getEnd();
279 if (NewStart.isInvalid() || NewEnd.isInvalid())
282 std::string ArraySizeExpr;
283 if (
const auto *ArraySize = New->getArraySize().value_or(
nullptr)) {
284 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
285 ArraySize->getSourceRange()),
295 auto HasListIntializedArgument = [](
const CXXConstructExpr *CE) {
296 for (
const auto *Arg : CE->arguments()) {
297 Arg = Arg->IgnoreImplicit();
299 if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
303 if (
const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
307 if (CEArg->isElidable()) {
308 if (
const auto *TempExp = CEArg->getArg(0)) {
309 if (
const auto *UnwrappedCE =
310 dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
314 if (CEArg->isStdInitListInitialization())
320 switch (New->getInitializationStyle()) {
321 case CXXNewInitializationStyle::None: {
322 if (ArraySizeExpr.empty()) {
323 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
327 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
332 case CXXNewInitializationStyle::Parens: {
350 if (
const auto *CE = New->getConstructExpr()) {
351 if (HasListIntializedArgument(CE))
354 if (ArraySizeExpr.empty()) {
355 const SourceRange InitRange = New->getDirectInitRange();
356 Diag << FixItHint::CreateRemoval(
357 SourceRange(NewStart, InitRange.getBegin()));
358 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
363 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
368 case CXXNewInitializationStyle::Braces: {
370 SourceRange InitRange;
371 if (
const auto *NewConstruct = New->getConstructExpr()) {
372 if (NewConstruct->isStdInitListInitialization() ||
373 HasListIntializedArgument(NewConstruct)) {
396 InitRange = SourceRange(
397 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
398 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
409 if (
const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
410 if (llvm::any_of(RD->ctors(), [](
const CXXConstructorDecl *Ctor) {
411 return Ctor->isCopyOrMoveConstructor() &&
412 (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
417 InitRange = SourceRange(
418 New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
419 New->getInitializer()->getSourceRange().getEnd());
421 Diag << FixItHint::CreateRemoval(
422 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
423 Diag << FixItHint::CreateRemoval(
424 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
431void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
432 if (MakeSmartPtrFunctionHeader.empty())
434 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
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