================
@@ -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

Reply via email to