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