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
14using namespace clang::ast_matchers;
15
16namespace clang::tidy::modernize {
17
18namespace {
19AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
20 return Node.getNumInits() == N;
21}
22} // namespace
23
24static StringRef getValueOfValueInit(const QualType InitType) {
25 switch (InitType->getScalarTypeKind()) {
26 case Type::STK_CPointer:
27 case Type::STK_BlockPointer:
28 case Type::STK_ObjCObjectPointer:
29 case Type::STK_MemberPointer:
30 return "nullptr";
31
32 case Type::STK_Bool:
33 return "false";
34
35 case Type::STK_Integral:
36 switch (InitType->castAs<BuiltinType>()->getKind()) {
37 case BuiltinType::Char_U:
38 case BuiltinType::UChar:
39 case BuiltinType::Char_S:
40 case BuiltinType::SChar:
41 return "'\\0'";
42 case BuiltinType::WChar_U:
43 case BuiltinType::WChar_S:
44 return "L'\\0'";
45 case BuiltinType::Char16:
46 return "u'\\0'";
47 case BuiltinType::Char32:
48 return "U'\\0'";
49 default:
50 return "0";
51 }
52
53 case Type::STK_Floating:
54 switch (InitType->castAs<BuiltinType>()->getKind()) {
55 case BuiltinType::Half:
56 case BuiltinType::Float:
57 return "0.0f";
58 default:
59 return "0.0";
60 }
61
62 case Type::STK_FloatingComplex:
63 case Type::STK_IntegralComplex:
64 return getValueOfValueInit(
65 InitType: InitType->castAs<ComplexType>()->getElementType());
66
67 case Type::STK_FixedPoint:
68 switch (InitType->castAs<BuiltinType>()->getKind()) {
69 case BuiltinType::ShortAccum:
70 case BuiltinType::SatShortAccum:
71 return "0.0hk";
72 case BuiltinType::Accum:
73 case BuiltinType::SatAccum:
74 return "0.0k";
75 case BuiltinType::LongAccum:
76 case BuiltinType::SatLongAccum:
77 return "0.0lk";
78 case BuiltinType::UShortAccum:
79 case BuiltinType::SatUShortAccum:
80 return "0.0uhk";
81 case BuiltinType::UAccum:
82 case BuiltinType::SatUAccum:
83 return "0.0uk";
84 case BuiltinType::ULongAccum:
85 case BuiltinType::SatULongAccum:
86 return "0.0ulk";
87 case BuiltinType::ShortFract:
88 case BuiltinType::SatShortFract:
89 return "0.0hr";
90 case BuiltinType::Fract:
91 case BuiltinType::SatFract:
92 return "0.0r";
93 case BuiltinType::LongFract:
94 case BuiltinType::SatLongFract:
95 return "0.0lr";
96 case BuiltinType::UShortFract:
97 case BuiltinType::SatUShortFract:
98 return "0.0uhr";
99 case BuiltinType::UFract:
100 case BuiltinType::SatUFract:
101 return "0.0ur";
102 case BuiltinType::ULongFract:
103 case BuiltinType::SatULongFract:
104 return "0.0ulr";
105 default:
106 llvm_unreachable("Unhandled fixed point BuiltinType");
107 }
108 }
109 llvm_unreachable("Invalid scalar type kind");
110}
111
112static bool isZero(const Expr *E) {
113 switch (E->getStmtClass()) {
114 case Stmt::CXXNullPtrLiteralExprClass:
115 case Stmt::ImplicitValueInitExprClass:
116 return true;
117 case Stmt::InitListExprClass:
118 return cast<InitListExpr>(Val: E)->getNumInits() == 0;
119 case Stmt::CharacterLiteralClass:
120 return !cast<CharacterLiteral>(Val: E)->getValue();
121 case Stmt::CXXBoolLiteralExprClass:
122 return !cast<CXXBoolLiteralExpr>(Val: E)->getValue();
123 case Stmt::IntegerLiteralClass:
124 return !cast<IntegerLiteral>(Val: E)->getValue();
125 case Stmt::FloatingLiteralClass: {
126 llvm::APFloat Value = cast<FloatingLiteral>(Val: E)->getValue();
127 return Value.isZero() && !Value.isNegative();
128 }
129 default:
130 return false;
131 }
132}
133
134static const Expr *ignoreUnaryPlus(const Expr *E) {
135 auto *UnaryOp = dyn_cast<UnaryOperator>(Val: E);
136 if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
137 return UnaryOp->getSubExpr();
138 return E;
139}
140
141static const Expr *getInitializer(const Expr *E) {
142 auto *InitList = dyn_cast<InitListExpr>(Val: E);
143 if (InitList && InitList->getNumInits() == 1)
144 return InitList->getInit(Init: 0)->IgnoreParenImpCasts();
145 return E;
146}
147
148static bool sameValue(const Expr *E1, const Expr *E2) {
149 E1 = ignoreUnaryPlus(E: getInitializer(E: E1->IgnoreParenImpCasts()));
150 E2 = ignoreUnaryPlus(E: getInitializer(E: E2->IgnoreParenImpCasts()));
151
152 if (isZero(E: E1) && isZero(E: E2))
153 return true;
154
155 if (E1->getStmtClass() != E2->getStmtClass())
156 return false;
157
158 switch (E1->getStmtClass()) {
159 case Stmt::UnaryOperatorClass:
160 return sameValue(E1: cast<UnaryOperator>(Val: E1)->getSubExpr(),
161 E2: cast<UnaryOperator>(Val: E2)->getSubExpr());
162 case Stmt::BinaryOperatorClass: {
163 const auto *BinOp1 = cast<BinaryOperator>(Val: E1);
164 const auto *BinOp2 = cast<BinaryOperator>(Val: E2);
165 return BinOp1->getOpcode() == BinOp2->getOpcode() &&
166 sameValue(E1: BinOp1->getLHS(), E2: BinOp2->getLHS()) &&
167 sameValue(E1: BinOp1->getRHS(), E2: BinOp2->getRHS());
168 }
169 case Stmt::CharacterLiteralClass:
170 return cast<CharacterLiteral>(Val: E1)->getValue() ==
171 cast<CharacterLiteral>(Val: E2)->getValue();
172 case Stmt::CXXBoolLiteralExprClass:
173 return cast<CXXBoolLiteralExpr>(Val: E1)->getValue() ==
174 cast<CXXBoolLiteralExpr>(Val: E2)->getValue();
175 case Stmt::IntegerLiteralClass:
176 return cast<IntegerLiteral>(Val: E1)->getValue() ==
177 cast<IntegerLiteral>(Val: E2)->getValue();
178 case Stmt::FloatingLiteralClass:
179 return cast<FloatingLiteral>(Val: E1)->getValue().bitwiseIsEqual(
180 RHS: cast<FloatingLiteral>(Val: E2)->getValue());
181 case Stmt::StringLiteralClass:
182 return cast<StringLiteral>(Val: E1)->getString() ==
183 cast<StringLiteral>(Val: E2)->getString();
184 case Stmt::DeclRefExprClass:
185 return cast<DeclRefExpr>(Val: E1)->getDecl() == cast<DeclRefExpr>(Val: E2)->getDecl();
186 case Stmt::CStyleCastExprClass:
187 case Stmt::CXXStaticCastExprClass:
188 case Stmt::CXXFunctionalCastExprClass:
189 return sameValue(cast<ExplicitCastExpr>(Val: E1)->getSubExpr(),
190 cast<ExplicitCastExpr>(Val: E2)->getSubExpr());
191 default:
192 return false;
193 }
194}
195
196UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
197 ClangTidyContext *Context)
198 : ClangTidyCheck(Name, Context),
199 UseAssignment(Options.get(LocalName: "UseAssignment", Default: false)),
200 IgnoreMacros(Options.getLocalOrGlobal(LocalName: "IgnoreMacros", Default: true)) {}
201
202void UseDefaultMemberInitCheck::storeOptions(
203 ClangTidyOptions::OptionMap &Opts) {
204 Options.store(Options&: Opts, LocalName: "UseAssignment", Value: UseAssignment);
205 Options.store(Options&: Opts, LocalName: "IgnoreMacros", Value: IgnoreMacros);
206}
207
208void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
209 auto NumericLiteral = anyOf(integerLiteral(), floatLiteral());
210 auto UnaryNumericLiteral = unaryOperator(hasAnyOperatorName("+", "-"),
211 hasUnaryOperand(InnerMatcher: NumericLiteral));
212
213 auto ConstExprRef = varDecl(anyOf(isConstexpr(), isStaticStorageClass()));
214 auto ImmutableRef =
215 declRefExpr(to(InnerMatcher: decl(anyOf(enumConstantDecl(), ConstExprRef))));
216
217 auto BinaryNumericExpr = binaryOperator(
218 hasOperands(Matcher1: anyOf(NumericLiteral, ImmutableRef, binaryOperator()),
219 Matcher2: anyOf(NumericLiteral, ImmutableRef, binaryOperator())));
220
221 auto InitBase =
222 anyOf(stringLiteral(), characterLiteral(), NumericLiteral,
223 UnaryNumericLiteral, cxxBoolLiteral(), cxxNullPtrLiteralExpr(),
224 implicitValueInitExpr(), ImmutableRef, BinaryNumericExpr);
225
226 auto ExplicitCastExpr = castExpr(hasSourceExpression(InnerMatcher: InitBase));
227 auto InitMatcher = anyOf(InitBase, ExplicitCastExpr);
228
229 auto Init =
230 anyOf(initListExpr(anyOf(allOf(initCountIs(N: 1), hasInit(N: 0, InnerMatcher: InitMatcher)),
231 initCountIs(N: 0), hasType(InnerMatcher: arrayType()))),
232 InitBase, ExplicitCastExpr);
233
234 Finder->addMatcher(
235 NodeMatch: cxxConstructorDecl(forEachConstructorInitializer(
236 InnerMatcher: cxxCtorInitializer(
237 forField(InnerMatcher: unless(anyOf(
238 getLangOpts().CPlusPlus20 ? unless(anything()) : isBitField(),
239 hasInClassInitializer(InnerMatcher: anything()),
240 hasParent(recordDecl(isUnion()))))),
241 withInitializer(InnerMatcher: Init))
242 .bind(ID: "default"))),
243 Action: this);
244
245 Finder->addMatcher(
246 NodeMatch: cxxConstructorDecl(forEachConstructorInitializer(
247 InnerMatcher: cxxCtorInitializer(forField(InnerMatcher: hasInClassInitializer(InnerMatcher: anything())),
248 withInitializer(InnerMatcher: Init))
249 .bind(ID: "existing"))),
250 Action: this);
251}
252
253void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
254 if (const auto *Default =
255 Result.Nodes.getNodeAs<CXXCtorInitializer>(ID: "default"))
256 checkDefaultInit(Result, Init: Default);
257 else if (const auto *Existing =
258 Result.Nodes.getNodeAs<CXXCtorInitializer>(ID: "existing"))
259 checkExistingInit(Result, Init: Existing);
260 else
261 llvm_unreachable("Bad Callback. No node provided.");
262}
263
264void UseDefaultMemberInitCheck::checkDefaultInit(
265 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
266 const FieldDecl *Field = Init->getAnyMember();
267
268 // Check whether we have multiple hand-written constructors and bomb out, as
269 // it is hard to reconcile their sets of member initializers.
270 const auto *ClassDecl = cast<CXXRecordDecl>(Val: Field->getParent());
271 if (llvm::count_if(ClassDecl->decls(), [](const Decl *D) {
272 if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(Val: D))
273 D = FTD->getTemplatedDecl();
274 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Val: D))
275 return !Ctor->isCopyOrMoveConstructor();
276 return false;
277 }) > 1)
278 return;
279
280 SourceLocation StartLoc = Field->getBeginLoc();
281 if (StartLoc.isMacroID() && IgnoreMacros)
282 return;
283
284 SourceLocation FieldEnd =
285 Lexer::getLocForEndOfToken(Loc: Field->getSourceRange().getEnd(), Offset: 0,
286 SM: *Result.SourceManager, LangOpts: getLangOpts());
287 SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
288 Loc: Init->getLParenLoc(), Offset: 0, SM: *Result.SourceManager, LangOpts: getLangOpts());
289 CharSourceRange InitRange =
290 CharSourceRange::getCharRange(B: LParenEnd, E: Init->getRParenLoc());
291
292 const Expr *InitExpression = Init->getInit();
293 const QualType InitType = InitExpression->getType();
294
295 const bool ValueInit =
296 isa<ImplicitValueInitExpr>(Val: InitExpression) && !isa<ArrayType>(Val: InitType);
297 const bool CanAssign =
298 UseAssignment && (!ValueInit || !InitType->isEnumeralType());
299 const bool NeedsBraces = !CanAssign || isa<ArrayType>(Val: InitType);
300
301 auto Diag =
302 diag(Field->getLocation(), "use default member initializer for %0")
303 << Field;
304
305 if (CanAssign)
306 Diag << FixItHint::CreateInsertion(InsertionLoc: FieldEnd, Code: " = ");
307 if (NeedsBraces)
308 Diag << FixItHint::CreateInsertion(InsertionLoc: FieldEnd, Code: "{");
309
310 if (CanAssign && ValueInit)
311 Diag << FixItHint::CreateInsertion(InsertionLoc: FieldEnd, Code: getValueOfValueInit(InitType));
312 else
313 Diag << FixItHint::CreateInsertionFromRange(InsertionLoc: FieldEnd, FromRange: InitRange);
314
315 if (NeedsBraces)
316 Diag << FixItHint::CreateInsertion(InsertionLoc: FieldEnd, Code: "}");
317
318 Diag << FixItHint::CreateRemoval(RemoveRange: Init->getSourceRange());
319}
320
321void UseDefaultMemberInitCheck::checkExistingInit(
322 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
323 const FieldDecl *Field = Init->getAnyMember();
324
325 if (!sameValue(E1: Field->getInClassInitializer(), E2: Init->getInit()))
326 return;
327
328 diag(Loc: Init->getSourceLocation(), Description: "member initializer for %0 is redundant")
329 << Field << FixItHint::CreateRemoval(RemoveRange: Init->getSourceRange());
330}
331
332} // namespace clang::tidy::modernize
333

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp