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