Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ protected override TypeProvider[] BuildSerializationProviders()
protected internal override IReadOnlyList<EnumTypeMember>? BuildEnumValuesForBackCompatibility(IReadOnlyList<EnumTypeMember> currentValues)
{
var lastContractProperties = LastContractView?.Properties
.Where(p => MethodSignatureHelper.IsPublicApi(p.Modifiers));
.Where(p => MethodSignatureHelper.IsPublicApi(p.Modifiers, p.Attributes));

if (lastContractProperties == null || !lastContractProperties.Any())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,17 @@ private static bool HasNoUnsupportedLastContractMembers(ModelProvider candidate)
return lastContract.Implements.All(previous => currentInterfaces.Any(current =>
ModelBaseMemberCompatibility.AreTypesCompatible(previous, current))) &&
lastContract.Constructors
.Where(constructor => MethodSignatureHelper.IsPublicApi(constructor.Signature.Modifiers))
.Where(constructor => MethodSignatureHelper.IsPublicApi(constructor.Signature))
.All(constructor => SystemObjectModelProvider.HasSupportedConstructorParameters(constructor.Signature.Parameters) &&
constructor.Signature.Parameters.All(parameter => candidate.Properties.Any(property =>
property.AsParameter.Name == parameter.Name &&
(ModelBaseMemberCompatibility.AreTypesCompatible(parameter.Type, property.Type) ||
ModelBaseMemberCompatibility.AreTypesCompatible(parameter.Type, property.Type.InputType))))) &&
lastContract.Methods
.Where(method => MethodSignatureHelper.IsPublicApi(method.Signature.Modifiers))
.Where(method => MethodSignatureHelper.IsPublicApi(method.Signature))
.All(previous => currentGeneratedMethods.Any(current =>
AreGeneratedMethodSignaturesEquivalent(previous.Signature, current.Signature))) &&
!lastContract.Fields.Any(field =>
field.Modifiers.HasFlag(FieldModifiers.Public) ||
field.Modifiers.HasFlag(FieldModifiers.Protected));
!lastContract.Fields.Any(field => MethodSignatureHelper.IsPublicApi(field.Modifiers, field.Attributes));
}

private static bool AreGeneratedMethodSignaturesEquivalent(
Expand All @@ -128,8 +126,8 @@ private bool HasCompatibleLastContractProperties(ModelProvider candidate)
return true;
}

var currentProperties = GetUniquePublicProperties(candidate.Properties);
var previousProperties = GetUniquePublicProperties(lastContract.Properties);
var currentProperties = GetUniquePublicProperties(candidate.Properties, isLastContract: false);
var previousProperties = GetUniquePublicProperties(lastContract.Properties, isLastContract: true);
if (currentProperties is null || previousProperties is null)
{
return false;
Expand All @@ -143,10 +141,14 @@ private bool HasCompatibleLastContractProperties(ModelProvider candidate)
!LastContractDerivedConstructorHasRequiredParameter(property)));
}

