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/2] [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/2] [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(_:)" _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
