diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 0518d806ad4feb5e6996b62c1ac283ee173103c1..39e813c6a55501e116099ff8461b39f98c40e244 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -6416,7 +6416,7 @@ void N(string? s) } }", MainDescription($"({FeaturesResources.parameter}) string? s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_may_be_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6434,7 +6434,7 @@ void N(string? s) } }", MainDescription($"({FeaturesResources.parameter}) string? s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_is_not_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6453,7 +6453,7 @@ void N() } }", MainDescription($"({FeaturesResources.field}) string? X.s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_may_be_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6473,7 +6473,7 @@ void N() } }", MainDescription($"({FeaturesResources.field}) string? X.s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_is_not_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6492,7 +6492,7 @@ void N() } }", MainDescription("string? X.S { get; set; }"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_may_be_null_here, "S"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "S"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6512,7 +6512,7 @@ void N() } }", MainDescription("string? X.S { get; set; }"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_is_not_null_here, "S"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "S"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6536,7 +6536,7 @@ void N() } }", MainDescription($"({FeaturesResources.local_variable}) string? s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_may_be_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6560,7 +6560,7 @@ void N() } }", MainDescription($"({FeaturesResources.local_variable}) string? s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_is_not_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6580,7 +6580,7 @@ void N() } }", MainDescription($"({FeaturesResources.local_variable}) string? s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_may_be_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_may_be_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6600,7 +6600,7 @@ void N() } }", MainDescription($"({FeaturesResources.local_variable}) string? s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_is_not_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] @@ -6658,7 +6658,7 @@ void N() } }", MainDescription($"({FeaturesResources.local_variable}) string s"), - NullabilityAnalysis(string.Format(CSharpFeaturesResources._0_is_not_null_here, "s"))); + NullabilityAnalysis(string.Format(FeaturesResources._0_is_not_null_here, "s"))); } [Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)] diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index 5158c6b331a84a5e2ba2040dae9e8d4298d73238..660ecb85318304556236081e8c807e321a873b37 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -574,12 +574,6 @@ Make 'ref struct' {Locked="ref"}{Locked="struct"} "ref" and "struct" are C# keywords and should not be localized. - - '{0}' is not null here. - - - '{0}' may be null here. - Assign 'out' parameters {Locked="out"} "out" is a C# keyword and should not be localized. diff --git a/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs b/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs index d1a707646bff302046e18e2528a09325ffb8215b..044912f97b9a2ef8e3a5c77911649e948bf750e7 100644 --- a/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs +++ b/src/Features/CSharp/Portable/QuickInfo/CSharpSemanticQuickInfoProvider.cs @@ -4,15 +4,13 @@ #nullable enable -using System.Collections.Immutable; +using System; using System.Composition; +using System.Diagnostics.CodeAnalysis; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.LanguageServices; -using Microsoft.CodeAnalysis.QuickInfo; -using System.Diagnostics.CodeAnalysis; -using System; using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.QuickInfo; namespace Microsoft.CodeAnalysis.CSharp.QuickInfo { @@ -66,37 +64,28 @@ protected override bool GetBindableNodeForTokenIndicatingPossibleIndexerAccess(S protected override bool ShouldCheckPreviousToken(SyntaxToken token) => !token.Parent.IsKind(SyntaxKind.XmlCrefAttribute); - protected override ImmutableArray TryGetNullabilityAnalysis(Workspace workspace, SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken) + protected override NullableFlowState GetNullabilityAnalysis(Workspace workspace, SemanticModel semanticModel, ISymbol symbol, SyntaxNode node, CancellationToken cancellationToken) { // Anything less than C# 8 we just won't show anything, even if the compiler could theoretically give analysis - var parseOptions = (CSharpParseOptions)token.SyntaxTree!.Options; + var parseOptions = (CSharpParseOptions)semanticModel.SyntaxTree!.Options; if (parseOptions.LanguageVersion < LanguageVersion.CSharp8) { - return default; + return NullableFlowState.None; } // If the user doesn't have nullable enabled, don't show anything. For now we're not trying to be more precise if the user has just annotations or just // warnings. If the user has annotations off then things that are oblivious might become non-null (which is a lie) and if the user has warnings off then // that probably implies they're not actually trying to know if their code is correct. We can revisit this if we have specific user scenarios. - var nullableContext = semanticModel.GetNullableContext(token.SpanStart); + var nullableContext = semanticModel.GetNullableContext(node.SpanStart); if (!nullableContext.WarningsEnabled() || !nullableContext.AnnotationsEnabled()) { - return default; - } - - var syntaxFacts = workspace.Services.GetLanguageServices(semanticModel.Language).GetRequiredService(); - var bindableParent = syntaxFacts.TryGetBindableParent(token); - var symbolInfo = bindableParent != null ? semanticModel.GetSymbolInfo(bindableParent, cancellationToken) : default; - - if (symbolInfo.Symbol == null || string.IsNullOrEmpty(symbolInfo.Symbol.Name)) - { - return default; + return NullableFlowState.None; } // Although GetTypeInfo can return nullability for uses of all sorts of things, it's not always useful for quick info. // For example, if you have a call to a method with a nullable return, the fact it can be null is already captured // in the return type shown -- there's no flow analysis information there. - switch (symbolInfo.Symbol) + switch (symbol) { // Ignore constant values for nullability flow state case IFieldSymbol { HasConstantValue: true }: return default; @@ -114,7 +103,7 @@ protected override ImmutableArray TryGetNullabilityAnalysis(Workspac return default; } - var typeInfo = bindableParent != null ? semanticModel.GetTypeInfo(bindableParent, cancellationToken) : default; + var typeInfo = semanticModel.GetTypeInfo(node, cancellationToken); // Nullability is a reference type only feature, value types can use // something like "int?" to be nullable but that ends up encasing as @@ -125,25 +114,7 @@ protected override ImmutableArray TryGetNullabilityAnalysis(Workspac return default; } - string? messageTemplate = null; - - if (typeInfo.Nullability.FlowState == NullableFlowState.NotNull) - { - messageTemplate = CSharpFeaturesResources._0_is_not_null_here; - } - else if (typeInfo.Nullability.FlowState == NullableFlowState.MaybeNull) - { - messageTemplate = CSharpFeaturesResources._0_may_be_null_here; - } - - if (messageTemplate != null) - { - return ImmutableArray.Create(new TaggedText(TextTags.Text, string.Format(messageTemplate, symbolInfo.Symbol.Name))); - } - else - { - return default; - } + return typeInfo.Nullability.FlowState; } } } diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf index ba30201576c77496e1c9b68e8489e9db40e9c891..d0d897ddea2d4fe60baf1785c724119b4a44bd40 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - {0} tady není null. - - - - '{0}' may be null here. - {0} tady může být null. - - asynchronous foreach statement asynchronní příkaz foreach diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf index 4a7b194567149898c4b985dfbd6c33f31de659b2..89d5435e78f849bd043a9bf1ae8bb8cd1c57f653 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - "{0}" ist hier nicht NULL. - - - - '{0}' may be null here. - "{0}" darf hier NULL sein. - - asynchronous foreach statement asynchrone foreach-Anweisung diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf index 4b6a0754218ab17482d5e733522ebd4f208087a3..08543f5dfffc0b5387bfcac621b4c6b0f8d8f9d1 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - "{0}" no es NULL aquí. - - - - '{0}' may be null here. - "{0}" puede ser NULL aquí. - - asynchronous foreach statement instrucción foreach asincrónica diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf index 3cb937f8604239f2d088364ad08413c21c7076f4..f9f264f8abb0c60f062f04408e9c30cc6d8bc961 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - '{0}' n'a pas une valeur null ici. - - - - '{0}' may be null here. - '{0}' a peut-être une valeur null ici. - - asynchronous foreach statement instruction foreach asynchrone diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf index 0b7e8bc921e06eed46ebe4a765da7b7fbce61dfa..87de8e4d96caf364fc1caacf88b3436a1b94e8e3 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - '{0}' non è Null in questo punto. - - - - '{0}' may be null here. - '{0}' può essere Null in questo punto. - - asynchronous foreach statement istruzione foreach asincrona diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf index 9768da58b06f952ece8cb7b7ea56a2012759c030..b29a57f61b8137aff2651c5ffb1ac738e435d235 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - ここでは、'{0}' は null ではありません。 - - - - '{0}' may be null here. - ここでは、'{0}' は null である可能性があります。 - - asynchronous foreach statement 非同期の foreach ステートメント diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf index 2f729533f0d0697be0f81971ea11dd0460ba8d08..a322722b068a13be9e3c9564bb93f03798d6e23f 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - '{0}'은(는) 여기에서 null이 아닙니다. - - - - '{0}' may be null here. - '{0}'은(는) 여기에서 null일 수 있습니다. - - asynchronous foreach statement 비동기 foreach 문 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf index c28041bfb803c95907862739cb322e197f084662..05559e9ab8afb2c084c18a5e58e2f4768b063743 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - Element „{0}” nie ma wartości null w tym miejscu. - - - - '{0}' may be null here. - Element „{0}” może mieć wartość null w tym miejscu. - - asynchronous foreach statement asynchroniczna instrukcja foreach diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf index 717d77e91c908f8734510070da4d2570f6389b9f..dcf8daa97cf655f173a3fcc78a05c8f7409c229a 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - '{0}' não é nulo aqui. - - - - '{0}' may be null here. - '{0}' pode ser nulo aqui. - - asynchronous foreach statement instrução foreach assíncrona diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf index 78a80e68eca0b9e496583e8fad7494af7a5d4fe1..b00e83f453e5c283e87b47b09b36731ca898c16b 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - Здесь "{0}" имеет значение, отличное от NULL. - - - - '{0}' may be null here. - Здесь "{0}" может иметь значение NULL. - - asynchronous foreach statement асинхронная инструкция foreach diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf index 07287de23aca3864479c9f1ddfa5642965deb868..1453666c69bf60e526e7d5a330468a5109726d7c 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - '{0}' burada null değil. - - - - '{0}' may be null here. - '{0}' burada null olabilir. - - asynchronous foreach statement zaman uyumsuz foreach deyimi diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf index 371372589c18b7a82c46c783df5ece7e50fb1835..ffa89212cfbfd4e47a1c0468ffd6f541c7623ddb 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - “{0}”在此处不为 null。 - - - - '{0}' may be null here. - “{0}”可能在此处为 null。 - - asynchronous foreach statement 异步 foreach 语句 diff --git a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf index 05e2290af1d0de603d5bd3ef6bc8008d29183bb4..d899cca981e447533bb98bf6fd3497db5f88e13e 100644 --- a/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf +++ b/src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf @@ -192,16 +192,6 @@ Warning: Inlining temporary variable may change code meaning. - - '{0}' is not null here. - '{0}' 在此不是 null。 - - - - '{0}' may be null here. - '{0}' 在此可能為 null。 - - asynchronous foreach statement 非同步 foreach 陳述式 diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index a9544541d748cedcd6623383f869387702e4c440..2f4f934dc39b0bbe05a3f385753a8c77b13c32b5 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -2753,6 +2753,12 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + + + '{0}' may be null here. + <infer> diff --git a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs index a871113503bb240df7c0c5ed1721b348669b503d..19093500ac10f545a70951e2edce08b85d6e09cc 100644 --- a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs @@ -20,10 +20,16 @@ public struct TokenInformation /// public readonly bool ShowAwaitReturn; - public TokenInformation(ImmutableArray symbols, bool showAwaitReturn = false) + /// + /// The nullable flow state to show in Quick Info; will be to show nothing. + /// + public readonly NullableFlowState NullableFlowState; + + public TokenInformation(ImmutableArray symbols, bool showAwaitReturn = false, NullableFlowState nullableFlowState = NullableFlowState.None) { Symbols = symbols; ShowAwaitReturn = showAwaitReturn; + NullableFlowState = nullableFlowState; } } } diff --git a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs index 6ce3ea578dfef64b9b99fff37f718b4e89369b89..fdc137d7aadb57a18f10203e4f8cfa744728e96b 100644 --- a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs @@ -148,7 +148,7 @@ private static bool HasNoErrors(ImmutableArray symbols) return default; } - protected async Task CreateContentAsync( + protected static async Task CreateContentAsync( Workspace workspace, SyntaxToken token, SemanticModel semanticModel, @@ -211,7 +211,7 @@ void AddSection(string kind, ImmutableArray taggedParts) // if generating quick info for an attribute, bind to the class instead of the constructor if (syntaxFactsService.IsAttributeName(token.Parent) && - documentedSymbol?.ContainingType?.IsAttribute() == true) + documentedSymbol.ContainingType?.IsAttribute() == true) { documentedSymbol = documentedSymbol.ContainingType; } @@ -284,10 +284,16 @@ void AddSection(string kind, ImmutableArray taggedParts) usageTextBuilder.AddRange(awaitableUsageText); } - var nullableAnalysis = TryGetNullabilityAnalysis(workspace, semanticModel, token, cancellationToken); - if (!nullableAnalysis.IsDefaultOrEmpty) + var nullableMessage = tokenInformation.NullableFlowState switch { - AddSection(QuickInfoSectionKinds.NullabilityAnalysis, nullableAnalysis); + NullableFlowState.MaybeNull => string.Format(FeaturesResources._0_may_be_null_here, documentedSymbol.Name), + NullableFlowState.NotNull => string.Format(FeaturesResources._0_is_not_null_here, documentedSymbol.Name), + _ => null + }; + + if (nullableMessage != null) + { + AddSection(QuickInfoSectionKinds.NullabilityAnalysis, ImmutableArray.Create(new TaggedText(TextTags.Text, nullableMessage))); } if (supportedPlatforms != null) @@ -426,13 +432,12 @@ void AddSection(string kind, ImmutableArray taggedParts) protected abstract bool GetBindableNodeForTokenIndicatingLambda(SyntaxToken token, [NotNullWhen(returnValue: true)] out SyntaxNode? found); protected abstract bool GetBindableNodeForTokenIndicatingPossibleIndexerAccess(SyntaxToken token, [NotNullWhen(returnValue: true)] out SyntaxNode? found); - protected virtual ImmutableArray TryGetNullabilityAnalysis(Workspace workspace, SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken) => default; + protected virtual NullableFlowState GetNullabilityAnalysis(Workspace workspace, SemanticModel semanticModel, ISymbol symbol, SyntaxNode node, CancellationToken cancellationToken) => NullableFlowState.None; private async Task<(SemanticModel semanticModel, TokenInformation tokenInformation)> BindTokenAsync( Document document, SyntaxToken token, CancellationToken cancellationToken) { var syntaxFacts = document.GetRequiredLanguageService(); - var isAwait = syntaxFacts.IsAwaitKeyword(token); var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); var enclosingType = semanticModel.GetEnclosingNamedType(token.SpanStart, cancellationToken); @@ -451,8 +456,14 @@ void AddSection(string kind, ImmutableArray taggedParts) if (symbols.Any()) { - var discardSymbols = (symbols.First() as ITypeParameterSymbol)?.TypeParameterKind == TypeParameterKind.Cref; - return (semanticModel, new TokenInformation(discardSymbols ? ImmutableArray.Empty : symbols, isAwait)); + var firstSymbol = symbols.First(); + var isAwait = syntaxFacts.IsAwaitKeyword(token); + var nullableFlowState = NullableFlowState.None; + if (bindableParent != null) + { + nullableFlowState = GetNullabilityAnalysis(document.Project.Solution.Workspace, semanticModel, firstSymbol, bindableParent, cancellationToken); + } + return (semanticModel, new TokenInformation(symbols, isAwait, nullableFlowState)); } // Couldn't bind the token to specific symbols. If it's an operator, see if we can at @@ -491,7 +502,18 @@ private ImmutableArray GetSymbolsFromToken(SyntaxToken token, Workspace } private static bool IsOk([NotNullWhen(returnValue: true)] ISymbol? symbol) - => symbol != null && !symbol.IsErrorType(); + { + if (symbol == null) + return false; + + if (symbol.IsErrorType()) + return false; + + if (symbol is ITypeParameterSymbol { TypeParameterKind: TypeParameterKind.Cref }) + return false; + + return true; + } private static bool IsAccessible(ISymbol symbol, INamedTypeSymbol? within) => within == null diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 06646dc71641986069bc5a51d2d8f0f38fbd1723..dfc231c55815513712315b149b1dccce371cdb1a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 5a83cc19dc860e276685760c71779b68a0e6bf90..a149b4ad8bfd1ef11f59887900d04c2186e1468f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index b7709da738e9c63945862a94b6908f470ab36a2e..953dce19ca9f806553f98a0aad466cc02092ebe4 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index f669a05cdc1e8c5df23ff7c5731d0773a194c7e9..ac4cad737a5e9fdcf4ac29c5ba188ae95a59ac08 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 1cfa08ab600b310909dcc6caa20c414609cca11d..5f6c68b8d10dc2f29bd5e92f525adeb01ec13877 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 22f40a93f761521ef61c83a4fcf6029a6598a291..00f051284456f96e5e2e29207d5e44adcfb8696c 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 9325807e90fcdbad28316f9467c5383e2a80b9bc..2270ea15cc31bd55ae6bf39194a02976daee7879 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 6a3b7810889566f28fbe999dff204051ecef8360..b7d181d6808aa46ce3dbb24bd79addb1e6b2c639 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 24af010c8b2705cf74a710c92e39444985a052d3..035b63c66fb99a09a6586af0f86835cf95ed8070 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index b2fcd5e1386c2712df0aa8c15f566d2cb6c3cf73..dd76a04e96c389cb49d3a8d75dca6f1a862f9c30 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 272f25d04e2508a84088dae221135aedfc59d8d5..5b7fbc203421e42eeefa7a79b8828c59865f429a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index c2e4ea3a2ee2575530106204b1336e5c226ad1f7..e00b5fdbaaea2733299692a491a83e8058abeb35 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index e7ecef827e7dc37d3f25e2c44e0ae7c11fff54a0..ce6cd136b32c17b4a13d6d4c4d5ae6131bfef548 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -2030,6 +2030,16 @@ Zero-width positive lookbehind assertions are typically used at the beginning of '{0}' cannot be null or whitespace + + '{0}' is not null here. + '{0}' is not null here. + + + + '{0}' may be null here. + '{0}' may be null here. + + 10,000,000ths of a second 10,000,000ths of a second