9#include "../utils/TypeTraits.h"
11#include "clang/Frontend/CompilerInstance.h"
12#include "clang/Lex/Lexer.h"
13#include "clang/Lex/Preprocessor.h"
21constexpr char ConstructorCall[] =
"constructorCall";
22constexpr char ResetCall[] =
"resetCall";
23constexpr char NewExpression[] =
"newExpression";
25std::string getNewExprName(
const CXXNewExpr *NewExpr,
const SourceManager &SM,
26 const LangOptions &
Lang) {
27 StringRef WrittenName = Lexer::getSourceText(
28 CharSourceRange::getTokenRange(
29 NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
31 if (NewExpr->isArray()) {
32 return (WrittenName +
"[]").str();
34 return WrittenName.str();
42 StringRef MakeSmartPtrFunctionName)
44 Inserter(Options.getLocalOrGlobal(
"IncludeStyle",
45 utils::IncludeSorter::IS_LLVM),
46 areDiagsSelfContained()),
47 MakeSmartPtrFunctionHeader(
48 Options.get(
"MakeSmartPtrFunctionHeader",
"<memory>")),
49 MakeSmartPtrFunctionName(
50 Options.get(
"MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
51 IgnoreMacros(Options.getLocalOrGlobal(
"IgnoreMacros", true)),
52 IgnoreDefaultInitialization(
53 Options.get(
"IgnoreDefaultInitialization", true)) {}
57 Options.
store(Opts,
"MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
58 Options.
store(Opts,
"MakeSmartPtrFunction", MakeSmartPtrFunctionName);
61 IgnoreDefaultInitialization);
65 const LangOptions &LangOpts)
const {
66 return LangOpts.CPlusPlus11;
71 Preprocessor *ModuleExpanderPP) {
78 auto CanCallCtor = unless(has(ignoringImpCasts(
79 cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
81 auto IsPlacement = hasAnyPlacementArg(anything());
86 cxxBindTemporaryExpr(has(ignoringParenImpCasts(
90 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
92 CanCallCtor, unless(IsPlacement))
93 .bind(NewExpression)),
94 unless(isInTemplateInstantiation()))
95 .bind(ConstructorCall))))),
102 unless(isInTemplateInstantiation()),
103 hasArgument(0, cxxNewExpr(CanCallCtor, unless(IsPlacement))
104 .bind(NewExpression)),
105 callee(cxxMethodDecl(hasName(
"reset"))),
107 on(ignoringImplicit(anyOf(
119 SourceManager &SM = *Result.SourceManager;
120 const auto *Construct =
121 Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
122 const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
124 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
127 if (New->getType()->getPointeeType()->getContainedAutoType())
138 bool Initializes = New->hasInitializer() ||
140 New->getAllocatedType(), *Result.Context);
141 if (!Initializes && IgnoreDefaultInitialization)
144 checkConstruct(SM, Result.Context, Construct,
Type, New);
146 checkReset(SM, Result.Context, Reset, New);
149void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *Ctx,
150 const CXXConstructExpr *Construct,
151 const QualType *
Type,
152 const CXXNewExpr *New) {
153 SourceLocation ConstructCallStart = Construct->getExprLoc();
154 bool InMacro = ConstructCallStart.isMacroID();
156 if (InMacro && IgnoreMacros) {
160 bool Invalid =
false;
161 StringRef ExprStr = Lexer::getSourceText(
162 CharSourceRange::getCharRange(
163 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
168 auto Diag =
diag(ConstructCallStart,
"use %0 instead")
169 << MakeSmartPtrFunctionName;
176 if (!replaceNew(Diag, New, SM, Ctx)) {
181 size_t LAngle = ExprStr.find(
'<');
182 SourceLocation ConstructCallEnd;
183 if (LAngle == StringRef::npos) {
186 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
187 Diag << FixItHint::CreateInsertion(
188 ConstructCallEnd,
"<" + getNewExprName(New, SM,
getLangOpts()) +
">");
190 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
193 Diag << FixItHint::CreateReplacement(
194 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
195 MakeSmartPtrFunctionName);
199 if (Construct->isListInitialization()) {
200 SourceRange BraceRange = Construct->getParenOrBraceRange();
201 Diag << FixItHint::CreateReplacement(
202 CharSourceRange::getCharRange(
203 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
205 Diag << FixItHint::CreateReplacement(
206 CharSourceRange::getCharRange(BraceRange.getEnd(),
207 BraceRange.getEnd().getLocWithOffset(1)),
211 insertHeader(Diag, SM.getFileID(ConstructCallStart));
214void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
215 const CXXMemberCallExpr *Reset,
216 const CXXNewExpr *New) {
217 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
218 SourceLocation OperatorLoc = Expr->getOperatorLoc();
219 SourceLocation ResetCallStart = Reset->getExprLoc();
220 SourceLocation ExprStart = Expr->getBeginLoc();
221 SourceLocation ExprEnd =
222 Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM,
getLangOpts());
224 bool InMacro = ExprStart.isMacroID();
226 if (InMacro && IgnoreMacros) {
233 if (OperatorLoc.isInvalid()) {
237 auto Diag =
diag(ResetCallStart,
"use %0 instead")
238 << MakeSmartPtrFunctionName;
245 if (!replaceNew(Diag, New, SM, Ctx)) {
249 Diag << FixItHint::CreateReplacement(
250 CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
251 (llvm::Twine(
" = ") + MakeSmartPtrFunctionName +
"<" +
256 Diag << FixItHint::CreateInsertion(ExprStart,
"*");
258 insertHeader(Diag, SM.getFileID(OperatorLoc));
261bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
262 const CXXNewExpr *New, SourceManager &SM,
264 auto SkipParensParents = [&](
const Expr *
E) {
265 TraversalKindScope RAII(*Ctx, TK_AsIs);
267 for (
const Expr *OldE =
nullptr;
E != OldE;) {
269 for (
const auto &Node : Ctx->getParents(*
E)) {
270 if (
const Expr *
Parent =
Node.get<ParenExpr>()) {
279 SourceRange NewRange = SkipParensParents(New)->getSourceRange();
280 SourceLocation NewStart = NewRange.getBegin();
281 SourceLocation NewEnd = NewRange.getEnd();
284 if (NewStart.isInvalid() || NewEnd.isInvalid())
287 std::string ArraySizeExpr;
288 if (
const auto *ArraySize = New->getArraySize().value_or(
nullptr)) {
289 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
290 ArraySize->getSourceRange()),
300 auto HasListIntializedArgument = [](
const CXXConstructExpr *
CE) {
301 for (
const auto *Arg :
CE->arguments()) {
302 Arg = Arg->IgnoreImplicit();
304 if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
308 if (
const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
312 if (CEArg->isElidable()) {
313 if (
const auto *TempExp = CEArg->getArg(0)) {
314 if (
const auto *UnwrappedCE =
315 dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
319 if (CEArg->isStdInitListInitialization())
325 switch (New->getInitializationStyle()) {
326 case CXXNewInitializationStyle::None: {
327 if (ArraySizeExpr.empty()) {
328 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
332 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
337 case CXXNewInitializationStyle::Parens: {
355 if (
const auto *
CE = New->getConstructExpr()) {
356 if (HasListIntializedArgument(
CE))
359 if (ArraySizeExpr.empty()) {
360 SourceRange InitRange = New->getDirectInitRange();
361 Diag << FixItHint::CreateRemoval(
362 SourceRange(NewStart, InitRange.getBegin()));
363 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
369 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
374 case CXXNewInitializationStyle::Braces: {
376 SourceRange InitRange;
377 if (
const auto *NewConstruct = New->getConstructExpr()) {
378 if (NewConstruct->isStdInitListInitialization() ||
379 HasListIntializedArgument(NewConstruct)) {
402 InitRange = SourceRange(
403 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
404 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
415 if (
const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
416 if (llvm::any_of(RD->ctors(), [](
const CXXConstructorDecl *Ctor) {
417 return Ctor->isCopyOrMoveConstructor() &&
418 (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
423 InitRange = SourceRange(
424 New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
425 New->getInitializer()->getSourceRange().getEnd());
427 Diag << FixItHint::CreateRemoval(
428 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
429 Diag << FixItHint::CreateRemoval(
430 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
437void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
438 if (MakeSmartPtrFunctionHeader.empty()) {
llvm::SmallString< 256U > Name
::clang::DynTypedNode Node
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const =0
Returns matcher that match with different smart pointer types.
static const char PointerType[]
MakeSmartPtrCheck(StringRef Name, ClangTidyContext *Context, StringRef MakeSmartPtrFunctionName)
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
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
Override this to register PPCallbacks in the preprocessor.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Override this to register AST matchers with Finder.
void registerPreprocessor(Preprocessor *PP)
Registers this with the Preprocessor PP, must be called before this class is used.
std::optional< FixItHint > createIncludeInsertion(FileID FileID, llvm::StringRef Header)
Creates a Header inclusion directive fixit in the File FileID.
IncludeSorter::IncludeStyle getStyle() const
bool isTriviallyDefaultConstructible(QualType Type, const ASTContext &Context)
Returns true if Type is trivially default constructible.
llvm::StringMap< ClangTidyValue > OptionMap