clang-tools 6.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
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 }
64 llvm_unreachable("Invalid scalar type kind");
65 }
66
67 static bool isZero(const Expr *E) {
68 switch (E->getStmtClass()) {
69 case Stmt::CXXNullPtrLiteralExprClass:
70 case Stmt::ImplicitValueInitExprClass:
71 return true;
72 case Stmt::InitListExprClass:
73 return cast<InitListExpr>(E)->getNumInits() == 0;
74 case Stmt::CharacterLiteralClass:
75 return !cast<CharacterLiteral>(E)->getValue();
76 case Stmt::CXXBoolLiteralExprClass:
77 return !cast<CXXBoolLiteralExpr>(E)->getValue();
78 case Stmt::IntegerLiteralClass:
79 return !cast<IntegerLiteral>(E)->getValue();
80 case Stmt::FloatingLiteralClass: {
81 llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
82 return Value.isZero() && !Value.isNegative();
83 }
84 default:
85 return false;
86 }
87 }
88
89 static const Expr *ignoreUnaryPlus(const Expr *E) {
90 auto *UnaryOp = dyn_cast<UnaryOperator>(E);
91 if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
92 return UnaryOp->getSubExpr();
93 return E;
94 }
95
96 static const Expr *getInitializer(const Expr *E) {
97 auto *InitList = dyn_cast<InitListExpr>(E);
98 if (InitList && InitList->getNumInits() == 1)
99 return InitList->getInit(0);
100 return E;
101 }
102
103 static bool sameValue(const Expr *E1, const Expr *E2) {
104 E1 = ignoreUnaryPlus(getInitializer(E1->IgnoreParenImpCasts()));
105 E2 = ignoreUnaryPlus(getInitializer(E2->IgnoreParenImpCasts()));
106
107 if (isZero(E1) && isZero(E2))
108 return true;
109
110 if (E1->getStmtClass() != E2->getStmtClass())
111 return false;
112
113 switch (E1->getStmtClass()) {
114 case Stmt::UnaryOperatorClass:
115 return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),
116 cast<UnaryOperator>(E2)->getSubExpr());
117 case Stmt::CharacterLiteralClass:
118 return cast<CharacterLiteral>(E1)->getValue() ==
119 cast<CharacterLiteral>(E2)->getValue();
120 case Stmt::CXXBoolLiteralExprClass:
121 return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
122 cast<CXXBoolLiteralExpr>(E2)->getValue();
123 case Stmt::IntegerLiteralClass:
124 return cast<IntegerLiteral>(E1)->getValue() ==
125 cast<IntegerLiteral>(E2)->getValue();
126 case Stmt::FloatingLiteralClass:
127 return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
128 cast<FloatingLiteral>(E2)->getValue());
129 case Stmt::StringLiteralClass:
130 return cast<StringLiteral>(E1)->getString() ==
131 cast<StringLiteral>(E2)->getString();
132 case Stmt::DeclRefExprClass:
133 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
134 default:
135 return false;
136 }
137 }
138
139 UseDefaultMemberInitCheck::UseDefaultMemberInitCheck(StringRef Name,
140 ClangTidyContext *Context)
141 : ClangTidyCheck(Name, Context),
142 UseAssignment(Options.get("UseAssignment", 0) != 0),
143 IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true) != 0) {}
144
145 void UseDefaultMemberInitCheck::storeOptions(
146 ClangTidyOptions::OptionMap &Opts) {
147 Options.store(Opts, "UseAssignment", UseAssignment);
148 Options.store(Opts, "IgnoreMacros", IgnoreMacros);
149 }
150
151 void UseDefaultMemberInitCheck::registerMatchers(MatchFinder *Finder) {
152 if (!getLangOpts().CPlusPlus11)
153 return;
154
155 auto Init =
156 anyOf(stringLiteral(), characterLiteral(), integerLiteral(),
157 unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
158 hasUnaryOperand(integerLiteral())),
159 floatLiteral(),
160 unaryOperator(anyOf(hasOperatorName("+"), hasOperatorName("-")),
161 hasUnaryOperand(floatLiteral())),
162 cxxBoolLiteral(), cxxNullPtrLiteralExpr(), implicitValueInitExpr(),
163 declRefExpr(to(enumConstantDecl())));
164
165 Finder->addMatcher(
166 cxxConstructorDecl(
167 isDefaultConstructor(), unless(isInstantiated()),
168 forEachConstructorInitializer(
169 cxxCtorInitializer(
170 forField(unless(anyOf(isBitField(),
171 hasInClassInitializer(anything()),
172 hasParent(recordDecl(isUnion()))))),
173 isWritten(), withInitializer(ignoringImplicit(Init)))
174 .bind("default"))),
175 this);
176
177 Finder->addMatcher(
178 cxxConstructorDecl(
179 unless(ast_matchers::isTemplateInstantiation()),
180 forEachConstructorInitializer(
181 cxxCtorInitializer(forField(hasInClassInitializer(anything())),
182 isWritten(),
183 withInitializer(ignoringImplicit(Init)))
184 .bind("existing"))),
185 this);
186 }
187
188 void UseDefaultMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
189 if (const auto *Default =
190 Result.Nodes.getNodeAs<CXXCtorInitializer>("default"))
191 checkDefaultInit(Result, Default);
192 else if (const auto *Existing =
193 Result.Nodes.getNodeAs<CXXCtorInitializer>("existing"))
194 checkExistingInit(Result, Existing);
195 else
196 llvm_unreachable("Bad Callback. No node provided.");
197 }
198
199 void UseDefaultMemberInitCheck::checkDefaultInit(
200 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
201 const FieldDecl *Field = Init->getAnyMember();
202
203 SourceLocation StartLoc = Field->getLocStart();
204 if (StartLoc.isMacroID() && IgnoreMacros)
205 return;
206
207 SourceLocation FieldEnd =
208 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
209 *Result.SourceManager, getLangOpts());
210 SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
211 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
212 CharSourceRange InitRange =
213 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
214
215 auto Diag =
216 diag(Field->getLocation(), "use default member initializer for %0")
217 << Field
218 << FixItHint::CreateInsertion(FieldEnd, UseAssignment ? " = " : "{")
219 << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
220
221 if (UseAssignment && isa<ImplicitValueInitExpr>(Init->getInit()))
222 Diag << FixItHint::CreateInsertion(
223 FieldEnd, getValueOfValueInit(Init->getInit()->getType()));
224
225 if (!UseAssignment)
226 Diag << FixItHint::CreateInsertion(FieldEnd, "}");
227
228 Diag << FixItHint::CreateRemoval(Init->getSourceRange());
229 }
230
231 void UseDefaultMemberInitCheck::checkExistingInit(
232 const MatchFinder::MatchResult &Result, const CXXCtorInitializer *Init) {
233 const FieldDecl *Field = Init->getMember();
234
235 if (!sameValue(Field->getInClassInitializer(), Init->getInit()))
236 return;
237
238 diag(Init->getSourceLocation(), "member initializer for %0 is redundant")
239 << Field
240 << FixItHint::CreateRemoval(Init->getSourceRange());
241 }
242
243 } // namespace modernize
244 } // namespace tidy
245 } // namespace clang
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:188
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::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: ClangTidy.cpp:449
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
clang::tidy::ClangTidyCheck::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:187
clang::tidy::modernize::getInitializer
static const Expr * getInitializer(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:96
clang::tidy::modernize::ignoreUnaryPlus
static const Expr * ignoreUnaryPlus(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:89
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:127
clang::tidy::modernize::UseDefaultMemberInitCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: UseDefaultMemberInitCheck.cpp:151
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:183
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:99
clang::tidy::modernize::getValueOfValueInit
static StringRef getValueOfValueInit(const QualType InitType)
Definition: UseDefaultMemberInitCheck.cpp:21
clang::tidy::modernize::isZero
static bool isZero(const Expr *E)
Definition: UseDefaultMemberInitCheck.cpp:67
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:145
clang
Definition: AndroidTidyModule.cpp:28
UseDefaultMemberInitCheck.h
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:104
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check&#39;s name.
Definition: ClangTidy.cpp:416
clang::tidy::modernize::sameValue
static bool sameValue(const Expr *E1, const Expr *E2)
Definition: UseDefaultMemberInitCheck.cpp:103