Skip to content

Commit

Permalink
Fix crash when compiling internal attribute constructor (#42192)
Browse files Browse the repository at this point in the history
* Fix crash when compiling internal attribute constructor

* PR feedback

* Add new rule to refout.md
  • Loading branch information
khyperia authored Mar 16, 2020
1 parent d626cc5 commit 2b86338
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/features/refout.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Ref assemblies further remove metadata (private members) from metadata-only asse

- A ref assembly only has references for what it needs in the API surface. The real assembly may have additional references related to specific implementations. For instance, the ref assembly for `class C { private void M() { dynamic d = 1; ... } }` does not reference any types required for `dynamic`.
- Private function-members (methods, properties and events) are removed. If there are no `InternalsVisibleTo` attributes, do the same for internal function-members.
- But all types (including private or nested types) are kept in ref assemblies. All attributes are kept (even internal ones).
- But all types (including private or nested types) are kept in ref assemblies. All attributes are kept (even internal ones), as well as their (internal) constructors.
- All virtual methods are kept. Explicit interface implementations are kept. Explicitly-implemented properties and events are kept, as their accessors are virtual (and are therefore kept).
- All fields of a struct are kept. (This is a candidate for post-C#-7.1 refinement)
- Any resources included on the command-line are not emitted into ref assemblies (produced either with `/refout` or `/refonly`). (This was fixed in dev16)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -661,10 +661,14 @@ internal virtual bool IsMetadataSealed
{
CheckDefinitionInvariant();

// All constructors in attributes should be emitted.
// Don't compute IsAttributeType if IncludePrivateMembers is true, as we'll include it anyway.
bool alwaysIncludeConstructors = context.IncludePrivateMembers || DeclaringCompilation.IsAttributeType(this);

foreach (var method in this.GetMethodsToEmit())
{
Debug.Assert((object)method != null);
if (method.ShouldInclude(context))
if ((alwaysIncludeConstructors && method.MethodKind == MethodKind.Constructor) || method.ShouldInclude(context))
{
yield return method;
}
Expand All @@ -676,7 +680,7 @@ internal virtual bool IsMetadataSealed
{
foreach (var m in generated)
{
if (m.ShouldInclude(context))
if ((alwaysIncludeConstructors && m.IsConstructor) || m.ShouldInclude(context))
{
yield return m;
}
Expand Down
74 changes: 74 additions & 0 deletions src/Compilers/CSharp/Test/Emit/Emit/CompilationEmitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,80 @@ public event System.Action PrivateRemover { add { } private remove { } }
);
}

[Fact]
[WorkItem(38444, "https://github.com/dotnet/roslyn/issues/38444")]
public void EmitRefAssembly_InternalAttributeConstructor()
{
CSharpCompilation comp = CreateCompilation(@"
using System;
internal class SomeAttribute : Attribute
{
internal SomeAttribute()
{
}
}
[Some]
public class C
{
}
");

using (var output = new MemoryStream())
using (var metadataOutput = new MemoryStream())
{
EmitResult emitResult = comp.Emit(output, metadataPEStream: metadataOutput,
options: new EmitOptions(includePrivateMembers: false));
emitResult.Diagnostics.Verify();
Assert.True(emitResult.Success);

VerifyMethods(output, "C", new[] { "C..ctor()" });
VerifyMethods(metadataOutput, "C", new[] { "C..ctor()" });
VerifyMethods(output, "SomeAttribute", new[] { "SomeAttribute..ctor()" });
VerifyMethods(metadataOutput, "SomeAttribute", new[] { "SomeAttribute..ctor()" });
}
}

[Fact]
[WorkItem(38444, "https://github.com/dotnet/roslyn/issues/38444")]
public void EmitRefAssembly_InternalAttributeConstructor_DoesntIncludeMethodsOrStaticConstructors()
{
CSharpCompilation comp = CreateCompilation(@"
using System;
internal class SomeAttribute : Attribute
{
internal SomeAttribute()
{
}
static SomeAttribute()
{
}
internal void F()
{
}
}
[Some]
public class C
{
}
");

using (var output = new MemoryStream())
using (var metadataOutput = new MemoryStream())
{
EmitResult emitResult = comp.Emit(output, metadataPEStream: metadataOutput,
options: new EmitOptions(includePrivateMembers: false));
emitResult.Diagnostics.Verify();
Assert.True(emitResult.Success);

VerifyMethods(output, "C", new[] { "C..ctor()" });
VerifyMethods(metadataOutput, "C", new[] { "C..ctor()" });
VerifyMethods(output, "SomeAttribute", new[] { "SomeAttribute..ctor()", "SomeAttribute..cctor()", "void SomeAttribute.F()" });
VerifyMethods(metadataOutput, "SomeAttribute", new[] { "SomeAttribute..ctor()" });
}
}

private static void VerifyMethods(MemoryStream stream, string containingType, string[] expectedMethods)
{
stream.Position = 0;
Expand Down

0 comments on commit 2b86338

Please sign in to comment.