================
@@ -0,0 +1,256 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Decl.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 <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 isInNamespaceOrTU(const Decl *D) {
+ const DeclContext *DC = D->getDeclContext();
+ return DC && (DC->isTranslationUnit() || isa<NamespaceDecl>(DC));
+}
+
+static bool isAliasTemplate(const TypeAliasDecl *Alias) {
+ return Alias->getDescribedAliasTemplate() != nullptr;
+}
+
+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) {
+ while (!TL.isNull()) {
+ if (const auto ParenTL = TL.getAs<ParenTypeLoc>()) {
+ TL = ParenTL.getInnerLoc();
+ continue;
+ }
+ if (const auto AttrTL = TL.getAs<AttributedTypeLoc>()) {
+ // Preserve any attributes by rewriting only the alias name and '='.
+ TL = AttrTL.getModifiedLoc();
+ continue;
+ }
+
+ 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;
+ }
+ 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 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);
+}
+
+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) {
+ Finder->addMatcher(typeAliasDecl().bind("alias"), this);
+}
+
+void RedundantQualifiedAliasCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ const auto *Alias = Result.Nodes.getNodeAs<TypeAliasDecl>("alias");
+ if (!Alias)
----------------
zeyi2 wrote:
Sorry for the late reply, I came up with pseudocode that looks like this: (you
might need to fine-tune the actual implementation)
```c++
AST_MATCHER(TypeAliasDecl, isAliasTemplate) {
return Node.getDescribedAliasTemplate() != nullptr;
}
AST_MATCHER(TypeLoc, hasQualifiedNominalTypeLoc) {
auto Result = peelToNominalTypeLoc(Node);
return Result && Result->HasQualifier;
}
void RedundantQualifiedAliasCheck::registerMatchers(MatchFinder *Finder) {
auto Matcher = typeAliasDecl(
unless(isAliasTemplate()),
unless(isImplicit()),
unless(isExpansionInSystemHeader()),
hasTypeLoc(hasQualifiedNominalTypeLoc())
);
if (OnlyNamespaceScope) {
Finder->addMatcher(typeAliasDecl(Matcher,
hasDeclContext(anyOf(translationUnitDecl(), namespaceDecl()))).bind("alias"),
this);
} else {
Finder->addMatcher(Matcher.bind("alias"), this);
}
}
```
https://github.com/llvm/llvm-project/pull/180404
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits