Author: StoeckOverflow
Date: 2026-07-29T09:50:35+01:00
New Revision: a6614ee07b916785321eb046d525374a022b8ebb

URL: 
https://github.com/llvm/llvm-project/commit/a6614ee07b916785321eb046d525374a022b8ebb
DIFF: 
https://github.com/llvm/llvm-project/commit/a6614ee07b916785321eb046d525374a022b8ebb.diff

LOG: [APINotes] Diagnose invalid Where.Parameters selectors (#209408)

This PR adds diagnostics for exact `Where.Parameters` selectors on top
of the existing parsing, serialization, and Sema matching support.

It diagnoses duplicate exact selectors during API notes conversion,
including duplicate `Where.Parameters: []`, while still allowing broad
name-only entries and same-name entries with different selectors.

It also adds reader/Sema support to warn under `-Wapinotes` when an
exact selector in API notes does not match any visible overload. The
reader can now enumerate stored exact selectors for global functions and
C++ methods, and Sema compares those against the selector candidates
derived from the visible overload set.

The diagnostic path follows the same matching policy as Sema, including
the desugared alias fallback, so valid matched selectors do not produce
false warnings.

Tests cover duplicate selector errors, unmatched selector warnings for
globals and C++ methods, broad-plus-exact coexistence, exact notes not
silently applying on mismatch, and no false warnings for matched or
alias-fallback selectors.

Reviewers: @Xazax-hun @j-hui @egorzhdan

---------

Co-authored-by: John Hui <[email protected]>

Added: 
    clang/lib/Sema/SemaAPINotesInternal.h
    clang/test/APINotes/where-parameters-diagnostics.cpp

Modified: 
    clang/include/clang/APINotes/APINotesReader.h
    clang/include/clang/APINotes/Types.h
    clang/include/clang/Sema/Sema.h
    clang/lib/APINotes/APINotesFormat.h
    clang/lib/APINotes/APINotesReader.cpp
    clang/lib/APINotes/APINotesTypes.cpp
    clang/lib/APINotes/APINotesYAMLCompiler.cpp
    clang/lib/Sema/Sema.cpp
    clang/lib/Sema/SemaAPINotes.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/APINotes/APINotesReader.h 
b/clang/include/clang/APINotes/APINotesReader.h
index 761745e20b61a..d74232bc334c6 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,16 @@ class APINotesReader {
   lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
                   llvm::ArrayRef<std::string> Parameters);
 
+  /// 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.
   ///
   /// \param Name The name of the global variable.
@@ -195,6 +206,27 @@ class APINotesReader {
                        llvm::ArrayRef<std::string> Parameters,
                        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.
+  void collectExactFunctionParameterSelectors(
+      llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors);
+
+  /// Reconstruct parameter selector strings for a stored exact selector key.
+  std::optional<llvm::SmallVector<std::string, 4>>
+  getParameterSelectorSpellingsForDiagnostics(
+      const APINotesFunctionSelectorKey &Key);
+
   /// Look for information regarding the given enumerator.
   ///
   /// \param Name The name of the enumerator.

diff  --git a/clang/include/clang/APINotes/Types.h 
b/clang/include/clang/APINotes/Types.h
index db00c42f4561b..af989d3a1b7f0 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -11,9 +11,16 @@
 
 #include "clang/Basic/Specifiers.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMapInfo.h"
+#include "llvm/ADT/Hashing.h"
+#include "llvm/ADT/PointerEmbeddedInt.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
 #include <climits>
 #include <optional>
+#include <string>
 #include <vector>
 
 namespace llvm {
@@ -22,6 +29,17 @@ class raw_ostream;
 
 namespace clang {
 namespace api_notes {
+
+template <typename RangeT>
+std::string formatAPINotesParameterSelector(RangeT &&Parameters) {
+  std::string Result;
+  llvm::raw_string_ostream OS(Result);
+  OS << "[";
+  llvm::interleaveComma(Parameters, OS);
+  OS << "]";
+  return Result;
+}
+
 enum class RetainCountConventionKind {
   None,
   CFReturnsRetained,
@@ -988,6 +1006,83 @@ struct Context {
   Context(ContextID id, ContextKind kind) : id(id), kind(kind) {}
 };
 
+using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>;
+
+/// A key for a stored global-function or C++-method API notes entry.
+///
+/// The key is represented by the ID of its parent context, the declaration
+/// name, and optional exact parameter types.
+struct FunctionTableKey {
+  uint32_t parentContextID;
+  uint32_t nameID;
+  std::optional<llvm::SmallVector<IdentifierID, 2>> parameterTypeIDs;
+
+  FunctionTableKey() : parentContextID(-1), nameID(-1) {}
+
+  FunctionTableKey(uint32_t ParentContextID, uint32_t NameID)
+      : parentContextID(ParentContextID), nameID(NameID) {}
+
+  FunctionTableKey(uint32_t ParentContextID, uint32_t NameID,
+                   const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
+      : parentContextID(ParentContextID), nameID(NameID) {
+    parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
+  }
+
+  FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID)
+      : parentContextID(ParentCtx ? ParentCtx->id.Value
+                                  : static_cast<uint32_t>(-1)),
+        nameID(NameID) {}
+
+  FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID,
+                   const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
+      : parentContextID(ParentCtx ? ParentCtx->id.Value
+                                  : static_cast<uint32_t>(-1)),
+        nameID(NameID) {
+    parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
+  }
+
+  llvm::hash_code hashValue() const {
+    auto Hash = llvm::hash_combine(parentContextID, nameID,
+                                   static_cast<bool>(parameterTypeIDs));
+    if (parameterTypeIDs) {
+      Hash = llvm::hash_combine(Hash, parameterTypeIDs->size());
+      for (IdentifierID TypeID : *parameterTypeIDs)
+        Hash = llvm::hash_combine(Hash, static_cast<unsigned>(TypeID));
+    }
+    return Hash;
+  }
+};
+
+inline bool operator==(const FunctionTableKey &LHS,
+                       const FunctionTableKey &RHS) {
+  return LHS.parentContextID == RHS.parentContextID &&
+         LHS.nameID == RHS.nameID &&
+         LHS.parameterTypeIDs == RHS.parameterTypeIDs;
+}
+
+/// Stable reader-facing identity for an API notes function selector entry.
+///
+/// The key keeps the serialized function table identity together with the 
table
+/// kind. parentContextID names only the declaration context and does not say
+/// whether the entry came from the global-function or C++-method table.
+struct APINotesFunctionSelectorKey {
+  FunctionTableKey Key;
+  bool IsCXXMethod = false;
+
+  APINotesFunctionSelectorKey getWithoutParameterSelector() const {
+    return {FunctionTableKey(Key.parentContextID, Key.nameID), IsCXXMethod};
+  }
+
+  llvm::hash_code hashValue() const {
+    return llvm::hash_combine(Key.hashValue(), IsCXXMethod);
+  }
+};
+
+inline bool operator==(const APINotesFunctionSelectorKey &LHS,
+                       const APINotesFunctionSelectorKey &RHS) {
+  return LHS.Key == RHS.Key && LHS.IsCXXMethod == RHS.IsCXXMethod;
+}
+
 /// A temporary reference to an Objective-C selector, suitable for
 /// referencing selector data on the stack.
 ///
@@ -1001,4 +1096,30 @@ struct ObjCSelectorRef {
 } // namespace api_notes
 } // namespace clang
 
+namespace llvm {
+template <> struct DenseMapInfo<clang::api_notes::FunctionTableKey> {
+  static unsigned getHashValue(const clang::api_notes::FunctionTableKey &Key) {
+    return Key.hashValue();
+  }
+
+  static bool isEqual(const clang::api_notes::FunctionTableKey &LHS,
+                      const clang::api_notes::FunctionTableKey &RHS) {
+    return LHS == RHS;
+  }
+};
+
+template <> struct DenseMapInfo<clang::api_notes::APINotesFunctionSelectorKey> 
{
+  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 d46edeb0d2872..778c1a2f5c427 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -156,6 +156,7 @@ enum class OverloadCandidateParamOrder : char;
 enum OverloadCandidateRewriteKind : unsigned;
 class OverloadCandidateSet;
 class Preprocessor;
+struct APINotesSelectorDiagnosticState;
 class SemaAMDGPU;
 class SemaARM;
 class SemaAVR;
@@ -1312,6 +1313,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:
@@ -1664,6 +1667,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/APINotesFormat.h 
b/clang/lib/APINotes/APINotesFormat.h
index f34ca2e2e363f..30fc8599349bf 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -35,7 +35,6 @@ const uint16_t VERSION_MINOR = 41; // 39 for BoundsSafety;
 const uint8_t kSwiftConforms = 1;
 const uint8_t kSwiftDoesNotConform = 2;
 
-using IdentifierID = llvm::PointerEmbeddedInt<unsigned, 31>;
 using IdentifierIDField = llvm::BCVBR<16>;
 
 using SelectorID = llvm::PointerEmbeddedInt<unsigned, 31>;
@@ -365,47 +364,6 @@ constexpr uint8_t FunctionKeyHasParameterSelector = 0x01;
 constexpr unsigned FunctionTableKeyBaseLength =
     sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint8_t) + sizeof(uint16_t);
 
-struct FunctionTableKey {
-  uint32_t parentContextID;
-  uint32_t nameID;
-  std::optional<llvm::SmallVector<IdentifierID, 2>> parameterTypeIDs;
-
-  FunctionTableKey() : parentContextID(-1), nameID(-1) {}
-
-  FunctionTableKey(uint32_t ParentContextID, uint32_t NameID)
-      : parentContextID(ParentContextID), nameID(NameID) {}
-
-  FunctionTableKey(uint32_t ParentContextID, uint32_t NameID,
-                   const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
-      : parentContextID(ParentContextID), nameID(NameID) {
-    parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
-  }
-
-  FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID)
-      : parentContextID(ParentCtx ? ParentCtx->id.Value
-                                  : static_cast<uint32_t>(-1)),
-        nameID(NameID) {}
-
-  FunctionTableKey(std::optional<Context> ParentCtx, IdentifierID NameID,
-                   const llvm::SmallVectorImpl<IdentifierID> &ParameterTypeIDs)
-      : parentContextID(ParentCtx ? ParentCtx->id.Value
-                                  : static_cast<uint32_t>(-1)),
-        nameID(NameID) {
-    parameterTypeIDs.emplace(ParameterTypeIDs.begin(), ParameterTypeIDs.end());
-  }
-
-  llvm::hash_code hashValue() const {
-    auto Hash = llvm::hash_combine(parentContextID, nameID,
-                                   static_cast<bool>(parameterTypeIDs));
-    if (parameterTypeIDs) {
-      Hash = llvm::hash_combine(Hash, parameterTypeIDs->size());
-      for (IdentifierID TypeID : *parameterTypeIDs)
-        Hash = llvm::hash_combine(Hash, static_cast<unsigned>(TypeID));
-    }
-    return Hash;
-  }
-};
-
 template <typename GetIdentifierFn>
 std::optional<FunctionTableKey>
 getFunctionKeyImpl(uint32_t ParentContextID, llvm::StringRef Name,
@@ -438,13 +396,6 @@ getFunctionKeyImpl(uint32_t ParentContextID, 
llvm::StringRef Name,
   return FunctionTableKey(ParentContextID, *NameID, ParameterTypeIDs);
 }
 
-inline bool operator==(const FunctionTableKey &lhs,
-                       const FunctionTableKey &rhs) {
-  return lhs.parentContextID == rhs.parentContextID &&
-         lhs.nameID == rhs.nameID &&
-         lhs.parameterTypeIDs == rhs.parameterTypeIDs;
-}
-
 } // namespace api_notes
 } // namespace clang
 
@@ -492,18 +443,6 @@ template <> struct 
DenseMapInfo<clang::api_notes::SingleDeclTableKey> {
   }
 };
 
-template <> struct DenseMapInfo<clang::api_notes::FunctionTableKey> {
-  static unsigned
-  getHashValue(const clang::api_notes::FunctionTableKey &value) {
-    return value.hashValue();
-  }
-
-  static bool isEqual(const clang::api_notes::FunctionTableKey &lhs,
-                      const clang::api_notes::FunctionTableKey &rhs) {
-    return lhs == rhs;
-  }
-};
-
 } // namespace llvm
 
 #endif

diff  --git a/clang/lib/APINotes/APINotesReader.cpp 
b/clang/lib/APINotes/APINotesReader.cpp
index 888844fbb6cd9..aad597d93d9ee 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -15,10 +15,14 @@
 #include "clang/APINotes/APINotesReader.h"
 #include "APINotesFormat.h"
 #include "clang/APINotes/Types.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/Hashing.h"
 #include "llvm/Bitstream/BitstreamReader.h"
 #include "llvm/Support/DJB.h"
 #include "llvm/Support/OnDiskHashTable.h"
+#include <string>
+#include <type_traits>
+#include <utility>
 
 namespace clang {
 namespace api_notes {
@@ -758,6 +762,9 @@ class APINotesReader::Implementation {
   /// The identifier table.
   std::unique_ptr<SerializedIdentifierTable> IdentifierTable;
 
+  /// Lazy reverse lookup cache from identifier ID to string.
+  std::optional<llvm::DenseMap<uint32_t, llvm::StringRef>> IdentifierStrings;
+
   using SerializedContextIDTable =
       llvm::OnDiskIterableChainedHashTable<ContextIDTableInfo>;
 
@@ -833,6 +840,17 @@ 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 selector keys stored in the given function-like
+  /// table.
+  template <typename TableT>
+  void collectExactFunctionParameterSelectors(
+      TableT &Table,
+      llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &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 +911,54 @@ 
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();
+
+  if (!IdentifierStrings) {
+    IdentifierStrings.emplace();
+    // keys() and data() iterate over the same serialized entries in lockstep.
+    // The serialized hash-table order is not guaranteed to be identifier-ID
+    // order, so keep an explicit ID-to-string map rather than indexing a 
vector
+    // by ID.
+    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)
+      IdentifierStrings->try_emplace(static_cast<uint32_t>(*KnownID),
+                                     *Identifier);
+  }
+
+  auto Known = IdentifierStrings->find(static_cast<uint32_t>(ID));
+  if (Known == IdentifierStrings->end())
+    return std::nullopt;
+  return Known->second;
+}
+
+template <typename TableT>
+void APINotesReader::Implementation::collectExactFunctionParameterSelectors(
+    TableT &Table,
+    llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) {
+  static_assert(std::is_same_v<TableT, SerializedGlobalFunctionTable> ||
+                std::is_same_v<TableT, SerializedCXXMethodTable>);
+  constexpr bool IsCXXMethod = std::is_same_v<TableT, 
SerializedCXXMethodTable>;
+
+  for (const FunctionTableKey &Key : Table.keys()) {
+    if (!Key.parameterTypeIDs)
+      continue;
+
+    Selectors.push_back(APINotesFunctionSelectorKey{Key, IsCXXMethod});
+  }
+}
+
 std::optional<FunctionTableKey>
 APINotesReader::Implementation::getFunctionKey(uint32_t ParentContextID,
                                                llvm::StringRef Name) {
@@ -2351,6 +2417,26 @@ auto APINotesReader::lookupCXXMethod(ContextID CtxID, 
llvm::StringRef Name,
   return lookupCXXMethodImpl(CtxID, Name, Parameters);
 }
 
+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 APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
+}
+
+std::optional<APINotesFunctionSelectorKey>
+APINotesReader::getCXXMethodSelectorKey(
+    ContextID CtxID, llvm::StringRef Name,
+    llvm::ArrayRef<std::string> Parameters) {
+  std::optional<FunctionTableKey> Key =
+      Implementation->getFunctionKey(CtxID.Value, Name, Parameters);
+  if (!Key)
+    return std::nullopt;
+  return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
+}
+
 auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name)
     -> VersionedInfo<CXXMethodInfo> {
   if (!Implementation->CXXMethodTable)
@@ -2418,6 +2504,55 @@ auto APINotesReader::lookupGlobalFunction(
   return lookupGlobalFunctionImpl(Name, Parameters, Ctx);
 }
 
+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 APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false};
+}
+
+std::optional<APINotesFunctionSelectorKey>
+APINotesReader::getGlobalFunctionSelectorKey(
+    llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters,
+    std::optional<Context> Ctx) {
+  std::optional<FunctionTableKey> Key =
+      Implementation->getFunctionKey(Ctx, Name, Parameters);
+  if (!Key)
+    return std::nullopt;
+  return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false};
+}
+
+void APINotesReader::collectExactFunctionParameterSelectors(
+    llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) {
+  if (Implementation->GlobalFunctionTable)
+    Implementation->collectExactFunctionParameterSelectors(
+        *Implementation->GlobalFunctionTable, Selectors);
+  if (Implementation->CXXMethodTable)
+    Implementation->collectExactFunctionParameterSelectors(
+        *Implementation->CXXMethodTable, Selectors);
+}
+
+std::optional<llvm::SmallVector<std::string, 4>>
+APINotesReader::getParameterSelectorSpellingsForDiagnostics(
+    const APINotesFunctionSelectorKey &Key) {
+  if (!Key.Key.parameterTypeIDs)
+    return std::nullopt;
+
+  llvm::SmallVector<std::string, 4> ParameterSpellings;
+  ParameterSpellings.reserve(Key.Key.parameterTypeIDs->size());
+  for (IdentifierID TypeID : *Key.Key.parameterTypeIDs) {
+    std::optional<llvm::StringRef> TypeName =
+        Implementation->getIdentifierString(TypeID);
+    if (!TypeName)
+      return std::nullopt;
+    ParameterSpellings.push_back(TypeName->str());
+  }
+  return ParameterSpellings;
+}
+
 auto APINotesReader::lookupGlobalFunctionImpl(llvm::StringRef Name,
                                               std::optional<Context> Ctx)
     -> VersionedInfo<GlobalFunctionInfo> {

diff  --git a/clang/lib/APINotes/APINotesTypes.cpp 
b/clang/lib/APINotes/APINotesTypes.cpp
index 96dd722587c10..c8b9272aa0ab7 100644
--- a/clang/lib/APINotes/APINotesTypes.cpp
+++ b/clang/lib/APINotes/APINotesTypes.cpp
@@ -11,6 +11,7 @@
 
 namespace clang {
 namespace api_notes {
+
 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 1c3a59873db25..4079675228a21 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -18,11 +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>
 
@@ -788,6 +793,22 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI,
 namespace {
 using namespace api_notes;
 
+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 << ';';
+  for (llvm::StringRef Parameter : Parameters)
+    AppendKeyPart(Parameter);
+  return Key.str().str();
+}
+
 class YAMLConverter {
   const Module &M;
   APINotesWriter Writer;
@@ -1162,11 +1183,24 @@ class YAMLConverter {
       Writer.addField(TagCtxID, Field.Name, FI, SwiftVersion);
     }
 
+    llvm::StringSet<> KnownMethodSelectors;
     for (const auto &CXXMethod : T.Methods) {
       auto WhereParameters = getWhereParameters(CXXMethod);
       if (!WhereParameters.first)
         continue;
 
+      if (WhereParameters.second) {
+        if (!KnownMethodSelectors
+                 .insert(getFunctionSelectorKey(CXXMethod.Name,
+                                                *WhereParameters.second))
+                 .second) {
+          emitError(llvm::Twine("multiple API notes entries for C++ method '") 
+
+                    CXXMethod.Name + "' with Where.Parameters " +
+                    formatAPINotesParameterSelector(*WhereParameters.second));
+          continue;
+        }
+      }
+
       CXXMethodInfo MI;
       convertFunction(CXXMethod, MI);
       if (WhereParameters.second)
@@ -1243,13 +1277,26 @@ class YAMLConverter {
 
     // Write all global functions.
     llvm::StringSet<> KnownNameOnlyFunctions;
+    llvm::StringSet<> 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 (!KnownFunctionSelectors
+                 .insert(getFunctionSelectorKey(Function.Name,
+                                                *WhereParameters.second))
+                 .second) {
+          emitError(
+              llvm::Twine("multiple API notes entries for global function '") +
+              Function.Name + "' with Where.Parameters " +
+              formatAPINotesParameterSelector(*WhereParameters.second));
+          continue;
+        }
+      }
+
+      // 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/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 9f962912148ab..3b987bb308d32 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"
@@ -1327,6 +1328,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 1ab1eac4a5434..c5560605124c8 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"
@@ -1000,6 +1001,7 @@ static void stripAPINotesParameterNullability(QualType 
&ParamType) {
   }
 }
 
+namespace clang {
 struct APINotesParameterSelector {
   SmallVector<std::string, 4> Parameters;
 
@@ -1016,6 +1018,7 @@ struct APINotesParameterSelectorCandidates {
   APINotesParameterSelector Source;
   std::optional<APINotesParameterSelector> Desugared;
 };
+} // namespace clang
 
 static PrintingPolicy
 getAPINotesParameterSelectorPrintingPolicy(const ASTContext &Context) {
@@ -1072,6 +1075,42 @@ getAPINotesParameterSelectorCandidates(const Sema &S, 
const FunctionDecl *FD) {
   return Candidates;
 }
 
+APINotesSelectorDiagnosticReaderState &
+APINotesSelectorDiagnosticState::getOrCreateReaderState(
+    api_notes::APINotesReader &Reader) {
+  auto [StateIt, Inserted] = Readers.try_emplace(&Reader);
+  APINotesSelectorDiagnosticReaderState &State = StateIt->second;
+  if (!Inserted)
+    return State;
+
+  SmallVector<api_notes::APINotesFunctionSelectorKey, 4> Selectors;
+  Reader.collectExactFunctionParameterSelectors(Selectors);
+  State.addSelectors(Selectors);
+  return State;
+}
+
+static APINotesSelectorDiagnosticReaderState &
+getAPINotesSelectorDiagnosticState(Sema &S, api_notes::APINotesReader *Reader) 
{
+  if (!S.APINotesSelectorDiagnostics)
+    S.APINotesSelectorDiagnostics =
+        std::make_unique<APINotesSelectorDiagnosticState>();
+
+  return S.APINotesSelectorDiagnostics->getOrCreateReaderState(*Reader);
+}
+
+void APINotesSelectorDiagnosticReaderState::markCandidatesUsed(
+    llvm::function_ref<std::optional<api_notes::APINotesFunctionSelectorKey>(
+        ArrayRef<std::string>)>
+        GetSelectorKey,
+    const APINotesParameterSelectorCandidates &Candidates) {
+  if (auto Key = GetSelectorKey(Candidates.Source.Parameters))
+    markUsed(*Key);
+  if (Candidates.Desugared) {
+    if (auto Key = GetSelectorKey(Candidates.Desugared->Parameters))
+      markUsed(*Key);
+  }
+}
+
 // 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 +1170,7 @@ void Sema::ProcessAPINotes(Decl *D) {
       if (FD->getDeclName().isIdentifier()) {
         auto ParameterSelectorCandidates =
             getAPINotesParameterSelectorCandidates(*this, FD);
+
         for (auto Reader : Readers) {
           auto Info =
               Reader->lookupGlobalFunction(FD->getName(), APINotesContext);
@@ -1143,6 +1183,21 @@ void Sema::ProcessAPINotes(Decl *D) {
                   return Reader->lookupGlobalFunction(FD->getName(), 
Parameters,
                                                       APINotesContext);
                 });
+
+          if (ParameterSelectorCandidates) {
+            auto &DiagnosticState =
+                getAPINotesSelectorDiagnosticState(*this, Reader);
+            if (auto BroadKey = Reader->getGlobalFunctionSelectorKey(
+                    FD->getName(), APINotesContext))
+              DiagnosticState.noteSeenDeclaration(*BroadKey, FD->getName(),
+                                                  FD->getLocation());
+            DiagnosticState.markCandidatesUsed(
+                [&](ArrayRef<std::string> Parameters) {
+                  return Reader->getGlobalFunctionSelectorKey(
+                      FD->getName(), Parameters, APINotesContext);
+                },
+                *ParameterSelectorCandidates);
+          }
         }
       }
 
@@ -1348,6 +1403,21 @@ void Sema::ProcessAPINotes(Decl *D) {
                     return Reader->lookupCXXMethod(Context->id, MethodName,
                                                    Parameters);
                   });
+
+            if (ParameterSelectorCandidates) {
+              auto &DiagnosticState =
+                  getAPINotesSelectorDiagnosticState(*this, Reader);
+              if (auto BroadKey =
+                      Reader->getCXXMethodSelectorKey(Context->id, MethodName))
+                DiagnosticState.noteSeenDeclaration(*BroadKey, MethodName,
+                                                    CXXMethod->getLocation());
+              DiagnosticState.markCandidatesUsed(
+                  [&](ArrayRef<std::string> Parameters) {
+                    return Reader->getCXXMethodSelectorKey(
+                        Context->id, MethodName, Parameters);
+                  },
+                  *ParameterSelectorCandidates);
+            }
           }
         }
       }
@@ -1374,3 +1444,41 @@ void Sema::ProcessAPINotes(Decl *D) {
     }
   }
 }
+
+void APINotesSelectorDiagnosticReaderState::diagnoseUnused(
+    Sema &S, api_notes::APINotesReader &Reader) const {
+  for (const auto &Selector : SelectorUsed) {
+    if (Selector.second)
+      continue;
+
+    auto SeenName =
+        SeenNames.find(Selector.first.getWithoutParameterSelector());
+    if (SeenName == SeenNames.end())
+      continue;
+
+    std::optional<SmallVector<std::string, 4>> ParameterSpellings =
+        Reader.getParameterSelectorSpellingsForDiagnostics(Selector.first);
+    if (!ParameterSpellings)
+      continue;
+
+    S.Diag(SeenName->second.Loc, diag::warn_apinotes_message)
+        << (llvm::Twine("API notes entry for '") + SeenName->second.Name +
+            "' has unmatched Where.Parameters " +
+            api_notes::formatAPINotesParameterSelector(*ParameterSpellings))
+               .str();
+  }
+}
+
+void APINotesSelectorDiagnosticState::diagnoseUnused(Sema &S) const {
+  for (const auto &ReaderSelectors : Readers)
+    ReaderSelectors.second.diagnoseUnused(S, *ReaderSelectors.first);
+}
+
+void Sema::DiagnoseUnusedAPINotesSelectors() {
+  if (!APINotesSelectorDiagnostics)
+    return;
+
+  if (!Diags.isIgnored(diag::warn_apinotes_message, SourceLocation()))
+    APINotesSelectorDiagnostics->diagnoseUnused(*this);
+  APINotesSelectorDiagnostics.reset();
+}

diff  --git a/clang/lib/Sema/SemaAPINotesInternal.h 
b/clang/lib/Sema/SemaAPINotesInternal.h
new file mode 100644
index 0000000000000..5766343956ce5
--- /dev/null
+++ b/clang/lib/Sema/SemaAPINotesInternal.h
@@ -0,0 +1,98 @@
+//===--- 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/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLFunctionalExtras.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include <string>
+#include <utility>
+
+namespace clang {
+class Sema;
+struct APINotesParameterSelectorCandidates;
+namespace api_notes {
+class APINotesReader;
+} // namespace api_notes
+
+/// Source name and location for a declaration seen by Sema.
+struct APINotesSelectorDiagnosticName {
+  SourceLocation Loc;
+  std::string Name;
+};
+
+/// Tracks exact Where.Parameters selectors from one API notes reader.
+///
+/// Sema marks selectors as used when a visible declaration matches them. It
+/// also records broad/name-only declarations seen in the translation unit, so
+/// end-of-TU diagnostics can warn about exact selectors for known names that
+/// were never matched.
+struct APINotesSelectorDiagnosticReaderState {
+  /// Exact Where.Parameters selector keys stored by API notes. The bool is
+  /// true once Sema sees a declaration matching the exact selector.
+  llvm::DenseMap<api_notes::APINotesFunctionSelectorKey, bool> SelectorUsed;
+
+  /// Maps broad/name-only keys to a declaration location/name used for
+  /// diagnostics.
+  llvm::DenseMap<api_notes::APINotesFunctionSelectorKey,
+                 APINotesSelectorDiagnosticName>
+      SeenNames;
+
+  void addSelector(const api_notes::APINotesFunctionSelectorKey &Key) {
+    SelectorUsed.try_emplace(Key, false);
+  }
+
+  void addSelectors(
+      llvm::ArrayRef<api_notes::APINotesFunctionSelectorKey> Selectors) {
+    SelectorUsed.reserve(Selectors.size());
+    SeenNames.reserve(Selectors.size());
+    for (const auto &Selector : Selectors)
+      addSelector(Selector);
+  }
+
+  void noteSeenDeclaration(const api_notes::APINotesFunctionSelectorKey &Key,
+                           llvm::StringRef Name, SourceLocation Loc) {
+    SeenNames.insert({Key.getWithoutParameterSelector(), {Loc, Name.str()}});
+  }
+
+  void markUsed(const api_notes::APINotesFunctionSelectorKey &Key) {
+    auto KnownSelector = SelectorUsed.find(Key);
+    if (KnownSelector != SelectorUsed.end())
+      KnownSelector->second = true;
+  }
+
+  void markCandidatesUsed(
+      llvm::function_ref<std::optional<api_notes::APINotesFunctionSelectorKey>(
+          llvm::ArrayRef<std::string>)>
+          GetSelectorKey,
+      const APINotesParameterSelectorCandidates &Candidates);
+
+  void diagnoseUnused(Sema &S, api_notes::APINotesReader &Reader) const;
+};
+
+/// Selector diagnostic state for all API notes readers used by one Sema.
+struct APINotesSelectorDiagnosticState {
+  llvm::DenseMap<api_notes::APINotesReader *,
+                 APINotesSelectorDiagnosticReaderState>
+      Readers;
+
+  APINotesSelectorDiagnosticReaderState &
+  getOrCreateReaderState(api_notes::APINotesReader &Reader);
+
+  void diagnoseUnused(Sema &S) const;
+};
+
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_SEMA_SEMAAPINOTESINTERNAL_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..68ff7a1b113df
--- /dev/null
+++ b/clang/test/APINotes/where-parameters-diagnostics.cpp
@@ -0,0 +1,239 @@
+// RUN: rm -rf %t && split-file %s %t
+// RUN: not %clang_cc1 -fsyntax-only -fapinotes %t/diagnostics.cpp -I 
%t/WhereParametersDuplicateSelectorDiag 2>&1 | FileCheck 
%t/WhereParametersDuplicateSelectorDiag/APINotes.apinotes 
--check-prefix=DUPLICATE
+// RUN: rm -rf %t/ModulesCache && mkdir -p %t/ModulesCache
+// RUN: rm -rf %t/PragmaModulesCache && mkdir -p %t/PragmaModulesCache
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/PragmaModulesCache -fdisable-module-hash 
-fapinotes-modules 
-fmodule-map-file=%t/WhereParametersPragmaDiag/module.modulemap -Wapinotes 
-fsyntax-only -I %t/WhereParametersPragmaDiag %t/pragma-diagnostics.cpp -x c++ 
2>&1 | count 0
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersDiagnostics 
-fdisable-module-hash -fapinotes-modules 
-fmodule-map-file=%t/WhereParametersDiagnostics/module.modulemap -Wapinotes 
-fsyntax-only -I %t/WhereParametersDiagnostics %t/diagnostics.cpp -x c++ 2>&1 | 
FileCheck %t/WhereParametersDiagnostics/WhereParametersDiagnostics.apinotes 
--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 
-fmodule-map-file=%t/WhereParametersDiagnostics/module.modulemap -Wno-apinotes 
-I %t/WhereParametersDiagnostics %t/diagnostics.cpp -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 
-fmodule-map-file=%t/WhereParametersDiagnostics/module.modulemap -Wno-apinotes 
-I %t/WhereParametersDiagnostics %t/diagnostics.cpp -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 
-fmodule-map-file=%t/WhereParametersDiagnostics/module.modulemap -Wno-apinotes 
-I %t/WhereParametersDiagnostics %t/diagnostics.cpp -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 
-fmodule-map-file=%t/WhereParametersDiagnostics/module.modulemap -Wno-apinotes 
-I %t/WhereParametersDiagnostics %t/diagnostics.cpp -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 
-fmodule-map-file=%t/WhereParametersDiagnostics/module.modulemap -Wno-apinotes 
-I %t/WhereParametersDiagnostics %t/diagnostics.cpp -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 
-fmodule-map-file=%t/WhereParametersDiagnostics/module.modulemap -Wno-apinotes 
-I %t/WhereParametersDiagnostics %t/diagnostics.cpp -ast-dump -ast-dump-filter 
DiagnosticWidget::diagnosticAliasMatchedMethod -x c++ | FileCheck %s 
--check-prefix=ALIAS-MATCHED-METHOD
+
+// 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(_:)"
+
+//--- diagnostics.cpp
+#include "WhereParametersDiagnostics.h"
+
+//--- WhereParametersDiagnostics/module.modulemap
+module WhereParametersDiagnostics {
+  header "WhereParametersDiagnostics.h"
+  export *
+}
+
+//--- WhereParametersDiagnostics/WhereParametersDiagnostics.h
+#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
+
+//--- WhereParametersDiagnostics/WhereParametersDiagnostics.apinotes
+---
+Name: WhereParametersDiagnostics
+Functions:
+- Name: unmatchedGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: shouldNotApplyGlobal(_:)
+# UNMATCHED-DAG: warning: API notes entry for 'unmatchedGlobal' has unmatched 
Where.Parameters [int]
+- Name: diagnosticBroadGlobal
+  SwiftPrivate: true
+- Name: diagnosticBroadGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: shouldNotApplyBroadGlobal(_:)
+# UNMATCHED-DAG: warning: API notes entry for 'diagnosticBroadGlobal' has 
unmatched Where.Parameters [int]
+- 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(_:)
+# UNMATCHED-DAG: warning: API notes entry for 'unmatchedMethod' has unmatched 
Where.Parameters [int]
+  - Name: diagnosticBroadMethod
+    SwiftPrivate: true
+  - Name: diagnosticBroadMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: shouldNotApplyBroadMethod(_:)
+# UNMATCHED-DAG: warning: API notes entry for 'diagnosticBroadMethod' has 
unmatched Where.Parameters [int]
+  - Name: diagnosticMatchedMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: diagnosticMatchedMethod(_:)
+  - Name: diagnosticAliasMatchedMethod
+    Where:
+      Parameters:
+      - int
+    SwiftName: diagnosticAliasMatchedMethod(_:)
+
+
+//--- WhereParametersDuplicateSelectorDiag/WhereParametersDiagnostics.h
+#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
+
+//--- WhereParametersDuplicateSelectorDiag/APINotes.apinotes
+---
+Name: WhereParametersDiagnostics
+Functions:
+- Name: duplicateGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: duplicateGlobalA(_:)
+- Name: duplicateGlobal
+  Where:
+    Parameters:
+    - int
+  SwiftName: duplicateGlobalB(_:)
+# DUPLICATE: error: multiple API notes entries for global function 
'duplicateGlobal' with Where.Parameters [int]
+- Name: duplicateEmpty
+  Where:
+    Parameters: []
+  SwiftName: duplicateEmptyA()
+- Name: duplicateEmpty
+  Where:
+    Parameters: []
+  SwiftName: duplicateEmptyB()
+# DUPLICATE: error: multiple API notes entries for global function 
'duplicateEmpty' with Where.Parameters []
+- 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(_:)
+# DUPLICATE: error: multiple API notes entries for C++ method 
'duplicateMethod' with Where.Parameters [int]
+  - Name: duplicateEmpty
+    Where:
+      Parameters: []
+    SwiftName: duplicateEmptyA()
+  - Name: duplicateEmpty
+    Where:
+      Parameters: []
+    SwiftName: duplicateEmptyB()
+# DUPLICATE: error: multiple API notes entries for C++ method 'duplicateEmpty' 
with Where.Parameters []
+  - Name: allowed
+    SwiftPrivate: true
+  - Name: allowed
+    Where:
+      Parameters:
+      - int
+    SwiftName: allowedInt(_:)
+  - Name: allowed
+    Where:
+      Parameters:
+      - double
+    SwiftName: allowedDouble(_:)
+
+
+//--- pragma-diagnostics.cpp
+#include "WhereParametersPragma.h"
+
+//--- WhereParametersPragmaDiag/module.modulemap
+module WhereParametersPragma { header "WhereParametersPragma.h" }
+
+//--- WhereParametersPragmaDiag/WhereParametersPragma.h
+#ifndef WHERE_PARAMETERS_PRAGMA_H
+#define WHERE_PARAMETERS_PRAGMA_H
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wapinotes"
+void pragmaMatched(int);
+#pragma clang diagnostic pop
+
+void pragmaMatched(float);
+
+#endif // WHERE_PARAMETERS_PRAGMA_H
+
+//--- WhereParametersPragmaDiag/WhereParametersPragma.apinotes
+---
+Name: WhereParametersPragma
+Functions:
+- Name: pragmaMatched
+  Where:
+    Parameters:
+    - int
+  SwiftName: pragmaMatched(_:)
+...


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

Reply via email to