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