10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Expr.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/ASTMatchers/ASTMatchers.h"
14#include "clang/Lex/Lexer.h"
15#include "llvm/ADT/TypeSwitch.h"
24 return llvm::TypeSwitch<const Expr *, bool>(E)
25 .Case<IntegerLiteral, FloatingLiteral, CXXBoolLiteralExpr,
26 CXXNullPtrLiteralExpr, CharacterLiteral, StringLiteral>(
27 [](
const auto *) {
return true; })
28 .Case<ImplicitValueInitExpr>([](
const auto *) {
return true; })
29 .Case<ParenExpr>([](
const ParenExpr *PE) {
32 .Case<UnaryOperator>([](
const UnaryOperator *UO) {
35 .Case<BinaryOperator>([](
const BinaryOperator *BO) {
39 .Case<CastExpr>([](
const CastExpr *CE) {
42 .Case<DeclRefExpr>([](
const DeclRefExpr *DRE) {
43 if (
const ValueDecl *D = DRE->getDecl()) {
44 if (isa<EnumConstantDecl>(D))
46 if (
const auto *VD = dyn_cast<VarDecl>(D))
47 return VD->isConstexpr() || VD->getStorageClass() == SC_Static;
56AST_MATCHER_P(InitListExpr, initCountIs,
unsigned, N) {
57 return Node.getNumInits() == N;
65 switch (InitType->getScalarTypeKind()) {
66 case Type::STK_CPointer:
67 case Type::STK_BlockPointer:
68 case Type::STK_ObjCObjectPointer:
69 case Type::STK_MemberPointer:
75 case Type::STK_Integral:
76 switch (InitType->castAs<BuiltinType>()->getKind()) {
77 case BuiltinType::Char_U:
78 case BuiltinType::UChar:
79 case BuiltinType::Char_S:
80 case BuiltinType::SChar:
82 case BuiltinType::WChar_U:
83 case BuiltinType::WChar_S:
85 case BuiltinType::Char16:
87 case BuiltinType::Char32:
93 case Type::STK_Floating:
94 switch (InitType->castAs<BuiltinType>()->getKind()) {
95 case BuiltinType::Half:
96 case BuiltinType::Float:
102 case Type::STK_FloatingComplex:
103 case Type::STK_IntegralComplex:
105 InitType->castAs<ComplexType>()->getElementType());
107 case Type::STK_FixedPoint:
108 switch (InitType->castAs<BuiltinType>()->getKind()) {
109 case BuiltinType::ShortAccum:
110 case BuiltinType::SatShortAccum:
112 case BuiltinType::Accum:
113 case BuiltinType::SatAccum:
115 case BuiltinType::LongAccum:
116 case BuiltinType::SatLongAccum:
118 case BuiltinType::UShortAccum:
119 case BuiltinType::SatUShortAccum:
121 case BuiltinType::UAccum:
122 case BuiltinType::SatUAccum:
124 case BuiltinType::ULongAccum:
125 case BuiltinType::SatULongAccum:
127 case BuiltinType::ShortFract:
128 case BuiltinType::SatShortFract:
130 case BuiltinType::Fract:
131 case BuiltinType::SatFract:
133 case BuiltinType::LongFract:
134 case BuiltinType::SatLongFract:
136 case BuiltinType::UShortFract:
137 case BuiltinType::SatUShortFract:
139 case BuiltinType::UFract:
140 case BuiltinType::SatUFract:
142 case BuiltinType::ULongFract:
143 case BuiltinType::SatULongFract:
146 llvm_unreachable(
"Unhandled fixed point BuiltinType");
149 llvm_unreachable(
"Invalid scalar type kind");
153 switch (E->getStmtClass()) {
154 case Stmt::CXXNullPtrLiteralExprClass:
155 case Stmt::ImplicitValueInitExprClass:
157 case Stmt::InitListExprClass:
158 return cast<InitListExpr>(E)->getNumInits() == 0;
159 case Stmt::CharacterLiteralClass:
160 return !cast<CharacterLiteral>(E)->getValue();
161 case Stmt::CXXBoolLiteralExprClass:
162 return !cast<CXXBoolLiteralExpr>(E)->getValue();
163 case Stmt::IntegerLiteralClass:
164 return !cast<IntegerLiteral>(E)->getValue();
165 case Stmt::FloatingLiteralClass: {
166 llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
167 return Value.isZero() && !Value.isNegative();
175 auto *UnaryOp = dyn_cast<UnaryOperator>(E);
176 if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
177 return UnaryOp->getSubExpr();
182 auto *InitList = dyn_cast<InitListExpr>(E);
183 if (InitList && InitList->getNumInits() == 1)
184 return InitList->getInit(0)->IgnoreParenImpCasts();
195 if (E1->getStmtClass() != E2->getStmtClass())
198 switch (E1->getStmtClass()) {
199 case Stmt::UnaryOperatorClass:
200 return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),
201 cast<UnaryOperator>(E2)->getSubExpr());
202 case Stmt::BinaryOperatorClass: {
203 const auto *BinOp1 = cast<BinaryOperator>(E1);
204 const auto *BinOp2 = cast<BinaryOperator>(E2);
205 return BinOp1->getOpcode() == BinOp2->getOpcode() &&
206 sameValue(BinOp1->getLHS(), BinOp2->getLHS()) &&
207 sameValue(BinOp1->getRHS(), BinOp2->getRHS());
209 case Stmt::CharacterLiteralClass:
210 return cast<CharacterLiteral>(E1)->getValue() ==
211 cast<CharacterLiteral>(E2)->getValue();
212 case Stmt::CXXBoolLiteralExprClass:
213 return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
214 cast<CXXBoolLiteralExpr>(E2)->getValue();
215 case Stmt::IntegerLiteralClass:
216 return cast<IntegerLiteral>(E1)->getValue() ==
217 cast<IntegerLiteral>(E2)->getValue();
218 case Stmt::FloatingLiteralClass:
219 return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
220 cast<FloatingLiteral>(E2)->getValue());
221 case Stmt::StringLiteralClass:
222 return cast<StringLiteral>(E1)->getString() ==
223 cast<StringLiteral>(E2)->getString();
224 case Stmt::DeclRefExprClass:
225 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
226 case Stmt::CStyleCastExprClass:
227 case Stmt::CXXStaticCastExprClass:
228 case Stmt::CXXFunctionalCastExprClass:
229 return sameValue(cast<ExplicitCastExpr>(E1)->getSubExpr(),
230 cast<ExplicitCastExpr>(E2)->getSubExpr());
239 UseAssignment(Options.get(
"UseAssignment", false)),
240 IgnoreMacros(Options.get(
"IgnoreMacros", true)) {}
244 Options.store(Opts,
"UseAssignment", UseAssignment);
245 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
250 initListExpr(anyOf(allOf(initCountIs(1), hasInit(0, allowedInitExpr())),
251 initCountIs(0), hasType(arrayType()))),
255 cxxConstructorDecl(forEachConstructorInitializer(
257 forField(unless(anyOf(
258 getLangOpts().CPlusPlus20 ? unless(anything()) : isBitField(),
259 hasInClassInitializer(anything()),
260 hasParent(recordDecl(isUnion()))))),
261 withInitializer(Init))
266 cxxConstructorDecl(forEachConstructorInitializer(
267 cxxCtorInitializer(forField(hasInClassInitializer(anything())),
268 withInitializer(Init))
274 if (
const auto *Default =
275 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"default"))
276 checkDefaultInit(Result, Default);
277 else if (
const auto *Existing =
278 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"existing"))
279 checkExistingInit(Result, Existing);
281 llvm_unreachable(
"Bad Callback. No node provided.");
284void UseDefaultMemberInitCheck::checkDefaultInit(
285 const MatchFinder::MatchResult &Result,
const CXXCtorInitializer *Init) {
286 const FieldDecl *Field = Init->getAnyMember();
290 const auto *ClassDecl = cast<CXXRecordDecl>(Field->getParent());
291 if (llvm::count_if(ClassDecl->decls(), [](
const Decl *D) {
292 if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
293 D = FTD->getTemplatedDecl();
294 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
295 return !Ctor->isCopyOrMoveConstructor();
300 SourceLocation StartLoc = Field->getBeginLoc();
301 if (StartLoc.isMacroID() && IgnoreMacros)
304 SourceLocation FieldEnd =
305 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
306 *Result.SourceManager, getLangOpts());
307 SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
308 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
309 CharSourceRange InitRange =
310 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
312 const Expr *InitExpression = Init->getInit();
313 const QualType InitType = InitExpression->getType();
315 const bool ValueInit =
316 isa<ImplicitValueInitExpr>(InitExpression) && !isa<ArrayType>(InitType);
317 const bool CanAssign =
318 UseAssignment && (!ValueInit || !InitType->isEnumeralType());
319 const bool NeedsBraces = !CanAssign || isa<ArrayType>(InitType);
322 diag(Field->getLocation(),
"use default member initializer for %0")
326 Diag << FixItHint::CreateInsertion(FieldEnd,
" = ");
328 Diag << FixItHint::CreateInsertion(FieldEnd,
"{");
330 if (CanAssign && ValueInit)
333 Diag << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
336 Diag << FixItHint::CreateInsertion(FieldEnd,
"}");
338 Diag << FixItHint::CreateRemoval(Init->getSourceRange());
341void UseDefaultMemberInitCheck::checkExistingInit(
342 const MatchFinder::MatchResult &Result,
const CXXCtorInitializer *Init) {
343 const FieldDecl *Field = Init->getAnyMember();
345 if (!
sameValue(Field->getInClassInitializer(), Init->getInit()))
348 diag(Init->getSourceLocation(),
"member initializer for %0 is redundant")
349 << Field << FixItHint::CreateRemoval(Init->getSourceRange());
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
UseDefaultMemberInitCheck(StringRef Name, ClangTidyContext *Context)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
static bool sameValue(const Expr *E1, const Expr *E2)
static const Expr * getInitializer(const Expr *E)
static bool isZero(const Expr *E)
static StringRef getValueOfValueInit(const QualType InitType)
static bool isExprAllowedInMemberInit(const Expr *E)
static const Expr * ignoreUnaryPlus(const Expr *E)
llvm::StringMap< ClangTidyValue > OptionMap