Skip to content
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

Find dependent types more efficiently by storing inheritance info directly in our indices. #11313

Merged
merged 73 commits into from
May 17, 2016
Merged
Show file tree
Hide file tree
Changes from 65 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
cdd6ae2
Store inforation in DeclaredSymbolInfo about what type names the type…
CyrusNajmabadi May 12, 2016
05edce6
Improve the implementation of GetTypesImmediatelyDerivedFromClassesAs…
CyrusNajmabadi May 12, 2016
7b3f3fc
Simplify code.
CyrusNajmabadi May 12, 2016
2b53f67
Simplify code.
CyrusNajmabadi May 12, 2016
34cfbc8
Switch interface searching to use the same declaration map.
CyrusNajmabadi May 12, 2016
3231d5c
Stop using MoveToImmutable.
CyrusNajmabadi May 12, 2016
1bc2a33
Keep track of aliases, and use them to augment the list of inheritanc…
CyrusNajmabadi May 12, 2016
895aae8
Handle when there are aliases in the inheritance list points to those…
CyrusNajmabadi May 12, 2016
db529bb
Check to make sure there is actually a base list before continuing.
CyrusNajmabadi May 12, 2016
a0b4ae8
Fix issue where we weren't properly creating the DeclaredTypeInfo for…
CyrusNajmabadi May 13, 2016
0f10675
Add test for finding derived types, even when aliases are involved.
CyrusNajmabadi May 13, 2016
e530f35
Don't search for derived types for sealed types.
CyrusNajmabadi May 13, 2016
5899191
Build FindDerivedClassesAsync on top of GetTypesImmediatelyDerivedFro…
CyrusNajmabadi May 13, 2016
b7cbe4b
Build FindImplementingTypesAsync on top of GetTypesImmediatelyDerived…
CyrusNajmabadi May 13, 2016
9dc1d81
Remove unused method.
CyrusNajmabadi May 13, 2016
2a64e25
Remove method.
CyrusNajmabadi May 13, 2016
e22be5c
Remove method.
CyrusNajmabadi May 13, 2016
00aab8f
Remove method.
CyrusNajmabadi May 13, 2016
38fc108
Remove unused methods.
CyrusNajmabadi May 13, 2016
7d9d0aa
Remove unused fields.
CyrusNajmabadi May 13, 2016
c000f95
Move fields.
CyrusNajmabadi May 13, 2016
079b33e
Properly pass the project collection around.
CyrusNajmabadi May 13, 2016
c7d1078
Don't add structs to the class queue.
CyrusNajmabadi May 13, 2016
e43b127
Use simple comparison for type equality.
CyrusNajmabadi May 13, 2016
a52813b
Remove dead code.
CyrusNajmabadi May 13, 2016
907cdfe
Cache semantic models as we walk the projects.
CyrusNajmabadi May 14, 2016
7e69ca2
Process one project at a time.
CyrusNajmabadi May 14, 2016
3e7ebcf
Flesh out metadata reading
CyrusNajmabadi May 15, 2016
96810ec
More refactoring to search a single project at a time.
CyrusNajmabadi May 15, 2016
dba9b28
Process each project in order.
CyrusNajmabadi May 15, 2016
deb4fe3
Name members more clearly.
CyrusNajmabadi May 15, 2016
f70f829
Rename method.
CyrusNajmabadi May 15, 2016
bb4137c
Remove unused method.
CyrusNajmabadi May 15, 2016
4bf06cc
Rename method.
CyrusNajmabadi May 15, 2016
bdf09da
Fix null refs.
CyrusNajmabadi May 15, 2016
091d664
Fix null ref.
CyrusNajmabadi May 15, 2016
386df78
Make method into a delegate.
CyrusNajmabadi May 15, 2016
6692376
Rename local.
CyrusNajmabadi May 15, 2016
0f13e98
Properly search for special types.
CyrusNajmabadi May 15, 2016
b366740
Pass along a flag to demarcate transitive or non-transitive searching.
CyrusNajmabadi May 15, 2016
445e325
Build the immediate functions on top of the basic functions.
CyrusNajmabadi May 15, 2016
2f73524
Expose proper semantics.
CyrusNajmabadi May 15, 2016
6dc136f
Actually return result.
CyrusNajmabadi May 15, 2016
a16a09c
properly pass along argument.
CyrusNajmabadi May 15, 2016
ed984a5
Need these caches to eb concurrency safe as we process documents in p…
CyrusNajmabadi May 15, 2016
fed0a33
Fix topological ordering of projects.
CyrusNajmabadi May 15, 2016
78131a7
Cache teh top level declaration info, not all the child infos.
CyrusNajmabadi May 15, 2016
0f087bf
Move methods out of DependentTypeFinder. They're not used there anym…
CyrusNajmabadi May 15, 2016
05f17f2
PR feedback.
CyrusNajmabadi May 16, 2016
ca411a8
Inline methods.
CyrusNajmabadi May 16, 2016
37b9999
Set an initial capacity on our dictionary.
CyrusNajmabadi May 16, 2016
419e2d9
Rename method.
CyrusNajmabadi May 16, 2016
993c2d5
Inline methods.
CyrusNajmabadi May 16, 2016
2f3e1ab
Rename static field.
CyrusNajmabadi May 16, 2016
302c924
Remove unused type.
CyrusNajmabadi May 16, 2016
3e6558e
Make types nested and private.
CyrusNajmabadi May 16, 2016
653d1bd
Clarify code.
CyrusNajmabadi May 16, 2016
8f704c1
Inline methods.
CyrusNajmabadi May 16, 2016
4e96456
Inline methods.
CyrusNajmabadi May 16, 2016
5632828
Remove unused delegate types.
CyrusNajmabadi May 16, 2016
b19cb19
Move method to delegate.'
CyrusNajmabadi May 16, 2016
942c751
Reorder parameters.
CyrusNajmabadi May 16, 2016
0aef7d4
Inline initialization.
CyrusNajmabadi May 16, 2016
d377f78
Make an n^2 loop into O(n)
CyrusNajmabadi May 16, 2016
33be43f
Make loop O(n)
CyrusNajmabadi May 16, 2016
253eb57
Add named parameters
CyrusNajmabadi May 16, 2016
01a74a2
Add comments.
CyrusNajmabadi May 16, 2016
a6e2bea
Remove unnecessary code.
CyrusNajmabadi May 16, 2016
e1be5c9
Comment code.
CyrusNajmabadi May 16, 2016
77c293e
Comment code.
CyrusNajmabadi May 16, 2016
31acc1f
Add comments.
CyrusNajmabadi May 16, 2016
28f6f3e
Rename parameters.
CyrusNajmabadi May 17, 2016
0662815
Rename type.
CyrusNajmabadi May 17, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/Compilers/Core/Portable/InternalUtilities/ConcurrentSet.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
Expand All @@ -11,7 +12,7 @@ namespace Roslyn.Utilities
/// A concurrent, simplified HashSet.
/// </summary>
[DebuggerDisplay("Count = {Count}")]
internal sealed class ConcurrentSet<T> : IEnumerable<T>
internal sealed class ConcurrentSet<T> : ICollection<T>
{
/// <summary>
/// The default concurrency level is 2. That means the collection can cope with up to two
Expand Down Expand Up @@ -65,6 +66,8 @@ public bool IsEmpty
get { return _dictionary.IsEmpty; }
}

