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

>From d55d8f7edc3fb34ad1eae87ac7f2b2cf55b703d1 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <[email protected]>
Date: Fri, 19 Jun 2026 13:42:06 +0200
Subject: [PATCH] [APINotes] Normalize Where.Parameters selector spellings

---
 clang/include/clang/APINotes/Types.h          |   3 +
 clang/include/clang/Sema/APINotesSelector.h   |  34 +++
 clang/lib/APINotes/APINotesTypes.cpp          | 114 ++++++++++
 clang/lib/APINotes/APINotesYAMLCompiler.cpp   |  79 +++++--
 clang/lib/Sema/APINotesSelector.cpp           |  79 +++++++
 clang/lib/Sema/CMakeLists.txt                 |   1 +
 clang/lib/Sema/SemaAPINotes.cpp               |  86 +-------
 .../WhereParametersNormalization.apinotes     | 136 ++++++++++++
 .../Headers/WhereParametersNormalization.h    |  50 +++++
 .../APINotes/Inputs/Headers/module.modulemap  |   5 +
 .../APINotes/where-parameters-diagnostics.cpp |  24 +++
 .../where-parameters-normalization.cpp        | 116 +++++++++++
 clang/unittests/Sema/APINotesSelectorTest.cpp | 196 ++++++++++++++++++
 clang/unittests/Sema/CMakeLists.txt           |   1 +
 14 files changed, 825 insertions(+), 99 deletions(-)
 create mode 100644 clang/include/clang/Sema/APINotesSelector.h
 create mode 100644 clang/lib/Sema/APINotesSelector.cpp
 create mode 100644 
clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.apinotes
 create mode 100644 
clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.h
 create mode 100644 clang/test/APINotes/where-parameters-normalization.cpp
 create mode 100644 clang/unittests/Sema/APINotesSelectorTest.cpp

