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