blob: eb084efcdfa4fd6eccde8d2a844b6730edb4d497 [file] [log] [blame]
Malcolm Parsonsd5508b42016-12-20 21:26:07 +00001//===--- 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
15using namespace clang::ast_matchers;
16
17namespace clang {
18namespace tidy {
19namespace modernize {
20
21static 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 llvm_unreachable("Invalid scalar type kind");
65}
66
67static bool isZero(const Expr *E) {
68 switch (E->getStmtClass()) {
69 case Stmt::CXXNullPtrLiteralExprClass:
70 case Stmt::ImplicitValueInitExprClass:
71 return true;
72 case Stmt::InitListExprClass:
73 return cast<InitListExpr>(E)->getNumInits() == 0;
74 case Stmt::CharacterLiteralClass:
75 return !cast<CharacterLiteral>(E)->getValue();
76 case Stmt::CXXBoolLiteralExprClass:
77 return !cast<CXXBoolLiteralExpr>(E)->getValue();
78 case Stmt::IntegerLiteralClass:
79 return !cast<IntegerLiteral>(E)->getValue();
80 case Stmt::FloatingLiteralClass: {
81 llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
82 return Value.isZero() && !Value.isNegative();
83 }
84 default:
85 return false;
86 }
87}
88
89static const Expr *ignoreUnaryPlus(const Expr *E) {
90 auto *UnaryOp = dyn_cast<UnaryOperator>(E);
91 if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
92 return UnaryOp->getSubExpr();
93 return E;
94}
95
96static const Expr *getInitializer(const Expr *E) {
97 auto *InitList = dyn_cast<InitListExpr>(E);
98 if (InitList && InitList->getNumInits() == 1)
99 return InitList->getInit(0);
100 return E;
101}
102
103static bool sameValue(const Expr *E1, const Expr *E2) {
104 E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts()));
105 E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts()));
106
107 if (isZero(E1) && isZero(E2))
108 return true;
109
110 if (E1->getStmtClass() != E2->getStmtClass())
111 return false;
112
113 switch (E1->getStmtClass()) {
114 case Stmt::UnaryOperatorClass:
115 return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),
116 cast<UnaryOperator>(E2)->getSubExpr());
117 case Stmt::CharacterLiteralClass:
118 return cast<CharacterLiteral>(E1)->getValue() ==
119 cast<CharacterLiteral>(E2)->getValue();
120 case Stmt::CXXBoolLiteralExprClass:
121 return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
122 cast<CXXBoolLiteralExpr>(E2)->getValue();
123 case Stmt::IntegerLiteralClass:
124 return cast<IntegerLiteral>(E1)->getValue() ==
125 cast<IntegerLiteral>(E2)->getValue();
126 case Stmt::FloatingLiteralClass:
127 return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
128 cast<FloatingLiteral>(E2)->getValue());
129 case Stmt::StringLiteralClass:
130 return cast<StringLiteral>(E1)->getString() ==
131 cast<StringLiteral>(E2)->getString();
132 case Stmt::DeclRefExprClass:
133 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
134 default:
135 return false;
136 }
137}
138
139UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
140 ClangTidyContext *Context)
141 : ClangTidyCheck(Name, Context),
142 UseAssignment(Options.get("UseAssignment", 0) != 0) {}
143
144void UseDefaultMemberInitCheck::storeOptions(
145 ClangTidyOptions::OptionMap &Opts) {
146 Options.store(Opts, "UseAssignment", UseAssignment);
147}
148
149AST_MATCHER(FieldDecl, hasInClassInitializer) {
150 return Node.hasInClassInitializer();
151}
152
153void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
154 if (!getLangOpts().CPlusPlus11)
155 return;
156
157 auto Init =
158 anyOf(stringLiteral(), characterLiteral(), integerLiteral(),
159 unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
160 hasUnaryOperand(integerLiteral())),
161 floatLiteral(),
162 unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
163 hasUnaryOperand(floatLiteral())),
164 cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(),
165 declRefExpr());
166
167 Finder->addMatcher(
168 cxxConstructorDecl(
169 isDefaultConstructor(), unless(isInstantiated()),
170 forEachConstructorInitializer(allOf(
171 forField(unless(anyOf(isBitField(), hasInClassInitializer()))),
172 cxxCtorInitializer(isWritten(),
173 withInitializer(ignoringImplicit(Init)))
174 .bind("default")))),
175 this);
176
177 Finder->addMatcher(
178 cxxConstructorDecl(
179 unless(ast_matchers::isTemplateInstantiation()),
180 forEachConstructorInitializer(
181 allOf(forField(hasInClassInitializer()),
182 cxxCtorInitializer(isWritten(),
183 withInitializer(ignoringImplicit(Init)))
184 .bind("existing")))),
185 this);
186}
187
188void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
189 if (const auto *Default =
190 Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))
191 checkDefaultInit(Result, Default);
192 else if (const auto *Existing =
193 Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))
194 checkExistingInit(Result, Existing);
195 else
196 llvm_unreachable("Bad Callback. No node provided.");
197}
198
199void UseDefaultMemberInitCheck::checkDefaultInit(
200 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
201 const FieldDecl *Field = Init->getMember();
202
203 SourceLocation FieldEnd =
204 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
205 *Result.SourceManager, getLangOpts());
206 SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
207 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
208 CharSourceRange InitRange =
209 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
210
211 auto Diag =
212 diag(Field->getLocation(), "use default member initializer for %0")
213 << Field
214 << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{")
215 << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
216
217 if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit()))
218 Diag << FixItHint::CreateInsertion(
219 FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
220
221 if (!UseAssignment)
222 Diag << FixItHint::CreateInsertion(FieldEnd, "}");
223
224 Diag << FixItHint::CreateRemoval(Init->getSourceRange());
225}
226
227void UseDefaultMemberInitCheck::checkExistingInit(
228 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
229 const FieldDecl *Field = Init->getMember();
230
231 if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
232 return;
233
234 diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
235 << Field
236 << FixItHint::CreateRemoval(Init->getSourceRange());
237}
238
239} // namespace modernize
240} // namespace tidy
241} // namespace clang