Skip to content

[clang-tidy][NFC] Refactor modernize-use-trailing-return-type-check check code and tests #140759

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ struct UnqualNameVisitor : public RecursiveASTVisitor<UnqualNameVisitor> {
};
} // namespace

constexpr llvm::StringLiteral Message =
constexpr llvm::StringLiteral ErrorMessageOnFunction =
"use a trailing return type for this function";

static SourceLocation expandIfMacroId(SourceLocation Loc,
Expand All @@ -125,7 +125,7 @@ static SourceLocation expandIfMacroId(SourceLocation Loc,
return Loc;
}

SourceLocation UseTrailingReturnTypeCheck::findTrailingReturnTypeSourceLocation(
static SourceLocation findTrailingReturnTypeSourceLocation(
const FunctionDecl &F, const FunctionTypeLoc &FTL, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts) {
// We start with the location of the closing parenthesis.
Expand Down Expand Up @@ -217,10 +217,11 @@ classifyToken(const FunctionDecl &F, Preprocessor &PP, Token Tok) {
return CT;
}

std::optional<SmallVector<ClassifiedToken, 8>>
UseTrailingReturnTypeCheck::classifyTokensBeforeFunctionName(
const FunctionDecl &F, const ASTContext &Ctx, const SourceManager &SM,
const LangOptions &LangOpts) {
static std::optional<SmallVector<ClassifiedToken, 8>>
classifyTokensBeforeFunctionName(const FunctionDecl &F, const ASTContext &Ctx,
const SourceManager &SM,
const LangOptions &LangOpts,
Preprocessor *PP) {
SourceLocation BeginF = expandIfMacroId(F.getBeginLoc(), SM);
SourceLocation BeginNameF = expandIfMacroId(F.getLocation(), SM);

Expand All @@ -242,7 +243,6 @@ UseTrailingReturnTypeCheck::classifyTokensBeforeFunctionName(
const MacroInfo *MI = PP->getMacroInfo(&Info);
if (!MI || MI->isFunctionLike()) {
// Cannot handle function style macros.
diag(F.getLocation(), Message);
return std::nullopt;
}
}
Expand All @@ -253,10 +253,8 @@ UseTrailingReturnTypeCheck::classifyTokensBeforeFunctionName(

if (std::optional<ClassifiedToken> CT = classifyToken(F, *PP, T))
ClassifiedTokens.push_back(*CT);
else {
diag(F.getLocation(), Message);
else
return std::nullopt;
}
}

return ClassifiedTokens;
Expand All @@ -273,17 +271,17 @@ static bool hasAnyNestedLocalQualifiers(QualType Type) {
return Result;
}

SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
const FunctionDecl &F, const TypeLoc &ReturnLoc, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts) {
static SourceRange
findReturnTypeAndCVSourceRange(const FunctionDecl &F, const TypeLoc &ReturnLoc,
const ASTContext &Ctx, const SourceManager &SM,
const LangOptions &LangOpts, Preprocessor *PP) {

// We start with the range of the return type and expand to neighboring
// qualifiers (const, volatile and restrict).
SourceRange ReturnTypeRange = F.getReturnTypeSourceRange();
if (ReturnTypeRange.isInvalid()) {
// Happens if e.g. clang cannot resolve all includes and the return type is
// unknown.
diag(F.getLocation(), Message);
return {};
}

Expand All @@ -294,7 +292,7 @@ SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(

// Include qualifiers to the left and right of the return type.
std::optional<SmallVector<ClassifiedToken, 8>> MaybeTokens =
classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts);
classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts, PP);
if (!MaybeTokens)
return {};
const SmallVector<ClassifiedToken, 8> &Tokens = *MaybeTokens;
Expand Down Expand Up @@ -331,10 +329,11 @@ SourceRange UseTrailingReturnTypeCheck::findReturnTypeAndCVSourceRange(
return ReturnTypeRange;
}

void UseTrailingReturnTypeCheck::keepSpecifiers(
std::string &ReturnType, std::string &Auto, SourceRange ReturnTypeCVRange,
const FunctionDecl &F, const FriendDecl *Fr, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts) {
static void keepSpecifiers(std::string &ReturnType, std::string &Auto,
SourceRange ReturnTypeCVRange, const FunctionDecl &F,
const FriendDecl *Fr, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts,
Preprocessor *PP) {
// Check if there are specifiers inside the return type. E.g. unsigned
// inline int.
const auto *M = dyn_cast<CXXMethodDecl>(&F);
Expand All @@ -346,7 +345,7 @@ void UseTrailingReturnTypeCheck::keepSpecifiers(
// Tokenize return type. If it contains macros which contain a mix of
// qualifiers, specifiers and types, give up.
std::optional<SmallVector<ClassifiedToken, 8>> MaybeTokens =
classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts);
classifyTokensBeforeFunctionName(F, Ctx, SM, LangOpts, PP);
if (!MaybeTokens)
return;

Expand Down Expand Up @@ -423,7 +422,7 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
if (F->getDeclaredReturnType()->isFunctionPointerType() ||
F->getDeclaredReturnType()->isMemberFunctionPointerType() ||
F->getDeclaredReturnType()->isMemberPointerType()) {
diag(F->getLocation(), Message);
diag(F->getLocation(), ErrorMessageOnFunction);
return;
}

Expand All @@ -440,24 +439,26 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
// FIXME: This may happen if we have __attribute__((...)) on the function.
// We abort for now. Remove this when the function type location gets
// available in clang.
diag(F->getLocation(), Message);
diag(F->getLocation(), ErrorMessageOnFunction);
return;
}

SourceLocation InsertionLoc =
findTrailingReturnTypeSourceLocation(*F, FTL, Ctx, SM, LangOpts);
if (InsertionLoc.isInvalid()) {
diag(F->getLocation(), Message);
diag(F->getLocation(), ErrorMessageOnFunction);
return;
}

// Using the declared return type via F->getDeclaredReturnType().getAsString()
// discards user formatting and order of const, volatile, type, whitespace,
// space before & ... .
SourceRange ReturnTypeCVRange =
findReturnTypeAndCVSourceRange(*F, FTL.getReturnLoc(), Ctx, SM, LangOpts);
if (ReturnTypeCVRange.isInvalid())
SourceRange ReturnTypeCVRange = findReturnTypeAndCVSourceRange(
*F, FTL.getReturnLoc(), Ctx, SM, LangOpts, PP);
if (ReturnTypeCVRange.isInvalid()) {
diag(F->getLocation(), ErrorMessageOnFunction);
return;
}

// Check if unqualified names in the return type conflict with other entities
// after the rewrite.
Expand All @@ -470,7 +471,7 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
UnqualNameVisitor UNV{*F};
UNV.TraverseTypeLoc(FTL.getReturnLoc());
if (UNV.Collision) {
diag(F->getLocation(), Message);
diag(F->getLocation(), ErrorMessageOnFunction);
return;
}

Expand All @@ -486,10 +487,10 @@ void UseTrailingReturnTypeCheck::check(const MatchFinder::MatchResult &Result) {
std::string Auto = NeedSpaceAfterAuto ? "auto " : "auto";
std::string ReturnType =
std::string(tooling::fixit::getText(ReturnTypeCVRange, Ctx));
keepSpecifiers(ReturnType, Auto, ReturnTypeCVRange, *F, Fr, Ctx, SM,
LangOpts);
keepSpecifiers(ReturnType, Auto, ReturnTypeCVRange, *F, Fr, Ctx, SM, LangOpts,
PP);

diag(F->getLocation(), Message)
diag(F->getLocation(), ErrorMessageOnFunction)
<< FixItHint::CreateReplacement(ReturnTypeCVRange, Auto)
<< FixItHint::CreateInsertion(InsertionLoc, " -> " + ReturnType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

#include "../ClangTidyCheck.h"
#include "clang/Lex/Token.h"
#include <optional>

namespace clang::tidy::modernize {

Expand Down Expand Up @@ -39,23 +38,6 @@ class UseTrailingReturnTypeCheck : public ClangTidyCheck {

private:
Preprocessor *PP = nullptr;

SourceLocation findTrailingReturnTypeSourceLocation(
const FunctionDecl &F, const FunctionTypeLoc &FTL, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts);
std::optional<SmallVector<ClassifiedToken, 8>>
classifyTokensBeforeFunctionName(const FunctionDecl &F, const ASTContext &Ctx,
const SourceManager &SM,
const LangOptions &LangOpts);
SourceRange findReturnTypeAndCVSourceRange(const FunctionDecl &F,
const TypeLoc &ReturnLoc,
const ASTContext &Ctx,
const SourceManager &SM,
const LangOptions &LangOpts);
void keepSpecifiers(std::string &ReturnType, std::string &Auto,
SourceRange ReturnTypeCVRange, const FunctionDecl &F,
const FriendDecl *Fr, const ASTContext &Ctx,
const SourceManager &SM, const LangOptions &LangOpts);
};

} // namespace clang::tidy::modernize
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-trailing-return-type %t
// RUN: %check_clang_tidy -std=c++20-or-later %s modernize-use-trailing-return-type %t

namespace std {
template <typename T, typename U>
Expand Down
Loading