clang-tools  8.0.0
UseDefaultMemberInitCheck.cpp
Go to the documentation of this file.
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 
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 
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 
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 
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 
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 
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 
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 
189  Options.store(Opts, "UseAssignment", UseAssignment);
190  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
191 }
192 
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 
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 
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 
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
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidy.cpp:473
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
static const Expr * getInitializer(const Expr *E)
static const Expr * ignoreUnaryPlus(const Expr *E)
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
static StringRef getValueOfValueInit(const QualType InitType)
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
static bool isZero(const Expr *E)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:438
static bool sameValue(const Expr *E1, const Expr *E2)