Skip to content

Commit

Permalink
Merge pull request dotnet#60173 from CyrusNajmabadi/depTypeCrash
Browse files Browse the repository at this point in the history
Fix symbol-finder crash in mixed C#/TypeScript projects
  • Loading branch information
CyrusNajmabadi authored Mar 15, 2022
2 parents 0055818 + 4450a2e commit ca64a6d
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 17 deletions.
45 changes: 45 additions & 0 deletions src/EditorFeatures/Test/SymbolFinder/DependentTypeFinderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

#nullable disable

using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.CodeAnalysis.Editor.UnitTests;
Expand Down Expand Up @@ -611,5 +613,48 @@ enum E
Assert.True(enums.Any(i => i.Locations.Any(loc => loc.IsInMetadata)), "We should find a metadata enum");
Assert.Single(enums.Where(i => i.Locations.Any(loc => loc.IsInSource))); // We should find a single source type
}

[Theory, CombinatorialData]
[WorkItem(1464142, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1464142")]
public async Task DependentTypeFinderSkipsNoCompilationLanguages(TestHost host)
{
var composition = EditorTestCompositions.EditorFeatures.WithTestHostParts(host);

using var workspace = TestWorkspace.Create(
@"
<Workspace>
<Project Name=""TSProject"" Language=""TypeScript"">
<Document>
Dummy ts content
</Document>
</Project>
<Project Name=""CSProject"" Language=""C#"">
<Document>
public class Base { }
public class Derived : Base { }
</Document>
</Project>
</Workspace>", composition: composition);
var solution = workspace.CurrentSolution;

var csProject = solution.Projects.Single(p => p.Language == LanguageNames.CSharp);
var otherProject = solution.Projects.Single(p => p != csProject);
var csDoc = csProject.Documents.Single();

var semanticModel = await csDoc.GetSemanticModelAsync();
var csRoot = await csDoc.GetSyntaxRootAsync();

var firstDecl = csRoot.DescendantNodes().First(d => d is CSharp.Syntax.TypeDeclarationSyntax);
var firstType = (INamedTypeSymbol)semanticModel.GetDeclaredSymbol(firstDecl);

// Should find one result in the c# project.
var results = await SymbolFinder.FindDerivedClassesArrayAsync(firstType, solution, transitive: true, ImmutableHashSet.Create(csProject), CancellationToken.None);
Assert.Single(results);
Assert.Equal("Derived", results[0].Name);

// Should find zero results in the TS project (and should not crash).
results = await SymbolFinder.FindDerivedClassesArrayAsync(firstType, solution, transitive: true, ImmutableHashSet.Create(otherProject), CancellationToken.None);
Assert.Empty(results);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ private static async Task<ImmutableArray<INamedTypeSymbol>> DescendInheritanceTr
// So we can just limit ourselves to that single project.

// First find all the projects that could potentially reference this type.
List<Project> orderedProjectsToExamine;
ImmutableArray<Project> orderedProjectsToExamine;

if (projects.Count == 1)
{
orderedProjectsToExamine = projects.ToList();
orderedProjectsToExamine = projects.ToImmutableArray();
}
else
{
Expand Down Expand Up @@ -136,14 +136,16 @@ private static async Task<ImmutableArray<INamedTypeSymbol>> DescendInheritanceTr
{
cancellationToken.ThrowIfCancellationRequested();

Debug.Assert(project.SupportsCompilation);
await DescendInheritanceTreeInProjectAsync(
searchInMetadata, result,
currentMetadataTypes, currentSourceAndMetadataTypes,
project,
typeMatches,
shouldContinueSearching,
transitive, cancellationToken).ConfigureAwait(false);
if (project.SupportsCompilation)
{
await DescendInheritanceTreeInProjectAsync(
searchInMetadata, result,
currentMetadataTypes, currentSourceAndMetadataTypes,
project,
typeMatches,
shouldContinueSearching,
transitive, cancellationToken).ConfigureAwait(false);
}
}

return result.ToImmutableArray();
Expand Down Expand Up @@ -294,7 +296,7 @@ private static IEnumerable<ProjectId> GetProjectsThatCouldReferenceType(
.Concat(project.Id);
}

private static List<Project> GetOrderedProjectsToExamine(
private static ImmutableArray<Project> GetOrderedProjectsToExamine(
Solution solution,
IImmutableSet<Project> projects,
IEnumerable<ProjectId> projectsThatCouldReferenceType)
Expand All @@ -308,7 +310,7 @@ private static List<Project> GetOrderedProjectsToExamine(
return OrderTopologically(solution, projectsToExamine);
}

private static List<Project> OrderTopologically(
private static ImmutableArray<Project> OrderTopologically(
Solution solution, IEnumerable<Project> projectsToExamine)
{
var order = new Dictionary<ProjectId, int>(capacity: solution.ProjectIds.Count);
Expand All @@ -322,10 +324,10 @@ private static List<Project> OrderTopologically(
index++;
}

return projectsToExamine.OrderBy((p1, p2) => order[p1.Id] - order[p2.Id]).ToList();
return projectsToExamine.OrderBy((p1, p2) => order[p1.Id] - order[p2.Id]).ToImmutableArray();
}

private static IEnumerable<Project> GetProjectsToExamineWorker(
private static ImmutableArray<Project> GetProjectsToExamineWorker(
Solution solution,
IImmutableSet<Project> projects,
IEnumerable<ProjectId> projectsThatCouldReferenceType)
Expand Down Expand Up @@ -358,8 +360,7 @@ private static IEnumerable<Project> GetProjectsToExamineWorker(
// that actually supports compilations.
return projectsThatCouldReferenceType.Intersect(allProjectsThatTheseProjectsDependOn)
.Select(id => solution.GetRequiredProject(id))
.Where(p => p.SupportsCompilation)
.ToList();
.ToImmutableArray();
}

private static async Task AddDescendantMetadataTypesInProjectAsync(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static async Task<ImmutableArray<INamedTypeSymbol>> FindTypesAsync(
var client = await RemoteHostClient.TryGetClientAsync(solution.Workspace, cancellationToken).ConfigureAwait(false);
if (client != null)
{
var projectIds = projects?.SelectAsArray(p => p.Id) ?? default;
var projectIds = projects?.Where(p => RemoteSupportedLanguages.IsSupported(p.Language)).SelectAsArray(p => p.Id) ?? default;

var result = await client.TryInvokeAsync<IRemoteDependentTypeFinderService, ImmutableArray<SerializableSymbolAndProjectId>>(
solution,
Expand Down

0 comments on commit ca64a6d

Please sign in to comment.