roslyn
roslyn copied to clipboard
EditorBrowsable(EditorBrowsableState.Never) in VS 2015 does not work as well as it does in VS 2013
Problem
Consider the following projects and classes:
ProjectA
is not included in the solution
namespace ProjectA
{
using System;
using System.ComponentModel;
public class BaseClass
{
[EditorBrowsable(EditorBrowsableState.Never)]
public new Type GetType() { return base.GetType(); }
[EditorBrowsable(EditorBrowsableState.Never)]
public new static bool Equals(object objA, object objB) { return Object.Equals(objA, objB); }
[EditorBrowsable(EditorBrowsableState.Never)]
public override bool Equals(object obj) { return base.Equals(obj); }
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() { return base.GetHashCode(); }
[EditorBrowsable(EditorBrowsableState.Never)]
protected new object MemberwiseClone() { return base.MemberwiseClone(); }
[EditorBrowsable(EditorBrowsableState.Never)]
public new static bool ReferenceEquals(object objA, object objB) { return Object.ReferenceEquals(objA, objB); }
[EditorBrowsable(EditorBrowsableState.Never)]
public override string ToString() { return base.ToString(); }
public static void DoSomeBasicThing() {}
}
}
namespace ProjectA
{
public class ClassA : BaseClass
{
public static void InitializeClassA() { }
public void DoSomethingA() {}
}
}
ProjectB
is included in the solution; has a binary reference to ProjectA
namespace ProjectB
{
using System.ComponentModel;
using ProjectA;
[EditorBrowsable(EditorBrowsableState.Never)]
public class ClassB : BaseClass, IInterfaceA<ClassB>
{
public void DoSomethingB()
{
var objectA = new ClassA();
}
}
}
ProjectC
is included in the solution; has a binary reference to ProjectA; has a project reference to ProjectB
namespace ProjectC
{
using ProjectB;
public class ClassC
{
public void TestEm()
{
var objectB = new ClassB();
}
}
}
In Visual Studio 2013 the EditorBrowsable
attribute is honored correctly for members of types defined in assemblies that are included using a binary reference. However, in Visual Studio 2015, some of the methods defined on the System.Object
[static Equals, static ReferenceEquals, and GetType] appear in the Intellisense menu despite being marked with the EditorBrowsable
attribute with the EditorBrowsableState.Never
argument in a sub class via new
hiding methods.
Here are some screenshots:
Visual Studio 2013 - viewing ProjectA.ClassA's Intellisense menu from ProjectB.ClassB: | Visual Studio 2015 - viewing ProjectA.ClassA's Intellisense menu from ProjectB.ClassB: |
---|---|
![]() |
![]() |
Visual Studio 2013 - viewing objectA's Intellisense menu from ProjectB.ClassB: | Visual Studio 2015 - viewing objectA's Intellisense menu from ProjectB.ClassB: |
---|---|
![]() |
![]() |
Visual Studio 2013 - viewing ProjectB.ClassB's Intellisense menu from ProjectC.ClassC: | Visual Studio 2015 - viewing ProjectB.ClassB's Intellisense menu from ProjectC.ClassC: |
---|---|
![]() |
![]() |
Visual Studio 2013 - viewing objectB's Intellisense menu from ProjectC.ClassC: | Visual Studio 2015 - viewing objectB's Intellisense menu from ProjectC.ClassC: |
---|---|
![]() |
![]() |
Note that this is the additional problem I mentioned in #4358 in the footnote of this comment.
Is this fixed? I can still see this bug on VS 2015 Professional Update 1 build 14.0.24720.00
The NUnit team is suffering from this issue when developing our Assert
API. We'd like to hide Equals
, to prevent users from accidentally entering the following:
Assert.Equals(a, b);
Assert.That(a, Is.Equals...
When they should have entered:
Assert.AreEqual(a, b);
Assert.That(a, Is.EqualTo(b));
The following appears to work fine in Visual Studio 2013:
[EditorBrowsable(EditorBrowsableState.Never)]
new public static bool Equals(object ob1, object ob2)
{
throw new NotImplementedException("I'm afraid I can't do that");
}
[EditorBrowsable(EditorBrowsableState.Never)]
new public static bool ReferenceEquals(object ob1, object ob2)
{
throw new NotImplementedException("I'm afraid I can't do that");
}
But no longer works in Visual Studio 2015:
You can find the issue here: https://github.com/nunit/nunit/issues/1726#issuecomment-255089011
See also: https://twitter.com/jcansdale/status/788704315695398912 😉
Also reported in https://developercommunity.visualstudio.com/content/problem/32768/editorbrowsable-attribute-not-respected-by-intelli.html.
I don't understand why this is so hard to fix? It should be a piece of cake now that Intellisense already got filtering by types, methods, properties, etc.
Is this a VS thing, or Roslyn? Which system is responsible for Intellisense population?
So that's up with this? It was added to the 2.0 milestone but appears to still be an issue. Is there any work for a fix going on regarding this bug?
Any update?
@ryo0ka PRs welcome :)
@dpoeschl and I are interested in seeing this completed, but it doesn't currently fix into our scheduling. I'm hoping a community user picks it up so it can ship in 15.5.
Requirements
- Define the intended behavior
- Target item is in the current project
- Target item is found via a project reference
- Target item is found via an assembly (metadata) reference
- Locally experiment with Visual Studio 2013 to identify any differences between the intended behavior and the behavior you observe in Visual Studio 2013
- Post results of intended behavior and Visual Studio 2013 experiments here for review/sign-off (feel free to copy from the original bug report comment where it helps, but review text for accuracy)
- Implement the completion filtering according to the intended behavior
- Implement tests covering each of the intended behavior scenarios
Time requirement
This will probably take a new user up to a week or more, or an experienced user a few days.
Work would need to start within the next week to ensure time for this to ship in 15.5 (the earliest release where it would currently be possible).
Interested?
If you are interested, please let us know. If more than one person is interested, that would be acceptable for this issue. In particular, it will be good to have more than one person test the intended behavior in Visual Studio 2013, and more than one person review the test cases to ensure they accurately represent the intended behavior so we do not regress in the future.
As far as intended behavior, I'd say the members with EditorBrowsableState.Never
should never be show, regardless of how the member was discovered (current project, project reference or assembly reference). An improvement would be to show it anyway for the first two, but I'd say it's very much a nice too have with very low pri.
@JieCarolHu @sharwell what is the status of this? I am interested in helping to get this issue fixed.
@JieCarolHu it looks like you have done some work on this in the past (#25290), but the PR is closed and appears dead for several months. Is there still work on this? Was it killed because of the required public API changes?
The work in #25290, although incomplete, does appear to fix some cursory unit tests I wrote up for this issue. What is the blocker on finishing this work? Presumably approval or buy-in from whomever owns the relevant public APIs?
After more than 4 years this is still an issue with VS 2017. Can we expect a fix or is it just that low a priority that nobody has time for it?
Can we expect a fix or is it just that low a priority that nobody has time for it?
@MikeLimaSierra In this case, i believe this would be taken as a community contribution if someone was interested. However, what's important (to me at least) is that a design be created that doesn't break very specific and intentional design changes that were intentionally made to this feature.
Specifically, it was intentional, and based on enough feedback, that the EditorBrowable attribute have different behavior for your current project versus references. This was because there were many teams that effectively wanted to have members that they would see, but that they wanted to hide from others.
So, the design of the attribute was updated to very intentionally operate different within projects in a solution vs metadata references pulled in. The intuition being: if this is your own solution, you understand the design and want to have access to your own members, but when distributing as a library you want to be able to control how others view your assembly.
If we can fix these issues while keeping the above design intact, I'm personally happy.
@CyrusNajmabadi The bug here is unrelated to the situation you describe, which is covered by #37478. This bug is about a failure of the Roslyn implementation to also eliminate methods hidden by a method whenever it eliminates a method with this attribute.
Ping... it sounds like a conservative fix - one that does not change the original constraints of the attribute, but simply restores VS 2013 behavior - has been validated. What's the status of releasing this fix? Many code generators would benefit greatly.
@Scottj1s There has been no forward progress on this issue. Sorry!
Can we expect a fix or is it just that low a priority that nobody has time for it?
@MikeLimaSierra In this case, i believe this would be taken as a community contribution if someone was interested. However, what's important (to me at least) is that a design be created that doesn't break very specific and intentional design changes that were intentionally made to this feature.
Specifically, it was intentional, and based on enough feedback, that the EditorBrowable attribute have different behavior for your current project versus references. This was because there were many teams that effectively wanted to have members that they would see, but that they wanted to hide from others.
So, the design of the attribute was updated to very intentionally operate different within projects in a solution vs metadata references pulled in. The intuition being: if this is your own solution, you understand the design and want to have access to your own members, but when distributing as a library you want to be able to control how others view your assembly.
If we can fix these issues while keeping the above design intact, I'm personally happy.
Can we have a new EditorBrowsableState.Never_We_Really_Mean_It_This_Time
state for those of us who don't want to see members even in their own projects? Or perhaps a more appropriate option would be to add a EditorBrowsableState.InternalOnly
for those who want to see the member in their projects but not outside of their project. It's strange to change the meaning of Never
after so many years of respecting it.
I'm not opposed to a Never_We_Really_Mean_It_This_Time enhancement, as long as it doesn't hold hostage just getting the regression fixed. The former has a higher acceptance bar (new feature), so should be tracked and implemented as a separate issue.
I've marked this up for grabs. We would be ok taking in a contribution as per the rules that Sam has laid out. Thanks!
We seemed to have traded one evil for another and at the same time introduced a breaking change. Making changes like this doesn't seem like good engineering practice to me.
@Scottj1s @TyreeJackson, something like EditorBrowsableState.Never_We_Really_Mean_It_This_Time
feels like the incorrect solution to a problem that never should've existed. I imagine if we went back in time before this breaking change was introduced, we wouldn't come up with this solution.
I understanding wanting to preserve the current behavior of EditorBrowsableState.Never
for those that are now dependent on it. But I would vote for an MSBuild project property that can used to opt-in to this behavior:
<PropertyGroup>
<EditorBrowsableNeverReallyMeansInternalOnly>true</EditorBrowsableNeverReallyMeansInternalOnly>
</PropertyGroup>
Then we can have a more correct solution using your suggestion of EditorBrowsableState.InternalOnly
. This should behave identically to the internal
keyword, meaning that assemblies who are granted internal access via InternalsVisibleTo
would also see metadata tagged with EditorBrowsableState.InternalOnly
.
As above, we woudl be willing to take a contribution here that matches the change that Sam laid out.
No updates on this after 5 years?!
@AradAral See https://github.com/dotnet/roslyn/issues/4434#issuecomment-675676789
Fixing this just requires this code:
https://github.com/dotnet/roslyn/blob/089be987071b4d088dda1f5a1e35a9cf8d03d2b5/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs#L664-L676
to use this code instead:
https://github.com/dotnet/roslyn/blob/089be987071b4d088dda1f5a1e35a9cf8d03d2b5/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs#L4121-L4149
And the VB side would be derived from this:
https://github.com/dotnet/roslyn/blob/089be987071b4d088dda1f5a1e35a9cf8d03d2b5/src/Compilers/VisualBasic/Portable/Symbols/Source/OverrideHidingHelper.vb#L278-L314
This is a case where given that a team member even knows what code needs to be touched, waiting for the community to figure out how to make a unit test for it, understand how that fits with other unit tests, solely to (mostly) blindly apply the suggested code change, is not very productive work for a contributor (maybe yes?).
Hopefully @sharwell will just submit a PR 🙏
@kzu everyone is chock full of a ton of work to do. If you could help out, it would be very valuable and appreciated.
The problem here is the implementations we want are in the compiler and do not have any public API surface.
Totally understood @CyrusNajmabadi, as a maintainer of oss myself, I know that's pretty much always the case 😢 .
@sharwell I suppose that makes it a plus rather than a problem? 😉
Source generators are making this more painful. Mentioned in https://github.com/dotnet/roslyn/issues/44929 and a community member was asking about it on Gitter.
I am the surprised community member :)
In my case the EditorBrowsable.Never properties were internal
.
As internal
members are never visible outside their own project, I think it's clear that the intention here was to hide them even in the project that declares them.
I suppose EditorBrowsableState.Never
+ internal
could be ruled as hidden in IDE, even in same project.
(For context, this is inside generated code, marked with <auto-generated>
at top of file.)