clang-tools 10.0.0

UseDefaultMemberInitCheck.cpp
Go to the documentation of this file.
1 //===--- UseDefaultMemberInitCheck.cpp - clang-tidy------------------------===//
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
9 #include "UseDefaultMemberInitCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13
14 using namespace clang::ast_matchers;
15
16 namespace clang {
17 namespace tidy {
18 namespace modernize {
19
20 static StringRef getValueOfValueInit(const QualType InitType) {
21 switch (InitType->getScalarTypeKind()) {
22 case Type::STK_CPointer:
23 case Type::STK_BlockPointer:
24 case Type::STK_ObjCObjectPointer:
25 case Type::STK_MemberPointer:
26 return "nullptr";
27
28 case Type::STK_Bool:
29 return "false";
30
31 case Type::STK_Integral:
32 switch (InitType->castAs<BuiltinType>()->getKind()) {
33 case BuiltinType::Char_U:
34 case BuiltinType::UChar:
35 case BuiltinType::Char_S:
36 case BuiltinType::SChar:
37 return "'\\0'";
38 case BuiltinType::WChar_U:
39 case BuiltinType::WChar_S:
40 return "L'\\0'";
41 case BuiltinType::Char16:
42 return "u'\\0'";
43 case BuiltinType::Char32:
44 return "U'\\0'";
45 default:
46 return "0";
47 }
48
49 case Type::STK_Floating:
50 switch (InitType->castAs<BuiltinType>()->getKind()) {
51 case BuiltinType::Half:
52 case BuiltinType::Float:
53 return "0.0f";
54 default:
55 return "0.0";
56 }
57
58 case Type::STK_FloatingComplex:
59 case Type::STK_IntegralComplex:
60 return getValueOfValueInit(
61 InitType->castAs<ComplexType>()->getElementType());
62
63 case Type::STK_FixedPoint:
64 switch (InitType->castAs<BuiltinType>()->getKind()) {
65 case BuiltinType::ShortAccum:
66 case BuiltinType::SatShortAccum:
67 return "0.0hk";
68 case BuiltinType::Accum:
69 case BuiltinType::SatAccum:
70 return "0.0k";
71 case BuiltinType::LongAccum:
72 case BuiltinType::SatLongAccum:
73 return "0.0lk";
74 case BuiltinType::UShortAccum:
75 case BuiltinType::SatUShortAccum:
76 return "0.0uhk";
77 case BuiltinType::UAccum:
78 case BuiltinType::SatUAccum:
79 return "0.0uk";
80 case BuiltinType::ULongAccum:
81 case BuiltinType::SatULongAccum:
82 return "0.0ulk";
83 case BuiltinType::ShortFract:
84 case BuiltinType::SatShortFract:
85 return "0.0hr";
86 case BuiltinType::Fract:
87 case BuiltinType::SatFract:
88 return "0.0r";
89 case BuiltinType::LongFract:
90 case BuiltinType::SatLongFract:
91 return "0.0lr";
92 case BuiltinType::UShortFract:
93 case BuiltinType::SatUShortFract:
94 return "0.0uhr";
95 case BuiltinType::UFract:
96 case BuiltinType::SatUFract:
97 return "0.0ur";
98 case BuiltinType::ULongFract:
99 case BuiltinType::SatULongFract:
100 return "0.0ulr";
101 default:
102 llvm_unreachable("Unhandled fixed point BuiltinType");
103 }
104 }
105 llvm_unreachable("Invalid scalar type kind");
106 }
107
108 static bool isZero(const Expr *E) {
109 switch (E->getStmtClass()) {
110 case Stmt::CXXNullPtrLiteralExprClass:
111 case Stmt::ImplicitValueInitExprClass:
112 return true;
113 case Stmt::InitListExprClass:
114 return cast<InitListExpr>(E)->getNumInits() == 0;
115 case Stmt::CharacterLiteralClass:
116 return !cast<CharacterLiteral>(E)->getValue();
117 case Stmt::CXXBoolLiteralExprClass:
118 return !cast<CXXBoolLiteralExpr>(E)->getValue();
119 case Stmt::IntegerLiteralClass:
120 return !cast<IntegerLiteral>(E)->getValue();
121 case Stmt::FloatingLiteralClass: {
122 llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
123 return Value.isZero() && !Value.isNegative();
124 }
125 default:
126 return false;
127 }
128 }
129
130 static const Expr *ignoreUnaryPlus(const Expr *E) {
131 auto *UnaryOp = dyn_cast<UnaryOperator>(E);
132 if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
133 return UnaryOp->getSubExpr();
134 return E;
135 }
136
137 static const Expr *getInitializer(const Expr *E) {
138 auto *InitList = dyn_cast<InitListExpr>(E);
139 if (InitList && InitList->getNumInits() == 1)
140 return InitList->getInit(0)->IgnoreParenImpCasts();
141 return E;
142 }
143
144 static bool sameValue(const Expr *E1, const Expr *E2) {
145 E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts()));
146 E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts()));
147
148 if (isZero(E1) && isZero(E2))
149 return true;
150
151 if (E1->getStmtClass() != E2->getStmtClass())
152 return false;
153
154 switch (E1->getStmtClass()) {
155 case Stmt::UnaryOperatorClass:
156 return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),
157 cast<UnaryOperator>(E2)->getSubExpr());
158 case Stmt::CharacterLiteralClass:
159 return cast<CharacterLiteral>(E1)->getValue() ==
160 cast<CharacterLiteral>(E2)->getValue();
161 case Stmt::CXXBoolLiteralExprClass:
162 return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
163 cast<CXXBoolLiteralExpr>(E2)->getValue();
164 case Stmt::IntegerLiteralClass:
165 return cast<IntegerLiteral>(E1)->getValue() ==
166 cast<IntegerLiteral>(E2)->getValue();
167 case Stmt::FloatingLiteralClass:
168 return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
169 cast<FloatingLiteral>(E2)->getValue());
170 case Stmt::StringLiteralClass:
171 return cast<StringLiteral>(E1)->getString() ==
172 cast<StringLiteral>(E2)->getString();
173 case Stmt::DeclRefExprClass:
174 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
175 default:
176 return false;
177 }
178 }
179
180 UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
181 ClangTidyContext *Context)
182 : ClangTidyCheck(Name, Context),
183 UseAssignment(Options.get("UseAssignment", 0) != 0),
184 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true) != 0) {}
185
186 void UseDefaultMemberInitCheck::storeOptions(
187 ClangTidyOptions::OptionMap &Opts) {
188 Options.store(Opts, "UseAssignment", UseAssignment);
189 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
190 }
191
192 void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
193 if (!getLangOpts().CPlusPlus11)
194 return;
195
196 auto Init =
197 anyOf(stringLiteral(), characterLiteral(), integerLiteral(),
198 unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
199 hasUnaryOperand(integerLiteral())),
200 floatLiteral(),
201 unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
202 hasUnaryOperand(floatLiteral())),
203 cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(),
204 initListExpr(), declRefExpr(to(enumConstantDecl())));
205
206 Finder->addMatcher(
207 cxxConstructorDecl(
208 isDefaultConstructor(), unless(isInstantiated()),
209 forEachConstructorInitializer(
210 cxxCtorInitializer(
211 forField(unless(anyOf(getLangOpts().CPlusPlus2a
212 ? unless(anything())
213 : isBitField(),
214 hasInClassInitializer(anything()),
215 hasParent(recordDecl(isUnion()))))),
216 isWritten(), withInitializer(ignoringImplicit(Init)))
217 .bind("default"))),
218 this);
219
220 Finder->addMatcher(
221 cxxConstructorDecl(
222 unless(ast_matchers::isTemplateInstantiation()),
223 forEachConstructorInitializer(
224 cxxCtorInitializer(forField(hasInClassInitializer(anything())),
225 isWritten(),
226 withInitializer(ignoringImplicit(Init)))
227 .bind("existing"))),
228 this);
229 }
230
231 void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
232 if (const auto *Default =
233 Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))
234 checkDefaultInit(Result, Default);
235 else if (const auto *Existing =
236 Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))
237 checkExistingInit(Result, Existing);
238 else
239 llvm_unreachable("Bad Callback. No node provided.");
240 }
241
242 void UseDefaultMemberInitCheck::checkDefaultInit(
243 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
244 const FieldDecl *Field = Init->getAnyMember();
245
246 SourceLocation StartLoc = Field->getBeginLoc();
247 if (StartLoc.isMacroID() && IgnoreMacros)
248 return;
249
250 SourceLocation FieldEnd =
251 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
252 *Result.SourceManager, getLangOpts());
253 SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
254 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
255 CharSourceRange InitRange =
256 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
257
258 bool ValueInit = isa<ImplicitValueInitExpr>(Init->getInit());
259 bool CanAssign = UseAssignment && (!ValueInit || !Init->getInit()->getType()->isEnumeralType());
260
261 auto Diag =
262 diag(Field->getLocation(), "use default member initializer for %0")
263 << Field
264 << FixItHint::CreateInsertion(FieldEnd, CanAssign ? " = " : "{")
265 << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
266
267 if (CanAssign && ValueInit)
268 Diag << FixItHint::CreateInsertion(
269 FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
270
271 if (!CanAssign)
272 Diag << FixItHint::CreateInsertion(FieldEnd, "}");
273
274 Diag << FixItHint::CreateRemoval(Init->getSourceRange());
275 }
276
277 void UseDefaultMemberInitCheck::checkExistingInit(
278 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
279 const FieldDecl *Field = Init->getAnyMember();
280
281 if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
282 return;
283
284 diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
285 << Field
286 << FixItHint::CreateRemoval(Init->getSourceRange());
287 }
288
289 } // namespace modernize
290 } // namespace tidy
291 } // namespace clang
clang::tidy::modernize::UseDefaultMemberInitCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: UseDefaultMemberInitCheck.cpp:231
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::modernize::getInitializer
static const Expr * getInitializer(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:137
clang::tidy::modernize::ignoreUnaryPlus
static const Expr * ignoreUnaryPlus(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:130
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:47
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:188
clang::tidy::modernize::UseDefaultMemberInitCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: UseDefaultMemberInitCheck.cpp:192
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:184
clang::tidy::ClangTidyCheck::OptionsView::store
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.
Definition: ClangTidyCheck.cpp:58
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:97
clang::tidy::modernize::getValueOfValueInit
static StringRef getValueOfValueInit(const QualType InitType)
Definition: UseDefaultMemberInitCheck.cpp:20
clang::tidy::modernize::isZero
static bool isZero(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:108
clang::tidy::modernize::UseDefaultMemberInitCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: UseDefaultMemberInitCheck.cpp:186
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
UseDefaultMemberInitCheck.h
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:77
E
const Expr * E
Definition: AvoidBindCheck.cpp:82
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidyCheck.cpp:21
clang::tidy::modernize::sameValue
static bool sameValue(const Expr *E1, const Expr *E2)
Definition: UseDefaultMemberInitCheck.cpp:144