private static Dictionary<string, PropertyProvider>? GetUniquePublicProperties(IReadOnlyList<PropertyProvider> properties)
private static Dictionary<string, PropertyProvider>? GetUniquePublicProperties(
Comment thread
jorgerangel-msft marked this conversation as resolved.
IReadOnlyList<PropertyProvider> properties,
bool isLastContract)
{
var result = new Dictionary<string, PropertyProvider>(StringComparer.Ordinal);
foreach (var property in properties.Where(property => MethodSignatureHelper.IsPublicApi(property.Modifiers)))
foreach (var property in properties.Where(property => isLastContract
? MethodSignatureHelper.IsPublicApi(property.Modifiers, property.Attributes)
: MethodSignatureHelper.IsPublicApi(property.Modifiers)))
{
// Overloaded indexers can share a symbol name. They are unsupported contracts, not
// malformed input that should abort generation with a duplicate-key exception.
Expand All @@ -164,7 +166,7 @@ private static bool IsRequiredInitializationProperty(PropertyProvider property)

private bool LastContractDerivedConstructorHasRequiredParameter(PropertyProvider property)
=> _model.LastContractView?.Constructors.Any(constructor =>
MethodSignatureHelper.IsPublicApi(constructor.Signature.Modifiers) &&
MethodSignatureHelper.IsPublicApi(constructor.Signature) &&
constructor.Signature.Parameters.Any(parameter =>
parameter.Name == property.AsParameter.Name &&
parameter.Type.Equals(property.Type, ignoreNullable: true) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ protected internal sealed override IReadOnlyList<MethodProvider> BuildMethodsFor

foreach (var previousMethod in LastContractView.Methods)
{
if (!MethodSignatureHelper.IsPublicApi(previousMethod.Signature.Modifiers))
if (!MethodSignatureHelper.IsPublicApi(previousMethod.Signature))
{
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ _inputModel.BaseModel is not null

private IDictionary<string, CSharpType> LastContractPropertiesMap
=> _lastContractPropertiesMap ??= LastContractView?.Properties
.Where(p => MethodSignatureHelper.IsPublicApi(p.Modifiers))
.Where(p => MethodSignatureHelper.IsPublicApi(p.Modifiers, p.Attributes))
.ToDictionary(p => p.Name, p => p.Type) ?? [];

private IDictionary<string, CSharpType>? _lastContractPropertiesMap;
Expand Down Expand Up @@ -1111,7 +1111,7 @@ protected internal override IReadOnlyList<ConstructorProvider> BuildConstructors

foreach (var previousConstructor in restorablePreviousConstructors)
{
if (!MethodSignatureHelper.IsPublicApi(previousConstructor.Signature.Modifiers))
if (!MethodSignatureHelper.IsPublicApi(previousConstructor.Signature))
{
continue;
}
Expand Down Expand Up @@ -1183,7 +1183,7 @@ private void RestorePreviousConstructorParameterNames(
const MethodSignatureModifiers privateProtected = MethodSignatureModifiers.Private | MethodSignatureModifiers.Protected;
foreach (var previousConstructor in previousConstructors)
{
if (!MethodSignatureHelper.IsPublicApi(previousConstructor.Signature.Modifiers))
if (!MethodSignatureHelper.IsPublicApi(previousConstructor.Signature))
{
continue;
}
Expand Down Expand Up @@ -2056,7 +2056,7 @@ private bool ShouldUseObjectAdditionalProperties()

// Check if the property exists in the last contract by name
var lastContractProperty = LastContractView.Properties.FirstOrDefault(p =>
MethodSignatureHelper.IsPublicApi(p.Modifiers) &&
MethodSignatureHelper.IsPublicApi(p.Modifiers, p.Attributes) &&
p.Name == AdditionalPropertiesHelper.DefaultAdditionalPropertiesPropertyName);

if (lastContractProperty == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public NamedTypeSymbolProvider(INamedTypeSymbol namedTypeSymbol, Compilation com
{
_namedTypeSymbol = namedTypeSymbol;
_compilation = compilation;
IsExperimental = HasExperimentalAttribute(namedTypeSymbol);
}

internal string MetadataName
Expand All @@ -51,6 +52,11 @@ internal string MetadataName

internal string MetadataSimpleName => _metadataSimpleName ??= _namedTypeSymbol.Name;

internal bool IsExperimental { get; }

private static bool HasExperimentalAttribute(ISymbol symbol)
=> symbol.GetAttributes().Any(a => a.AttributeClass?.ToDisplayString() == typeof(ExperimentalAttribute).FullName);

// Nested declarations are not materialized as providers for this symbol-backed view.
internal bool HasPublicApiNestedTypes => _namedTypeSymbol.GetTypeMembers().Any(type =>
type.DeclaredAccessibility is Accessibility.Public or Accessibility.Protected or
Expand Down Expand Up @@ -329,6 +335,9 @@ protected internal override ConstructorProvider[] BuildConstructors()
GetSymbolXmlDoc(constructorSymbol, "summary"),
GetAccessModifier(constructorSymbol.DeclaredAccessibility),
[.. constructorSymbol.Parameters.Select(p => ConvertToParameterProvider(constructorSymbol, p))],
attributes: [.. constructorSymbol.GetAttributes()
Comment thread
jorgerangel-msft marked this conversation as resolved.
.Where(a => a.AttributeClass?.ToDisplayString() == typeof(ExperimentalAttribute).FullName)
.Select(a => new AttributeStatement(a))],
initializer: initializer);
constructors.Add(new ConstructorProvider(signature, MethodBodyStatement.Empty, this));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ private PropertyProvider(InputProperty inputProperty, CSharpType propertyType, T
var canonicalName = identifierName.NormalizeCSharpAcronyms(isDateTime);
var enclosingTypeName = enclosingType.Name;
var lastContractProperties = enclosingType.LastContractView?.Properties
.Where(p => MethodSignatureHelper.IsPublicApi(p.Modifiers))
.Where(p => MethodSignatureHelper.IsPublicApi(p.Modifiers, p.Attributes))
.ToList();

// An exact input-identifier match is authoritative: it is the name this property would have had
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ private bool HasCallableInitializationConstructor()
}

foreach (var constructor in constructors.Where(constructor =>
MethodSignatureHelper.IsPublicApi(constructor.Signature.Modifiers) &&
MethodSignatureHelper.IsPublicApi(constructor.Signature) &&
HasSupportedConstructorParameters(constructor.Signature.Parameters)))
{
var matches = constructor.Signature.Parameters
Expand Down Expand Up @@ -269,7 +269,7 @@ private bool TryGetLastContractConstructor(
{
var properties = Properties;
foreach (var candidate in _lastContractType!.Constructors
.Where(constructor => MethodSignatureHelper.IsPublicApi(constructor.Signature.Modifiers) &&
.Where(constructor => MethodSignatureHelper.IsPublicApi(constructor.Signature) &&
HasSupportedConstructorParameters(constructor.Signature.Parameters))
.OrderByDescending(constructor => constructor.Signature.Parameters.Count))
{
Expand Down Expand Up @@ -316,7 +316,7 @@ protected internal override PropertyProvider[] BuildProperties()
for (var provider = _lastContractType; provider is not null; provider = provider.BaseTypeProvider)
{
foreach (var property in provider.Properties.Where(property =>
MethodSignatureHelper.IsPublicApi(property.Modifiers) && seenPropertyNames.Add(property.Name)))
MethodSignatureHelper.IsPublicApi(property.Modifiers, property.Attributes) && seenPropertyNames.Add(property.Name)))
{
if (ApplyCurrentInputMetadata(property) is { } projectedProperty)
{
Expand All @@ -342,7 +342,7 @@ internal bool HasCompatibleLastContractProperties()
for (var provider = _lastContractType; provider is not null; provider = provider.BaseTypeProvider)
{
if (provider.Properties
.Where(property => MethodSignatureHelper.IsPublicApi(property.Modifiers))
.Where(property => MethodSignatureHelper.IsPublicApi(property.Modifiers, property.Attributes))
.Any(property => FindEffectiveFrameworkProperty(property.Name) is not { } candidate ||
!IsCompatibleProperty(property, candidate)))
{
Expand Down Expand Up @@ -391,7 +391,7 @@ internal bool HasCompatibleLastContractNonPropertyMembers()
}

foreach (var method in provider.Methods.Where(method =>
MethodSignatureHelper.IsPublicApi(method.Signature.Modifiers)))
MethodSignatureHelper.IsPublicApi(method.Signature)))
{
var signature = method.Signature;
if (overriddenMethods.Any(overrideSignature =>
Expand All @@ -407,7 +407,7 @@ internal bool HasCompatibleLastContractNonPropertyMembers()
overriddenMethods.AddRange(provider.Methods.Select(method => method.Signature)
.Where(signature => signature.Modifiers.HasFlag(MethodSignatureModifiers.Override)));
if (provider.Fields
.Where(field => IsPublicApiField(field.Modifiers))
.Where(field => IsPublicApiField(field))
.Any(field => FindEffectiveFrameworkMember(field.Name) is not FieldInfo candidate ||
!IsCompatibleField(field, candidate)))
{
Expand All @@ -425,16 +425,16 @@ internal IEnumerable<string> GetPublicApiMemberNames()
for (var provider = _lastContractType; provider is not null; provider = provider.BaseTypeProvider)
{
foreach (var property in provider.Properties.Where(property =>
MethodSignatureHelper.IsPublicApi(property.Modifiers)))
MethodSignatureHelper.IsPublicApi(property.Modifiers, property.Attributes)))
{
yield return property.Name;
}
foreach (var method in provider.Methods.Where(method =>
MethodSignatureHelper.IsPublicApi(method.Signature.Modifiers)))
MethodSignatureHelper.IsPublicApi(method.Signature)))
{
yield return GetPublicApiMemberName(method.Signature.Name);
}
foreach (var field in provider.Fields.Where(field => IsPublicApiField(field.Modifiers)))
foreach (var field in provider.Fields.Where(IsPublicApiField))
{
yield return field.Name;
}
Expand Down Expand Up @@ -669,9 +669,8 @@ private static bool RequiresOverridableMethod(MethodSignatureModifiers modifiers
modifiers.HasFlag(MethodSignatureModifiers.Abstract) ||
modifiers.HasFlag(MethodSignatureModifiers.Override));

private static bool IsPublicApiField(FieldModifiers modifiers)
=> modifiers.HasFlag(FieldModifiers.Public) ||
modifiers.HasFlag(FieldModifiers.Protected) && !modifiers.HasFlag(FieldModifiers.Private);
private static bool IsPublicApiField(FieldProvider field)
=> MethodSignatureHelper.IsPublicApi(field.Modifiers, field.Attributes);

private static bool IsCompatibleField(FieldProvider previous, FieldInfo current)
// Historical constant expressions are not necessarily available (e.g. metadata symbols).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1218,7 +1218,7 @@ protected internal virtual IReadOnlyList<MethodProvider> BuildMethodsForBackComp
foreach (var previousMethod in previousMethods)
{
if (currentMethodSignatures.ContainsKey(previousMethod.Signature)
|| !MethodSignatureHelper.IsPublicApi(previousMethod.Signature.Modifiers)
|| !MethodSignatureHelper.IsPublicApi(previousMethod.Signature)
|| BackCompatHelper.IsMethodRemovalAcceptedInBaseline(this, previousMethod.Signature))
{
continue;
Expand Down Expand Up @@ -1283,7 +1283,8 @@ protected internal virtual IReadOnlyList<ConstructorProvider> BuildConstructorsF
// Check if the last contract had a public constructor with matching parameters
foreach (var previousConstructor in LastContractView.Constructors)
{
if (!previousConstructor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public))
if (!previousConstructor.Signature.Modifiers.HasFlag(MethodSignatureModifiers.Public) ||
!MethodSignatureHelper.IsPublicApi(previousConstructor.Signature))
{
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,28 @@
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Snippets;
using Microsoft.TypeSpec.Generator.Statements;
using Microsoft.TypeSpec.Generator.Utilities;

namespace Microsoft.TypeSpec.Generator
{
internal class MethodSignatureHelper
{
internal static bool IsPublicApi(MethodSignatureModifiers modifiers)
internal static bool IsPublicApi(MethodSignatureBase signature)
=> IsPublicApi(signature.Modifiers, signature.Attributes);

internal static bool IsPublicApi(
MethodSignatureModifiers modifiers,
IReadOnlyList<AttributeStatement>? attributes = null)
=> (modifiers.HasFlag(MethodSignatureModifiers.Public) || modifiers.HasFlag(MethodSignatureModifiers.Protected))
&& !modifiers.HasFlag(MethodSignatureModifiers.Private);
&& !modifiers.HasFlag(MethodSignatureModifiers.Private)
&& attributes?.Any(ExperimentalApiHelpers.IsExperimentalAttribute) != true;

internal static bool IsPublicApi(
Comment thread
jorgerangel-msft marked this conversation as resolved.
FieldModifiers modifiers,
IReadOnlyList<AttributeStatement>? attributes = null)
=> (modifiers.HasFlag(FieldModifiers.Public) || modifiers.HasFlag(FieldModifiers.Protected))
&& !modifiers.HasFlag(FieldModifiers.Private)
&& attributes?.Any(ExperimentalApiHelpers.IsExperimentalAttribute) != true;

internal static bool ContainsSameParameters(MethodSignature method1, MethodSignature method2)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis;
using Microsoft.TypeSpec.Generator.Providers;
using Microsoft.TypeSpec.Generator.Utilities;

namespace Microsoft.TypeSpec.Generator.SourceInput
{
Expand Down Expand Up @@ -69,7 +70,8 @@ private IReadOnlyDictionary<string, INamedTypeSymbol> PopulateNameMap()

public TypeProvider? FindForTypeInLastContract(string ns, string name, string? declaringTypeName = null)
Comment thread
jorgerangel-msft marked this conversation as resolved.
{
return FindTypeInCompilation(LastContract, ns, name, true, declaringTypeName, includeInternal: false);
var type = FindTypeInCompilation(LastContract, ns, name, true, declaringTypeName, includeInternal: false);
return type is NamedTypeSymbolProvider { IsExperimental: true } ? null : type;
}

private IReadOnlyList<TypeProvider> PopulateCustomizationTypeProviders()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ internal bool IsLastContractModelBasePropertyCompatible(
CSharpType mappedBase,
InputModelProperty currentProperty,
PropertyProvider lastContractProperty)
=> !lastContractProperty.Modifiers.HasFlag(MethodSignatureModifiers.Static) &&
=> MethodSignatureHelper.IsPublicApi(lastContractProperty.Modifiers, lastContractProperty.Attributes) &&
!lastContractProperty.Modifiers.HasFlag(MethodSignatureModifiers.Static) &&
IsLastContractModelBasePropertyCompatibleCore(mappedBase, currentProperty, lastContractProperty);

/// <summary>
Expand Down
Loading
Loading