xref: /llvm-project/clang-tools-extra/clang-tidy/readability/RedundantMemberInitCheck.cpp (revision 461dcd4a000fa2b88759a275bc6803b89efc5972)
1 //===--- RedundantMemberInitCheck.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 
9 #include "RedundantMemberInitCheck.h"
10 #include "../utils/LexerUtils.h"
11 #include "../utils/Matchers.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/Lex/Lexer.h"
15 #include <algorithm>
16 
17 using namespace clang::ast_matchers;
18 using namespace clang::tidy::matchers;
19 
20 namespace clang::tidy::readability {
21 
22 static SourceRange
getFullInitRangeInclWhitespaces(SourceRange Range,const SourceManager & SM,const LangOptions & LangOpts)23 getFullInitRangeInclWhitespaces(SourceRange Range, const SourceManager &SM,
24                                 const LangOptions &LangOpts) {
25   const Token PrevToken =
26       utils::lexer::getPreviousToken(Range.getBegin(), SM, LangOpts, false);
27   if (PrevToken.is(tok::unknown))
28     return Range;
29 
30   if (PrevToken.isNot(tok::equal))
31     return {PrevToken.getEndLoc(), Range.getEnd()};
32 
33   return getFullInitRangeInclWhitespaces(
34       {PrevToken.getLocation(), Range.getEnd()}, SM, LangOpts);
35 }
36 
storeOptions(ClangTidyOptions::OptionMap & Opts)37 void RedundantMemberInitCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
38   Options.store(Opts, "IgnoreBaseInCopyConstructors",
39                 IgnoreBaseInCopyConstructors);
40 }
41 
registerMatchers(MatchFinder * Finder)42 void RedundantMemberInitCheck::registerMatchers(MatchFinder *Finder) {
43   auto ConstructorMatcher =
44       cxxConstructExpr(
45           argumentCountIs(0),
46           hasDeclaration(cxxConstructorDecl(
47               ofClass(cxxRecordDecl(unless(isTriviallyDefaultConstructible()))
48                           .bind("class")))))
49           .bind("construct");
50 
51   auto HasUnionAsParent = hasParent(recordDecl(isUnion()));
52 
53   auto HasTypeEqualToConstructorClass = hasType(qualType(
54       hasCanonicalType(qualType(hasDeclaration(equalsBoundNode("class"))))));
55 
56   Finder->addMatcher(
57       cxxConstructorDecl(
58           unless(isDelegatingConstructor()), ofClass(unless(isUnion())),
59           forEachConstructorInitializer(
60               cxxCtorInitializer(
61                   withInitializer(ConstructorMatcher),
62                   anyOf(isBaseInitializer(),
63                         forField(fieldDecl(unless(hasType(isConstQualified())),
64                                            unless(HasUnionAsParent),
65                                            HasTypeEqualToConstructorClass))))
66                   .bind("init")))
67           .bind("constructor"),
68       this);
69 
70   Finder->addMatcher(fieldDecl(hasInClassInitializer(ConstructorMatcher),
71                                HasTypeEqualToConstructorClass,
72                                unless(HasUnionAsParent))
73                          .bind("field"),
74                      this);
75 }
76 
check(const MatchFinder::MatchResult & Result)77 void RedundantMemberInitCheck::check(const MatchFinder::MatchResult &Result) {
78   const auto *Construct = Result.Nodes.getNodeAs<CXXConstructExpr>("construct");
79 
80   if (const auto *Field = Result.Nodes.getNodeAs<FieldDecl>("field")) {
81     const Expr *Init = Field->getInClassInitializer();
82     diag(Construct->getExprLoc(), "initializer for member %0 is redundant")
83         << Field
84         << FixItHint::CreateRemoval(getFullInitRangeInclWhitespaces(
85                Init->getSourceRange(), *Result.SourceManager, getLangOpts()));
86     return;
87   }
88 
89   const auto *Init = Result.Nodes.getNodeAs<CXXCtorInitializer>("init");
90   const auto *ConstructorDecl =
91       Result.Nodes.getNodeAs<CXXConstructorDecl>("constructor");
92 
93   if (IgnoreBaseInCopyConstructors && ConstructorDecl->isCopyConstructor() &&
94       Init->isBaseInitializer())
95     return;
96 
97   if (Init->isAnyMemberInitializer()) {
98     diag(Init->getSourceLocation(), "initializer for member %0 is redundant")
99         << Init->getAnyMember()
100         << FixItHint::CreateRemoval(Init->getSourceRange());
101   } else {
102     diag(Init->getSourceLocation(),
103          "initializer for base class %0 is redundant")
104         << Construct->getType()
105         << FixItHint::CreateRemoval(Init->getSourceRange());
106   }
107 }
108 
109 } // namespace clang::tidy::readability
110