diff --git a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs index a3d83cd8c6eda2c08273aa796c90d0ca04730245..39e813c6a55501e116099ff8461b39f98c40e244 100644 --- a/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs +++ b/src/EditorFeatures/CSharpTest/QuickInfo/SemanticQuickInfoSourceTests.cs @@ -1345,7 +1345,7 @@ async Task UseAsync() result = await lambda(); } }"; - await TestAsync(markup, MainDescription($"({CSharpFeaturesResources.awaitable}) {string.Format(FeaturesResources.Awaited_task_returns_0, "class System.Threading.Tasks.Task")}"), + await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, $"({CSharpFeaturesResources.awaitable}) class System.Threading.Tasks.Task")), TypeParameterMap($"\r\nTResult {FeaturesResources.is_} int")); } @@ -5230,7 +5230,7 @@ async Task M() awa$$it M(); } }"; - await TestAsync(markup, MainDescription("int[]")); + await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "int[]"))); } [WorkItem(1114300, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1114300")] @@ -5247,7 +5247,7 @@ async Task M() awa$$it M(); } }"; - await TestAsync(markup, MainDescription("dynamic")); + await TestAsync(markup, MainDescription(string.Format(FeaturesResources.Awaited_task_returns_0, "dynamic"))); } [Fact, Trait(Traits.Feature, Traits.Features.Completion)] @@ -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/EditorFeatures/VisualBasicTest/QuickInfo/SemanticQuickInfoSourceTests.vb b/src/EditorFeatures/VisualBasicTest/QuickInfo/SemanticQuickInfoSourceTests.vb index 40207854b15f640f373872bc684c6cb44fbb4f8c..f25ff75d024e9a5b62cc766a4a67a3d52720ef96 100644 --- a/src/EditorFeatures/VisualBasicTest/QuickInfo/SemanticQuickInfoSourceTests.vb +++ b/src/EditorFeatures/VisualBasicTest/QuickInfo/SemanticQuickInfoSourceTests.vb @@ -1897,7 +1897,7 @@ End Class .ToString() - Dim description = <<%= VBFeaturesResources.Awaitable %>> <%= String.Format(FeaturesResources.Awaited_task_returns_0, "Class System.Threading.Tasks.Task(Of TResult)") %>.ConvertTestSourceTag() + Dim description = <%= String.Format(FeaturesResources.Awaited_task_returns_0, $"<{VBFeaturesResources.Awaitable}> Class System.Threading.Tasks.Task(Of TResult)") %>.ConvertTestSourceTag() Await TestFromXmlAsync(markup, MainDescription(description), TypeParameterMap(vbCrLf & $"TResult {FeaturesResources.is_} Integer")) End Function 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 41801ff2b59933b98aebb75a0acd35f415142302..a1a81e3daa50b32b22feb27472167c7a541a1910 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -2750,6 +2750,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/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs index 2570282fa9e8b0b828b8c798cce1d47dd11c53ea..f4d895df686b9fabac117f3c42a2632a808130cb 100644 --- a/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs +++ b/src/Features/Core/Portable/LanguageServices/SymbolDisplayService/AbstractSymbolDisplayService.AbstractSymbolDescriptionBuilder.cs @@ -283,7 +283,7 @@ private async Task AddDescriptionPartAsync(ISymbol symbol) } else { - await AddDescriptionForNamedTypeAsync(namedType).ConfigureAwait(false); + AddDescriptionForNamedType(namedType); } } else if (symbol is INamespaceSymbol namespaceSymbol) @@ -380,37 +380,14 @@ private void AddDescriptionForDynamicType() PlainText(FeaturesResources.Represents_an_object_whose_operations_will_be_resolved_at_runtime)); } - private async Task AddDescriptionForNamedTypeAsync(INamedTypeSymbol symbol) + private void AddDescriptionForNamedType(INamedTypeSymbol symbol) { if (symbol.IsAwaitableNonDynamic(_semanticModel, _position)) { AddAwaitablePrefix(); } - var token = await _semanticModel.SyntaxTree.GetTouchingTokenAsync(_position, CancellationToken).ConfigureAwait(false); - if (token != default) - { - var syntaxFactsService = Workspace.Services.GetLanguageServices(token.Language).GetService(); - if (syntaxFactsService.IsAwaitKeyword(token)) - { - if (symbol.SpecialType == SpecialType.System_Void) - { - AddToGroup(SymbolDescriptionGroups.MainDescription, - PlainText(FeaturesResources.Awaited_task_returns_no_value)); - return; - } - - AddAwaitSymbolDescription(symbol); - } - else - { - AddSymbolDescription(symbol); - } - } - else - { - AddSymbolDescription(symbol); - } + AddSymbolDescription(symbol); if (!symbol.IsUnboundGenericType && !TypeArgumentsAndParametersAreSame(symbol)) { @@ -421,18 +398,6 @@ private async Task AddDescriptionForNamedTypeAsync(INamedTypeSymbol symbol) } } - private void AddAwaitSymbolDescription(INamedTypeSymbol symbol) - { - var defaultSymbol = "{0}"; - var symbolIndex = FeaturesResources.Awaited_task_returns_0.IndexOf(defaultSymbol); - - AddToGroup(SymbolDescriptionGroups.MainDescription, - PlainText(FeaturesResources.Awaited_task_returns_0.Substring(0, symbolIndex))); - AddSymbolDescription(symbol); - AddToGroup(SymbolDescriptionGroups.MainDescription, - PlainText(FeaturesResources.Awaited_task_returns_0.Substring(symbolIndex + defaultSymbol.Length))); - } - private void AddSymbolDescription(INamedTypeSymbol symbol) { if (symbol.TypeKind == TypeKind.Delegate) diff --git a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs new file mode 100644 index 0000000000000000000000000000000000000000..19093500ac10f545a70951e2edce08b85d6e09cc --- /dev/null +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.TokenInfo.cs @@ -0,0 +1,36 @@ +// 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 enable + +using System.Collections.Immutable; + +namespace Microsoft.CodeAnalysis.QuickInfo +{ + internal abstract partial class CommonSemanticQuickInfoProvider + { + public struct TokenInformation + { + public readonly ImmutableArray Symbols; + + /// + /// True if this quick info came from hovering over an 'await' keyword, which we show the return + /// type of with special text. + /// + public readonly bool ShowAwaitReturn; + + /// + /// 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 b21f6ee08526fbd4c213a8f51ad6b673422fb717..10383a9292c232e639547246f447b1066de1c062 100644 --- a/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs +++ b/src/Features/Core/Portable/QuickInfo/CommonSemanticQuickInfoProvider.cs @@ -26,19 +26,19 @@ internal abstract partial class CommonSemanticQuickInfoProvider : CommonQuickInf SyntaxToken token, CancellationToken cancellationToken) { - var (model, symbols, supportedPlatforms) = await ComputeQuickInfoDataAsync(document, token, cancellationToken).ConfigureAwait(false); + var (model, tokenInformation, supportedPlatforms) = await ComputeQuickInfoDataAsync(document, token, cancellationToken).ConfigureAwait(false); - if (symbols.IsDefaultOrEmpty) + if (tokenInformation.Symbols.IsDefaultOrEmpty) { return null; } return await CreateContentAsync(document.Project.Solution.Workspace, - token, model, symbols, supportedPlatforms, + token, model, tokenInformation, supportedPlatforms, cancellationToken).ConfigureAwait(false); } - private async Task<(SemanticModel model, ImmutableArray symbols, SupportedPlatformData? supportedPlatforms)> ComputeQuickInfoDataAsync( + private async Task<(SemanticModel model, TokenInformation tokenInformation, SupportedPlatformData? supportedPlatforms)> ComputeQuickInfoDataAsync( Document document, SyntaxToken token, CancellationToken cancellationToken) @@ -49,12 +49,12 @@ internal abstract partial class CommonSemanticQuickInfoProvider : CommonQuickInf return await ComputeFromLinkedDocumentsAsync(document, linkedDocumentIds, token, cancellationToken).ConfigureAwait(false); } - var (model, symbols) = await BindTokenAsync(document, token, cancellationToken).ConfigureAwait(false); + var (model, tokenInformation) = await BindTokenAsync(document, token, cancellationToken).ConfigureAwait(false); - return (model, symbols, supportedPlatforms: null); + return (model, tokenInformation, supportedPlatforms: null); } - private async Task<(SemanticModel model, ImmutableArray symbols, SupportedPlatformData supportedPlatforms)> ComputeFromLinkedDocumentsAsync( + private async Task<(SemanticModel model, TokenInformation, SupportedPlatformData supportedPlatforms)> ComputeFromLinkedDocumentsAsync( Document document, ImmutableArray linkedDocumentIds, SyntaxToken token, @@ -70,14 +70,14 @@ internal abstract partial class CommonSemanticQuickInfoProvider : CommonQuickInf // Instead, we need to find the head in which we get the best binding, // which in this case is the one with no errors. - var (model, symbols) = await BindTokenAsync(document, token, cancellationToken).ConfigureAwait(false); + var (model, tokenInformation) = await BindTokenAsync(document, token, cancellationToken).ConfigureAwait(false); var candidateProjects = new List() { document.Project.Id }; var invalidProjects = new List(); - var candidateResults = new List<(DocumentId docId, SemanticModel model, ImmutableArray symbols)> + var candidateResults = new List<(DocumentId docId, SemanticModel model, TokenInformation tokenInformation)> { - (document.Id, model, symbols) + (document.Id, model, tokenInformation) }; foreach (var linkedDocumentId in linkedDocumentIds) @@ -96,10 +96,10 @@ internal abstract partial class CommonSemanticQuickInfoProvider : CommonQuickInf // Take the first result with no errors. // If every file binds with errors, take the first candidate, which is from the current file. - var bestBinding = candidateResults.FirstOrNull(c => HasNoErrors(c.symbols)) + var bestBinding = candidateResults.FirstOrNull(c => HasNoErrors(c.tokenInformation.Symbols)) ?? candidateResults.First(); - if (bestBinding.symbols.IsDefaultOrEmpty) + if (bestBinding.tokenInformation.Symbols.IsDefaultOrEmpty) { return default; } @@ -109,7 +109,7 @@ internal abstract partial class CommonSemanticQuickInfoProvider : CommonQuickInf foreach (var candidate in candidateResults) { // Does the candidate have anything remotely equivalent? - if (!candidate.symbols.Intersect(bestBinding.symbols, LinkedFilesSymbolEquivalenceComparer.Instance).Any()) + if (!candidate.tokenInformation.Symbols.Intersect(bestBinding.tokenInformation.Symbols, LinkedFilesSymbolEquivalenceComparer.Instance).Any()) { invalidProjects.Add(candidate.docId.ProjectId); } @@ -117,7 +117,7 @@ internal abstract partial class CommonSemanticQuickInfoProvider : CommonQuickInf var supportedPlatforms = new SupportedPlatformData(invalidProjects, candidateProjects, document.Project.Solution.Workspace); - return (bestBinding.model, bestBinding.symbols, supportedPlatforms); + return (bestBinding.model, bestBinding.tokenInformation, supportedPlatforms); } private static bool HasNoErrors(ImmutableArray symbols) @@ -148,21 +148,21 @@ private static bool HasNoErrors(ImmutableArray symbols) return default; } - protected async Task CreateContentAsync( + protected static async Task CreateContentAsync( Workspace workspace, SyntaxToken token, SemanticModel semanticModel, - IEnumerable symbols, + TokenInformation tokenInformation, SupportedPlatformData? supportedPlatforms, CancellationToken cancellationToken) { var descriptionService = workspace.Services.GetLanguageServices(token.Language).GetRequiredService(); var formatter = workspace.Services.GetLanguageServices(semanticModel.Language).GetRequiredService(); var syntaxFactsService = workspace.Services.GetLanguageServices(semanticModel.Language).GetRequiredService(); + var showWarningGlyph = supportedPlatforms != null && supportedPlatforms.HasValidAndInvalidProjects(); - var showSymbolGlyph = true; - var groups = await descriptionService.ToDescriptionGroupsAsync(workspace, semanticModel, token.SpanStart, symbols.AsImmutable(), cancellationToken).ConfigureAwait(false); + var groups = await descriptionService.ToDescriptionGroupsAsync(workspace, semanticModel, token.SpanStart, tokenInformation.Symbols, cancellationToken).ConfigureAwait(false); bool TryGetGroupText(SymbolDescriptionGroups group, out ImmutableArray taggedParts) => groups.TryGetValue(group, out taggedParts) && !taggedParts.IsDefaultOrEmpty; @@ -172,34 +172,58 @@ bool TryGetGroupText(SymbolDescriptionGroups group, out ImmutableArray taggedParts) => sections.Add(QuickInfoSection.Create(kind, taggedParts)); - if (TryGetGroupText(SymbolDescriptionGroups.MainDescription, out var mainDescriptionTaggedParts)) + if (tokenInformation.ShowAwaitReturn) { - AddSection(QuickInfoSectionKinds.Description, mainDescriptionTaggedParts); + // We show a special message if the Task being awaited has no return + if ((tokenInformation.Symbols.First() as INamedTypeSymbol)?.SpecialType == SpecialType.System_Void) + { + var builder = ImmutableArray.CreateBuilder(); + builder.AddText(FeaturesResources.Awaited_task_returns_no_value); + AddSection(QuickInfoSectionKinds.Description, builder.ToImmutable()); + return QuickInfoItem.Create(token.Span, sections: sections.ToImmutable()); + } + else + { + if (TryGetGroupText(SymbolDescriptionGroups.MainDescription, out var mainDescriptionTaggedParts)) + { + // We'll take the existing message and wrap it with a message saying this was returned from the task. + var defaultSymbol = "{0}"; + var symbolIndex = FeaturesResources.Awaited_task_returns_0.IndexOf(defaultSymbol); + + var builder = ImmutableArray.CreateBuilder(); + builder.AddText(FeaturesResources.Awaited_task_returns_0.Substring(0, symbolIndex)); + builder.AddRange(mainDescriptionTaggedParts); + builder.AddText(FeaturesResources.Awaited_task_returns_0.Substring(symbolIndex + defaultSymbol.Length)); + + AddSection(QuickInfoSectionKinds.Description, builder.ToImmutable()); + } + } + } + else + { + if (TryGetGroupText(SymbolDescriptionGroups.MainDescription, out var mainDescriptionTaggedParts)) + { + AddSection(QuickInfoSectionKinds.Description, mainDescriptionTaggedParts); + } } - var documentedSymbol = symbols.FirstOrDefault(); + var symbol = tokenInformation.Symbols.First(); // if generating quick info for an attribute, bind to the class instead of the constructor if (syntaxFactsService.IsAttributeName(token.Parent) && - documentedSymbol?.ContainingType?.IsAttribute() == true) + symbol.ContainingType?.IsAttribute() == true) { - documentedSymbol = documentedSymbol.ContainingType; + symbol = symbol.ContainingType; } - var documentationContent = GetDocumentationContent(documentedSymbol, groups, semanticModel, token, formatter, cancellationToken); - if (syntaxFactsService.IsAwaitKeyword(token) && - (symbols.First() as INamedTypeSymbol)?.SpecialType == SpecialType.System_Void) - { - documentationContent = default; - showSymbolGlyph = false; - } + var documentationContent = GetDocumentationContent(symbol, groups, semanticModel, token, formatter, cancellationToken); if (!documentationContent.IsDefaultOrEmpty) { AddSection(QuickInfoSectionKinds.DocumentationComments, documentationContent); } - var remarksDocumentationContent = GetRemarksDocumentationContent(workspace, documentedSymbol, groups, semanticModel, token, formatter, cancellationToken); + var remarksDocumentationContent = GetRemarksDocumentationContent(workspace, symbol, groups, semanticModel, token, formatter, cancellationToken); if (!remarksDocumentationContent.IsDefaultOrEmpty) { var builder = ImmutableArray.CreateBuilder(); @@ -212,7 +236,7 @@ void AddSection(string kind, ImmutableArray taggedParts) AddSection(QuickInfoSectionKinds.RemarksDocumentationComments, builder.ToImmutable()); } - var returnsDocumentationContent = GetReturnsDocumentationContent(documentedSymbol, groups, semanticModel, token, formatter, cancellationToken); + var returnsDocumentationContent = GetReturnsDocumentationContent(symbol, groups, semanticModel, token, formatter, cancellationToken); if (!returnsDocumentationContent.IsDefaultOrEmpty) { var builder = ImmutableArray.CreateBuilder(); @@ -225,7 +249,7 @@ void AddSection(string kind, ImmutableArray taggedParts) AddSection(QuickInfoSectionKinds.ReturnsDocumentationComments, builder.ToImmutable()); } - var valueDocumentationContent = GetValueDocumentationContent(documentedSymbol, groups, semanticModel, token, formatter, cancellationToken); + var valueDocumentationContent = GetValueDocumentationContent(symbol, groups, semanticModel, token, formatter, cancellationToken); if (!valueDocumentationContent.IsDefaultOrEmpty) { var builder = ImmutableArray.CreateBuilder(); @@ -260,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 + { + NullableFlowState.MaybeNull => string.Format(FeaturesResources._0_may_be_null_here, symbol.Name), + NullableFlowState.NotNull => string.Format(FeaturesResources._0_is_not_null_here, symbol.Name), + _ => null + }; + + if (nullableMessage != null) { - AddSection(QuickInfoSectionKinds.NullabilityAnalysis, nullableAnalysis); + AddSection(QuickInfoSectionKinds.NullabilityAnalysis, ImmutableArray.Create(new TaggedText(TextTags.Text, nullableMessage))); } if (supportedPlatforms != null) @@ -286,11 +316,7 @@ void AddSection(string kind, ImmutableArray taggedParts) AddSection(QuickInfoSectionKinds.Captures, capturesText); } - var tags = ImmutableArray.Empty; - if (showSymbolGlyph) - { - tags = tags.AddRange(GlyphTags.GetTags(symbols.First().GetGlyph())); - } + var tags = ImmutableArray.CreateRange(GlyphTags.GetTags(tokenInformation.Symbols.First().GetGlyph())); if (showWarningGlyph) { @@ -301,7 +327,7 @@ void AddSection(string kind, ImmutableArray taggedParts) } private static ImmutableArray GetDocumentationContent( - ISymbol? documentedSymbol, + ISymbol documentedSymbol, IDictionary> sections, SemanticModel semanticModel, SyntaxToken token, @@ -312,13 +338,11 @@ void AddSection(string kind, ImmutableArray taggedParts) { return parts; } - else if (documentedSymbol is object) + + var documentation = documentedSymbol.GetDocumentationParts(semanticModel, token.SpanStart, formatter, cancellationToken); + if (documentation != null) { - var documentation = documentedSymbol.GetDocumentationParts(semanticModel, token.SpanStart, formatter, cancellationToken); - if (documentation != null) - { - return documentation.ToImmutableArray(); - } + return documentation.ToImmutableArray(); } return default; @@ -326,7 +350,7 @@ void AddSection(string kind, ImmutableArray taggedParts) private static ImmutableArray GetRemarksDocumentationContent( Workspace workspace, - ISymbol? documentedSymbol, + ISymbol documentedSymbol, IDictionary> sections, SemanticModel semanticModel, SyntaxToken token, @@ -343,20 +367,18 @@ void AddSection(string kind, ImmutableArray taggedParts) { return parts; } - else if (documentedSymbol is object) + + var documentation = documentedSymbol.GetRemarksDocumentationParts(semanticModel, token.SpanStart, formatter, cancellationToken); + if (documentation != null) { - var documentation = documentedSymbol.GetRemarksDocumentationParts(semanticModel, token.SpanStart, formatter, cancellationToken); - if (documentation != null) - { - return documentation.ToImmutableArray(); - } + return documentation.ToImmutableArray(); } return default; } private static ImmutableArray GetReturnsDocumentationContent( - ISymbol? documentedSymbol, + ISymbol documentedSymbol, IDictionary> sections, SemanticModel semanticModel, SyntaxToken token, @@ -367,20 +389,18 @@ void AddSection(string kind, ImmutableArray taggedParts) { return parts; } - else if (documentedSymbol is object) + + var documentation = documentedSymbol.GetReturnsDocumentationParts(semanticModel, token.SpanStart, formatter, cancellationToken); + if (documentation != null) { - var documentation = documentedSymbol.GetReturnsDocumentationParts(semanticModel, token.SpanStart, formatter, cancellationToken); - if (documentation != null) - { - return documentation.ToImmutableArray(); - } + return documentation.ToImmutableArray(); } return default; } private static ImmutableArray GetValueDocumentationContent( - ISymbol? documentedSymbol, + ISymbol documentedSymbol, IDictionary> sections, SemanticModel semanticModel, SyntaxToken token, @@ -391,13 +411,11 @@ void AddSection(string kind, ImmutableArray taggedParts) { return parts; } - else if (documentedSymbol is object) + + var documentation = documentedSymbol.GetValueDocumentationParts(semanticModel, token.SpanStart, formatter, cancellationToken); + if (documentation != null) { - var documentation = documentedSymbol.GetValueDocumentationParts(semanticModel, token.SpanStart, formatter, cancellationToken); - if (documentation != null) - { - return documentation.ToImmutableArray(); - } + return documentation.ToImmutableArray(); } return default; @@ -406,9 +424,9 @@ 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, ImmutableArray symbols)> BindTokenAsync( + private async Task<(SemanticModel semanticModel, TokenInformation tokenInformation)> BindTokenAsync( Document document, SyntaxToken token, CancellationToken cancellationToken) { var syntaxFacts = document.GetRequiredLanguageService(); @@ -430,8 +448,14 @@ void AddSection(string kind, ImmutableArray taggedParts) if (symbols.Any()) { - var discardSymbols = (symbols.First() as ITypeParameterSymbol)?.TypeParameterKind == TypeParameterKind.Cref; - return (semanticModel, discardSymbols ? ImmutableArray.Empty : symbols); + 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 @@ -441,11 +465,11 @@ void AddSection(string kind, ImmutableArray taggedParts) var typeInfo = semanticModel.GetTypeInfo(token.Parent!, cancellationToken); if (IsOk(typeInfo.Type)) { - return (semanticModel, ImmutableArray.Create(typeInfo.Type)); + return (semanticModel, new TokenInformation(ImmutableArray.Create(typeInfo.Type))); } } - return (semanticModel, ImmutableArray.Empty); + return (semanticModel, new TokenInformation(ImmutableArray.Empty)); } private ImmutableArray GetSymbolsFromToken(SyntaxToken token, Workspace workspace, SemanticModel semanticModel, CancellationToken cancellationToken) @@ -470,7 +494,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 1aad5a9f6f5c84c62fe5b11deb70deb03895e360..97b3d2f81415fe3e2fbbebec59d754361939f23c 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -2040,6 +2040,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 f595a71eaed71d308215dcbdcb67698eaa79281e..7ff15ae6bb234fe8419a8400178830642fd48a4c 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -2040,6 +2040,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 2739bc528c05e9a718fb8f3304794b24a8f0f506..582d9c030b96f37284c107b7730f1e57239d7585 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -2040,6 +2040,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 6bde10fc12d0c5cd75ae0697a3796a702ec90560..2d7846f34e413be3b8e7a94d63c0a1f6cefb0f90 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -2040,6 +2040,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 830d50306202bd568b41d5d7b47efaad35db6083..cbbfc8010d945b6c7d9e1115e4db6dc8ec419df0 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -2040,6 +2040,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 bb73d7ad3ff123cf712693631cc015362716eeea..7a65a5cd6691547c225e1ca0583b1aeff4c42ad7 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -2040,6 +2040,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 dd81dc835aa1a0d383eedd251884062331cf1a95..328c4f833f9f338a97bb56b7c7c5797a5fb7886f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -2040,6 +2040,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 132c11a930b383f0f7a625713f55318712aa47b1..1d0d3bfa504df827df51adb56c31db3532ca0898 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -2040,6 +2040,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 9e1233a7dc2c466c11c57520a66926610c5f0307..2e83e9e4ffa7b50ce07bcf905a02cb935a65dacc 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -2040,6 +2040,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 db13af884fa09a412b7fd458154eb7c375f0e448..9bdc59bfa09c7395d53dd96f97d82602f38c2e63 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -2040,6 +2040,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 405face7000becfee354ef2e73a8312c97b6bad2..276edc18085b99042b9375a8b714c3ee2249b969 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -2040,6 +2040,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 ed25769566371e1c0e4c95e354c3e46be83ff02f..c17df75d0e116be3da5e49b4ae5453bdfc6f9c21 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -2040,6 +2040,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 1560035b8c161c27c232cea9f1389c02a11fe0da..904968c0b4218560927f7ebf71d642add9cfbb4c 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -2040,6 +2040,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/VisualBasic/Portable/QuickInfo/VisualBasicSemanticQuickInfoProvider.vb b/src/Features/VisualBasic/Portable/QuickInfo/VisualBasicSemanticQuickInfoProvider.vb index d1e34a170698deadedf8ad630b17f63535e09617..04a8463f960ac71d51abe540321897f3e8549a73 100644 --- a/src/Features/VisualBasic/Portable/QuickInfo/VisualBasicSemanticQuickInfoProvider.vb +++ b/src/Features/VisualBasic/Portable/QuickInfo/VisualBasicSemanticQuickInfoProvider.vb @@ -121,7 +121,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.QuickInfo Return False End Function - Private Overloads Async Function BuildContentAsync( + Private Overloads Shared Async Function BuildContentAsync( document As Document, token As SyntaxToken, declarators As SeparatedSyntaxList(Of VariableDeclaratorSyntax), @@ -134,7 +134,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.QuickInfo Dim semantics = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False) Dim types = declarators.SelectMany(Function(d) d.Names).Select( - Function(n) + Function(n) As ISymbol Dim symbol = semantics.GetDeclaredSymbol(n, cancellationToken) If symbol Is Nothing Then Return Nothing @@ -147,17 +147,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.QuickInfo Else Return Nothing End If - End Function).WhereNotNull().Distinct().ToList() + End Function).WhereNotNull().Distinct().ToImmutableArray() - If types.Count = 0 Then + If types.Length = 0 Then Return Nothing End If - If types.Count > 1 Then + If types.Length > 1 Then Return QuickInfoItem.Create(token.Span, sections:=ImmutableArray.Create(QuickInfoSection.Create(QuickInfoSectionKinds.Description, ImmutableArray.Create(New TaggedText(TextTags.Text, VBFeaturesResources.Multiple_Types))))) End If - Return Await CreateContentAsync(document.Project.Solution.Workspace, token, semantics, types, supportedPlatforms:=Nothing, cancellationToken:=cancellationToken).ConfigureAwait(False) + Return Await CreateContentAsync(document.Project.Solution.Workspace, token, semantics, New TokenInformation(types), supportedPlatforms:=Nothing, cancellationToken:=cancellationToken).ConfigureAwait(False) End Function Private Shared Async Function BuildContentForIntrinsicOperatorAsync(document As Document,