|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics
{
public class NullableContextTests : CSharpTestBase
{
[InlineData("#nullable enable", NullableContextOptions.Disable, NullableContext.Enabled)]
[InlineData("#nullable enable", NullableContextOptions.Annotations, NullableContext.Enabled)]
[InlineData("#nullable enable", NullableContextOptions.Warnings, NullableContext.Enabled)]
[InlineData("#nullable enable", NullableContextOptions.Enable, NullableContext.Enabled)]
[InlineData("#nullable enable warnings", NullableContextOptions.Disable, NullableContext.WarningsEnabled | NullableContext.AnnotationsContextInherited)]
[InlineData("#nullable enable warnings", NullableContextOptions.Warnings, NullableContext.WarningsEnabled | NullableContext.AnnotationsContextInherited)]
[InlineData("#nullable enable warnings", NullableContextOptions.Annotations, NullableContext.Enabled | NullableContext.AnnotationsContextInherited)]
[InlineData("#nullable enable warnings", NullableContextOptions.Enable, NullableContext.Enabled | NullableContext.AnnotationsContextInherited)]
[InlineData("#nullable enable annotations", NullableContextOptions.Disable, NullableContext.AnnotationsEnabled | NullableContext.WarningsContextInherited)]
[InlineData("#nullable enable annotations", NullableContextOptions.Warnings, NullableContext.Enabled | NullableContext.WarningsContextInherited)]
[InlineData("#nullable enable annotations", NullableContextOptions.Annotations, NullableContext.AnnotationsEnabled | NullableContext.WarningsContextInherited)]
[InlineData("#nullable enable annotations", NullableContextOptions.Enable, NullableContext.Enabled | NullableContext.WarningsContextInherited)]
[InlineData("#nullable disable", NullableContextOptions.Disable, NullableContext.Disabled)]
[InlineData("#nullable disable", NullableContextOptions.Annotations, NullableContext.Disabled)]
[InlineData("#nullable disable", NullableContextOptions.Warnings, NullableContext.Disabled)]
[InlineData("#nullable disable", NullableContextOptions.Enable, NullableContext.Disabled)]
[InlineData("#nullable disable warnings", NullableContextOptions.Disable, NullableContext.Disabled | NullableContext.AnnotationsContextInherited)]
[InlineData("#nullable disable warnings", NullableContextOptions.Warnings, NullableContext.Disabled | NullableContext.AnnotationsContextInherited)]
[InlineData("#nullable disable warnings", NullableContextOptions.Annotations, NullableContext.AnnotationsEnabled | NullableContext.AnnotationsContextInherited)]
[InlineData("#nullable disable warnings", NullableContextOptions.Enable, NullableContext.AnnotationsEnabled | NullableContext.AnnotationsContextInherited)]
[InlineData("#nullable disable annotations", NullableContextOptions.Disable, NullableContext.Disabled | NullableContext.WarningsContextInherited)]
[InlineData("#nullable disable annotations", NullableContextOptions.Warnings, NullableContext.WarningsEnabled | NullableContext.WarningsContextInherited)]
[InlineData("#nullable disable annotations", NullableContextOptions.Annotations, NullableContext.Disabled | NullableContext.WarningsContextInherited)]
[InlineData("#nullable disable annotations", NullableContextOptions.Enable, NullableContext.WarningsEnabled | NullableContext.WarningsContextInherited)]
[Theory]
public void NullableContextExplicitlySpecifiedAndRestoredInFile(string pragma, NullableContextOptions globalContext, NullableContext expectedContext)
{
var source = $@"
{pragma}
class C
{{
#nullable restore
void M() {{}}
}}";
var comp = CreateCompilation(source, options: WithNullable(globalContext));
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var classDeclPosition = syntaxTree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().Single().SpanStart;
var methodDeclPosition = syntaxTree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single().SpanStart;
Assert.Equal(expectedContext, model.GetNullableContext(classDeclPosition));
// The context at the start of the file should always be inherited and match the global context
var restoredContext = ((NullableContext)globalContext) | NullableContext.ContextInherited;
Assert.Equal(restoredContext, model.GetNullableContext(0));
Assert.Equal(restoredContext, model.GetNullableContext(methodDeclPosition));
}
[Fact]
public void NullableContextMultipleFiles()
{
var source1 = @"
#nullable enable
partial class C
{
void M1() {};
}";
var source2 = @"
partial class C
{
#nullable enable
void M2();
}";
var comp = CreateCompilation(new[] { source1, source2 }, options: WithNullableEnable());
var syntaxTree1 = comp.SyntaxTrees[0];
var model1 = comp.GetSemanticModel(syntaxTree1);
var syntaxTree2 = comp.SyntaxTrees[1];
var model2 = comp.GetSemanticModel(syntaxTree2);
var classDecl1 = syntaxTree1.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().Single().SpanStart;
var classDecl2 = syntaxTree2.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().Single().SpanStart;
Assert.Equal(NullableContext.Enabled, model1.GetNullableContext(classDecl1));
Assert.Equal(NullableContext.Enabled | NullableContext.ContextInherited, model2.GetNullableContext(classDecl2));
}
[Fact]
public void NullableContextOptionsFlags()
{
Assert.True(NullableContextOptions.Enable.AnnotationsEnabled());
Assert.True(NullableContextOptions.Enable.WarningsEnabled());
Assert.True(NullableContextOptions.Annotations.AnnotationsEnabled());
Assert.False(NullableContextOptions.Annotations.WarningsEnabled());
Assert.False(NullableContextOptions.Warnings.AnnotationsEnabled());
Assert.True(NullableContextOptions.Warnings.WarningsEnabled());
Assert.False(NullableContextOptions.Disable.AnnotationsEnabled());
Assert.False(NullableContextOptions.Disable.WarningsEnabled());
}
[Fact]
public void NullableContextFlags()
{
AssertEnabledForInheritance(NullableContext.Disabled, warningsEnabled: false, annotationsEnabled: false);
AssertEnabledForInheritance(NullableContext.WarningsEnabled, warningsEnabled: true, annotationsEnabled: false);
AssertEnabledForInheritance(NullableContext.AnnotationsEnabled, warningsEnabled: false, annotationsEnabled: true);
AssertEnabledForInheritance(NullableContext.Enabled, warningsEnabled: true, annotationsEnabled: true);
void AssertEnabledForInheritance(NullableContext context, bool warningsEnabled, bool annotationsEnabled)
{
Assert.Equal(warningsEnabled, context.WarningsEnabled());
Assert.Equal(annotationsEnabled, context.AnnotationsEnabled());
Assert.False(context.WarningsInherited());
Assert.False(context.AnnotationsInherited());
var warningsInherited = context | NullableContext.WarningsContextInherited;
Assert.Equal(warningsEnabled, warningsInherited.WarningsEnabled());
Assert.Equal(annotationsEnabled, warningsInherited.AnnotationsEnabled());
Assert.True(warningsInherited.WarningsInherited());
Assert.False(warningsInherited.AnnotationsInherited());
var annotationsInherited = context | NullableContext.AnnotationsContextInherited;
Assert.Equal(warningsEnabled, annotationsInherited.WarningsEnabled());
Assert.Equal(annotationsEnabled, annotationsInherited.AnnotationsEnabled());
Assert.False(annotationsInherited.WarningsInherited());
Assert.True(annotationsInherited.AnnotationsInherited());
var contextInherited = context | NullableContext.ContextInherited;
Assert.Equal(warningsEnabled, contextInherited.WarningsEnabled());
Assert.Equal(annotationsEnabled, contextInherited.AnnotationsEnabled());
Assert.True(contextInherited.WarningsInherited());
Assert.True(contextInherited.AnnotationsInherited());
}
}
// See also CommandLineTests.NullableAnalysisFlags().
[ConditionalFact(typeof(NoUsedAssembliesValidation), Reason = "GetEmitDiagnostics affects result")]
public void NullableAnalysisFlags_01()
{
var source =
@"#nullable enable
class Program
{
const object? C1 = null;
const object? C2 = null;
#nullable enable
static object F1() => C1;
#nullable disable
static object F2() => C2;
}";
var expectedAnalyzedKeysAll = new[] { ".cctor", ".ctor", "F1", "F2" };
var expectedAnalyzedKeysDefault =
#if DEBUG
new[] { ".cctor", ".ctor", "F1", "F2" };
#else
new[] { ".cctor", "F1" };
#endif
verify(parseOptions: TestOptions.Regular, expectedAnalyzedKeysDefault);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", null), expectedAnalyzedKeysDefault);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "always"), expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "never"));
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "ALWAYS"), expectedAnalyzedKeysDefault); // unrecognized value (incorrect case) ignored
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "NEVER"), expectedAnalyzedKeysDefault); // unrecognized value (incorrect case) ignored
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "true"), expectedAnalyzedKeysDefault); // unrecognized value ignored
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "false"), expectedAnalyzedKeysDefault); // unrecognized value ignored
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "unknown"), expectedAnalyzedKeysDefault); // unrecognized value ignored
void verify(CSharpParseOptions parseOptions, params string[] expectedAnalyzedKeys)
{
var comp = CreateCompilation(source, parseOptions: parseOptions);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
if (expectedAnalyzedKeys.Length > 0)
{
comp.VerifyDiagnostics(
// (7,27): warning CS8603: Possible null reference return.
// static object F1() => C1;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "C1").WithLocation(7, 27));
}
else
{
comp.VerifyDiagnostics();
}
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData);
AssertEx.Equal(expectedAnalyzedKeys, actualAnalyzedKeys);
}
}
[ConditionalFact(typeof(NoUsedAssembliesValidation), Reason = "GetEmitDiagnostics affects result")]
public void NullableAnalysisFlags_02()
{
var source =
@"#nullable enable
class Program
{
const object? C1 = null;
const object? C2 = null;
#nullable enable
static void F1(object obj = C1) { }
#nullable disable
static void F2(object obj = C2) { }
}";
var expectedAnalyzedKeysAll = new[] { ".cctor", ".ctor", "= C1", "= C2", "F1", "F2" };
var expectedAnalyzedKeysDefault =
#if DEBUG
new[] { ".cctor", ".ctor", "= C1", "= C2", "F1", "F2" };
#else
new[] { ".cctor", "= C1", "F1" };
#endif
verify(parseOptions: TestOptions.Regular, expectedAnalyzedKeysDefault);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", null), expectedAnalyzedKeysDefault);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "always"), expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "never"));
void verify(CSharpParseOptions parseOptions, params string[] expectedAnalyzedKeys)
{
var comp = CreateCompilation(source, parseOptions: parseOptions);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
if (expectedAnalyzedKeys.Length > 0)
{
comp.VerifyDiagnostics(
// (7,33): warning CS8625: Cannot convert null literal to non-nullable reference type.
// static void F1(object obj = C1) { }
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "C1").WithLocation(7, 33));
}
else
{
comp.VerifyDiagnostics();
}
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData);
AssertEx.Equal(expectedAnalyzedKeys, actualAnalyzedKeys);
}
}
[ConditionalFact(typeof(NoUsedAssembliesValidation), Reason = "GetEmitDiagnostics affects result")]
public void NullableAnalysisFlags_03()
{
var sourceA =
@"#nullable enable
public class A : System.Attribute
{
public A(object obj) { }
public const object? C1 = null;
public const object? C2 = null;
}";
var refA = CreateCompilation(sourceA).EmitToImageReference();
var sourceB =
@"#nullable enable
[A(A.C1)]
struct B1
{
}
#nullable disable
[A(A.C2)]
struct B2
{
}";
var expectedAnalyzedKeysAll = new[] { ".cctor", ".cctor", "A(A.C1)", "A(A.C2)" };
var expectedAnalyzedKeysDefault =
#if DEBUG
new[] { ".cctor", ".cctor", "A(A.C1)", "A(A.C2)" };
#else
new[] { "A(A.C1)" };
#endif
verify(parseOptions: TestOptions.Regular, expectedAnalyzedKeysDefault);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", null), expectedAnalyzedKeysDefault);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "always"), expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "never"));
void verify(CSharpParseOptions parseOptions, params string[] expectedAnalyzedKeys)
{
var comp = CreateCompilation(sourceB, references: new[] { refA }, parseOptions: parseOptions);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
if (expectedAnalyzedKeys.Length > 0)
{
comp.VerifyDiagnostics(
// (2,4): warning CS8625: Cannot convert null literal to non-nullable reference type.
// [A(A.C1)]
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "A.C1").WithLocation(2, 4));
}
else
{
comp.VerifyDiagnostics();
}
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData);
AssertEx.Equal(expectedAnalyzedKeys, actualAnalyzedKeys);
}
}
[Fact]
public void NullableAnalysisFlags_MethodBodySemanticModel()
{
var source =
@"#nullable enable
class Program
{
static object F(object? obj)
{
if (obj == null) return null;
return obj;
}
}";
var expectedAnalyzedKeysAll = new[] { "F" };
verify(parseOptions: TestOptions.Regular, expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", null), expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "always"), expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "never"), expectedFlowState: false);
void verify(CSharpParseOptions parseOptions, bool expectedFlowState, params string[] expectedAnalyzedKeys)
{
var comp = CreateCompilation(source, parseOptions: parseOptions);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var syntax = syntaxTree.GetRoot().DescendantNodes().OfType<ReturnStatementSyntax>().Skip(1).Single();
Assert.Equal("return obj;", syntax.ToString());
var typeInfo = model.GetTypeInfo(syntax.Expression);
var expectedNullability = expectedFlowState ? Microsoft.CodeAnalysis.NullableFlowState.NotNull : Microsoft.CodeAnalysis.NullableFlowState.None;
Assert.Equal(expectedNullability, typeInfo.Nullability.FlowState);
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData);
AssertEx.Equal(expectedAnalyzedKeys, actualAnalyzedKeys);
}
}
[Fact]
public void NullableAnalysisFlags_AttributeSemanticModel()
{
var source =
@"#nullable enable
class A : System.Attribute
{
public A(object obj) { }
public static object F;
}
[A(A.F = null)]
class B
{
}";
var expectedAnalyzedKeysAll = new[] { "A(A.F = null)" };
verify(parseOptions: TestOptions.Regular, expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", null), expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "always"), expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "never"), expectedFlowState: false);
void verify(CSharpParseOptions parseOptions, bool expectedFlowState, params string[] expectedAnalyzedKeys)
{
var comp = CreateCompilation(source, parseOptions: parseOptions);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var syntax = syntaxTree.GetRoot().DescendantNodes().OfType<AttributeArgumentSyntax>().Single();
Assert.Equal("A.F = null", syntax.ToString());
var typeInfo = model.GetTypeInfo(syntax.Expression);
var expectedNullability = expectedFlowState ? Microsoft.CodeAnalysis.NullableFlowState.MaybeNull : Microsoft.CodeAnalysis.NullableFlowState.None;
Assert.Equal(expectedNullability, typeInfo.Nullability.FlowState);
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData);
AssertEx.Equal(expectedAnalyzedKeys, actualAnalyzedKeys);
}
}
[Fact]
public void NullableAnalysisFlags_InitializerSemanticModel()
{
var source =
@"#nullable enable
class Program
{
static object F;
static void M(object arg = (F = null)) { }
}";
var expectedAnalyzedKeysAll = new[] { "arg" };
verify(parseOptions: TestOptions.Regular, expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", null), expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "always"), expectedFlowState: true, expectedAnalyzedKeysAll);
verify(parseOptions: TestOptions.Regular.WithFeature("run-nullable-analysis", "never"), expectedFlowState: false);
void verify(CSharpParseOptions parseOptions, bool expectedFlowState, params string[] expectedAnalyzedKeys)
{
var comp = CreateCompilation(source, parseOptions: parseOptions);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var syntax = syntaxTree.GetRoot().DescendantNodes().OfType<EqualsValueClauseSyntax>().First().Value;
Assert.Equal("(F = null)", syntax.ToString());
var typeInfo = model.GetTypeInfo(syntax);
var expectedNullability = expectedFlowState ? Microsoft.CodeAnalysis.NullableFlowState.MaybeNull : Microsoft.CodeAnalysis.NullableFlowState.None;
Assert.Equal(expectedNullability, typeInfo.Nullability.FlowState);
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData);
AssertEx.Equal(expectedAnalyzedKeys, actualAnalyzedKeys);
}
}
public readonly struct NullableDirectives
{
internal readonly string[] Directives;
internal readonly NullableContextState.State ExpectedWarningsState;
internal readonly NullableContextState.State ExpectedAnnotationsState;
internal NullableDirectives(string[] directives, NullableContextState.State expectedWarningsState, NullableContextState.State expectedAnnotationsState)
{
Directives = directives;
ExpectedWarningsState = expectedWarningsState;
ExpectedAnnotationsState = expectedAnnotationsState;
}
public override string ToString()
{
var builder = new StringBuilder();
foreach (var str in Directives) builder.AppendLine(str);
return builder.ToString();
}
}
private static readonly NullableDirectives[] s_nullableDirectives = new[]
{
new NullableDirectives(new string[0], NullableContextState.State.Unknown, NullableContextState.State.Unknown),
new NullableDirectives(new[] { "#nullable disable" }, NullableContextState.State.Disabled, NullableContextState.State.Disabled),
new NullableDirectives(new[] { "#nullable enable" }, NullableContextState.State.Enabled, NullableContextState.State.Enabled),
new NullableDirectives(new[] { "#nullable restore" }, NullableContextState.State.ExplicitlyRestored, NullableContextState.State.ExplicitlyRestored),
new NullableDirectives(new[] { "#nullable disable annotations" }, NullableContextState.State.Unknown, NullableContextState.State.Disabled),
new NullableDirectives(new[] { "#nullable enable warnings", "#nullable disable annotations", }, NullableContextState.State.Enabled, NullableContextState.State.Disabled),
new NullableDirectives(new[] { "#nullable restore warnings", "#nullable disable annotations" }, NullableContextState.State.ExplicitlyRestored, NullableContextState.State.Disabled),
new NullableDirectives(new[] { "#nullable enable annotations" }, NullableContextState.State.Unknown, NullableContextState.State.Enabled),
new NullableDirectives(new[] { "#nullable disable warnings", "#nullable enable annotations" }, NullableContextState.State.Disabled, NullableContextState.State.Enabled),
new NullableDirectives(new[] { "#nullable restore warnings", "#nullable enable annotations" }, NullableContextState.State.ExplicitlyRestored, NullableContextState.State.Enabled),
new NullableDirectives(new[] { "#nullable restore annotations" }, NullableContextState.State.Unknown, NullableContextState.State.ExplicitlyRestored),
new NullableDirectives(new[] { "#nullable enable warnings" , "#nullable restore annotations" }, NullableContextState.State.Enabled, NullableContextState.State.ExplicitlyRestored),
new NullableDirectives(new[] { "#nullable disable warnings", "#nullable restore annotations" }, NullableContextState.State.Disabled, NullableContextState.State.ExplicitlyRestored),
new NullableDirectives(new[] { "#nullable disable warnings" }, NullableContextState.State.Disabled, NullableContextState.State.Unknown),
new NullableDirectives(new[] { "#nullable enable warnings" }, NullableContextState.State.Enabled, NullableContextState.State.Unknown),
new NullableDirectives(new[] { "#nullable restore warnings" }, NullableContextState.State.ExplicitlyRestored, NullableContextState.State.Unknown),
};
// AnalyzeMethodsInEnabledContextOnly_01_Data is split due to https://github.com/dotnet/roslyn/issues/50337
public static IEnumerable<object[]> AnalyzeMethodsInEnabledContextOnly_01_Data1()
{
var projectSettings = new[]
{
(NullableContextOptions?)null,
NullableContextOptions.Disable,
};
foreach (var projectSetting in projectSettings)
{
foreach (var classDirectives in s_nullableDirectives)
{
foreach (var methodDirectives in s_nullableDirectives)
{
yield return new object[] { projectSetting, classDirectives, methodDirectives };
}
}
}
}
public static IEnumerable<object[]> AnalyzeMethodsInEnabledContextOnly_01_Data2()
{
var projectSettings = new[]
{
NullableContextOptions.Warnings,
NullableContextOptions.Annotations,
NullableContextOptions.Enable,
};
foreach (var projectSetting in projectSettings)
{
foreach (var classDirectives in s_nullableDirectives)
{
foreach (var methodDirectives in s_nullableDirectives)
{
yield return new object[] { projectSetting, classDirectives, methodDirectives };
}
}
}
}
[Theory]
[MemberData(nameof(AnalyzeMethodsInEnabledContextOnly_01_Data1))]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_01A(NullableContextOptions? projectContext, NullableDirectives classDirectives, NullableDirectives methodDirectives)
{
AnalyzeMethodsInEnabledContextOnly_01_Execute(projectContext, classDirectives, methodDirectives);
}
[Theory]
[MemberData(nameof(AnalyzeMethodsInEnabledContextOnly_01_Data2))]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_01B(NullableContextOptions? projectContext, NullableDirectives classDirectives, NullableDirectives methodDirectives)
{
AnalyzeMethodsInEnabledContextOnly_01_Execute(projectContext, classDirectives, methodDirectives);
}
private static void AnalyzeMethodsInEnabledContextOnly_01_Execute(NullableContextOptions? projectContext, NullableDirectives classDirectives, NullableDirectives methodDirectives)
{
var sourceA =
@"#nullable enable
public class A
{
public static void M(object obj) { }
}";
var refA = CreateCompilation(sourceA).EmitToImageReference();
var sourceB =
$@"{classDirectives}
static class B
{{
{methodDirectives}
static void Main() {{ A.M(null); }}
}}";
var expectedWarningsStateForMethod = CombineState(methodDirectives.ExpectedWarningsState, classDirectives.ExpectedWarningsState);
var expectedAnnotationsStateForMethod = CombineState(methodDirectives.ExpectedAnnotationsState, classDirectives.ExpectedAnnotationsState);
bool isNullableEnabledForProject = projectContext != null && (projectContext.Value & NullableContextOptions.Warnings) != 0;
bool isNullableEnabledForMethod = IsNullableEnabled(expectedWarningsStateForMethod, isNullableEnabledForProject);
var options = TestOptions.ReleaseDll;
if (projectContext != null) options = options.WithNullableContextOptions(projectContext.Value);
var comp = CreateCompilation(sourceB, options: options, references: new[] { refA });
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
if (isNullableEnabledForMethod)
{
comp.VerifyDiagnostics(
// (4,30): warning CS8625: Cannot convert null literal to non-nullable reference type.
// static void Main() { A.M(null); }
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null"));
}
else
{
comp.VerifyDiagnostics();
}
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true);
Assert.Equal(isNullableEnabledForMethod, actualAnalyzedKeys.Contains("Main"));
var tree = (CSharpSyntaxTree)comp.SyntaxTrees[0];
var syntaxNodes = tree.GetRoot().DescendantNodes();
verifyContextState(tree, syntaxNodes.OfType<ClassDeclarationSyntax>().Single(), classDirectives.ExpectedWarningsState, classDirectives.ExpectedAnnotationsState);
verifyContextState(tree, syntaxNodes.OfType<MethodDeclarationSyntax>().Single(), expectedWarningsStateForMethod, expectedAnnotationsStateForMethod);
static void verifyContextState(CSharpSyntaxTree tree, CSharpSyntaxNode syntax, NullableContextState.State expectedWarningsState, NullableContextState.State expectedAnnotationsState)
{
var actualState = tree.GetNullableContextState(syntax.SpanStart);
Assert.Equal(expectedWarningsState, actualState.WarningsState);
Assert.Equal(expectedAnnotationsState, actualState.AnnotationsState);
}
}
private static NullableContextState.State CombineState(NullableContextState.State currentState, NullableContextState.State previousState)
{
return currentState == NullableContextState.State.Unknown ? previousState : currentState;
}
private static bool IsNullableEnabled(NullableContextState.State state, bool isNullableEnabledForProject)
{
return state switch
{
NullableContextState.State.Enabled => true,
NullableContextState.State.Disabled => false,
_ => isNullableEnabledForProject,
};
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_02()
{
var source =
@"#pragma warning disable 219
static class Program
{
#nullable disable
static void M1
#nullable enable
()
{
object obj = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { "M1" },
// (9,22): warning CS8600: Converting null literal or possible null value to non-nullable type.
// object obj = null;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(9, 22));
source =
@"#pragma warning disable 219
static class Program
{
#nullable disable
static void M2()
#nullable enable
{
object obj = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { "M2" },
// (8,22): warning CS8600: Converting null literal or possible null value to non-nullable type.
// object obj = null;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 22));
source =
@"#pragma warning disable 219
static class Program
{
#nullable disable
static void M3()
{
#nullable enable
object obj = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { "M3" },
// (8,22): warning CS8600: Converting null literal or possible null value to non-nullable type.
// object obj = null;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 22));
source =
@"#pragma warning disable 219
static class Program
{
#nullable disable
static void M4()
{
#nullable disable
object obj = null;
}
#nullable enable
}";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"#pragma warning disable 219
static class Program
{
#nullable enable
static void M5
#nullable disable
()
{
object obj = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { "M5" });
source =
@"#pragma warning disable 219
static class Program
{
static void M6
#nullable enable
()
#nullable disable
{
object obj = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { "M6" });
source =
@"static class Program
{
static object M7()
#nullable enable
=> default(object);
}";
verify(source, expectedAnalyzedKeys: new[] { "M7" });
source =
@"static class Program
{
#nullable disable
static object M8() =>
default(
#nullable enable
object);
}";
verify(source, expectedAnalyzedKeys: new[] { "M8" });
static void verify(string source, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
var tree = (CSharpSyntaxTree)comp.SyntaxTrees[0];
var methodDeclarations = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().ToArray();
foreach (var methodDeclaration in methodDeclarations)
{
bool expectedAnalysis = expectedAnalyzedKeys.Contains(methodDeclaration.Identifier.Text);
bool actualAnalysis = tree.IsNullableAnalysisEnabled(methodDeclaration.Span).Value;
Assert.Equal(expectedAnalysis, actualAnalysis);
}
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_03()
{
var source =
@"class Program
{
Program(object o) { o = null; }
}
#nullable enable
";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"#nullable enable
#nullable disable
class Program
{
Program(object o) { o = null; }
}";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"class Program
{
Program(object o) { o = null; }
#nullable enable
}";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"#pragma warning disable 414
class Program
{
#nullable disable
object F1 = null;
#nullable enable
}";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"#pragma warning disable 414
class Program
{
#nullable disable
object F1 = null;
#nullable enable
static object F2 = null;
}";
verify(source, expectedAnalyzedKeys: new[] { ".cctor" },
// (7,24): warning CS8625: Cannot convert null literal to non-nullable reference type.
// static object F2 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 24));
source =
@"#pragma warning disable 414
class Program
{
#nullable enable
object F1 = null;
#nullable disable
static object F2 = null;
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" },
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F1 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17));
source =
@"#pragma warning disable 414
static class Program
{
#nullable disable
static object F1 = null;
#nullable enable
static Program() { F1 = null; }
}";
verify(source, expectedAnalyzedKeys: new[] { ".cctor" });
source =
@"#pragma warning disable 414
static class Program
{
#nullable enable
static object F1 = null;
#nullable disable
static Program() { F1 = null; }
}";
verify(source, expectedAnalyzedKeys: new[] { ".cctor" },
// (5,24): warning CS8625: Cannot convert null literal to non-nullable reference type.
// static object F1 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 24));
source =
@"#pragma warning disable 414
class Program
{
#nullable disable
object F1 = null;
#nullable enable
Program() { F1 = null; }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" });
source =
@"#pragma warning disable 414
class Program
{
#nullable enable
object F1 = null;
#nullable disable
Program() { F1 = null; }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" },
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F1 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17));
source =
@"#pragma warning disable 414
class Program
{
#nullable enable
object F1 = null;
#nullable disable
object F2 = null;
#nullable enable
object F3 = null;
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" },
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F1 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17),
// (9,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F3 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(9, 17));
source =
@"#pragma warning disable 414
class Program
{
#nullable disable
object F1 = null;
#nullable enable
object F2() => null;
#nullable disable
object F3 = null;
}";
verify(source, expectedAnalyzedKeys: new[] { "F2" },
// (7,20): warning CS8603: Possible null reference return.
// object F2() => null;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(7, 20));
source =
@"#pragma warning disable 169
class Program
{
#nullable enable
object F1;
#nullable disable
Program() { }
Program(object obj) : base() { }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor", ".ctor" });
source =
@"#pragma warning disable 169
class Program
{
#nullable enable
object F1;
#nullable disable
Program() { }
Program(object obj) : this() { }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" });
source =
@"#pragma warning disable 169
class Program
{
#nullable enable
object F1;
#nullable disable
Program() { }
#nullable enable
Program(object obj) : this() { }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor", ".ctor" });
source =
@"#pragma warning disable 169
class Program
{
#nullable disable
object F1;
Program() { }
#nullable enable
Program(object obj) : this() { }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" });
source =
@"#pragma warning disable 169
class Program
{
#nullable disable
object F1;
#nullable enable
Program() { }
#nullable disable
Program(object obj) : this() { }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" });
source =
@"#pragma warning disable 169
struct S
{
#nullable enable
object F1;
S(object obj) : this() { }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" },
// (6,5): warning CS8618: Non-nullable field 'F1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the field as nullable.
// S(object obj) : this() { }
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "S").WithArguments("field", "F1").WithLocation(6, 5)
);
source =
@"#pragma warning disable 169
struct S
{
#nullable enable
object F1;
#nullable disable
S(object obj) : this() { }
}";
verify(source, expectedAnalyzedKeys: new string[0]);
static void verify(string source, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_04()
{
var source1 =
@"#pragma warning disable 414
partial class Program
{
object F1 = null;
}";
var source2 =
@"#pragma warning disable 414
partial class Program
{
#nullable disable
object F2 = null;
}";
var source3 =
@"#pragma warning disable 414
partial class Program
{
#nullable enable
object F3 = null;
}";
var source4 =
@"#pragma warning disable 414
partial class Program
{
#nullable restore
object F4 = null;
}";
var options = TestOptions.ReleaseDll.WithNullableContextOptions(NullableContextOptions.Disable);
verify(new[] { source1, source2 }, options, new string[0]);
verify(new[] { source1, source3 }, options, new[] { ".ctor" },
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F3 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17));
verify(new[] { source1, source4 }, options, new string[0]);
verify(new[] { source2, source3 }, options, new[] { ".ctor" },
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F3 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17));
verify(new[] { source2, source4 }, options, new string[0]);
verify(new[] { source3, source4 }, options, new[] { ".ctor" },
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F3 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17));
verify(new[] { source1, source2, source3, source4 }, options, new[] { ".ctor" },
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F3 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17));
verify(new[] { source1, source2, source3, source4 }, TestOptions.ReleaseDll.WithNullableContextOptions(NullableContextOptions.Enable), new[] { ".ctor" },
// (4,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F1 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 17),
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F3 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17),
// (5,17): warning CS8625: Cannot convert null literal to non-nullable reference type.
// object F4 = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 17));
static void verify(string[] source, CSharpCompilationOptions options, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var comp = CreateCompilation(source, options: options);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
}
[ConditionalFact(typeof(NoUsedAssembliesValidation), Reason = "GetEmitDiagnostics affects result")]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_05()
{
var source =
@"class Program
{
#nullable disable
object P1 { get; set; }
#nullable enable
static object P2 { get; set; }
}";
verify(source, expectedAnalyzedKeys: new[] { ".cctor" },
// (6,19): warning CS8618: Non-nullable property 'P2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
// static object P2 { get; set; }
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "P2").WithArguments("property", "P2").WithLocation(6, 19));
source =
@"class Program
{
#nullable enable
object P1 { get; set; }
#nullable disable
static object P2 { get; set; }
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" },
// (4,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
// object P1 { get; set; }
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "P1").WithArguments("property", "P1").WithLocation(4, 12));
source =
@"class Program
{
#nullable enable
object P1 { get; }
object P2 { get { return 2; } set { } }
object P3 => 3;
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor", "get_P2", "get_P3", "set_P2" },
// (4,12): warning CS8618: Non-nullable property 'P1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
// object P1 { get; }
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "P1").WithArguments("property", "P1").WithLocation(4, 12));
source =
@"#pragma warning disable 67
delegate void D();
class Program
{
#nullable disable
event D E1;
#nullable enable
static event D E2;
}";
verify(source, expectedAnalyzedKeys: new[] { ".cctor" },
// (8,20): warning CS8618: Non-nullable event 'E2' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable.
// static event D E2;
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "E2").WithArguments("event", "E2").WithLocation(8, 20));
source =
@"#pragma warning disable 67
delegate void D();
class Program
{
#nullable enable
event D E1;
#nullable disable
static event D E2;
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" },
// (6,13): warning CS8618: Non-nullable event 'E1' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the event as nullable.
// event D E1;
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "E1").WithArguments("event", "E1").WithLocation(6, 13));
static void verify(string source, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_06()
{
var source =
@"#pragma warning disable 414
class Program
{
#nullable enable
object F = 1;
#nullable disable
~Program()
{
F = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor" });
source =
@"#pragma warning disable 414
class Program
{
#nullable disable
object F = 1;
#nullable enable
~Program()
{
F = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { "Finalize" });
source =
@"#pragma warning disable 414
class Program
{
#nullable enable
object F = 1;
~Program()
{
F = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor", "Finalize" },
// (8,13): warning CS8625: Cannot convert null literal to non-nullable reference type.
// F = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 13));
static void verify(string source, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_07()
{
var source =
@"class Program
{
#nullable enable
object P
#nullable disable
#nullable enable
=> null;
object Q
#nullable disable
=> null;
}";
verify(source, expectedAnalyzedKeys: new[] { "get_P" },
// (7,12): warning CS8603: Possible null reference return.
// => null;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(7, 12));
source =
@"class Program
{
#nullable enable
object P
{
get { return null; }
#nullable disable
set { value = null; }
}
#nullable enable
object Q
{
#nullable disable
get => null;
#nullable enable
set => value = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { "get_P", "set_Q" },
// (6,22): warning CS8603: Possible null reference return.
// get { return null; }
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(6, 22),
// (16,24): warning CS8600: Converting null literal or possible null value to non-nullable type.
// set => value = null;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(16, 24));
source =
@"#pragma warning disable 414
delegate void D();
class Program
{
#nullable enable
D _e = null!;
D _f = null!;
#nullable disable
event D E
{
#nullable enable
add { _e = null; }
#nullable disable
remove { _e = null; }
}
event D F
{
#nullable disable
add => _f = null;
#nullable enable
remove => _f = null;
}
}";
verify(source, expectedAnalyzedKeys: new[] { ".ctor", "add_E", "remove_F" },
// (12,20): warning CS8625: Cannot convert null literal to non-nullable reference type.
// add { _e = null; }
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(12, 20),
// (21,24): warning CS8625: Cannot convert null literal to non-nullable reference type.
// remove => _f = null;
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(21, 24));
static void verify(string source, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_08()
{
var source =
@"class A
{
}
class B
{
public static explicit operator B(A a) => null;
}";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"class A
{
}
class B
{
#nullable enable
public static explicit operator B(A a) => null;
}";
verify(source, expectedAnalyzedKeys: new[] { "op_Explicit" },
// (7,47): warning CS8603: Possible null reference return.
// public static explicit operator B(A a) => null;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(7, 47));
source =
@"class C
{
public static C operator~(C c) => null;
}";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"class C
{
#nullable enable
public static C operator~(C c) => null;
}";
verify(source, expectedAnalyzedKeys: new[] { "op_OnesComplement" },
// (4,39): warning CS8603: Possible null reference return.
// public static C operator~(C c) => null;
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(4, 39));
static void verify(string source, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_09()
{
var source =
@"#nullable enable
record R;
";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"#nullable enable
record R();
";
verify(source, expectedAnalyzedKeys: new[] { "R..ctor()" });
source =
@"record R(object P
#nullable enable
);
";
verify(source, expectedAnalyzedKeys: new[] { "R..ctor(System.Object P)" });
source =
@"record A;
#nullable disable
record B0 : A
#nullable enable
{
}
#nullable disable
record B1() : A
#nullable enable
{
}
#nullable disable
record B2() : A
#nullable enable
();
";
verify(source, expectedAnalyzedKeys: new[] { "B2..ctor()" });
source =
@"record A;
#nullable disable
record B0
#nullable enable
:
#nullable disable
A;
#nullable disable
record B1 :
#nullable enable
A
#nullable disable
;
#nullable disable
record B2() :
#nullable enable
A
#nullable disable
();
";
verify(source, expectedAnalyzedKeys: new string[0]);
source =
@"record A(object P)
{
#nullable enable
internal static object F(object obj) => obj;
#nullable disable
}
#nullable disable
record B1() : A(
F(null));
record B2() : A(
#nullable enable
F(null));
";
verify(source, expectedAnalyzedKeys: new[] { "B2..ctor()", "System.Object A.F(System.Object obj)" },
// (12,7): warning CS8625: Cannot convert null literal to non-nullable reference type.
// F(null));
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(12, 7));
static void verify(string source, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var comp = CreateCompilation(new[] { source, IsExternalInitTypeDefinition });
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
var actualAnalyzedKeys = GetIsNullableEnabledMethods(comp.TestOnlyCompilationData, key => ((MethodSymbol)key).ToTestDisplayString());
AssertEx.Equal(expectedAnalyzedKeys, actualAnalyzedKeys);
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_10()
{
var source1 =
@"partial class Program
{
#nullable enable
partial void F1(ref object o1) { o1 = null; }
partial void F2(ref object o2);
#nullable disable
partial void F3(ref object o3) { o3 = null; }
partial void F4(ref object o4);
}";
var source2 =
@"partial class Program
{
#nullable disable
partial void F1(ref object o1);
partial void F2(ref object o2) { o2 = null; }
#nullable enable
partial void F3(ref object o3);
partial void F4(ref object o4) { o4 = null; }
}";
var comp = CreateCompilation(new[] { source1, source2 });
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(
// (4,43): warning CS8625: Cannot convert null literal to non-nullable reference type.
// partial void F1(ref object o1) { o1 = null; }
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 43),
// (8,43): warning CS8625: Cannot convert null literal to non-nullable reference type.
// partial void F4(ref object o4) { o4 = null; }
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 43));
var expectedAnalyzedKeys = new[] { "F1", "F4" };
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_11()
{
var source =
@"partial class Program
{
#nullable enable
partial void F1(object x = null);
partial void F2(object y = null) { }
#nullable disable
partial void F1(object x = null) { }
partial void F2(object y = null);
}";
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(
// (4,32): warning CS8625: Cannot convert null literal to non-nullable reference type.
// partial void F1(object x = null);
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 32),
// (5,28): warning CS1066: The default value specified for parameter 'y' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
// partial void F2(object y = null) { }
Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "y").WithArguments("y").WithLocation(5, 28),
// (5,32): warning CS8625: Cannot convert null literal to non-nullable reference type.
// partial void F2(object y = null) { }
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 32),
// (7,28): warning CS1066: The default value specified for parameter 'x' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
// partial void F1(object x = null) { }
Diagnostic(ErrorCode.WRN_DefaultValueForUnconsumedLocation, "x").WithArguments("x").WithLocation(7, 28));
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true);
AssertEx.Equal(new[] { "= null", "= null", "F2" }, actualAnalyzedKeys);
}
[ConditionalFact(typeof(NoUsedAssembliesValidation), Reason = "GetEmitDiagnostics affects result")]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_12()
{
var source =
@"using System.Runtime.InteropServices;
class Program
{
#nullable enable
const object? C1 = null;
const object? C2 = null;
const object? C3 = null;
const object? C4 = null;
#nullable disable
static void F1(
[DefaultParameterValue(
#nullable enable
C1
#nullable disable
)]
[A] object x)
{
}
static void F2(
#nullable enable
[DefaultParameterValue(C2)]
#nullable disable
[A] object x)
{
}
#nullable disable
static void F3(
[DefaultParameterValue(C3)]
#nullable enable
[A] object x
#nullable disable
)
{
}
static void F4(
[DefaultParameterValue(C4)]
[A] object x)
#nullable enable
#nullable disable
{
}
}
class A : System.Attribute
{
}";
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics();
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true);
var expectedAnalyzedKeys = new[]
{
".cctor",
@"[DefaultParameterValue(
#nullable enable
C1
#nullable disable
)]
[A] object x",
@"[DefaultParameterValue(C2)]
#nullable disable
[A] object x",
@"[DefaultParameterValue(C3)]
#nullable enable
[A] object x",
"A",
@"DefaultParameterValue(
#nullable enable
C1
#nullable disable
)",
"DefaultParameterValue(C2)",
"F1",
"F2",
"F3",
"F4",
};
AssertEx.Equal(expectedAnalyzedKeys, actualAnalyzedKeys);
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_13()
{
var source =
@"#nullable disable
object x = typeof(string);
if (x == null) { }
_ = x.ToString();
#nullable enable
";
verify(new[] { source }, projectContext: null, expectedAnalyzedKeys: new string[0]);
source =
@"#nullable disable
object x = typeof(string);
if (x == null) { }
#nullable enable
_ = x.ToString();
";
verify(new[] { source }, projectContext: null, expectedAnalyzedKeys: new[] { "<Main>$" },
// (5,5): warning CS8602: Dereference of a possibly null reference.
// _ = x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(5, 5));
source =
@"#nullable disable
object x = typeof(string);
if (x == null) { }
#nullable restore
_ = x.ToString();
";
verify(new[] { source }, projectContext: NullableContextOptions.Warnings, expectedAnalyzedKeys: new[] { "<Main>$" },
// (5,5): warning CS8602: Dereference of a possibly null reference.
// _ = x.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(5, 5));
source =
@"object x = typeof(string);
if (x == null) { }
#nullable enable
#nullable disable
_ = x.ToString();
";
verify(new[] { source }, projectContext: null, expectedAnalyzedKeys: new string[0]);
source =
@"object x = A.F();
if (x == null) { }
_ = x.ToString();
static class A
{
#nullable enable
internal static object F() => new object();
}";
verify(new[] { source }, projectContext: null, expectedAnalyzedKeys: new[] { "F" });
source =
@"object x = typeof(A);
class A
{
#nullable enable
#nullable disable
}
if (x == null) { }
_ = x.ToString();
";
verify(new[] { source }, projectContext: null, expectedAnalyzedKeys: new string[0],
// (7,1): error CS8803: Top-level statements must precede namespace and type declarations.
// if (x == null) { }
Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "if (x == null) { }").WithLocation(7, 1));
source =
@"#nullable enable
object x = A.F();
#nullable disable
static class A
{
internal static object F() => new object();
}
if (x == null) { }
_ = x.ToString();
";
verify(new[] { source }, projectContext: null, expectedAnalyzedKeys: new[] { "<Main>$" },
// (8,1): error CS8803: Top-level statements must precede namespace and type declarations.
// if (x == null) { }
Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "if (x == null) { }").WithLocation(8, 1));
source =
@"object x = F();
if (x == null) { }
_ = x.ToString();
static object F()
{
#nullable enable
return new object();
#nullable disable
}";
verify(new[] { source }, projectContext: null, expectedAnalyzedKeys: new[] { "<Main>$" });
var sourceA =
@"static class A
{
#nullable enable
internal static object F() => new object();
}";
var sourceB =
@"object x = A.F();
if (x == null) { }
_ = x.ToString();
";
verify(new[] { sourceA, sourceB }, projectContext: null, expectedAnalyzedKeys: new[] { "F" });
static void verify(string[] source, NullableContextOptions? projectContext, string[] expectedAnalyzedKeys, params DiagnosticDescription[] expectedDiagnostics)
{
var options = TestOptions.ReleaseExe;
if (projectContext != null) options = options.WithNullableContextOptions(projectContext.GetValueOrDefault());
var comp = CreateCompilation(source, options: options);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
comp.VerifyDiagnostics(expectedDiagnostics);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_MethodBodySemanticModel_01()
{
var source =
@"class Program
{
#nullable enable
static object F1(object o1)
{
if (o1 == null) { }
return o1;
}
#nullable disable
static object F2(object o2)
{
if (o2 == null) { }
return o2;
}
}";
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var returnStatements = syntaxTree.GetRoot().DescendantNodes().OfType<ReturnStatementSyntax>().ToArray();
var syntax = returnStatements[0];
Assert.Equal("return o1;", syntax.ToString());
var typeInfo = model.GetTypeInfo(syntax.Expression);
Assert.Equal(Microsoft.CodeAnalysis.NullableFlowState.MaybeNull, typeInfo.Nullability.FlowState);
syntax = returnStatements[1];
Assert.Equal("return o2;", syntax.ToString());
typeInfo = model.GetTypeInfo(syntax.Expression);
Assert.Equal(Microsoft.CodeAnalysis.NullableFlowState.None, typeInfo.Nullability.FlowState);
var expectedAnalyzedKeys = new[] { "F1" };
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_MethodBodySemanticModel_02()
{
var source =
@"class Program
{
#nullable enable
object F;
Program(object obj)
#nullable disable
{
if (obj == null) { }
F = obj;
}
}";
verify(source, Microsoft.CodeAnalysis.NullableFlowState.MaybeNull, ".ctor");
source =
@"class Program
{
#nullable enable
object F;
#nullable disable
Program(object obj)
{
if (obj == null) { }
F = obj;
}
}";
verify(source, Microsoft.CodeAnalysis.NullableFlowState.MaybeNull, ".ctor");
source =
@"class Program
{
#nullable enable
object F = new object();
#nullable disable
Program(object obj)
{
if (obj == null) { }
F = obj;
}
}";
verify(source, Microsoft.CodeAnalysis.NullableFlowState.MaybeNull, ".ctor");
static void verify(string source, Microsoft.CodeAnalysis.NullableFlowState expectedFlowState, params string[] expectedAnalyzedKeys)
{
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var syntax = syntaxTree.GetRoot().DescendantNodes().OfType<AssignmentExpressionSyntax>().Single().Right;
Assert.Equal("obj", syntax.ToString());
var typeInfo = model.GetTypeInfo(syntax);
Assert.Equal(expectedFlowState, typeInfo.Nullability.FlowState);
AssertEx.Equal(expectedAnalyzedKeys, GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true));
AssertEx.Equal(expectedAnalyzedKeys, GetIsNullableEnabledMethods(comp.TestOnlyCompilationData));
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_AttributeSemanticModel_01()
{
var source =
@"class A : System.Attribute
{
#nullable enable
public A(object obj) { }
public static object F1;
public static object F2;
}
#nullable enable
[A(A.F1 = null)]
class B1
{
}
#nullable disable
[A(A.F2 = null)]
class B2
{
}";
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var attributeArguments = syntaxTree.GetRoot().DescendantNodes().OfType<AttributeArgumentSyntax>().ToArray();
verify(attributeArguments[0], "A.F1 = null", Microsoft.CodeAnalysis.NullableFlowState.MaybeNull);
verify(attributeArguments[1], "A.F2 = null", Microsoft.CodeAnalysis.NullableFlowState.None);
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true);
AssertEx.Equal(new[] { "A(A.F1 = null)" }, actualAnalyzedKeys);
void verify(AttributeArgumentSyntax syntax, string expectedText, Microsoft.CodeAnalysis.NullableFlowState expectedFlowState)
{
Assert.Equal(expectedText, syntax.ToString());
var typeInfo = model.GetTypeInfo(syntax.Expression);
Assert.Equal(expectedFlowState, typeInfo.Nullability.FlowState);
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_AttributeSemanticModel_02()
{
var source =
@"using System.Runtime.InteropServices;
class Program
{
#nullable enable
const object? C1 = null;
const object? C2 = null;
#nullable disable
static void F1([DefaultParameterValue(
#nullable enable
C1
#nullable disable
)] object x)
{
}
#nullable disable
static void F2([DefaultParameterValue(C2)]
#nullable enable
object x
#nullable disable
)
{
}
}";
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var attributeArguments = syntaxTree.GetRoot().DescendantNodes().OfType<AttributeArgumentSyntax>().ToArray();
verify(attributeArguments[0], "C1", Microsoft.CodeAnalysis.NullableFlowState.MaybeNull);
verify(attributeArguments[1], "C2", Microsoft.CodeAnalysis.NullableFlowState.None);
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true);
var expectedAnalyzedKey =
@"DefaultParameterValue(
#nullable enable
C1
#nullable disable
)";
AssertEx.Equal(new[] { expectedAnalyzedKey }, actualAnalyzedKeys);
void verify(AttributeArgumentSyntax syntax, string expectedText, Microsoft.CodeAnalysis.NullableFlowState expectedFlowState)
{
Assert.Equal(expectedText, syntax.ToString());
var typeInfo = model.GetTypeInfo(syntax.Expression);
Assert.Equal(expectedFlowState, typeInfo.Nullability.FlowState);
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_InitializerSemanticModel_01()
{
var source =
@"class Program
{
#nullable enable
static object F1;
static object F2;
#nullable enable
static void M1(object o1 = (F1 = null)) { }
#nullable disable
static void M2(object o2 = (F2 = null)) { }
}";
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var equalsValueClauses = syntaxTree.GetRoot().DescendantNodes().OfType<EqualsValueClauseSyntax>().ToArray();
verify(equalsValueClauses[0], "(F1 = null)", Microsoft.CodeAnalysis.NullableFlowState.MaybeNull);
verify(equalsValueClauses[1], "(F2 = null)", Microsoft.CodeAnalysis.NullableFlowState.None);
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true);
AssertEx.Equal(new[] { "o1" }, actualAnalyzedKeys);
void verify(EqualsValueClauseSyntax syntax, string expectedText, Microsoft.CodeAnalysis.NullableFlowState expectedFlowState)
{
var value = syntax.Value;
Assert.Equal(expectedText, value.ToString());
var typeInfo = model.GetTypeInfo(value);
Assert.Equal(expectedFlowState, typeInfo.Nullability.FlowState);
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_InitializerSemanticModel_02()
{
var source =
@"#pragma warning disable 414
class A
{
#nullable disable
object F1 = null;
}
class B
{
#nullable disable
object F2 = null;
#nullable enable
object F3 = null;
}";
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var declarations = syntaxTree.GetRoot().DescendantNodes().OfType<FieldDeclarationSyntax>().Select(f => f.Declaration.Variables[0]).ToArray();
verify(declarations[0], "F1", Microsoft.CodeAnalysis.NullableFlowState.None);
verify(declarations[1], "F2", Microsoft.CodeAnalysis.NullableFlowState.MaybeNull);
verify(declarations[2], "F3", Microsoft.CodeAnalysis.NullableFlowState.MaybeNull);
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true);
AssertEx.Equal(new[] { "F2", "F3" }, actualAnalyzedKeys);
void verify(VariableDeclaratorSyntax syntax, string expectedText, Microsoft.CodeAnalysis.NullableFlowState expectedFlowState)
{
Assert.Equal(expectedText, syntax.Identifier.ValueText);
var typeInfo = model.GetTypeInfo(syntax.Initializer.Value);
Assert.Equal(expectedFlowState, typeInfo.Nullability.FlowState);
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_InitializerSemanticModel_03()
{
var source =
@"class A
{
#nullable disable
object P1 { get; set; } = null;
}
class B
{
#nullable disable
object P2 { get; set; } = null;
#nullable enable
object P3 { get; set; } = null;
}";
var comp = CreateCompilation(source);
comp.TestOnlyCompilationData = new NullableWalker.NullableAnalysisData();
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var declarations = syntaxTree.GetRoot().DescendantNodes().OfType<PropertyDeclarationSyntax>().ToArray();
verify(declarations[0], "P1", Microsoft.CodeAnalysis.NullableFlowState.None);
verify(declarations[1], "P2", Microsoft.CodeAnalysis.NullableFlowState.MaybeNull);
verify(declarations[2], "P3", Microsoft.CodeAnalysis.NullableFlowState.MaybeNull);
var actualAnalyzedKeys = GetNullableDataKeysAsStrings(comp.TestOnlyCompilationData, requiredAnalysis: true);
AssertEx.Equal(new[] { "P2", "P3" }, actualAnalyzedKeys);
void verify(PropertyDeclarationSyntax syntax, string expectedText, Microsoft.CodeAnalysis.NullableFlowState expectedFlowState)
{
Assert.Equal(expectedText, syntax.Identifier.ValueText);
var typeInfo = model.GetTypeInfo(syntax.Initializer.Value);
Assert.Equal(expectedFlowState, typeInfo.Nullability.FlowState);
}
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_SpeculativeSemanticModel_MethodBody()
{
var source =
@"class Program
{
#nullable disable
static void Main()
{
object obj = typeof(object);
}
#nullable enable
}";
VerifySpeculativeSemanticModel(source, null, "string", Microsoft.CodeAnalysis.NullableAnnotation.None);
source =
@"class Program
{
#nullable disable
static void Main()
{
object obj =
#nullable enable
typeof(object);
}
}";
VerifySpeculativeSemanticModel(source, null, "string", Microsoft.CodeAnalysis.NullableAnnotation.NotAnnotated);
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_SpeculativeSemanticModel_Initializer()
{
var source =
@"class Program
{
#nullable disable
static object F = typeof(object);
#nullable enable
}";
VerifySpeculativeSemanticModel(source, null, "string", Microsoft.CodeAnalysis.NullableAnnotation.None);
source =
@"class Program
{
#nullable disable
static object F =
#nullable enable
typeof(object);
}";
VerifySpeculativeSemanticModel(source, null, "string", Microsoft.CodeAnalysis.NullableAnnotation.NotAnnotated);
source =
@"class Program
{
#nullable disable
static object P { get; } = typeof(object);
#nullable enable
}";
VerifySpeculativeSemanticModel(source, null, "string", Microsoft.CodeAnalysis.NullableAnnotation.None);
source =
@"class Program
{
#nullable disable
static object P { get; } =
#nullable enable
typeof(object);
}";
VerifySpeculativeSemanticModel(source, null, "string", Microsoft.CodeAnalysis.NullableAnnotation.NotAnnotated);
}
[Fact]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_SpeculativeSemanticModel_Attribute()
{
var source =
@"class A : System.Attribute
{
internal A(object obj) { }
}
class Program
{
#nullable disable
[A(typeof(object)]
static void Main()
{
}
#nullable enable
}";
VerifySpeculativeSemanticModel(source, null, "string", Microsoft.CodeAnalysis.NullableAnnotation.None);
source =
@"class A : System.Attribute
{
internal A(object obj) { }
}
class Program
{
#nullable disable
[A(
#nullable enable
typeof(object)]
static void Main()
{
}
}";
VerifySpeculativeSemanticModel(source, null, "string", Microsoft.CodeAnalysis.NullableAnnotation.NotAnnotated);
}
[Theory]
[MemberData(nameof(AnalyzeMethodsInEnabledContextOnly_01_Data1))]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_SpeculativeSemanticModel_A(NullableContextOptions? projectContext, NullableDirectives sourceDirectives, NullableDirectives speculativeDirectives)
{
AnalyzeMethodsInEnabledContextOnly_SpeculativeSemanticModel_Execute(projectContext, sourceDirectives, speculativeDirectives);
}
[Theory]
[MemberData(nameof(AnalyzeMethodsInEnabledContextOnly_01_Data2))]
[WorkItem(49746, "https://github.com/dotnet/roslyn/issues/49746")]
public void AnalyzeMethodsInEnabledContextOnly_SpeculativeSemanticModel_B(NullableContextOptions? projectContext, NullableDirectives sourceDirectives, NullableDirectives speculativeDirectives)
{
AnalyzeMethodsInEnabledContextOnly_SpeculativeSemanticModel_Execute(projectContext, sourceDirectives, speculativeDirectives);
}
private static void AnalyzeMethodsInEnabledContextOnly_SpeculativeSemanticModel_Execute(NullableContextOptions? projectContext, NullableDirectives sourceDirectives, NullableDirectives speculativeDirectives)
{
// https://github.com/dotnet/roslyn/issues/50234: SyntaxTreeSemanticModel.IsNullableAnalysisEnabledAtSpeculativePosition()
// does not handle '#nullable restore'.
if (speculativeDirectives.ExpectedWarningsState == NullableContextState.State.ExplicitlyRestored) return;
var source =
$@"class Program
{{
{sourceDirectives}
static void Main()
{{
object obj = typeof(object);
}}
}}";
var typeName =
$@"{speculativeDirectives}
string";
var expectedWarningsState = CombineState(speculativeDirectives.ExpectedWarningsState, sourceDirectives.ExpectedWarningsState);
bool isNullableEnabledForProject = projectContext != null && (projectContext.Value & NullableContextOptions.Warnings) != 0;
Microsoft.CodeAnalysis.NullableAnnotation expectedAnnotation = IsNullableEnabled(expectedWarningsState, isNullableEnabledForProject) ?
Microsoft.CodeAnalysis.NullableAnnotation.NotAnnotated :
Microsoft.CodeAnalysis.NullableAnnotation.None;
VerifySpeculativeSemanticModel(source, projectContext, typeName, expectedAnnotation);
}
private static void VerifySpeculativeSemanticModel(string source, NullableContextOptions? projectContext, string typeName, Microsoft.CodeAnalysis.NullableAnnotation expectedAnnotation)
{
var options = TestOptions.ReleaseDll;
if (projectContext != null) options = options.WithNullableContextOptions(projectContext.GetValueOrDefault());
var comp = CreateCompilation(source, options: options);
var syntaxTree = comp.SyntaxTrees[0];
var model = comp.GetSemanticModel(syntaxTree);
var typeOf = syntaxTree.GetRoot().DescendantNodes().OfType<TypeOfExpressionSyntax>().Single();
var type = SyntaxFactory.ParseTypeName(typeName);
Assert.True(model.TryGetSpeculativeSemanticModel(typeOf.Type.SpanStart, type, out model, SpeculativeBindingOption.BindAsTypeOrNamespace));
var typeInfo = model.GetTypeInfo(type);
Assert.Equal(expectedAnnotation, typeInfo.Nullability.Annotation);
}
private static string[] GetNullableDataKeysAsStrings(object compilationData, bool requiredAnalysis = false)
{
return ((NullableWalker.NullableAnalysisData)compilationData).Data.
Where(pair => !requiredAnalysis || pair.Value.RequiredAnalysis).
Select(pair => GetNullableDataKeyAsString(pair.Key)).
OrderBy(key => key).
ToArray();
}
private static string[] GetIsNullableEnabledMethods(object compilationData, Func<object, string> toString = null)
{
toString ??= GetNullableDataKeyAsString;
return ((NullableWalker.NullableAnalysisData)compilationData).Data.
Where(pair => pair.Value.RequiredAnalysis && pair.Key is MethodSymbol method && method.IsNullableAnalysisEnabled()).
Select(pair => toString(pair.Key)).
OrderBy(key => key).
ToArray();
}
private static string GetNullableDataKeyAsString(object key) =>
key is Symbol symbol ? symbol.MetadataName : ((SyntaxNode)key).ToString();
}
}
|