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/Basic/DiagnosticIDs.h"
15#include "clang/Lex/Lexer.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/TypeSwitch.h"
26 return llvm::TypeSwitch<const Expr *, bool>(E)
27 .Case<IntegerLiteral, FloatingLiteral, CXXBoolLiteralExpr,
28 CXXNullPtrLiteralExpr, CharacterLiteral, StringLiteral>(
29 [](
const auto *) {
return true; })
30 .Case([](
const ImplicitValueInitExpr *) {
return true; })
31 .Case([](
const ParenExpr *PE) {
34 .Case([](
const UnaryOperator *UO) {
37 .Case([](
const BinaryOperator *BO) {
41 .Case([](
const CastExpr *CE) {
44 .Case([](
const DeclRefExpr *DRE) {
45 if (
const ValueDecl *D = DRE->getDecl()) {
46 if (isa<EnumConstantDecl>(D))
48 if (
const auto *VD = dyn_cast<VarDecl>(D))
49 return VD->isConstexpr() || VD->getStorageClass() == SC_Static;
57 const FieldDecl *Field,
58 const SourceManager &SM) {
62 const auto *FieldClass = cast<CXXRecordDecl>(Field->getParent());
63 for (
const DeclContext *DC = ND->getDeclContext(); DC; DC = DC->getParent())
64 if (DC->Equals(FieldClass))
67 const SourceLocation DeclLoc = SM.getExpansionLoc(ND->getLocation());
68 const SourceLocation FieldLoc = SM.getExpansionLoc(Field->getLocation());
69 if (DeclLoc.isInvalid() || FieldLoc.isInvalid())
72 return DeclLoc == FieldLoc || SM.isBeforeInTranslationUnit(DeclLoc, FieldLoc);
76 const FieldDecl *Field,
77 const SourceManager &SM) {
81 if (
const auto *DRE = dyn_cast<DeclRefExpr>(S);
87 for (
const Stmt *Child : S->children())
96AST_MATCHER_P(InitListExpr, initCountIs,
unsigned, N) {
97 return Node.getNumInits() == N;
102AST_MATCHER(CXXCtorInitializer, hasOnlyVisibleReferencedDecls) {
103 const FieldDecl *Field = Node.getAnyMember();
107 const SourceManager &SM = Finder->getASTContext().getSourceManager();
114 switch (InitType->getScalarTypeKind()) {
115 case Type::STK_CPointer:
116 case Type::STK_BlockPointer:
117 case Type::STK_ObjCObjectPointer:
118 case Type::STK_MemberPointer:
124 case Type::STK_Integral:
125 switch (InitType->castAs<BuiltinType>()->getKind()) {
126 case BuiltinType::Char_U:
127 case BuiltinType::UChar:
128 case BuiltinType::Char_S:
129 case BuiltinType::SChar:
131 case BuiltinType::WChar_U:
132 case BuiltinType::WChar_S:
134 case BuiltinType::Char16:
136 case BuiltinType::Char32:
142 case Type::STK_Floating:
143 switch (InitType->castAs<BuiltinType>()->getKind()) {
144 case BuiltinType::Half:
145 case BuiltinType::Float:
151 case Type::STK_FloatingComplex:
152 case Type::STK_IntegralComplex:
154 InitType->castAs<ComplexType>()->getElementType());
156 case Type::STK_FixedPoint:
157 switch (InitType->castAs<BuiltinType>()->getKind()) {
158 case BuiltinType::ShortAccum:
159 case BuiltinType::SatShortAccum:
161 case BuiltinType::Accum:
162 case BuiltinType::SatAccum:
164 case BuiltinType::LongAccum:
165 case BuiltinType::SatLongAccum:
167 case BuiltinType::UShortAccum:
168 case BuiltinType::SatUShortAccum:
170 case BuiltinType::UAccum:
171 case BuiltinType::SatUAccum:
173 case BuiltinType::ULongAccum:
174 case BuiltinType::SatULongAccum:
176 case BuiltinType::ShortFract:
177 case BuiltinType::SatShortFract:
179 case BuiltinType::Fract:
180 case BuiltinType::SatFract:
182 case BuiltinType::LongFract:
183 case BuiltinType::SatLongFract:
185 case BuiltinType::UShortFract:
186 case BuiltinType::SatUShortFract:
188 case BuiltinType::UFract:
189 case BuiltinType::SatUFract:
191 case BuiltinType::ULongFract:
192 case BuiltinType::SatULongFract:
195 llvm_unreachable(
"Unhandled fixed point BuiltinType");
198 llvm_unreachable(
"Invalid scalar type kind");
202 switch (E->getStmtClass()) {
203 case Stmt::CXXNullPtrLiteralExprClass:
204 case Stmt::ImplicitValueInitExprClass:
206 case Stmt::InitListExprClass:
207 return cast<InitListExpr>(E)->getNumInits() == 0;
208 case Stmt::CharacterLiteralClass:
209 return !cast<CharacterLiteral>(E)->getValue();
210 case Stmt::CXXBoolLiteralExprClass:
211 return !cast<CXXBoolLiteralExpr>(E)->getValue();
212 case Stmt::IntegerLiteralClass:
213 return !cast<IntegerLiteral>(E)->getValue();
214 case Stmt::FloatingLiteralClass: {
215 const llvm::APFloat Value = cast<FloatingLiteral>(E)->getValue();
216 return Value.isZero() && !Value.isNegative();
224 auto *UnaryOp = dyn_cast<UnaryOperator>(E);
225 if (UnaryOp && UnaryOp->getOpcode() == UO_Plus)
226 return UnaryOp->getSubExpr();
231 auto *InitList = dyn_cast<InitListExpr>(E);
232 if (InitList && InitList->getNumInits() == 1)
233 return InitList->getInit(0)->IgnoreParenImpCasts();
244 if (E1->getStmtClass() != E2->getStmtClass())
247 switch (E1->getStmtClass()) {
248 case Stmt::UnaryOperatorClass:
249 return sameValue(cast<UnaryOperator>(E1)->getSubExpr(),
250 cast<UnaryOperator>(E2)->getSubExpr());
251 case Stmt::BinaryOperatorClass: {
252 const auto *BinOp1 = cast<BinaryOperator>(E1);
253 const auto *BinOp2 = cast<BinaryOperator>(E2);
254 return BinOp1->getOpcode() == BinOp2->getOpcode() &&
255 sameValue(BinOp1->getLHS(), BinOp2->getLHS()) &&
256 sameValue(BinOp1->getRHS(), BinOp2->getRHS());
258 case Stmt::CharacterLiteralClass:
259 return cast<CharacterLiteral>(E1)->getValue() ==
260 cast<CharacterLiteral>(E2)->getValue();
261 case Stmt::CXXBoolLiteralExprClass:
262 return cast<CXXBoolLiteralExpr>(E1)->getValue() ==
263 cast<CXXBoolLiteralExpr>(E2)->getValue();
264 case Stmt::IntegerLiteralClass:
265 return cast<IntegerLiteral>(E1)->getValue() ==
266 cast<IntegerLiteral>(E2)->getValue();
267 case Stmt::FloatingLiteralClass:
268 return cast<FloatingLiteral>(E1)->getValue().bitwiseIsEqual(
269 cast<FloatingLiteral>(E2)->getValue());
270 case Stmt::StringLiteralClass:
271 return cast<StringLiteral>(E1)->getString() ==
272 cast<StringLiteral>(E2)->getString();
273 case Stmt::DeclRefExprClass:
274 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
275 case Stmt::CStyleCastExprClass:
276 case Stmt::CXXStaticCastExprClass:
277 case Stmt::CXXFunctionalCastExprClass:
278 return sameValue(cast<ExplicitCastExpr>(E1)->getSubExpr(),
279 cast<ExplicitCastExpr>(E2)->getSubExpr());
288 UseAssignment(Options.get(
"UseAssignment", false)),
289 IgnoreMacros(Options.get(
"IgnoreMacros", true)),
290 IgnoreNonVisibleReferences(
291 Options.get(
"IgnoreNonVisibleReferences", true)) {}
295 Options.store(Opts,
"UseAssignment", UseAssignment);
296 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
297 Options.store(Opts,
"IgnoreNonVisibleReferences", IgnoreNonVisibleReferences);
301 const auto Init = anyOf(
302 initListExpr(anyOf(allOf(initCountIs(1), hasInit(0, allowedInitExpr())),
303 initCountIs(0), hasType(arrayType()))),
306 const auto CandidateField = forField(unless(anyOf(
307 getLangOpts().CPlusPlus20 ? unless(anything()) : isBitField(),
308 hasInClassInitializer(anything()), hasParent(recordDecl(isUnion())))));
310 const auto DefaultInit =
311 cxxCtorInitializer(CandidateField, withInitializer(Init));
312 auto VisibleDefaultInit =
313 cxxCtorInitializer(DefaultInit, hasOnlyVisibleReferencedDecls())
314 .bind(
"visible-init");
315 ast_matchers::internal::Matcher<CXXCtorInitializer> DefaultInitMatcher =
318 if (!IgnoreNonVisibleReferences) {
319 DefaultInitMatcher = cxxCtorInitializer(anyOf(
321 cxxCtorInitializer(DefaultInit, unless(hasOnlyVisibleReferencedDecls()))
322 .bind(
"non-visible-init")));
326 cxxConstructorDecl(forEachConstructorInitializer(DefaultInitMatcher)),
330 cxxConstructorDecl(forEachConstructorInitializer(
331 cxxCtorInitializer(forField(hasInClassInitializer(anything())),
332 withInitializer(Init))
338 if (
const auto *Default =
339 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"visible-init"))
340 checkDefaultInit(Result, Default,
true);
341 else if (
const auto *DefaultWithoutFix =
342 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"non-visible-init"))
343 checkDefaultInit(Result, DefaultWithoutFix,
false);
344 else if (
const auto *Existing =
345 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"existing"))
346 checkExistingInit(Result, Existing);
348 llvm_unreachable(
"Bad Callback. No node provided.");
351void UseDefaultMemberInitCheck::checkDefaultInit(
352 const MatchFinder::MatchResult &Result,
const CXXCtorInitializer *Init,
354 const FieldDecl *Field = Init->getAnyMember();
358 const auto *ClassDecl = cast<CXXRecordDecl>(Field->getParent());
359 if (llvm::count_if(ClassDecl->decls(), [](
const Decl *D) {
360 if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
361 D = FTD->getTemplatedDecl();
362 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
363 return !Ctor->isCopyOrMoveConstructor();
368 const SourceLocation StartLoc = Field->getBeginLoc();
369 if (StartLoc.isMacroID() && IgnoreMacros)
372 const auto DiagDefaultMemberInitializer = [&] {
373 return diag(Field->getLocation(),
"use default member initializer for %0")
378 DiagDefaultMemberInitializer();
379 diag(Init->getLParenLoc(),
380 "move the referenced declaration or definition before the field "
381 "declaration to use a default member initializer",
382 DiagnosticIDs::Note);
386 const auto Diag = DiagDefaultMemberInitializer();
388 const SourceLocation FieldEnd =
389 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
390 *Result.SourceManager, getLangOpts());
391 const SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
392 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
393 const CharSourceRange InitRange =
394 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
396 const Expr *InitExpression = Init->getInit();
397 const QualType InitType = InitExpression->getType();
399 const bool ValueInit =
400 isa<ImplicitValueInitExpr>(InitExpression) && !isa<ArrayType>(InitType);
401 const bool CanAssign =
402 UseAssignment && (!ValueInit || !InitType->isEnumeralType());
403 const bool NeedsBraces = !CanAssign || isa<ArrayType>(InitType);
406 Diag << FixItHint::CreateInsertion(FieldEnd,
" = ");
408 Diag << FixItHint::CreateInsertion(FieldEnd,
"{");
410 if (CanAssign && ValueInit)
413 Diag << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
416 Diag << FixItHint::CreateInsertion(FieldEnd,
"}");
418 Diag << FixItHint::CreateRemoval(Init->getSourceRange());
421void UseDefaultMemberInitCheck::checkExistingInit(
422 const MatchFinder::MatchResult &Result,
const CXXCtorInitializer *Init) {
423 const FieldDecl *Field = Init->getAnyMember();
425 if (!
sameValue(Field->getInClassInitializer(), Init->getInit()))
428 diag(Init->getSourceLocation(),
"member initializer for %0 is redundant")
429 << 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 isVisibleFromDefaultMemberInitializer(const NamedDecl *ND, const FieldDecl *Field, const SourceManager &SM)
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)
static const DeclRefExpr * findFirstNonVisibleDeclRef(const Stmt *S, const FieldDecl *Field, const SourceManager &SM)
llvm::StringMap< ClangTidyValue > OptionMap