Skip to content

Commit

Permalink
Fix global keyword null exception (#9)
Browse files Browse the repository at this point in the history
* Add global keyword test

* Bypass global alias emit
  • Loading branch information
tcz717 authored Jan 24, 2022
1 parent 19c8de8 commit 1214338
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 27 deletions.
108 changes: 108 additions & 0 deletions LsifDotnet.Tests/KeywordTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using LsifDotnet.Lsif;
using LsifDotnet.Roslyn;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

namespace LsifDotnet.Tests
{
public class KeywordTest
{
/// <summary>
/// The adhoc workspace.
/// </summary>
private readonly AdhocWorkspace _adhocWorkspace;

private readonly Project _project;

private readonly ServiceProvider _serviceProvider;

public KeywordTest()
{
this._adhocWorkspace = new AdhocWorkspace();
this._project = AddProject(nameof(KeywordTest));
this._serviceProvider = new ServiceCollection()
.AddLogging()
.AddTransient<IdentifierCollectorFactory>()
.AddTransient<LsifIndexer>()
.AddSingleton(this._adhocWorkspace as Workspace)
.BuildServiceProvider();
}

private Project AddProject(string projectName)
{
return this._adhocWorkspace.AddProject(ProjectInfo.Create(ProjectId.CreateNewId(),
VersionStamp.Create(),
projectName, projectName, "C#", metadataReferences: new[]
{
MetadataReference.CreateFromFile(typeof(object).Assembly.Location)
}));
}

[Fact]
public async Task GlobalKeywordTest()
{
var code1 = SourceText.From(
@"// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute("".NETCoreApp,Version=v6.0"", FrameworkDisplayName = """")]");

var code2 = SourceText.From(@"class Test : global::System.object {} // Adding extra global symbol");

this._adhocWorkspace.AddDocument(this._project.Id, $"{nameof(this.GlobalKeywordTest)}.1.cs", code1);
this._adhocWorkspace.AddDocument(AddProject("SecondProject").Id, $"{nameof(this.GlobalKeywordTest)}.2.cs", code2);

var indexer = this._serviceProvider.GetRequiredService<LsifIndexer>();

await foreach (var item in indexer.EmitLsif())
{
Assert.NotNull(item);
}
}

/// <summary>
/// See https://github.com/dotnet/roslyn/issues/58999
/// </summary>
/// <returns></returns>
[Fact]
public async Task RoslynAliasEqualBugTest()
{
{
var code1 = SourceText.From(
@"// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute("".NETCoreApp,Version=v6.0"", FrameworkDisplayName = """")]");

var code2 = SourceText.From(@"using global::System.IO; // Adding extra global symbol");

var doc1 = this._adhocWorkspace.AddDocument(this._project.Id, $"{nameof(this.GlobalKeywordTest)}.1.cs",
code1);
var doc2 = this._adhocWorkspace.AddDocument(this._project.Id, $"{nameof(this.GlobalKeywordTest)}.2.cs",
code2);

var globalSymbol1 = (await doc1.GetSemanticModelAsync())!
.GetAliasInfo((await doc1.GetSyntaxRootAsync())!
.DescendantNodes()
.OfType<IdentifierNameSyntax>()
.First(ident => ident.Identifier.Text == "global"));
var globalSymbol2 = (await doc2.GetSemanticModelAsync())!
.GetAliasInfo((await doc2.GetSyntaxRootAsync())!
.DescendantNodes()
.OfType<IdentifierNameSyntax>()
.First(ident => ident.Identifier.Text == "global"));

Assert.NotNull(globalSymbol1);
Assert.NotNull(globalSymbol2);

Assert.Throws<NullReferenceException>(() => globalSymbol1!.Equals(globalSymbol2));
}
}
}
}
27 changes: 27 additions & 0 deletions LsifDotnet.Tests/LsifDotnet.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\LsifDotnet\LsifDotnet.csproj" />
</ItemGroup>

