xref: /llvm-project/clang-tools-extra/clang-tidy/modernize/UseDefaultMemberInitCheck.cpp (revision 88ccbd75431c4dc9e3e7bb48fba86a264d6ddfa1)
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 
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   case Type::STK_FixedPoint:
64     switch (InitType->getAs<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);
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             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   auto Diag =
259       diag(Field->getLocation(), "use default member initializer for %0")
260       << Field
261       << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{")
262       << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
263 
264   if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit()))
265     Diag << FixItHint::CreateInsertion(
266         FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
267 
268   if (!UseAssignment)
269     Diag << FixItHint::CreateInsertion(FieldEnd, "}");
270 
271   Diag << FixItHint::CreateRemoval(Init->getSourceRange());
272 }
273 
274 void UseDefaultMemberInitCheck::checkExistingInit(
275     const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
276   const FieldDecl *Field = Init->getMember();
277 
278   if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
279     return;
280 
281   diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
282       << Field
283       << FixItHint::CreateRemoval(Init->getSourceRange());
284 }
285 
286 } // namespace modernize
287 } // namespace tidy
288 } // namespace clang
289