public bool IsReadOnly => false;

/// <summary>
/// Determine whether the given value is in the set.
/// </summary>
Expand All @@ -85,6 +88,17 @@ public bool Add(T value)
return _dictionary.TryAdd(value, 0);
}

public void AddRange(IEnumerable<T> values)
{
if (values != null)
{
foreach (var v in values)
{
Add(v);
}
}
}

/// <summary>
/// Attempts to remove a value from the set.
/// </summary>
Expand Down Expand Up @@ -161,5 +175,15 @@ IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumeratorImpl();
}

void ICollection<T>.Add(T item)
{
Add(item);
}

public void CopyTo(T[] array, int arrayIndex)
{
throw new NotImplementedException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,7 @@ private ISymbol FindSymbol()
// CancellationToken.None.
var semanticModel = Document.GetPartialSemanticModelAsync(CancellationToken.None).GetAwaiter().GetResult();

var root = semanticModel.SyntaxTree.GetRoot(CancellationToken.None);
var node = root.FindNode(_declaredSymbolInfo.Span);
return semanticModel.GetDeclaredSymbol(node, CancellationToken.None);
return _declaredSymbolInfo.Resolve(semanticModel, CancellationToken.None);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ public static INavigableItem GetItemFromDeclaredSymbolInfo(DeclaredSymbolInfo de
return new DeclaredSymbolNavigableItem(document, declaredSymbolInfo);
}


public static IEnumerable<INavigableItem> GetItemsFromPreferredSourceLocations(Solution solution, ISymbol symbol, string displayString = null)
{
var locations = GetPreferredSourceLocations(solution, symbol);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ internal static async Task<IEnumerable<ValueTuple<DeclaredSymbolInfo, Document,
foreach (var document in project.Documents)
{
cancellationToken.ThrowIfCancellationRequested();
var declaredSymbolInfos = await document.GetDeclaredSymbolInfosAsync(cancellationToken).ConfigureAwait(false);
foreach (var declaredSymbolInfo in declaredSymbolInfos)
var declarationInfo = await document.GetDeclarationInfoAsync(cancellationToken).ConfigureAwait(false);

foreach (var declaredSymbolInfo in declarationInfo.DeclaredSymbolInfos)
{
cancellationToken.ThrowIfCancellationRequested();
var patternMatches = patternMatcher.GetMatches(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c

if (namedType.TypeKind == TypeKind.Class)
{
var derivedTypes = await DependentTypeFinder.GetTypesImmediatelyDerivedFromClassesAsync(namedType, solution, cancellationToken).ConfigureAwait(false);
var derivedTypes = await DependentTypeFinder.FindImmediatelyDerivedClassesAsync(namedType, solution, cancellationToken).ConfigureAwait(false);
foreach (var derivedType in derivedTypes)
{
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(derivedType, relatedNode: node).ConfigureAwait(false);
Expand All @@ -34,7 +34,8 @@ public async Task<GraphBuilder> GetGraphAsync(Solution solution, IGraphContext c
}
else if (namedType.TypeKind == TypeKind.Interface)
{
var derivedTypes = await DependentTypeFinder.GetTypesImmediatelyDerivedFromInterfacesAsync(namedType, solution, cancellationToken).ConfigureAwait(false);
var derivedTypes = await DependentTypeFinder.FindImmediatelyDerivedAndImplementingTypesAsync(
namedType, solution, cancellationToken).ConfigureAwait(false);
foreach (var derivedType in derivedTypes)
{
var symbolNode = await graphBuilder.AddNodeForSymbolAsync(derivedType, relatedNode: node).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Diagnostics;
using System.Linq;
Expand All @@ -20,7 +21,7 @@
namespace Microsoft.CodeAnalysis.CSharp
{
[ExportLanguageService(typeof(ISyntaxFactsService), LanguageNames.CSharp), Shared]
internal class CSharpSyntaxFactsService : ISyntaxFactsService
internal class CSharpSyntaxFactsService : AbstractSyntaxFactsService, ISyntaxFactsService
{
public bool IsAwaitKeyword(SyntaxToken token)
{
Expand Down Expand Up @@ -747,7 +748,8 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
declaredSymbolInfo = new DeclaredSymbolInfo(classDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Class, classDecl.Identifier.Span);
DeclaredSymbolInfoKind.Class, classDecl.Identifier.Span,
GetInheritanceNames(classDecl.BaseList));
return true;
case SyntaxKind.ConstructorDeclaration:
var ctorDecl = (ConstructorDeclarationSyntax)node;
Expand All @@ -757,49 +759,56 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Constructor,
ctorDecl.Identifier.Span,
ImmutableArray<string>.Empty,
Copy link
Member

@jasonmalinowski jasonmalinowski May 16, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

named arguments (for this and rest of file)

parameterCount: (ushort)(ctorDecl.ParameterList?.Parameters.Count ?? 0));
return true;
case SyntaxKind.DelegateDeclaration:
var delegateDecl = (DelegateDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(delegateDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Delegate, delegateDecl.Identifier.Span);
DeclaredSymbolInfoKind.Delegate, delegateDecl.Identifier.Span,
ImmutableArray<string>.Empty);
return true;
case SyntaxKind.EnumDeclaration:
var enumDecl = (EnumDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(enumDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Enum, enumDecl.Identifier.Span);
DeclaredSymbolInfoKind.Enum, enumDecl.Identifier.Span,
ImmutableArray<string>.Empty);
return true;
case SyntaxKind.EnumMemberDeclaration:
var enumMember = (EnumMemberDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(enumMember.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.EnumMember, enumMember.Identifier.Span);
DeclaredSymbolInfoKind.EnumMember, enumMember.Identifier.Span,
ImmutableArray<string>.Empty);
return true;
case SyntaxKind.EventDeclaration:
var eventDecl = (EventDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(ExpandExplicitInterfaceName(eventDecl.Identifier.ValueText, eventDecl.ExplicitInterfaceSpecifier),
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Event, eventDecl.Identifier.Span);
DeclaredSymbolInfoKind.Event, eventDecl.Identifier.Span,
ImmutableArray<string>.Empty);
return true;
case SyntaxKind.IndexerDeclaration:
var indexerDecl = (IndexerDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(WellKnownMemberNames.Indexer,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Indexer, indexerDecl.ThisKeyword.Span);
DeclaredSymbolInfoKind.Indexer, indexerDecl.ThisKeyword.Span,
ImmutableArray<string>.Empty);
return true;
case SyntaxKind.InterfaceDeclaration:
var interfaceDecl = (InterfaceDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(interfaceDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Interface, interfaceDecl.Identifier.Span);
DeclaredSymbolInfoKind.Interface, interfaceDecl.Identifier.Span,
GetInheritanceNames(interfaceDecl.BaseList));
return true;
case SyntaxKind.MethodDeclaration:
var method = (MethodDeclarationSyntax)node;
Expand All @@ -809,6 +818,7 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Method,
method.Identifier.Span,
ImmutableArray<string>.Empty,
parameterCount: (ushort)(method.ParameterList?.Parameters.Count ?? 0),
typeParameterCount: (ushort)(method.TypeParameterList?.Parameters.Count ?? 0));
return true;
Expand All @@ -817,14 +827,16 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
declaredSymbolInfo = new DeclaredSymbolInfo(ExpandExplicitInterfaceName(property.Identifier.ValueText, property.ExplicitInterfaceSpecifier),
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Property, property.Identifier.Span);
DeclaredSymbolInfoKind.Property, property.Identifier.Span,
ImmutableArray<string>.Empty);
return true;
case SyntaxKind.StructDeclaration:
var structDecl = (StructDeclarationSyntax)node;
declaredSymbolInfo = new DeclaredSymbolInfo(structDecl.Identifier.ValueText,
GetContainerDisplayName(node.Parent),
GetFullyQualifiedContainerName(node.Parent),
DeclaredSymbolInfoKind.Struct, structDecl.Identifier.Span);
DeclaredSymbolInfoKind.Struct, structDecl.Identifier.Span,
GetInheritanceNames(structDecl.BaseList));
return true;
case SyntaxKind.VariableDeclarator:
// could either be part of a field declaration or an event field declaration
Expand All @@ -839,10 +851,12 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
? DeclaredSymbolInfoKind.Constant
: DeclaredSymbolInfoKind.Field;

declaredSymbolInfo = new DeclaredSymbolInfo(variableDeclarator.Identifier.ValueText,
GetContainerDisplayName(fieldDeclaration.Parent),
GetFullyQualifiedContainerName(fieldDeclaration.Parent),
kind, variableDeclarator.Identifier.Span);
declaredSymbolInfo = new DeclaredSymbolInfo(
variableDeclarator.Identifier.ValueText,
GetContainerDisplayName(fieldDeclaration.Parent),
GetFullyQualifiedContainerName(fieldDeclaration.Parent),
kind, variableDeclarator.Identifier.Span,
ImmutableArray<string>.Empty);
return true;
}

Expand All @@ -853,6 +867,126 @@ public bool TryGetDeclaredSymbolInfo(SyntaxNode node, out DeclaredSymbolInfo dec
return false;
}

private ImmutableArray<string> GetInheritanceNames(BaseListSyntax baseList)
{
if (baseList == null)
{
return ImmutableArray<string>.Empty;
}

var builder = ImmutableArray.CreateBuilder<string>(baseList.Types.Count);

var aliasMaps = AllocateAliasMapList();
try
{
AddAliasMaps(baseList, aliasMaps);

foreach (var baseType in baseList.Types)
{
AddInheritanceName(builder, baseType.Type, aliasMaps);
}

return builder.ToImmutable();
}
finally
{
FreeAliasMapList(aliasMaps);
}
}

private void AddAliasMaps(SyntaxNode node, List<Dictionary<string, string>> aliasMaps)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you document what the List<Dictionary<string, string>> is, other than the ultimate stringly-typed data structure? 😄

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!

{
for (var current = node; current != null; current = current.Parent)
{
if (current.IsKind(SyntaxKind.NamespaceDeclaration))
{
ProcessUsings(aliasMaps, ((NamespaceDeclarationSyntax)current).Usings);
}
else if (current.IsKind(SyntaxKind.CompilationUnit))
{
ProcessUsings(aliasMaps, ((CompilationUnitSyntax)current).Usings);
}
}
}

private void ProcessUsings(List<Dictionary<string, string>> aliasMaps, SyntaxList<UsingDirectiveSyntax> usings)
{
Dictionary<string, string> aliasMap = null;

foreach (var usingDecl in usings)
{
if (usingDecl.Alias != null)
{
var mappedName = GetTypeName(usingDecl.Name);
if (mappedName != null)
{
aliasMap = aliasMap ?? AllocateAliasMap();

// If we have: using X = Foo, then we store a mapping from X -> Foo
// here. That way if we see a class that inherits from X we also state
// that it inherits from Foo as well.
aliasMap[usingDecl.Alias.Name.Identifier.ValueText] = mappedName;
}
}
}

if (aliasMap != null)
{
aliasMaps.Add(aliasMap);
}
}

private void AddInheritanceName(
ImmutableArray<string>.Builder builder, TypeSyntax type,
List<Dictionary<string, string>> aliasMaps)
{
var name = GetTypeName(type);
if (name != null)
{
// First, add the name that the typename that the type directly says it inherits from.
builder.Add(name);

// Now, walk the alias chain and add any names this alias may eventually map to.
var currentName = name;
foreach (var aliasMap in aliasMaps)
{
string mappedName;
if (aliasMap.TryGetValue(currentName, out mappedName))
{
// Looks like this could be an alias. Also include the name the alias points to
builder.Add(mappedName);

// Keep on searching. An alias in an inner namespcae can refer to an
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"namespace"

// alias in an outer namespace.
currentName = mappedName;
}
}
}
}

private string GetTypeName(TypeSyntax type)
{
if (type is SimpleNameSyntax)
{
return GetSimpleTypeName((SimpleNameSyntax)type);
}
else if (type is QualifiedNameSyntax)
{
return GetSimpleTypeName(((QualifiedNameSyntax)type).Right);
}
else if (type is AliasQualifiedNameSyntax)
{
return GetSimpleTypeName(((AliasQualifiedNameSyntax)type).Name);
}

return null;
}

private static string GetSimpleTypeName(SimpleNameSyntax name)
{
return name.Identifier.ValueText;
}

private static string ExpandExplicitInterfaceName(string identifier, ExplicitInterfaceSpecifierSyntax explicitInterfaceSpecifier)
{
if (explicitInterfaceSpecifier == null)
Expand Down
Loading