clang-tools  15.0.0git
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 namespace {
21 AST_MATCHER_P(InitListExpr, initCountIs, unsigned, N) {
22  return Node.getNumInits() == N;
23 }
24 } // namespace
25 
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 
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 
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 
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 
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 
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 
194  Options.store(Opts, "UseAssignment", UseAssignment);
195  Options.store(Opts, "IgnoreMacros", IgnoreMacros);
196 }
197 
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(allOf(initCountIs(1), hasInit(0, InitBase)),
211  initCountIs(0))),
212  InitBase);
213 
214  Finder->addMatcher(
215  cxxConstructorDecl(forEachConstructorInitializer(
216  cxxCtorInitializer(
217  forField(unless(anyOf(
218  getLangOpts().CPlusPlus20 ? unless(anything()) : isBitField(),
219  hasInClassInitializer(anything()),
220  hasParent(recordDecl(isUnion()))))),
221  withInitializer(Init))
222  .bind("default"))),
223  this);
224 
225  Finder->addMatcher(
226  cxxConstructorDecl(forEachConstructorInitializer(
227  cxxCtorInitializer(forField(hasInClassInitializer(anything())),
228  withInitializer(Init))
229  .bind("existing"))),
230  this);
231 }
232 
233 void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
234  if (const auto *Default =
235  Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))
236  checkDefaultInit(Result, Default);
237  else if (const auto *Existing =
238  Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))
239  checkExistingInit(Result, Existing);
240  else
241  llvm_unreachable("Bad Callback. No node provided.");
242 }
243 
244 void UseDefaultMemberInitCheck::checkDefaultInit(
245  const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
246  const FieldDecl *Field = Init->getAnyMember();
247 
248  // Check whether we have multiple hand-written constructors and bomb out, as
249  // it is hard to reconcile their sets of member initializers.
250  const auto *ClassDecl = cast<CXXRecordDecl>(Field->getParent());
251  if (llvm::count_if(ClassDecl->ctors(), [](const CXXConstructorDecl *Ctor) {
252  return !Ctor->isCopyOrMoveConstructor();
253  }) > 1)
254  return;
255 
256  SourceLocation StartLoc = Field->getBeginLoc();
257  if (StartLoc.isMacroID() && IgnoreMacros)
258  return;
259 
260  SourceLocation FieldEnd =
261  Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
262  *Result.SourceManager, getLangOpts());
263  SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
264  Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
265  CharSourceRange InitRange =
266  CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
267 
268  bool ValueInit = isa<ImplicitValueInitExpr>(Init->getInit());
269  bool CanAssign = UseAssignment && (!ValueInit || !Init->getInit()->getType()->isEnumeralType());
270 
271  auto Diag =
272  diag(Field->getLocation(), "use default member initializer for %0")
273  << Field
274  << FixItHint::CreateInsertion(FieldEnd, CanAssign ? " = " : "{")
275  << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
276 
277  if (CanAssign && ValueInit)
278  Diag << FixItHint::CreateInsertion(
279  FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
280 
281  if (!CanAssign)
282  Diag << FixItHint::CreateInsertion(FieldEnd, "}");
283 
284  Diag << FixItHint::CreateRemoval(Init->getSourceRange());
285 }
286 
287 void UseDefaultMemberInitCheck::checkExistingInit(
288  const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
289  const FieldDecl *Field = Init->getAnyMember();
290 
291  if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
292  return;
293 
294  diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
295  << Field
296  << FixItHint::CreateRemoval(Init->getSourceRange());
297 }
298 
299 } // namespace modernize
300 } // namespace tidy
301 } // namespace clang
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
clang::tidy::modernize::getInitializer
static const Expr * getInitializer(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:143
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::tidy::modernize::getValueOfValueInit
static StringRef getValueOfValueInit(const QualType InitType)
Definition: UseDefaultMemberInitCheck.cpp:26
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:53
clang::tidy::ClangTidyCheck::getLangOpts
const LangOptions & getLangOpts() const
Returns the language options from the context.
Definition: ClangTidyCheck.h:419
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::abseil::getValue
static double getValue(const IntegerLiteral *IntLit, const FloatingLiteral *FloatLit)
Definition: DurationFactoryScaleCheck.cpp:38
clang::tidy::modernize::UseDefaultMemberInitCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: UseDefaultMemberInitCheck.cpp:192
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:415
clang::tidy::modernize::UseDefaultMemberInitCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: UseDefaultMemberInitCheck.cpp:233
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:66
clang::tidy::modernize::sameValue
static bool sameValue(const Expr *E1, const Expr *E2)
Definition: UseDefaultMemberInitCheck.cpp:150
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
UseDefaultMemberInitCheck.h
clang::tidy::modernize::isZero
static bool isZero(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:114
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::ClangTidyCheck::OptionsView::store
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: ClangTidyCheck.cpp:120
clang::tidy::modernize::UseDefaultMemberInitCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: UseDefaultMemberInitCheck.cpp:198
clang::tidy::modernize::ignoreUnaryPlus
static const Expr * ignoreUnaryPlus(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:136
Field
const FieldDecl * Field
Definition: MemberwiseConstructor.cpp:260
clang::tidy::bugprone::AST_MATCHER_P
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
Definition: EasilySwappableParametersCheck.cpp:1888