10 #include "clang/Frontend/CompilerInstance.h" 11 #include "clang/Lex/Lexer.h" 12 #include "clang/Lex/Preprocessor.h" 22 constexpr
char StdMemoryHeader[] =
"memory";
23 constexpr
char ConstructorCall[] =
"constructorCall";
24 constexpr
char ResetCall[] =
"resetCall";
25 constexpr
char NewExpression[] =
"newExpression";
27 std::string GetNewExprName(
const CXXNewExpr *NewExpr,
28 const SourceManager &SM,
29 const LangOptions &Lang) {
30 StringRef WrittenName = Lexer::getSourceText(
32 NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
34 if (NewExpr->isArray()) {
35 return (WrittenName +
"[]").str();
37 return WrittenName.str();
42 const char MakeSmartPtrCheck::PointerType[] =
"pointerType";
44 MakeSmartPtrCheck::MakeSmartPtrCheck(StringRef
Name,
46 StringRef MakeSmartPtrFunctionName)
48 IncludeStyle(utils::IncludeSorter::parseIncludeStyle(
49 Options.getLocalOrGlobal(
"IncludeStyle",
"llvm"))),
50 MakeSmartPtrFunctionHeader(
51 Options.get(
"MakeSmartPtrFunctionHeader", StdMemoryHeader)),
52 MakeSmartPtrFunctionName(
53 Options.get(
"MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
54 IgnoreMacros(Options.getLocalOrGlobal(
"IgnoreMacros", true)) {}
58 Options.
store(Opts,
"MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
59 Options.
store(Opts,
"MakeSmartPtrFunction", MakeSmartPtrFunctionName);
64 const LangOptions &LangOpts)
const {
65 return LangOpts.CPlusPlus11;
70 Preprocessor *ModuleExpanderPP) {
72 Inserter = llvm::make_unique<utils::IncludeInserter>(SM,
getLangOpts(),
74 PP->addPPCallbacks(Inserter->CreatePPCallbacks());
84 auto CanCallCtor = unless(has(ignoringImpCasts(
85 cxxConstructExpr(hasDeclaration(decl(unless(
isPublic())))))));
88 cxxBindTemporaryExpr(has(ignoringParenImpCasts(
92 cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
95 .bind(NewExpression)),
96 unless(isInTemplateInstantiation()))
97 .bind(ConstructorCall)))),
103 callee(cxxMethodDecl(hasName(
"reset"))),
104 hasArgument(0, cxxNewExpr(CanCallCtor).bind(NewExpression)),
105 unless(isInTemplateInstantiation()))
115 SourceManager &SM = *Result.SourceManager;
116 const auto *Construct =
117 Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
118 const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
120 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
122 if (New->getNumPlacementArgs() != 0)
125 if (New->getType()->getPointeeType()->getContainedAutoType())
135 if (New->isArray() && !New->hasInitializer())
138 checkConstruct(SM, Result.Context, Construct, Type, New);
140 checkReset(SM, Result.Context, Reset, New);
143 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *
Ctx,
144 const CXXConstructExpr *Construct,
145 const QualType *
Type,
146 const CXXNewExpr *New) {
147 SourceLocation ConstructCallStart = Construct->getExprLoc();
148 bool InMacro = ConstructCallStart.isMacroID();
150 if (InMacro && IgnoreMacros) {
154 bool Invalid =
false;
155 StringRef ExprStr = Lexer::getSourceText(
156 CharSourceRange::getCharRange(
157 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
162 auto Diag =
diag(ConstructCallStart,
"use %0 instead")
163 << MakeSmartPtrFunctionName;
170 if (!replaceNew(Diag, New, SM, Ctx)) {
175 size_t LAngle = ExprStr.find(
"<");
176 SourceLocation ConstructCallEnd;
177 if (LAngle == StringRef::npos) {
180 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
181 Diag << FixItHint::CreateInsertion(
183 "<" + GetNewExprName(New, SM,
getLangOpts()) +
">");
185 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
188 Diag << FixItHint::CreateReplacement(
189 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
190 MakeSmartPtrFunctionName);
194 if (Construct->isListInitialization()) {
195 SourceRange BraceRange = Construct->getParenOrBraceRange();
196 Diag << FixItHint::CreateReplacement(
197 CharSourceRange::getCharRange(
198 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
200 Diag << FixItHint::CreateReplacement(
201 CharSourceRange::getCharRange(BraceRange.getEnd(),
202 BraceRange.getEnd().getLocWithOffset(1)),
206 insertHeader(Diag, SM.getFileID(ConstructCallStart));
209 void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *Ctx,
210 const CXXMemberCallExpr *Reset,
211 const CXXNewExpr *New) {
212 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
213 SourceLocation OperatorLoc = Expr->getOperatorLoc();
214 SourceLocation ResetCallStart = Reset->getExprLoc();
215 SourceLocation ExprStart = Expr->getBeginLoc();
216 SourceLocation ExprEnd =
217 Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM,
getLangOpts());
219 bool InMacro = ExprStart.isMacroID();
221 if (InMacro && IgnoreMacros) {
228 if (OperatorLoc.isInvalid()) {
232 auto Diag =
diag(ResetCallStart,
"use %0 instead")
233 << MakeSmartPtrFunctionName;
240 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));
256 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
257 const CXXNewExpr *New, SourceManager &SM,
259 auto SkipParensParents = [&](
const Expr *E) {
260 for (
const Expr *OldE =
nullptr; E != OldE;) {
262 for (
const auto &Node : Ctx->getParents(*E)) {
263 if (
const Expr *Parent = Node.get<ParenExpr>()) {
272 SourceRange NewRange = SkipParensParents(New)->getSourceRange();
273 SourceLocation NewStart = NewRange.getBegin();
274 SourceLocation NewEnd = NewRange.getEnd();
277 if (NewStart.isInvalid() || NewEnd.isInvalid())
280 std::string ArraySizeExpr;
281 if (
const auto* ArraySize = New->getArraySize().getValueOr(
nullptr)) {
283 ArraySize->getSourceRange()),
293 auto HasListIntializedArgument = [](
const CXXConstructExpr *CE) {
294 for (
const auto *Arg : CE->arguments()) {
295 Arg = Arg->IgnoreImplicit();
297 if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
301 if (
const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
305 if (CEArg->isElidable()) {
306 if (
const auto *TempExp = CEArg->getArg(0)) {
307 if (
const auto *UnwrappedCE =
308 dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
312 if (CEArg->isStdInitListInitialization())
318 switch (New->getInitializationStyle()) {
319 case CXXNewExpr::NoInit: {
320 if (ArraySizeExpr.empty()) {
321 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
325 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
330 case CXXNewExpr::CallInit: {
348 if (
const auto *CE = New->getConstructExpr()) {
349 if (HasListIntializedArgument(CE))
352 if (ArraySizeExpr.empty()) {
353 SourceRange InitRange = New->getDirectInitRange();
354 Diag << FixItHint::CreateRemoval(
355 SourceRange(NewStart, InitRange.getBegin()));
356 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
362 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
367 case CXXNewExpr::ListInit: {
369 SourceRange InitRange;
370 if (
const auto *NewConstruct = New->getConstructExpr()) {
371 if (NewConstruct->isStdInitListInitialization() ||
372 HasListIntializedArgument(NewConstruct)) {
395 InitRange = SourceRange(
396 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
397 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
409 if (
const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
410 if (llvm::find_if(RD->ctors(), [](
const CXXConstructorDecl *Ctor) {
411 return Ctor->isCopyOrMoveConstructor() &&
412 (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
413 }) != RD->ctor_end()) {
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));
431 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
432 if (MakeSmartPtrFunctionHeader.empty()) {
435 if (
auto IncludeFixit = Inserter->CreateIncludeInsertion(
436 FD, MakeSmartPtrFunctionHeader,
437 MakeSmartPtrFunctionHeader == StdMemoryHeader)) {
438 Diag << *IncludeFixit;
virtual bool isLanguageVersionSupported(const LangOptions &LangOpts) const
Returns whether the C++ version is compatible with current check.
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Override this to register AST matchers with Finder.
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const =0
Returns matcher that match with different smart pointer types.
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
Override this to register PPCallbacks in the preprocessor.
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.
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
static const char PointerType[]
llvm::Optional< Range > getTokenRange(const SourceManager &SM, const LangOptions &LangOpts, SourceLocation TokLoc)
Returns the taken range at TokLoc.
static bool isPublic(const clang::AccessSpecifier AS, const clang::Linkage Link)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.