Skip to content

Initial support for [AllowNull], [DisallowNull], [MaybeNull], [NotNull] in NullableWalker#35955

Merged
jcouv merged 9 commits intodotnet:masterfrom
cston:annotations-plus
Jun 4, 2019
Merged

Initial support for [AllowNull], [DisallowNull], [MaybeNull], [NotNull] in NullableWalker#35955
jcouv merged 9 commits intodotnet:masterfrom
cston:annotations-plus

Conversation

@cston
Copy link
Copy Markdown
Contributor

@cston cston commented May 24, 2019

Some support for simple pre- and post-condition nullable annotations on by-value parameters and return types.


Added to this PR:

  • attributes on ref/out parameters
  • test behavior on OHI
  • test behavior for implementers of a method
  • enforcing attributes from PEMethodSymbol
  • test attribute on ref-returning methods (ignored)

A number of scenarios and tests will be covered in later PRs:

@cston
Copy link
Copy Markdown
Contributor Author

cston commented May 25, 2019

cc @jaredpar, @jcouv #Closed

@cston cston force-pushed the annotations-plus branch from d08eabf to 47626d8 Compare May 25, 2019 06:33
@cston cston force-pushed the annotations-plus branch from b83c04c to c498357 Compare May 25, 2019 08:13
@cston cston marked this pull request as ready for review May 25, 2019 16:43
@cston cston requested a review from a team as a code owner May 25, 2019 16:43
NullableFlowState state;
if (type.CanContainNull())
{
if ((annotations & FlowAnalysisAnnotations.MaybeNull) != 0)
Copy link
Copy Markdown
Member

@chsienki chsienki May 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did LDM confirm that MaybeNull takes precedence over NotNull? #Resolved

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems sensible (conservative/safe to return the worst case state).
I took a note in dotnet/csharplang#2201 to let LDM know.


In reply to: 288212991 [](ancestors = 288212991)

@"using System.Runtime.CompilerServices;
class Program
{
[return: MaybeNull] static T F1<T>(T t) => t;
Copy link
Copy Markdown
Member

@chsienki chsienki May 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add some tests where a user specifies both MaybeNull and NotNull, and show the behavior? #Resolved

var type = typeWithAnnotations.Type;
if (type is null)
{
return new TypeWithState(null, NullableFlowState.MaybeNull);
Copy link
Copy Markdown
Member

@jcouv jcouv May 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return new TypeWithState(null, NullableFlowState.MaybeNull); [](start = 16, length = 60)

📝 This line is not covered. Not sure if it could be. #Closed

}
else
{
state = typeWithAnnotations.NullableAnnotation switch
Copy link
Copy Markdown
Member

@jcouv jcouv May 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typeWithAnnotations.NullableAnnotation switch [](start = 28, length = 45)

📝 I expected we'd return typeWithAnnotations.ToTypeWithState() here. #Closed

}
static void M2(int? i2)
{
F2(i2);
Copy link
Copy Markdown
Member

@jcouv jcouv May 28, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

F2(i2); [](start = 8, length = 7)

I expected a warning here (we're passing a maybe-null state into a parameter marked with [DisallowNull]). Or maybe the warning is disallowing this attribute on non-generic types...
Filed #36009 #Resolved

@jcouv jcouv self-assigned this May 29, 2019
}
}

public TypeWithAnnotations ApplyLValueAnnotations(TypeWithAnnotations declaredType, FlowAnalysisAnnotations flowAnalysisAnnotations = FlowAnalysisAnnotations.None)
Copy link
Copy Markdown
Contributor Author

@cston cston May 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private static #Closed

@cston
Copy link
Copy Markdown
Contributor Author

cston commented May 29, 2019

b5f348d LGTM
#Closed

@jcouv jcouv added this to the 16.2.P3 milestone May 29, 2019
@jcouv jcouv force-pushed the annotations-plus branch from 1c88e40 to 13b37a0 Compare May 29, 2019 00:51
@jcouv
Copy link
Copy Markdown
Member

jcouv commented May 29, 2019

@dotnet/roslyn-compiler for review.
#Closed

F1(x).ToString(); // 2
F1(y).ToString();
F2(x).ToString(); // 3
F2(y).ToString(); // 4
Copy link
Copy Markdown
Member

@jcouv jcouv May 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// 4 [](start = 25, length = 5)

📝 incorrect comment actually, missing diagnostic #Resolved

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I agree. The attribute was meaningless where written, as the return type could have been written T?. So I think it is safe to ignore it in analysis.


In reply to: 288763015 [](ancestors = 288763015)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having said that, we should have more tests for reading from assembly images with meaningful annotations.


In reply to: 289210231 [](ancestors = 289210231,288763015)

@gafter gafter self-requested a review May 29, 2019 22:12
// x1.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(11, 9),
// (12,9): warning CS8602: Dereference of a possibly null reference.
// y1.ToString();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

y1.ToString(); [](start = 27, length = 14)

📝 I'm investigating this. I'll push a commit to fix.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is correct. The argument type and state is object?, so that is what we infer for T. Then we report the constraint failure. Since we don't use constraints in type inference, I believe this is correct.


In reply to: 288805395 [](ancestors = 288805395)

@jcouv jcouv force-pushed the annotations-plus branch from 185d7f3 to 575bdf4 Compare May 30, 2019 19:09
{
var type = typeWithAnnotations.Type;
if (type is null)
{
Copy link
Copy Markdown
Member

@gafter gafter May 30, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand the purpose of this if statement. If it isn't necessary, please remove it. #Resolved

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

static void F2<T>([AllowNull]T t) where T : class { }

I think there should be a warning here, as this attribute doesn't actually allow anything (see errors on calls to F2 below), while it could have been written simply T?. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:22170 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

static void F1([DisallowNull]out string t) => throw null!;

Warning scenario? #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:22802 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@jcouv
Copy link
Copy Markdown
Member

jcouv commented May 30, 2019

            Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void A.F2<object>(object? t)").WithLocation(9, 12)

You're correct. The problem with loading from PE is only for the return value. It's a simple fix, so I'll include in this PR (next commit).


In reply to: 497494525 [](ancestors = 497494525)


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:22791 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

[return: MaybeNull] static T F2<T>(T t) where T : class => t;

I think the attributes on F2, F3, and F4 deserve warnings. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:22877 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

[return: MaybeNull] static string F1() => string.Empty;

Both of these deserve a warning. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:23248 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

[return: MaybeNull] static int F1() => 1;

Both deserve warnings. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:23273 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

[return: MaybeNull] public static T F2<T>(T t) where T : class => t;

This deserves a warning (because you could have written T?) #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:23296 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

static void F2<T>(T t, [MaybeNull]out T t2) where T : class { t2 = t; }

F2, F3, and F4 deserve a warning. Probably F5 too. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:23394 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

static void F2([MaybeNull]out string? t) => throw null!;

Probably deserves a warning. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:23708 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

static void F1([MaybeNull]out int i) => throw null!;

Both probably deserve a warning. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:23736 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

public static void F2<T>(T t, [MaybeNull]out T t2) where T : class => throw null!;

Probably deserves a warning. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:23763 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@gafter
Copy link
Copy Markdown
Member

gafter commented May 30, 2019

Generally looks good, but I think it would be good to have warnings where the attributes make no sense or could be expressed directly in annotations. It would be good to have a couple of cross-compilation tests. If you want to defer that by adding issues tracking that additional work I'd be OK with this as is (Iteration 6) #Resolved

@jcouv
Copy link
Copy Markdown
Member

jcouv commented May 31, 2019

    if (t5 == null) return;

I linked to issue on applying attributes to nullable value types.


In reply to: 497492449 [](ancestors = 497492449)


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:22657 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)

@jcouv jcouv force-pushed the annotations-plus branch from 185641c to e02ea67 Compare May 31, 2019 01:03
@jcouv
Copy link
Copy Markdown
Member

jcouv commented May 31, 2019

    F1(x); // 2 TODO2

My bad. It's right indeed


In reply to: 497485383 [](ancestors = 497485383)


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:22404 in 575bdf4. [](commit_id = 575bdf4, deletion_comment = False)


private static TypeWithAnnotations ApplyRValueAnnotations(TypeWithAnnotations declaredType, FlowAnalysisAnnotations flowAnalysisAnnotations)
{
if ((flowAnalysisAnnotations & FlowAnalysisAnnotations.NotNull) == FlowAnalysisAnnotations.NotNull)
Copy link
Copy Markdown
Contributor Author

@cston cston May 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

== FlowAnalysisAnnotations.NotNull [](start = 76, length = 34)

The comparison style should be consistent between this method and the one above. #Resolved

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change, but the reason for this comparison style is that NotNull is two bits, whereas DisallowNull is only one bit (so simpler comparison works ok there)


In reply to: 289486556 [](ancestors = 289486556)

else if ((flowAnalysisAnnotations & FlowAnalysisAnnotations.MaybeNull) == FlowAnalysisAnnotations.MaybeNull)
{
var typeSymbol = declaredType.Type;
if (!declaredType.NullableAnnotation.IsNotAnnotated() || (typeSymbol.IsValueType && typeSymbol.IsNullableType()))
Copy link
Copy Markdown
Contributor Author

@cston cston May 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!declaredType.NullableAnnotation.IsNotAnnotated() [](start = 20, length = 49)

declaredType.NullableAnnotation.IsAnnotated() #Resolved

return declaredType;
}

return TypeWithAnnotations.Create(typeSymbol, NullableAnnotation.NotAnnotated, declaredType.CustomModifiers);
Copy link
Copy Markdown
Contributor Author

@cston cston May 31, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting helper methods to share code between this method and the one above. For instance, this could be return AsNotAnnotated(declaredType); #Resolved

@cston
Copy link
Copy Markdown
Contributor Author

cston commented May 31, 2019

    s7.ToString();

// 8 #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs:23583 in e02ea67. [](commit_id = e02ea67, deletion_comment = False)

@jcouv jcouv closed this May 31, 2019
@jcouv jcouv reopened this May 31, 2019
@cston
Copy link
Copy Markdown
Contributor Author

cston commented May 31, 2019

1ab07a0 LGTM

Copy link
Copy Markdown
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:shipit:

@gafter
Copy link
Copy Markdown
Member

gafter commented Jun 4, 2019

Iteration 9 LGTM #Closed

@jcouv jcouv merged commit 090e52e into dotnet:master Jun 4, 2019
@jaredpar jaredpar mentioned this pull request Jun 4, 2019
23 tasks
@cston cston deleted the annotations-plus branch March 15, 2024 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants