clang-tools 23.0.0git
RedundantMemberInitCheck.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "../utils/LexerUtils.h"
11#include "../utils/Matchers.h"
12#include "clang/AST/ASTContext.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/Lex/Lexer.h"
15
16using namespace clang::ast_matchers;
17using namespace clang::tidy::matchers;
18
20
21static SourceRange
22getFullInitRangeInclWhitespaces(SourceRange Range, const SourceManager &SM,
23 const LangOptions &LangOpts) {
24 const std::optional<Token> PrevToken =
25 utils::lexer::getPreviousToken(Range.getBegin(), SM, LangOpts, false);
26 if (!PrevToken)
27 return Range;
28
29 if (PrevToken->isNot(tok::equal))
30 return {PrevToken->getEndLoc(), Range.getEnd()};
31
33 {PrevToken->getLocation(), Range.getEnd()}, SM, LangOpts);
34}
35
36namespace {
37// Matches a ``CXXConstructExpr`` whose written argument list (i.e. the
38// source text between the parentheses or braces) involves a macro.
39AST_MATCHER(CXXConstructExpr, initListContainsMacro) {
40 const SourceRange InitRange = Node.getParenOrBraceRange();
41 if (InitRange.isInvalid())
42 return false;
43 if (InitRange.getBegin().isMacroID() || InitRange.getEnd().isMacroID())
44 return true;
45 const ASTContext &Context = Finder->getASTContext();
46 const std::optional<Token> NextTok =
48 Context.getSourceManager(),
49 Context.getLangOpts());
50 if (!NextTok)
51 return true;
52 return NextTok->getLocation() != InitRange.getEnd();
53}
54} // namespace
55
57 Options.store(Opts, "IgnoreBaseInCopyConstructors",
58 IgnoreBaseInCopyConstructors);
59 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
60}
61
63 auto ConstructorMatcher =
64 cxxConstructExpr(
65 argumentCountIs(0),
66 hasDeclaration(cxxConstructorDecl(
67 ofClass(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))
68 .bind("class")))),
69 IgnoreMacros
70 ? unless(initListContainsMacro())
71 : static_cast<ast_matchers::internal::Matcher<CXXConstructExpr>>(
72 anything()))
73 .bind("construct");
74
75 auto HasUnionAsParent = hasParent(recordDecl(isUnion()));
76
77 auto HasTypeEqualToConstructorClass = hasType(qualType(
78 hasCanonicalType(qualType(hasDeclaration(equalsBoundNode("class"))))));
79
80 Finder->addMatcher(
81 cxxConstructorDecl(
82 unless(isDelegatingConstructor()), ofClass(unless(isUnion())),
83 forEachConstructorInitializer(
84 cxxCtorInitializer(
85 withInitializer(ConstructorMatcher),
86 anyOf(isBaseInitializer(),
87 forField(fieldDecl(unless(hasType(isConstQualified())),
88 unless(HasUnionAsParent),
89 HasTypeEqualToConstructorClass))))
90 .bind("init")))
91 .bind("constructor"),
92 this);
93
94 Finder->addMatcher(fieldDecl(hasInClassInitializer(ConstructorMatcher),
95 HasTypeEqualToConstructorClass,
96 unless(HasUnionAsParent))
97 .bind("field"),
98 this);
99}
100
101void RedundantMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
102 const auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
103
104 if (const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field")) {
105 const Expr *Init = Field->getInClassInitializer();
106 auto Diag =
107 diag(Construct->getExprLoc(), "initializer for member %0 is redundant")
108 << Field;
109 if (!Init->getBeginLoc().isMacroID() && !Init->getEndLoc().isMacroID())
110 Diag << FixItHint::CreateRemoval(getFullInitRangeInclWhitespaces(
111 Init->getSourceRange(), *Result.SourceManager, getLangOpts()));
112 return;
113 }
114
115 const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
116 const auto *ConstructorDecl =
117 Result.Nodes.getNodeAs<CXXConstructorDecl>("constructor");
118
119 if (IgnoreBaseInCopyConstructors && ConstructorDecl->isCopyConstructor() &&
120 Init->isBaseInitializer())
121 return;
122
123 if (Init->isAnyMemberInitializer()) {
124 diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
125 << Init->getAnyMember()
126 << FixItHint::CreateRemoval(Init->getSourceRange());
127 } else {
128 diag(Init->getSourceLocation(),
129 "initializer for base class %0 is redundant")
130 << Construct->getType()
131 << FixItHint::CreateRemoval(Init->getSourceRange());
132 }
133}
134
135} // namespace clang::tidy::readability
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_MATCHER(BinaryOperator, isRelationalOperator)
static SourceRange getFullInitRangeInclWhitespaces(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
std::optional< Token > getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or std::nullopt if not found.
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
Definition LexerUtils.h:106
llvm::StringMap< ClangTidyValue > OptionMap