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