https://github.com/StoeckOverflow updated https://github.com/llvm/llvm-project/pull/205307
>From ad76085780d755900971db13c1e468be68663153 Mon Sep 17 00:00:00 2001 From: stoeckoverflow <[email protected]> Date: Fri, 19 Jun 2026 11:17:22 +0200 Subject: [PATCH 1/5] [APINotes] Apply Where.Parameters selectors in Sema --- clang/lib/Sema/SemaAPINotes.cpp | 45 +++++++ .../Headers/WhereParametersSema.apinotes | 126 ++++++++++++++++++ .../Inputs/Headers/WhereParametersSema.h | 51 +++++++ .../APINotes/Inputs/Headers/module.modulemap | 5 + clang/test/APINotes/where-parameters-sema.cpp | 110 +++++++++++++++ 5 files changed, 337 insertions(+) create mode 100644 clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes create mode 100644 clang/test/APINotes/Inputs/Headers/WhereParametersSema.h create mode 100644 clang/test/APINotes/where-parameters-sema.cpp diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 67c08d239e758..269b96a57fa4b 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -993,6 +993,29 @@ UnwindTagContext(TagDecl *DC, api_notes::APINotesManager &APINotes) { return std::nullopt; } +static std::optional<SmallVector<std::string, 4>> +getAPINotesParameterSelector(const Sema &S, const FunctionDecl *FD) { + const auto *FPT = FD->getType()->getAs<FunctionProtoType>(); + if (!FPT) + return std::nullopt; + + SmallVector<std::string, 4> Parameters; + Parameters.reserve(FPT->getNumParams()); + for (QualType ParamType : FPT->param_types()) + Parameters.push_back(ParamType.getUnqualifiedType().getAsString( + S.Context.getPrintingPolicy())); + return Parameters; +} + +static SmallVector<StringRef, 4> +getAPINotesParameterSelectorRefs(ArrayRef<std::string> Strings) { + SmallVector<StringRef, 4> Refs; + Refs.reserve(Strings.size()); + for (const std::string &String : Strings) + Refs.push_back(String); + return Refs; +} + /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { @@ -1024,10 +1047,21 @@ void Sema::ProcessAPINotes(Decl *D) { // Global functions. if (auto FD = dyn_cast<FunctionDecl>(D)) { if (FD->getDeclName().isIdentifier()) { + std::optional<SmallVector<std::string, 4>> ParameterStrings = + getAPINotesParameterSelector(*this, FD); + SmallVector<StringRef, 4> Parameters; + if (ParameterStrings) + Parameters = getAPINotesParameterSelectorRefs(*ParameterStrings); for (auto Reader : Readers) { auto Info = Reader->lookupGlobalFunction(FD->getName(), APINotesContext); ProcessVersionedAPINotes(*this, FD, Info); + + if (ParameterStrings) { + Info = Reader->lookupGlobalFunction(FD->getName(), Parameters, + APINotesContext); + ProcessVersionedAPINotes(*this, FD, Info); + } } } @@ -1211,6 +1245,11 @@ void Sema::ProcessAPINotes(Decl *D) { if (!isa<CXXConstructorDecl>(CXXMethod) && !isa<CXXDestructorDecl>(CXXMethod) && !isa<CXXConversionDecl>(CXXMethod)) { + std::optional<SmallVector<std::string, 4>> ParameterStrings = + getAPINotesParameterSelector(*this, CXXMethod); + SmallVector<StringRef, 4> Parameters; + if (ParameterStrings) + Parameters = getAPINotesParameterSelectorRefs(*ParameterStrings); for (auto Reader : Readers) { if (auto Context = UnwindTagContext(TagContext, APINotes)) { std::string MethodName; @@ -1223,6 +1262,12 @@ void Sema::ProcessAPINotes(Decl *D) { auto Info = Reader->lookupCXXMethod(Context->id, MethodName); ProcessVersionedAPINotes(*this, CXXMethod, Info); + + if (ParameterStrings) { + Info = + Reader->lookupCXXMethod(Context->id, MethodName, Parameters); + ProcessVersionedAPINotes(*this, CXXMethod, Info); + } } } } diff --git a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes new file mode 100644 index 0000000000000..2a9f9c921347b --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes @@ -0,0 +1,126 @@ +--- +Name: WhereParametersSema +Functions: +- Name: makeWidget + Where: + Parameters: + - int + SwiftName: makeIntWidget(_:) +- Name: makeWidget + Where: + Parameters: + - double + SwiftName: makeDoubleWidget(_:) +- Name: makeWidget + Where: + Parameters: [] + SwiftName: makeCurrentWidget() +- Name: broadGlobal + SwiftPrivate: true +- Name: coexistGlobal + SwiftPrivate: true +- Name: coexistGlobal + Where: + Parameters: + - int + SwiftName: coexistGlobalInt(_:) +- Name: mismatchGlobal + Where: + Parameters: + - int + SwiftName: shouldNotApplyGlobal(_:) +- Name: aliasGlobal + Where: + Parameters: + - int + SwiftName: shouldNotApplyAliasGlobal(_:) +- Name: rawIntGlobal + Where: + Parameters: + - int + SwiftName: rawIntGlobal(_:) +- Name: constValueGlobal + Where: + Parameters: + - int + SwiftName: constValueGlobal(_:) +Namespaces: +- Name: SelectorNamespace + Functions: + - Name: makeNamespaced + Where: + Parameters: + - int + SwiftName: makeNamespacedInt(_:) + - Name: makeNamespaced + Where: + Parameters: + - double + SwiftName: makeNamespacedDouble(_:) +Tags: +- Name: SelectorWidget + Methods: + - Name: setValue + Where: + Parameters: + - int + SwiftName: setIntValue(_:) + - Name: setValue + Where: + Parameters: + - double + SwiftName: setDoubleValue(_:) + - Name: setValue + Where: + Parameters: [] + SwiftName: currentValue() + - Name: broad + SwiftPrivate: true + - Name: coexist + SwiftPrivate: true + - Name: coexist + Where: + Parameters: + - int + SwiftName: coexistInt(_:) + - Name: defaults + Where: + Parameters: + - int + - double + SwiftName: defaultsWithTwoParameters(_:_:) + - Name: configure + Where: + Parameters: + - int + SwiftName: configureInt(_:) + - Name: mismatch + Where: + Parameters: + - int + SwiftName: shouldNotApplyMethod(_:) + - Name: alias + Where: + Parameters: + - int + SwiftName: shouldNotApplyAliasMethod(_:) + - Name: rawInt + Where: + Parameters: + - int + SwiftName: rawInt(_:) + - Name: constValue + Where: + Parameters: + - int + SwiftName: constValue(_:) + - Name: operator+ + Where: + Parameters: + - int + SwiftName: plusInt(_:) + - Name: operator+ + Where: + Parameters: + - double + SwiftName: plusDouble(_:) diff --git a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h new file mode 100644 index 0000000000000..1cd10676e5533 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h @@ -0,0 +1,51 @@ +#ifndef WHERE_PARAMETERS_SEMA_H +#define WHERE_PARAMETERS_SEMA_H + +using AliasInt = int; + +void makeWidget(int); +void makeWidget(double); +void makeWidget(); + +void broadGlobal(int); +void broadGlobal(double); + +void coexistGlobal(int); +void coexistGlobal(double); + +void mismatchGlobal(float); +void aliasGlobal(AliasInt); +void rawIntGlobal(int); +void constValueGlobal(const int); + +namespace SelectorNamespace { +void makeNamespaced(int); +void makeNamespaced(double); +} + +struct SelectorWidget { + void setValue(int); + void setValue(double); + void setValue(); + + void broad(int); + void broad(double); + + void coexist(int); + void coexist(double); + + void defaults(int, double = 0); + void defaults(int); + + static void configure(int); + + void mismatch(float); + void alias(AliasInt); + void rawInt(int); + void constValue(const int); + + SelectorWidget operator+(int); + SelectorWidget operator+(double); +}; + +#endif // WHERE_PARAMETERS_SEMA_H diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap b/clang/test/APINotes/Inputs/Headers/module.modulemap index 7bcf33644a14f..592d482ea7a57 100644 --- a/clang/test/APINotes/Inputs/Headers/module.modulemap +++ b/clang/test/APINotes/Inputs/Headers/module.modulemap @@ -70,3 +70,8 @@ module UnsafeBufferUsage { header "UnsafeBufferUsage.h" export * } + +module WhereParametersSema { + header "WhereParametersSema.h" + export * +} diff --git a/clang/test/APINotes/where-parameters-sema.cpp b/clang/test/APINotes/where-parameters-sema.cpp new file mode 100644 index 0000000000000..7d1dcf7b61abb --- /dev/null +++ b/clang/test/APINotes/where-parameters-sema.cpp @@ -0,0 +1,110 @@ +// RUN: rm -rf %t && mkdir -p %t +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -fsyntax-only -I %S/Inputs/Headers %s -x c++ +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter makeWidget -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-OVERLOADS %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter broadGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-BROAD %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter coexistGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-COEXIST %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter mismatchGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-MISMATCH %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter aliasGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-ALIAS %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter rawIntGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-RAW-INT %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter constValueGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-CONST %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorNamespace::makeNamespaced -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-NAMESPACE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::setValue -x c++ | FileCheck --check-prefix=CHECK-METHOD-OVERLOADS %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::broad -x c++ | FileCheck --check-prefix=CHECK-METHOD-BROAD %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::coexist -x c++ | FileCheck --check-prefix=CHECK-METHOD-COEXIST %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::defaults -x c++ | FileCheck --check-prefix=CHECK-METHOD-DEFAULTS %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::configure -x c++ | FileCheck --check-prefix=CHECK-METHOD-STATIC %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::mismatch -x c++ | FileCheck --check-prefix=CHECK-METHOD-MISMATCH %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::alias -x c++ | FileCheck --check-prefix=CHECK-METHOD-ALIAS %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::rawInt -x c++ | FileCheck --check-prefix=CHECK-METHOD-RAW-INT %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::constValue -x c++ | FileCheck --check-prefix=CHECK-METHOD-CONST %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::operator+ -x c++ | FileCheck --check-prefix=CHECK-METHOD-OPERATOR %s + +#include "WhereParametersSema.h" + +// CHECK-GLOBAL-OVERLOADS: FunctionDecl {{.+}} makeWidget 'void (int)' +// CHECK-GLOBAL-OVERLOADS-NEXT: ParmVarDecl {{.+}} 'int' +// CHECK-GLOBAL-OVERLOADS-NEXT: SwiftNameAttr {{.+}} "makeIntWidget(_:)" +// CHECK-GLOBAL-OVERLOADS: FunctionDecl {{.+}} makeWidget 'void (double)' +// CHECK-GLOBAL-OVERLOADS-NEXT: ParmVarDecl {{.+}} 'double' +// CHECK-GLOBAL-OVERLOADS-NEXT: SwiftNameAttr {{.+}} "makeDoubleWidget(_:)" +// CHECK-GLOBAL-OVERLOADS: FunctionDecl {{.+}} makeWidget 'void ()' +// CHECK-GLOBAL-OVERLOADS-NEXT: SwiftNameAttr {{.+}} "makeCurrentWidget()" + +// CHECK-GLOBAL-BROAD: FunctionDecl {{.+}} broadGlobal 'void (int)' +// CHECK-GLOBAL-BROAD: SwiftPrivateAttr +// CHECK-GLOBAL-BROAD: FunctionDecl {{.+}} broadGlobal 'void (double)' +// CHECK-GLOBAL-BROAD: SwiftPrivateAttr + +// CHECK-GLOBAL-COEXIST: FunctionDecl {{.+}} coexistGlobal 'void (int)' +// CHECK-GLOBAL-COEXIST: SwiftPrivateAttr +// CHECK-GLOBAL-COEXIST: SwiftNameAttr {{.+}} "coexistGlobalInt(_:)" +// CHECK-GLOBAL-COEXIST: FunctionDecl {{.+}} coexistGlobal 'void (double)' +// CHECK-GLOBAL-COEXIST: SwiftPrivateAttr +// CHECK-GLOBAL-COEXIST-NOT: SwiftNameAttr + +// CHECK-GLOBAL-MISMATCH: FunctionDecl {{.+}} mismatchGlobal 'void (float)' +// CHECK-GLOBAL-MISMATCH-NOT: SwiftNameAttr + +// CHECK-GLOBAL-ALIAS: FunctionDecl {{.+}} aliasGlobal 'void (AliasInt)' +// CHECK-GLOBAL-ALIAS-NOT: SwiftNameAttr + +// CHECK-GLOBAL-RAW-INT: FunctionDecl {{.+}} rawIntGlobal 'void (int)' +// CHECK-GLOBAL-RAW-INT: SwiftNameAttr {{.+}} "rawIntGlobal(_:)" + +// CHECK-GLOBAL-CONST: FunctionDecl {{.+}} constValueGlobal 'void (const int)' +// CHECK-GLOBAL-CONST: SwiftNameAttr {{.+}} "constValueGlobal(_:)" + +// CHECK-GLOBAL-NAMESPACE: FunctionDecl {{.+}} makeNamespaced 'void (int)' +// CHECK-GLOBAL-NAMESPACE-NEXT: ParmVarDecl {{.+}} 'int' +// CHECK-GLOBAL-NAMESPACE-NEXT: SwiftNameAttr {{.+}} "makeNamespacedInt(_:)" +// CHECK-GLOBAL-NAMESPACE: FunctionDecl {{.+}} makeNamespaced 'void (double)' +// CHECK-GLOBAL-NAMESPACE-NEXT: ParmVarDecl {{.+}} 'double' +// CHECK-GLOBAL-NAMESPACE-NEXT: SwiftNameAttr {{.+}} "makeNamespacedDouble(_:)" + +// CHECK-METHOD-OVERLOADS: CXXMethodDecl {{.+}} setValue 'void (int)' +// CHECK-METHOD-OVERLOADS-NEXT: ParmVarDecl {{.+}} 'int' +// CHECK-METHOD-OVERLOADS-NEXT: SwiftNameAttr {{.+}} "setIntValue(_:)" +// CHECK-METHOD-OVERLOADS: CXXMethodDecl {{.+}} setValue 'void (double)' +// CHECK-METHOD-OVERLOADS-NEXT: ParmVarDecl {{.+}} 'double' +// CHECK-METHOD-OVERLOADS-NEXT: SwiftNameAttr {{.+}} "setDoubleValue(_:)" +// CHECK-METHOD-OVERLOADS: CXXMethodDecl {{.+}} setValue 'void ()' +// CHECK-METHOD-OVERLOADS-NEXT: SwiftNameAttr {{.+}} "currentValue()" + +// CHECK-METHOD-BROAD: CXXMethodDecl {{.+}} broad 'void (int)' +// CHECK-METHOD-BROAD: SwiftPrivateAttr +// CHECK-METHOD-BROAD: CXXMethodDecl {{.+}} broad 'void (double)' +// CHECK-METHOD-BROAD: SwiftPrivateAttr + +// CHECK-METHOD-COEXIST: CXXMethodDecl {{.+}} coexist 'void (int)' +// CHECK-METHOD-COEXIST: SwiftPrivateAttr +// CHECK-METHOD-COEXIST: SwiftNameAttr {{.+}} "coexistInt(_:)" +// CHECK-METHOD-COEXIST: CXXMethodDecl {{.+}} coexist 'void (double)' +// CHECK-METHOD-COEXIST: SwiftPrivateAttr +// CHECK-METHOD-COEXIST-NOT: SwiftNameAttr + +// CHECK-METHOD-DEFAULTS: CXXMethodDecl {{.+}} defaults 'void (int, double)' +// CHECK-METHOD-DEFAULTS: SwiftNameAttr {{.+}} "defaultsWithTwoParameters(_:_:)" +// CHECK-METHOD-DEFAULTS: CXXMethodDecl {{.+}} defaults 'void (int)' +// CHECK-METHOD-DEFAULTS-NOT: SwiftNameAttr + +// CHECK-METHOD-STATIC: CXXMethodDecl {{.+}} configure 'void (int)' static +// CHECK-METHOD-STATIC: SwiftNameAttr {{.+}} "configureInt(_:)" + +// CHECK-METHOD-MISMATCH: CXXMethodDecl {{.+}} mismatch 'void (float)' +// CHECK-METHOD-MISMATCH-NOT: SwiftNameAttr + +// CHECK-METHOD-ALIAS: CXXMethodDecl {{.+}} alias 'void (AliasInt)' +// CHECK-METHOD-ALIAS-NOT: SwiftNameAttr + +// CHECK-METHOD-RAW-INT: CXXMethodDecl {{.+}} rawInt 'void (int)' +// CHECK-METHOD-RAW-INT: SwiftNameAttr {{.+}} "rawInt(_:)" + +// CHECK-METHOD-CONST: CXXMethodDecl {{.+}} constValue 'void (const int)' +// CHECK-METHOD-CONST: SwiftNameAttr {{.+}} "constValue(_:)" + +// CHECK-METHOD-OPERATOR: CXXMethodDecl {{.+}} operator+ 'SelectorWidget (int)' +// CHECK-METHOD-OPERATOR-NEXT: ParmVarDecl {{.+}} 'int' +// CHECK-METHOD-OPERATOR-NEXT: SwiftNameAttr {{.+}} "plusInt(_:)" +// CHECK-METHOD-OPERATOR: CXXMethodDecl {{.+}} operator+ 'SelectorWidget (double)' +// CHECK-METHOD-OPERATOR-NEXT: ParmVarDecl {{.+}} 'double' +// CHECK-METHOD-OPERATOR-NEXT: SwiftNameAttr {{.+}} "plusDouble(_:)" >From f704aa74f2751e803d59aa7223ed5c2ce4cbb686 Mon Sep 17 00:00:00 2001 From: stoeckoverflow <[email protected]> Date: Thu, 2 Jul 2026 11:31:45 +0200 Subject: [PATCH 2/5] [APINotes] Add permissive alias and nullability matching for Where.Parameters --- clang/lib/Sema/SemaAPINotes.cpp | 114 +++++++++++++----- .../Headers/WhereParametersSema.apinotes | 34 +++++- .../Inputs/Headers/WhereParametersSema.h | 4 + clang/test/APINotes/where-parameters-sema.cpp | 22 +++- 4 files changed, 142 insertions(+), 32 deletions(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 269b96a57fa4b..cae73fe3f4e3b 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -993,18 +993,56 @@ UnwindTagContext(TagDecl *DC, api_notes::APINotesManager &APINotes) { return std::nullopt; } -static std::optional<SmallVector<std::string, 4>> -getAPINotesParameterSelector(const Sema &S, const FunctionDecl *FD) { +static void stripAPINotesParameterNullability(QualType &ParamType) { + while (true) { + if (!AttributedType::stripOuterNullability(ParamType)) + return; + } +} + +// Print the APINotes selector spelling for one parameter. The source-spelled +// selector is tried first. The desugared spelling is only a permissive +// fallback. +static std::string getAPINotesParameterSelectorSpelling( + QualType ParamType, const ASTContext &Context, const PrintingPolicy &Policy, + bool Desugar) { + ParamType.removeLocalConst(); + stripAPINotesParameterNullability(ParamType); + + if (Desugar) { + ParamType = ParamType.getDesugaredType(Context); + ParamType.removeLocalConst(); + stripAPINotesParameterNullability(ParamType); + } + + return ParamType.getAsString(Policy); +} + +static std::optional<SmallVector<SmallVector<std::string, 4>, 2>> +getAPINotesParameterSelectorCandidates(const Sema &S, const FunctionDecl *FD) { const auto *FPT = FD->getType()->getAs<FunctionProtoType>(); if (!FPT) return std::nullopt; - SmallVector<std::string, 4> Parameters; - Parameters.reserve(FPT->getNumParams()); - for (QualType ParamType : FPT->param_types()) - Parameters.push_back(ParamType.getUnqualifiedType().getAsString( - S.Context.getPrintingPolicy())); - return Parameters; + SmallVector<std::string, 4> SourceParameters; + SmallVector<std::string, 4> DesugaredParameters; + SourceParameters.reserve(FPT->getNumParams()); + DesugaredParameters.reserve(FPT->getNumParams()); + + const PrintingPolicy &Policy = S.Context.getPrintingPolicy(); + for (QualType ParamType : FPT->param_types()) { + SourceParameters.push_back(getAPINotesParameterSelectorSpelling( + ParamType, S.Context, Policy, /*Desugar=*/false)); + DesugaredParameters.push_back(getAPINotesParameterSelectorSpelling( + ParamType, S.Context, Policy, /*Desugar=*/true)); + } + + SmallVector<SmallVector<std::string, 4>, 2> Candidates; + Candidates.push_back(std::move(SourceParameters)); + if (Candidates.front() != DesugaredParameters) + Candidates.push_back(std::move(DesugaredParameters)); + + return Candidates; } static SmallVector<StringRef, 4> @@ -1016,6 +1054,26 @@ getAPINotesParameterSelectorRefs(ArrayRef<std::string> Strings) { return Refs; } +// Apply the first exact selector entry found. This preserves source-spelling +// precedence over the desugared fallback and avoids applying multiple exact +// entries for the same declaration. +template <typename SpecificDecl, typename LookupExactFn> +static void processExactAPINotes( + Sema &S, SpecificDecl *D, + ArrayRef<SmallVector<std::string, 4>> ParameterSelectorCandidates, + LookupExactFn LookupExact) { + for (ArrayRef<std::string> ParameterStrings : ParameterSelectorCandidates) { + SmallVector<StringRef, 4> Parameters = + getAPINotesParameterSelectorRefs(ParameterStrings); + auto Info = LookupExact(Parameters); + if (Info.size() == 0) + continue; + + ProcessVersionedAPINotes(S, D, Info); + return; + } +} + /// Process API notes that are associated with this declaration, mapping them /// to attributes as appropriate. void Sema::ProcessAPINotes(Decl *D) { @@ -1047,21 +1105,21 @@ void Sema::ProcessAPINotes(Decl *D) { // Global functions. if (auto FD = dyn_cast<FunctionDecl>(D)) { if (FD->getDeclName().isIdentifier()) { - std::optional<SmallVector<std::string, 4>> ParameterStrings = - getAPINotesParameterSelector(*this, FD); - SmallVector<StringRef, 4> Parameters; - if (ParameterStrings) - Parameters = getAPINotesParameterSelectorRefs(*ParameterStrings); + std::optional<SmallVector<SmallVector<std::string, 4>, 2>> + ParameterSelectorCandidates = + getAPINotesParameterSelectorCandidates(*this, FD); for (auto Reader : Readers) { auto Info = Reader->lookupGlobalFunction(FD->getName(), APINotesContext); ProcessVersionedAPINotes(*this, FD, Info); - if (ParameterStrings) { - Info = Reader->lookupGlobalFunction(FD->getName(), Parameters, - APINotesContext); - ProcessVersionedAPINotes(*this, FD, Info); - } + if (ParameterSelectorCandidates) + processExactAPINotes(*this, FD, *ParameterSelectorCandidates, + [&](ArrayRef<StringRef> Parameters) { + return Reader->lookupGlobalFunction( + FD->getName(), Parameters, + APINotesContext); + }); } } @@ -1245,11 +1303,9 @@ void Sema::ProcessAPINotes(Decl *D) { if (!isa<CXXConstructorDecl>(CXXMethod) && !isa<CXXDestructorDecl>(CXXMethod) && !isa<CXXConversionDecl>(CXXMethod)) { - std::optional<SmallVector<std::string, 4>> ParameterStrings = - getAPINotesParameterSelector(*this, CXXMethod); - SmallVector<StringRef, 4> Parameters; - if (ParameterStrings) - Parameters = getAPINotesParameterSelectorRefs(*ParameterStrings); + std::optional<SmallVector<SmallVector<std::string, 4>, 2>> + ParameterSelectorCandidates = + getAPINotesParameterSelectorCandidates(*this, CXXMethod); for (auto Reader : Readers) { if (auto Context = UnwindTagContext(TagContext, APINotes)) { std::string MethodName; @@ -1263,11 +1319,13 @@ void Sema::ProcessAPINotes(Decl *D) { auto Info = Reader->lookupCXXMethod(Context->id, MethodName); ProcessVersionedAPINotes(*this, CXXMethod, Info); - if (ParameterStrings) { - Info = - Reader->lookupCXXMethod(Context->id, MethodName, Parameters); - ProcessVersionedAPINotes(*this, CXXMethod, Info); - } + if (ParameterSelectorCandidates) + processExactAPINotes(*this, CXXMethod, + *ParameterSelectorCandidates, + [&](ArrayRef<StringRef> Parameters) { + return Reader->lookupCXXMethod( + Context->id, MethodName, Parameters); + }); } } } diff --git a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes index 2a9f9c921347b..2f3b31148661f 100644 --- a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes +++ b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes @@ -33,7 +33,22 @@ Functions: Where: Parameters: - int - SwiftName: shouldNotApplyAliasGlobal(_:) + SwiftName: aliasGlobal(_:) +- Name: aliasPrecedenceGlobal + Where: + Parameters: + - int + SwiftName: fallbackAliasPrecedenceGlobal(_:) +- Name: aliasPrecedenceGlobal + Where: + Parameters: + - AliasInt + SwiftName: aliasPrecedenceGlobal(_:) +- Name: nullableGlobal + Where: + Parameters: + - 'char *' + SwiftName: nullableGlobal(_:) - Name: rawIntGlobal Where: Parameters: @@ -103,7 +118,22 @@ Tags: Where: Parameters: - int - SwiftName: shouldNotApplyAliasMethod(_:) + SwiftName: alias(_:) + - Name: aliasPrecedence + Where: + Parameters: + - int + SwiftName: fallbackAliasPrecedence(_:) + - Name: aliasPrecedence + Where: + Parameters: + - AliasInt + SwiftName: aliasPrecedence(_:) + - Name: nullable + Where: + Parameters: + - 'char *' + SwiftName: nullable(_:) - Name: rawInt Where: Parameters: diff --git a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h index 1cd10676e5533..85990e4ad7434 100644 --- a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h +++ b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h @@ -15,6 +15,8 @@ void coexistGlobal(double); void mismatchGlobal(float); void aliasGlobal(AliasInt); +void aliasPrecedenceGlobal(AliasInt); +void nullableGlobal(char * _Nonnull); void rawIntGlobal(int); void constValueGlobal(const int); @@ -41,6 +43,8 @@ struct SelectorWidget { void mismatch(float); void alias(AliasInt); + void aliasPrecedence(AliasInt); + void nullable(char * _Nonnull); void rawInt(int); void constValue(const int); diff --git a/clang/test/APINotes/where-parameters-sema.cpp b/clang/test/APINotes/where-parameters-sema.cpp index 7d1dcf7b61abb..6e19719593197 100644 --- a/clang/test/APINotes/where-parameters-sema.cpp +++ b/clang/test/APINotes/where-parameters-sema.cpp @@ -5,6 +5,8 @@ // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter coexistGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-COEXIST %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter mismatchGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-MISMATCH %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter aliasGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-ALIAS %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter aliasPrecedenceGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-ALIAS-PRECEDENCE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter nullableGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-NULLABILITY %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter rawIntGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-RAW-INT %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter constValueGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-CONST %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorNamespace::makeNamespaced -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-NAMESPACE %s @@ -15,6 +17,8 @@ // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::configure -x c++ | FileCheck --check-prefix=CHECK-METHOD-STATIC %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::mismatch -x c++ | FileCheck --check-prefix=CHECK-METHOD-MISMATCH %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::alias -x c++ | FileCheck --check-prefix=CHECK-METHOD-ALIAS %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::aliasPrecedence -x c++ | FileCheck --check-prefix=CHECK-METHOD-ALIAS-PRECEDENCE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::nullable -x c++ | FileCheck --check-prefix=CHECK-METHOD-NULLABILITY %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::rawInt -x c++ | FileCheck --check-prefix=CHECK-METHOD-RAW-INT %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::constValue -x c++ | FileCheck --check-prefix=CHECK-METHOD-CONST %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::operator+ -x c++ | FileCheck --check-prefix=CHECK-METHOD-OPERATOR %s @@ -46,7 +50,14 @@ // CHECK-GLOBAL-MISMATCH-NOT: SwiftNameAttr // CHECK-GLOBAL-ALIAS: FunctionDecl {{.+}} aliasGlobal 'void (AliasInt)' -// CHECK-GLOBAL-ALIAS-NOT: SwiftNameAttr +// CHECK-GLOBAL-ALIAS: SwiftNameAttr {{.+}} "aliasGlobal(_:)" + +// CHECK-GLOBAL-ALIAS-PRECEDENCE: FunctionDecl {{.+}} aliasPrecedenceGlobal 'void (AliasInt)' +// CHECK-GLOBAL-ALIAS-PRECEDENCE-NOT: fallbackAliasPrecedenceGlobal +// CHECK-GLOBAL-ALIAS-PRECEDENCE: SwiftNameAttr {{.+}} "aliasPrecedenceGlobal(_:)" + +// CHECK-GLOBAL-NULLABILITY: FunctionDecl {{.+}} nullableGlobal 'void (char * _Nonnull)' +// CHECK-GLOBAL-NULLABILITY: SwiftNameAttr {{.+}} "nullableGlobal(_:)" // CHECK-GLOBAL-RAW-INT: FunctionDecl {{.+}} rawIntGlobal 'void (int)' // CHECK-GLOBAL-RAW-INT: SwiftNameAttr {{.+}} "rawIntGlobal(_:)" @@ -94,7 +105,14 @@ // CHECK-METHOD-MISMATCH-NOT: SwiftNameAttr // CHECK-METHOD-ALIAS: CXXMethodDecl {{.+}} alias 'void (AliasInt)' -// CHECK-METHOD-ALIAS-NOT: SwiftNameAttr +// CHECK-METHOD-ALIAS: SwiftNameAttr {{.+}} "alias(_:)" + +// CHECK-METHOD-ALIAS-PRECEDENCE: CXXMethodDecl {{.+}} aliasPrecedence 'void (AliasInt)' +// CHECK-METHOD-ALIAS-PRECEDENCE-NOT: fallbackAliasPrecedence +// CHECK-METHOD-ALIAS-PRECEDENCE: SwiftNameAttr {{.+}} "aliasPrecedence(_:)" + +// CHECK-METHOD-NULLABILITY: CXXMethodDecl {{.+}} nullable 'void (char * _Nonnull)' +// CHECK-METHOD-NULLABILITY: SwiftNameAttr {{.+}} "nullable(_:)" // CHECK-METHOD-RAW-INT: CXXMethodDecl {{.+}} rawInt 'void (int)' // CHECK-METHOD-RAW-INT: SwiftNameAttr {{.+}} "rawInt(_:)" >From 06e6217d3f09728c67e7e6ec00ccd9e84abc3b95 Mon Sep 17 00:00:00 2001 From: StoeckOverflow <[email protected]> Date: Fri, 3 Jul 2026 08:48:03 +0200 Subject: [PATCH 3/5] Update clang/lib/Sema/SemaAPINotes.cpp Co-authored-by: John Hui <[email protected]> --- clang/lib/Sema/SemaAPINotes.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index cae73fe3f4e3b..448c6b410fbfc 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -1105,9 +1105,8 @@ void Sema::ProcessAPINotes(Decl *D) { // Global functions. if (auto FD = dyn_cast<FunctionDecl>(D)) { if (FD->getDeclName().isIdentifier()) { - std::optional<SmallVector<SmallVector<std::string, 4>, 2>> - ParameterSelectorCandidates = - getAPINotesParameterSelectorCandidates(*this, FD); + auto ParameterSelectorCandidates = + getAPINotesParameterSelectorCandidates(*this, FD); for (auto Reader : Readers) { auto Info = Reader->lookupGlobalFunction(FD->getName(), APINotesContext); >From 75d4bc723bbd9ae515c49c9c9b5f5f001bd255db Mon Sep 17 00:00:00 2001 From: stoeckoverflow <[email protected]> Date: Fri, 3 Jul 2026 15:48:42 +0200 Subject: [PATCH 4/5] [APINotes] Refine Where.Parameters selector matching --- clang/lib/Sema/SemaAPINotes.cpp | 121 +++++++++++------- .../Headers/WhereParametersSema.apinotes | 10 ++ .../Inputs/Headers/WhereParametersSema.h | 4 + clang/test/APINotes/where-parameters-sema.cpp | 8 ++ 4 files changed, 98 insertions(+), 45 deletions(-) diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index 448c6b410fbfc..a2176a3b8b976 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -1000,6 +1000,40 @@ static void stripAPINotesParameterNullability(QualType &ParamType) { } } +struct APINotesParameterSelector { + SmallVector<std::string, 4> Parameters; + + SmallVector<StringRef, 4> getParameterRefs() const { + SmallVector<StringRef, 4> Refs; + Refs.reserve(Parameters.size()); + for (const std::string &Parameter : Parameters) + Refs.push_back(Parameter); + return Refs; + } + + bool operator==(const APINotesParameterSelector &Other) const { + return Parameters == Other.Parameters; + } +}; + +struct APINotesParameterSelectorCandidates { + APINotesParameterSelector Source; + std::optional<APINotesParameterSelector> Desugared; +}; + +static PrintingPolicy +getAPINotesParameterSelectorPrintingPolicy(const ASTContext &Context) { + PrintingPolicy Policy(Context.getLangOpts()); + Policy.PrintAsCanonical = false; + Policy.FullyQualifiedName = false; + Policy.SuppressScope = false; + Policy.UsePreferredNames = false; + Policy.MSVCFormatting = false; + Policy.SplitTemplateClosers = false; + Policy.IncludeNewlines = false; + return Policy; +} + // Print the APINotes selector spelling for one parameter. The source-spelled // selector is tried first. The desugared spelling is only a permissive // fallback. @@ -1018,60 +1052,58 @@ static std::string getAPINotesParameterSelectorSpelling( return ParamType.getAsString(Policy); } -static std::optional<SmallVector<SmallVector<std::string, 4>, 2>> +static std::optional<APINotesParameterSelectorCandidates> getAPINotesParameterSelectorCandidates(const Sema &S, const FunctionDecl *FD) { const auto *FPT = FD->getType()->getAs<FunctionProtoType>(); if (!FPT) return std::nullopt; - SmallVector<std::string, 4> SourceParameters; - SmallVector<std::string, 4> DesugaredParameters; - SourceParameters.reserve(FPT->getNumParams()); - DesugaredParameters.reserve(FPT->getNumParams()); + APINotesParameterSelectorCandidates Candidates; + APINotesParameterSelector Desugared; + Candidates.Source.Parameters.reserve(FPT->getNumParams()); + Desugared.Parameters.reserve(FPT->getNumParams()); - const PrintingPolicy &Policy = S.Context.getPrintingPolicy(); + const PrintingPolicy Policy = + getAPINotesParameterSelectorPrintingPolicy(S.Context); for (QualType ParamType : FPT->param_types()) { - SourceParameters.push_back(getAPINotesParameterSelectorSpelling( - ParamType, S.Context, Policy, /*Desugar=*/false)); - DesugaredParameters.push_back(getAPINotesParameterSelectorSpelling( + Candidates.Source.Parameters.push_back( + getAPINotesParameterSelectorSpelling(ParamType, S.Context, Policy, + /*Desugar=*/false)); + Desugared.Parameters.push_back(getAPINotesParameterSelectorSpelling( ParamType, S.Context, Policy, /*Desugar=*/true)); } - SmallVector<SmallVector<std::string, 4>, 2> Candidates; - Candidates.push_back(std::move(SourceParameters)); - if (Candidates.front() != DesugaredParameters) - Candidates.push_back(std::move(DesugaredParameters)); + if (!(Candidates.Source == Desugared)) + Candidates.Desugared = std::move(Desugared); return Candidates; } -static SmallVector<StringRef, 4> -getAPINotesParameterSelectorRefs(ArrayRef<std::string> Strings) { - SmallVector<StringRef, 4> Refs; - Refs.reserve(Strings.size()); - for (const std::string &String : Strings) - Refs.push_back(String); - return Refs; -} - // Apply the first exact selector entry found. This preserves source-spelling // precedence over the desugared fallback and avoids applying multiple exact // entries for the same declaration. -template <typename SpecificDecl, typename LookupExactFn> +template <typename SpecificInfo, typename SpecificDecl> static void processExactAPINotes( Sema &S, SpecificDecl *D, - ArrayRef<SmallVector<std::string, 4>> ParameterSelectorCandidates, - LookupExactFn LookupExact) { - for (ArrayRef<std::string> ParameterStrings : ParameterSelectorCandidates) { - SmallVector<StringRef, 4> Parameters = - getAPINotesParameterSelectorRefs(ParameterStrings); + const APINotesParameterSelectorCandidates &ParameterSelectorCandidates, + llvm::function_ref<api_notes::APINotesReader::VersionedInfo<SpecificInfo>( + ArrayRef<StringRef>)> + LookupExact) { + auto ProcessSelector = [&](const APINotesParameterSelector &Selector) { + SmallVector<StringRef, 4> Parameters = Selector.getParameterRefs(); auto Info = LookupExact(Parameters); if (Info.size() == 0) - continue; + return false; ProcessVersionedAPINotes(S, D, Info); + return true; + }; + + if (ProcessSelector(ParameterSelectorCandidates.Source)) return; - } + + if (ParameterSelectorCandidates.Desugared) + ProcessSelector(*ParameterSelectorCandidates.Desugared); } /// Process API notes that are associated with this declaration, mapping them @@ -1113,12 +1145,12 @@ void Sema::ProcessAPINotes(Decl *D) { ProcessVersionedAPINotes(*this, FD, Info); if (ParameterSelectorCandidates) - processExactAPINotes(*this, FD, *ParameterSelectorCandidates, - [&](ArrayRef<StringRef> Parameters) { - return Reader->lookupGlobalFunction( - FD->getName(), Parameters, - APINotesContext); - }); + processExactAPINotes<api_notes::GlobalFunctionInfo>( + *this, FD, *ParameterSelectorCandidates, + [&](ArrayRef<StringRef> Parameters) { + return Reader->lookupGlobalFunction(FD->getName(), Parameters, + APINotesContext); + }); } } @@ -1302,9 +1334,8 @@ void Sema::ProcessAPINotes(Decl *D) { if (!isa<CXXConstructorDecl>(CXXMethod) && !isa<CXXDestructorDecl>(CXXMethod) && !isa<CXXConversionDecl>(CXXMethod)) { - std::optional<SmallVector<SmallVector<std::string, 4>, 2>> - ParameterSelectorCandidates = - getAPINotesParameterSelectorCandidates(*this, CXXMethod); + auto ParameterSelectorCandidates = + getAPINotesParameterSelectorCandidates(*this, CXXMethod); for (auto Reader : Readers) { if (auto Context = UnwindTagContext(TagContext, APINotes)) { std::string MethodName; @@ -1319,12 +1350,12 @@ void Sema::ProcessAPINotes(Decl *D) { ProcessVersionedAPINotes(*this, CXXMethod, Info); if (ParameterSelectorCandidates) - processExactAPINotes(*this, CXXMethod, - *ParameterSelectorCandidates, - [&](ArrayRef<StringRef> Parameters) { - return Reader->lookupCXXMethod( - Context->id, MethodName, Parameters); - }); + processExactAPINotes<api_notes::CXXMethodInfo>( + *this, CXXMethod, *ParameterSelectorCandidates, + [&](ArrayRef<StringRef> Parameters) { + return Reader->lookupCXXMethod(Context->id, MethodName, + Parameters); + }); } } } diff --git a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes index 2f3b31148661f..7ffee9223f8c3 100644 --- a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes +++ b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.apinotes @@ -44,6 +44,11 @@ Functions: Parameters: - AliasInt SwiftName: aliasPrecedenceGlobal(_:) +- Name: multiAliasGlobal + Where: + Parameters: + - int + SwiftName: multiAliasGlobal(_:) - Name: nullableGlobal Where: Parameters: @@ -129,6 +134,11 @@ Tags: Parameters: - AliasInt SwiftName: aliasPrecedence(_:) + - Name: multiAlias + Where: + Parameters: + - int + SwiftName: multiAlias(_:) - Name: nullable Where: Parameters: diff --git a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h index 85990e4ad7434..8226fa287b115 100644 --- a/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h +++ b/clang/test/APINotes/Inputs/Headers/WhereParametersSema.h @@ -2,6 +2,8 @@ #define WHERE_PARAMETERS_SEMA_H using AliasInt = int; +using AliasAliasInt = AliasInt; +using DeepAliasInt = AliasAliasInt; void makeWidget(int); void makeWidget(double); @@ -16,6 +18,7 @@ void coexistGlobal(double); void mismatchGlobal(float); void aliasGlobal(AliasInt); void aliasPrecedenceGlobal(AliasInt); +void multiAliasGlobal(DeepAliasInt); void nullableGlobal(char * _Nonnull); void rawIntGlobal(int); void constValueGlobal(const int); @@ -44,6 +47,7 @@ struct SelectorWidget { void mismatch(float); void alias(AliasInt); void aliasPrecedence(AliasInt); + void multiAlias(DeepAliasInt); void nullable(char * _Nonnull); void rawInt(int); void constValue(const int); diff --git a/clang/test/APINotes/where-parameters-sema.cpp b/clang/test/APINotes/where-parameters-sema.cpp index 6e19719593197..fbb7cb45b9441 100644 --- a/clang/test/APINotes/where-parameters-sema.cpp +++ b/clang/test/APINotes/where-parameters-sema.cpp @@ -6,6 +6,7 @@ // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter mismatchGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-MISMATCH %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter aliasGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-ALIAS %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter aliasPrecedenceGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-ALIAS-PRECEDENCE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter multiAliasGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-MULTI-ALIAS %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter nullableGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-NULLABILITY %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter rawIntGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-RAW-INT %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter constValueGlobal -x c++ | FileCheck --check-prefix=CHECK-GLOBAL-CONST %s @@ -18,6 +19,7 @@ // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::mismatch -x c++ | FileCheck --check-prefix=CHECK-METHOD-MISMATCH %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::alias -x c++ | FileCheck --check-prefix=CHECK-METHOD-ALIAS %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::aliasPrecedence -x c++ | FileCheck --check-prefix=CHECK-METHOD-ALIAS-PRECEDENCE %s +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::multiAlias -x c++ | FileCheck --check-prefix=CHECK-METHOD-MULTI-ALIAS %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::nullable -x c++ | FileCheck --check-prefix=CHECK-METHOD-NULLABILITY %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::rawInt -x c++ | FileCheck --check-prefix=CHECK-METHOD-RAW-INT %s // RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache/WhereParametersSema -fdisable-module-hash -fapinotes-modules -I %S/Inputs/Headers %s -ast-dump -ast-dump-filter SelectorWidget::constValue -x c++ | FileCheck --check-prefix=CHECK-METHOD-CONST %s @@ -56,6 +58,9 @@ // CHECK-GLOBAL-ALIAS-PRECEDENCE-NOT: fallbackAliasPrecedenceGlobal // CHECK-GLOBAL-ALIAS-PRECEDENCE: SwiftNameAttr {{.+}} "aliasPrecedenceGlobal(_:)" +// CHECK-GLOBAL-MULTI-ALIAS: FunctionDecl {{.+}} multiAliasGlobal 'void (DeepAliasInt)' +// CHECK-GLOBAL-MULTI-ALIAS: SwiftNameAttr {{.+}} "multiAliasGlobal(_:)" + // CHECK-GLOBAL-NULLABILITY: FunctionDecl {{.+}} nullableGlobal 'void (char * _Nonnull)' // CHECK-GLOBAL-NULLABILITY: SwiftNameAttr {{.+}} "nullableGlobal(_:)" @@ -111,6 +116,9 @@ // CHECK-METHOD-ALIAS-PRECEDENCE-NOT: fallbackAliasPrecedence // CHECK-METHOD-ALIAS-PRECEDENCE: SwiftNameAttr {{.+}} "aliasPrecedence(_:)" +// CHECK-METHOD-MULTI-ALIAS: CXXMethodDecl {{.+}} multiAlias 'void (DeepAliasInt)' +// CHECK-METHOD-MULTI-ALIAS: SwiftNameAttr {{.+}} "multiAlias(_:)" + // CHECK-METHOD-NULLABILITY: CXXMethodDecl {{.+}} nullable 'void (char * _Nonnull)' // CHECK-METHOD-NULLABILITY: SwiftNameAttr {{.+}} "nullable(_:)" >From 778b61cf434fbeb310b4107a8d283c4a984d491c Mon Sep 17 00:00:00 2001 From: stoeckoverflow <[email protected]> Date: Wed, 8 Jul 2026 18:54:53 +0200 Subject: [PATCH 5/5] [APINotes] Clean up Where.Parameters Sema matching --- clang/include/clang/APINotes/APINotesReader.h | 11 ++++--- clang/lib/APINotes/APINotesFormat.h | 9 +++--- clang/lib/APINotes/APINotesReader.cpp | 22 ++++++++----- clang/lib/Sema/SemaAPINotes.cpp | 32 +++++++------------ 4 files changed, 38 insertions(+), 36 deletions(-) diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h index 8e748b5803189..761745e20b61a 100644 --- a/clang/include/clang/APINotes/APINotesReader.h +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -22,6 +22,7 @@ #include "llvm/Support/VersionTuple.h" #include <memory> #include <optional> +#include <string> namespace clang { namespace api_notes { @@ -166,7 +167,7 @@ class APINotesReader { /// key, and a non-empty list uses an exact ordered parameter key. VersionedInfo<CXXMethodInfo> lookupCXXMethod(ContextID CtxID, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters); + llvm::ArrayRef<std::string> Parameters); /// Look for information regarding the given global variable. /// @@ -191,7 +192,7 @@ class APINotesReader { /// key, and a non-empty list uses an exact ordered parameter key. VersionedInfo<GlobalFunctionInfo> lookupGlobalFunction(llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters, + llvm::ArrayRef<std::string> Parameters, std::optional<Context> Ctx = std::nullopt); /// Look for information regarding the given enumerator. @@ -242,15 +243,17 @@ class APINotesReader { private: VersionedInfo<CXXMethodInfo> lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name); + template <typename ParameterT> VersionedInfo<CXXMethodInfo> lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters); + llvm::ArrayRef<ParameterT> Parameters); VersionedInfo<GlobalFunctionInfo> lookupGlobalFunctionImpl(llvm::StringRef Name, std::optional<Context> Ctx); + template <typename ParameterT> VersionedInfo<GlobalFunctionInfo> lookupGlobalFunctionImpl(llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters, + llvm::ArrayRef<ParameterT> Parameters, std::optional<Context> Ctx); }; diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h index df67da0845baf..f34ca2e2e363f 100644 --- a/clang/lib/APINotes/APINotesFormat.h +++ b/clang/lib/APINotes/APINotesFormat.h @@ -417,10 +417,10 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, return FunctionTableKey(ParentContextID, *NameID); } -template <typename GetIdentifierFn> +template <typename ParameterT, typename GetIdentifierFn> std::optional<FunctionTableKey> getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters, + llvm::ArrayRef<ParameterT> Parameters, GetIdentifierFn GetIdentifier) { std::optional<IdentifierID> NameID = GetIdentifier(Name); if (!NameID) @@ -428,8 +428,9 @@ getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name, llvm::SmallVector<IdentifierID, 2> ParameterTypeIDs; ParameterTypeIDs.reserve(Parameters.size()); - for (llvm::StringRef Parameter : Parameters) { - std::optional<IdentifierID> ParameterID = GetIdentifier(Parameter); + for (const ParameterT &Parameter : Parameters) { + std::optional<IdentifierID> ParameterID = + GetIdentifier(llvm::StringRef(Parameter)); if (!ParameterID) return std::nullopt; ParameterTypeIDs.push_back(*ParameterID); diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp index 9305a5f10b4e8..c51ced8f27448 100644 --- a/clang/lib/APINotes/APINotesReader.cpp +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -857,14 +857,16 @@ class APINotesReader::Implementation { llvm::SmallVectorImpl<uint64_t> &Scratch); std::optional<FunctionTableKey> getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name); + template <typename ParameterT> std::optional<FunctionTableKey> getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters); + llvm::ArrayRef<ParameterT> Parameters); std::optional<FunctionTableKey> getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name); + template <typename ParameterT> std::optional<FunctionTableKey> getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters); + llvm::ArrayRef<ParameterT> Parameters); llvm::Error readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch); @@ -899,9 +901,10 @@ APINotesReader::Implementation::getFunctionKey(uint32_t ParentContextID, }); } +template <typename ParameterT> std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey( uint32_t ParentContextID, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters) { + llvm::ArrayRef<ParameterT> Parameters) { return getFunctionKeyImpl( ParentContextID, Name, Parameters, [this](llvm::StringRef S) { return getIdentifier(S); }); @@ -914,9 +917,10 @@ std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey( return getFunctionKey(ParentContextID, Name); } +template <typename ParameterT> std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey( std::optional<Context> ParentContext, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters) { + llvm::ArrayRef<ParameterT> Parameters) { uint32_t ParentContextID = ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1); return getFunctionKey(ParentContextID, Name, Parameters); @@ -2342,7 +2346,7 @@ auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name) } auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters) + llvm::ArrayRef<std::string> Parameters) -> VersionedInfo<CXXMethodInfo> { return lookupCXXMethodImpl(CtxID, Name, Parameters); } @@ -2364,9 +2368,10 @@ auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name) return {Implementation->SwiftVersion, *Known}; } +template <typename ParameterT> auto APINotesReader::lookupCXXMethodImpl( ContextID CtxID, llvm::StringRef Name, - llvm::ArrayRef<llvm::StringRef> Parameters) + llvm::ArrayRef<ParameterT> Parameters) -> VersionedInfo<CXXMethodInfo> { if (!Implementation->CXXMethodTable) return std::nullopt; @@ -2409,7 +2414,7 @@ auto APINotesReader::lookupGlobalFunction(llvm::StringRef Name, } auto APINotesReader::lookupGlobalFunction( - llvm::StringRef Name, llvm::ArrayRef<llvm::StringRef> Parameters, + llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters, std::optional<Context> Ctx) -> VersionedInfo<GlobalFunctionInfo> { return lookupGlobalFunctionImpl(Name, Parameters, Ctx); } @@ -2432,8 +2437,9 @@ auto APINotesReader::lookupGlobalFunctionImpl(llvm::StringRef Name, return {Implementation->SwiftVersion, *Known}; } +template <typename ParameterT> auto APINotesReader::lookupGlobalFunctionImpl( - llvm::StringRef Name, llvm::ArrayRef<llvm::StringRef> Parameters, + llvm::StringRef Name, llvm::ArrayRef<ParameterT> Parameters, std::optional<Context> Ctx) -> VersionedInfo<GlobalFunctionInfo> { if (!Implementation->GlobalFunctionTable) return std::nullopt; diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp index a2176a3b8b976..1ab1eac4a5434 100644 --- a/clang/lib/Sema/SemaAPINotes.cpp +++ b/clang/lib/Sema/SemaAPINotes.cpp @@ -1003,17 +1003,13 @@ static void stripAPINotesParameterNullability(QualType &ParamType) { struct APINotesParameterSelector { SmallVector<std::string, 4> Parameters; - SmallVector<StringRef, 4> getParameterRefs() const { - SmallVector<StringRef, 4> Refs; - Refs.reserve(Parameters.size()); - for (const std::string &Parameter : Parameters) - Refs.push_back(Parameter); - return Refs; - } - bool operator==(const APINotesParameterSelector &Other) const { return Parameters == Other.Parameters; } + + bool operator!=(const APINotesParameterSelector &Other) const { + return !(*this == Other); + } }; struct APINotesParameterSelectorCandidates { @@ -1040,15 +1036,12 @@ getAPINotesParameterSelectorPrintingPolicy(const ASTContext &Context) { static std::string getAPINotesParameterSelectorSpelling( QualType ParamType, const ASTContext &Context, const PrintingPolicy &Policy, bool Desugar) { + if (Desugar) + ParamType = ParamType.getDesugaredType(Context); + ParamType.removeLocalConst(); stripAPINotesParameterNullability(ParamType); - if (Desugar) { - ParamType = ParamType.getDesugaredType(Context); - ParamType.removeLocalConst(); - stripAPINotesParameterNullability(ParamType); - } - return ParamType.getAsString(Policy); } @@ -1073,7 +1066,7 @@ getAPINotesParameterSelectorCandidates(const Sema &S, const FunctionDecl *FD) { ParamType, S.Context, Policy, /*Desugar=*/true)); } - if (!(Candidates.Source == Desugared)) + if (Candidates.Source != Desugared) Candidates.Desugared = std::move(Desugared); return Candidates; @@ -1087,11 +1080,10 @@ static void processExactAPINotes( Sema &S, SpecificDecl *D, const APINotesParameterSelectorCandidates &ParameterSelectorCandidates, llvm::function_ref<api_notes::APINotesReader::VersionedInfo<SpecificInfo>( - ArrayRef<StringRef>)> + ArrayRef<std::string>)> LookupExact) { auto ProcessSelector = [&](const APINotesParameterSelector &Selector) { - SmallVector<StringRef, 4> Parameters = Selector.getParameterRefs(); - auto Info = LookupExact(Parameters); + auto Info = LookupExact(Selector.Parameters); if (Info.size() == 0) return false; @@ -1147,7 +1139,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (ParameterSelectorCandidates) processExactAPINotes<api_notes::GlobalFunctionInfo>( *this, FD, *ParameterSelectorCandidates, - [&](ArrayRef<StringRef> Parameters) { + [&](ArrayRef<std::string> Parameters) { return Reader->lookupGlobalFunction(FD->getName(), Parameters, APINotesContext); }); @@ -1352,7 +1344,7 @@ void Sema::ProcessAPINotes(Decl *D) { if (ParameterSelectorCandidates) processExactAPINotes<api_notes::CXXMethodInfo>( *this, CXXMethod, *ParameterSelectorCandidates, - [&](ArrayRef<StringRef> Parameters) { + [&](ArrayRef<std::string> Parameters) { return Reader->lookupCXXMethod(Context->id, MethodName, Parameters); }); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
