================
@@ -0,0 +1,317 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "RedundantQualifiedAliasCheck.h"
+#include "../utils/LexerUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/TypeSwitch.h"
+#include <cassert>
+#include <optional>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+struct NominalTypeLocInfo {
+ TypeLoc Loc;
+ bool HasQualifier = false;
+};
+
+struct EqualTokenInfo {
+ SourceLocation AfterEqualLoc;
+ bool SawComment = false;
+};
+
+} // namespace
+
+static bool hasMacroInRange(SourceRange Range, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ if (Range.isInvalid())
+ return true;
+ return utils::lexer::rangeContainsExpansionsOrDirectives(Range, SM,
LangOpts);
+}
+
+static std::optional<NominalTypeLocInfo> peelToNominalTypeLoc(TypeLoc TL) {
+ if (TL.isNull())
+ return std::nullopt;
+
+ if (const auto TypedefTL = TL.getAs<TypedefTypeLoc>()) {
+ // Avoid rewriting aliases that use an elaborated keyword
+ // (class/struct/enum).
+ if (TypedefTL.getElaboratedKeywordLoc().isValid())
+ return std::nullopt;
+ const bool HasQualifier =
+
static_cast<bool>(TypedefTL.getQualifierLoc().getNestedNameSpecifier());
+ return NominalTypeLocInfo{TypedefTL, HasQualifier};
+ }
+
+ if (const auto TagTL = TL.getAs<TagTypeLoc>()) {
+ // Avoid rewriting aliases that use an elaborated keyword
+ // (class/struct/enum).
+ if (TagTL.getElaboratedKeywordLoc().isValid())
+ return std::nullopt;
+ const bool HasQualifier =
+ static_cast<bool>(TagTL.getQualifierLoc().getNestedNameSpecifier());
+ return NominalTypeLocInfo{TagTL, HasQualifier};
+ }
+
+ return std::nullopt;
+}
+
+namespace {
+
+AST_MATCHER(TypeAliasDecl, isAliasTemplate) {
+ return Node.getDescribedAliasTemplate() != nullptr;
+}
+
+AST_MATCHER(TypeLoc, hasQualifiedNominalTypeLoc) {
+ std::optional<NominalTypeLocInfo> Result = peelToNominalTypeLoc(Node);
+ return Result && Result->HasQualifier;
+}
+
+} // namespace
+
+static const NamedDecl *getNamedDeclFromNominalTypeLoc(TypeLoc TL) {
+ if (const auto TypedefTL = TL.getAs<TypedefTypeLoc>())
+ return TypedefTL.getTypePtr()->getDecl();
+ if (const auto TagTL = TL.getAs<TagTypeLoc>())
+ return TagTL.getDecl();
+ return nullptr;
+}
+
+static bool hasSameUnqualifiedName(const TypeAliasDecl *Alias,
+ const NamedDecl *Target) {
+ return Alias->getName() == Target->getName();
+}
+
+static bool isControlFlowInitParent(const DeclStmt *DeclS,
+ const DynTypedNode &Parent) {
+ return llvm::TypeSwitch<const Stmt *, bool>(Parent.get<Stmt>())
+ .Case<IfStmt, SwitchStmt, ForStmt, CXXForRangeStmt>(
+ [&](const auto *S) { return S->getInit() == DeclS; })
+ .Default(false);
+}
+
+static bool isInControlFlowInitStatement(const TypeAliasDecl *Alias,
+ ASTContext &Context) {
+ for (const DynTypedNode &AliasParent : Context.getParents(*Alias)) {
+ const auto *DeclS = AliasParent.get<DeclStmt>();
+ if (!DeclS)
+ continue;
+
+ for (const DynTypedNode &DeclSParent : Context.getParents(*DeclS))
+ if (isControlFlowInitParent(DeclS, DeclSParent))
+ return true;
+ }
+ return false;
+}
+
+static std::optional<EqualTokenInfo>
+findEqualTokenAfter(SourceLocation StartLoc, SourceLocation LimitLoc,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ if (StartLoc.isInvalid() || LimitLoc.isInvalid())
+ return std::nullopt;
+ if (StartLoc.isMacroID() || LimitLoc.isMacroID())
+ return std::nullopt;
+ if (!SM.isBeforeInTranslationUnit(StartLoc, LimitLoc))
+ return std::nullopt;
+
+ const SourceLocation SpellingStart = SM.getSpellingLoc(StartLoc);
+ const SourceLocation SpellingLimit = SM.getSpellingLoc(LimitLoc);
+ const FileID File = SM.getFileID(SpellingStart);
+ if (File != SM.getFileID(SpellingLimit))
+ return std::nullopt;
+
+ const StringRef Buf = SM.getBufferData(File);
+ const char *StartChar = SM.getCharacterData(SpellingStart);
+ Lexer Lex(SpellingStart, LangOpts, StartChar, StartChar, Buf.end());
+ Lex.SetCommentRetentionState(true);
+
+ Token Tok;
+ bool SawComment = false;
+ do {
+ Lex.LexFromRawLexer(Tok);
+ if (Tok.is(tok::comment))
+ SawComment = true;
+ if (Tok.is(tok::equal)) {
+ Token NextTok;
+ Lex.LexFromRawLexer(NextTok);
+ // Return the location *after* '=' so removal is token-preserving.
+ return EqualTokenInfo{NextTok.getLocation(), SawComment};
+ }
+ } while (Tok.isNot(tok::eof) && Tok.getLocation() < SpellingLimit);
+
+ return std::nullopt;
+}
+
+static std::optional<FixItHint> buildRemovalFixItAfterEqual(
+ const TypeAliasDecl *Alias, SourceLocation AfterEqualLoc,
+ const SourceManager &SM, const LangOptions &LangOpts) {
+ SourceLocation AliasLoc = Alias->getLocation();
+ if (AliasLoc.isInvalid() || AfterEqualLoc.isInvalid())
+ return std::nullopt;
+ if (AliasLoc.isMacroID() || AfterEqualLoc.isMacroID())
+ return std::nullopt;
+
+ AliasLoc = Lexer::GetBeginningOfToken(AliasLoc, SM, LangOpts);
+ if (AliasLoc.isInvalid())
+ return std::nullopt;
+
+ const CharSourceRange RemovalRange = Lexer::makeFileCharRange(
+ CharSourceRange::getCharRange(AliasLoc, AfterEqualLoc), SM, LangOpts);
+ if (RemovalRange.isInvalid())
+ return std::nullopt;
+
+ if (hasMacroInRange(RemovalRange.getAsRange(), SM, LangOpts))
+ // Avoid rewriting tokens that come from macro expansions.
+ return std::nullopt;
+
+ return FixItHint::CreateRemoval(RemovalRange);
+}
+
+static bool hasAliasAttributes(const TypeAliasDecl *Alias, TypeLoc TL) {
+ if (Alias->hasAttrs())
+ return true;
+ for (TypeLoc CurTL = TL; !CurTL.isNull(); CurTL = CurTL.getNextTypeLoc())
+ if (CurTL.getAs<AttributedTypeLoc>())
+ return true;
+ return false;
+}
+
+static bool hasTrailingSyntaxAfterRhsType(TypeLoc TL, const SourceManager &SM,
+ const LangOptions &LangOpts) {
+ const SourceLocation TypeEndLoc = TL.getEndLoc();
+ if (TypeEndLoc.isInvalid())
+ return true;
+ if (TypeEndLoc.isMacroID())
+ return true;
+ const std::optional<Token> NextToken =
+ utils::lexer::findNextTokenSkippingComments(TypeEndLoc, SM, LangOpts);
+ return !NextToken || NextToken->isNot(tok::semi);
+}
+
+RedundantQualifiedAliasCheck::RedundantQualifiedAliasCheck(
+ StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ OnlyNamespaceScope(Options.get("OnlyNamespaceScope", false)) {}
+
+void RedundantQualifiedAliasCheck::storeOptions(
+ ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "OnlyNamespaceScope", OnlyNamespaceScope);
+}
+
+void RedundantQualifiedAliasCheck::registerMatchers(MatchFinder *Finder) {
+ if (OnlyNamespaceScope) {
+ Finder->addMatcher(
+ typeAliasDecl(
+ unless(isAliasTemplate()), unless(isImplicit()),
+ hasTypeLoc(hasQualifiedNominalTypeLoc()),
+ hasDeclContext(anyOf(translationUnitDecl(), namespaceDecl())))
+ .bind("alias"),
+ this);
+ return;
+ }
+ Finder->addMatcher(typeAliasDecl(unless(isAliasTemplate()),
+ unless(isImplicit()),
+ hasTypeLoc(hasQualifiedNominalTypeLoc()))
+ .bind("alias"),
+ this);
+}
+
+void RedundantQualifiedAliasCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Alias = Result.Nodes.getNodeAs<TypeAliasDecl>("alias");
+ assert(Alias && "matcher must bind alias");
+
+ if (Alias->getLocation().isInvalid() || Alias->getLocation().isMacroID())
+ return;
+
+ assert(Result.Context && "match result should always carry ASTContext");
+ if (isInControlFlowInitStatement(Alias, *Result.Context))
+ return;
+
+ const TypeSourceInfo *TSI = Alias->getTypeSourceInfo();
+ if (!TSI)
+ return;
+
+ const TypeLoc WrittenTL = TSI->getTypeLoc();
+ if (WrittenTL.isNull())
+ return;
----------------
vbvictor wrote:
you can bind to typeLoc in matchers:
`hasTypeLoc(typeLoc(hasQualifiedNominalTypeLoc()).bind("loc"))`
Then use predicates: `typeLoc(hasType(unless(isDependantType(), isInMacro())))`
You can probably put `hasAliasAttributes` inside matchers too.
https://github.com/llvm/llvm-project/pull/180404
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits