clang-tools  9.0.0
UseDefaultMemberInitCheck.cpp
Go to the documentation of this file.
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 
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 static StringRef getValueOfValueInit(const QualType InitType) {
21  switch (InitType->getScalarTypeKind()) {
22  case Type::STK_CPointer:
23  case Type::STK_BlockPointer:
24  case Type::STK_ObjCObjectPointer:
25  case Type::STK_MemberPointer:
26  return "nullptr";
27 
28  case Type::STK_Bool:
29  return "false";
30 
31  case Type::STK_Integral:
32  switch (InitType->getAs<BuiltinType>()->getKind()) {
33  case BuiltinType::Char_U:
34  case BuiltinType::UChar:
35  case BuiltinType::Char_S:
36  case BuiltinType::SChar:
37  return "'\\0'";
38  case BuiltinType::WChar_U:
39  case BuiltinType::WChar_S:
40  return "L'\\0'";
41  case BuiltinType::Char16:
42  return "u'\\0'";
43  case BuiltinType::Char32:
44  return "U'\\0'";
45  default:
46  return "0";
47  }
48 
49  case Type::STK_Floating:
50  switch (InitType->getAs<BuiltinType>()->getKind()) {
51  case BuiltinType::Half:
52  case BuiltinType::Float:
53  return "0.0f";
54  default:
55  return "0.0";
56  }
57 
58  case Type::STK_FloatingComplex:
59  case Type::STK_IntegralComplex:
60  return getValueOfValueInit(
61  InitType->getAs<ComplexType>()->getElementType());
62 
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 
188  Options.store(Opts, "UseAssignment", UseAssignment);
189  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
190 }
191 
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  bool ValueInit = isa<ImplicitValueInitExpr>(Init->getInit());
259  bool CanAssign = UseAssignment && (!ValueInit || !Init->getInit()->getType()->isEnumeralType());
260 
261  auto Diag =
262  diag(Field->getLocation(), "use default member initializer for %0")
263  << Field
264  << FixItHint::CreateInsertion(FieldEnd, CanAssign ? " = " : "{")
265  << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
266 
267  if (CanAssign && ValueInit)
268  Diag << FixItHint::CreateInsertion(
269  FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
270 
271  if (!CanAssign)
272  Diag << FixItHint::CreateInsertion(FieldEnd, "}");
273 
274  Diag << FixItHint::CreateRemoval(Init->getSourceRange());
275 }
276 
277 void UseDefaultMemberInitCheck::checkExistingInit(
278  const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
279  const FieldDecl *Field = Init->getAnyMember();
280 
281  if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
282  return;
283 
284  diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
285  << Field
286  << FixItHint::CreateRemoval(Init->getSourceRange());
287 }
288 
289 } // namespace modernize
290 } // namespace tidy
291 } // namespace clang
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static const Expr * getInitializer(const Expr *E)
static const Expr * ignoreUnaryPlus(const Expr *E)
Base class for all clang-tidy checks.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
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.
static constexpr llvm::StringLiteral Name
std::map< std::string, std::string > OptionMap
static StringRef getValueOfValueInit(const QualType InitType)
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.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
static bool sameValue(const Expr *E1, const Expr *E2)