22 bool HasQualifier =
false;
28 const LangOptions &LangOpts) {
29 if (Range.isInvalid())
38 const auto MakeTypeLocInfo = [](
auto TypeTL) {
39 const bool HasQualifier =
40 static_cast<bool>(TypeTL.getQualifierLoc().getNestedNameSpecifier());
41 return TypeLocInfo{TypeTL, HasQualifier};
44 if (
const auto TypedefTL = TL.getAs<TypedefTypeLoc>())
45 return MakeTypeLocInfo(TypedefTL);
47 if (
const auto TagTL = TL.getAs<TagTypeLoc>())
48 return MakeTypeLocInfo(TagTL);
54 if (
const auto TypedefTL = TL.getAs<TypedefTypeLoc>())
55 return TypedefTL.getDecl();
56 if (
const auto TagTL = TL.getAs<TagTypeLoc>())
57 return TagTL.getDecl();
62 return LHS->getName() == RHS->getName();
66 return isa<TranslationUnitDecl, NamespaceDecl>(DC);
70 const NamedDecl *Target) {
71 const DeclContext *AliasContext = Alias->getDeclContext()->getRedeclContext();
72 const DeclContext *TargetContext =
73 Target->getDeclContext()->getRedeclContext();
75 const auto *AliasRecord = dyn_cast<CXXRecordDecl>(AliasContext);
79 const auto *TargetRecord = dyn_cast<CXXRecordDecl>(TargetContext);
80 return TargetRecord && AliasRecord->isDerivedFrom(TargetRecord);
84 const LangOptions &LangOpts) {
85 const SourceLocation TypeEndLoc = TL.getEndLoc();
86 if (TypeEndLoc.isInvalid() || TypeEndLoc.isMacroID())
88 const std::optional<Token> NextToken =
90 return !NextToken || NextToken->isNot(tok::semi);
95AST_MATCHER(TypeAliasDecl, isAliasTemplate) {
96 return Node.getDescribedAliasTemplate() !=
nullptr;
99AST_MATCHER(NamedDecl, isInMacro) {
return Node.getLocation().isMacroID(); }
101AST_MATCHER(TypeAliasDecl, hasAliasAttributes) {
104 const TypeSourceInfo *TSI = Node.getTypeSourceInfo();
107 for (TypeLoc CurTL = TSI->getTypeLoc(); !CurTL.isNull();
108 CurTL = CurTL.getNextTypeLoc())
109 if (CurTL.getAs<AttributedTypeLoc>())
115 return !Node.getType().isNull() && !Node.getType()->isDependentType();
119 const auto IsNonElaboratedTypeLoc = [](
auto TL) {
120 return !TL.isNull() && !TL.getElaboratedKeywordLoc().isValid();
122 return IsNonElaboratedTypeLoc(Node.getAs<TypedefTypeLoc>()) ||
123 IsNonElaboratedTypeLoc(Node.getAs<TagTypeLoc>());
127 const ASTContext &Context = Finder->getASTContext();
128 return !
hasMacroInRange(Node.getSourceRange(), Context.getSourceManager(),
129 Context.getLangOpts());
132AST_MATCHER(TypeLoc, hasNoTrailingSyntaxAfterTypeLoc) {
133 const ASTContext &Context = Finder->getASTContext();
135 Context.getLangOpts());
138AST_MATCHER(TypeAliasDecl, hasUsingDeclarationEquivalentTarget) {
139 const TypeSourceInfo *TSI = Node.getTypeSourceInfo();
142 const std::optional<TypeLocInfo> TypeInfo =
getTypeLocInfo(TSI->getTypeLoc());
143 if (!TypeInfo || !TypeInfo->HasQualifier)
155 OnlyNamespaceScope(Options.get(
"OnlyNamespaceScope", false)) {}
159 Options.store(Opts,
"OnlyNamespaceScope", OnlyNamespaceScope);
163 const auto ControlFlowInitStatementMatcher = stmt(
164 anyOf(mapAnyOf(ifStmt, switchStmt, cxxForRangeStmt)
165 .with(hasInitStatement(stmt(equalsBoundNode(
"initDeclStmt")))),
166 forStmt(hasLoopInit(stmt(equalsBoundNode(
"initDeclStmt"))))));
168 const auto AliasPreconditions =
169 allOf(unless(isInMacro()), unless(isAliasTemplate()),
170 unless(hasAliasAttributes()));
171 const auto InControlFlowInit =
172 allOf(hasParent(declStmt().bind(
"initDeclStmt")),
173 hasAncestor(ControlFlowInitStatementMatcher));
174 const auto RewriteableTypeLoc =
175 typeLoc(allOf(isNonDependentTypeLoc(), isNonElaboratedTypeLoc(),
176 isMacroFreeTypeLoc(), hasNoTrailingSyntaxAfterTypeLoc()))
179 const auto RedundantQualifiedAliasMatcher = typeAliasDecl(
180 AliasPreconditions, unless(InControlFlowInit),
181 hasUsingDeclarationEquivalentTarget(), hasTypeLoc(RewriteableTypeLoc));
183 if (OnlyNamespaceScope) {
184 Finder->addMatcher(typeAliasDecl(RedundantQualifiedAliasMatcher,
185 hasDeclContext(anyOf(translationUnitDecl(),
191 Finder->addMatcher(RedundantQualifiedAliasMatcher.bind(
"alias"),
this);
195 const MatchFinder::MatchResult &Result) {
196 const auto *Alias = Result.Nodes.getNodeAs<TypeAliasDecl>(
"alias");
197 assert(Alias &&
"matcher must bind alias");
198 const auto *WrittenTLNode = Result.Nodes.getNodeAs<TypeLoc>(
"loc");
199 assert(WrittenTLNode &&
"matcher must bind loc");
200 const TypeLoc WrittenTL = *WrittenTLNode;
202 const SourceManager &SM = *Result.SourceManager;
203 const LangOptions &LangOpts = getLangOpts();
205 const SourceLocation AliasLoc = Alias->getLocation();
206 const SourceLocation RhsBeginLoc = WrittenTL.getBeginLoc();
208 CharSourceRange::getCharRange(AliasLoc, RhsBeginLoc), SM, LangOpts,
209 [](
const Token &Tok) {
return Tok.is(tok::equal); });
210 if (EqualRange.isInvalid())
213 auto Diag = diag(Alias->getLocation(),
214 "type alias is redundant; use a using-declaration instead");
216 Diag << FixItHint::CreateRemoval(Alias->getLocation())
217 << FixItHint::CreateRemoval(EqualRange.getBegin());
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
RedundantQualifiedAliasCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_MATCHER(BinaryOperator, isRelationalOperator)
static bool isNamespaceLikeDeclContext(const DeclContext *DC)
static std::optional< TypeLocInfo > getTypeLocInfo(TypeLoc TL)
static bool hasSameUnqualifiedName(const NamedDecl *LHS, const NamedDecl *RHS)
static bool canUseUsingDeclarationForTarget(const TypeAliasDecl *Alias, const NamedDecl *Target)
static const NamedDecl * getNamedDeclFromTypeLoc(TypeLoc TL)
static bool hasTrailingSyntaxAfterRhsType(TypeLoc TL, const SourceManager &SM, const LangOptions &LangOpts)
static bool hasMacroInRange(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
bool rangeContainsExpansionsOrDirectives(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts)
Re-lex the provide Range and return false if either a macro spans multiple tokens,...
std::optional< Token > findNextTokenSkippingComments(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
CharSourceRange findTokenTextInRange(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, llvm::function_ref< bool(const Token &)> Pred)
Returns source range of the first token in Range matching Pred. The returned char range starts at the...
llvm::StringMap< ClangTidyValue > OptionMap