================
@@ -0,0 +1,240 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/TypeLoc.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Basic/SourceManager.h"
+#include <cassert>
+#include <optional>
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+namespace {
+
+struct NominalTypeLocInfo {
+  TypeLoc Loc;
+  bool HasQualifier = 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;
+}
+
+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 isNamespaceLikeDeclContext(const DeclContext *DC) {
+  return isa<TranslationUnitDecl>(DC) || isa<NamespaceDecl>(DC);
+}
+
+static bool canUseUsingDeclarationForTarget(const TypeAliasDecl *Alias,
+                                            const NamedDecl *Target) {
+  const DeclContext *AliasContext = 
Alias->getDeclContext()->getRedeclContext();
+  const DeclContext *TargetContext =
+      Target->getDeclContext()->getRedeclContext();
+
+  const auto *AliasRecord = dyn_cast<CXXRecordDecl>(AliasContext);
+  if (!AliasRecord)
+    return isNamespaceLikeDeclContext(TargetContext);
+
+  const auto *TargetRecord = dyn_cast<CXXRecordDecl>(TargetContext);
+  return TargetRecord && AliasRecord->isDerivedFrom(TargetRecord);
+}
+
+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);
+}
+
+namespace {
+
+AST_MATCHER(TypeAliasDecl, isAliasTemplate) {
+  return Node.getDescribedAliasTemplate() != nullptr;
+}
+
+AST_MATCHER(TypeAliasDecl, hasAliasAttributes) {
+  if (Node.hasAttrs())
+    return true;
+  const TypeSourceInfo *TSI = Node.getTypeSourceInfo();
+  if (!TSI)
+    return false;
+  for (TypeLoc CurTL = TSI->getTypeLoc(); !CurTL.isNull();
+       CurTL = CurTL.getNextTypeLoc())
+    if (CurTL.getAs<AttributedTypeLoc>())
+      return true;
+  return false;
+}
+
+AST_MATCHER(TypeLoc, isNonDependentTypeLoc) {
+  return !Node.getType().isNull() && !Node.getType()->isDependentType();
+}
+
+AST_MATCHER(TypeLoc, isMacroFreeTypeLoc) {
+  const ASTContext &Context = Finder->getASTContext();
+  return !hasMacroInRange(Node.getSourceRange(), Context.getSourceManager(),
+                          Context.getLangOpts());
+}
+
+AST_MATCHER(TypeLoc, hasNoTrailingSyntaxAfterTypeLoc) {
+  const ASTContext &Context = Finder->getASTContext();
+  return !hasTrailingSyntaxAfterRhsType(Node, Context.getSourceManager(),
+                                        Context.getLangOpts());
+}
+
+AST_MATCHER(TypeAliasDecl, hasUsingDeclarationEquivalentTarget) {
+  const TypeSourceInfo *TSI = Node.getTypeSourceInfo();
+  if (!TSI)
+    return false;
+  const std::optional<NominalTypeLocInfo> NominalInfo =
+      peelToNominalTypeLoc(TSI->getTypeLoc());
+  if (!NominalInfo || !NominalInfo->HasQualifier)
+    return false;
+  const NamedDecl *Target = getNamedDeclFromNominalTypeLoc(NominalInfo->Loc);
+  return Target && hasSameUnqualifiedName(&Node, Target) &&
+         canUseUsingDeclarationForTarget(&Node, Target);
+}
+
+} // namespace
+
+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) {
+  const auto ControlFlowInitStatementMatcher = stmt(
+      anyOf(mapAnyOf(ifStmt, switchStmt, cxxForRangeStmt)
+                .with(hasInitStatement(stmt(equalsBoundNode("initDeclStmt")))),
+            forStmt(hasLoopInit(stmt(equalsBoundNode("initDeclStmt"))))));
+
+  const auto AliasPreconditions =
+      allOf(unless(isAliasTemplate()), unless(isImplicit()),
+            unless(hasAliasAttributes()));
+  const auto InControlFlowInit =
+      allOf(hasParent(declStmt().bind("initDeclStmt")),
+            hasAncestor(ControlFlowInitStatementMatcher));
+  const auto RewriteableTypeLoc =
+      typeLoc(allOf(isNonDependentTypeLoc(), isMacroFreeTypeLoc(),
+                    hasNoTrailingSyntaxAfterTypeLoc()))
+          .bind("loc");
+
+  const auto RedundantQualifiedAliasMatcher = typeAliasDecl(
+      AliasPreconditions, unless(InControlFlowInit),
+      hasUsingDeclarationEquivalentTarget(), hasTypeLoc(RewriteableTypeLoc));
+
+  if (OnlyNamespaceScope) {
+    Finder->addMatcher(typeAliasDecl(RedundantQualifiedAliasMatcher,
+                                     
hasDeclContext(anyOf(translationUnitDecl(),
+                                                          namespaceDecl())))
+                           .bind("alias"),
+                       this);
+    return;
+  }
+  Finder->addMatcher(RedundantQualifiedAliasMatcher.bind("alias"), this);
+}
+
+void RedundantQualifiedAliasCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *Alias = Result.Nodes.getNodeAs<TypeAliasDecl>("alias");
+  assert(Alias && "matcher must bind alias");
+  const auto *WrittenTLNode = Result.Nodes.getNodeAs<TypeLoc>("loc");
+  assert(WrittenTLNode && "matcher must bind loc");
+  const TypeLoc WrittenTL = *WrittenTLNode;
+
+  if (Alias->getLocation().isMacroID())
+    return;
+
+  const SourceManager &SM = *Result.SourceManager;
+  const LangOptions &LangOpts = getLangOpts();
+
+  const SourceLocation AliasLoc = Alias->getLocation();
+  const SourceLocation RhsBeginLoc = WrittenTL.getBeginLoc();
+  const CharSourceRange EqualRange = utils::lexer::findTokenInRange(
+      CharSourceRange::getCharRange(AliasLoc, RhsBeginLoc), SM, LangOpts,
+      [](const Token &Tok) { return Tok.is(tok::equal); });
+  if (EqualRange.isInvalid())
+    return;
+
+  auto Diag = diag(Alias->getLocation(),
+                   "type alias is redundant; use a using-declaration instead");
+
+  if (!utils::lexer::getCommentsInRange(
+           CharSourceRange::getCharRange(AliasLoc, EqualRange.getBegin()), SM,
+           LangOpts)
+           .empty()) {
+    // Suppress fix-it: avoid deleting comments between alias name and '='.
----------------
unterumarmung wrote:

I totally missed this, thanks!

https://github.com/llvm/llvm-project/pull/180404
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to