diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs index e4464bfd1b295..5d5fea2b11c59 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/DeclarationNameCompletionProviderTests.cs @@ -2606,6 +2606,32 @@ void M(object o) } """, "customers"); + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79417")] + public Task TestNestedParameter1() + => VerifyItemExistsAsync(""" + class C + { + void M(MyWidget myWidget) + { + void LocalFunction(MyWidget $$) { } + } + } + """, "myWidget"); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79417")] + public Task TestNestedParameter2() + => VerifyItemExistsAsync(""" + class MyWidget { } + class C(MyWidget myWidget) + { + class D(MyWidget $$) { } + } + """, "myWidget"); + +#if false + +#endif + private static NamingStylePreferences MultipleCamelCaseLocalRules() { var styles = new[] diff --git a/src/EditorFeatures/CSharpTest/ObsoleteSymbol/CSharpObsoleteSymbolTests.cs b/src/EditorFeatures/CSharpTest/ObsoleteSymbol/CSharpObsoleteSymbolTests.cs index c2fc40c102216..0320ccf60e73e 100644 --- a/src/EditorFeatures/CSharpTest/ObsoleteSymbol/CSharpObsoleteSymbolTests.cs +++ b/src/EditorFeatures/CSharpTest/ObsoleteSymbol/CSharpObsoleteSymbolTests.cs @@ -2,9 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.ObsoleteSymbol; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ObsoleteSymbol; @@ -14,6 +16,9 @@ public sealed class CSharpObsoleteSymbolTests : AbstractObsoleteSymbolTests protected override EditorTestWorkspace CreateWorkspace(string markup) => EditorTestWorkspace.CreateCSharp(markup); + private new Task TestAsync([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string markup) + => base.TestAsync(markup); + [Theory] [InlineData("class")] [InlineData("struct")] @@ -183,4 +188,15 @@ void Method() } } """); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/79413")] + public Task TestObsoleteFeatureAttribute() + => TestAsync( + """ + [System.Obsolete] + [System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute("This is an obsolete feature.")] + static class ObsoleteType + { + } + """); } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationName/DeclarationNameCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationName/DeclarationNameCompletionProvider.cs index f4e23b4df0e69..03502a9ac9874 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationName/DeclarationNameCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationName/DeclarationNameCompletionProvider.cs @@ -21,12 +21,12 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers; -[ExportCompletionProvider(nameof(DeclarationNameCompletionProvider), LanguageNames.CSharp)] +[ExportCompletionProvider(nameof(DeclarationNameCompletionProvider), LanguageNames.CSharp), Shared] [ExtensionOrder(After = nameof(TupleNameCompletionProvider))] -[Shared] [method: ImportingConstructor] [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] -internal sealed partial class DeclarationNameCompletionProvider([ImportMany] IEnumerable> recommenders) : LSPCompletionProvider +internal sealed partial class DeclarationNameCompletionProvider( + [ImportMany] IEnumerable> recommenders) : LSPCompletionProvider { private ImmutableArray> Recommenders { get; } = [.. ExtensionOrderer.Order(recommenders)]; diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationName/DeclarationNameRecommender.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationName/DeclarationNameRecommender.cs index 17fed3ba63b3f..fa228b9f4e78c 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationName/DeclarationNameRecommender.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationName/DeclarationNameRecommender.cs @@ -29,13 +29,10 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers.DeclarationName; [ExportDeclarationNameRecommender(nameof(DeclarationNameRecommender)), Shared] -internal sealed partial class DeclarationNameRecommender : IDeclarationNameRecommender +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed partial class DeclarationNameRecommender() : IDeclarationNameRecommender { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public DeclarationNameRecommender() - { } - public async Task> ProvideRecommendedNamesAsync( CompletionContext completionContext, Document document, @@ -255,9 +252,9 @@ static void ProcessRules( context.TargetToken.GetRequiredParent(), container: null, baseName: name, - filter: s => IsRelevantSymbolKind(s), + filter: IsRelevantSymbolKind, usedNames: [], - cancellationToken: cancellationToken); + cancellationToken); if (seenUniqueNames.Add(uniqueName.Text)) { @@ -331,9 +328,5 @@ static ImmutableArray GetOverloads(INamedTypeSymbol namedType, Ba /// Only relevant if symbol could cause a conflict with a local variable. /// private static bool IsRelevantSymbolKind(ISymbol symbol) - { - return symbol.Kind is SymbolKind.Local or - SymbolKind.Parameter or - SymbolKind.RangeVariable; - } + => symbol.Kind is SymbolKind.Local or SymbolKind.Parameter or SymbolKind.RangeVariable; } diff --git a/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs index 2db0568718b2c..9de1171a566ac 100644 --- a/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs +++ b/src/LanguageServer/Protocol.TestUtilities/LanguageServer/AbstractLanguageServerProtocolTests.cs @@ -644,14 +644,25 @@ protected virtual RoslynLanguageServer CreateLanguageServer(Stream inputStream, public async Task GetDocumentAsync(DocumentUri uri) { - var document = await GetCurrentSolution().GetDocumentAsync(new LSP.TextDocumentIdentifier { DocumentUri = uri }, CancellationToken.None).ConfigureAwait(false); + var textDocument = await GetTextDocumentAsync(uri).ConfigureAwait(false); + if (textDocument is not Document document) + { + throw new InvalidOperationException($"Found TextDocument with {uri} in solution, but it is not a Document"); + } + + return document; + } + + public async Task GetTextDocumentAsync(DocumentUri uri) + { + var document = await GetCurrentSolution().GetTextDocumentAsync(new LSP.TextDocumentIdentifier { DocumentUri = uri }, CancellationToken.None).ConfigureAwait(false); Contract.ThrowIfNull(document, $"Unable to find document with {uri} in solution"); return document; } public async Task GetDocumentTextAsync(DocumentUri uri) { - var document = await GetDocumentAsync(uri).ConfigureAwait(false); + var document = await GetTextDocumentAsync(uri).ConfigureAwait(false); return await document.GetTextAsync(CancellationToken.None).ConfigureAwait(false); } diff --git a/src/LanguageServer/Protocol/Handler/Diagnostics/AbstractDocumentPullDiagnosticHandler.cs b/src/LanguageServer/Protocol/Handler/Diagnostics/AbstractDocumentPullDiagnosticHandler.cs index a6ac09fcfea3c..aa35b95813701 100644 --- a/src/LanguageServer/Protocol/Handler/Diagnostics/AbstractDocumentPullDiagnosticHandler.cs +++ b/src/LanguageServer/Protocol/Handler/Diagnostics/AbstractDocumentPullDiagnosticHandler.cs @@ -35,7 +35,7 @@ protected override ValueTask> GetOrderedDiagno // Only consider open documents here (and only closed ones in the WorkspacePullDiagnosticHandler). Each // handler treats those as separate worlds that they are responsible for. var identifier = GetTextDocumentIdentifier(diagnosticsParams); - if (identifier is null || context.Document is null) + if (identifier is null || context.TextDocument is null) { context.TraceDebug("Ignoring diagnostics request because no text document was provided"); return new([]); diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs index c4025e83d9ffe..82ac970ee9e6d 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/AbstractPullDiagnosticTestsBase.cs @@ -227,7 +227,7 @@ private protected static async Task InsertTextAsync( await testLspServer.InsertTextAsync(document.GetURI(), (lineInfo.Start.Line, lineInfo.Start.Character, text)); } - private protected static Task OpenDocumentAsync(TestLspServer testLspServer, Document document) => testLspServer.OpenDocumentAsync(document.GetURI()); + private protected static Task OpenDocumentAsync(TestLspServer testLspServer, TextDocument document) => testLspServer.OpenDocumentAsync(document.GetURI()); private protected static Task> RunGetDocumentPullDiagnosticsAsync( TestLspServer testLspServer, diff --git a/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs b/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs index 1552f098d01fa..a40f429582e35 100644 --- a/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs +++ b/src/LanguageServer/ProtocolUnitTests/Diagnostics/AdditionalFileDiagnosticsTests.cs @@ -2,11 +2,17 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Collections.Immutable; using System.Composition; using System.Linq; +using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServer.Handler; +using Microsoft.CodeAnalysis.LanguageServer.Handler.Diagnostics; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -22,6 +28,32 @@ public AdditionalFileDiagnosticsTests(ITestOutputHelper testOutputHelper) : base { } + [Theory, CombinatorialData, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2531252")] + public async Task TestDocumentDiagnosticsReportsAdditionalFileDiagnostic(bool useVSDiagnostics, bool mutatingLspWorkspace) + { + var workspaceXml = + $""" + + + + + + + """; + + await using var testLspServer = await CreateTestWorkspaceFromXmlAsync(workspaceXml, mutatingLspWorkspace, BackgroundAnalysisScope.FullSolution, useVSDiagnostics); + + var additionalDocument = testLspServer.GetCurrentSolution().Projects.Single().AdditionalDocuments.Single(); + await testLspServer.OpenDocumentAsync(additionalDocument.GetURI()); + + var results = await RunGetDocumentPullDiagnosticsAsync(testLspServer, additionalDocument.GetURI(), useVSDiagnostics, category: TestAdditionalFileDocumentSourceProvider.DiagnosticSourceProviderName); + Assert.NotEmpty(results); + AssertEx.Equal( + [ + @$"C:\Test.xaml: [{MockAdditionalFileDiagnosticAnalyzer.Id}]", + ], results.Select(r => $"{r.Uri.GetRequiredParsedUri().LocalPath}: [{string.Join(", ", r.Diagnostics!.Select(d => d.Code?.Value?.ToString()))}]")); + } + [Theory, CombinatorialData] public async Task TestWorkspaceDiagnosticsReportsAdditionalFileDiagnostic(bool useVSDiagnostics, bool mutatingLspWorkspace) { @@ -119,7 +151,7 @@ public async Task TestWorkspaceDiagnosticsWithAdditionalFileInMultipleProjects(b AssertEx.Empty(results2); } - protected override TestComposition Composition => base.Composition.AddParts(typeof(MockAdditionalFileDiagnosticAnalyzer)); + protected override TestComposition Composition => base.Composition.AddParts(typeof(MockAdditionalFileDiagnosticAnalyzer), typeof(TestAdditionalFileDocumentSourceProvider)); private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersReference() => new(ImmutableDictionary>.Empty.Add(LanguageNames.CSharp, [DiagnosticExtensions.GetCompilerDiagnosticAnalyzer(LanguageNames.CSharp), new MockAdditionalFileDiagnosticAnalyzer()])); @@ -128,10 +160,10 @@ private protected override TestAnalyzerReferenceByLanguage CreateTestAnalyzersRe private sealed class MockAdditionalFileDiagnosticAnalyzer : DiagnosticAnalyzer { public const string Id = "MockAdditionalDiagnostic"; - private readonly DiagnosticDescriptor _descriptor = new(Id, "MockAdditionalDiagnostic", "MockAdditionalDiagnostic", "InternalCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://github.com/dotnet/roslyn"); + internal static readonly DiagnosticDescriptor Descriptor = new(Id, "MockAdditionalDiagnostic", "MockAdditionalDiagnostic", "InternalCategory", DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: "https://github.com/dotnet/roslyn"); public override ImmutableArray SupportedDiagnostics - => [_descriptor]; + => [Descriptor]; public override void Initialize(AnalysisContext context) => context.RegisterCompilationStartAction(CreateAnalyzerWithinCompilation); @@ -140,7 +172,54 @@ public void CreateAnalyzerWithinCompilation(CompilationStartAnalysisContext cont => context.RegisterAdditionalFileAction(AnalyzeCompilation); public void AnalyzeCompilation(AdditionalFileAnalysisContext context) - => context.ReportDiagnostic(Diagnostic.Create(_descriptor, + => context.ReportDiagnostic(Diagnostic.Create(Descriptor, location: Location.Create(context.AdditionalFile.Path, Text.TextSpan.FromBounds(0, 0), new Text.LinePositionSpan(new Text.LinePosition(0, 0), new Text.LinePosition(0, 0))), "args")); } + + [Export(typeof(IDiagnosticSourceProvider)), Shared, PartNotDiscoverable] + [method: ImportingConstructor] + [method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + internal sealed class TestAdditionalFileDocumentSourceProvider() : IDiagnosticSourceProvider + { + internal const string DiagnosticSourceProviderName = "TestAdditionalFileSource"; + + bool IDiagnosticSourceProvider.IsDocument => true; + + string IDiagnosticSourceProvider.Name => DiagnosticSourceProviderName; + + bool IDiagnosticSourceProvider.IsEnabled(LSP.ClientCapabilities clientCapabilities) => true; + + ValueTask> IDiagnosticSourceProvider.CreateDiagnosticSourcesAsync(RequestContext context, CancellationToken cancellationToken) + { + if (context.TextDocument is not null && context.TextDocument is not Document) + { + return new([new TestAdditionalFileDocumentSource(context.TextDocument!)]); + } + + return new([]); + } + + private class TestAdditionalFileDocumentSource(TextDocument textDocument) : IDiagnosticSource + { + public Task> GetDiagnosticsAsync(RequestContext context, CancellationToken cancellationToken) + { + var diagnostic = Diagnostic.Create(MockAdditionalFileDiagnosticAnalyzer.Descriptor, + location: Location.Create(context.TextDocument!.FilePath!, Text.TextSpan.FromBounds(0, 0), new Text.LinePositionSpan(new Text.LinePosition(0, 0), new Text.LinePosition(0, 0))), "args"); + return Task.FromResult>([DiagnosticData.Create(diagnostic, context.TextDocument.Project)]); + } + + public LSP.TextDocumentIdentifier? GetDocumentIdentifier() => new LSP.TextDocumentIdentifier + { + DocumentUri = textDocument.GetURI() + }; + + public ProjectOrDocumentId GetId() => new(textDocument.Id); + + public Project GetProject() => textDocument.Project; + + public bool IsLiveSource() => true; + + public string ToDisplayString() => textDocument.ToString()!; + } + } } diff --git a/src/VisualStudio/CSharp/Impl/CSharpPackage.cs b/src/VisualStudio/CSharp/Impl/CSharpPackage.cs index bd22bb8b85c95..51b9e1a423311 100644 --- a/src/VisualStudio/CSharp/Impl/CSharpPackage.cs +++ b/src/VisualStudio/CSharp/Impl/CSharpPackage.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel.Design; using System.Runtime.InteropServices; using System.Threading; using Microsoft.CodeAnalysis; @@ -52,6 +53,7 @@ namespace Microsoft.VisualStudio.LanguageServices.CSharp.LanguageService; [ProvideLanguageEditorOptionPage(typeof(Options.NamingStylesOptionPage), "CSharp", @"Code Style", "Naming", pageNameResourceId: "#115", keywordListResourceId: 314)] [ProvideLanguageEditorOptionPage(typeof(Options.IntelliSenseOptionPage), "CSharp", null, "IntelliSense", pageNameResourceId: "#103", keywordListResourceId: 312)] [ProvideSettingsManifest(PackageRelativeManifestFile = @"UnifiedSettings\csharpSettings.registration.json")] +[ProvideService(typeof(ICSharpTempPECompilerService), IsAsyncQueryable = false, IsCacheable = true, IsFreeThreaded = true, ServiceName = "C# TempPE Compiler Service")] [Guid(Guids.CSharpPackageIdString)] internal sealed class CSharpPackage : AbstractPackage, IVsUserSettingsQuery { @@ -69,10 +71,9 @@ private Task PackageInitializationBackgroundThreadAsync(PackageLoadTasks package { try { - this.RegisterService(async ct => + ((IServiceContainer)this).AddService(typeof(ICSharpTempPECompilerService), (_, _) => { var workspace = this.ComponentModel.GetService(); - await JoinableTaskFactory.SwitchToMainThreadAsync(ct); return new TempPECompilerService(workspace.Services.GetService()); }); } diff --git a/src/VisualStudio/CSharp/Impl/PackageRegistration.pkgdef b/src/VisualStudio/CSharp/Impl/PackageRegistration.pkgdef index 1a5637a51aad2..6de5ab0d67479 100644 --- a/src/VisualStudio/CSharp/Impl/PackageRegistration.pkgdef +++ b/src/VisualStudio/CSharp/Impl/PackageRegistration.pkgdef @@ -34,11 +34,6 @@ "Name"="C# Language Service" "IsAsyncQueryable"=dword:00000001 -[$RootKey$\Services\{dba64c84-56df-4e20-8aa6-02332a97f474}] -@="{13c3bbb4-f18f-4111-9f54-a0fb010d9194}" -"Name"="C# TempPE Compiler Service" -"IsAsyncQueryable"=dword:00000001 - [$RootKey$\AutomationProperties\TextEditor\CSharp-Specific] @="#104" "Description"="#105" diff --git a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs index 63d18eda90210..4cfe2ae247343 100644 --- a/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs +++ b/src/VisualStudio/Core/Def/LanguageService/AbstractPackage`2.cs @@ -136,9 +136,6 @@ protected override async Task LoadComponentsAsync(CancellationToken cancellation protected abstract IEnumerable CreateEditorFactories(); protected abstract TLanguageService CreateLanguageService(); - protected void RegisterService(Func> serviceCreator) - => AddService(typeof(T), async (container, cancellationToken, type) => await serviceCreator(cancellationToken).ConfigureAwait(true), promote: true); - // When registering a language service, we need to take its ComAggregate wrapper. protected void RegisterLanguageService(Type t, Func> serviceCreator) => AddService(t, async (container, cancellationToken, type) => await serviceCreator(cancellationToken).ConfigureAwait(true), promote: true); diff --git a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb index 1889c4c1ab9e0..90f17e9234ec4 100644 --- a/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb +++ b/src/VisualStudio/VisualBasic/Impl/LanguageService/VisualBasicPackage.vb @@ -2,6 +2,7 @@ ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. +Imports System.ComponentModel.Design Imports System.Runtime.InteropServices Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.ErrorReporting @@ -37,6 +38,7 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic + Friend NotInheritable Class VisualBasicPackage Inherits AbstractPackage(Of VisualBasicPackage, VisualBasicLanguageService) @@ -73,12 +75,9 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic Try RegisterLanguageService(GetType(IVbCompilerService), Function() Task.FromResult(_comAggregate)) - RegisterService(Of IVbTempPECompilerFactory)( - Async Function(ct) - Dim workspace = Me.ComponentModel.GetService(Of VisualStudioWorkspace)() - Await JoinableTaskFactory.SwitchToMainThreadAsync(ct) - Return New TempPECompilerFactory(workspace) - End Function) + DirectCast(Me, IServiceContainer).AddService( + GetType(IVbTempPECompilerFactory), + Function(_1, _2) New TempPECompilerFactory(Me.ComponentModel.GetService(Of VisualStudioWorkspace)())) Catch ex As Exception When FatalError.ReportAndPropagateUnlessCanceled(ex) Throw ExceptionUtilities.Unreachable End Try diff --git a/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef b/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef index 04cf35e3f6e42..a0c8dc331b294 100644 --- a/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef +++ b/src/VisualStudio/VisualBasic/Impl/PackageRegistration.pkgdef @@ -32,11 +32,6 @@ "Name"="Visual Basic Project System Shim" "IsAsyncQueryable"=dword:00000001 -[$RootKey$\Services\{8df9750d-069b-4b81-973a-152e97420c5c}] -@="{574fc912-f74f-4b4e-92c3-f695c208a2bb}" -"Name"="Visual Basic TempPE Compiler Factory Service" -"IsAsyncQueryable"=dword:00000001 - [$RootKey$\AutomationProperties\TextEditor\Basic-Specific] @="#104" "Description"="#106" diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs index c6ba0e34aaafd..efa6b676b9ec7 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Extensions/Symbols/ISymbolExtensions.cs @@ -11,6 +11,8 @@ using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime; +using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Utilities; @@ -795,16 +797,41 @@ public static bool IsSymbolWithSpecialDiscardName(this ISymbol symbol) (symbol.Name.Length == 1 || uint.TryParse(symbol.Name[1..], out _) || symbol.Name.All(n => n.Equals('_'))); /// - /// Returns , if the symbol is marked with the . + /// Returns , if the symbol is marked with the and does + /// not have the attribute. /// - /// - /// if the symbol is marked with the . + /// + /// The compiler will emit ObsoleteAttributes on symbols along with CompilerFeatureRequiredAttribute to indicate + /// that the symbol is conditionally obsolete, depending on if the target compiler supports that particular + /// feature or not. This information is then used just to tell the user a specific message, it is not actually + /// intended to indicate the symbol is actually obsolete. As such, we return 'false' here as this check is + /// intended for use by features that use the traditional concept of an actual [Obsolete] attribute added + /// in source by the user themselves. + /// public static bool IsObsolete(this ISymbol symbol) - => symbol.GetAttributes().Any(static x => x.AttributeClass is + { + if (symbol.GetAttributes().Any(static x => x.AttributeClass is + { + MetadataName: nameof(ObsoleteAttribute), + ContainingNamespace.Name: nameof(System), + ContainingNamespace.ContainingNamespace.IsGlobalNamespace: true, + })) { - MetadataName: nameof(ObsoleteAttribute), - ContainingNamespace.Name: nameof(System), - }); + if (!symbol.GetAttributes().Any(static x => x.AttributeClass is + { + MetadataName: nameof(CompilerFeatureRequiredAttribute), + ContainingNamespace.Name: nameof(System.Runtime.CompilerServices), + ContainingNamespace.ContainingNamespace.Name: nameof(System.Runtime), + ContainingNamespace.ContainingNamespace.ContainingNamespace.Name: nameof(System), + ContainingNamespace.ContainingNamespace.ContainingNamespace.ContainingNamespace.IsGlobalNamespace: true, + })) + { + return true; + } + } + + return false; + } public static bool HasAttribute([NotNullWhen(true)] this ISymbol? symbol, [NotNullWhen(true)] INamedTypeSymbol? attributeClass) { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs index 57dcf24f1f50e..44ca77938ca4f 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs @@ -33,24 +33,25 @@ private CSharpSemanticFactsService() protected override SyntaxToken ToIdentifierToken(string identifier) => identifier.ToIdentifierToken(); - protected override IEnumerable GetCollidableSymbols(SemanticModel semanticModel, SyntaxNode location, SyntaxNode container, CancellationToken cancellationToken) + protected override IEnumerable GetCollidableSymbols(SemanticModel semanticModel, SyntaxNode location, SyntaxNode? container, CancellationToken cancellationToken) { // Get all the symbols visible to the current location. var visibleSymbols = semanticModel.LookupSymbols(location.SpanStart); // Local function parameter is allowed to shadow variables since C# 8. - if (semanticModel.Compilation.LanguageVersion().MapSpecifiedToEffectiveVersion() >= LanguageVersion.CSharp8) + // Similarly, a nested primary constructor parameter list can shadow outer parameters in outer types. + var languageVersion = semanticModel.Compilation.LanguageVersion().MapSpecifiedToEffectiveVersion(); + var isLanguageVersionGreaterOrEqualToCSharp8 = languageVersion >= LanguageVersion.CSharp8; + if (isLanguageVersionGreaterOrEqualToCSharp8 && + SyntaxFacts.IsParameterList(container)) { - if (SyntaxFacts.IsParameterList(container) && SyntaxFacts.IsLocalFunctionStatement(container.Parent)) - { + if (container.Parent is LocalFunctionStatementSyntax or TypeDeclarationSyntax) visibleSymbols = visibleSymbols.WhereAsArray(s => !s.MatchesKind(SymbolKind.Local, SymbolKind.Parameter)); - } } // Some symbols in the enclosing block could cause conflicts even if they are not available at the location. // E.g. symbols inside if statements / try catch statements. - var symbolsInBlock = semanticModel.GetAllDeclaredSymbols(container, cancellationToken, - descendInto: n => ShouldDescendInto(n)); + var symbolsInBlock = semanticModel.GetAllDeclaredSymbols(container, cancellationToken, descendInto: ShouldDescendInto); return symbolsInBlock.Concat(visibleSymbols); @@ -60,10 +61,7 @@ protected override IEnumerable GetCollidableSymbols(SemanticModel seman // b) Symbols declared inside the local function do not cause collisions with symbols declared outside them, so avoid considering those symbols. // Exclude lambdas as well when the language version is C# 8 or higher because symbols declared inside no longer collide with outer variables. bool ShouldDescendInto(SyntaxNode node) - { - var isLanguageVersionGreaterOrEqualToCSharp8 = (semanticModel.Compilation as CSharpCompilation)?.LanguageVersion >= LanguageVersion.CSharp8; - return isLanguageVersionGreaterOrEqualToCSharp8 ? !SyntaxFacts.IsAnonymousOrLocalFunction(node) : !SyntaxFacts.IsLocalFunctionStatement(node); - } + => isLanguageVersionGreaterOrEqualToCSharp8 ? !SyntaxFacts.IsAnonymousOrLocalFunction(node) : !SyntaxFacts.IsLocalFunctionStatement(node); } public bool IsExpressionContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs index 12f64b48eb158..3644a722d669a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/AbstractSemanticFactsService.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -#nullable disable - using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -38,7 +36,7 @@ SymbolKind.Field or s is { Kind: SymbolKind.NamedType, IsStatic: true }; public SyntaxToken GenerateUniqueName( - SemanticModel semanticModel, SyntaxNode location, SyntaxNode containerOpt, + SemanticModel semanticModel, SyntaxNode location, SyntaxNode? containerOpt, string baseName, CancellationToken cancellationToken) { return GenerateUniqueName( @@ -46,7 +44,7 @@ public SyntaxToken GenerateUniqueName( } public SyntaxToken GenerateUniqueName( - SemanticModel semanticModel, SyntaxNode location, SyntaxNode containerOpt, + SemanticModel semanticModel, SyntaxNode location, SyntaxNode? containerOpt, string baseName, IEnumerable usedNames, CancellationToken cancellationToken) { return GenerateUniqueName( @@ -54,7 +52,7 @@ public SyntaxToken GenerateUniqueName( } public SyntaxToken GenerateUniqueLocalName( - SemanticModel semanticModel, SyntaxNode location, SyntaxNode containerOpt, + SemanticModel semanticModel, SyntaxNode location, SyntaxNode? containerOpt, string baseName, CancellationToken cancellationToken) { return GenerateUniqueName( @@ -62,7 +60,7 @@ public SyntaxToken GenerateUniqueLocalName( } public SyntaxToken GenerateUniqueLocalName( - SemanticModel semanticModel, SyntaxNode location, SyntaxNode containerOpt, + SemanticModel semanticModel, SyntaxNode location, SyntaxNode? containerOpt, string baseName, IEnumerable usedNames, CancellationToken cancellationToken) { return GenerateUniqueName( @@ -71,10 +69,15 @@ public SyntaxToken GenerateUniqueLocalName( public SyntaxToken GenerateUniqueName( SemanticModel semanticModel, - SyntaxNode location, SyntaxNode containerOpt, - string baseName, Func filter, - IEnumerable usedNames, CancellationToken cancellationToken) + SyntaxNode location, + SyntaxNode? containerOpt, + string baseName, + Func? filter, + IEnumerable? usedNames, + CancellationToken cancellationToken) { + usedNames ??= []; + var container = containerOpt ?? location.AncestorsAndSelf().FirstOrDefault( a => BlockFacts.IsExecutableBlock(a) || SyntaxFacts.IsParameterList(a) || SyntaxFacts.IsMethodBody(a)); @@ -89,7 +92,7 @@ public SyntaxToken GenerateUniqueName( /// A symbol can possibly collide with the location if it is available to that location and/or /// could cause a compiler error if its name is re-used at that location. /// - protected virtual IEnumerable GetCollidableSymbols(SemanticModel semanticModel, SyntaxNode location, SyntaxNode container, CancellationToken cancellationToken) + protected virtual IEnumerable GetCollidableSymbols(SemanticModel semanticModel, SyntaxNode location, SyntaxNode? container, CancellationToken cancellationToken) => semanticModel.LookupSymbols(location.SpanStart).Concat(semanticModel.GetAllDeclaredSymbols(container, cancellationToken)); public SyntaxToken GenerateUniqueName(string baseName, IEnumerable usedNames) @@ -99,8 +102,6 @@ public SyntaxToken GenerateUniqueName(string baseName, IEnumerable usedN baseName, usedNames, this.SyntaxFacts.IsCaseSensitive)); } -#nullable enable - protected static IMethodSymbol? FindDisposeMethod(Compilation compilation, ITypeSymbol? type, bool isAsync) { if (type is null) @@ -167,8 +168,6 @@ public SyntaxToken GenerateUniqueName(string baseName, IEnumerable usedN } } -#nullable disable - #region ISemanticFacts implementation public bool SupportsImplicitInterfaceImplementation => SemanticFacts.SupportsImplicitInterfaceImplementation; @@ -177,31 +176,31 @@ public SyntaxToken GenerateUniqueName(string baseName, IEnumerable usedN public bool ExposesAnonymousFunctionParameterNames => SemanticFacts.ExposesAnonymousFunctionParameterNames; - public bool IsWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + public bool IsWrittenTo(SemanticModel semanticModel, SyntaxNode? node, CancellationToken cancellationToken) => SemanticFacts.IsWrittenTo(semanticModel, node, cancellationToken); - public bool IsOnlyWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + public bool IsOnlyWrittenTo(SemanticModel semanticModel, SyntaxNode? node, CancellationToken cancellationToken) => SemanticFacts.IsOnlyWrittenTo(semanticModel, node, cancellationToken); - public bool IsInOutContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + public bool IsInOutContext(SemanticModel semanticModel, SyntaxNode? node, CancellationToken cancellationToken) => SemanticFacts.IsInOutContext(semanticModel, node, cancellationToken); - public bool IsInRefContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + public bool IsInRefContext(SemanticModel semanticModel, SyntaxNode? node, CancellationToken cancellationToken) => SemanticFacts.IsInRefContext(semanticModel, node, cancellationToken); - public bool IsInInContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + public bool IsInInContext(SemanticModel semanticModel, SyntaxNode? node, CancellationToken cancellationToken) => SemanticFacts.IsInInContext(semanticModel, node, cancellationToken); - public bool CanReplaceWithRValue(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken) + public bool CanReplaceWithRValue(SemanticModel semanticModel, SyntaxNode? expression, CancellationToken cancellationToken) => SemanticFacts.CanReplaceWithRValue(semanticModel, expression, cancellationToken); - public ISymbol GetDeclaredSymbol(SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken) + public ISymbol? GetDeclaredSymbol(SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken) => SemanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken); public bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol) => SemanticFacts.LastEnumValueHasInitializer(namedTypeSymbol); - public bool TryGetSpeculativeSemanticModel(SemanticModel oldSemanticModel, SyntaxNode oldNode, SyntaxNode newNode, out SemanticModel speculativeModel) + public bool TryGetSpeculativeSemanticModel(SemanticModel oldSemanticModel, SyntaxNode oldNode, SyntaxNode newNode, [NotNullWhen(true)] out SemanticModel? speculativeModel) => SemanticFacts.TryGetSpeculativeSemanticModel(oldSemanticModel, oldNode, newNode, out speculativeModel); public ImmutableHashSet GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken) @@ -213,7 +212,7 @@ public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode public SymbolInfo GetCollectionInitializerSymbolInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) => SemanticFacts.GetCollectionInitializerSymbolInfo(semanticModel, node, cancellationToken); - public IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node) + public IMethodSymbol? GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node) => SemanticFacts.GetGetAwaiterMethod(semanticModel, node); public ImmutableArray GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node) @@ -228,42 +227,42 @@ public bool IsPartial(INamedTypeSymbol typeSymbol, CancellationToken cancellatio public IEnumerable GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken) => SemanticFacts.GetDeclaredSymbols(semanticModel, memberDeclaration, cancellationToken); - public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) + public IParameterSymbol? FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) => SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, allowUncertainCandidates, allowParams, cancellationToken); - public IParameterSymbol FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) + public IParameterSymbol? FindParameterForAttributeArgument(SemanticModel semanticModel, SyntaxNode argumentNode, bool allowUncertainCandidates, bool allowParams, CancellationToken cancellationToken) => SemanticFacts.FindParameterForAttributeArgument(semanticModel, argumentNode, allowUncertainCandidates, allowParams, cancellationToken); - public ISymbol FindFieldOrPropertyForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken) + public ISymbol? FindFieldOrPropertyForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken) => SemanticFacts.FindFieldOrPropertyForArgument(semanticModel, argumentNode, cancellationToken); - public ISymbol FindFieldOrPropertyForAttributeArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken) + public ISymbol? FindFieldOrPropertyForAttributeArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken) => SemanticFacts.FindFieldOrPropertyForAttributeArgument(semanticModel, argumentNode, cancellationToken); - public ImmutableArray GetBestOrAllSymbols(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken) + public ImmutableArray GetBestOrAllSymbols(SemanticModel semanticModel, SyntaxNode? node, SyntaxToken token, CancellationToken cancellationToken) => SemanticFacts.GetBestOrAllSymbols(semanticModel, node, token, cancellationToken); - public bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken) + public bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode? node, CancellationToken cancellationToken) => SemanticFacts.IsInsideNameOfExpression(semanticModel, node, cancellationToken); public ImmutableArray GetLocalFunctionSymbols(Compilation compilation, ISymbol symbol, CancellationToken cancellationToken) => SemanticFacts.GetLocalFunctionSymbols(compilation, symbol, cancellationToken); - public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol expressionTypeOpt, CancellationToken cancellationToken) + public bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol? expressionTypeOpt, CancellationToken cancellationToken) => SemanticFacts.IsInExpressionTree(semanticModel, node, expressionTypeOpt, cancellationToken); public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken) => SemanticFacts.GenerateNameForExpression(semanticModel, expression, capitalize, cancellationToken); - public IPreprocessingSymbol GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node) + public IPreprocessingSymbol? GetPreprocessingSymbol(SemanticModel semanticModel, SyntaxNode node) => SemanticFacts.GetPreprocessingSymbol(semanticModel, node); - public bool TryGetPrimaryConstructor(INamedTypeSymbol typeSymbol, [NotNullWhen(true)] out IMethodSymbol primaryConstructor) + public bool TryGetPrimaryConstructor(INamedTypeSymbol typeSymbol, [NotNullWhen(true)] out IMethodSymbol? primaryConstructor) => SemanticFacts.TryGetPrimaryConstructor(typeSymbol, out primaryConstructor); #if WORKSPACE - public Task GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken) + public Task GetInterceptorSymbolAsync(Document document, int position, CancellationToken cancellationToken) => SemanticFacts.GetInterceptorSymbolAsync(document, position, cancellationToken); #endif