Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make event backing fields accessible to third-party analyzers #40572

Closed
wants to merge 9 commits into from

Conversation

jnm2
Copy link
Contributor

@jnm2 jnm2 commented Dec 24, 2019

Fixes #36259. Other than where I commented, all changes are meant to restore identical outcomes now that the event backing field comes along with the other fields for free. I seemed to remove a lot of workarounds.

@jnm2 jnm2 requested review from a team as code owners December 24, 2019 02:39
@jnm2 jnm2 changed the title Event field symbol Make event backing fields accessible to third-party analyzers Dec 24, 2019
Comment on lines +668 to +671
if ((member as FieldSymbol)?.AssociatedSymbol is EventSymbol)
{
continue;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have suggestions for alternatives? This is added to avoid the assertion on line 72:

Debug.Assert(result.IsMultiViable || result.IsClear || result.Error != null);

Comment on lines -174 to +154
var symbol = member switch
{
FieldSymbol { AssociatedSymbol: PropertySymbol p } => p,
_ => member
};

var symbol = field.AssociatedSymbol is PropertySymbol || field.AssociatedSymbol is EventSymbol ? field.AssociatedSymbol : field;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think of making this field.AssociatedSymbol ?? field?

{
continue;
}

var fieldType = field.TypeWithAnnotations;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be perfectly equivalent to the original code, this would be:

var fieldType = (field.AssociatedField as EventSymbol)?.TypeWithAnnotations ?? field.TypeWithAnnotations;

But it seems unlikely that they would differ.

Comment on lines 462 to +465
case SymbolKind.Field:
if (((FieldSymbol)this).AssociatedSymbol is EventSymbol)
return false;
break;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this correct?

Comment on lines +623 to +644
private static MetadataReference CompileToPEReference(string source)
{
using var peStream = new MemoryStream();

Assert.True(CreateCompilation(source).Emit(peStream).Success);

peStream.Position = 0;

return MetadataReference.CreateFromStream(peStream);
}

private static IAssemblySymbol GetAssemblySymbolForReference(MetadataReference reference)
{
var compilation = (Compilation)CSharpCompilation.Create(
assemblyName: null,
references: new[] { reference },
options: new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
metadataImportOptions: MetadataImportOptions.All));

return (IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol(reference);
}
Copy link
Contributor Author

@jnm2 jnm2 Dec 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Are there existing helpers I should use/places to add tests if I want to specifically test end to end, source to PublicModel.ISymbol?

  • If not, where should this code go?

}
}

members.AddRange(fieldMembers);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

members.AddRange(fieldMembers); [](start = 19, length = 32)

Does this also affects fields that back auto-properties?

Copy link
Contributor Author

@jnm2 jnm2 Dec 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the original Debug.Assert, field.AssociatedSymbol is either null or its Kind is SymbolKind.Event. That means that property backing fields are never in fieldMembers at this point. I'm not sure why.

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we would want to change the set of members returned for a NamedTypeSymbol. The model is meant to reflect the language and, form the C# language point of view, there is no backing field member. As you can see we went through a big trouble to hide fields like that and that was intentional. If analyzers or other tools need to get to the backing fields, it would be interesting to understand the real world scenarios. And if we will find them compelling, then perhaps we can add AssociatedSymbol API (or AssociatedField API) to IEventSymbol interface.

@333fred 333fred added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Dec 26, 2019
@jnm2
Copy link
Contributor Author

jnm2 commented Dec 26, 2019

@AlekseyTs

I don't think we would want to change the set of members returned for a NamedTypeSymbol. The model is meant to reflect the language and, form the C# language point of view, there is no backing field member. As you can see we went through a big trouble to hide fields like that and that was intentional.

Why is it that property backing fields are returned in GetMembers? One real-world scenario cares about backing fields but doesn't care about the difference between properties and events.

💡 Is there any way you'd consider removing property backing fields from GetMembers? That would solve an unrelated problem that is complicating a C# feature.

@333fred
Copy link
Member

333fred commented Dec 26, 2019

I don't think we would want to change the set of members returned for a NamedTypeSymbol. The model is meant to reflect the language and, form the C# language point of view, there is no backing field member. As you can see we went through a big trouble to hide fields like that and that was intentional. If analyzers or other tools need to get to the backing fields, it would be interesting to understand the real world scenarios. And if we will find them compelling, then perhaps we can add AssociatedSymbol API (or AssociatedField API) to IEventSymbol interface.

@AlekseyTs clearly there is a community ask to expose these members. Even our documentation for IFieldSymbol.AssociatedSymbol suggests that we should be exposing the backing field: If this field serves as a backing variable for an automatically generated property or a field-like event, returns that property/event. Given that, I think it makes sense to make a change here, and do it in the same way that we expose the backing fields for autoproperties.

@AlekseyTs
Copy link
Contributor

@333fred

clearly there is a community ask to expose these members

Then it should be easy to present real world compelling scenarios.

Given that, I think it makes sense to make a change here, and do it in the same way that we expose the backing fields for autoproperties.

This is a significant design change not merely a bug fix. Why don't you bring this to public API council?

@jnm2
Copy link
Contributor Author

jnm2 commented Dec 26, 2019

Here are the two real-world use cases I know of: #36259 (comment)

@AlekseyTs
Copy link
Contributor

Here are the two real-world use cases I know of: #36259 (comment)

Someone will have distill these to clear summary about the use cases, so that multiple people wouldn't have to sift trough discussion threads, and/or try to infer something from code snippets, etc

@AlekseyTs
Copy link
Contributor

Is there any way you'd consider removing property backing fields from GetMembers? That would solve an unrelated problem that is complicating a C# feature.

Yes, we could consider doing this. Again, having a compelling scenario can help us make a decision. In fact, there is a "TODO" comment in code indicating that we probably had intention to do so, and it is not clear whether we explicitly decided against that, or simply didn't get to implementing the change (https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs#L3098):

                            FieldSymbol backingField = property.BackingField;

                            // TODO: can we leave this out of the member list?
                            // From the 10/12/11 design notes:
                            //   In addition, we will change autoproperties to behavior in 
                            //   a similar manner and make the autoproperty fields private.

@333fred
Copy link
Member

333fred commented Feb 28, 2020

@jnm2 can we please get a clear summary of the use case here? A motivating example will help us understand if this is the correct way to solve the issue, or if a separate API is needed. Thanks.

@jcouv jcouv added the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Mar 7, 2020
@jnm2
Copy link
Contributor Author

jnm2 commented Mar 12, 2020

I believe a separate API is needed, so I'll close this.

Further, it would unblock design options for dotnet/csharplang#140 if property backing fields were also deliberately excluded from the ITypeSymbol.GetMembers() collection the way event backing fields are. That would necessitate an API to retrieve backing fields for property symbols as well as event symbols for the sake of third-party analyzers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee. PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

No associated IFieldSymbol is provided for a field-like event
4 participants