From 5cf5afdc1cb7c9d30c7e901b83ba41ffc7c4fd54 Mon Sep 17 00:00:00 2001 From: Jason Malinowski Date: Thu, 28 May 2020 17:25:22 -0700 Subject: [PATCH] Move the nullable flow state shown from quick info into TokenInformation This moves the fetching of the nullable state into the first part of quick info, which is figuring out the data/model. The function to take that model and convert it to the display information can now go static since it's not relying on other indirection. --- .../QuickInfo/SemanticQuickInfoSourceTests.cs | 22 ++++---- .../Portable/CSharpFeaturesResources.resx | 6 --- .../CSharpSemanticQuickInfoProvider.cs | 51 ++++--------------- .../xlf/CSharpFeaturesResources.cs.xlf | 10 ---- .../xlf/CSharpFeaturesResources.de.xlf | 10 ---- .../xlf/CSharpFeaturesResources.es.xlf | 10 ---- .../xlf/CSharpFeaturesResources.fr.xlf | 10 ---- .../xlf/CSharpFeaturesResources.it.xlf | 10 ---- .../xlf/CSharpFeaturesResources.ja.xlf | 10 ---- .../xlf/CSharpFeaturesResources.ko.xlf | 10 ---- .../xlf/CSharpFeaturesResources.pl.xlf | 10 ---- .../xlf/CSharpFeaturesResources.pt-BR.xlf | 10 ---- .../xlf/CSharpFeaturesResources.ru.xlf | 10 ---- .../xlf/CSharpFeaturesResources.tr.xlf | 10 ---- .../xlf/CSharpFeaturesResources.zh-Hans.xlf | 10 ---- .../xlf/CSharpFeaturesResources.zh-Hant.xlf | 10 ---- .../Core/Portable/FeaturesResources.resx | 6 +++ ...mmonSemanticQuickInfoProvider.TokenInfo.cs | 8 ++- .../CommonSemanticQuickInfoProvider.cs | 42 +++++++++++---- .../Portable/xlf/FeaturesResources.cs.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.de.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.es.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.fr.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.it.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.ja.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.ko.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.pl.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.pt-BR.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.ru.xlf | 10 ++++ .../Portable/xlf/FeaturesResources.tr.xlf | 10 ++++ .../xlf/FeaturesResources.zh-Hans.xlf | 10 ++++ .../xlf/FeaturesResources.zh-Hant.xlf | 10 ++++ 32 files changed, 197 insertions(+), 198 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index 0518d806ad4..39e813c6a55 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 5158c6b331a..660ecb85318 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 d1a707646bf..044912f97b9 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 ba30201576c..d0d897ddea2 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 4a7b1945671..89d5435e78f 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 4b6a0754218..08543f5dfff 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 3cb937f8604..f9f264f8abb 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 0b7e8bc921e..87de8e4d96c 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 9768da58b06..b29a57f61b8 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 2f729533f0d..a322722b068 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 c28041bfb80..05559e9ab8a 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 717d77e91c9..dcf8daa97cf 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 78a80e68eca..b00e83f453e 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 07287de23ac..1453666c69b 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 371372589c1..ffa89212cfb 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 05e2290af1d..d899cca981e 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 a9544541d74..2f4f934dc39 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 a871113503b..19093500ac1 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 6ce3ea578df..fdc137d7aad 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 06646dc7164..dfc231c5581 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 5a83cc19dc8..a149b4ad8bf 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 b7709da738e..953dce19ca9 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 f669a05cdc1..ac4cad737a5 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 1cfa08ab600..5f6c68b8d10 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 22f40a93f76..00f05128445 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 9325807e90f..2270ea15cc3 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 6a3b7810889..b7d181d6808 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 24af010c8b2..035b63c66fb 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 b2fcd5e1386..dd76a04e96c 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 272f25d04e2..5b7fbc20342 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 c2e4ea3a2ee..e00b5fdbaae 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 e7ecef827e7..ce6cd136b32 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 -- GitLab