-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
IDE shows warnings for invalid code that isn't bound in a ref-only build #54141
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
What should the IDE behavior be? Should all method bodies be considered unreachable and thus faded out with no binding? |
@terrajobst If the decision is that the IDE shouldn't be showing squiggles (either by default or by toggle), I do like the idea that the member contents gets greyed out if it's inactive code. However, if you put invalid syntax in the method body (for example |
Generally speaking, unreachable code is still parsed/bound. The problem with not binding method bodies is that you won't get any tooling support (code completion, parameter info, quick info etc). One could argue that this would be OK because the developer gets to choose which target framework they want to get IntelliSense for (via the drop down in the top left corner of the editor). For target frameworks that have |
I guess another option is to both show squiggles AND grey out the code? (and perhaps turn the level of red down a notch) |
What errors are you getting? |
Personally, I think the errors are appropriate. The code you're looking at is not correct, and what is the IDEs job if not to show you code? |
The point is I shouldn't really need to waste my time doing that everywhere in the ref-only target. I mean this is a whole lot cleaner: public void MyMethod() => MethodNotInRefOnlyBuild(); Than this: public void MyMethod()
{
#if REF_ONLY
throw null;
#else
MethodNotInRefOnlyBuild();
#endif
} Now multiply that by the 30 methods I have just in one of my classes and it gets ugly. The other alternative is to declare |
Indeed. Logically, specifying public void MyMethod()
{
#if !REF_ONLY
MethodNotInRefOnlyBuild();
#endif
} And for code that isn't active, the IDE reports not errors either. |
Can someone explain what errors are being reported? |
So the ide doesn't generate errors. It just reports the errors that the compiler produces. So we need to know what errors are being produced here. If that information can be provided, that would be helpful. Thanks! |
@CyrusNajmabadi The main question is more whether errors that doesn't happen at compile time should be reported at all. The errors are sort of in a way valid, except it is occuring in code that is never used. So the specific error (and there are multiple) isn't as important as the IDE behavior, however the repro is above. |
I literally just want to know waht errors are being reported :) It is relevant to me as i'm trying to figure out waht subsystem is reporting the issue. I literallyu do not have enough information to make a call here as i cannot really understand what is going on. Can you please provide the requested information as it will help be understand a lot better what is happening here. Thanks! |
@CyrusNajmabadi got it. I'll get that to you in a bit |
@CyrusNajmabadi from the twitter thread: |
Thanks! I'm trying to understand the goal here from teh end author. Why was the body supplied with: if (LELPROPDECLARED)
return; I'm legit just trying to understand why that would be written and why it would be in that sort of form. i.e. it's C# code... but isn't following the rules of the language. What was the reason for adding that? |
I'm not sure what that code is. I think I made my case for this in the initial description of the issue with some more realistic code. |
In your 'more realistic code' what errors are you getting? Thanks! |
@CyrusNajmabadi These only show when you select "Build + Intellisense": |
Ok. So my question remains:
Basically... what is the reason for writing I'm genuinely coming up blank what the end customer scenario is here and why this would be something to try to support. I've also seen teh larger example, but i have an unanswered question on that here: #54141 (comment) |
@CyrusNajmabadi That specific code was to just repro the issue the simplest way possible. See my second example in the issue description. |
I get that. But the IDE itself is many components interoperating (including calling into the compiler). This information is really helpful to get an immediate gauge on which of the components are likely having a problem and eliminate those that are likely not involved. Thanks! :) |
Adding @jaredpar's original diagnosis from twitter:
void M() {
DoesNotExist e;
}
@CyrusNajmabadi As I understand it: all semantic errors are not emitted by the compiler (at the commandline) if they are inside of a method body and that method body's contents are being elided as part of generating a reference assembly. While @jaredpar labels this as "By Design" I believe the operserved error behavior is more of an implementation detaul. The compiler never attempts to bind the method body so at the commandline no binding errors are reported. This is what is happening from the compiler's perspective (approximately): using System;
class Program
{
static void M()
{
#if SOME_UNDEFINED_SYMBOL
Console.WriteLine("Hello World!");
#endif
}
} So if I am writing some code that I expect to compile but I accidentally added a binding error (say I mis spell using System;
public class C
{
public void M()
{
#if NETSTANDARD2_0
Console.WirteLine("Hello World");
#else
Console.WriteLine("Hello World");
#endif
}
} |
We legit just ask the compiler for diagnostics. We have no idea about this flag or what it would mean. It's not even clear what the ide could do here being trying to I've 8 the compiler's rules for what it does here. But that goes against our philosophy of not reimplementing compiler logic. :-) |
I agree with not re-implementing compiler logic. I had thought a bit about whether or not we should be showing or hiding these diagnostics. The problem with hiding them is that it creates other strange behaviors. For example consider the following: public class C {
public void M() {
DoesNotExist e;
}
} Lets say that the compiler chooses to suppress all diagnostics in Under the hood these errors do exist, it's just that the compiler is choosing not to surface them in |
Assumptions
Background
ProposalAfter talking with @jcouv I am amending this proposal From the IDE perspective we should treat command-line options that emit a ref assembly as putting us in a special mode where the compiler does not produce diagnostics if a program is invalid. In this particular case, we should gray out everything in the using System;
public class C
{
public void M()
{
#if NETSTANDARD2_0
Console.WirteLine("Hello World");
#else
Console.WriteLine("Hello World");
#endif
}
} I would also like a compiler API to let us know that a method body is not going to be emitted because (as @CyrusNajmabadi points out) we do not what to re-implement all the logic about how to strip method bodies the compiler has already done. Related Conversions
@jcouv I couldn't find the original discussion on where we decided to not emit binding diagnostics, do you remember? |
The design here is to specifically allow ref assembly generation in the face of errors. That opens up a number of other scenarios. Flipping that decision would remove those scenarios, reduce throughput and be a back compat break. Low chance of us making that change.
This decision exists outside the standard The IDE though should have access to this when building up the initial workspace. That, in theory at least, has access to the entire command line which will expose |
Both me and @sharwell did describe a scenario where we don't even want to have a .net standard implementation and just need ref assemblies for that. I'm not fully understanding the arguments above - they seem to me to be more technical than practical, but I'll admit I'm not fully grasping that part. However in that case it seems to me that there are two use-cases, and the above conclusions only account for one. Perhaps an option is to allow us to "disable" these warnings for ref-only as an opt-in at the project level? |
@jaredpar this is a wild idea but what if all binding errors inside of a reference-assembly method were just warnings? That feels like the point of warnings: "Its weird that this is happening, but you can continue." We used to call this feature "Error-as-Warning" anyways :)
Great, if we have the APIs necessary to do this in the short term. We obviously have the compilation options and emit options If someone could detail a sample of what the correct api calls would look like that would be ideal. Still dream of a day when the compiler is in charge of diagnostics full-stop and things like #7536 are resolved. |
@jmarolf I don't agree with the proposed behavior. I would rather aim to show the same diagnostics in the IDE as we do in the command-line build (ie. none for this project). [Update:] From chat with Jon, here's my understanding: the IDE runs some analyzers that are not part of the command-line compilation. Such analyzers may call semantic model APIs which cause method bodies to be bound, and this has a side-effect of adding diagnostics to the compilation the IDE uses for the Error List. But we agree that the IDE and the command-line should preferably display the same diagnostics. There's no easy way to fix this:
In the short-term, I would recommend to |
The command line compiler should not report diagnostics of any kind for semantic errors inside of method bodies when |
Design review conclusion: The IDE should pass through the same options to the compiler that would be used in a command line build. A follow-up issue should be filed to separately implement an analyzer that grays out all code inside ref-only implementation methods as unnecessary code, to avoid having users think the code is being checked for semantic accuracy. |
@sharwell Thanks Sam! I think this sounds like a great decision |
Closing out due to lack of movement. |
I'm confused why this was closed without further explanation. It seems the design review concluded that an action should be taken. Is that not true? |
Primarily because this was assigned to no one with no milestone. This was effectively never going to get done. It read closed to reflect that that's the case. If this is something that a partner team needs, please let us know, including when you need it by. We can then assign to a dev and milestone for the work to actually happen. |
Version Used:
16.11-preview, .net6-preview4
Steps to Reproduce:
Expected Behavior:
Not sure. Is the IDE wrong?
See long discussion here: https://twitter.com/JonathonMarolf/status/1405046803477667846
There are arguments for why this is correct behavior, but the squiggles showing an error, while things compile fine confused me a lot. In my case, not having to implement stubs for the netstandard build is actually a really nice benefit and saves me a lot of time, but the IDE showing errors is very confusing to the developer. If it is considered correct that the IDE is showing these "errors" even when the compilation is fine, perhaps consider a project setting that will make the IDE allow this and ignore these errors for the cases where we want to take advantage of not needing implementation for a specific target.
Here's a more real-world example of the above class (in my case it's a lot larger implementation of classes required for stubbing, or if-def every single method calling into a native implementation).
In my specific case I'm creating .NET MAUI handlers and wrapping native UI controls across several platforms that don't exist in the netstandard build, and would never actually be used at runtime. It is really nice that I don't have to either litter all my code with if-def or waste time writing .net standard stubs that'll not even get compiled/used; just so I can satisfy the IDE and not confuse developers editing the code.
The text was updated successfully, but these errors were encountered: