Make nullable analysis conditional on whether the user

enabled the nullable feature, either on the command line
or via a nullable enable directive.
上级 0197e110
......@@ -101,7 +101,7 @@ public TypeWithAnnotations GetInferredReturnType(ConversionsBase conversions, Nu
useSiteDiagnostics.Add(info);
}
}
if (nullableState == null)
if (nullableState == null || !Binder.Compilation.RunNullableWalker)
{
return InferredReturnType.TypeWithAnnotations;
}
......
......@@ -120,6 +120,12 @@ internal Conversions Conversions
/// </summary>
private HashSet<SyntaxTree> _lazyCompilationUnitCompletedTrees;
/// <summary>
/// Run the nullable walker during the flow analysis passes. True if the project-level nullable
/// context option is set, or if any file file enables nullable or just the nullable warnings.
/// </summary>
private ThreeState _lazyRunNullableWalker;
public override string Language
{
get
......@@ -171,9 +177,12 @@ internal override CommonAnonymousTypeManager CommonAnonymousTypeManager
internal bool FeatureStrictEnabled => Feature("strict") != null;
/// <summary>
/// True if we should enable nullable analysis in this compilation.
/// True if we should enable nullable semantic analysis in this compilation.
/// </summary>
internal bool NullableAnalysisEnabled => Feature("run-nullable-analysis") is "true";
// Note that this is intentionally not conditioned off RunNullableWalker currently. Once we fully enable
// the nullable analysis semantic model feature and feel confident in it, we only run the analysis if
// the feature flag is set.
internal bool NullableSemanticAnalysisEnabled => Feature("run-nullable-analysis") is "true";
/// <summary>
/// True when the "peverify-compat" feature flag is set or the language version is below C# 7.2.
......@@ -183,6 +192,34 @@ internal override CommonAnonymousTypeManager CommonAnonymousTypeManager
/// </summary>
internal bool IsPeVerifyCompatEnabled => LanguageVersion < LanguageVersion.CSharp7_2 || Feature("peverify-compat") != null;
internal bool RunNullableWalker
{
get
{
if (!_lazyRunNullableWalker.HasValue())
{
if (Options.NullableContextOptions != NullableContextOptions.Disable)
{
_lazyRunNullableWalker = ThreeState.True;
return true;
}
foreach (var syntaxTree in SyntaxTrees)
{
if (((CSharpSyntaxTree)syntaxTree).HasNullableEnables())
{
_lazyRunNullableWalker = ThreeState.True;
return true;
}
}
_lazyRunNullableWalker = ThreeState.False;
}
return _lazyRunNullableWalker.Value();
}
}
/// <summary>
/// The language version that was used to parse the syntax trees of this compilation.
/// </summary>
......
......@@ -1443,8 +1443,8 @@ protected void GuardedAddBoundTreeForStandaloneSyntax(SyntaxNode syntax, BoundNo
// cached nodes for the root syntax node, and the existing snapshot manager must be null.
Debug.Assert((IsSpeculativeSemanticModel && manager is null) ||
(!IsSpeculativeSemanticModel &&
(manager is null && (!Compilation.NullableAnalysisEnabled || syntax != Root)) ||
(manager is object && syntax == Root && Compilation.NullableAnalysisEnabled && _lazySnapshotManager is null)));
(manager is null && (!Compilation.NullableSemanticAnalysisEnabled || syntax != Root)) ||
(manager is object && syntax == Root && Compilation.NullableSemanticAnalysisEnabled && _lazySnapshotManager is null)));
if (!IsSpeculativeSemanticModel && manager is object)
{
_lazySnapshotManager = manager;
......@@ -1846,7 +1846,7 @@ protected void EnsureRootBoundForNullabilityIfNecessary()
DiagnosticBag diagnostics = _ignoredDiagnostics;
// If we're in DEBUG mode, always enable the analysis, but throw away the results
if (!Compilation.NullableAnalysisEnabled)
if (!Compilation.NullableSemanticAnalysisEnabled)
{
#if DEBUG
diagnostics = new DiagnosticBag();
......@@ -1871,7 +1871,7 @@ protected void EnsureRootBoundForNullabilityIfNecessary()
// In DEBUG mode, we don't want to increase test run times, so if
// nullable analysis isn't enabled and some node has already been bound
// we assume we've already done this test binding and just return
|| (!Compilation.NullableAnalysisEnabled && _guardedNodeMap.Count > 0)
|| (!Compilation.NullableSemanticAnalysisEnabled && _guardedNodeMap.Count > 0)
#endif
)
{
......@@ -1914,7 +1914,7 @@ void bindAndRewrite(bool takeSnapshots)
boundRoot = RewriteNullableBoundNodesWithSnapshots(boundRoot, binder, diagnostics, takeSnapshots, out var snapshotManager);
#if DEBUG
// Don't actually cache the results if the nullable analysis is not enabled in debug mode.
if (!Compilation.NullableAnalysisEnabled) return;
if (!Compilation.NullableSemanticAnalysisEnabled) return;
#endif
GuardedAddBoundTreeForStandaloneSyntax(bindableRoot, boundRoot, snapshotManager);
}
......
......@@ -1651,7 +1651,7 @@ internal static BoundBlock BindMethodBody(MethodSymbol method, TypeCompilationSt
bodyBinder.ValidateIteratorMethods(diagnostics);
BoundNode methodBody = bodyBinder.BindMethodBody(syntaxNode, diagnostics);
if (bodyBinder.Compilation.NullableAnalysisEnabled)
if (bodyBinder.Compilation.NullableSemanticAnalysisEnabled)
{
// Currently, we're passing an empty DiagnosticBag here because the flow analysis pass later will
// also run the nullable walker, and issue duplicate warnings. We should try to only run the pass
......
......@@ -376,7 +376,7 @@ public static void Analyze(CSharpCompilation compilation, MethodSymbol member, B
walker.Free();
}
if (compilation.LanguageVersion >= MessageID.IDS_FeatureNullableReferenceTypes.RequiredVersion())
if (compilation.LanguageVersion >= MessageID.IDS_FeatureNullableReferenceTypes.RequiredVersion() && compilation.RunNullableWalker)
{
NullableWalker.Analyze(compilation, member, node, diagnostics);
}
......
......@@ -475,7 +475,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
#if DEBUG
// https://github.com/dotnet/roslyn/issues/34993 Enable for all calls
if (compilation.NullableAnalysisEnabled)
if (compilation.NullableSemanticAnalysisEnabled)
{
DebugVerifier.Verify(analyzedNullabilitiesMap, snapshotManager, node);
}
......@@ -496,7 +496,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
var analyzedNullabilitiesMap = analyzedNullabilities.ToImmutable();
#if DEBUG
if (binder.Compilation.NullableAnalysisEnabled)
if (binder.Compilation.NullableSemanticAnalysisEnabled)
{
DebugVerifier.Verify(analyzedNullabilitiesMap, snapshotManagerOpt: null, node);
}
......@@ -511,7 +511,7 @@ protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
DiagnosticBag diagnostics)
{
var compilation = binder.Compilation;
if (compilation.LanguageVersion < MessageID.IDS_FeatureNullableReferenceTypes.RequiredVersion())
if (compilation.LanguageVersion < MessageID.IDS_FeatureNullableReferenceTypes.RequiredVersion() || !compilation.RunNullableWalker)
{
return;
}
......
......@@ -619,17 +619,31 @@ internal PragmaWarningState GetPragmaDirectiveWarningState(string id, int positi
return _lazyPragmaWarningStateMap.GetWarningState(id, position);
}
internal NullableContextState GetNullableContextState(int position)
private void EnsureNullableContextMapInitialized()
{
if (_lazyNullableContextStateMap == null)
{
// Create the #nullable directive map on demand.
Interlocked.CompareExchange(ref _lazyNullableContextStateMap, NullableContextStateMap.Create(this, IsGeneratedCode()), null);
}
}
internal NullableContextState GetNullableContextState(int position)
{
EnsureNullableContextMapInitialized();
return _lazyNullableContextStateMap.GetContextState(position);
}
/// <summary>
/// Returns true if there are any nullable directives that enable annotations, warnings, or both.
/// This does not include any restore directives.
/// </summary>
internal bool HasNullableEnables()
{
EnsureNullableContextMapInitialized();
return _lazyNullableContextStateMap.HasNullableEnables();
}
internal bool IsGeneratedCode()
{
if (_lazyIsGeneratedCode == ThreeState.Unknown)
......
......@@ -89,6 +89,23 @@ internal NullableContextState GetContextState(int position)
return context;
}
/// <summary>
/// Returns true if any of the NullableContexts in this map enable annotations, warnings, or both.
/// This does not include any restore directives.
/// </summary>
internal bool HasNullableEnables()
{
foreach (var context in _contexts)
{
if (context.AnnotationsState == true || context.WarningsState == true)
{
return true;
}
}
return false;
}
private static ImmutableArray<NullableContextState> GetContexts(SyntaxTree tree, bool isGeneratedCode)
{
var previousContext = GetContextForFileStart(position: 0, isGeneratedCode: isGeneratedCode);
......
......@@ -105747,5 +105747,190 @@ class Program
var comp = CreateCompilation(source);
comp.VerifyDiagnostics();
}
[Fact]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirective()
{
var source = @"
class C
{
void M(object? o1)
{
object o2 = o1;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (6,21): warning CS8600: Converting null literal or possible null value to non-nullable type.
// object o2 = o1;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "o1").WithLocation(6, 21));
Assert.True(comp.RunNullableWalker);
}
[Theory]
[InlineData("")]
[InlineData("annotations")]
[InlineData("warnings")]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirective_WithNullDisables(string directive)
{
var source = $@"
#nullable disable {directive}
class C
{{
void M(object? o1)
{{
object o2 = o1;
}}
}}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(directive == "warnings" ?
DiagnosticDescription.None :
new[] {
// (5,18): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void M(object? o1)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(5, 18)
});
Assert.True(comp.RunNullableWalker);
}
[Fact]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirectiveOff_WithNullableEnable()
{
RunNullableWalker_GlobalDirectiveOff_WithNullableDirective("",
// (7,21): warning CS8600: Converting null literal or possible null value to non-nullable type.
// object o2 = o1;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "o1").WithLocation(7, 21));
}
[Fact]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirectiveOff_WithNullableEnableAnnotations()
{
RunNullableWalker_GlobalDirectiveOff_WithNullableDirective("annotations");
}
[Fact]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirectiveOff_WithNullableEnableWarnings()
{
RunNullableWalker_GlobalDirectiveOff_WithNullableDirective("warnings",
// (5,18): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void M(object? o1)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(5, 18));
}
private void RunNullableWalker_GlobalDirectiveOff_WithNullableDirective(string directive, params DiagnosticDescription[] expectedDiagnostics)
{
var source = $@"
#nullable enable {directive}
class C
{{
void M(object? o1)
{{
object o2 = o1;
}}
}}";
var comp = CreateCompilation(source, options: WithNonNullTypesFalse());
comp.VerifyDiagnostics(expectedDiagnostics);
Assert.True(comp.RunNullableWalker);
}
[Theory]
[InlineData("disable")]
[InlineData("disable annotations")]
[InlineData("disable warnings")]
[InlineData("restore")]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirectiveOff_WithNonInfluencingNullableDirectives(string directive)
{
var source = $@"
#nullable {directive}
class C
{{
void M(object? o1)
{{
object o2 = o1;
}}
}}";
var comp = CreateCompilation(source, options: WithNonNullTypesFalse());
comp.VerifyDiagnostics(
// (5,18): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void M(object? o1)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(5, 18));
Assert.False(comp.RunNullableWalker);
}
[Fact]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirectiveOff_MultipleFiles_NullableEnable()
{
RunNullableWalker_GlobalDirectiveOff_MultipleFiles("",
// (4,18): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void M(object? o1)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(4, 18),
// (7,21): warning CS8600: Converting null literal or possible null value to non-nullable type.
// object o4 = o3;
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "o3").WithLocation(7, 21));
}
[Fact]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirectiveOff_MultipleFiles_NullableEnableAnnotations()
{
RunNullableWalker_GlobalDirectiveOff_MultipleFiles("annotations",
// (4,18): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void M(object? o1)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(4, 18));
}
[Fact]
[WorkItem(36131, "https://github.com/dotnet/roslyn/issues/36131")]
public void RunNullableWalker_GlobalDirectiveOff_MultipleFiles_NullableEnableWarnings()
{
RunNullableWalker_GlobalDirectiveOff_MultipleFiles("warnings",
// (4,18): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void M(object? o1)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(4, 18),
// (5,18): warning CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
// void M(object? o3)
Diagnostic(ErrorCode.WRN_MissingNonNullTypesContextForAnnotation, "?").WithLocation(5, 18));
}
private void RunNullableWalker_GlobalDirectiveOff_MultipleFiles(string directive, params DiagnosticDescription[] expectedDiagnostics)
{
var source1 = @"
class C
{
void M(object? o1)
{
object o2 = o1;
}
}";
var source2 = $@"
#nullable enable {directive}
class D
{{
void M(object? o3)
{{
object o4 = o3;
}}
}}";
var comp = CreateCompilation(new[] { source1, source2 }, options: WithNonNullTypesFalse());
comp.VerifyDiagnostics(expectedDiagnostics);
Assert.True(comp.RunNullableWalker);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册