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<ImplicitValueInitExpr>([](
const auto *) {
return true; })
31 .Case<ParenExpr>([](
const ParenExpr *PE) {
34 .Case<UnaryOperator>([](
const UnaryOperator *UO) {
37 .Case<BinaryOperator>([](
const BinaryOperator *BO) {
41 .Case<CastExpr>([](
const CastExpr *CE) {
44 .Case<DeclRefExpr>([](
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);
302 initListExpr(anyOf(allOf(initCountIs(1), hasInit(0, allowedInitExpr())),
303 initCountIs(0), hasType(arrayType()))),
306 auto CandidateField = forField(unless(anyOf(
307 getLangOpts().CPlusPlus20 ? unless(anything()) : isBitField(),
308 hasInClassInitializer(anything()), hasParent(recordDecl(isUnion())))));
310 auto DefaultInit = cxxCtorInitializer(CandidateField, withInitializer(Init));
311 auto VisibleDefaultInit =
312 cxxCtorInitializer(DefaultInit, hasOnlyVisibleReferencedDecls())
313 .bind(
"visible-init");
314 ast_matchers::internal::Matcher<CXXCtorInitializer> DefaultInitMatcher =
317 if (!IgnoreNonVisibleReferences) {
318 DefaultInitMatcher = cxxCtorInitializer(anyOf(
320 cxxCtorInitializer(DefaultInit, unless(hasOnlyVisibleReferencedDecls()))
321 .bind(
"non-visible-init")));
325 cxxConstructorDecl(forEachConstructorInitializer(DefaultInitMatcher)),
329 cxxConstructorDecl(forEachConstructorInitializer(
330 cxxCtorInitializer(forField(hasInClassInitializer(anything())),
331 withInitializer(Init))
337 if (
const auto *Default =
338 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"visible-init"))
339 checkDefaultInit(Result, Default,
true);
340 else if (
const auto *DefaultWithoutFix =
341 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"non-visible-init"))
342 checkDefaultInit(Result, DefaultWithoutFix,
false);
343 else if (
const auto *Existing =
344 Result.Nodes.getNodeAs<CXXCtorInitializer>(
"existing"))
345 checkExistingInit(Result, Existing);
347 llvm_unreachable(
"Bad Callback. No node provided.");
350void UseDefaultMemberInitCheck::checkDefaultInit(
351 const MatchFinder::MatchResult &Result,
const CXXCtorInitializer *Init,
353 const FieldDecl *Field = Init->getAnyMember();
357 const auto *ClassDecl = cast<CXXRecordDecl>(Field->getParent());
358 if (llvm::count_if(ClassDecl->decls(), [](
const Decl *D) {
359 if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
360 D = FTD->getTemplatedDecl();
361 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(D))
362 return !Ctor->isCopyOrMoveConstructor();
367 const SourceLocation StartLoc = Field->getBeginLoc();
368 if (StartLoc.isMacroID() && IgnoreMacros)
371 auto DiagDefaultMemberInitializer = [&] {
372 return diag(Field->getLocation(),
"use default member initializer for %0")
377 DiagDefaultMemberInitializer();
378 diag(Init->getLParenLoc(),
379 "move the referenced declaration or definition before the field "
380 "declaration to use a default member initializer",
381 DiagnosticIDs::Note);
385 auto Diag = DiagDefaultMemberInitializer();
387 const SourceLocation FieldEnd =
388 Lexer::getLocForEndOfToken(Field->getSourceRange().getEnd(), 0,
389 *Result.SourceManager, getLangOpts());
390 const SourceLocation LParenEnd = Lexer::getLocForEndOfToken(
391 Init->getLParenLoc(), 0, *Result.SourceManager, getLangOpts());
392 const CharSourceRange InitRange =
393 CharSourceRange::getCharRange(LParenEnd, Init->getRParenLoc());
395 const Expr *InitExpression = Init->getInit();
396 const QualType InitType = InitExpression->getType();
398 const bool ValueInit =
399 isa<ImplicitValueInitExpr>(InitExpression) && !isa<ArrayType>(InitType);
400 const bool CanAssign =
401 UseAssignment && (!ValueInit || !InitType->isEnumeralType());
402 const bool NeedsBraces = !CanAssign || isa<ArrayType>(InitType);
405 Diag << FixItHint::CreateInsertion(FieldEnd,
" = ");
407 Diag << FixItHint::CreateInsertion(FieldEnd,
"{");
409 if (CanAssign && ValueInit)
412 Diag << FixItHint::CreateInsertionFromRange(FieldEnd, InitRange);
415 Diag << FixItHint::CreateInsertion(FieldEnd,
"}");
417 Diag << FixItHint::CreateRemoval(Init->getSourceRange());
420void UseDefaultMemberInitCheck::checkExistingInit(
421 const MatchFinder::MatchResult &Result,
const CXXCtorInitializer *Init) {
422 const FieldDecl *Field = Init->getAnyMember();
424 if (!
sameValue(Field->getInClassInitializer(), Init->getInit()))
427 diag(Init->getSourceLocation(),
"member initializer for %0 is redundant")
428 << 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