-
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
Nullable API Additions #28860
Nullable API Additions #28860
Conversation
@@ -14,5 +14,7 @@ public interface IDiscardSymbol : ISymbol | |||
/// The type of the discarded value. | |||
/// </summary> | |||
ITypeSymbol Type { get; } | |||
|
|||
Nullability TypeNullability { get; } |
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.
TypeNullability [](start = 20, length = 15)
Not sure if we need this one. Maybe just for regularity... #Closed
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 debated on this one for a minute as well, but ultimately decided to add it for both the regularity, and because it will likely be needed by QuickInfo as it will still have to show a type for a discard node.
In reply to: 205583015 [](ancestors = 205583015)
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.
Drop "Type" from its name. #Pending
@@ -65,13 +65,17 @@ public interface INamedTypeSymbol : ITypeSymbol | |||
/// </summary> | |||
ImmutableArray<ITypeParameterSymbol> TypeParameters { get; } | |||
|
|||
ImmutableArray<Nullability> TypeParameterNullability { get; } |
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.
Type parameters do not have nullability. #Pending
CanBeNull, | ||
Unknown | ||
} | ||
} |
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.
Let's discuss with Chuck. From our discussion yesterday, we thought maybe the public API should expose IsAnnotated
(?
or blank) plus NonNullTypes
context as primitives rather than IsNullable
(tri-state).
Also, you may want to take a look into the public APIs related to conversions. Maybe they also need to be extended. #Pending
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.
Also, FWIW, the scenarios we could think of so far, for an analyzer writer:
- should I add a
?
to this type when I generate code? (for example, IntroduceLocal) - could I assign
null
to this type? - could I get a
null
when reading from this type?
In reply to: 205584504 [](ancestors = 205584504)
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.
Honestly, I'm not sure what IsAnnotated
+ NonNullTypes
gets public users over a tri-state, and we'd still need this anyway. IsAnnotated
might be true even after a null check, but the type should be non-nullable at that point.
Note that, from the name, I've determined that IsAnnotated
refers to the declaration of the variable, not current state. If that's not what you meant, then we probably need a better name.
In reply to: 205585491 [](ancestors = 205585491,205584504)
public enum Nullability | ||
{ | ||
NonNull, | ||
CanBeNull, |
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.
The term we've tended to use is MaybeNull
. #Pending
{ | ||
NonNull, | ||
CanBeNull, | ||
Unknown |
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.
The enum
values should probably be explicit, and it feels like Unknown
should be 0
. #Pending
@@ -17,12 +17,16 @@ public struct TypeInfo : IEquatable<TypeInfo> | |||
/// </summary> | |||
public ITypeSymbol Type { get; } | |||
|
|||
public Nullability TypeNullability { get; } |
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.
Doc comment on new APIs would be helpful. #Pending
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.
The name "Type" as part of this API probably doesn't make sense. #Pending
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 included Type
to give the user something to key off of.
In reply to: 205845601 [](ancestors = 205845601)
/// <summary> | ||
/// The type of the expression after it has undergone an implicit conversion. If the type | ||
/// did not undergo an implicit conversion, returns the same as Type. | ||
/// </summary> | ||
public ITypeSymbol ConvertedType { get; } | ||
|
||
public Nullability ConvertedTypeNullability { get; } |
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.
The only way this could be different from TypeNullability
is if it is a user-defined conversion. In all other cases whether the expression is null or not doesn't change. #Pending
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 could see calling GetTypeInfo
on an expression with a !
expressing the change in nullability via this field.
In reply to: 205845726 [](ancestors = 205845726)
/// <summary> | ||
/// Get the type parameters on this method. If the method has not generic, | ||
/// returns an empty list. | ||
/// </summary> | ||
ImmutableArray<ITypeParameterSymbol> TypeParameters { get; } | ||
|
||
ImmutableArray<Nullability> TypeParameterNullability { get; } |
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.
What does this API mean? I think it can be deleted. #Pending
@@ -135,6 +141,8 @@ public interface IMethodSymbol : ISymbol | |||
/// </summary> | |||
ITypeSymbol ReceiverType { get; } | |||
|
|||
Nullability ReceiverTypeNullability { get; } |
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.
A receiver can never be null (except in a reduced extension method). I suggest just removing this API. #Pending
@@ -72,6 +72,8 @@ public interface INamedTypeSymbol : ITypeSymbol | |||
/// </summary> | |||
ImmutableArray<ITypeSymbol> TypeArguments { get; } | |||
|
|||
ImmutableArray<Nullability> TypeArgumentNullability { get; } |
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.
It feels like there should be an "s" somewhere in the name to make it plural. #Pending
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 agree with the feeling, but I couldn't find a spot that felt good. TypeArgumentNullabilities
is the closes I got, thoughts?
In reply to: 205846588 [](ancestors = 205846588)
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.
Maybe TypeArgumentsNullability
? #Pending
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.
Is there any reason we aren't doing some struct that holds a TypeSymbol and it's nullability, and having an ImmutableArray of that struct to hold both at once? My gut right now is saying the IDE is immediately going to create such a thing, and use that wherever we're using ITypeSymbols today. #WontFix
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.
Such a struct/interface to represent the pair (type+nullability) seems appealing, but I don't see how to introduce it without breaking existing API. #WontFix
@@ -14,5 +14,7 @@ public interface IDiscardSymbol : ISymbol | |||
/// The type of the discarded value. | |||
/// </summary> | |||
ITypeSymbol Type { get; } | |||
|
|||
Nullability TypeNullability { get; } |
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.
Drop "Type" from its name. #Pending
@@ -19,6 +19,8 @@ public interface IEventSymbol : ISymbol | |||
/// </summary> | |||
ITypeSymbol Type { get; } | |||
|
|||
Nullability TypeNullability { get; } |
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.
Drop "Type" #Pending
@@ -44,6 +44,8 @@ public interface IFieldSymbol : ISymbol | |||
/// </summary> | |||
ITypeSymbol Type { get; } | |||
|
|||
Nullability TypeNullability { get; } |
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.
Ditto #Pending
@@ -16,6 +16,8 @@ public interface ILocalSymbol : ISymbol | |||
/// </summary> | |||
ITypeSymbol Type { get; } | |||
|
|||
Nullability TypeNullability { get; } |
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 should be named "DeclaredNullability" for clarity. #Pending
@@ -90,19 +90,25 @@ public interface IMethodSymbol : ISymbol | |||
/// </summary> | |||
ITypeSymbol ReturnType { get; } | |||
|
|||
Nullability ReturnTypeNullability { get; } |
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.
ReturnNullability #Pending
Unknown = 0, | ||
NonNull = 1, | ||
MaybeNull = 2 | ||
} |
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 document this enum
, perhaps including examples. For instance, what is the nullability of a reference to an unconstrained type parameter T
? What is the nullability of int?
? #Pending
d0b8c54
to
02909f6
Compare
/// o2.ToString(); // Warning reported | ||
/// if (o2 != null) o2.ToString(); // No warning reported | ||
/// </code> | ||
/// <code>o1</code> is inferred to be <see cref="NonNull"/>, despite being declared |
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.
o1
is inferred to be [](start = 8, length = 55)
This should be clarified; it is not the variable that is inferred to be NonNull
, but the use of the variable in its context. #Pending
/// not provide nullability information is being used. Expressions that | ||
/// have unknown nullability are also referred to as oblivious expressions, | ||
/// and they generally do not provide warnings when converting to | ||
/// <see langword="null"/>, or when dereferencing them. |
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 don't know what "converting to null
" means. #Pending
namespace Microsoft.CodeAnalysis | ||
{ | ||
/// <summary> | ||
/// Enumeration of the possible nullability states for types. |
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.
types [](start = 59, length = 5)
"types" should be "variables and expressions". #Pending
/// </remarks> | ||
Unknown = 0, | ||
/// <summary> | ||
/// The variable is either known or declared to be non-null. |
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.
variable [](start = 16, length = 8)
variable or value #Pending
/// </remarks> | ||
NonNull = 1, | ||
/// <summary> | ||
/// The variable is either known or declared to possibly be null. |
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.
variable [](start = 16, length = 8)
variable or value #Pending
/// </summary> | ||
/// <remarks> | ||
/// Nullable value types will always be considered to be <see cref="MaybeNull"/>. | ||
/// </remarks> |
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 don't think this is correct. Didn't we decide to track nullability for nullable value types? If so, they can possibly be non-null. #Resolved
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.
@@ -19,6 +19,11 @@ public interface IEventSymbol : ISymbol | |||
/// </summary> | |||
ITypeSymbol Type { get; } | |||
|
|||
/// <summary> | |||
/// The declareted nullability of the this event. |
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.
declareted [](start = 16, length = 10)
mispel #Pending
@@ -16,6 +16,12 @@ public interface ILocalSymbol : ISymbol | |||
/// </summary> | |||
ITypeSymbol Type { get; } | |||
|
|||
/// <summary> | |||
/// The initial declared nullability of this local. If the local's type was inferred (i.e. via <code>var</code>), then |
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.
initial [](start = 16, length = 7)
I would drop "initial". #Pending
Remove Declared from the name of the Nullabilities. #Pending |
Range Variables #Pending |
/// <param name="nullability">The nullability to use for the symbol.</param> | ||
/// <param name="format">Format or null for the default.</param> | ||
/// <returns>A formatted string representation of the symbol.</returns> | ||
string ToDisplayString(Nullability nullability, SymbolDisplayFormat format = null); |
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.
format [](start = 76, length = 6)
This default is not one that ignores nullability
/// <summary> | ||
/// The type of the expression after it has undergone an implicit conversion. If the type | ||
/// did not undergo an implicit conversion, returns the same as Type. | ||
/// </summary> | ||
public ITypeSymbol ConvertedType { get; } | ||
|
||
/// <summary> | ||
/// The nullability of the expression after conversions have been applied. For most expressions, |
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.
conversions [](start = 52, length = 11)
Specify implicit
/// <summary> | ||
/// The inferred nullability of the expression represented by the syntax node. | ||
/// </summary> | ||
public Nullability Nullability { get; } |
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.
is there ever any concern that the 'Type' vs the 'ConvertedType' might have different Nullability? Or are tey guaranteed to be the same?
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.
Lol. i can't read. sorry.
/// <summary> | ||
/// Gets the nullability of the elements stored in the array. | ||
/// </summary> | ||
Nullability ElementNullability { get; } |
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.
So, i'm not disagreeing with this approach. But how come this is not necessarily part of the ITypeSymobl itself at this projected level?
For example, there is no "IsElementTypeDynamic" even though "Dynamic" and "Object" are effectively the same under the covers. INstead, we represent each of those with different types. Why not the same here?
/// <summary> | ||
/// The declareted nullability of the this event. | ||
/// </summary> | ||
Nullability DeclaredNullability { get; } |
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 don't like the naming here. If type field is called ITypeSymbol Type
it feels like this should be Nullability TypeNullability
. That way you can easily track what this is referring to.
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.
The key thing here is that Nullability is not referring to the nullability of the Type
. It's referring to the nullability of the value.
/// <summary> | ||
/// The inferred nullability of the expression represented by the syntax node. | ||
/// </summary> | ||
public Nullability Nullability { get; } |
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 don't like the naming here. If type field is called ITypeSymbol Type
it feels like this should be Nullability TypeNullability
. That way you can easily track what this is referring to. This would also apply to 'ConvertedType' and 'ConvertedTypeNullability'. it basically augments the other field with that name.
/// Returns the nullability of the type arguments that have been substituted for the | ||
/// type paramters. | ||
/// </summary> | ||
ImmutableArray<Nullability> TypeArgumentsNullability { get; } |
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 makes me like things even less. Note: the naming here is good (though the naming of ReturnNullability is inconsistent). But it seems pretty unfortunate that it's exposed through an entirely separate array, instead of being exposed on the individual TypeArgument type.
/// <summary> | ||
/// Gets the declared nullability of the property | ||
/// </summary> | ||
Nullability DeclaredNullability { get; } |
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 this shuld be TypeNullability. I don't know what 'Declared' means here.
/// <summary> | ||
/// The nullability of the constraints on the type parameter. | ||
/// </summary> | ||
ImmutableArray<Nullability> ConstraintsNullability { get; } |
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 name is inconsistent with ConstraintTypes. it also doesn't seem to match the naming of anything else in the PR.
@CyrusNajmabadi this PR is somewhat out of date, just FYI. Many of your naming concerns have already been addressed locally. |
/// <remarks> | ||
/// This is used for legacy code scenarios, where a legacy API that does | ||
/// not provide nullability information is being used. Expressions that | ||
/// have unknown nullability are also referred to as oblivious expressions, |
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.
is there a good reason to have both names? if we refer to them as Oblivious, shoudl the name of this enum member be that?
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 have it now because we used that terminology in the LDM, but I expect this will probably be removed. I don't know that we're fully satisfied with the work Unknown either, so that may also change.
Great. i'll look again when it's updaed. @jcouv pointed me this way due to questions i had around IDE impact of things. |
/// </summary> | ||
// PROTOTYPE(NullableReferenceTypes): Potentially link to the correct methods to compute? | ||
// Some of them are defined on CSharpSemanticModel, so this could be difficult | ||
NotComputed = 1, |
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.
exposing this publicly seems... weird. does that mean users can observe when we have haven't comptued this stuff?
SemanticModel semanticModel, | ||
int position, | ||
Nullability nullability, | ||
SymbolDisplayFormat format = null); |
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 really feels like we're going to be virally spreading this outward. consdier anything in the IDE that ends up calling helpers like this. this info needs to pass outwards to any and all callpaths.
public static TypeInfo GetTypeWithNullability(this SemanticModel semanticModel, IFieldSymbol field, CancellationToken ct = default) | ||
{ | ||
throw new NotImplementedException(); | ||
} |
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.
what if you don't have a semantic model... feels super weird that this would be at the semantic model level.
Consider any and all IDE features that do any sort of generation. Previously there was no need to pass along any sort of compilation. They could just use symbols.
Note: this affects public APIs we've shipped. These APIs would now seemingly become 'wrong' for any cases involving nullability. taht concerns me greatly.
@@ -35,11 +35,13 @@ public struct TypeInfo : IEquatable<TypeInfo> | |||
/// </summary> | |||
public Nullability ConvertedNullability { get; } | |||
|
|||
internal TypeInfo(ITypeSymbol type, ITypeSymbol convertedType) | |||
internal TypeInfo(ITypeSymbol type, ITypeSymbol convertedType, Nullability nullability = Nullability.NotComputed, Nullability convertedNullability = Nullability.NotComputed) |
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.
TypeInfo is not strongly typed. So APIs that need to pass nullability information along now have hte unpalatable position of moving to this, but can lose the info if they were originally typed to something like an INamedTypeSymbol.
or they pass both along, but need to know that the both symbols must be the exact same instance.
this feels super unclean. Let's do a thought experiment. Consider if we'd done 'dynamic' this way. Or tuple types this way. Where we had the 'underlying' type (like Object, or ValueTuple) and people needed to pass side-band information along about the 'projected type information' the compiler understood for that type. Tihs would have a very viral and unpleasant impact on all downstream APIs. |
I dont' understand how i could use this API to support a scenario like "Generate me the The way this API works today for genering But how would i generate such How would i ask the compiler to make me the we would now need sidechannels for anywhere that type information flowed to pass this stuff along. And it's not like that info is just at a single level. It can be deeply nested anywhere into where you might find a type signature. Also note that we have many public APIs for users to do generation based on symbols. It feels like none of tehse APIs woudl work at all properly for nullable. This impacts not only our customers, but our own features. We'd end up having to deprecate and duplicate everything for this case of passing along side-channel info. |
I think any API likely needs to actually do the legwork to show the impact on actual consumptive components (like many of the IDE components). We have deeply baked in (and publicly exposed) the means to do things using ISymbols as our expected currency. That whole approach seems to be thrown out the window, with effectively much of our existing old API getting deprecated. For example, it seems virtually certain that using the old ToDisplayString is now wrong (as it won't properly reflect nullability info). So literally all code taht uses it has to move foward. It's most likely that lots of code will miss this (as well as huge swaths of external code). So we'll basically have things just not behaving right. This is what would have happened with any other changes to symbols that we exposed in this manner. For example, if dynamic symbols were exposed as an extra boolean you passed along with your regular type symbol, or if value-tuples passed along their names as extra information next to the itypesymbol (instead of being exposed through the symbol). If we go the sidechannel approach, then that basically will force a viral update of any and all codepaths that need to funnel this information down to interior compoments and will virtually guarantee that many existing entrypoints (which we need to keep for backcompat) will be doing the wrong thing. |
@CyrusNajmabadi PR updated after today's meeting. At this point, I think we can separate the two concepts that we're trying to add with this API:
|
I agree that this is a significant thing to be concerned about. I would very much be curious though what the actual end impact is and would appreciate a true measurement to understand the impact. |
For comparison, the TypeScript compiler needs to deal with this situation and worse. it is super common to have all sorts of ephemeral types being sythesized through code-flow analysis due to recursion in structural types. TS also has to handle things like However, it is worth noting that TS represents these things differently. Nullable reference types are represented similarly to how we would represent nullable value types. In that, it's not a property on a type. Instead, there's a wrapper type that is instantiated. For TS, these are types like Despite all this, the memory overhead of being able to represent nullable types was not actually problematic, and did not impact IDE scenarios. Note that TypeScript has an even harder job than Roslyn/C#/VB. That's because in IDE scenarios, TypeScript has to deal with inferred function return types (something which doesn't exist in teh C# language). So TS constantly has to jump across functions to analyze them and determine their return types. Even with this, perf (both latency and memory) remains super good there. So my gut makes me feel that it would be unlikely to have a perf issue for C#/VB. IMO, we should try these approaches out and actually measure the core scenarios. For example we should see the impact on:
At the same time, we be willing to understand what we might think was acceptable marginal overhead. For example, if this causes memory usage in VS to jump from 100MB to 200MB, that's a lot. But, for example, if it only increased memory in real use cases by a few percent, then it can be argued that the benefit may be worth it. Analysis of measurements should take into account relative impact and what should be acceptable bounds here. |
Note: if we do '1' (GetNullabilityAwareSemanticModel), then it seems like the concerns with 'Nullability on ITypeSymbol' are vastly diminished right? For example, the compiler would practically never call GetNullabilityAwareSemanticModel. Internally it would still use the appropriate efficient struct-based impl and would avoid the 'make lots of type symbols' problem. Basically, it would use the API as described as per dcbd24e internally. But when these symbols were exposed through the public API, that view would flatten into synthesized symbols that exposed through something like a 'Nullability' property. This would only impact IDE features that then wanted this information. And IDE features are really only going to light up and need this for analyzing the code right around where the user is. So while there will be extra symbols here, it should be fairly well contained and not explosive in impact (IMO at least). This also then takes care of 2.i and 2.ii really nicely. To me, this seems like it would be the best of all worlds, and would be easy to test for. Thoughts? |
Contains the additions we are considering to add to the symbol API to expose nullability. This PR only has API additions, no implementation. It is expected to fail CI. @jcouv, @cston, @gafter, please review to make sure this is in line with what you were expecting.