[clang] [APINotes] Normalize Where.Parameters selector spellings (PR #213043)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 13 06:30:39 PDT 2026
https://github.com/StoeckOverflow updated https://github.com/llvm/llvm-project/pull/213043
>From d55d8f7edc3fb34ad1eae87ac7f2b2cf55b703d1 Mon Sep 17 00:00:00 2001
From: stoeckoverflow <dominic-st at gmx.de>
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
More information about the cfe-commits
mailing list