-
Notifications
You must be signed in to change notification settings - Fork 4k
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
There is no API to determine whether we are in a #nullable enable context #36101
Comments
API Proposal: [Flags]
public enum NullableContext
{
Disabled,
WarningsOnly = 0b01,
AnnotationsOnly = 0b10,
Enabled = WarningsOnly | AnnotationsOnly
}
public static class NullableContextExtensions
{
public bool WarningsEnabled(this NullableContext context);
public bool AnnotationsEnabled(this NullableContext context);
}
public class CSharpSemanticModel : SemanticModel
{
public NullableContext GetNullableContextAtPosition(int position);
}
public class CSharpCompilation : Compilation
{
public NullableContext GetDefaultNullableContext();
} This is very similar to the current internal APIs, except that instead of a struct with two As a followup, we should also consider adding methods to the syntax factory that take a current nullable state, a target nullable state, and a syntax node, and wraps that node in the correct declarations to give it the specified state, but I'm not proposing anything concrete there for now and will leave that for later. @jasonmalinowski @ryzngard @jcouv @gafter @agocke could you please take a look at this proposal and leave any initial thoughts, and then we can schedule a quick design review after initial feedback. |
Proposal 2: public enum NullableContext
{
Enabled,
WarningsOnly,
AnnotationsOnly,
Restore,
Disabled
}
public class CSharpSyntaxTree : SyntaxTree
{
public NullableContext GetNullableContextAtPosition(int position);
}
public class CSharpCompilation : Compilation
{
public NullableContext GetDefaultNullableContext();
} |
I agree, is not intuitive here. What's the argument for needing the |
I rather like proposal 1 (simpler), assuming it satisfies use cases. But I'm having trouble understanding whether users of this API would need the So it would help if we could identify our own use cases for the IDE and confirm that proposal 1 suits those needs. |
Does So if the IDE is generating any new code that it has to surround with a Not sure why this isn't a separate boolean for each axis being returned as a struct though. If we have that enum we're probably going to immediately write a "IsAnnotationsEnabled" extension method that compares to Enabled/AnnotationsOnly and IsWarningsEnabled that compares to WarningsOnly/Enabled. |
Also, can I do a restore on just warnings or just annotations? |
It restores to the project settings.
Yes, you can restore the warnings and the annotations contexts independently. |
I think GetDefaultNullableContext will be a problem since generated files have a different default, don't they? |
|
Maybe the name was just screwing me up. The Compilation-level GetDefaultNullableContext is just asking what the compilation setting is, then? The file default without explicit annotation may be diferent? |
@jasonmalinowski, I've updated the original proposal to make the enum flags, and added extension methods to determine whether warnings/annotations are enabled. Does that satisfy your concern about the booleans? |
Correct. |
Yeah, |
Why not just return a struct? Also means if/when you ever add another mode there's a logical way to expand the API. |
Because using an enum will make followup generation APIs easier. I do think the SyntaxFactory will want an API that takes a syntax node, an enum for target nullability, and an enum for original nullability, and returns a syntax node wrapped with the correct pragmas to set to the target nullability and return to the original nullability after the syntax. |
@agocke that would be fine with me. @jasonmalinowski, how do you feel about that from the IDE side? |
@333fred Wait, don't we already have http://source.roslyn.io/#Microsoft.CodeAnalysis.CSharp/CSharpCompilationOptions.cs,41? How would this API be different from the NullableContextOptions? |
In that case, doing a struct is just as easy as an enum: the consumer of such an API isn't constructing anything directly. They're just calling the existing APIs and passing it around. |
@agocke I did not realize we had that. However, it will need to have the annotations setting added. |
The latest version includes the cross product of warnings x annotations |
@jasonmalinowski I don't think that's quite true, because you can't introduce a refactoring that explicitly turns the feature on at a specific point as easily as you could with an enum. |
@agocke I think I would still propose that the contents of that enum be structured the way I did in this proposal, using Flags and having the extension methods. |
That's fine with me |
After meeting with @jasonmalinowski, @ryzngard, @jcouv, and @agocke, we have an updated API proposal. We observed a few things in discussion:
Given these observations, we have an updated proposal below: // The first two bits communicate whether the state is inherited. The third and fourth bits
// communicate the state of the nullable feature
[Flags]
public enum NullableContext
{
WarningsContextInherited = 0b0001,
AnnotationsContextInherited = 0b0010,
// Set by default at the start of a file
ContextInherited = WarningsContextInherited | AnnotationsContextInherited,
WarningsEnabled = 0b0100,
AnnotationsEnabled = 0b1000,
Enabled = WarningsEnabled | AnnotationsEnabled
}
// Simple helper methods to do the bitwise operations
public static class NullableContextExtensions
{
public bool WarningsEnabled(this NullableContext context);
public bool AnnotationsEnabled(this NullableContext context);
public bool WarningsInherited(this NullableContext context);
public bool AnnotationsInherited(this NullableContext context);
}
class CSharpSemanticModel
{
public NullableContext GetNullableContext(int position);
}
public enum NullableContextOptions : byte
{
Disable,
Enable,
Warnings,
Annotations
}
public static class NullableContextOptionsExtensions
{
public bool WarningsEnabled(this NullableContextOptions context);
public bool AnnotationsEnabled(this NullableContextOptions context);
}
public class CSharpCompilationOptions
{
public NullableContextOptions NullableContextOptions { get; private set; }
} We did consider a Another important point is that the default file-level context is not always the same as the compilation context, namely in generated files. This is representable in the API, but will have to be doc'd so that people don't always assume that the "inherited" nullability state is the same as the compilation-level state. I'm not fully satisfied with the name of this second enum: it's not meaningfully different from /cc @cartermp and @CyrusNajmabadi, would love to hear your thoughts on this proposal as well. |
As a customer of the language, i have to admit i'm super confused about what's going on here. It feels very tough to know about all the options available and how i'm supposed to use them properly. Is that information explained anywhere? For example, what are annotations? How are they different from warnings? Why is tehre a split between them? When we did this in TS, we just had a single bool option to opt into this all or not. That was it. Now, even if a single-bool option at the compilation level isn't sufficient, why is there a need for such fine-grained control at a narrow level? |
Basically:
|
Just to be clear, is this for the first or second enum? Generally I think the split between "what the compiler has to care about" and "what things that are not the compiler has to care about" is a good distinction. I probably just need to wrap my head around this a bit more to have more of an opinion.
Although there are more options here, the one that we'll be documenting and recommending is a single option: For a source file: // To enable it for the file (or scope) you want
#nullable enable
// To enable it for the file (or scope) you don't want to enable it for
#nullable disable For a project file or <!-- To enable for the project or directory -->
<Nullable>enable</Nullable>
<!-- To disable for the project or directory (implies it's enabled at a higher scope) -->
<Nullable>disable</Nullable> There's also The rest are basically useful for Microsoft employees and power users/advanced component authors. We'll need to flow all the other options through the API and enable them via tools, but |
@cartermp this is for the second enum, instead of @CyrusNajmabadi as Phil said, most these options won't actually matter in practice, which is why we're providing extension methods to help you ask the questions that the IDE/analyzers actually care about. |
I would still like to understand them :D |
I think the name |
@CyrusNajmabadi the best explanation of the nullable context stuff we have currently is here: https://github.com/dotnet/csharplang/blob/master/meetings/2019/LDM-2019-05-13.md |
I've created a draft PR with the latest proposal here: #37017 |
This has been added. |
If the IDE needs to know if we're in a #nullable enable'd context, there's no API today to do so. This comes up in a bunch of cases: if we're generating code from one file to another and one file has it enabled but the other doesn't, we need to know to drop annotations or add #nullable enable or other things.
The text was updated successfully, but these errors were encountered: