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   llvm_unreachable("Invalid scalar type kind");
65 }
66 
isZero(const Expr * E)67 static 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 
ignoreUnaryPlus(const Expr * E)89 static 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 
getInitializer(const Expr * E)96 static 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 
sameValue(const Expr * E1,const Expr * E2)103 static 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 
UseDefaultMemberInitCheck(StringRef Name,ClangTidyContext * Context)139 UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
140                                                      ClangTidyContext *Context)
141     : ClangTidyCheck(Name, Context),
142       UseAssignment(Options.get("UseAssignment", 0) != 0),
143       IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true) != 0) {}
144 
storeOptions(ClangTidyOptions::OptionMap & Opts)145 void UseDefaultMemberInitCheck::storeOptions(
146     ClangTidyOptions::OptionMap &Opts) {
147   Options.store(Opts, "UseAssignment", UseAssignment);
148   Options.store(Opts, "IgnoreMacros", IgnoreMacros);
149 }
150 
registerMatchers(MatchFinder * Finder)151 void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
152   if (!getLangOpts().CPlusPlus11)
153     return;
154 
155   auto Init =
156       anyOf(stringLiteral(), characterLiteral(), integerLiteral(),
157             unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
158                           hasUnaryOperand(integerLiteral())),
159             floatLiteral(),
160             unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
161                           hasUnaryOperand(floatLiteral())),
162             cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(),
163             declRefExpr(to(enumConstantDecl())));
164 
165   Finder->addMatcher(
166       cxxConstructorDecl(
167           isDefaultConstructor(), unless(isInstantiated()),
168           forEachConstructorInitializer(
169               cxxCtorInitializer(
170                   forField(unless(anyOf(getLangOpts().CPlusPlus2a
171                                             ? unless(anything())
172                                             : isBitField(),
173                                         hasInClassInitializer(anything()),
174                                         hasParent(recordDecl(isUnion()))))),
175                   isWritten(), withInitializer(ignoringImplicit(Init)))
176                   .bind("default"))),
177       this);
178 
179   Finder->addMatcher(
180       cxxConstructorDecl(
181           unless(ast_matchers::isTemplateInstantiation()),
182           forEachConstructorInitializer(
183               cxxCtorInitializer(forField(hasInClassInitializer(anything())),
184                                  isWritten(),
185                                  withInitializer(ignoringImplicit(Init)))
186                   .bind("existing"))),
187       this);
188 }
189 
check(const MatchFinder::MatchResult & Result)190 void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
191   if (const auto *Default =
192           Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))
193     checkDefaultInit(Result, Default);
194   else if (const auto *Existing =
195                Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))
196     checkExistingInit(Result, Existing);
197   else
198     llvm_unreachable("Bad Callback. No node provided.");
199 }
200 
checkDefaultInit(const MatchFinder::MatchResult & Result,const CXXCtorInitializer * Init)201 void UseDefaultMemberInitCheck::checkDefaultInit(
202     const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
203   const FieldDecl *Field = Init->getAnyMember();
204 
205   SourceLocation StartLoc = Field->getLocStart();
206   if (StartLoc.isMacroID() && IgnoreMacros)
207     return;
208 
209   SourceLocation FieldEnd =
210       Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
211                                  *Result.SourceManager, getLangOpts());
212   SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
213       Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
214   CharSourceRange InitRange =
215       CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
216 
217   auto Diag =
218       diag(Field->getLocation(), "use default member initializer for %0")
219       << Field
220       << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{")
221       << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
222 
223   if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit()))
224     Diag << FixItHint::CreateInsertion(
225         FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
226 
227   if (!UseAssignment)
228     Diag << FixItHint::CreateInsertion(FieldEnd, "}");
229 
230   Diag << FixItHint::CreateRemoval(Init->getSourceRange());
231 }
232 
checkExistingInit(const MatchFinder::MatchResult & Result,const CXXCtorInitializer * Init)233 void UseDefaultMemberInitCheck::checkExistingInit(
234     const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
235   const FieldDecl *Field = Init->getMember();
236 
237   if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
238     return;
239 
240   diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
241       << Field
242       << FixItHint::CreateRemoval(Init->getSourceRange());
243 }
244 
245 } // namespace modernize
246 } // namespace tidy
247 } // namespace clang
248