diff options
author | Julian Schmidt <git.julian.schmidt@gmail.com> | 2024-12-21 13:06:30 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-12-21 13:06:30 +0100 |
commit | f77152d9a44b49ee38b15085034bbfe22b4b3992 (patch) | |
tree | a5baadb072fdc10227293f1a9132ce3b7143eae8 /clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp | |
parent | b9a2658a3e8bd13b0f9e7a8a440832a95b377216 (diff) | |
download | llvm-f77152d9a44b49ee38b15085034bbfe22b4b3992.zip llvm-f77152d9a44b49ee38b15085034bbfe22b4b3992.tar.gz llvm-f77152d9a44b49ee38b15085034bbfe22b4b3992.tar.bz2 |
[clang-tidy] use specified type verbatim in modernize-use-using fix (#113837)
Previously, the implementation used the printed type, which contains
expanded
macro arguments, deletes comments, and removes function argument names
from the alias declaration. Instead, this check can be more surgical and
use the
actual written type verbatim.
Fixes #33760
Fixes #37846
Fixes #41685
Fixes #83568
Fixes #95716
Fixes #97009
Diffstat (limited to 'clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp | 75 |
1 files changed, 60 insertions, 15 deletions
diff --git a/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp index 24eefdb..30fcba3 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseUsingCheck.cpp @@ -7,9 +7,14 @@ //===----------------------------------------------------------------------===// #include "UseUsingCheck.h" -#include "clang/AST/ASTContext.h" +#include "../utils/LexerUtils.h" #include "clang/AST/DeclGroup.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.h" #include "clang/Lex/Lexer.h" +#include <string> using namespace clang::ast_matchers; namespace { @@ -83,6 +88,9 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) { if (!ParentDecl) return; + const SourceManager &SM = *Result.SourceManager; + const LangOptions &LO = getLangOpts(); + // Match CXXRecordDecl only to store the range of the last non-implicit full // declaration, to later check whether it's within the typdef itself. const auto *MatchedTagDecl = Result.Nodes.getNodeAs<TagDecl>(TagDeclName); @@ -119,14 +127,51 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) { return; } - PrintingPolicy PrintPolicy(getLangOpts()); - PrintPolicy.SuppressScope = true; - PrintPolicy.ConstantArraySizeAsWritten = true; - PrintPolicy.UseVoidForZeroParams = false; - PrintPolicy.PrintInjectedClassNameWithArguments = false; + const TypeLoc TL = MatchedDecl->getTypeSourceInfo()->getTypeLoc(); + + auto [Type, QualifierStr] = [MatchedDecl, this, &TL, &SM, + &LO]() -> std::pair<std::string, std::string> { + SourceRange TypeRange = TL.getSourceRange(); + + // Function pointer case, get the left and right side of the identifier + // without the identifier. + if (TypeRange.fullyContains(MatchedDecl->getLocation())) { + const auto RangeLeftOfIdentifier = CharSourceRange::getCharRange( + TypeRange.getBegin(), MatchedDecl->getLocation()); + const auto RangeRightOfIdentifier = CharSourceRange::getCharRange( + Lexer::getLocForEndOfToken(MatchedDecl->getLocation(), 0, SM, LO), + Lexer::getLocForEndOfToken(TypeRange.getEnd(), 0, SM, LO)); + const std::string VerbatimType = + (Lexer::getSourceText(RangeLeftOfIdentifier, SM, LO) + + Lexer::getSourceText(RangeRightOfIdentifier, SM, LO)) + .str(); + return {VerbatimType, ""}; + } + + StringRef ExtraReference = ""; + if (MainTypeEndLoc.isValid() && TypeRange.fullyContains(MainTypeEndLoc)) { + // Each type introduced in a typedef can specify being a reference or + // pointer type seperately, so we need to sigure out if the new using-decl + // needs to be to a reference or pointer as well. + const SourceLocation Tok = utils::lexer::findPreviousAnyTokenKind( + MatchedDecl->getLocation(), SM, LO, tok::TokenKind::star, + tok::TokenKind::amp, tok::TokenKind::comma, + tok::TokenKind::kw_typedef); + + ExtraReference = Lexer::getSourceText( + CharSourceRange::getCharRange(Tok, Tok.getLocWithOffset(1)), SM, LO); - std::string Type = MatchedDecl->getUnderlyingType().getAsString(PrintPolicy); - std::string Name = MatchedDecl->getNameAsString(); + if (ExtraReference != "*" && ExtraReference != "&") + ExtraReference = ""; + + TypeRange.setEnd(MainTypeEndLoc); + } + return { + Lexer::getSourceText(CharSourceRange::getTokenRange(TypeRange), SM, LO) + .str(), + ExtraReference.str()}; + }(); + StringRef Name = MatchedDecl->getName(); SourceRange ReplaceRange = MatchedDecl->getSourceRange(); // typedefs with multiple comma-separated definitions produce multiple @@ -143,7 +188,8 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) { // This is the first (and possibly the only) TypedefDecl in a typedef. Save // Type and Name in case we find subsequent TypedefDecl's in this typedef. FirstTypedefType = Type; - FirstTypedefName = Name; + FirstTypedefName = Name.str(); + MainTypeEndLoc = TL.getEndLoc(); } else { // This is additional TypedefDecl in a comma-separated typedef declaration. // Start replacement *after* prior replacement and separate with semicolon. @@ -153,10 +199,10 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) { // If this additional TypedefDecl's Type starts with the first TypedefDecl's // type, make this using statement refer back to the first type, e.g. make // "typedef int Foo, *Foo_p;" -> "using Foo = int;\nusing Foo_p = Foo*;" - if (Type.size() > FirstTypedefType.size() && - Type.substr(0, FirstTypedefType.size()) == FirstTypedefType) - Type = FirstTypedefName + Type.substr(FirstTypedefType.size() + 1); + if (Type == FirstTypedefType && !QualifierStr.empty()) + Type = FirstTypedefName; } + if (!ReplaceRange.getEnd().isMacroID()) { const SourceLocation::IntTy Offset = MatchedDecl->getFunctionType() ? 0 : Name.size(); @@ -171,13 +217,12 @@ void UseUsingCheck::check(const MatchFinder::MatchResult &Result) { LastTagDeclRange->second.isValid() && ReplaceRange.fullyContains(LastTagDeclRange->second)) { Type = std::string(Lexer::getSourceText( - CharSourceRange::getTokenRange(LastTagDeclRange->second), - *Result.SourceManager, getLangOpts())); + CharSourceRange::getTokenRange(LastTagDeclRange->second), SM, LO)); if (Type.empty()) return; } - std::string Replacement = Using + Name + " = " + Type; + std::string Replacement = (Using + Name + " = " + Type + QualifierStr).str(); Diag << FixItHint::CreateReplacement(ReplaceRange, Replacement); } } // namespace clang::tidy::modernize |