-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Add analyzer for detecting BestFriend usages on public declarations #2434
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
Add analyzer for detecting BestFriend usages on public declarations #2434
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2434 +/- ##
=========================================
Coverage ? 71.22%
=========================================
Files ? 787
Lines ? 140993
Branches ? 16112
=========================================
Hits ? 100417
Misses ? 36109
Partials ? 4467
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, thanks @mareklinka !! I get the feeling eventually we're going to start paying for these analyses over the semantic model.
private const string Format = "The " + AttributeName + " should not be applied to publicly visible members."; | ||
|
||
private const string Description = | ||
"The " + AttributeName + " attribute is only valid on internal identifiers."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
valid on internal identifiers. [](start = 57, length = 30)
Well, technically this rule is only about public declaration, so no one will stop evil me to put [BestFriend]
on private declarations, and this description wouldn't be right.
I'm not saying you should verify private declarations, it's just description and Title slightly out of sync :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private readonly Lazy<string> SourceDeclaration = TestUtils.LazySource("BestFriendOnPublicDeclaration.cs"); | ||
|
||
[Fact] | ||
public void BestFriendOnPublicDeclaration() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 We should really get this switched over to Microsoft.CodeAnalysis.Testing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like the whole analyzer test project could benefit from this. However, this extends beyond the scope of the original issue - could we track this as a separate issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we track this as a separate issue?
Yes, of course 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tracked as #2480.
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.RegisterSemanticModelAction(Analyze); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❗️ Needs to call EnableConcurrentExecution
💡 Should call ConfigureGeneratedCodeAnalysis
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.RegisterSemanticModelAction(Analyze); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❕ For efficiency purposes, this should be a symbol analyzer, not a semantic model analyzer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
|
||
// Get the symbols of the key types we are analyzing. If we can't find it | ||
// there is no point in going further. | ||
var bestFriendAttributeType = comp.GetTypeByMetadataName(attributeName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Ideally this will be called once in a compilation start action and used directly in the symbol analyzer
@mareklinka thanks for taking care of this. |
I definitely should have it signed. I contributed to corefx as well some time ago so it should be sorted out. I also double checked yesterday (I saw the pending check as well) and the .NET foundation CLA page says I signed it. Not sure what's going on. |
Just pushed new changes and now it reports correctly. Weird. |
|
||
private void AnalyzeCore(SymbolAnalysisContext context, INamedTypeSymbol attributeType) | ||
{ | ||
if (context.Symbol.DeclaredAccessibility != Accessibility.Public) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 This will report a diagnostic on something like the following. You'll want to review and determine if this is desirable.
internal class NotPublicType
{
[BestFriend]
public void DeclaredPublicButNotExposed() { }
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good point. The effective visibility is internal
here but a declaration like this is still not "correct". How do you imagine we handle this, @TomFinley?
We could either just keep reporting this as incorrect (simple 😄) or recursively compute the effective visibility. That would require us to walk all the way to the assembly level for each symbol. That might get expensive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi just saw this. If it will report something like this, that is good, not bad. So I'd say @mareklinka's change here is desirable, if it in fact does warn on this.
Indeed, it's unclear to me what the above snippet would do? My best guess is, the thinking that references to NotPublicType
should fail, but references to NotPublicType.DeclaredPublicButNotExposed
should succeed? If my guess as to what the point is is correct, that is against the spirit of #1519 which is about making it obvious when one of these "cross-assembly-infrastructure" types and members were being declared. So the snippet's usage of [BestFriend]
is functionally not useful (since it serves no purpose), which means that I'd think it should warn.
We could either just keep reporting this as incorrect (simple 😄) or recursively compute the effective visibility. That would require us to walk all the way to the assembly level for each symbol. That might get expensive.
It may be expensive, but perhaps more relevant it's against the intended spirit of #1519. The thing I was trying to avoid when we decided, "well, we need to use InternalsVisibleTo
to help lock down the public vs. infrastructure code" is to avoid the chaos that would result if usage of internals no longer was useful to distinguish ML.NET wide infrastructure code vs. assembly specific infrastructure code. For it to serve that purpose though, it is crucial that it be explicit. (I think it's fairly important that the maintainers of this code thing of ML.NET wide accessibility of internal
classes as somehow something "special." It definitely does not serve my needs to make it easy, since then I expect it will become a wild free for all.)
As proposed in #2415, the
BestFriend
attribute should never be applied to public declarations. While this is not a problem per se it still introduces unnecessary noise. I implemented a code analyzer that will flag these occurrences as errors whenever a[BestFriend]
attribute is applied to a:I didn't implement an automated code fix for this diagnostic as fixing the problem is trivial - either remove the attribute or make the declaration internal. If we still want a code fix to be created I'll gladly take care of that as well.
Fixes #2415.