diff --git a/clang/include/clang/APINotes/Types.h 
b/clang/include/clang/APINotes/Types.h
index af989d3a1b7f0..a839c08300b40 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -66,6 +66,9 @@ enum class SwiftNewTypeKind {
 
 enum class SwiftSafetyKind { Unspecified, Safe, Unsafe, None };
 
+/// Normalize an API notes parameter selector spelling for matching.
+std::string normalizeAPINotesParameterSelector(llvm::StringRef Spelling);
+
 /// Describes API notes data for any entity.
 ///
 /// This is used as the base of all API notes.
diff --git a/clang/include/clang/Sema/APINotesSelector.h 
b/clang/include/clang/Sema/APINotesSelector.h
new file mode 100644
index 0000000000000..e0eb20fd58d3c
--- /dev/null
+++ b/clang/include/clang/Sema/APINotesSelector.h
@@ -0,0 +1,34 @@
+//===--- APINotesSelector.h - API Notes selector helpers --------*- 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_SEMA_APINOTESSELECTOR_H
+#define LLVM_CLANG_SEMA_APINOTESSELECTOR_H
+
+#include "llvm/ADT/SmallVector.h"
+#include <optional>
+#include <string>
+
+namespace clang {
+
+class ASTContext;
+class FunctionDecl;
+
+using APINotesParameterSelector = llvm::SmallVector<std::string, 4>;
+
+struct APINotesParameterSelectorCandidates {
+  APINotesParameterSelector Source;
+  std::optional<APINotesParameterSelector> Desugared;
+};
+
+std::optional<APINotesParameterSelectorCandidates>
+getAPINotesParameterSelectorCandidates(const ASTContext &Context,
+                                       const FunctionDecl *FD);
+
+} // namespace clang
+
+#endif // LLVM_CLANG_SEMA_APINOTESSELECTOR_H
diff --git a/clang/lib/APINotes/APINotesTypes.cpp 
b/clang/lib/APINotes/APINotesTypes.cpp
index c8b9272aa0ab7..1f6e0fa88779e 100644
--- a/clang/lib/APINotes/APINotesTypes.cpp
+++ b/clang/lib/APINotes/APINotesTypes.cpp
@@ -7,11 +7,125 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/APINotes/Types.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/Support/raw_ostream.h"
 
 namespace clang {
 namespace api_notes {
 
+// Conservatively detect spellings where cv-qualification belongs to an
+// indirect/declarator layer rather than the by-value parameter itself.
+static bool
+hasTopLevelIndirectParameterSelectorSpelling(llvm::StringRef Spelling) {
+  unsigned Depth = 0;
+
+  for (char C : Spelling) {
+    switch (C) {
+    case '*':
+    case '&':
+      if (Depth == 0)
+        return true;
+      break;
+
+    case '[':
+    case '(':
+      if (Depth == 0)
+        return true;
+      ++Depth;
+      break;
+
+    case '<':
+      ++Depth;
+      break;
+
+    case '>':
+    case ']':
+    case ')':
+      if (Depth != 0)
+        --Depth;
+      break;
+
+    default:
+      break;
+    }
+  }
+
+  return false;
+}
+
+static bool shouldDropParameterSelectorSpace(char Previous, char Next) {
+  if (Previous == '<' || Previous == ',' || Next == '>' || Next == ',' ||
+      Next == '<')
+    return true;
+
+  if (Next == '*' || Next == '&')
+    return true;
+
+  if (Previous == '&' && Next == '&')
+    return true;
+
+  return false;
+}
+
+static std::string
+collapseParameterSelectorWhitespace(llvm::StringRef Spelling) {
+  llvm::SmallVector<llvm::StringRef, 4> Tokens;
+  llvm::SplitString(Spelling, Tokens);
+  return llvm::join(Tokens, " ");
+}
+
+static llvm::StringRef normalizeUnsignedIntSpelling(llvm::StringRef Spelling) {
+  if (Spelling == "unsigned")
+    return "unsigned int";
+  return Spelling;
+}
+
+static llvm::StringRef stripTopLevelValueConst(llvm::StringRef Spelling) {
+  if (hasTopLevelIndirectParameterSelectorSpelling(Spelling))
+    return Spelling;
+
+  Spelling.consume_front("const ");
+  Spelling.consume_back(" const");
+  return Spelling;
+}
+
+// Remove spaces around selector punctuation while preserving token-separating
+// spaces such as the one in "unsigned int".
+static void removeParameterSelectorPunctuationSpaces(
+    llvm::StringRef Spelling, llvm::SmallVectorImpl<char> &Normalized) {
+  Normalized.clear();
+  for (unsigned I = 0, E = Spelling.size(); I != E; ++I) {
+    char C = Spelling[I];
+    if (C == ' ' && I != 0 && I + 1 != E &&
+        shouldDropParameterSelectorSpace(Spelling[I - 1], Spelling[I + 1]))
+      continue;
+
+    Normalized.push_back(C);
+  }
+}
+
+static std::string stripTopLevelPointerConst(llvm::StringRef Spelling) {
+  if (!Spelling.consume_back("*const") && !Spelling.consume_back("* const"))
+    return Spelling.str();
+
+  std::string WithoutTopLevelConst = Spelling.str();
+  WithoutTopLevelConst += '*';
+  return WithoutTopLevelConst;
+}
+
+std::string normalizeAPINotesParameterSelector(llvm::StringRef Spelling) {
+  std::string Collapsed = collapseParameterSelectorWhitespace(Spelling);
+
+  llvm::StringRef WithoutTopLevelValueConst =
+      normalizeUnsignedIntSpelling(stripTopLevelValueConst(Collapsed));
+
+  llvm::SmallString<32> WithoutPunctuationSpaces;
+  removeParameterSelectorPunctuationSpaces(WithoutTopLevelValueConst,
+                                           WithoutPunctuationSpaces);
+  return stripTopLevelPointerConst(WithoutPunctuationSpaces);
+}
+
 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 4079675228a21..4d399c29f3d66 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -21,6 +21,7 @@
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
 #include "llvm/ADT/StringSet.h"
 #include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/VersionTuple.h"
@@ -793,9 +794,9 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI,
 namespace {
 using namespace api_notes;
 
-static std::string
-getFunctionSelectorKey(llvm::StringRef Name,
-                       llvm::ArrayRef<llvm::StringRef> Parameters) {
+template <typename ParametersT>
+static std::string getFunctionSelectorKey(llvm::StringRef Name,
+                                          const ParametersT &Parameters) {
   llvm::SmallString<64> Key;
   llvm::raw_svector_ostream OS(Key);
   auto AppendKeyPart = [&OS](llvm::StringRef Part) {
@@ -809,6 +810,31 @@ getFunctionSelectorKey(llvm::StringRef Name,
   return Key.str().str();
 }
 
+// YAML conversion has parameter spellings but no AST context. Keep this as a
+// narrow lexical normalization step. Declaration spellings are normalized with
+// QualType in Sema before using the same lexical selector normalization.
+static void normalizeWhereParameterList(
+    llvm::ArrayRef<llvm::StringRef> Parameters,
+    llvm::SmallVectorImpl<std::string> &NormalizedParameters) {
+  NormalizedParameters.clear();
+  NormalizedParameters.reserve(Parameters.size());
+
+  for (llvm::StringRef Parameter : Parameters)
+    NormalizedParameters.push_back(
+        normalizeAPINotesParameterSelector(Parameter));
+}
+
+static llvm::SmallVector<llvm::StringRef, 4>
+getParameterSelectorRefs(llvm::ArrayRef<std::string> Parameters) {
+  llvm::SmallVector<llvm::StringRef, 4> ParameterRefs;
+  ParameterRefs.reserve(Parameters.size());
+
+  for (const std::string &Parameter : Parameters)
+    ParameterRefs.push_back(Parameter);
+
+  return ParameterRefs;
+}
+
 class YAMLConverter {
   const Module &M;
   APINotesWriter Writer;
@@ -1190,24 +1216,32 @@ class YAMLConverter {
         continue;
 
       if (WhereParameters.second) {
+        llvm::SmallVector<std::string, 4> NormalizedWhereParameters;
+        normalizeWhereParameterList(*WhereParameters.second,
+                                    NormalizedWhereParameters);
+        auto NormalizedWhereParameterRefs =
+            getParameterSelectorRefs(NormalizedWhereParameters);
         if (!KnownMethodSelectors
                  .insert(getFunctionSelectorKey(CXXMethod.Name,
-                                                *WhereParameters.second))
+                                                NormalizedWhereParameters))
                  .second) {
           emitError(llvm::Twine("multiple API notes entries for C++ method '") 
+
                     CXXMethod.Name + "' with Where.Parameters " +
-                    formatAPINotesParameterSelector(*WhereParameters.second));
+                    api_notes::formatAPINotesParameterSelector(
+                        NormalizedWhereParameters));
           continue;
         }
+
+        CXXMethodInfo MI;
+        convertFunction(CXXMethod, MI);
+        Writer.addCXXMethod(TagCtxID, CXXMethod.Name,
+                            NormalizedWhereParameterRefs, MI, SwiftVersion);
+        continue;
       }
 
       CXXMethodInfo MI;
       convertFunction(CXXMethod, MI);
-      if (WhereParameters.second)
-        Writer.addCXXMethod(TagCtxID, CXXMethod.Name, *WhereParameters.second,
-                            MI, SwiftVersion);
-      else
-        Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion);
+      Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion);
     }
 
     // Convert nested tags.
@@ -1284,21 +1318,32 @@ class YAMLConverter {
         continue;
 
       if (WhereParameters.second) {
+        llvm::SmallVector<std::string, 4> NormalizedWhereParameters;
+        normalizeWhereParameterList(*WhereParameters.second,
+                                    NormalizedWhereParameters);
+        auto NormalizedWhereParameterRefs =
+            getParameterSelectorRefs(NormalizedWhereParameters);
         if (!KnownFunctionSelectors
                  .insert(getFunctionSelectorKey(Function.Name,
-                                                *WhereParameters.second))
+                                                NormalizedWhereParameters))
                  .second) {
           emitError(
               llvm::Twine("multiple API notes entries for global function '") +
               Function.Name + "' with Where.Parameters " +
-              formatAPINotesParameterSelector(*WhereParameters.second));
+              formatAPINotesParameterSelector(NormalizedWhereParameters));
           continue;
         }
+
+        GlobalFunctionInfo GFI;
+        convertFunction(Function, GFI);
+        Writer.addGlobalFunction(Ctx, Function.Name,
+                                 NormalizedWhereParameterRefs, GFI,
+                                 SwiftVersion);
+        continue;
       }
 
       // Check for duplicate name-only global functions.
-      if (!WhereParameters.second &&
-          !KnownNameOnlyFunctions.insert(Function.Name).second) {
+      if (!KnownNameOnlyFunctions.insert(Function.Name).second) {
         emitError(llvm::Twine("multiple definitions of global function '") +
                   Function.Name + "'");
         continue;
@@ -1306,11 +1351,7 @@ class YAMLConverter {
 
       GlobalFunctionInfo GFI;
       convertFunction(Function, GFI);
-      if (WhereParameters.second)
-        Writer.addGlobalFunction(Ctx, Function.Name, *WhereParameters.second,
-                                 GFI, SwiftVersion);
-      else
-        Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion);
+      Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion);
     }
 
     // Write all enumerators.
diff --git a/clang/lib/Sema/APINotesSelector.cpp 
b/clang/lib/Sema/APINotesSelector.cpp
new file mode 100644
index 0000000000000..a344f6497f3e0
--- /dev/null
+++ b/clang/lib/Sema/APINotesSelector.cpp
@@ -0,0 +1,79 @@
+//===--- APINotesSelector.cpp - API Notes selector helpers 
----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Sema/APINotesSelector.h"
+#include "clang/APINotes/Types.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Type.h"
+
+using namespace clang;
+
+namespace {
+
+PrintingPolicy
+getAPINotesParameterSelectorPrintingPolicy(const ASTContext &Context) {
+  PrintingPolicy Policy(Context.getLangOpts());
+  Policy.PrintAsCanonical = false;
+  Policy.FullyQualifiedName = false;
+  Policy.SuppressScope = false;
+  Policy.UsePreferredNames = false;
+  Policy.MSVCFormatting = false;
+  Policy.SplitTemplateClosers = false;
+  Policy.IncludeNewlines = false;
+  return Policy;
+}
+
+// Print the APINotes selector spelling for one parameter. The source-spelled
+// selector is tried first. The desugared spelling is only a permissive
+// fallback.
+std::string getAPINotesParameterSelectorSpelling(QualType ParamType,
+                                                 const ASTContext &Context,
+                                                 const PrintingPolicy &Policy,
+                                                 bool Desugar) {
+  if (Desugar)
+    ParamType = ParamType.getDesugaredType(Context);
+
+  ParamType.removeLocalConst();
+  ParamType.removeLocalVolatile();
+  ParamType = ParamType.stripNullability(Context);
+
+  return api_notes::normalizeAPINotesParameterSelector(
+      ParamType.getAsString(Policy));
+}
+
+} // namespace
+
+std::optional<APINotesParameterSelectorCandidates>
+clang::getAPINotesParameterSelectorCandidates(const ASTContext &Context,
+                                              const FunctionDecl *FD) {
+  const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
+  if (!FPT)
+    return std::nullopt;
+
+  APINotesParameterSelectorCandidates Candidates;
+  APINotesParameterSelector Desugared;
+  Candidates.Source.reserve(FPT->getNumParams());
+  Desugared.reserve(FPT->getNumParams());
+
+  const PrintingPolicy Policy =
+      getAPINotesParameterSelectorPrintingPolicy(Context);
+  for (QualType ParamType : FPT->param_types()) {
+    Candidates.Source.push_back(
+        getAPINotesParameterSelectorSpelling(ParamType, Context, Policy,
+                                             /*Desugar=*/false));
+    Desugared.push_back(getAPINotesParameterSelectorSpelling(
+        ParamType, Context, Policy, /*Desugar=*/true));
+  }
+
+  if (Candidates.Source != Desugared)
+    Candidates.Desugared = std::move(Desugared);
+
+  return Candidates;
+}
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 88f0c993888d9..7ab1916eb568b 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -14,6 +14,7 @@ clang_tablegen(OpenCLBuiltins.inc -gen-clang-opencl-builtins
   )
 
 add_clang_library(clangSema
+  APINotesSelector.cpp
   AnalysisBasedWarnings.cpp
   CheckExprLifetime.cpp
   CodeCompleteConsumer.cpp
diff --git a/clang/lib/Sema/SemaAPINotes.cpp b/clang/lib/Sema/SemaAPINotes.cpp
index 78153d9ddf39d..d8529afa596b7 100644
--- a/clang/lib/Sema/SemaAPINotes.cpp
+++ b/clang/lib/Sema/SemaAPINotes.cpp
@@ -21,6 +21,7 @@
 #include "clang/Analysis/Analyses/LifetimeSafety/LifetimeAnnotations.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Lex/Lexer.h"
+#include "clang/Sema/APINotesSelector.h"
 #include "clang/Sema/SemaObjC.h"
 #include "clang/Sema/SemaSwift.h"
 #include <stack>
@@ -1001,81 +1002,6 @@ UnwindTagContext(TagDecl *DC, api_notes::APINotesManager 
&APINotes) {
   return std::nullopt;
 }
 
-namespace clang {
-struct APINotesParameterSelector {
-  SmallVector<std::string, 4> Parameters;
-
-  bool operator==(const APINotesParameterSelector &Other) const {
-    return Parameters == Other.Parameters;
-  }
-
-  bool operator!=(const APINotesParameterSelector &Other) const {
-    return !(*this == Other);
-  }
-};
-
-struct APINotesParameterSelectorCandidates {
-  APINotesParameterSelector Source;
-  std::optional<APINotesParameterSelector> Desugared;
-};
-} // namespace clang
-
-static PrintingPolicy
-getAPINotesParameterSelectorPrintingPolicy(const ASTContext &Context) {
-  PrintingPolicy Policy(Context.getLangOpts());
-  Policy.PrintAsCanonical = false;
-  Policy.FullyQualifiedName = false;
-  Policy.SuppressScope = false;
-  Policy.UsePreferredNames = false;
-  Policy.MSVCFormatting = false;
-  Policy.SplitTemplateClosers = false;
-  Policy.IncludeNewlines = false;
-  return Policy;
-}
-
-// Print the APINotes selector spelling for one parameter. The source-spelled
-// selector is tried first. The desugared spelling is only a permissive
-// fallback.
-static std::string getAPINotesParameterSelectorSpelling(
-    QualType ParamType, const ASTContext &Context, const PrintingPolicy 
&Policy,
-    bool Desugar) {
-  if (Desugar)
-    ParamType = ParamType.getDesugaredType(Context);
-
-  ParamType.removeLocalConst();
-  ParamType.removeLocalVolatile();
-  ParamType = ParamType.stripNullability(Context);
-
-  return ParamType.getAsString(Policy);
-}
-
-static std::optional<APINotesParameterSelectorCandidates>
-getAPINotesParameterSelectorCandidates(const Sema &S, const FunctionDecl *FD) {
-  const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
-  if (!FPT)
-    return std::nullopt;
-
-  APINotesParameterSelectorCandidates Candidates;
-  APINotesParameterSelector Desugared;
-  Candidates.Source.Parameters.reserve(FPT->getNumParams());
-  Desugared.Parameters.reserve(FPT->getNumParams());
-
-  const PrintingPolicy Policy =
-      getAPINotesParameterSelectorPrintingPolicy(S.Context);
-  for (QualType ParamType : FPT->param_types()) {
-    Candidates.Source.Parameters.push_back(
-        getAPINotesParameterSelectorSpelling(ParamType, S.Context, Policy,
-                                             /*Desugar=*/false));
-    Desugared.Parameters.push_back(getAPINotesParameterSelectorSpelling(
-        ParamType, S.Context, Policy, /*Desugar=*/true));
-  }
-
-  if (Candidates.Source != Desugared)
-    Candidates.Desugared = std::move(Desugared);
-
-  return Candidates;
-}
-
 APINotesSelectorDiagnosticReaderState &
 APINotesSelectorDiagnosticState::getOrCreateReaderState(
     api_notes::APINotesReader &Reader) {
@@ -1104,10 +1030,10 @@ void 
APINotesSelectorDiagnosticReaderState::markCandidatesUsed(
         ArrayRef<std::string>)>
         GetSelectorKey,
     const APINotesParameterSelectorCandidates &Candidates) {
-  if (auto Key = GetSelectorKey(Candidates.Source.Parameters))
+  if (auto Key = GetSelectorKey(Candidates.Source))
     markUsed(*Key);
   if (Candidates.Desugared) {
-    if (auto Key = GetSelectorKey(Candidates.Desugared->Parameters))
+    if (auto Key = GetSelectorKey(*Candidates.Desugared))
       markUsed(*Key);
   }
 }
@@ -1123,7 +1049,7 @@ static void processExactAPINotes(
         ArrayRef<std::string>)>
         LookupExact) {
   auto ProcessSelector = [&](const APINotesParameterSelector &Selector) {
-    auto Info = LookupExact(Selector.Parameters);
+    auto Info = LookupExact(Selector);
     if (Info.size() == 0)
       return false;
 
@@ -1170,7 +1096,7 @@ void Sema::ProcessAPINotes(Decl *D) {
     if (auto FD = dyn_cast<FunctionDecl>(D)) {
       if (FD->getDeclName().isIdentifier()) {
         auto ParameterSelectorCandidates =
-            getAPINotesParameterSelectorCandidates(*this, FD);
+            getAPINotesParameterSelectorCandidates(Context, FD);
 
         for (auto Reader : Readers) {
           auto Info =
@@ -1383,7 +1309,7 @@ void Sema::ProcessAPINotes(Decl *D) {
           !isa<CXXDestructorDecl>(CXXMethod) &&
           !isa<CXXConversionDecl>(CXXMethod)) {
         auto ParameterSelectorCandidates =
-            getAPINotesParameterSelectorCandidates(*this, CXXMethod);
+            getAPINotesParameterSelectorCandidates(getASTContext(), CXXMethod);
         for (auto Reader : Readers) {
           if (auto Context = UnwindTagContext(TagContext, APINotes)) {
             std::string MethodName;
diff --git 
a/clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.apinotes 
b/clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.apinotes
new file mode 100644
index 0000000000000..051c677b5d8dc
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.apinotes
@@ -0,0 +1,136 @@
+---
+Name: WhereParametersNormalization
+Functions:
+- Name: normalizedEmpty
+  Where:
+    Parameters: []
+  SwiftName: normalizedEmpty()
+- Name: normalizedDefaults
+  Where:
+    Parameters:
+    - int
+    - double
+  SwiftName: normalizedDefaults(_:_:)
+- Name: normalizedWhitespace
+  Where:
+    Parameters:
+    - '  unsigned   int  '
+  SwiftName: normalizedWhitespace(_:)
+- Name: normalizedUnsigned
+  Where:
+    Parameters:
+    - unsigned
+  SwiftName: normalizedUnsigned(_:)
+- Name: normalizedTemplateSpacing
+  Where:
+    Parameters:
+    - 'NormalizationBox<int,double>'
+  SwiftName: normalizedTemplateSpacing(_:)
+- Name: normalizedPointerSpacing
+  Where:
+    Parameters:
+    - 'int*'
+  SwiftName: normalizedPointerSpacing(_:)
+- Name: normalizedRValueReferenceSpacing
+  Where:
+    Parameters:
+    - 'int&&'
+  SwiftName: normalizedRValueReferenceSpacing(_:)
+- Name: normalizedConstValue
+  Where:
+    Parameters:
+    - int
+  SwiftName: normalizedConstValue(_:)
+- Name: normalizedConstSpelling
+  Where:
+    Parameters:
+    - const int
+  SwiftName: normalizedConstSpelling(_:)
+- Name: normalizedConstSuffixSpelling
+  Where:
+    Parameters:
+    - int const
+  SwiftName: normalizedConstSuffixSpelling(_:)
+- Name: normalizedPointerConst
+  Where:
+    Parameters:
+    - 'int * const'
+  SwiftName: normalizedPointerConst(_:)
+- Name: normalizedPointeeConst
+  Where:
+    Parameters:
+    - 'const int *'
+  SwiftName: normalizedPointeeConst(_:)
+- Name: normalizedPointeeConstMismatch
+  Where:
+    Parameters:
+    - 'int *'
+  SwiftName: shouldNotApplyNormalizedPointeeConst(_:)
+- Name: normalizedAlias
+  Where:
+    Parameters:
+    - int
+  SwiftName: normalizedAlias(_:)
+- Name: normalizedDeepAlias
+  Where:
+    Parameters:
+    - int
+  SwiftName: normalizedDeepAlias(_:)
+- Name: normalizedDeepAliasSource
+  Where:
+    Parameters:
+    - NormalizationDeepAliasInt
+  SwiftName: normalizedDeepAliasSource(_:)
+- Name: normalizedIntermediateAliasMismatch
+  Where:
+    Parameters:
+    - NormalizationAliasAliasInt
+  SwiftName: shouldNotApplyNormalizedIntermediateAlias(_:)
+- Name: normalizedConstAlias
+  Where:
+    Parameters:
+    - int
+  SwiftName: normalizedConstAlias(_:)
+- Name: normalizedNullable
+  Where:
+    Parameters:
+    - 'char *'
+  SwiftName: normalizedNullable(_:)
+- Name: normalizedRawInt
+  Where:
+    Parameters:
+    - int
+  SwiftName: normalizedRawInt(_:)
+Tags:
+- Name: NormalizationWidget
+  Methods:
+  - Name: empty
+    Where:
+      Parameters: []
+    SwiftName: empty()
+  - Name: defaults
+    Where:
+      Parameters:
+      - int
+      - double
+    SwiftName: defaults(_:_:)
+  - Name: configure
+    Where:
+      Parameters:
+      - int
+    SwiftName: configure(_:)
+  - Name: pointerSpacing
+    Where:
+      Parameters:
+      - 'int*'
+    SwiftName: pointerSpacing(_:)
+  - Name: pointeeConstMismatch
+    Where:
+      Parameters:
+      - 'int *'
+    SwiftName: shouldNotApplyNormalizedPointeeConst(_:)
+  - Name: deepAlias
+    Where:
+      Parameters:
+      - int
+    SwiftName: deepAlias(_:)
diff --git a/clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.h 
b/clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.h
new file mode 100644
index 0000000000000..cacab4f573830
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.h
@@ -0,0 +1,50 @@
+#ifndef WHERE_PARAMETERS_NORMALIZATION_H
+#define WHERE_PARAMETERS_NORMALIZATION_H
+
+using NormalizationAliasInt = int;
+using NormalizationAliasAliasInt = NormalizationAliasInt;
+using NormalizationDeepAliasInt = NormalizationAliasAliasInt;
+using NormalizationConstAliasInt = const int;
+
+template <typename T, typename U> struct NormalizationBox {};
+
+void normalizedEmpty();
+void normalizedEmpty(int);
+
+void normalizedDefaults(int, double = 0);
+void normalizedDefaults(int);
+
+void normalizedWhitespace(unsigned int);
+void normalizedUnsigned(unsigned);
+void normalizedTemplateSpacing(NormalizationBox<int, double>);
+void normalizedPointerSpacing(int *);
+void normalizedRValueReferenceSpacing(int &&);
+void normalizedConstValue(const int);
+void normalizedConstSpelling(int);
+void normalizedConstSuffixSpelling(int);
+void normalizedPointerConst(int *const);
+void normalizedPointeeConst(const int *);
+void normalizedPointeeConstMismatch(const int *);
+void normalizedAlias(NormalizationAliasInt);
+void normalizedDeepAlias(NormalizationDeepAliasInt);
+void normalizedDeepAliasSource(NormalizationDeepAliasInt);
+void normalizedIntermediateAliasMismatch(NormalizationDeepAliasInt);
+void normalizedConstAlias(NormalizationConstAliasInt);
+void normalizedNullable(char * _Nullable);
+void normalizedRawInt(int);
+
+struct NormalizationWidget {
+  void empty();
+  void empty(int);
+
+  void defaults(int, double = 0);
+  void defaults(int);
+
+  static void configure(int);
+
+  void pointerSpacing(int *);
+  void pointeeConstMismatch(const int *);
+  void deepAlias(NormalizationDeepAliasInt);
+};
+
+#endif // WHERE_PARAMETERS_NORMALIZATION_H
diff --git a/clang/test/APINotes/Inputs/Headers/module.modulemap 
b/clang/test/APINotes/Inputs/Headers/module.modulemap
index 592d482ea7a57..644828ad0cfb6 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 WhereParametersNormalization {
+  header "WhereParametersNormalization.h"
+  export *
+}
diff --git a/clang/test/APINotes/where-parameters-diagnostics.cpp 
b/clang/test/APINotes/where-parameters-diagnostics.cpp
index 68ff7a1b113df..b553503834983 100644
--- a/clang/test/APINotes/where-parameters-diagnostics.cpp
+++ b/clang/test/APINotes/where-parameters-diagnostics.cpp
@@ -123,12 +123,14 @@ Name: WhereParametersDiagnostics
 
 void duplicateGlobal(int);
 void duplicateEmpty();
+void duplicateNormalizedGlobal(int *);
 void allowedGlobal(int);
 void allowedGlobal(double);
 
 struct DiagnosticWidget {
   void duplicateMethod(int);
   void duplicateEmpty();
+  void duplicateNormalizedMethod(int *);
   void allowed(int);
   void allowed(double);
 };
@@ -159,6 +161,17 @@ Name: WhereParametersDiagnostics
     Parameters: []
   SwiftName: duplicateEmptyB()
 # DUPLICATE: error: multiple API notes entries for global function 
'duplicateEmpty' with Where.Parameters []
+- Name: duplicateNormalizedGlobal
+  Where:
+    Parameters:
+    - int *
+  SwiftName: duplicateNormalizedGlobalA(_:)
+- Name: duplicateNormalizedGlobal
+  Where:
+    Parameters:
+    - int*
+  SwiftName: duplicateNormalizedGlobalB(_:)
+# DUPLICATE: error: multiple API notes entries for global function 
'duplicateNormalizedGlobal' with Where.Parameters [int*]
 - Name: allowedGlobal
   SwiftPrivate: true
 - Name: allowedGlobal
@@ -194,6 +207,17 @@ Name: WhereParametersDiagnostics
       Parameters: []
     SwiftName: duplicateEmptyB()
 # DUPLICATE: error: multiple API notes entries for C++ method 'duplicateEmpty' 
with Where.Parameters []
+  - Name: duplicateNormalizedMethod
+    Where:
+      Parameters:
+      - int *
+    SwiftName: duplicateNormalizedMethodA(_:)
+  - Name: duplicateNormalizedMethod
+    Where:
+      Parameters:
+      - int*
+    SwiftName: duplicateNormalizedMethodB(_:)
+# DUPLICATE: error: multiple API notes entries for C++ method 
'duplicateNormalizedMethod' with Where.Parameters [int*]
   - Name: allowed
     SwiftPrivate: true
   - Name: allowed
diff --git a/clang/test/APINotes/where-parameters-normalization.cpp 
b/clang/test/APINotes/where-parameters-normalization.cpp
new file mode 100644
index 0000000000000..3170d55503d15
--- /dev/null
+++ b/clang/test/APINotes/where-parameters-normalization.cpp
@@ -0,0 +1,116 @@
+// RUN: rm -rf %t && mkdir -p %t
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -fsyntax-only -I 
%S/Inputs/Headers %s -x c++
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedEmpty -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-EMPTY %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedDefaults -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-DEFAULTS %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedWhitespace -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-WHITESPACE %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedUnsigned -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-UNSIGNED %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedTemplateSpacing -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-TEMPLATE-SPACING %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedPointerSpacing -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-POINTER-SPACING %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedRValueReferenceSpacing -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-RVALUE-REFERENCE-SPACING %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedConstValue -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-CONST-VALUE %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedConstSpelling -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-CONST-SPELLING %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedConstSuffixSpelling -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-CONST-SUFFIX-SPELLING %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedPointerConst -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-POINTER-CONST %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedPointeeConst -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-POINTEE-CONST %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedPointeeConstMismatch -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-POINTEE-CONST-MISMATCH %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedAlias -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-ALIAS %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedDeepAlias -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-DEEP-ALIAS %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedDeepAliasSource -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-DEEP-ALIAS-SOURCE %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedIntermediateAliasMismatch -x c++ | 
FileCheck --check-prefix=CHECK-GLOBAL-INTERMEDIATE-ALIAS-MISMATCH %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedConstAlias -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-CONST-ALIAS %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedNullable -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-NULLABLE %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter normalizedRawInt -x c++ | FileCheck 
--check-prefix=CHECK-GLOBAL-RAW-INT %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter NormalizationWidget::empty -x c++ | FileCheck 
--check-prefix=CHECK-METHOD-EMPTY %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter NormalizationWidget::defaults -x c++ | FileCheck 
--check-prefix=CHECK-METHOD-DEFAULTS %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter NormalizationWidget::configure -x c++ | FileCheck 
--check-prefix=CHECK-METHOD-STATIC %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter NormalizationWidget::pointerSpacing -x c++ | 
FileCheck --check-prefix=CHECK-METHOD-POINTER-SPACING %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter NormalizationWidget::pointeeConstMismatch -x c++ | 
FileCheck --check-prefix=CHECK-METHOD-POINTEE-CONST-MISMATCH %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps 
-fmodules-cache-path=%t/ModulesCache/WhereParametersNormalization 
-fdisable-module-hash -fapinotes-modules -Wno-apinotes -I %S/Inputs/Headers %s 
-ast-dump -ast-dump-filter NormalizationWidget::deepAlias -x c++ | FileCheck 
--check-prefix=CHECK-METHOD-DEEP-ALIAS %s
+
+#include "WhereParametersNormalization.h"
+
+// CHECK-GLOBAL-EMPTY: FunctionDecl {{.+}} normalizedEmpty 'void ()'
+// CHECK-GLOBAL-EMPTY-NEXT: SwiftNameAttr {{.+}} "normalizedEmpty()"
+// CHECK-GLOBAL-EMPTY: FunctionDecl {{.+}} normalizedEmpty 'void (int)'
+// CHECK-GLOBAL-EMPTY-NOT: SwiftNameAttr
+
+// CHECK-GLOBAL-DEFAULTS: FunctionDecl {{.+}} normalizedDefaults 'void (int, 
double)'
+// CHECK-GLOBAL-DEFAULTS: SwiftNameAttr {{.+}} "normalizedDefaults(_:_:)"
+// CHECK-GLOBAL-DEFAULTS: FunctionDecl {{.+}} normalizedDefaults 'void (int)'
+// CHECK-GLOBAL-DEFAULTS-NOT: SwiftNameAttr
+
+// CHECK-GLOBAL-WHITESPACE: FunctionDecl {{.+}} normalizedWhitespace 'void 
(unsigned int)'
+// CHECK-GLOBAL-WHITESPACE: SwiftNameAttr {{.+}} "normalizedWhitespace(_:)"
+
+// CHECK-GLOBAL-UNSIGNED: FunctionDecl {{.+}} normalizedUnsigned 'void 
(unsigned int)'
+// CHECK-GLOBAL-UNSIGNED: SwiftNameAttr {{.+}} "normalizedUnsigned(_:)"
+
+// CHECK-GLOBAL-TEMPLATE-SPACING: FunctionDecl {{.+}} 
normalizedTemplateSpacing 'void (NormalizationBox<int, double>)'
+// CHECK-GLOBAL-TEMPLATE-SPACING: SwiftNameAttr {{.+}} 
"normalizedTemplateSpacing(_:)"
+
+// CHECK-GLOBAL-POINTER-SPACING: FunctionDecl {{.+}} normalizedPointerSpacing 
'void (int *)'
+// CHECK-GLOBAL-POINTER-SPACING: SwiftNameAttr {{.+}} 
"normalizedPointerSpacing(_:)"
+
+// CHECK-GLOBAL-RVALUE-REFERENCE-SPACING: FunctionDecl {{.+}} 
normalizedRValueReferenceSpacing 'void (int &&)'
+// CHECK-GLOBAL-RVALUE-REFERENCE-SPACING: SwiftNameAttr {{.+}} 
"normalizedRValueReferenceSpacing(_:)"
+
+// CHECK-GLOBAL-CONST-VALUE: FunctionDecl {{.+}} normalizedConstValue 'void 
(const int)'
+// CHECK-GLOBAL-CONST-VALUE: SwiftNameAttr {{.+}} "normalizedConstValue(_:)"
+
+// CHECK-GLOBAL-CONST-SPELLING: FunctionDecl {{.+}} normalizedConstSpelling 
'void (int)'
+// CHECK-GLOBAL-CONST-SPELLING: SwiftNameAttr {{.+}} 
"normalizedConstSpelling(_:)"
+
+// CHECK-GLOBAL-CONST-SUFFIX-SPELLING: FunctionDecl {{.+}} 
normalizedConstSuffixSpelling 'void (int)'
+// CHECK-GLOBAL-CONST-SUFFIX-SPELLING: SwiftNameAttr {{.+}} 
"normalizedConstSuffixSpelling(_:)"
+
+// CHECK-GLOBAL-POINTER-CONST: FunctionDecl {{.+}} normalizedPointerConst 
'void (int *const)'
+// CHECK-GLOBAL-POINTER-CONST: SwiftNameAttr {{.+}} 
"normalizedPointerConst(_:)"
+
+// CHECK-GLOBAL-POINTEE-CONST: FunctionDecl {{.+}} normalizedPointeeConst 
'void (const int *)'
+// CHECK-GLOBAL-POINTEE-CONST: SwiftNameAttr {{.+}} 
"normalizedPointeeConst(_:)"
+
+// CHECK-GLOBAL-POINTEE-CONST-MISMATCH: FunctionDecl {{.+}} 
normalizedPointeeConstMismatch 'void (const int *)'
+// CHECK-GLOBAL-POINTEE-CONST-MISMATCH-NOT: SwiftNameAttr
+
+// CHECK-GLOBAL-ALIAS: FunctionDecl {{.+}} normalizedAlias 'void 
(NormalizationAliasInt)'
+// CHECK-GLOBAL-ALIAS: SwiftNameAttr {{.+}} "normalizedAlias(_:)"
+
+// CHECK-GLOBAL-DEEP-ALIAS: FunctionDecl {{.+}} normalizedDeepAlias 'void 
(NormalizationDeepAliasInt)'
+// CHECK-GLOBAL-DEEP-ALIAS: SwiftNameAttr {{.+}} "normalizedDeepAlias(_:)"
+
+// CHECK-GLOBAL-DEEP-ALIAS-SOURCE: FunctionDecl {{.+}} 
normalizedDeepAliasSource 'void (NormalizationDeepAliasInt)'
+// CHECK-GLOBAL-DEEP-ALIAS-SOURCE: SwiftNameAttr {{.+}} 
"normalizedDeepAliasSource(_:)"
+
+// CHECK-GLOBAL-INTERMEDIATE-ALIAS-MISMATCH: FunctionDecl {{.+}} 
normalizedIntermediateAliasMismatch 'void (NormalizationDeepAliasInt)'
+// CHECK-GLOBAL-INTERMEDIATE-ALIAS-MISMATCH-NOT: SwiftNameAttr
+
+// CHECK-GLOBAL-CONST-ALIAS: FunctionDecl {{.+}} normalizedConstAlias 'void 
(NormalizationConstAliasInt)'
+// CHECK-GLOBAL-CONST-ALIAS: SwiftNameAttr {{.+}} "normalizedConstAlias(_:)"
+
+// CHECK-GLOBAL-NULLABLE: FunctionDecl {{.+}} normalizedNullable 'void (char * 
_Nullable)'
+// CHECK-GLOBAL-NULLABLE: SwiftNameAttr {{.+}} "normalizedNullable(_:)"
+
+// CHECK-GLOBAL-RAW-INT: FunctionDecl {{.+}} normalizedRawInt 'void (int)'
+// CHECK-GLOBAL-RAW-INT: SwiftNameAttr {{.+}} "normalizedRawInt(_:)"
+
+// CHECK-METHOD-EMPTY: CXXMethodDecl {{.+}} empty 'void ()'
+// CHECK-METHOD-EMPTY-NEXT: SwiftNameAttr {{.+}} "empty()"
+// CHECK-METHOD-EMPTY: CXXMethodDecl {{.+}} empty 'void (int)'
+// CHECK-METHOD-EMPTY-NOT: SwiftNameAttr
+
+// CHECK-METHOD-DEFAULTS: CXXMethodDecl {{.+}} defaults 'void (int, double)'
+// CHECK-METHOD-DEFAULTS: SwiftNameAttr {{.+}} "defaults(_:_:)"
+// CHECK-METHOD-DEFAULTS: CXXMethodDecl {{.+}} defaults 'void (int)'
+// CHECK-METHOD-DEFAULTS-NOT: SwiftNameAttr
+
+// CHECK-METHOD-STATIC: CXXMethodDecl {{.+}} configure 'void (int)' static
+// CHECK-METHOD-STATIC: SwiftNameAttr {{.+}} "configure(_:)"
+
+// CHECK-METHOD-POINTER-SPACING: CXXMethodDecl {{.+}} pointerSpacing 'void 
(int *)'
+// CHECK-METHOD-POINTER-SPACING: SwiftNameAttr {{.+}} "pointerSpacing(_:)"
+
+// CHECK-METHOD-POINTEE-CONST-MISMATCH: CXXMethodDecl {{.+}} 
pointeeConstMismatch 'void (const int *)'
+// CHECK-METHOD-POINTEE-CONST-MISMATCH-NOT: SwiftNameAttr
+
+// CHECK-METHOD-DEEP-ALIAS: CXXMethodDecl {{.+}} deepAlias 'void 
(NormalizationDeepAliasInt)'
+// CHECK-METHOD-DEEP-ALIAS: SwiftNameAttr {{.+}} "deepAlias(_:)"
diff --git a/clang/unittests/Sema/APINotesSelectorTest.cpp 
b/clang/unittests/Sema/APINotesSelectorTest.cpp
new file mode 100644
index 0000000000000..2208c226e3ed6
--- /dev/null
+++ b/clang/unittests/Sema/APINotesSelectorTest.cpp
@@ -0,0 +1,196 @@
+//===- unittests/Sema/APINotesSelectorTest.cpp 
----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Sema/APINotesSelector.h"
+#include "clang/APINotes/Types.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringRef.h"
+#include "gtest/gtest.h"
+#include <optional>
+#include <string>
+#include <vector>
+
+using namespace clang;
+
+namespace {
+
+using clang::ast_matchers::functionDecl;
+using clang::ast_matchers::hasName;
+using clang::ast_matchers::match;
+using clang::ast_matchers::unless;
+using clang::tooling::buildASTFromCodeWithArgs;
+
+llvm::SmallVector<std::string, 4>
+makeParameterList(llvm::ArrayRef<llvm::StringRef> Parameters) {
+  llvm::SmallVector<std::string, 4> Result;
+  for (llvm::StringRef Parameter : Parameters)
+    Result.push_back(Parameter.str());
+  return Result;
+}
+
+std::string formatSelector(llvm::ArrayRef<std::string> Parameters) {
+  return api_notes::formatAPINotesParameterSelector(Parameters);
+}
+
+void expectParameterList(llvm::ArrayRef<std::string> Actual,
+                         llvm::ArrayRef<llvm::StringRef> ExpectedRefs,
+                         llvm::StringRef Label) {
+  llvm::SmallVector<std::string, 4> Expected = makeParameterList(ExpectedRefs);
+
+  EXPECT_EQ(Actual.size(), Expected.size())
+      << Label << " selector: expected " << formatSelector(Expected) << ", got 
"
+      << formatSelector(Actual);
+  if (Actual.size() != Expected.size())
+    return;
+
+  for (unsigned I = 0, E = Expected.size(); I != E; ++I) {
+    EXPECT_EQ(Actual[I], Expected[I])
+        << Label << " selector: expected " << formatSelector(Expected)
+        << ", got " << formatSelector(Actual);
+  }
+}
+
+const FunctionDecl *findTarget(ASTUnit &AST) {
+  auto Results =
+      match(functionDecl(hasName("target"), unless(ast_matchers::isImplicit()))
+                .bind("fn"),
+            AST.getASTContext());
+  EXPECT_EQ(Results.size(), 1u);
+  if (Results.size() != 1u)
+    return nullptr;
+  return Results[0].getNodeAs<FunctionDecl>("fn");
+}
+
+void expectSelectorsImpl(
+    llvm::StringRef Code, llvm::ArrayRef<llvm::StringRef> Source,
+    std::optional<llvm::ArrayRef<llvm::StringRef>> Desugared,
+    bool IsObjectiveCXX) {
+  std::vector<std::string> Args = {"-std=c++20"};
+  std::string FileName = IsObjectiveCXX ? "input.mm" : "input.cpp";
+
+  std::unique_ptr<ASTUnit> AST = buildASTFromCodeWithArgs(Code, Args, 
FileName);
+  ASSERT_TRUE(AST);
+
+  const FunctionDecl *Target = findTarget(*AST);
+  ASSERT_NE(Target, nullptr);
+
+  std::optional<APINotesParameterSelectorCandidates> Candidates =
+      getAPINotesParameterSelectorCandidates(AST->getASTContext(), Target);
+  ASSERT_TRUE(Candidates);
+
+  expectParameterList(Candidates->Source, Source, "source");
+
+  EXPECT_EQ(Candidates->Desugared.has_value(), Desugared.has_value());
+  if (Desugared && Candidates->Desugared)
+    expectParameterList(*Candidates->Desugared, *Desugared, "desugared");
+}
+
+void expectSelectors(llvm::StringRef Code,
+                     llvm::ArrayRef<llvm::StringRef> Source,
+                     bool IsObjectiveCXX = false) {
+  expectSelectorsImpl(Code, Source, std::nullopt, IsObjectiveCXX);
+}
+
+void expectSelectorsWithDesugared(llvm::StringRef Code,
+                                  llvm::ArrayRef<llvm::StringRef> Source,
+                                  llvm::ArrayRef<llvm::StringRef> Desugared) {
+  expectSelectorsImpl(Code, Source, Desugared, /*IsObjectiveCXX=*/false);
+}
+
+TEST(APINotesSelectorTest, ExtractsZeroParameterSelector) {
+  expectSelectors("void target();", {});
+}
+
+TEST(APINotesSelectorTest, ExtractsMultipleParametersAndIgnoresDefaults) {
+  expectSelectors("void target(int, double = 0);", {"int", "double"});
+}
+
+TEST(APINotesSelectorTest, DropsTopLevelConstFromValueParameter) {
+  expectSelectors("void target(const int);", {"int"});
+}
+
+TEST(APINotesSelectorTest, NormalizesPointerAndReferenceSpacing) {
+  expectSelectors("void target(int *, int &, int &&);",
+                  {"int*", "int&", "int&&"});
+}
+
+TEST(APINotesSelectorTest, DropsTopLevelConstFromPointerValueParameter) {
+  expectSelectors("void target(int *const);", {"int*"});
+}
+
+TEST(APINotesSelectorTest, PreservesPointeeConstOnPointerParameter) {
+  expectSelectors("void target(const int *);", {"const int*"});
+}
+
+TEST(APINotesSelectorTest, PreservesNestedPointerConst) {
+  expectSelectors("void target(const char *const *);", {"const char*const*"});
+}
+
+TEST(APINotesSelectorTest, PreservesMemberFunctionPointerConst) {
+  expectSelectors(R"cpp(
+    struct Foo {};
+    void target(void (Foo::*)() const);
+  )cpp",
+                  {"void (Foo::*)() const"});
+}
+
+TEST(APINotesSelectorTest, NormalizesParameterSelectorSpellingsDirectly) {
+  EXPECT_EQ(api_notes::normalizeAPINotesParameterSelector("  unsigned   int  
"),
+            "unsigned int");
+  EXPECT_EQ(api_notes::normalizeAPINotesParameterSelector("unsigned"),
+            "unsigned int");
+  EXPECT_EQ(api_notes::normalizeAPINotesParameterSelector("const unsigned"),
+            "unsigned int");
+  EXPECT_EQ(api_notes::normalizeAPINotesParameterSelector("unsigned const"),
+            "unsigned int");
+  EXPECT_EQ(api_notes::normalizeAPINotesParameterSelector("int * const"),
+            "int*");
+  EXPECT_EQ(
+      api_notes::normalizeAPINotesParameterSelector("void (Foo::*)() const"),
+      "void (Foo::*)() const");
+}
+
+TEST(APINotesSelectorTest, NormalizesTemplateSpacing) {
+  expectSelectors(R"cpp(
+    template <typename T, typename U> struct Box {};
+    void target(Box<int, double>);
+  )cpp",
+                  {"Box<int,double>"});
+}
+
+TEST(APINotesSelectorTest,
+     PreservesAliasAsSourceSelectorWithDesugaredFallback) {
+  expectSelectorsWithDesugared(R"cpp(
+    using AliasInt = int;
+    void target(AliasInt);
+  )cpp",
+                               {"AliasInt"}, {"int"});
+}
+
+TEST(APINotesSelectorTest,
+     PreservesDeepAliasAsSourceSelectorWithDesugaredFallback) {
+  expectSelectorsWithDesugared(R"cpp(
+    using AliasInt = int;
+    using DeepAliasInt = AliasInt;
+    void target(DeepAliasInt);
+  )cpp",
+                               {"DeepAliasInt"}, {"int"});
+}
+
+TEST(APINotesSelectorTest, StripsParameterNullability) {
+  expectSelectors("void target(char * _Nonnull);", {"char*"},
+                  /*IsObjectiveCXX=*/true);
+}
+
+} // namespace
diff --git a/clang/unittests/Sema/CMakeLists.txt 
b/clang/unittests/Sema/CMakeLists.txt
index 188f6135a60ac..e11f04e35f296 100644
--- a/clang/unittests/Sema/CMakeLists.txt
+++ b/clang/unittests/Sema/CMakeLists.txt
@@ -3,6 +3,7 @@
 # large statically linked binary, but separating it out is
 # the right tradeoff today.
 add_distinct_clang_unittest(SemaTests
+  APINotesSelectorTest.cpp
   ExternalSemaSourceTest.cpp
   CodeCompleteTest.cpp
   HeuristicResolverTest.cpp

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

Reply via email to