150
|
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
|