</Project>
12 changes: 9 additions & 3 deletions LsifDotnet.sln
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32002.261
# Visual Studio Version 17
VisualStudioVersion = 17.1.32104.313
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LsifDotnet", "LsifDotnet\LsifDotnet.csproj", "{B426E5B4-869F-4B5B-A9DE-DAC355A98301}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LsifDotnet", "LsifDotnet\LsifDotnet.csproj", "{B426E5B4-869F-4B5B-A9DE-DAC355A98301}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LsifDotnet.Tests", "LsifDotnet.Tests\LsifDotnet.Tests.csproj", "{0F90A056-0000-49A9-86F7-99A4F7285C41}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -15,6 +17,10 @@ Global
{B426E5B4-869F-4B5B-A9DE-DAC355A98301}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B426E5B4-869F-4B5B-A9DE-DAC355A98301}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B426E5B4-869F-4B5B-A9DE-DAC355A98301}.Release|Any CPU.Build.0 = Release|Any CPU
{0F90A056-0000-49A9-86F7-99A4F7285C41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0F90A056-0000-49A9-86F7-99A4F7285C41}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0F90A056-0000-49A9-86F7-99A4F7285C41}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0F90A056-0000-49A9-86F7-99A4F7285C41}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
74 changes: 50 additions & 24 deletions LsifDotnet/Lsif/LsifIndexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using LsifDotnet.Roslyn;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -47,13 +48,13 @@ protected record CachedSymbolResult(int ResultSetId, int? DefinitionResultId, in
public async IAsyncEnumerable<LsifItem> EmitLsif()
{
var solution = Workspace.CurrentSolution;
yield return new MetaDataVertex(NextId(), new Uri(Path.GetDirectoryName(solution.FilePath) ?? "").AbsoluteUri);
yield return new MetaDataVertex(NextId(), ToAbsoluteUri(solution.FilePath));

foreach (var project in solution.Projects)
{
var projectId = NextId();
var documents = new List<int>();
yield return new ProjectVertex(projectId, new Uri(project.FilePath!).AbsoluteUri, project.Name);
yield return new ProjectVertex(projectId, ToAbsoluteUri(project.FilePath), project.Name);

if (project.Language != "C#")
{
Expand All @@ -65,7 +66,7 @@ public async IAsyncEnumerable<LsifItem> EmitLsif()
{
var documentId = NextId();
var ranges = new List<int>();
yield return new DocumentVertex(documentId, new Uri(document.FilePath!).AbsoluteUri);
yield return new DocumentVertex(documentId, ToAbsoluteUri(document.FilePath));
documents.Add(documentId);

var quickInfoService = QuickInfoService.GetService(document);
Expand All @@ -83,20 +84,11 @@ public async IAsyncEnumerable<LsifItem> EmitLsif()

if (!location.IsInSource)
{
Logger.LogWarning($"Skipped non in source token {token.Value}");
Logger.LogWarning($"Skipped not-in-source token {token.Value}");
continue;
}

// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (symbol is null)
{
if (NotKnownIdentifier(token))
{
Logger.LogWarning($"Symbol not found {token.Value} at {linePositionSpan}");
}

continue;
}
if (SkipSymbol(symbol, token)) continue;

var rangeVertex = new RangeVertex(NextId(), linePositionSpan);
ranges.Add(rangeVertex.Id);
Expand Down Expand Up @@ -156,22 +148,56 @@ public async IAsyncEnumerable<LsifItem> EmitLsif()
}
}

private bool SkipSymbol(ISymbol symbol, SyntaxToken token)
{
var linePositionSpan = token.GetLocation().GetMappedLineSpan();
switch (symbol)
{
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
case null:
{
if (NotKnownIdentifier(token))
{
Logger.LogWarning("Symbol not found {token.Value} at {linePositionSpan}", token.Value,
linePositionSpan);
}

return true;
}
// Bug: AliasSymbol.Equals throw NullReferenceException when comparing two global symbols https://github.com/tcz717/LsifDotnet/issues/8
// Remove this case when it is fixed
case IAliasSymbol { Name: "global" }:
Logger.LogTrace("Skipped one global symbol at {linePositionSpan}", linePositionSpan);
return true;
}

return false;
}

private static string ToAbsoluteUri(string? filePath)
{
var directoryName = Path.GetDirectoryName(filePath);
return directoryName == null ? string.Empty : new Uri(directoryName).AbsoluteUri;
}

private static bool NotKnownIdentifier(SyntaxToken token)
{
return token.Text != "nameof";
}


/// <summary>
/// Section kind for nullability analysis.
/// <para>
/// Based on https://github.com/dotnet/roslyn/blob/7dc32a952e77c96c31cae6a2ba6d253a558fc7ff/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs
/// These are internal tag values taken from https://github.com/dotnet/roslyn/blob/master/src/Features/Core/Portable/Common/TextTags.cs
/// </para>
/// <para>
/// They're copied here so that we can ensure we render blocks correctly in the markdown
/// https://github.com/dotnet/roslyn/issues/46254 tracks making these public
/// </para>
/// Section kind for nullability analysis.
/// <para>
/// Based on
/// https://github.com/dotnet/roslyn/blob/7dc32a952e77c96c31cae6a2ba6d253a558fc7ff/src/Features/LanguageServer/Protocol/Handler/Hover/HoverHandler.cs
/// These are internal tag values taken from
/// https://github.com/dotnet/roslyn/blob/master/src/Features/Core/Portable/Common/TextTags.cs
/// </para>
/// <para>
/// They're copied here so that we can ensure we render blocks correctly in the markdown
/// https://github.com/dotnet/roslyn/issues/46254 tracks making these public
/// </para>
/// </summary>
internal const string NullabilityAnalysis = nameof(NullabilityAnalysis);

Expand Down

0 comments on commit 1214338

Please sign in to comment.