clang-tools 23.0.0git
RedundantQualifiedAliasCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "../utils/LexerUtils.h"
11#include <cassert>
12#include <optional>
13
14using namespace clang::ast_matchers;
15
17
18namespace {
19
20struct TypeLocInfo {
21 TypeLoc Loc;
22 bool HasQualifier = false;
23};
24
25} // namespace
26
27static bool hasMacroInRange(SourceRange Range, const SourceManager &SM,
28 const LangOptions &LangOpts) {
29 if (Range.isInvalid())
30 return true;
31 return utils::lexer::rangeContainsExpansionsOrDirectives(Range, SM, LangOpts);
32}
33
34static std::optional<TypeLocInfo> getTypeLocInfo(TypeLoc TL) {
35 if (TL.isNull())
36 return std::nullopt;
37
38 const auto MakeTypeLocInfo = [](auto TypeTL) {
39 const bool HasQualifier =
40 static_cast<bool>(TypeTL.getQualifierLoc().getNestedNameSpecifier());
41 return TypeLocInfo{TypeTL, HasQualifier};
42 };
43
44 if (const auto TypedefTL = TL.getAs<TypedefTypeLoc>())
45 return MakeTypeLocInfo(TypedefTL);
46
47 if (const auto TagTL = TL.getAs<TagTypeLoc>())
48 return MakeTypeLocInfo(TagTL);
49
50 return std::nullopt;
51}
52
53static const NamedDecl *getNamedDeclFromTypeLoc(TypeLoc TL) {
54 if (const auto TypedefTL = TL.getAs<TypedefTypeLoc>())
55 return TypedefTL.getDecl();
56 if (const auto TagTL = TL.getAs<TagTypeLoc>())
57 return TagTL.getDecl();
58 return nullptr;
59}
60
61static bool hasSameUnqualifiedName(const NamedDecl *LHS, const NamedDecl *RHS) {
62 return LHS->getName() == RHS->getName();
63}
64
65static bool isNamespaceLikeDeclContext(const DeclContext *DC) {
66 return isa<TranslationUnitDecl, NamespaceDecl>(DC);
67}
68
69static bool canUseUsingDeclarationForTarget(const TypeAliasDecl *Alias,
70 const NamedDecl *Target) {
71 const DeclContext *AliasContext = Alias->getDeclContext()->getRedeclContext();
72 const DeclContext *TargetContext =
73 Target->getDeclContext()->getRedeclContext();
74
75 const auto *AliasRecord = dyn_cast<CXXRecordDecl>(AliasContext);
76 if (!AliasRecord)
77 return isNamespaceLikeDeclContext(TargetContext);
78
79 const auto *TargetRecord = dyn_cast<CXXRecordDecl>(TargetContext);
80 return TargetRecord && AliasRecord->isDerivedFrom(TargetRecord);
81}
82
83static bool hasTrailingSyntaxAfterRhsType(TypeLoc TL, const SourceManager &SM,
84 const LangOptions &LangOpts) {
85 const SourceLocation TypeEndLoc = TL.getEndLoc();
86 if (TypeEndLoc.isInvalid() || TypeEndLoc.isMacroID())
87 return true;
88 const std::optional<Token> NextToken =
89 utils::lexer::findNextTokenSkippingComments(TypeEndLoc, SM, LangOpts);
90 return !NextToken || NextToken->isNot(tok::semi);
91}
92
93namespace {
94
95AST_MATCHER(TypeAliasDecl, isAliasTemplate) {
96 return Node.getDescribedAliasTemplate() != nullptr;
97}
98
99AST_MATCHER(NamedDecl, isInMacro) { return Node.getLocation().isMacroID(); }
100
101AST_MATCHER(TypeAliasDecl, hasAliasAttributes) {
102 if (Node.hasAttrs())
103 return true;
104 const TypeSourceInfo *TSI = Node.getTypeSourceInfo();
105 if (!TSI)
106 return false;
107 for (TypeLoc CurTL = TSI->getTypeLoc(); !CurTL.isNull();
108 CurTL = CurTL.getNextTypeLoc())
109 if (CurTL.getAs<AttributedTypeLoc>())
110 return true;
111 return false;
112}
113
114AST_MATCHER(TypeLoc, isNonDependentTypeLoc) {
115 return !Node.getType().isNull() && !Node.getType()->isDependentType();
116}
117
118AST_MATCHER(TypeLoc, isNonElaboratedTypeLoc) {
119 const auto IsNonElaboratedTypeLoc = [](auto TL) {
120 return !TL.isNull() && !TL.getElaboratedKeywordLoc().isValid();
121 };
122 return IsNonElaboratedTypeLoc(Node.getAs<TypedefTypeLoc>()) ||
123 IsNonElaboratedTypeLoc(Node.getAs<TagTypeLoc>());
124}
125
126AST_MATCHER(TypeLoc, isMacroFreeTypeLoc) {
127 const ASTContext &Context = Finder->getASTContext();
128 return !hasMacroInRange(Node.getSourceRange(), Context.getSourceManager(),
129 Context.getLangOpts());
130}
131
132AST_MATCHER(TypeLoc, hasNoTrailingSyntaxAfterTypeLoc) {
133 const ASTContext &Context = Finder->getASTContext();
134 return !hasTrailingSyntaxAfterRhsType(Node, Context.getSourceManager(),
135 Context.getLangOpts());
136}
137
138AST_MATCHER(TypeAliasDecl, hasUsingDeclarationEquivalentTarget) {
139 const TypeSourceInfo *TSI = Node.getTypeSourceInfo();
140 if (!TSI)
141 return false;
142 const std::optional<TypeLocInfo> TypeInfo = getTypeLocInfo(TSI->getTypeLoc());
143 if (!TypeInfo || !TypeInfo->HasQualifier)
144 return false;
145 const NamedDecl *Target = getNamedDeclFromTypeLoc(TypeInfo->Loc);
146 return Target && hasSameUnqualifiedName(&Node, Target) &&
147 canUseUsingDeclarationForTarget(&Node, Target);
148}
149
150} // namespace
151
153 StringRef Name, ClangTidyContext *Context)
154 : ClangTidyCheck(Name, Context),
155 OnlyNamespaceScope(Options.get("OnlyNamespaceScope", false)) {}
156
159 Options.store(Opts, "OnlyNamespaceScope", OnlyNamespaceScope);
160}
161
163 const auto ControlFlowInitStatementMatcher = stmt(
164 anyOf(mapAnyOf(ifStmt, switchStmt, cxxForRangeStmt)
165 .with(hasInitStatement(stmt(equalsBoundNode("initDeclStmt")))),
166 forStmt(hasLoopInit(stmt(equalsBoundNode("initDeclStmt"))))));
167
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()))
177 .bind("loc");
178
179 const auto RedundantQualifiedAliasMatcher = typeAliasDecl(
180 AliasPreconditions, unless(InControlFlowInit),
181 hasUsingDeclarationEquivalentTarget(), hasTypeLoc(RewriteableTypeLoc));
182
183 if (OnlyNamespaceScope) {
184 Finder->addMatcher(typeAliasDecl(RedundantQualifiedAliasMatcher,
185 hasDeclContext(anyOf(translationUnitDecl(),
186 namespaceDecl())))
187 .bind("alias"),
188 this);
189 return;
190 }
191 Finder->addMatcher(RedundantQualifiedAliasMatcher.bind("alias"), this);
192}
193
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;
201
202 const SourceManager &SM = *Result.SourceManager;
203 const LangOptions &LangOpts = getLangOpts();
204
205 const SourceLocation AliasLoc = Alias->getLocation();
206 const SourceLocation RhsBeginLoc = WrittenTL.getBeginLoc();
207 const CharSourceRange EqualRange = utils::lexer::findTokenTextInRange(
208 CharSourceRange::getCharRange(AliasLoc, RhsBeginLoc), SM, LangOpts,
209 [](const Token &Tok) { return Tok.is(tok::equal); });
210 if (EqualRange.isInvalid())
211 return;
212
213 auto Diag = diag(Alias->getLocation(),
214 "type alias is redundant; use a using-declaration instead");
215
216 Diag << FixItHint::CreateRemoval(Alias->getLocation())
217 << FixItHint::CreateRemoval(EqualRange.getBegin());
218}
219
220} // namespace clang::tidy::readability
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)
Definition LexerUtils.h:106
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