-
Notifications
You must be signed in to change notification settings - Fork 676
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Cannot navigate between symbols in the same decompiled file #4818
Comments
I tried VS Enterprise 2019 16.10.4. it has no issue to further decompile the same stuff from the generated decompiled code. I noticed VS2019 uses #region Assembly Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
// C:\Users\???\.nuget\packages\newtonsoft.json\12.0.3\lib\netstandard2.0\Newtonsoft.Json.dll
// Decompiled with ICSharpCode.Decompiler 6.1.0.5902
#endregion
... while VSCode C# Extension uses #region Assembly Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
// Newtonsoft.Json.dll
// Decompiled with ICSharpCode.Decompiler 7.1.0.6543
...
#endregion |
Thanks for reporting this. This is not related to ILSpy version. The problem is when navigating within the same file (that's what you do in step 4). It works fine when navigating across different files (that's what you do in step 5). I will leave this issue here, but I suspect this has to be fixed in the OmniSharp server. |
Hi. |
I investigated this issue. Turned out it's quite tricky. See below code, when it's a decompiled source file (or called document in the implementation), it tried to add the decompiled document through Extracted from src\OmniSharp.Roslyn.CSharp\Services\Navigation\GotoDefinitionServiceV2.cs if (symbol.Locations[0].IsInSource)
{
return new GotoDefinitionResponse()
{
Definitions = symbol.Locations
.Select(location => new Definition
{
Location = location.GetMappedLineSpan().GetLocationFromFileLinePositionSpan(),
SourceGeneratedFileInfo = GoToDefinitionHelpers.GetSourceGeneratedFileInfo(_workspace, location)
})
.ToList()
};
}
else
{
var maybeSpan = await GoToDefinitionHelpers.GetMetadataMappedSpan(document, symbol, externalSourceService, cancellationToken);
if (maybeSpan is FileLinePositionSpan lineSpan)
{
return new GotoDefinitionResponse
{
Definitions = new()
{
new Definition
{
Location = lineSpan.GetLocationFromFileLinePositionSpan(),
MetadataSource = new OmniSharp.Models.Metadata.MetadataSource()
{
AssemblyName = symbol.ContainingAssembly.Name,
ProjectName = document.Project.Name,
TypeName = symbol.GetSymbolName()
}
}
}
};
}
return new GotoDefinitionResponse();
} But in the class Microsoft.CodeAnalysis.Solution's AddDocuments(...) (from the lib Microsoft.CodeAnalysis.Workspaces.dll), because public Solution AddDocuments(ImmutableArray<DocumentInfo> documentInfos)
{
SolutionState state = this._state.AddDocuments(documentInfos);
return state == this._state ? this : new Solution(state);
} So far so good. But later deeper in the call stack, it throws away the just newly created Solution instance! See this statement public Document AddDocument(string name, string text, IEnumerable<string>? folders = null, string? filePath = null)
{
DocumentId newId = DocumentId.CreateNewId(this.Id, name);
return this.Solution.AddDocument(newId, name, text, folders, filePath).GetDocument(newId);
} So, next time when trying to Go To another definition within the same decompiled document, it cannot find the it in the Solution. Extracted from src\OmniSharp.Roslyn.CSharp\Services\Navigation\GotoDefinitionServiceV2.cs if (symbol.Locations[0].IsInSource)
{
return new GotoDefinitionResponse()
{
Definitions = symbol.Locations
.Select(location => new Definition
{
Location = location.GetMappedLineSpan().GetLocationFromFileLinePositionSpan(),
SourceGeneratedFileInfo = GoToDefinitionHelpers.GetSourceGeneratedFileInfo(_workspace, location)
})
.ToList()
};
}
else
{
...
} |
As all those relevant ones are readonly and in a 3rd lib, it cannot be fixed by adding the new decompiled document straightforward. But I did go further to try a fix by following the way how the normal source files have been added to the Solution instance. So I passed the OmniSharpWorkspace instance Extracted from src\OmniSharp.Roslyn.CSharp\Services\Navigation\GotoDefinitionServiceV2.cs if (symbol.Locations[0].IsInSource)
{
...
}
else
{
var maybeSpan = await GoToDefinitionHelpers.GetMetadataMappedSpan(this._workspace, document, symbol, externalSourceService, cancellationToken); // <= Pass the OmniSharpWorkspace instance `this._workspace`
if (maybeSpan is FileLinePositionSpan lineSpan)
{
return new GotoDefinitionResponse
{
Definitions = new()
{
new Definition
{
Location = lineSpan.GetLocationFromFileLinePositionSpan(),
MetadataSource = new OmniSharp.Models.Metadata.MetadataSource()
{
AssemblyName = symbol.ContainingAssembly.Name,
ProjectName = document.Project.Name,
TypeName = symbol.GetSymbolName()
}
}
}
};
}
return new GotoDefinitionResponse();
}
} Then in src\OmniSharp.Roslyn.CSharp\Services\Decompilation\DecompilationExternalSourceService.cs, call public async Task<(Document document, string documentPath)> GetAndAddExternalSymbolDocument(OmniSharpWorkspace workspace, Project project, ISymbol symbol, CancellationToken cancellationToken)
{
...
if (!_cache.TryGetValue(fileName, out var document))
{
var topLevelSymbol = symbol.GetTopLevelContainingNamedType();
var temporaryDocument = decompilationProject.AddDocument(fileName, string.Empty);
var compilation = await decompilationProject.GetCompilationAsync();
document = await _service.Value.AddSourceToAsync(temporaryDocument, compilation, topLevelSymbol, cancellationToken);
VersionStamp version = VersionStamp.Create();
TextLoader loader = TextLoader.From(TextAndVersion.Create(Microsoft.CodeAnalysis.Text.SourceText.From(string.Empty), version, fileName));
workspace.AddDocument(DocumentInfo.Create(temporaryDocument.Id, fileName, null, temporaryDocument?.Project?.ParseOptions?.Kind ?? SourceCodeKind.Regular, loader, null, false));
_cache.TryAdd(fileName, document);
}
return (document, fileName);
} As a result, the decompiled document is successfully added to the Solution. Next time when trying to Go To another definition within the same decompiled document, it is able to find the the decompiled document in the Solution. During debugging, now I can see that public TState? GetState(DocumentId documentId)
{
TState state;
return !this._map.TryGetValue(documentId, out state) ? default (TState) : state;
} But after retrieving the decompiled document by internal DocumentState? GetDocumentState(
SyntaxTree? syntaxTree,
ProjectId? projectId)
{
if (syntaxTree != null)
{
DocumentId documentIdForTree = DocumentState.GetDocumentIdForTree(syntaxTree);
if (documentIdForTree != (DocumentId) null && (projectId == (ProjectId) null || documentIdForTree.ProjectId == projectId))
{
ProjectState projectState = this.GetProjectState(documentIdForTree.ProjectId);
if (projectState != null)
{
DocumentState state = projectState.DocumentStates.GetState(documentIdForTree);
if (state != null)
{
SyntaxTree syntaxTree1;
if (state.TryGetSyntaxTree(out syntaxTree1) && syntaxTree1 == syntaxTree)
return state;
}
else
{
SourceGeneratedDocumentState alreadyGeneratedId = this.TryGetSourceGeneratedDocumentStateForAlreadyGeneratedId(documentIdForTree);
SyntaxTree syntaxTree2;
if (alreadyGeneratedId != null && alreadyGeneratedId.TryGetSyntaxTree(out syntaxTree2) && syntaxTree2 == syntaxTree)
return (DocumentState) alreadyGeneratedId;
}
}
}
}
return (DocumentState) null;
} |
So I cannot find an elegant way to fix this issue. Look like there may be some flaw in the V2 Go To Definition feature. But I figured out a workaround for now, and it does work actually. Hope it helps. Here is the PR, please have a look. @filipw, @JoeRobich |
…/investigation Workaround for the issue dotnet/vscode-csharp#4818
Close it as the PR has been merged😄 |
Able to reproduce with extension version 1.25.2 with symbols in different files. .NET SDKs installed: Repro: using Microsoft.Extensions.DependencyInjection;
var fooBarService = serviceProvider.GetRequiredService<FooBarService>(); // <-- go to GetRequiredService definition Is it decompiled as ServiceProviderServiceExtensions.cs: public static T GetRequiredService<T>(this IServiceProvider provider) where T : notnull
{
ThrowHelper.ThrowIfNull(provider, "provider");
return (T)provider.GetRequiredService(typeof(T));
} You are able to navigate to ThrowHelper (ThrowHelper.cs), however you are unable to navigate to ThrowIfNull method (also ThrowHelper.cs).
*mediatr-contravariance is my proj name. |
I still get this error when using "go to definition" on a symbol defined in the same file
VSCode "About" info
C# extension: |
I am getting this problem too. using latest C# extension and latest dotnet SDK |
Environment data
dotnet --info
output:.NET SDK (reflecting any global.json):
Version: 5.0.302
Commit: c005824e35
Runtime Environment:
OS Name: Windows
OS Version: 10.0.18363
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\5.0.302\
Host (useful for support):
Version: 5.0.8
Commit: 35964c9215
.NET SDKs installed:
2.2.402 [C:\Program Files\dotnet\sdk]
5.0.100 [C:\Program Files\dotnet\sdk]
5.0.302 [C:\Program Files\dotnet\sdk]
.NET runtimes installed:
Microsoft.AspNetCore.All 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 5.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.28 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.7 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 5.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
VS Code version: 1.60.2
C# Extension version: 1.23.16
OmniSharp log
Steps to reproduce
%USERPROFILE%/.omnisharp/omnisharp.json
JsonConvert.DeserializeObject()
in[metadata] JsonConvert.cs
which is good.When clicking on
DeserializeObject
inreturn JsonConvert.DeserializeObject<T>(value, (JsonSerializerSettings?)null);
, the abovewarning
message shows up in the OmniSharp log.Hitting F12 to further decompile
DeserializeObject
inreturn JsonConvert.DeserializeObject<T>(value, (JsonSerializerSettings?)null);
results in pop up showing thatwarning
message as belowBut if clicking on
JsonSerializerSettings
inreturn JsonConvert.DeserializeObject<T>(value, (JsonSerializerSettings?)null);
, even the abovewarning
message also shows up in the OmniSharp log,but hitting F12 to further decompile
JsonSerializerSettings
actually succeeds without issues in this case.Expected behavior
Should be able to further decompile the from the decompiled code.
Actual behavior
Looks like many objects aren't able to be further compiled successfully from the decompiled code, while a few are able to. So the user experience isn't good. When i use
ILSpy
directly to decompile the same DLLs there's no problem.The text was updated successfully, but these errors were encountered: