https://github.com/StoeckOverflow created https://github.com/llvm/llvm-project/pull/213043
This PR normalizes exact `Where.Parameters` selector spellings and adds focused unit coverage for declaration-side selector extraction. It teaches API notes conversion to normalize selector strings before storing, comparing, and diagnosing them, so supported equivalent spellings such as `int *` and `int*`, `Box<int, double>` and `Box<int,double>`, or top-level by-value / pointer `const` variants compare consistently with Sema-created declaration selectors. The normalizer is intentionally lexical. It does not parse selector strings as Clang types and does not broaden `Where.Parameters` into canonical type matching. It moves the declaration-side selector code from `SemaAPINotes.cpp` into `clang/Sema/APINotesSelector.h` and `clang/lib/Sema/APINotesSelector.cpp`. The new helper, `getAPINotesParameterSelectorCandidates`, builds the source-spelling selector and optional desugared fallback selector for a `FunctionDecl`. `SemaAPINotes.cpp` now calls that helper for exact `Where.Parameters` matching and unmatched-selector diagnostics, while keeping API notes lookup, note application, and diagnostic emission there. The new `APINotesSelectorTest` unit test builds small Clang ASTs from declaration snippets and checks the selector strings returned by `getAPINotesParameterSelectorCandidates`. Tests cover whitespace normalization, pointer/reference punctuation spacing, template punctuation spacing, supported top-level `const` normalization, normalized duplicate-selector diagnostics, zero-parameter selectors, multi-parameter/defaulted declarations, alias and deep-alias fallback, and Objective-C++ nullability stripping. Existing Sema/API-notes coverage continues to exercise global-function and C++-method matching paths. Reviewers: @Xazax-hun @j-hui @egorzhdan >From a8d94426ecc21e5b8cb7c8f5edcbddbb240fcb71 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 | 44 +++++ clang/lib/APINotes/APINotesTypes.cpp | 117 ++++++++++++ clang/lib/APINotes/APINotesYAMLCompiler.cpp | 73 ++++++-- clang/lib/Sema/APINotesSelector.cpp | 85 +++++++++ clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/SemaAPINotes.cpp | 86 +-------- .../WhereParametersNormalization.apinotes | 131 ++++++++++++++ .../Headers/WhereParametersNormalization.h | 49 ++++++ .../APINotes/Inputs/Headers/module.modulemap | 5 + .../APINotes/where-parameters-diagnostics.cpp | 24 +++ .../where-parameters-normalization.cpp | 112 ++++++++++++ clang/unittests/Sema/APINotesSelectorTest.cpp | 166 ++++++++++++++++++ clang/unittests/Sema/CMakeLists.txt | 1 + 14 files changed, 798 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..8a95437354a4e --- /dev/null +++ b/clang/include/clang/Sema/APINotesSelector.h @@ -0,0 +1,44 @@ +//===--- 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; + +struct APINotesParameterSelector { + llvm::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; +}; + +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..35ea18a0ebfda 100644 --- a/clang/lib/APINotes/APINotesTypes.cpp +++ b/clang/lib/APINotes/APINotesTypes.cpp @@ -7,11 +7,128 @@ //===----------------------------------------------------------------------===// #include "clang/APINotes/Types.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/raw_ostream.h" +#include <tuple> 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 void +collapseParameterSelectorWhitespace(llvm::StringRef Spelling, + llvm::SmallVectorImpl<char> &Collapsed) { + Collapsed.clear(); + while (!Spelling.empty()) { + llvm::StringRef Token; + std::tie(Token, Spelling) = llvm::getToken(Spelling); + if (Token.empty()) + break; + + if (!Collapsed.empty()) + Collapsed.push_back(' '); + Collapsed.append(Token.begin(), Token.end()); + } +} + +static llvm::StringRef stripTopLevelValueConst(llvm::StringRef Spelling) { + if (!hasTopLevelIndirectParameterSelectorSpelling(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")) + return Spelling.str(); + + std::string WithoutTopLevelConst = Spelling.str(); + WithoutTopLevelConst += '*'; + return WithoutTopLevelConst; +} + +std::string normalizeAPINotesParameterSelector(llvm::StringRef Spelling) { + llvm::SmallString<32> Collapsed; + collapseParameterSelectorWhitespace(Spelling, Collapsed); + + llvm::StringRef WithoutTopLevelValueConst = + 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..e9b6cc847952c 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" @@ -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)) + NormalizedWhereParameterRefs)) .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)) + NormalizedWhereParameterRefs)) .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..c45c0f7d28927 --- /dev/null +++ b/clang/lib/Sema/APINotesSelector.cpp @@ -0,0 +1,85 @@ +//===--- 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 { + +void stripAPINotesParameterNullability(QualType &ParamType) { + while (true) { + if (!AttributedType::stripOuterNullability(ParamType)) + return; + } +} + +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(); + stripAPINotesParameterNullability(ParamType); + + 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.Parameters.reserve(FPT->getNumParams()); + Desugared.Parameters.reserve(FPT->getNumParams()); + + const PrintingPolicy Policy = + getAPINotesParameterSelectorPrintingPolicy(Context); + for (QualType ParamType : FPT->param_types()) { + Candidates.Source.Parameters.push_back( + getAPINotesParameterSelectorSpelling(ParamType, Context, Policy, + /*Desugar=*/false)); + Desugared.Parameters.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 c5560605124c8..0813e5a6603b2 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> @@ -994,87 +995,6 @@ UnwindTagContext(TagDecl *DC, api_notes::APINotesManager &APINotes) { return std::nullopt; } -static void stripAPINotesParameterNullability(QualType &ParamType) { - while (true) { - if (!AttributedType::stripOuterNullability(ParamType)) - return; - } -} - -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(); - stripAPINotesParameterNullability(ParamType); - - 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) { @@ -1169,7 +1089,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 = @@ -1382,7 +1302,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..6c9ed80e4a431 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.apinotes @@ -0,0 +1,131 @@ +--- +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: 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..8abb575c72985 --- /dev/null +++ b/clang/test/APINotes/Inputs/Headers/WhereParametersNormalization.h @@ -0,0 +1,49 @@ +#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 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..e3fb5f15a6e29 --- /dev/null +++ b/clang/test/APINotes/where-parameters-normalization.cpp @@ -0,0 +1,112 @@ +// 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 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-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..af08ad2754a8a --- /dev/null +++ b/clang/unittests/Sema/APINotesSelectorTest.cpp @@ -0,0 +1,166 @@ +//===- 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 <initializer_list> +#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(std::initializer_list<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, + std::initializer_list<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 expectSelectors(llvm::StringRef Code, + std::initializer_list<llvm::StringRef> Source, + std::initializer_list<llvm::StringRef> Desugared = {}, + bool ExpectDesugared = false, + bool IsObjectiveCXX = false) { + std::vector<std::string> Args; + std::string FileName; + if (IsObjectiveCXX) { + Args = {"-x", "objective-c++", "-std=c++20"}; + FileName = "input.mm"; + } else { + Args = {"-std=c++20"}; + FileName = "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.Parameters, Source, "source"); + + EXPECT_EQ(Candidates->Desugared.has_value(), ExpectDesugared); + if (ExpectDesugared) + expectParameterList(Candidates->Desugared->Parameters, Desugared, + "desugared"); +} + +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, NormalizesTemplateSpacing) { + expectSelectors(R"cpp( + template <typename T, typename U> struct Box {}; + void target(Box<int, double>); + )cpp", + {"Box<int,double>"}); +} + +TEST(APINotesSelectorTest, + PreservesAliasAsSourceSelectorWithDesugaredFallback) { + expectSelectors(R"cpp( + using AliasInt = int; + void target(AliasInt); + )cpp", + {"AliasInt"}, {"int"}, /*ExpectDesugared=*/true); +} + +TEST(APINotesSelectorTest, + PreservesDeepAliasAsSourceSelectorWithDesugaredFallback) { + expectSelectors(R"cpp( + using AliasInt = int; + using DeepAliasInt = AliasInt; + void target(DeepAliasInt); + )cpp", + {"DeepAliasInt"}, {"int"}, /*ExpectDesugared=*/true); +} + +TEST(APINotesSelectorTest, StripsParameterNullability) { + expectSelectors("void target(char * _Nonnull);", {"char*"}, + /*Desugared=*/{}, /*ExpectDesugared=*/false, + /*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
