Skip to content

Commit

Permalink
EnC: Implement support for editing types with primary constructors (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tmat authored Jun 26, 2023
1 parent b50a84b commit 6af144c
Show file tree
Hide file tree
Showing 32 changed files with 4,969 additions and 2,897 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,24 @@ internal sealed partial class EditAndContinueTest : IDisposable
private readonly CSharpCompilationOptions? _options;
private readonly CSharpParseOptions? _parseOptions;
private readonly TargetFramework _targetFramework;
private readonly Verification _verification;

private readonly List<IDisposable> _disposables = new();
private readonly List<GenerationInfo> _generations = new();
private readonly List<SourceWithMarkedNodes> _sources = new();

private bool _hasVerified;

public EditAndContinueTest(CSharpCompilationOptions? options = null, CSharpParseOptions? parseOptions = null, TargetFramework targetFramework = TargetFramework.Standard)
public EditAndContinueTest(
CSharpCompilationOptions? options = null,
CSharpParseOptions? parseOptions = null,
TargetFramework targetFramework = TargetFramework.Standard,
Verification? verification = null)
{
_options = options ?? TestOptions.DebugDll;
_targetFramework = targetFramework;
_parseOptions = parseOptions ?? TestOptions.Regular.WithNoRefSafetyRulesAttribute();
_verification = verification ?? Verification.Passes;
}

internal EditAndContinueTest AddBaseline(string source, Action<GenerationVerifier> validator)
Expand All @@ -60,7 +66,7 @@ internal EditAndContinueTest AddBaseline(SourceWithMarkedNodes source, Action<Ge
args: null,
manifestResources: null,
emitOptions: EmitOptions.Default,
peVerify: Verification.Passes,
peVerify: _verification,
expectedSignatures: null);

var md = ModuleMetadata.CreateFromImage(verifier.EmittedAssemblyData);
Expand Down Expand Up @@ -105,6 +111,8 @@ internal EditAndContinueTest AddGeneration(SourceWithMarkedNodes source, Semanti
throw new Exception($"Exception during generation #{_generations.Count}. See inner stack trace for details.", ex);
}

Assert.Empty(diff.EmitResult.Diagnostics);

// EncVariableSlotAllocator attempted to map from current source to the previous one,
// but the mapping failed for these nodes. Mark the nodes in sources with node markers <N:x>...</N:x>.
Assert.Empty(unmappedNodes);
Expand Down Expand Up @@ -155,13 +163,18 @@ private ImmutableArray<SemanticEdit> GetSemanticEdits(

return ImmutableArray.CreateRange(edits.Select(e =>
{
var oldSymbol = e.SymbolProvider(oldCompilation);
var oldSymbol = e.Kind is SemanticEditKind.Update or SemanticEditKind.Delete ? e.SymbolProvider(oldCompilation) : null;

// for delete the new symbol is the new containing type
var newSymbol = e.NewSymbolProvider(newCompilation);

Func<SyntaxNode, SyntaxNode?>? syntaxMap;
if (e.PreserveLocalVariables)
{
Assert.Equal(SemanticEditKind.Update, e.Kind);
Debug.Assert(oldSymbol != null);
Debug.Assert(newSymbol != null);

syntaxMap = syntaxMapFromMarkers ?? EditAndContinueTestBase.GetEquivalentNodesMap(
((Public.MethodSymbol)newSymbol).GetSymbol<MethodSymbol>(), ((Public.MethodSymbol)oldSymbol).GetSymbol<MethodSymbol>());
}
Expand Down

Large diffs are not rendered by default.

51 changes: 44 additions & 7 deletions src/Compilers/Test/Utilities/CSharp/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,25 +163,62 @@ private static ImmutableArray<string> SplitMemberName(string name)
}

public static Symbol GetMember(this CSharpCompilation compilation, string qualifiedName)
=> compilation.GlobalNamespace.GetMember(qualifiedName);

public static ISymbol GetMember(this Compilation compilation, string qualifiedName)
=> compilation.GlobalNamespace.GetMember(qualifiedName);

public static T GetMember<T>(this CSharpCompilation compilation, string qualifiedName) where T : Symbol
=> (T)compilation.GlobalNamespace.GetMember(qualifiedName);

public static T GetMember<T>(this Compilation compilation, string qualifiedName) where T : ISymbol
=> (T)compilation.GlobalNamespace.GetMember(qualifiedName);

public static IMethodSymbol GetCopyConstructor(this Compilation compilation, string qualifiedTypeName)
{
var type = compilation.GetMember<INamedTypeSymbol>(qualifiedTypeName);
if (!type.IsRecord)
{
throw new InvalidOperationException("Only records have copy-constructor");
}

return type.InstanceConstructors.Single(c => c.Parameters is [{ Type: var parameterType }] && parameterType.Equals(type, SymbolEqualityComparer.Default));
}

public static IMethodSymbol GetPrimaryConstructor(this Compilation compilation, string qualifiedTypeName)
{
return compilation.GlobalNamespace.GetMember(qualifiedName);
var type = compilation.GetMember<INamedTypeSymbol>(qualifiedTypeName);
return type.InstanceConstructors.Single(c => c.DeclaringSyntaxReferences.Any(r => r.GetSyntax() is TypeDeclarationSyntax));
}

public static ISymbol GetMember(this Compilation compilation, string qualifiedName)
public static IMethodSymbol GetParameterlessConstructor(this Compilation compilation, string qualifiedTypeName)
{
return compilation.GlobalNamespace.GetMember(qualifiedName);
var type = compilation.GetMember<INamedTypeSymbol>(qualifiedTypeName);
return type.InstanceConstructors.Single(c => c.Parameters is []);
}

public static T GetMember<T>(this CSharpCompilation compilation, string qualifiedName) where T : Symbol
public static IMethodSymbol GetSpecializedEqualsOverload(this Compilation compilation, string qualifiedTypeName)
{
return (T)compilation.GlobalNamespace.GetMember(qualifiedName);
var type = compilation.GetMember<INamedTypeSymbol>(qualifiedTypeName);
return type.GetMembers("Equals").OfType<IMethodSymbol>().Single(m => m.Parameters is [{ Type: var parameterType }] && parameterType.Equals(type, SymbolEqualityComparer.Default));
}

public static T GetMember<T>(this Compilation compilation, string qualifiedName) where T : ISymbol
public static IMethodSymbol GetPrimaryDeconstructor(this Compilation compilation, string qualifiedTypeName)
{
return (T)compilation.GlobalNamespace.GetMember(qualifiedName);
var primaryConstructor = compilation.GetPrimaryConstructor(qualifiedTypeName);
if (!primaryConstructor.ContainingType.IsRecord)
{
throw new InvalidOperationException("Only records have primary deconstructor");
}

return primaryConstructor.ContainingType.GetMembers("Deconstruct").OfType<IMethodSymbol>().Single(
m => m.Parameters.Length == primaryConstructor.Parameters.Length &&
m.Parameters.All(p => p.RefKind == RefKind.Out && p.Type.Equals(primaryConstructor.Parameters[p.Ordinal].Type, SymbolEqualityComparer.Default)));
}

public static ImmutableArray<T> GetMembers<T>(this Compilation compilation, string qualifiedName) where T : ISymbol
=> GetMembers(compilation, qualifiedName).SelectAsArray(s => (T)s.ISymbol);

public static ImmutableArray<Symbol> GetMembers(this Compilation compilation, string qualifiedName)
{
NamespaceOrTypeSymbol lastContainer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ internal enum MethodKind
public static string GetResource(string keyword, string symbolDisplayName)
=> string.Format(FeaturesResources.member_kind_and_name, TryGetResource(keyword) ?? throw ExceptionUtilities.UnexpectedValue(keyword), symbolDisplayName);

public static string GetResource(string keyword, string symbolDisplayName, string containerKeyword, string containerDisplayName)
=> string.Format(
FeaturesResources.symbol_kind_and_name_of_member_kind_and_name,
TryGetResource(keyword) ?? throw ExceptionUtilities.UnexpectedValue(keyword),
symbolDisplayName,
TryGetResource(containerKeyword) ?? throw ExceptionUtilities.UnexpectedValue(containerKeyword),
containerDisplayName);

public static string GetResource(string keyword)
=> TryGetResource(keyword) ?? throw ExceptionUtilities.UnexpectedValue(keyword);

Expand All @@ -63,6 +71,8 @@ public static string GetResource(string keyword)
"field" => FeaturesResources.field,
"method" => FeaturesResources.method,
"property" => FeaturesResources.property_,
"property getter" => CSharpFeaturesResources.property_getter,
"property setter" => CSharpFeaturesResources.property_setter,
"auto-property" => FeaturesResources.auto_property,
"indexer" => CSharpFeaturesResources.indexer,
"indexer getter" => CSharpFeaturesResources.indexer_getter,
Expand Down
Loading

0 comments on commit 6af144c

Please sign in to comment.