[clang] [clang-tools-extra] [clang][Sema] Unify getPrototypeLoc helpers in SemaCodeComplete and clangd (PR #143345)
Nathan Ridge via cfe-commits
cfe-commits at lists.llvm.org
Sun Jun 8 22:02:21 PDT 2025
https://github.com/HighCommander4 created https://github.com/llvm/llvm-project/pull/143345
HeuristicResolver houses the unified implementation.
Fixes https://github.com/llvm/llvm-project/issues/143240
>From 4c65b2a50e8ea0592459c11a775723b5759ef31e Mon Sep 17 00:00:00 2001
From: Nathan Ridge <zeratul976 at hotmail.com>
Date: Mon, 9 Jun 2025 00:58:47 -0400
Subject: [PATCH] [clang][Sema] Unify getPrototypeLoc helpers in
SemaCodeComplete and clangd
HeuristicResolver houses the unified implementation.
Fixes https://github.com/llvm/llvm-project/issues/143240
---
clang-tools-extra/clangd/InlayHints.cpp | 51 +-----------------
clang/include/clang/Sema/HeuristicResolver.h | 7 +++
clang/lib/Sema/HeuristicResolver.cpp | 55 ++++++++++++++++++++
clang/lib/Sema/SemaCodeComplete.cpp | 50 +-----------------
4 files changed, 65 insertions(+), 98 deletions(-)
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 20a238612a7e4..5879c7745c300 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -33,7 +33,6 @@
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
-#include "llvm/ADT/identity.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FormatVariadic.h"
@@ -339,53 +338,6 @@ QualType maybeDesugar(ASTContext &AST, QualType QT) {
return QT;
}
-// Given a callee expression `Fn`, if the call is through a function pointer,
-// try to find the declaration of the corresponding function pointer type,
-// so that we can recover argument names from it.
-// FIXME: This function is mostly duplicated in SemaCodeComplete.cpp; unify.
-static FunctionProtoTypeLoc getPrototypeLoc(Expr *Fn) {
- TypeLoc Target;
- Expr *NakedFn = Fn->IgnoreParenCasts();
- if (const auto *T = NakedFn->getType().getTypePtr()->getAs<TypedefType>()) {
- Target = T->getDecl()->getTypeSourceInfo()->getTypeLoc();
- } else if (const auto *DR = dyn_cast<DeclRefExpr>(NakedFn)) {
- const auto *D = DR->getDecl();
- if (const auto *const VD = dyn_cast<VarDecl>(D)) {
- Target = VD->getTypeSourceInfo()->getTypeLoc();
- }
- }
-
- if (!Target)
- return {};
-
- // Unwrap types that may be wrapping the function type
- while (true) {
- if (auto P = Target.getAs<PointerTypeLoc>()) {
- Target = P.getPointeeLoc();
- continue;
- }
- if (auto A = Target.getAs<AttributedTypeLoc>()) {
- Target = A.getModifiedLoc();
- continue;
- }
- if (auto P = Target.getAs<ParenTypeLoc>()) {
- Target = P.getInnerLoc();
- continue;
- }
- break;
- }
-
- if (auto F = Target.getAs<FunctionProtoTypeLoc>()) {
- // In some edge cases the AST can contain a "trivial" FunctionProtoTypeLoc
- // which has null parameters. Avoid these as they don't contain useful
- // information.
- if (llvm::all_of(F.getParams(), llvm::identity<ParmVarDecl *>()))
- return F;
- }
-
- return {};
-}
-
ArrayRef<const ParmVarDecl *>
maybeDropCxxExplicitObjectParameters(ArrayRef<const ParmVarDecl *> Params) {
if (!Params.empty() && Params.front()->isExplicitObjectParameter())
@@ -514,7 +466,8 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
Callee.Decl = FD;
else if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(CalleeDecls[0]))
Callee.Decl = FTD->getTemplatedDecl();
- else if (FunctionProtoTypeLoc Loc = getPrototypeLoc(E->getCallee()))
+ else if (FunctionProtoTypeLoc Loc =
+ Resolver->getProtoTypeLoc(E->getCallee()))
Callee.Loc = Loc;
else
return true;
diff --git a/clang/include/clang/Sema/HeuristicResolver.h b/clang/include/clang/Sema/HeuristicResolver.h
index df60d3359c6a6..5e73aea79a609 100644
--- a/clang/include/clang/Sema/HeuristicResolver.h
+++ b/clang/include/clang/Sema/HeuristicResolver.h
@@ -20,6 +20,7 @@ class CXXBasePath;
class CXXDependentScopeMemberExpr;
class DeclarationName;
class DependentScopeDeclRefExpr;
+class FunctionProtoTypeLoc;
class NamedDecl;
class Type;
class UnresolvedUsingValueDecl;
@@ -93,6 +94,12 @@ class HeuristicResolver {
// during simplification, and the operation fails if no pointer type is found.
QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer);
+ // Given an expression `Fn` representing the callee in a function call,
+ // if the call is through a function pointer, try to find the declaration of
+ // the corresponding function pointer type, so that we can recover argument
+ // names from it.
+ FunctionProtoTypeLoc getProtoTypeLoc(Expr *Fn) const;
+
private:
ASTContext &Ctx;
};
diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp
index 0c67f1f2a3878..704a3d13cc2a8 100644
--- a/clang/lib/Sema/HeuristicResolver.cpp
+++ b/clang/lib/Sema/HeuristicResolver.cpp
@@ -13,6 +13,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/Type.h"
+#include "llvm/ADT/identity.h"
namespace clang {
@@ -50,6 +51,7 @@ class HeuristicResolverImpl {
llvm::function_ref<bool(const NamedDecl *ND)> Filter);
TagDecl *resolveTypeToTagDecl(QualType T);
QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer);
+ FunctionProtoTypeLoc getProtoTypeLoc(Expr *Fn);
private:
ASTContext &Ctx;
@@ -506,6 +508,55 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveDependentMember(
}
return {};
}
+
+FunctionProtoTypeLoc HeuristicResolverImpl::getProtoTypeLoc(Expr *Fn) {
+ TypeLoc Target;
+ Expr *NakedFn = Fn->IgnoreParenCasts();
+ if (const auto *T = NakedFn->getType().getTypePtr()->getAs<TypedefType>()) {
+ Target = T->getDecl()->getTypeSourceInfo()->getTypeLoc();
+ } else if (const auto *DR = dyn_cast<DeclRefExpr>(NakedFn)) {
+ const auto *D = DR->getDecl();
+ if (const auto *const VD = dyn_cast<VarDecl>(D)) {
+ Target = VD->getTypeSourceInfo()->getTypeLoc();
+ }
+ } else if (const auto *ME = dyn_cast<MemberExpr>(Fn)) {
+ const auto *MD = ME->getMemberDecl();
+ if (const auto *FD = dyn_cast<FieldDecl>(MD)) {
+ Target = FD->getTypeSourceInfo()->getTypeLoc();
+ }
+ }
+
+ if (!Target)
+ return {};
+
+ // Unwrap types that may be wrapping the function type
+ while (true) {
+ if (auto P = Target.getAs<PointerTypeLoc>()) {
+ Target = P.getPointeeLoc();
+ continue;
+ }
+ if (auto A = Target.getAs<AttributedTypeLoc>()) {
+ Target = A.getModifiedLoc();
+ continue;
+ }
+ if (auto P = Target.getAs<ParenTypeLoc>()) {
+ Target = P.getInnerLoc();
+ continue;
+ }
+ break;
+ }
+
+ if (auto F = Target.getAs<FunctionProtoTypeLoc>()) {
+ // In some edge cases the AST can contain a "trivial" FunctionProtoTypeLoc
+ // which has null parameters. Avoid these as they don't contain useful
+ // information.
+ if (llvm::all_of(F.getParams(), llvm::identity<ParmVarDecl *>()))
+ return F;
+ }
+
+ return {};
+}
+
} // namespace
std::vector<const NamedDecl *> HeuristicResolver::resolveMemberExpr(
@@ -557,4 +608,8 @@ QualType HeuristicResolver::simplifyType(QualType Type, const Expr *E,
return HeuristicResolverImpl(Ctx).simplifyType(Type, E, UnwrapPointer);
}
+FunctionProtoTypeLoc HeuristicResolver::getProtoTypeLoc(Expr *Fn) const {
+ return HeuristicResolverImpl(Ctx).getProtoTypeLoc(Fn);
+}
+
} // namespace clang
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index f9f7c192f19d2..ec0976d4e9fcf 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -6283,54 +6283,6 @@ ProduceSignatureHelp(Sema &SemaRef, MutableArrayRef<ResultCandidate> Candidates,
return getParamType(SemaRef, Candidates, CurrentArg);
}
-// Given a callee expression `Fn`, if the call is through a function pointer,
-// try to find the declaration of the corresponding function pointer type,
-// so that we can recover argument names from it.
-static FunctionProtoTypeLoc GetPrototypeLoc(Expr *Fn) {
- TypeLoc Target;
-
- if (const auto *T = Fn->getType().getTypePtr()->getAs<TypedefType>()) {
- Target = T->getDecl()->getTypeSourceInfo()->getTypeLoc();
-
- } else if (const auto *DR = dyn_cast<DeclRefExpr>(Fn)) {
- const auto *D = DR->getDecl();
- if (const auto *const VD = dyn_cast<VarDecl>(D)) {
- Target = VD->getTypeSourceInfo()->getTypeLoc();
- }
- } else if (const auto *ME = dyn_cast<MemberExpr>(Fn)) {
- const auto *MD = ME->getMemberDecl();
- if (const auto *FD = dyn_cast<FieldDecl>(MD)) {
- Target = FD->getTypeSourceInfo()->getTypeLoc();
- }
- }
-
- if (!Target)
- return {};
-
- // Unwrap types that may be wrapping the function type
- while (true) {
- if (auto P = Target.getAs<PointerTypeLoc>()) {
- Target = P.getPointeeLoc();
- continue;
- }
- if (auto A = Target.getAs<AttributedTypeLoc>()) {
- Target = A.getModifiedLoc();
- continue;
- }
- if (auto P = Target.getAs<ParenTypeLoc>()) {
- Target = P.getInnerLoc();
- continue;
- }
- break;
- }
-
- if (auto F = Target.getAs<FunctionProtoTypeLoc>()) {
- return F;
- }
-
- return {};
-}
-
QualType
SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
SourceLocation OpenParLoc) {
@@ -6419,7 +6371,7 @@ SemaCodeCompletion::ProduceCallSignatureHelp(Expr *Fn, ArrayRef<Expr *> Args,
// Lastly we check whether expression's type is function pointer or
// function.
- FunctionProtoTypeLoc P = GetPrototypeLoc(NakedFn);
+ FunctionProtoTypeLoc P = Resolver.getProtoTypeLoc(NakedFn);
QualType T = NakedFn->getType();
if (!T->getPointeeType().isNull())
T = T->getPointeeType();
More information about the cfe-commits
mailing list