https://github.com/StoeckOverflow updated 
https://github.com/llvm/llvm-project/pull/209408

>From 91dbf3cd44e960b2568724e0c9bf4990d7684040 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <[email protected]>
Date: Fri, 19 Jun 2026 12:57:27 +0200
Subject: [PATCH 1/5] [APINotes] Diagnose invalid Where.Parameters selectors

---
 clang/include/clang/APINotes/APINotesReader.h |  12 ++
 clang/lib/APINotes/APINotesReader.cpp         |  79 +++++++++
 clang/lib/APINotes/APINotesYAMLCompiler.cpp   |  71 +++++++-
 clang/lib/Sema/SemaAPINotes.cpp               | 155 ++++++++++++++++++
 .../WhereParametersDiagnostics.apinotes       |  50 ++++++
 .../Headers/WhereParametersDiagnostics.h      |  18 ++
 .../APINotes/Inputs/Headers/module.modulemap  |   5 +
 .../APINotes.apinotes                         |  66 ++++++++
 .../WhereParametersDiagnostics.h              |  16 ++
 .../APINotes/where-parameters-diagnostics.cpp |  41 +++++
 10 files changed, 511 insertions(+), 2 deletions(-)
 create mode 100644 
clang/test/APINotes/Inputs/Headers/WhereParametersDiagnostics.apinotes
 create mode 100644 
clang/test/APINotes/Inputs/Headers/WhereParametersDiagnostics.h
 create mode 100644 
clang/test/APINotes/Inputs/WhereParametersDuplicateSelectorDiag/APINotes.apinotes
 create mode 100644 
clang/test/APINotes/Inputs/WhereParametersDuplicateSelectorDiag/WhereParametersDiagnostics.h
 create mode 100644 clang/test/APINotes/where-parameters-diagnostics.cpp

diff --git a/clang/include/clang/APINotes/APINotesReader.h 
b/clang/include/clang/APINotes/APINotesReader.h
index 761745e20b61a..640be8ad5acb3 100644
--- a/clang/include/clang/APINotes/APINotesReader.h
+++ b/clang/include/clang/APINotes/APINotesReader.h
@@ -17,6 +17,7 @@
 
 #include "clang/APINotes/Types.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "llvm/Support/VersionTuple.h"
@@ -169,6 +170,11 @@ class APINotesReader {
   lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
                   llvm::ArrayRef<std::string> Parameters);
 
+  /// Collect exact parameter selectors stored for the given C++ method.
+  void collectCXXMethodParameterSelectors(
+      ContextID CtxID, llvm::StringRef Name,
+      llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors);
+
   /// Look for information regarding the given global variable.
   ///
   /// \param Name The name of the global variable.
@@ -195,6 +201,12 @@ class APINotesReader {
                        llvm::ArrayRef<std::string> Parameters,
                        std::optional<Context> Ctx = std::nullopt);
 
+  /// Collect exact parameter selectors stored for the given global function.
+  void collectGlobalFunctionParameterSelectors(
+      llvm::StringRef Name,
+      llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors,
+      std::optional<Context> Ctx = std::nullopt);
+
   /// Look for information regarding the given enumerator.
   ///
   /// \param Name The name of the enumerator.
diff --git a/clang/lib/APINotes/APINotesReader.cpp 
b/clang/lib/APINotes/APINotesReader.cpp
index 888844fbb6cd9..944beccb9d2df 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -19,6 +19,8 @@
 #include "llvm/Bitstream/BitstreamReader.h"
 #include "llvm/Support/DJB.h"
 #include "llvm/Support/OnDiskHashTable.h"
+#include <string>
+#include <utility>
 
 namespace clang {
 namespace api_notes {
@@ -833,6 +835,16 @@ class APINotesReader::Implementation {
   /// optional if the string is unknown.
   std::optional<IdentifierID> getIdentifier(llvm::StringRef Str);
 
+  /// Retrieve the identifier string for the given ID, or an empty optional if
+  /// the ID is unknown.
+  std::optional<llvm::StringRef> getIdentifierString(IdentifierID ID);
+
+  /// Collect exact parameter selectors stored in the given function-like 
table.
+  template <typename TableT>
+  void collectFunctionParameterSelectors(
+      TableT *Table, uint32_t ParentContextID, llvm::StringRef Name,
+      llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors);
+
   /// Retrieve the selector ID for the given selector, or an empty
   /// optional if the string is unknown.
   std::optional<SelectorID> getSelector(ObjCSelectorRef Selector);
@@ -893,6 +905,56 @@ 
APINotesReader::Implementation::getIdentifier(llvm::StringRef Str) {
   return *Known;
 }
 
+std::optional<llvm::StringRef>
+APINotesReader::Implementation::getIdentifierString(IdentifierID ID) {
+  if (!IdentifierTable)
+    return std::nullopt;
+
+  if (ID == IdentifierID(0))
+    return llvm::StringRef();
+
+  for (llvm::StringRef Identifier : IdentifierTable->keys()) {
+    auto KnownID = IdentifierTable->find(Identifier);
+    if (KnownID != IdentifierTable->end() && *KnownID == ID)
+      return Identifier;
+  }
+
+  return std::nullopt;
+}
+
+template <typename TableT>
+void APINotesReader::Implementation::collectFunctionParameterSelectors(
+    TableT *Table, uint32_t ParentContextID, llvm::StringRef Name,
+    llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors) {
+  if (!Table)
+    return;
+
+  std::optional<IdentifierID> NameID = getIdentifier(Name);
+  if (!NameID)
+    return;
+
+  for (auto I = Table->key_begin(), E = Table->key_end(); I != E; ++I) {
+    FunctionTableKey Key = I.getInternalKey();
+    if (Key.parentContextID != ParentContextID ||
+        Key.nameID != static_cast<unsigned>(*NameID) || !Key.parameterTypeIDs)
+      continue;
+
+    llvm::SmallVector<std::string, 4> ParameterSelector;
+    ParameterSelector.reserve(Key.parameterTypeIDs->size());
+    bool Failed = false;
+    for (IdentifierID TypeID : *Key.parameterTypeIDs) {
+      std::optional<llvm::StringRef> TypeName = getIdentifierString(TypeID);
+      if (!TypeName) {
+        Failed = true;
+        break;
+      }
+      ParameterSelector.push_back(TypeName->str());
+    }
+    if (!Failed)
+      Selectors.push_back(std::move(ParameterSelector));
+  }
+}
+
 std::optional<FunctionTableKey>
 APINotesReader::Implementation::getFunctionKey(uint32_t ParentContextID,
                                                llvm::StringRef Name) {
@@ -2351,6 +2413,13 @@ auto APINotesReader::lookupCXXMethod(ContextID CtxID, 
llvm::StringRef Name,
   return lookupCXXMethodImpl(CtxID, Name, Parameters);
 }
 
+void APINotesReader::collectCXXMethodParameterSelectors(
+    ContextID CtxID, llvm::StringRef Name,
+    llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors) {
+  Implementation->collectFunctionParameterSelectors(
+      Implementation->CXXMethodTable.get(), CtxID.Value, Name, Selectors);
+}
+
 auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name)
     -> VersionedInfo<CXXMethodInfo> {
   if (!Implementation->CXXMethodTable)
@@ -2418,6 +2487,16 @@ auto APINotesReader::lookupGlobalFunction(
   return lookupGlobalFunctionImpl(Name, Parameters, Ctx);
 }
 
+void APINotesReader::collectGlobalFunctionParameterSelectors(
+    llvm::StringRef Name,
+    llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors,
+    std::optional<Context> Ctx) {
+  uint32_t ParentContextID = Ctx ? Ctx->id.Value : static_cast<uint32_t>(-1);
+  Implementation->collectFunctionParameterSelectors(
+      Implementation->GlobalFunctionTable.get(), ParentContextID, Name,
+      Selectors);
+}
+
 auto APINotesReader::lookupGlobalFunctionImpl(llvm::StringRef Name,
                                               std::optional<Context> Ctx)
     -> VersionedInfo<GlobalFunctionInfo> {
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp 
b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 1c3a59873db25..058f9da338661 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -18,6 +18,7 @@
 #include "clang/APINotes/Types.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/VersionTuple.h"
@@ -788,6 +789,47 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI,
 namespace {
 using namespace api_notes;
 
+struct KnownFunctionSelector {
+  llvm::StringRef Name;
+  llvm::ArrayRef<llvm::StringRef> Parameters;
+};
+
+static bool equalParameterSelectors(llvm::ArrayRef<llvm::StringRef> LHS,
+                                    llvm::ArrayRef<llvm::StringRef> RHS) {
+  if (LHS.size() != RHS.size())
+    return false;
+
+  for (unsigned I = 0, E = LHS.size(); I != E; ++I)
+    if (LHS[I] != RHS[I])
+      return false;
+
+  return true;
+}
+
+static bool
+hasFunctionSelector(llvm::ArrayRef<KnownFunctionSelector> KnownSelectors,
+                    llvm::StringRef Name,
+                    llvm::ArrayRef<llvm::StringRef> Parameters) {
+  for (const KnownFunctionSelector &Known : KnownSelectors)
+    if (Known.Name == Name &&
+        equalParameterSelectors(Known.Parameters, Parameters))
+      return true;
+
+  return false;
+}
+
+static std::string
+formatWhereParameters(llvm::ArrayRef<llvm::StringRef> Parameters) {
+  std::string Result = "[";
+  for (unsigned I = 0, E = Parameters.size(); I != E; ++I) {
+    if (I)
+      Result += ", ";
+    Result += Parameters[I];
+  }
+  Result += "]";
+  return Result;
+}
+
 class YAMLConverter {
   const Module &M;
   APINotesWriter Writer;
@@ -1162,11 +1204,24 @@ class YAMLConverter {
       Writer.addField(TagCtxID, Field.Name, FI, SwiftVersion);
     }
 
+    llvm::SmallVector<KnownFunctionSelector, 4> KnownMethodSelectors;
     for (const auto &CXXMethod : T.Methods) {
       auto WhereParameters = getWhereParameters(CXXMethod);
       if (!WhereParameters.first)
         continue;
 
+      if (WhereParameters.second) {
+        if (hasFunctionSelector(KnownMethodSelectors, CXXMethod.Name,
+                                *WhereParameters.second)) {
+          emitError(llvm::Twine("duplicate definition of C++ method '") +
+                    CXXMethod.Name + "' with Where.Parameters " +
+                    formatWhereParameters(*WhereParameters.second));
+          continue;
+        }
+        KnownMethodSelectors.push_back(
+            {CXXMethod.Name, *WhereParameters.second});
+      }
+
       CXXMethodInfo MI;
       convertFunction(CXXMethod, MI);
       if (WhereParameters.second)
@@ -1243,13 +1298,25 @@ class YAMLConverter {
 
     // Write all global functions.
     llvm::StringSet<> KnownNameOnlyFunctions;
+    llvm::SmallVector<KnownFunctionSelector, 4> KnownFunctionSelectors;
     for (const auto &Function : TLItems.Functions) {
       auto WhereParameters = getWhereParameters(Function);
       if (!WhereParameters.first)
         continue;
 
-      // Check for duplicate name-only global functions. Selector-aware
-      // duplicate diagnostics are handled by a later overload-matching PR.
+      if (WhereParameters.second) {
+        if (hasFunctionSelector(KnownFunctionSelectors, Function.Name,
+                                *WhereParameters.second)) {
+          emitError(llvm::Twine("duplicate definition of global function '") +
+                    Function.Name + "' with Where.Parameters " +
+                    formatWhereParameters(*WhereParameters.second));
+          continue;
+        }
+        KnownFunctionSelectors.push_back(
+            {Function.Name, *WhereParameters.second});
+      }
+
+      // Check for duplicate name-only global functions.
       if (!WhereParameters.second &&
           !KnownNameOnlyFunctions.insert(Function.Name).second) {
         emitError(llvm::Twine("multiple definitions of global function '") +
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 1ab1eac4a5434..e9e90c93755d3 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -1017,6 +1017,50 @@ struct APINotesParameterSelectorCandidates {
   std::optional<APINotesParameterSelector> Desugared;
 };
 
+struct APINotesParameterSelectorSet {
+  SmallVector<APINotesParameterSelector, 4> Selectors;
+
+  void add(const APINotesParameterSelector &Selector) {
+    Selectors.push_back(Selector);
+  }
+
+  void add(ArrayRef<std::string> Parameters) {
+    APINotesParameterSelector Selector;
+    Selector.Parameters.append(Parameters.begin(), Parameters.end());
+    add(Selector);
+  }
+
+  void add(const APINotesParameterSelectorCandidates &Candidates) {
+    add(Candidates.Source);
+    if (Candidates.Desugared)
+      add(*Candidates.Desugared);
+  }
+
+  bool contains(ArrayRef<std::string> Parameters) const {
+    for (const APINotesParameterSelector &Selector : Selectors) {
+      if (Selector.Parameters.size() != Parameters.size())
+        continue;
+
+      bool Matches = true;
+      for (unsigned I = 0, E = Parameters.size(); I != E; ++I) {
+        if (Selector.Parameters[I] == Parameters[I])
+          continue;
+        Matches = false;
+        break;
+      }
+      if (Matches)
+        return true;
+    }
+
+    return false;
+  }
+
+  bool empty() const { return Selectors.empty(); }
+
+  auto begin() const { return Selectors.begin(); }
+  auto end() const { return Selectors.end(); }
+};
+
 static PrintingPolicy
 getAPINotesParameterSelectorPrintingPolicy(const ASTContext &Context) {
   PrintingPolicy Policy(Context.getLangOpts());
@@ -1072,6 +1116,84 @@ getAPINotesParameterSelectorCandidates(const Sema &S, 
const FunctionDecl *FD) {
   return Candidates;
 }
 
+static APINotesParameterSelectorSet
+makeParameterSelectorSet(ArrayRef<SmallVector<std::string, 4>> Selectors) {
+  APINotesParameterSelectorSet Set;
+  for (const SmallVector<std::string, 4> &Selector : Selectors)
+    Set.add(Selector);
+  return Set;
+}
+
+static std::string
+formatParameterSelectorForDiagnostic(ArrayRef<std::string> Parameters) {
+  std::string Result = "[";
+  for (unsigned I = 0, E = Parameters.size(); I != E; ++I) {
+    if (I)
+      Result += ", ";
+    Result += Parameters[I];
+  }
+  Result += "]";
+  return Result;
+}
+
+static void collectOverloadParameterSelectors(const Sema &S,
+                                              const FunctionDecl *FD,
+                                              APINotesParameterSelectorSet 
&Set,
+                                              bool &IsRepresentative) {
+  IsRepresentative = false;
+  bool FoundRepresentative = false;
+
+  auto AddCandidate = [&](const FunctionDecl *Candidate) {
+    if (!FoundRepresentative) {
+      IsRepresentative =
+          Candidate->getCanonicalDecl() == FD->getCanonicalDecl();
+      FoundRepresentative = true;
+    }
+
+    if (auto Candidates = getAPINotesParameterSelectorCandidates(S, Candidate))
+      Set.add(*Candidates);
+  };
+
+  for (NamedDecl *ND : FD->getDeclContext()->lookup(FD->getDeclName())) {
+    if (auto *Candidate = dyn_cast<FunctionDecl>(ND))
+      AddCandidate(Candidate);
+  }
+
+  if (FoundRepresentative)
+    return;
+
+  for (Decl *D : FD->getDeclContext()->decls()) {
+    auto *ND = dyn_cast<NamedDecl>(D);
+    if (!ND || ND->getDeclName() != FD->getDeclName())
+      continue;
+
+    if (auto *Candidate = dyn_cast<FunctionDecl>(ND))
+      AddCandidate(Candidate);
+  }
+
+  if (!FoundRepresentative)
+    AddCandidate(FD);
+}
+
+static void diagnoseUnmatchedParameterSelectors(
+    Sema &S, SourceLocation Loc, StringRef Name,
+    const APINotesParameterSelectorSet &APINotesSelectors,
+    const APINotesParameterSelectorSet &DeclarationSelectors) {
+  if (DeclarationSelectors.empty())
+    return;
+
+  for (const APINotesParameterSelector &APINotesSelector : APINotesSelectors) {
+    if (DeclarationSelectors.contains(APINotesSelector.Parameters))
+      continue;
+
+    S.Diag(Loc, diag::warn_apinotes_message)
+        << (llvm::Twine("API notes entry for '") + Name +
+            "' has unmatched Where.Parameters " +
+            formatParameterSelectorForDiagnostic(APINotesSelector.Parameters))
+               .str();
+  }
+}
+
 // 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.
@@ -1131,6 +1253,12 @@ void Sema::ProcessAPINotes(Decl *D) {
       if (FD->getDeclName().isIdentifier()) {
         auto ParameterSelectorCandidates =
             getAPINotesParameterSelectorCandidates(*this, FD);
+
+        APINotesParameterSelectorSet DeclarationSelectors;
+        bool DiagnoseUnmatchedSelectors = false;
+        collectOverloadParameterSelectors(*this, FD, DeclarationSelectors,
+                                          DiagnoseUnmatchedSelectors);
+
         for (auto Reader : Readers) {
           auto Info =
               Reader->lookupGlobalFunction(FD->getName(), APINotesContext);
@@ -1143,6 +1271,17 @@ void Sema::ProcessAPINotes(Decl *D) {
                   return Reader->lookupGlobalFunction(FD->getName(), 
Parameters,
                                                       APINotesContext);
                 });
+
+          if (DiagnoseUnmatchedSelectors && !DeclarationSelectors.empty()) {
+            SmallVector<SmallVector<std::string, 4>, 4> RawAPINotesSelectors;
+            Reader->collectGlobalFunctionParameterSelectors(
+                FD->getName(), RawAPINotesSelectors, APINotesContext);
+            APINotesParameterSelectorSet APINotesSelectors =
+                makeParameterSelectorSet(RawAPINotesSelectors);
+            diagnoseUnmatchedParameterSelectors(
+                *this, FD->getLocation(), FD->getName(), APINotesSelectors,
+                DeclarationSelectors);
+          }
         }
       }
 
@@ -1348,6 +1487,22 @@ void Sema::ProcessAPINotes(Decl *D) {
                     return Reader->lookupCXXMethod(Context->id, MethodName,
                                                    Parameters);
                   });
+
+            APINotesParameterSelectorSet DeclarationSelectors;
+            bool DiagnoseUnmatchedSelectors = false;
+            collectOverloadParameterSelectors(*this, CXXMethod,
+                                              DeclarationSelectors,
+                                              DiagnoseUnmatchedSelectors);
+            if (DiagnoseUnmatchedSelectors && !DeclarationSelectors.empty()) {
+              SmallVector<SmallVector<std::string, 4>, 4> RawAPINotesSelectors;
+              Reader->collectCXXMethodParameterSelectors(
+                  Context->id, MethodName, RawAPINotesSelectors);
+              APINotesParameterSelectorSet APINotesSelectors =
+                  makeParameterSelectorSet(RawAPINotesSelectors);
+              diagnoseUnmatchedParameterSelectors(
+                  *this, CXXMethod->getLocation(), MethodName,
+                  APINotesSelectors, DeclarationSelectors);
+            }
           }
         }
       }
diff --git 
a/clang/test/APINotes/Inputs/Headers/WhereParametersDiagnostics.apinotes 
b/clang/test/APINotes/Inputs/Headers/WhereParametersDiagnostics.apinotes
new file mode 100644
index 0000000000000..2cf62c2b5e6be
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/WhereParametersDiagnostics.apinotes
@@ -0,0 +1,50 @@
+---
+Name: WhereParametersDiagnostics
+Functions:
+- Name: unmatchedGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: shouldNotApplyGlobal(_:)
+- Name: diagnosticBroadGlobal
+  SwiftPrivate: true
+- Name: diagnosticBroadGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: shouldNotApplyBroadGlobal(_:)
+- Name: diagnosticMatchedGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: diagnosticMatchedGlobal(_:)
+- Name: diagnosticAliasMatchedGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: diagnosticAliasMatchedGlobal(_:)
+Tags:
+- Name: DiagnosticWidget
+  Methods:
+  - Name: unmatchedMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: shouldNotApplyMethod(_:)
+  - Name: diagnosticBroadMethod
+    SwiftPrivate: true
+  - Name: diagnosticBroadMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: shouldNotApplyBroadMethod(_:)
+  - Name: diagnosticMatchedMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: diagnosticMatchedMethod(_:)
+  - Name: diagnosticAliasMatchedMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: diagnosticAliasMatchedMethod(_:)
diff --git a/clang/test/APINotes/Inputs/Headers/WhereParametersDiagnostics.h 
b/clang/test/APINotes/Inputs/Headers/WhereParametersDiagnostics.h
new file mode 100644
index 0000000000000..c39ccd57541aa
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/WhereParametersDiagnostics.h
@@ -0,0 +1,18 @@
+#ifndef WHERE_PARAMETERS_DIAGNOSTICS_H
+#define WHERE_PARAMETERS_DIAGNOSTICS_H
+
+using DiagnosticAliasInt = int;
+
+void unmatchedGlobal(float);
+void diagnosticBroadGlobal(float);
+void diagnosticMatchedGlobal(int);
+void diagnosticAliasMatchedGlobal(DiagnosticAliasInt);
+
+struct DiagnosticWidget {
+  void unmatchedMethod(float);
+  void diagnosticBroadMethod(float);
+  void diagnosticMatchedMethod(int);
+  void diagnosticAliasMatchedMethod(DiagnosticAliasInt);
+};
+
+#endif // WHERE_PARAMETERS_DIAGNOSTICS_H
diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap 
b/clang/test/APINotes/Inputs/Headers/module.modulemap
index 592d482ea7a57..d00b96c55b839 100644
--- a/clang/test/APINotes/Inputs/Headers/module.modulemap
+++ b/clang/test/APINotes/Inputs/Headers/module.modulemap
@@ -75,3 +75,8 @@ module WhereParametersSema {
   header "WhereParametersSema.h"
   export *
 }
+
+module WhereParametersDiagnostics {
+  header "WhereParametersDiagnostics.h"
+  export *
+}
diff --git 
a/clang/test/APINotes/Inputs/WhereParametersDuplicateSelectorDiag/APINotes.apinotes
 
b/clang/test/APINotes/Inputs/WhereParametersDuplicateSelectorDiag/APINotes.apinotes
new file mode 100644
index 0000000000000..d99ea9c0f4ce9
--- /dev/null
+++ 
b/clang/test/APINotes/Inputs/WhereParametersDuplicateSelectorDiag/APINotes.apinotes
@@ -0,0 +1,66 @@
+---
+Name: WhereParametersDiagnostics
+Functions:
+- Name: duplicateGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: duplicateGlobalA(_:)
+- Name: duplicateGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: duplicateGlobalB(_:)
+- Name: duplicateEmpty
+  Where:
+    Parameters: []
+  SwiftName: duplicateEmptyA()
+- Name: duplicateEmpty
+  Where:
+    Parameters: []
+  SwiftName: duplicateEmptyB()
+- Name: allowedGlobal
+  SwiftPrivate: true
+- Name: allowedGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: allowedGlobalInt(_:)
+- Name: allowedGlobal
+  Where:
+    Parameters:
+    - double
+  SwiftName: allowedGlobalDouble(_:)
+Tags:
+- Name: DiagnosticWidget
+  Methods:
+  - Name: duplicateMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: duplicateMethodA(_:)
+  - Name: duplicateMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: duplicateMethodB(_:)
+  - Name: duplicateEmpty
+    Where:
+      Parameters: []
+    SwiftName: duplicateEmptyA()
+  - Name: duplicateEmpty
+    Where:
+      Parameters: []
+    SwiftName: duplicateEmptyB()
+  - Name: allowed
+    SwiftPrivate: true
+  - Name: allowed
+    Where:
+      Parameters:
+      - int
+    SwiftName: allowedInt(_:)
+  - Name: allowed
+    Where:
+      Parameters:
+      - double
+    SwiftName: allowedDouble(_:)
diff --git 
a/clang/test/APINotes/Inputs/WhereParametersDuplicateSelectorDiag/WhereParametersDiagnostics.h
 
b/clang/test/APINotes/Inputs/WhereParametersDuplicateSelectorDiag/WhereParametersDiagnostics.h
new file mode 100644
index 0000000000000..406a848a04b03
--- /dev/null
+++ 
b/clang/test/APINotes/Inputs/WhereParametersDuplicateSelectorDiag/WhereParametersDiagnostics.h
@@ -0,0 +1,16 @@
+#ifndef WHERE_PARAMETERS_DIAGNOSTICS_H
+#define WHERE_PARAMETERS_DIAGNOSTICS_H
+
+void duplicateGlobal(int);
+void duplicateEmpty();
+void allowedGlobal(int);
+void allowedGlobal(double);
+
+struct DiagnosticWidget {
+  void duplicateMethod(int);
+  void duplicateEmpty();
+  void allowed(int);
+  void allowed(double);
+};
+
+#endif // WHERE_PARAMETERS_DIAGNOSTICS_H
diff --git a/clang/test/APINotes/where-parameters-diagnostics.cpp 
b/clang/test/APINotes/where-parameters-diagnostics.cpp
new file mode 100644
index 0000000000000..7bb033973f728
--- /dev/null
+++ b/clang/test/APINotes/where-parameters-diagnostics.cpp
@@ -0,0 +1,41 @@
+// RUN: not %clang_cc1 -fsyntax-only -fapinotes %s -I 
%S/Inputs/WhereParametersDuplicateSelectorDiag 2>&1 | FileCheck %s 
--check-prefix=DUPLICATE
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersDiagnostics 
-fdisable-module-hash -fapinotes-modules -Wapinotes -fsyntax-only -I 
%S/Inputs/Headers %s -x c++ 2>&1 | FileCheck %s --check-prefix=UNMATCHED 
--implicit-check-not=diagnosticMatchedGlobal 
--implicit-check-not=diagnosticAliasMatchedGlobal 
--implicit-check-not=diagnosticMatchedMethod 
--implicit-check-not=diagnosticAliasMatchedMethod
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersDiagnostics 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter diagnosticBroadGlobal -x c++ | FileCheck %s 
--check-prefix=BROAD-GLOBAL
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersDiagnostics 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter DiagnosticWidget::diagnosticBroadMethod -x c++ | 
FileCheck %s --check-prefix=BROAD-METHOD
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersDiagnostics 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter diagnosticMatchedGlobal -x c++ | FileCheck %s 
--check-prefix=MATCHED-GLOBAL
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersDiagnostics 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter diagnosticAliasMatchedGlobal -x c++ | FileCheck %s 
--check-prefix=ALIAS-MATCHED-GLOBAL
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersDiagnostics 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter DiagnosticWidget::diagnosticMatchedMethod -x c++ | 
FileCheck %s --check-prefix=MATCHED-METHOD
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersDiagnostics 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter DiagnosticWidget::diagnosticAliasMatchedMethod -x 
c++ | FileCheck %s --check-prefix=ALIAS-MATCHED-METHOD
+
+#include "WhereParametersDiagnostics.h"
+
+// DUPLICATE: error: duplicate definition of global function 'duplicateGlobal' 
with Where.Parameters [int]
+// DUPLICATE: error: duplicate definition of global function 'duplicateEmpty' 
with Where.Parameters []
+// DUPLICATE: error: duplicate definition of C++ method 'duplicateMethod' with 
Where.Parameters [int]
+// DUPLICATE: error: duplicate definition of C++ method 'duplicateEmpty' with 
Where.Parameters []
+
+// UNMATCHED-DAG: warning: API notes entry for 'unmatchedGlobal' has unmatched 
Where.Parameters [int]
+// UNMATCHED-DAG: warning: API notes entry for 'diagnosticBroadGlobal' has 
unmatched Where.Parameters [int]
+// UNMATCHED-DAG: warning: API notes entry for 'unmatchedMethod' has unmatched 
Where.Parameters [int]
+// UNMATCHED-DAG: warning: API notes entry for 'diagnosticBroadMethod' has 
unmatched Where.Parameters [int]
+
+// BROAD-GLOBAL: FunctionDecl {{.+}} diagnosticBroadGlobal 'void (float)'
+// BROAD-GLOBAL: SwiftPrivateAttr
+// BROAD-GLOBAL-NOT: SwiftNameAttr
+
+// BROAD-METHOD: CXXMethodDecl {{.+}} diagnosticBroadMethod 'void (float)'
+// BROAD-METHOD: SwiftPrivateAttr
+// BROAD-METHOD-NOT: SwiftNameAttr
+
+// MATCHED-GLOBAL: FunctionDecl {{.+}} diagnosticMatchedGlobal 'void (int)'
+// MATCHED-GLOBAL: SwiftNameAttr {{.+}} "diagnosticMatchedGlobal(_:)"
+
+// ALIAS-MATCHED-GLOBAL: FunctionDecl {{.+}} diagnosticAliasMatchedGlobal 
'void (DiagnosticAliasInt)'
+// ALIAS-MATCHED-GLOBAL: SwiftNameAttr {{.+}} 
"diagnosticAliasMatchedGlobal(_:)"
+
+// MATCHED-METHOD: CXXMethodDecl {{.+}} diagnosticMatchedMethod 'void (int)'
+// MATCHED-METHOD: SwiftNameAttr {{.+}} "diagnosticMatchedMethod(_:)"
+
+// ALIAS-MATCHED-METHOD: CXXMethodDecl {{.+}} diagnosticAliasMatchedMethod 
'void (DiagnosticAliasInt)'
+// ALIAS-MATCHED-METHOD: SwiftNameAttr {{.+}} 
"diagnosticAliasMatchedMethod(_:)"

>From a5b094c24c74322f598481359075d0469aa1c068 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <[email protected]>
Date: Tue, 14 Jul 2026 14:05:04 +0200
Subject: [PATCH 2/5] [APINotes] Make Where.Parameters diagnostics non-loading
 and and address reviewer feedback

---
 clang/include/clang/APINotes/APINotesReader.h |  4 +-
 clang/include/clang/APINotes/Types.h          |  7 +++
 clang/lib/APINotes/APINotesReader.cpp         | 31 +++++-----
 clang/lib/APINotes/APINotesTypes.cpp          | 25 ++++++++
 clang/lib/APINotes/APINotesYAMLCompiler.cpp   | 31 +---------
 clang/lib/Sema/SemaAPINotes.cpp               | 60 +++++++------------
 6 files changed, 72 insertions(+), 86 deletions(-)

diff --git a/clang/include/clang/APINotes/APINotesReader.h 
b/clang/include/clang/APINotes/APINotesReader.h
index 640be8ad5acb3..1289c1366437c 100644
--- a/clang/include/clang/APINotes/APINotesReader.h
+++ b/clang/include/clang/APINotes/APINotesReader.h
@@ -171,7 +171,7 @@ class APINotesReader {
                   llvm::ArrayRef<std::string> Parameters);
 
   /// Collect exact parameter selectors stored for the given C++ method.
-  void collectCXXMethodParameterSelectors(
+  bool collectCXXMethodParameterSelectors(
       ContextID CtxID, llvm::StringRef Name,
       llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors);
 
@@ -202,7 +202,7 @@ class APINotesReader {
                        std::optional<Context> Ctx = std::nullopt);
 
   /// Collect exact parameter selectors stored for the given global function.
-  void collectGlobalFunctionParameterSelectors(
+  bool collectGlobalFunctionParameterSelectors(
       llvm::StringRef Name,
       llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors,
       std::optional<Context> Ctx = std::nullopt);
diff --git a/clang/include/clang/APINotes/Types.h 
b/clang/include/clang/APINotes/Types.h
index db00c42f4561b..f0b6f589a7c5e 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -14,6 +14,7 @@
 #include "llvm/ADT/StringRef.h"
 #include <climits>
 #include <optional>
+#include <string>
 #include <vector>
 
 namespace llvm {
@@ -22,6 +23,12 @@ class raw_ostream;
 
 namespace clang {
 namespace api_notes {
+
+std::string
+formatAPINotesParameterSelector(llvm::ArrayRef<llvm::StringRef> Parameters);
+std::string
+formatAPINotesParameterSelector(llvm::ArrayRef<std::string> Parameters);
+
 enum class RetainCountConventionKind {
   None,
   CFReturnsRetained,
diff --git a/clang/lib/APINotes/APINotesReader.cpp 
b/clang/lib/APINotes/APINotesReader.cpp
index 944beccb9d2df..9520fbac75b70 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -841,7 +841,7 @@ class APINotesReader::Implementation {
 
   /// Collect exact parameter selectors stored in the given function-like 
table.
   template <typename TableT>
-  void collectFunctionParameterSelectors(
+  bool collectFunctionParameterSelectors(
       TableT *Table, uint32_t ParentContextID, llvm::StringRef Name,
       llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors);
 
@@ -923,36 +923,33 @@ 
APINotesReader::Implementation::getIdentifierString(IdentifierID ID) {
 }
 
 template <typename TableT>
-void APINotesReader::Implementation::collectFunctionParameterSelectors(
+bool APINotesReader::Implementation::collectFunctionParameterSelectors(
     TableT *Table, uint32_t ParentContextID, llvm::StringRef Name,
     llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors) {
   if (!Table)
-    return;
+    return true;
 
   std::optional<IdentifierID> NameID = getIdentifier(Name);
   if (!NameID)
-    return;
+    return true;
 
-  for (auto I = Table->key_begin(), E = Table->key_end(); I != E; ++I) {
-    FunctionTableKey Key = I.getInternalKey();
+  for (FunctionTableKey Key : Table->keys()) {
     if (Key.parentContextID != ParentContextID ||
         Key.nameID != static_cast<unsigned>(*NameID) || !Key.parameterTypeIDs)
       continue;
 
     llvm::SmallVector<std::string, 4> ParameterSelector;
     ParameterSelector.reserve(Key.parameterTypeIDs->size());
-    bool Failed = false;
     for (IdentifierID TypeID : *Key.parameterTypeIDs) {
       std::optional<llvm::StringRef> TypeName = getIdentifierString(TypeID);
-      if (!TypeName) {
-        Failed = true;
-        break;
-      }
+      if (!TypeName)
+        return false;
       ParameterSelector.push_back(TypeName->str());
     }
-    if (!Failed)
-      Selectors.push_back(std::move(ParameterSelector));
+    Selectors.push_back(std::move(ParameterSelector));
   }
+
+  return true;
 }
 
 std::optional<FunctionTableKey>
@@ -2413,10 +2410,10 @@ auto APINotesReader::lookupCXXMethod(ContextID CtxID, 
llvm::StringRef Name,
   return lookupCXXMethodImpl(CtxID, Name, Parameters);
 }
 
-void APINotesReader::collectCXXMethodParameterSelectors(
+bool APINotesReader::collectCXXMethodParameterSelectors(
     ContextID CtxID, llvm::StringRef Name,
     llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors) {
-  Implementation->collectFunctionParameterSelectors(
+  return Implementation->collectFunctionParameterSelectors(
       Implementation->CXXMethodTable.get(), CtxID.Value, Name, Selectors);
 }
 
@@ -2487,12 +2484,12 @@ auto APINotesReader::lookupGlobalFunction(
   return lookupGlobalFunctionImpl(Name, Parameters, Ctx);
 }
 
-void APINotesReader::collectGlobalFunctionParameterSelectors(
+bool APINotesReader::collectGlobalFunctionParameterSelectors(
     llvm::StringRef Name,
     llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors,
     std::optional<Context> Ctx) {
   uint32_t ParentContextID = Ctx ? Ctx->id.Value : static_cast<uint32_t>(-1);
-  Implementation->collectFunctionParameterSelectors(
+  return Implementation->collectFunctionParameterSelectors(
       Implementation->GlobalFunctionTable.get(), ParentContextID, Name,
       Selectors);
 }
diff --git a/clang/lib/APINotes/APINotesTypes.cpp 
b/clang/lib/APINotes/APINotesTypes.cpp
index 96dd722587c10..7e3f71f719763 100644
--- a/clang/lib/APINotes/APINotesTypes.cpp
+++ b/clang/lib/APINotes/APINotesTypes.cpp
@@ -7,10 +7,35 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/APINotes/Types.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/raw_ostream.h"
 
+namespace {
+template <typename ParameterT>
+std::string
+formatAPINotesParameterSelectorImpl(llvm::ArrayRef<ParameterT> Parameters) {
+  std::string Result;
+  llvm::raw_string_ostream OS(Result);
+  OS << "[";
+  llvm::interleaveComma(Parameters, OS);
+  OS << "]";
+  return Result;
+}
+} // namespace
+
 namespace clang {
 namespace api_notes {
+
+std::string
+formatAPINotesParameterSelector(llvm::ArrayRef<llvm::StringRef> Parameters) {
+  return formatAPINotesParameterSelectorImpl(Parameters);
+}
+
+std::string
+formatAPINotesParameterSelector(llvm::ArrayRef<std::string> Parameters) {
+  return formatAPINotesParameterSelectorImpl(Parameters);
+}
+
 LLVM_DUMP_METHOD void CommonEntityInfo::dump(llvm::raw_ostream &OS) const {
   if (Unavailable)
     OS << "[Unavailable] (" << UnavailableMsg << ")" << ' ';
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp 
b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 058f9da338661..10db3fbb8e659 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -794,42 +794,17 @@ struct KnownFunctionSelector {
   llvm::ArrayRef<llvm::StringRef> Parameters;
 };
 
-static bool equalParameterSelectors(llvm::ArrayRef<llvm::StringRef> LHS,
-                                    llvm::ArrayRef<llvm::StringRef> RHS) {
-  if (LHS.size() != RHS.size())
-    return false;
-
-  for (unsigned I = 0, E = LHS.size(); I != E; ++I)
-    if (LHS[I] != RHS[I])
-      return false;
-
-  return true;
-}
-
 static bool
 hasFunctionSelector(llvm::ArrayRef<KnownFunctionSelector> KnownSelectors,
                     llvm::StringRef Name,
                     llvm::ArrayRef<llvm::StringRef> Parameters) {
   for (const KnownFunctionSelector &Known : KnownSelectors)
-    if (Known.Name == Name &&
-        equalParameterSelectors(Known.Parameters, Parameters))
+    if (Known.Name == Name && Known.Parameters == Parameters)
       return true;
 
   return false;
 }
 
-static std::string
-formatWhereParameters(llvm::ArrayRef<llvm::StringRef> Parameters) {
-  std::string Result = "[";
-  for (unsigned I = 0, E = Parameters.size(); I != E; ++I) {
-    if (I)
-      Result += ", ";
-    Result += Parameters[I];
-  }
-  Result += "]";
-  return Result;
-}
-
 class YAMLConverter {
   const Module &M;
   APINotesWriter Writer;
@@ -1215,7 +1190,7 @@ class YAMLConverter {
                                 *WhereParameters.second)) {
           emitError(llvm::Twine("duplicate definition of C++ method '") +
                     CXXMethod.Name + "' with Where.Parameters " +
-                    formatWhereParameters(*WhereParameters.second));
+                    formatAPINotesParameterSelector(*WhereParameters.second));
           continue;
         }
         KnownMethodSelectors.push_back(
@@ -1309,7 +1284,7 @@ class YAMLConverter {
                                 *WhereParameters.second)) {
           emitError(llvm::Twine("duplicate definition of global function '") +
                     Function.Name + "' with Where.Parameters " +
-                    formatWhereParameters(*WhereParameters.second));
+                    formatAPINotesParameterSelector(*WhereParameters.second));
           continue;
         }
         KnownFunctionSelectors.push_back(
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index e9e90c93755d3..043af7e57165b 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -1124,20 +1124,7 @@ 
makeParameterSelectorSet(ArrayRef<SmallVector<std::string, 4>> Selectors) {
   return Set;
 }
 
-static std::string
-formatParameterSelectorForDiagnostic(ArrayRef<std::string> Parameters) {
-  std::string Result = "[";
-  for (unsigned I = 0, E = Parameters.size(); I != E; ++I) {
-    if (I)
-      Result += ", ";
-    Result += Parameters[I];
-  }
-  Result += "]";
-  return Result;
-}
-
-static void collectOverloadParameterSelectors(const Sema &S,
-                                              const FunctionDecl *FD,
+static void collectOverloadParameterSelectors(const Sema &S, FunctionDecl *FD,
                                               APINotesParameterSelectorSet 
&Set,
                                               bool &IsRepresentative) {
   IsRepresentative = false;
@@ -1153,16 +1140,8 @@ static void collectOverloadParameterSelectors(const Sema 
&S,
     if (auto Candidates = getAPINotesParameterSelectorCandidates(S, Candidate))
       Set.add(*Candidates);
   };
-
-  for (NamedDecl *ND : FD->getDeclContext()->lookup(FD->getDeclName())) {
-    if (auto *Candidate = dyn_cast<FunctionDecl>(ND))
-      AddCandidate(Candidate);
-  }
-
-  if (FoundRepresentative)
-    return;
-
-  for (Decl *D : FD->getDeclContext()->decls()) {
+  
+  for (Decl *D : FD->getDeclContext()->noload_decls()) {
     auto *ND = dyn_cast<NamedDecl>(D);
     if (!ND || ND->getDeclName() != FD->getDeclName())
       continue;
@@ -1189,7 +1168,8 @@ static void diagnoseUnmatchedParameterSelectors(
     S.Diag(Loc, diag::warn_apinotes_message)
         << (llvm::Twine("API notes entry for '") + Name +
             "' has unmatched Where.Parameters " +
-            formatParameterSelectorForDiagnostic(APINotesSelector.Parameters))
+            api_notes::formatAPINotesParameterSelector(
+                APINotesSelector.Parameters))
                .str();
   }
 }
@@ -1274,13 +1254,14 @@ void Sema::ProcessAPINotes(Decl *D) {
 
           if (DiagnoseUnmatchedSelectors && !DeclarationSelectors.empty()) {
             SmallVector<SmallVector<std::string, 4>, 4> RawAPINotesSelectors;
-            Reader->collectGlobalFunctionParameterSelectors(
-                FD->getName(), RawAPINotesSelectors, APINotesContext);
-            APINotesParameterSelectorSet APINotesSelectors =
-                makeParameterSelectorSet(RawAPINotesSelectors);
-            diagnoseUnmatchedParameterSelectors(
-                *this, FD->getLocation(), FD->getName(), APINotesSelectors,
-                DeclarationSelectors);
+            if (Reader->collectGlobalFunctionParameterSelectors(
+                    FD->getName(), RawAPINotesSelectors, APINotesContext)) {
+              APINotesParameterSelectorSet APINotesSelectors =
+                  makeParameterSelectorSet(RawAPINotesSelectors);
+              diagnoseUnmatchedParameterSelectors(
+                  *this, FD->getLocation(), FD->getName(), APINotesSelectors,
+                  DeclarationSelectors);
+            }
           }
         }
       }
@@ -1495,13 +1476,14 @@ void Sema::ProcessAPINotes(Decl *D) {
                                               DiagnoseUnmatchedSelectors);
             if (DiagnoseUnmatchedSelectors && !DeclarationSelectors.empty()) {
               SmallVector<SmallVector<std::string, 4>, 4> RawAPINotesSelectors;
-              Reader->collectCXXMethodParameterSelectors(
-                  Context->id, MethodName, RawAPINotesSelectors);
-              APINotesParameterSelectorSet APINotesSelectors =
-                  makeParameterSelectorSet(RawAPINotesSelectors);
-              diagnoseUnmatchedParameterSelectors(
-                  *this, CXXMethod->getLocation(), MethodName,
-                  APINotesSelectors, DeclarationSelectors);
+              if (Reader->collectCXXMethodParameterSelectors(
+                      Context->id, MethodName, RawAPINotesSelectors)) {
+                APINotesParameterSelectorSet APINotesSelectors =
+                    makeParameterSelectorSet(RawAPINotesSelectors);
+                diagnoseUnmatchedParameterSelectors(
+                    *this, CXXMethod->getLocation(), MethodName,
+                    APINotesSelectors, DeclarationSelectors);
+              }
             }
           }
         }

>From 1017bcf15acf65712f365a5c2ffdeef8a2c37765 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <[email protected]>
Date: Tue, 14 Jul 2026 14:12:05 +0200
Subject: [PATCH 3/5] [APINotes] Fix formatting in Where.Parameters diagnostics

---
 clang/lib/Sema/SemaAPINotes.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 043af7e57165b..c2ec720b7120e 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -1140,7 +1140,7 @@ static void collectOverloadParameterSelectors(const Sema 
&S, FunctionDecl *FD,
     if (auto Candidates = getAPINotesParameterSelectorCandidates(S, Candidate))
       Set.add(*Candidates);
   };
-  
+
   for (Decl *D : FD->getDeclContext()->noload_decls()) {
     auto *ND = dyn_cast<NamedDecl>(D);
     if (!ND || ND->getDeclName() != FD->getDeclName())

>From 68fa0f539405de82ab23a0e9bf8a742990a55e63 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <[email protected]>
Date: Thu, 16 Jul 2026 18:49:57 +0200
Subject: [PATCH 4/5] [APINotes] Simplify Where.Parameters diagnostic selector
 matching

---
 clang/lib/Sema/SemaAPINotes.cpp | 36 ++++++++++-----------------------
 1 file changed, 11 insertions(+), 25 deletions(-)

diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index c2ec720b7120e..a8d1babe5dbd9 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -1036,21 +1036,15 @@ struct APINotesParameterSelectorSet {
       add(*Candidates.Desugared);
   }
 
+  void add(ArrayRef<SmallVector<std::string, 4>> ParameterSelectors) {
+    for (const auto &Selector : ParameterSelectors)
+      add(ArrayRef<std::string>(Selector));
+  }
+
   bool contains(ArrayRef<std::string> Parameters) const {
-    for (const APINotesParameterSelector &Selector : Selectors) {
-      if (Selector.Parameters.size() != Parameters.size())
-        continue;
-
-      bool Matches = true;
-      for (unsigned I = 0, E = Parameters.size(); I != E; ++I) {
-        if (Selector.Parameters[I] == Parameters[I])
-          continue;
-        Matches = false;
-        break;
-      }
-      if (Matches)
+    for (const APINotesParameterSelector &Selector : Selectors)
+      if (ArrayRef<std::string>(Selector.Parameters) == Parameters)
         return true;
-    }
 
     return false;
   }
@@ -1116,14 +1110,6 @@ getAPINotesParameterSelectorCandidates(const Sema &S, 
const FunctionDecl *FD) {
   return Candidates;
 }
 
-static APINotesParameterSelectorSet
-makeParameterSelectorSet(ArrayRef<SmallVector<std::string, 4>> Selectors) {
-  APINotesParameterSelectorSet Set;
-  for (const SmallVector<std::string, 4> &Selector : Selectors)
-    Set.add(Selector);
-  return Set;
-}
-
 static void collectOverloadParameterSelectors(const Sema &S, FunctionDecl *FD,
                                               APINotesParameterSelectorSet 
&Set,
                                               bool &IsRepresentative) {
@@ -1256,8 +1242,8 @@ void Sema::ProcessAPINotes(Decl *D) {
             SmallVector<SmallVector<std::string, 4>, 4> RawAPINotesSelectors;
             if (Reader->collectGlobalFunctionParameterSelectors(
                     FD->getName(), RawAPINotesSelectors, APINotesContext)) {
-              APINotesParameterSelectorSet APINotesSelectors =
-                  makeParameterSelectorSet(RawAPINotesSelectors);
+              APINotesParameterSelectorSet APINotesSelectors;
+              APINotesSelectors.add(RawAPINotesSelectors);
               diagnoseUnmatchedParameterSelectors(
                   *this, FD->getLocation(), FD->getName(), APINotesSelectors,
                   DeclarationSelectors);
@@ -1478,8 +1464,8 @@ void Sema::ProcessAPINotes(Decl *D) {
               SmallVector<SmallVector<std::string, 4>, 4> RawAPINotesSelectors;
               if (Reader->collectCXXMethodParameterSelectors(
                       Context->id, MethodName, RawAPINotesSelectors)) {
-                APINotesParameterSelectorSet APINotesSelectors =
-                    makeParameterSelectorSet(RawAPINotesSelectors);
+                APINotesParameterSelectorSet APINotesSelectors;
+                APINotesSelectors.add(RawAPINotesSelectors);
                 diagnoseUnmatchedParameterSelectors(
                     *this, CXXMethod->getLocation(), MethodName,
                     APINotesSelectors, DeclarationSelectors);

>From 134a2e295f25447f883072a2d8e486bc967933af Mon Sep 17 00:00:00 2001
From: stoeckoverflow <[email protected]>
Date: Tue, 21 Jul 2026 15:00:55 +0200
Subject: [PATCH 5/5] [APINotes] Track unmatched parameter selectors by state

---
 clang/include/clang/APINotes/APINotesReader.h |  33 ++-
 clang/include/clang/APINotes/Types.h          |  67 ++++++
 clang/include/clang/Sema/Sema.h               |   7 +
 clang/lib/APINotes/APINotesReader.cpp         | 135 ++++++++---
 clang/lib/APINotes/APINotesYAMLCompiler.cpp   |  51 ++--
 clang/lib/Sema/Sema.cpp                       |   2 +
 clang/lib/Sema/SemaAPINotes.cpp               | 222 +++++++++---------
 clang/lib/Sema/SemaAPINotesInternal.h         |  52 ++++
 8 files changed, 393 insertions(+), 176 deletions(-)
 create mode 100644 clang/lib/Sema/SemaAPINotesInternal.h

diff --git a/clang/include/clang/APINotes/APINotesReader.h 
b/clang/include/clang/APINotes/APINotesReader.h
index 1289c1366437c..3b05dd2ef8cbe 100644
--- a/clang/include/clang/APINotes/APINotesReader.h
+++ b/clang/include/clang/APINotes/APINotesReader.h
@@ -170,10 +170,15 @@ class APINotesReader {
   lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
                   llvm::ArrayRef<std::string> Parameters);
 
-  /// Collect exact parameter selectors stored for the given C++ method.
-  bool collectCXXMethodParameterSelectors(
-      ContextID CtxID, llvm::StringRef Name,
-      llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors);
+  /// Build the selector key for the given C++ method.
+  std::optional<APINotesFunctionSelectorKey>
+  getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name);
+
+  /// Build the selector key for the given C++ method with an exact parameter
+  /// selector.
+  std::optional<APINotesFunctionSelectorKey>
+  getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name,
+                          llvm::ArrayRef<std::string> Parameters);
 
   /// Look for information regarding the given global variable.
   ///
@@ -201,11 +206,21 @@ class APINotesReader {
                        llvm::ArrayRef<std::string> Parameters,
                        std::optional<Context> Ctx = std::nullopt);
 
-  /// Collect exact parameter selectors stored for the given global function.
-  bool collectGlobalFunctionParameterSelectors(
-      llvm::StringRef Name,
-      llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors,
-      std::optional<Context> Ctx = std::nullopt);
+  /// Build the selector key for the given global function.
+  std::optional<APINotesFunctionSelectorKey>
+  getGlobalFunctionSelectorKey(llvm::StringRef Name,
+                               std::optional<Context> Ctx = std::nullopt);
+
+  /// Build the selector key for the given global function with an exact
+  /// parameter selector.
+  std::optional<APINotesFunctionSelectorKey>
+  getGlobalFunctionSelectorKey(llvm::StringRef Name,
+                               llvm::ArrayRef<std::string> Parameters,
+                               std::optional<Context> Ctx = std::nullopt);
+
+  /// Collect exact parameter selector keys stored by this reader.
+  bool collectExactFunctionParameterSelectors(
+      llvm::SmallVectorImpl<APINotesFunctionSelector> &Selectors);
 
   /// Look for information regarding the given enumerator.
   ///
diff --git a/clang/include/clang/APINotes/Types.h 
b/clang/include/clang/APINotes/Types.h
index f0b6f589a7c5e..39d6853574b2c 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -11,6 +11,9 @@
 
 #include "clang/Basic/Specifiers.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include <climits>
 #include <optional>
@@ -29,6 +32,43 @@ 
formatAPINotesParameterSelector(llvm::ArrayRef<llvm::StringRef> Parameters);
 std::string
 formatAPINotesParameterSelector(llvm::ArrayRef<std::string> Parameters);
 
+/// Stable reader-facing identity for an API notes function selector entry.
+///
+/// This mirrors the serialized function table key closely enough for Sema-side
+/// diagnostics to use it as a DenseMap key, without exposing the reader's
+/// private FunctionTableKey implementation type.
+struct APINotesFunctionSelectorKey {
+  bool IsCXXMethod = false;
+  uint32_t ParentContextID = 0;
+  uint32_t NameID = 0;
+  std::optional<llvm::SmallVector<uint32_t, 2>> ParameterTypeIDs;
+
+  llvm::hash_code hashValue() const {
+    auto Hash = llvm::hash_combine(IsCXXMethod, ParentContextID, NameID,
+                                   static_cast<bool>(ParameterTypeIDs));
+    if (ParameterTypeIDs) {
+      Hash = llvm::hash_combine(Hash, ParameterTypeIDs->size());
+      for (uint32_t TypeID : *ParameterTypeIDs)
+        Hash = llvm::hash_combine(Hash, TypeID);
+    }
+    return Hash;
+  }
+};
+
+inline bool operator==(const APINotesFunctionSelectorKey &LHS,
+                       const APINotesFunctionSelectorKey &RHS) {
+  return LHS.IsCXXMethod == RHS.IsCXXMethod &&
+         LHS.ParentContextID == RHS.ParentContextID &&
+         LHS.NameID == RHS.NameID &&
+         LHS.ParameterTypeIDs == RHS.ParameterTypeIDs;
+}
+
+/// Exact function selector key plus parameter spelling used for diagnostics.
+struct APINotesFunctionSelector {
+  APINotesFunctionSelectorKey Key;
+  llvm::SmallVector<std::string, 4> Parameters;
+};
+
 enum class RetainCountConventionKind {
   None,
   CFReturnsRetained,
@@ -1008,4 +1048,31 @@ struct ObjCSelectorRef {
 } // namespace api_notes
 } // namespace clang
 
+namespace llvm {
+template <> struct DenseMapInfo<clang::api_notes::APINotesFunctionSelectorKey> 
{
+  static clang::api_notes::APINotesFunctionSelectorKey getEmptyKey() {
+    clang::api_notes::APINotesFunctionSelectorKey Key;
+    Key.NameID = ~uint32_t(0);
+    return Key;
+  }
+
+  static clang::api_notes::APINotesFunctionSelectorKey getTombstoneKey() {
+    clang::api_notes::APINotesFunctionSelectorKey Key;
+    Key.NameID = ~uint32_t(0) - 1;
+    return Key;
+  }
+
+  static unsigned
+  getHashValue(const clang::api_notes::APINotesFunctionSelectorKey &Key) {
+    return Key.hashValue();
+  }
+
+  static bool
+  isEqual(const clang::api_notes::APINotesFunctionSelectorKey &LHS,
+          const clang::api_notes::APINotesFunctionSelectorKey &RHS) {
+    return LHS == RHS;
+  }
+};
+} // namespace llvm
+
 #endif
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index bb5697a16e090..2c5a257f41190 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -157,6 +157,7 @@ enum class OverloadCandidateParamOrder : char;
 enum OverloadCandidateRewriteKind : unsigned;
 class OverloadCandidateSet;
 class Preprocessor;
+struct APINotesSelectorDiagnosticState;
 class SemaAMDGPU;
 class SemaARM;
 class SemaAVR;
@@ -1313,6 +1314,8 @@ class Sema final : public SemaBase {
   SourceManager &SourceMgr;
   api_notes::APINotesManager APINotes;
 
+  std::unique_ptr<APINotesSelectorDiagnosticState> APINotesSelectorDiagnostics;
+
   /// A RAII object to enter scope of a compound statement.
   class CompoundScopeRAII {
   public:
@@ -1665,6 +1668,10 @@ class Sema final : public SemaBase {
   /// Apply the 'Type:' annotation to the specified declaration
   void ApplyAPINotesType(Decl *D, StringRef TypeString);
 
+  /// Diagnose exact API notes selectors that were not matched by any
+  /// declaration processed in this translation unit.
+  void DiagnoseUnusedAPINotesSelectors();
+
   /// Whether APINotes should be gathered for all applicable Swift language
   /// versions, without being applied. Leaving clients of the current module
   /// to select and apply the correct version.
diff --git a/clang/lib/APINotes/APINotesReader.cpp 
b/clang/lib/APINotes/APINotesReader.cpp
index 9520fbac75b70..fec23ac955a2a 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -760,6 +760,11 @@ class APINotesReader::Implementation {
   /// The identifier table.
   std::unique_ptr<SerializedIdentifierTable> IdentifierTable;
 
+  /// Lazy reverse lookup cache for identifier IDs. Identifier IDs are dense 
and
+  /// start at 1. Slot 0 is reserved for the empty identifier.
+  bool IdentifierStringsInitialized = false;
+  llvm::SmallVector<std::optional<llvm::StringRef>, 0> IdentifierStrings;
+
   using SerializedContextIDTable =
       llvm::OnDiskIterableChainedHashTable<ContextIDTableInfo>;
 
@@ -839,11 +844,12 @@ class APINotesReader::Implementation {
   /// the ID is unknown.
   std::optional<llvm::StringRef> getIdentifierString(IdentifierID ID);
 
-  /// Collect exact parameter selectors stored in the given function-like 
table.
+  /// Collect exact parameter selector keys stored in the given function-like
+  /// table.
   template <typename TableT>
-  bool collectFunctionParameterSelectors(
-      TableT *Table, uint32_t ParentContextID, llvm::StringRef Name,
-      llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors);
+  bool collectExactFunctionParameterSelectors(
+      TableT *Table, bool IsCXXMethod,
+      llvm::SmallVectorImpl<APINotesFunctionSelector> &Selectors);
 
   /// Retrieve the selector ID for the given selector, or an empty
   /// optional if the string is unknown.
@@ -913,40 +919,67 @@ 
APINotesReader::Implementation::getIdentifierString(IdentifierID ID) {
   if (ID == IdentifierID(0))
     return llvm::StringRef();
 
-  for (llvm::StringRef Identifier : IdentifierTable->keys()) {
-    auto KnownID = IdentifierTable->find(Identifier);
-    if (KnownID != IdentifierTable->end() && *KnownID == ID)
-      return Identifier;
-  }
+  if (!IdentifierStringsInitialized) {
+    IdentifierStringsInitialized = true;
+    // keys() and data() iterate over the same serialized entries in lockstep,
+    // so build the reverse cache without doing a lookup for each key.
+    auto Identifiers = IdentifierTable->keys();
+    auto IDs = IdentifierTable->data();
+    auto Identifier = Identifiers.begin();
+    auto KnownID = IDs.begin();
+    auto IdentifierEnd = Identifiers.end();
+    auto KnownIDEnd = IDs.end();
+    for (; Identifier != IdentifierEnd && KnownID != KnownIDEnd;
+         ++Identifier, ++KnownID) {
+      unsigned Index = static_cast<unsigned>(*KnownID);
+      if (IdentifierStrings.size() <= Index)
+        IdentifierStrings.resize(Index + 1);
+      IdentifierStrings[Index] = *Identifier;
+    }
+  }
+
+  unsigned Index = static_cast<unsigned>(ID);
+  if (Index >= IdentifierStrings.size())
+    return std::nullopt;
+  return IdentifierStrings[Index];
+}
 
-  return std::nullopt;
+static APINotesFunctionSelectorKey
+toAPINotesFunctionSelectorKey(const FunctionTableKey &Key, bool IsCXXMethod) {
+  APINotesFunctionSelectorKey SelectorKey;
+  SelectorKey.IsCXXMethod = IsCXXMethod;
+  SelectorKey.ParentContextID = Key.parentContextID;
+  SelectorKey.NameID = Key.nameID;
+  if (Key.parameterTypeIDs) {
+    SelectorKey.ParameterTypeIDs.emplace();
+    SelectorKey.ParameterTypeIDs->reserve(Key.parameterTypeIDs->size());
+    for (IdentifierID TypeID : *Key.parameterTypeIDs)
+      SelectorKey.ParameterTypeIDs->push_back(static_cast<unsigned>(TypeID));
+  }
+  return SelectorKey;
 }
 
 template <typename TableT>
-bool APINotesReader::Implementation::collectFunctionParameterSelectors(
-    TableT *Table, uint32_t ParentContextID, llvm::StringRef Name,
-    llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors) {
+bool APINotesReader::Implementation::collectExactFunctionParameterSelectors(
+    TableT *Table, bool IsCXXMethod,
+    llvm::SmallVectorImpl<APINotesFunctionSelector> &Selectors) {
   if (!Table)
     return true;
 
-  std::optional<IdentifierID> NameID = getIdentifier(Name);
-  if (!NameID)
-    return true;
-
   for (FunctionTableKey Key : Table->keys()) {
-    if (Key.parentContextID != ParentContextID ||
-        Key.nameID != static_cast<unsigned>(*NameID) || !Key.parameterTypeIDs)
+    if (!Key.parameterTypeIDs)
       continue;
 
-    llvm::SmallVector<std::string, 4> ParameterSelector;
-    ParameterSelector.reserve(Key.parameterTypeIDs->size());
+    APINotesFunctionSelector Selector;
+    Selector.Key = toAPINotesFunctionSelectorKey(Key, IsCXXMethod);
+    Selector.Parameters.reserve(Key.parameterTypeIDs->size());
     for (IdentifierID TypeID : *Key.parameterTypeIDs) {
       std::optional<llvm::StringRef> TypeName = getIdentifierString(TypeID);
       if (!TypeName)
         return false;
-      ParameterSelector.push_back(TypeName->str());
+      Selector.Parameters.push_back(TypeName->str());
     }
-    Selectors.push_back(std::move(ParameterSelector));
+    Selectors.push_back(std::move(Selector));
   }
 
   return true;
@@ -2410,11 +2443,24 @@ auto APINotesReader::lookupCXXMethod(ContextID CtxID, 
llvm::StringRef Name,
   return lookupCXXMethodImpl(CtxID, Name, Parameters);
 }
 
-bool APINotesReader::collectCXXMethodParameterSelectors(
+std::optional<APINotesFunctionSelectorKey>
+APINotesReader::getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name) 
{
+  std::optional<FunctionTableKey> Key =
+      Implementation->getFunctionKey(CtxID.Value, Name);
+  if (!Key)
+    return std::nullopt;
+  return toAPINotesFunctionSelectorKey(*Key, /*IsCXXMethod=*/true);
+}
+
+std::optional<APINotesFunctionSelectorKey>
+APINotesReader::getCXXMethodSelectorKey(
     ContextID CtxID, llvm::StringRef Name,
-    llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors) {
-  return Implementation->collectFunctionParameterSelectors(
-      Implementation->CXXMethodTable.get(), CtxID.Value, Name, Selectors);
+    llvm::ArrayRef<std::string> Parameters) {
+  std::optional<FunctionTableKey> Key =
+      Implementation->getFunctionKey(CtxID.Value, Name, Parameters);
+  if (!Key)
+    return std::nullopt;
+  return toAPINotesFunctionSelectorKey(*Key, /*IsCXXMethod=*/true);
 }
 
 auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name)
@@ -2484,14 +2530,35 @@ auto APINotesReader::lookupGlobalFunction(
   return lookupGlobalFunctionImpl(Name, Parameters, Ctx);
 }
 
-bool APINotesReader::collectGlobalFunctionParameterSelectors(
-    llvm::StringRef Name,
-    llvm::SmallVectorImpl<llvm::SmallVector<std::string, 4>> &Selectors,
+std::optional<APINotesFunctionSelectorKey>
+APINotesReader::getGlobalFunctionSelectorKey(llvm::StringRef Name,
+                                             std::optional<Context> Ctx) {
+  std::optional<FunctionTableKey> Key =
+      Implementation->getFunctionKey(Ctx, Name);
+  if (!Key)
+    return std::nullopt;
+  return toAPINotesFunctionSelectorKey(*Key, /*IsCXXMethod=*/false);
+}
+
+std::optional<APINotesFunctionSelectorKey>
+APINotesReader::getGlobalFunctionSelectorKey(
+    llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters,
     std::optional<Context> Ctx) {
-  uint32_t ParentContextID = Ctx ? Ctx->id.Value : static_cast<uint32_t>(-1);
-  return Implementation->collectFunctionParameterSelectors(
-      Implementation->GlobalFunctionTable.get(), ParentContextID, Name,
-      Selectors);
+  std::optional<FunctionTableKey> Key =
+      Implementation->getFunctionKey(Ctx, Name, Parameters);
+  if (!Key)
+    return std::nullopt;
+  return toAPINotesFunctionSelectorKey(*Key, /*IsCXXMethod=*/false);
+}
+
+bool APINotesReader::collectExactFunctionParameterSelectors(
+    llvm::SmallVectorImpl<APINotesFunctionSelector> &Selectors) {
+  return Implementation->collectExactFunctionParameterSelectors(
+             Implementation->GlobalFunctionTable.get(), /*IsCXXMethod=*/false,
+             Selectors) &&
+         Implementation->collectExactFunctionParameterSelectors(
+             Implementation->CXXMethodTable.get(), /*IsCXXMethod=*/true,
+             Selectors);
 }
 
 auto APINotesReader::lookupGlobalFunctionImpl(llvm::StringRef Name,
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp 
b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 10db3fbb8e659..e01dc84dde941 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -18,12 +18,16 @@
 #include "clang/APINotes/Types.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/VersionTuple.h"
 #include "llvm/Support/YAMLTraits.h"
+#include "llvm/Support/raw_ostream.h"
 #include <optional>
+#include <string>
 #include <type_traits>
 #include <vector>
 
@@ -789,20 +793,19 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI,
 namespace {
 using namespace api_notes;
 
-struct KnownFunctionSelector {
-  llvm::StringRef Name;
-  llvm::ArrayRef<llvm::StringRef> Parameters;
-};
-
-static bool
-hasFunctionSelector(llvm::ArrayRef<KnownFunctionSelector> KnownSelectors,
-                    llvm::StringRef Name,
-                    llvm::ArrayRef<llvm::StringRef> Parameters) {
-  for (const KnownFunctionSelector &Known : KnownSelectors)
-    if (Known.Name == Name && Known.Parameters == Parameters)
-      return true;
-
-  return false;
+static std::string
+getFunctionSelectorKey(llvm::StringRef Name,
+                       llvm::ArrayRef<llvm::StringRef> Parameters) {
+  llvm::SmallString<64> Key;
+  llvm::raw_svector_ostream OS(Key);
+  auto AppendKeyPart = [&OS](llvm::StringRef Part) {
+    OS << Part.size() << ':' << Part;
+  };
+
+  AppendKeyPart(Name);
+  OS << ';';
+  llvm::interleave(Parameters, OS, AppendKeyPart, "");
+  return Key.str().str();
 }
 
 class YAMLConverter {
@@ -1179,22 +1182,22 @@ class YAMLConverter {
       Writer.addField(TagCtxID, Field.Name, FI, SwiftVersion);
     }
 
-    llvm::SmallVector<KnownFunctionSelector, 4> KnownMethodSelectors;
+    llvm::StringSet<> KnownMethodSelectors;
     for (const auto &CXXMethod : T.Methods) {
       auto WhereParameters = getWhereParameters(CXXMethod);
       if (!WhereParameters.first)
         continue;
 
       if (WhereParameters.second) {
-        if (hasFunctionSelector(KnownMethodSelectors, CXXMethod.Name,
-                                *WhereParameters.second)) {
+        if (!KnownMethodSelectors
+                 .insert(getFunctionSelectorKey(CXXMethod.Name,
+                                                *WhereParameters.second))
+                 .second) {
           emitError(llvm::Twine("duplicate definition of C++ method '") +
                     CXXMethod.Name + "' with Where.Parameters " +
                     formatAPINotesParameterSelector(*WhereParameters.second));
           continue;
         }
-        KnownMethodSelectors.push_back(
-            {CXXMethod.Name, *WhereParameters.second});
       }
 
       CXXMethodInfo MI;
@@ -1273,22 +1276,22 @@ class YAMLConverter {
 
     // Write all global functions.
     llvm::StringSet<> KnownNameOnlyFunctions;
-    llvm::SmallVector<KnownFunctionSelector, 4> KnownFunctionSelectors;
+    llvm::StringSet<> KnownFunctionSelectors;
     for (const auto &Function : TLItems.Functions) {
       auto WhereParameters = getWhereParameters(Function);
       if (!WhereParameters.first)
         continue;
 
       if (WhereParameters.second) {
-        if (hasFunctionSelector(KnownFunctionSelectors, Function.Name,
-                                *WhereParameters.second)) {
+        if (!KnownFunctionSelectors
+                 .insert(getFunctionSelectorKey(Function.Name,
+                                                *WhereParameters.second))
+                 .second) {
           emitError(llvm::Twine("duplicate definition of global function '") +
                     Function.Name + "' with Where.Parameters " +
                     formatAPINotesParameterSelector(*WhereParameters.second));
           continue;
         }
-        KnownFunctionSelectors.push_back(
-            {Function.Name, *WhereParameters.second});
       }
 
       // Check for duplicate name-only global functions.
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index e689c75eb566c..ae9dbe345f7bf 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -11,6 +11,7 @@
 //
 
//===----------------------------------------------------------------------===//
 
+#include "SemaAPINotesInternal.h"
 #include "UsedDeclVisitor.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/ASTDiagnostic.h"
@@ -1313,6 +1314,7 @@ void Sema::ActOnEndOfTranslationUnit() {
   DiagnoseUnterminatedPragmaAttribute();
   OpenMP().DiagnoseUnterminatedOpenMPDeclareTarget();
   DiagnosePrecisionLossInComplexDivision();
+  DiagnoseUnusedAPINotesSelectors();
 
   // All delayed member exception specs should be checked or we end up 
accepting
   // incompatible declarations.
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index a8d1babe5dbd9..92b5d7080b7ac 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -10,6 +10,7 @@
 //
 
//===----------------------------------------------------------------------===//
 
+#include "SemaAPINotesInternal.h"
 #include "TypeLocBuilder.h"
 #include "clang/APINotes/APINotesReader.h"
 #include "clang/APINotes/Types.h"
@@ -1017,44 +1018,6 @@ struct APINotesParameterSelectorCandidates {
   std::optional<APINotesParameterSelector> Desugared;
 };
 
-struct APINotesParameterSelectorSet {
-  SmallVector<APINotesParameterSelector, 4> Selectors;
-
-  void add(const APINotesParameterSelector &Selector) {
-    Selectors.push_back(Selector);
-  }
-
-  void add(ArrayRef<std::string> Parameters) {
-    APINotesParameterSelector Selector;
-    Selector.Parameters.append(Parameters.begin(), Parameters.end());
-    add(Selector);
-  }
-
-  void add(const APINotesParameterSelectorCandidates &Candidates) {
-    add(Candidates.Source);
-    if (Candidates.Desugared)
-      add(*Candidates.Desugared);
-  }
-
-  void add(ArrayRef<SmallVector<std::string, 4>> ParameterSelectors) {
-    for (const auto &Selector : ParameterSelectors)
-      add(ArrayRef<std::string>(Selector));
-  }
-
-  bool contains(ArrayRef<std::string> Parameters) const {
-    for (const APINotesParameterSelector &Selector : Selectors)
-      if (ArrayRef<std::string>(Selector.Parameters) == Parameters)
-        return true;
-
-    return false;
-  }
-
-  bool empty() const { return Selectors.empty(); }
-
-  auto begin() const { return Selectors.begin(); }
-  auto end() const { return Selectors.end(); }
-};
-
 static PrintingPolicy
 getAPINotesParameterSelectorPrintingPolicy(const ASTContext &Context) {
   PrintingPolicy Policy(Context.getLangOpts());
@@ -1110,53 +1073,66 @@ getAPINotesParameterSelectorCandidates(const Sema &S, 
const FunctionDecl *FD) {
   return Candidates;
 }
 
-static void collectOverloadParameterSelectors(const Sema &S, FunctionDecl *FD,
-                                              APINotesParameterSelectorSet 
&Set,
-                                              bool &IsRepresentative) {
-  IsRepresentative = false;
-  bool FoundRepresentative = false;
-
-  auto AddCandidate = [&](const FunctionDecl *Candidate) {
-    if (!FoundRepresentative) {
-      IsRepresentative =
-          Candidate->getCanonicalDecl() == FD->getCanonicalDecl();
-      FoundRepresentative = true;
-    }
-
-    if (auto Candidates = getAPINotesParameterSelectorCandidates(S, Candidate))
-      Set.add(*Candidates);
-  };
-
-  for (Decl *D : FD->getDeclContext()->noload_decls()) {
-    auto *ND = dyn_cast<NamedDecl>(D);
-    if (!ND || ND->getDeclName() != FD->getDeclName())
-      continue;
+static api_notes::APINotesFunctionSelectorKey
+getBroadAPINotesSelectorKey(api_notes::APINotesFunctionSelectorKey Key) {
+  Key.ParameterTypeIDs = std::nullopt;
+  return Key;
+}
 
-    if (auto *Candidate = dyn_cast<FunctionDecl>(ND))
-      AddCandidate(Candidate);
+static APINotesSelectorDiagnosticReaderState &
+getAPINotesSelectorDiagnosticState(Sema &S, api_notes::APINotesReader *Reader) 
{
+  if (!S.APINotesSelectorDiagnostics)
+    S.APINotesSelectorDiagnostics =
+        std::make_unique<APINotesSelectorDiagnosticState>();
+  auto &State = S.APINotesSelectorDiagnostics->Readers[Reader];
+  if (State.Initialized)
+    return State;
+
+  State.Initialized = true;
+  SmallVector<api_notes::APINotesFunctionSelector, 4> Selectors;
+  if (!Reader->collectExactFunctionParameterSelectors(Selectors))
+    return State;
+
+  State.Selectors.reserve(Selectors.size());
+  State.SelectorIndices.reserve(Selectors.size());
+  State.SeenNames.reserve(Selectors.size());
+  for (auto &Selector : Selectors) {
+    unsigned Index = State.Selectors.size();
+    APINotesSelectorDiagnosticEntry Entry;
+    Entry.BroadKey = getBroadAPINotesSelectorKey(Selector.Key);
+    Entry.Parameters = std::move(Selector.Parameters);
+    State.Selectors.push_back(std::move(Entry));
+    State.SelectorIndices.insert({Selector.Key, Index});
   }
-
-  if (!FoundRepresentative)
-    AddCandidate(FD);
+  return State;
 }
 
-static void diagnoseUnmatchedParameterSelectors(
-    Sema &S, SourceLocation Loc, StringRef Name,
-    const APINotesParameterSelectorSet &APINotesSelectors,
-    const APINotesParameterSelectorSet &DeclarationSelectors) {
-  if (DeclarationSelectors.empty())
-    return;
+static void noteSeenAPINotesSelectorName(
+    APINotesSelectorDiagnosticReaderState &State,
+    const api_notes::APINotesFunctionSelectorKey &BroadKey, StringRef Name,
+    SourceLocation Loc) {
+  State.SeenNames.insert({BroadKey, {Loc, Name.str()}});
+}
 
-  for (const APINotesParameterSelector &APINotesSelector : APINotesSelectors) {
-    if (DeclarationSelectors.contains(APINotesSelector.Parameters))
-      continue;
+static void
+markAPINotesSelectorUsed(APINotesSelectorDiagnosticReaderState &State,
+                         const api_notes::APINotesFunctionSelectorKey &Key) {
+  auto KnownSelector = State.SelectorIndices.find(Key);
+  if (KnownSelector != State.SelectorIndices.end())
+    State.Selectors[KnownSelector->second].Used = true;
+}
 
-    S.Diag(Loc, diag::warn_apinotes_message)
-        << (llvm::Twine("API notes entry for '") + Name +
-            "' has unmatched Where.Parameters " +
-            api_notes::formatAPINotesParameterSelector(
-                APINotesSelector.Parameters))
-               .str();
+static void markAPINotesSelectorCandidatesUsed(
+    APINotesSelectorDiagnosticReaderState &State,
+    llvm::function_ref<std::optional<api_notes::APINotesFunctionSelectorKey>(
+        ArrayRef<std::string>)>
+        GetSelectorKey,
+    const APINotesParameterSelectorCandidates &Candidates) {
+  if (auto Key = GetSelectorKey(Candidates.Source.Parameters))
+    markAPINotesSelectorUsed(State, *Key);
+  if (Candidates.Desugared) {
+    if (auto Key = GetSelectorKey(Candidates.Desugared->Parameters))
+      markAPINotesSelectorUsed(State, *Key);
   }
 }
 
@@ -1220,11 +1196,6 @@ void Sema::ProcessAPINotes(Decl *D) {
         auto ParameterSelectorCandidates =
             getAPINotesParameterSelectorCandidates(*this, FD);
 
-        APINotesParameterSelectorSet DeclarationSelectors;
-        bool DiagnoseUnmatchedSelectors = false;
-        collectOverloadParameterSelectors(*this, FD, DeclarationSelectors,
-                                          DiagnoseUnmatchedSelectors);
-
         for (auto Reader : Readers) {
           auto Info =
               Reader->lookupGlobalFunction(FD->getName(), APINotesContext);
@@ -1238,16 +1209,22 @@ void Sema::ProcessAPINotes(Decl *D) {
                                                       APINotesContext);
                 });
 
-          if (DiagnoseUnmatchedSelectors && !DeclarationSelectors.empty()) {
-            SmallVector<SmallVector<std::string, 4>, 4> RawAPINotesSelectors;
-            if (Reader->collectGlobalFunctionParameterSelectors(
-                    FD->getName(), RawAPINotesSelectors, APINotesContext)) {
-              APINotesParameterSelectorSet APINotesSelectors;
-              APINotesSelectors.add(RawAPINotesSelectors);
-              diagnoseUnmatchedParameterSelectors(
-                  *this, FD->getLocation(), FD->getName(), APINotesSelectors,
-                  DeclarationSelectors);
-            }
+          if (ParameterSelectorCandidates &&
+              !Diags.isIgnored(diag::warn_apinotes_message,
+                               FD->getLocation())) {
+            auto &DiagnosticState =
+                getAPINotesSelectorDiagnosticState(*this, Reader);
+            if (auto BroadKey = Reader->getGlobalFunctionSelectorKey(
+                    FD->getName(), APINotesContext))
+              noteSeenAPINotesSelectorName(DiagnosticState, *BroadKey,
+                                           FD->getName(), FD->getLocation());
+            markAPINotesSelectorCandidatesUsed(
+                DiagnosticState,
+                [&](ArrayRef<std::string> Parameters) {
+                  return Reader->getGlobalFunctionSelectorKey(
+                      FD->getName(), Parameters, APINotesContext);
+                },
+                *ParameterSelectorCandidates);
           }
         }
       }
@@ -1455,21 +1432,23 @@ void Sema::ProcessAPINotes(Decl *D) {
                                                    Parameters);
                   });
 
-            APINotesParameterSelectorSet DeclarationSelectors;
-            bool DiagnoseUnmatchedSelectors = false;
-            collectOverloadParameterSelectors(*this, CXXMethod,
-                                              DeclarationSelectors,
-                                              DiagnoseUnmatchedSelectors);
-            if (DiagnoseUnmatchedSelectors && !DeclarationSelectors.empty()) {
-              SmallVector<SmallVector<std::string, 4>, 4> RawAPINotesSelectors;
-              if (Reader->collectCXXMethodParameterSelectors(
-                      Context->id, MethodName, RawAPINotesSelectors)) {
-                APINotesParameterSelectorSet APINotesSelectors;
-                APINotesSelectors.add(RawAPINotesSelectors);
-                diagnoseUnmatchedParameterSelectors(
-                    *this, CXXMethod->getLocation(), MethodName,
-                    APINotesSelectors, DeclarationSelectors);
-              }
+            if (ParameterSelectorCandidates &&
+                !Diags.isIgnored(diag::warn_apinotes_message,
+                                 CXXMethod->getLocation())) {
+              auto &DiagnosticState =
+                  getAPINotesSelectorDiagnosticState(*this, Reader);
+              if (auto BroadKey =
+                      Reader->getCXXMethodSelectorKey(Context->id, MethodName))
+                noteSeenAPINotesSelectorName(DiagnosticState, *BroadKey,
+                                             MethodName,
+                                             CXXMethod->getLocation());
+              markAPINotesSelectorCandidatesUsed(
+                  DiagnosticState,
+                  [&](ArrayRef<std::string> Parameters) {
+                    return Reader->getCXXMethodSelectorKey(
+                        Context->id, MethodName, Parameters);
+                  },
+                  *ParameterSelectorCandidates);
             }
           }
         }
@@ -1497,3 +1476,28 @@ void Sema::ProcessAPINotes(Decl *D) {
     }
   }
 }
+
+void Sema::DiagnoseUnusedAPINotesSelectors() {
+  if (!APINotesSelectorDiagnostics)
+    return;
+
+  for (auto &ReaderSelectors : APINotesSelectorDiagnostics->Readers) {
+    auto &State = ReaderSelectors.second;
+    for (const auto &Selector : State.Selectors) {
+      if (Selector.Used)
+        continue;
+
+      auto SeenName = State.SeenNames.find(Selector.BroadKey);
+      if (SeenName == State.SeenNames.end())
+        continue;
+
+      Diag(SeenName->second.Loc, diag::warn_apinotes_message)
+          << (llvm::Twine("API notes entry for '") + SeenName->second.Name +
+              "' has unmatched Where.Parameters " +
+              api_notes::formatAPINotesParameterSelector(Selector.Parameters))
+                 .str();
+    }
+  }
+
+  APINotesSelectorDiagnostics.reset();
+}
diff --git a/clang/lib/Sema/SemaAPINotesInternal.h 
b/clang/lib/Sema/SemaAPINotesInternal.h
new file mode 100644
index 0000000000000..1b9e2ffa60bb8
--- /dev/null
+++ b/clang/lib/Sema/SemaAPINotesInternal.h
@@ -0,0 +1,52 @@
+//===--- SemaAPINotesInternal.h - API Notes Sema Internals ------*- C++ 
-*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_SEMA_SEMAAPINOTESINTERNAL_H
+#define LLVM_CLANG_LIB_SEMA_SEMAAPINOTESINTERNAL_H
+
+#include "clang/APINotes/Types.h"
+#include "clang/Basic/SourceLocation.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include <string>
+
+namespace clang {
+namespace api_notes {
+class APINotesReader;
+}
+
+struct APINotesSelectorDiagnosticEntry {
+  api_notes::APINotesFunctionSelectorKey BroadKey;
+  llvm::SmallVector<std::string, 4> Parameters;
+  bool Used = false;
+};
+
+struct APINotesSelectorDiagnosticName {
+  SourceLocation Loc;
+  std::string Name;
+};
+
+struct APINotesSelectorDiagnosticReaderState {
+  bool Initialized = false;
+  llvm::DenseMap<api_notes::APINotesFunctionSelectorKey, unsigned>
+      SelectorIndices;
+  llvm::DenseMap<api_notes::APINotesFunctionSelectorKey,
+                 APINotesSelectorDiagnosticName>
+      SeenNames;
+  llvm::SmallVector<APINotesSelectorDiagnosticEntry, 4> Selectors;
+};
+
+struct APINotesSelectorDiagnosticState {
+  llvm::DenseMap<api_notes::APINotesReader *,
+                 APINotesSelectorDiagnosticReaderState>
+      Readers;
+};
+
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_SEMA_SEMAAPINOTESINTERNAL_H

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to