From efc4ca06dd72b418862366e4369a38274946828a Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Sat, 13 Aug 2022 14:50:23 -0700 Subject: [PATCH] Return generated file info for find references Allows find references to provide information about references in source-generated files. --- .../Helpers/LocationExtensions.cs | 14 ++++-- .../Extensions/SolutionExtensions.cs | 2 +- .../FindReferencesFacts.cs | 47 +++++++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/OmniSharp.Roslyn.CSharp/Helpers/LocationExtensions.cs b/src/OmniSharp.Roslyn.CSharp/Helpers/LocationExtensions.cs index 5ab5782ba5..c0defab838 100644 --- a/src/OmniSharp.Roslyn.CSharp/Helpers/LocationExtensions.cs +++ b/src/OmniSharp.Roslyn.CSharp/Helpers/LocationExtensions.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; +using OmniSharp.Extensions; using OmniSharp.Models; namespace OmniSharp.Helpers @@ -24,12 +24,17 @@ public static QuickFix GetQuickFix(this Location location, OmniSharpWorkspace wo var sourceText = GetSourceText(location, documents, lineSpan.HasMappedPath); var text = GetLineText(location, sourceText, lineSpan.StartLinePosition.Line); - var fileName = Path.IsPathRooted(lineSpan.Path) + var generatedInfo = workspace.CurrentSolution.GetSourceGeneratedFileInfo(location); + + var fileName = Path.IsPathRooted(lineSpan.Path) || generatedInfo != null + // If there is generated file information, the path is not rooted, but we don't want to try and locate it as it doesn't + // exist on disk ? lineSpan.Path // when a #line directive maps into a separate file using a relative path, get the full path relative to the folder containing the source tree : Path.GetFullPath(Path.Combine(Path.GetDirectoryName(location.SourceTree.FilePath), lineSpan.Path)); - return new QuickFix + + return new SymbolLocation { Text = text.Trim(), FileName = fileName, @@ -37,7 +42,8 @@ public static QuickFix GetQuickFix(this Location location, OmniSharpWorkspace wo Column = lineSpan.HasMappedPath ? 0 : lineSpan.StartLinePosition.Character, // when a #line directive maps into a separate file, assume columns (0,0) EndLine = lineSpan.EndLinePosition.Line, EndColumn = lineSpan.HasMappedPath ? 0 : lineSpan.EndLinePosition.Character, - Projects = documents.Select(document => document.Project.Name).ToArray() + Projects = documents.Select(document => document.Project.Name).ToArray(), + GeneratedFileInfo = generatedInfo }; static SourceText GetSourceText(Location location, IEnumerable documents, bool hasMappedPath) diff --git a/src/OmniSharp.Roslyn/Extensions/SolutionExtensions.cs b/src/OmniSharp.Roslyn/Extensions/SolutionExtensions.cs index 31c32b9714..bcf266131b 100644 --- a/src/OmniSharp.Roslyn/Extensions/SolutionExtensions.cs +++ b/src/OmniSharp.Roslyn/Extensions/SolutionExtensions.cs @@ -96,7 +96,7 @@ private static QuickFix ConvertSymbol(Solution solution, ISymbol symbol, Locatio }; } - internal static SourceGeneratedFileInfo? GetSourceGeneratedFileInfo(Solution solution, Location location) + internal static SourceGeneratedFileInfo? GetSourceGeneratedFileInfo(this Solution solution, Location location) { Debug.Assert(location.IsInSource); var document = solution.GetDocument(location.SourceTree); diff --git a/tests/OmniSharp.Roslyn.CSharp.Tests/FindReferencesFacts.cs b/tests/OmniSharp.Roslyn.CSharp.Tests/FindReferencesFacts.cs index 4d6328b115..7039012f0e 100644 --- a/tests/OmniSharp.Roslyn.CSharp.Tests/FindReferencesFacts.cs +++ b/tests/OmniSharp.Roslyn.CSharp.Tests/FindReferencesFacts.cs @@ -1,11 +1,15 @@ using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; using OmniSharp.Models; using OmniSharp.Models.FindUsages; +using OmniSharp.Models.v1.SourceGeneratedFile; using OmniSharp.Roslyn.CSharp.Services.Navigation; +using Roslyn.Test.Utilities; using TestUtility; using Xunit; using Xunit.Abstractions; @@ -545,6 +549,49 @@ public class Foo Assert.Null(exception); } + [Fact] + public async Task ReturnsGeneratedReferences() + { + const string Source = @" +public partial class Generated +{ + public int Property { get; set; } +} +"; + const string FileName = "real.cs"; + var testFile = new TestFile(FileName, @" +class C +{ + Generate$$d G; +} +"); + + TestHelpers.AddProjectToWorkspace(SharedOmniSharpTestHost.Workspace, + "project.csproj", + new[] { "netcoreapp3.1" }, + new[] { testFile }, + analyzerRefs: ImmutableArray.Create(new TestGeneratorReference( + context => context.AddSource("GeneratedFile", Source)))); + + var response = await FindUsagesAsync(testFile, onlyThisFile: false, excludeDefinition: false); + + var result = response.QuickFixes.Cast().Single(s => s.GeneratedFileInfo is not null); + Assert.NotNull(result); + AssertEx.Equal(@"OmniSharp.Roslyn.CSharp.Tests\OmniSharp.Roslyn.CSharp.Tests.TestSourceGenerator\GeneratedFile.cs", result.FileName.Replace("/", @"\")); + + var sourceGeneratedFileHandler = SharedOmniSharpTestHost.GetRequestHandler(OmniSharpEndpoints.SourceGeneratedFile); + var sourceGeneratedRequest = new SourceGeneratedFileRequest + { + DocumentGuid = result.GeneratedFileInfo.DocumentGuid, + ProjectGuid = result.GeneratedFileInfo.ProjectGuid + }; + + var sourceGeneratedFileResponse = await sourceGeneratedFileHandler.Handle(sourceGeneratedRequest); + Assert.NotNull(sourceGeneratedFileResponse); + AssertEx.Equal(Source, sourceGeneratedFileResponse.Source); + AssertEx.Equal(@"OmniSharp.Roslyn.CSharp.Tests\OmniSharp.Roslyn.CSharp.Tests.TestSourceGenerator\GeneratedFile.cs", sourceGeneratedFileResponse.SourceName.Replace("/", @"\")); + } + private Task FindUsagesAsync(string code, bool excludeDefinition = false) { return FindUsagesAsync(new[] { new TestFile("dummy.cs", code) }, false, excludeDefinition);