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

Merge master to features/readonly-members #33461

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async Task OnReferenceFoundAsync(Document document, TextSpan span)
var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(
document, span, _context.CancellationToken).ConfigureAwait(false);
await _context.OnReferenceFoundAsync(new SourceReferenceItem(
_definition, documentSpan, ValueUsageInfo.None)).ConfigureAwait(false);
_definition, documentSpan, SymbolUsageInfo.None)).ConfigureAwait(false);
}

public Task ReportProgressAsync(int current, int maximum)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ public static async Task<SourceReferenceItem> TryCreateSourceReferenceItemAsync(
var documentSpan = await ClassifiedSpansAndHighlightSpanFactory.GetClassifiedDocumentSpanAsync(
document, sourceSpan, cancellationToken).ConfigureAwait(false);

return new SourceReferenceItem(definitionItem, documentSpan, referenceLocation.ValueUsageInfo);
return new SourceReferenceItem(definitionItem, documentSpan, referenceLocation.SymbolUsageInfo);
}

private static SymbolDisplayFormat GetFormat(ISymbol definition)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ protected override async Task<ImmutableArray<FinderLocation>> FindReferencesInDo

return invocations.Concat(convertedAnonymousFunctions).SelectAsArray(
n => new FinderLocation(n, new ReferenceLocation(document, null, n.GetLocation(), isImplicit: false,
valueUsageInfo: semanticModel.GetValueUsageInfo(n, semanticFactsService, cancellationToken), candidateReason: CandidateReason.None)));
symbolUsageInfo: GetSymbolUsageInfo(n, semanticModel, syntaxFactsService, semanticFactsService, cancellationToken), candidateReason: CandidateReason.None)));
}
}
}
22 changes: 11 additions & 11 deletions src/Features/Core/Portable/FindUsages/SourceReferenceItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ namespace Microsoft.CodeAnalysis.FindUsages
/// </summary>
internal sealed class SourceReferenceItem
{
// We can have only a handful of different values for ValueUsageInfo flags enum, so the maximum size of this dictionary is capped.
// We can have only a handful of different values for enums within SymbolUsageInfo, so the maximum size of this dictionary is capped.
// So, we store this as a static dictionary which will be held in memory for the lifetime of the process.
private static readonly ConcurrentDictionary<ValueUsageInfo, ReferenceInfoMap> s_valueUsageInfoToReferenceInfoMap
= new ConcurrentDictionary<ValueUsageInfo, ReferenceInfoMap>();
private static readonly ConcurrentDictionary<SymbolUsageInfo, ReferenceInfoMap> s_symbolUsageInfoToReferenceInfoMap
= new ConcurrentDictionary<SymbolUsageInfo, ReferenceInfoMap>();

/// <summary>
/// The definition this reference corresponds to.
Expand Down Expand Up @@ -59,21 +59,21 @@ public SourceReferenceItem(DefinitionItem definition, DocumentSpan sourceSpan, R
ReferenceInfo = referenceInfo ?? ReferenceInfoMap.Empty;
}

internal SourceReferenceItem(DefinitionItem definition, DocumentSpan sourceSpan, ValueUsageInfo valueUsageInfo)
: this(definition, sourceSpan, GetOrCreateReferenceInfo(valueUsageInfo))
internal SourceReferenceItem(DefinitionItem definition, DocumentSpan sourceSpan, SymbolUsageInfo symbolUsageInfo)
: this(definition, sourceSpan, GetOrCreateReferenceInfo(symbolUsageInfo))
{
IsWrittenTo = valueUsageInfo.IsWrittenTo();
IsWrittenTo = symbolUsageInfo.IsWrittenTo();
}

private static ReferenceInfoMap GetOrCreateReferenceInfo(ValueUsageInfo valueUsageInfo)
=> s_valueUsageInfoToReferenceInfoMap.GetOrAdd(valueUsageInfo, v => CreateReferenceInfo(v));
private static ReferenceInfoMap GetOrCreateReferenceInfo(SymbolUsageInfo symbolUsageInfo)
=> s_symbolUsageInfoToReferenceInfoMap.GetOrAdd(symbolUsageInfo, v => CreateReferenceInfo(v));

private static ReferenceInfoMap CreateReferenceInfo(ValueUsageInfo valueUsageInfo)
private static ReferenceInfoMap CreateReferenceInfo(SymbolUsageInfo symbolUsageInfo)
{
var referenceInfoMap = ReferenceInfoMap.Empty;
if (valueUsageInfo != ValueUsageInfo.None)
if (!symbolUsageInfo.Equals(SymbolUsageInfo.None))
{
referenceInfoMap = referenceInfoMap.Add(nameof(ValueUsageInfo), valueUsageInfo.ToLocalizableValues());
referenceInfoMap = referenceInfoMap.Add(nameof(SymbolUsageInfo), symbolUsageInfo.ToLocalizableValues());
}

return referenceInfoMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using Microsoft.CodeAnalysis;
using Microsoft.VisualStudio.Shell.TableControl;
using Microsoft.VisualStudio.Utilities;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.FindUsages
{
Expand All @@ -27,15 +26,10 @@ private static readonly ConcurrentDictionary<ImmutableArray<string>, string> s_c
private static readonly ConcurrentDictionary<string, ImmutableArray<string>> s_displayValueToConstituentValuesMap
= new ConcurrentDictionary<string, ImmutableArray<string>>();

public const string ColumnName = nameof(ValueUsageInfo);
public const string ColumnName = nameof(SymbolUsageInfo);

// Allow filtering of the column by each ValueUsageInfo kind.
private static readonly ImmutableArray<string> s_defaultFilters = Enum.GetValues(typeof(ValueUsageInfo))
.Cast<ValueUsageInfo>()
.Where(value => value.IsSingleBitSet())
.Select(v => v.ToLocalizableString())
.ToImmutableArray();
public override IEnumerable<string> FilterPresets => s_defaultFilters;
// Allow filtering of the column by each allowed SymbolUsageInfo kind.
public override IEnumerable<string> FilterPresets => SymbolUsageInfo.LocalizableStringsForAllAllowedValues;
public override bool IsFilterable => true;

public override string Name => ColumnName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ public static bool IsLeftSideOfQualifiedName(this ExpressionSyntax expression)
expression.IsParentKind(SyntaxKind.QualifiedName) && ((QualifiedNameSyntax)expression.Parent).Left == expression;
}

public static bool IsLeftSideOfExplicitInterfaceSpecifier(this NameSyntax name)
=> name.IsParentKind(SyntaxKind.ExplicitInterfaceSpecifier);

public static bool IsExpressionOfInvocation(this ExpressionSyntax expression)
{
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,11 @@ public bool IsInNamespaceOrTypeContext(SyntaxNode node)
return SyntaxFacts.IsInNamespaceOrTypeContext(node as ExpressionSyntax);
}

public bool IsBaseTypeList(SyntaxNode node)
{
return node.IsKind(SyntaxKind.BaseList);
}

public SyntaxNode GetExpressionOfArgument(SyntaxNode node)
{
return ((ArgumentSyntax)node).Expression;
Expand All @@ -691,6 +696,11 @@ public bool IsSimpleArgument(SyntaxNode node)
argument.NameColon == null;
}

public bool IsTypeArgumentList(SyntaxNode node)
=> node.IsKind(SyntaxKind.TypeArgumentList);

public bool IsTypeConstraint(SyntaxNode node)
=> node.IsKind(SyntaxKind.TypeConstraint);

public bool IsInConstantContext(SyntaxNode node)
{
Expand Down Expand Up @@ -1351,6 +1361,9 @@ public SyntaxNode GetRightSideOfDot(SyntaxNode node)
(node as MemberAccessExpressionSyntax)?.Name;
}

public bool IsLeftSideOfExplicitInterfaceSpecifier(SyntaxNode node)
=> (node as NameSyntax).IsLeftSideOfExplicitInterfaceSpecifier();

public bool IsLeftSideOfAssignment(SyntaxNode node)
{
return (node as ExpressionSyntax).IsLeftSideOfAssignExpression();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ where void Foo(in T v)
operation.Parent is ITypeOfOperation ||
operation.Parent is ISizeOfOperation)
{
return ValueUsageInfo.NameOnly;
return ValueUsageInfo.Name;
}
else if (operation.Parent is IArgumentOperation argumentOperation)
{
Expand Down
84 changes: 84 additions & 0 deletions src/Workspaces/Core/Portable/Extensions/SymbolUsageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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.Immutable;
using System.Diagnostics;
using System.Linq;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis
{
/// <summary>
/// Provides information about the way a particular symbol is being used at a symbol reference node.
/// For namespaces and types, this corresponds to values from <see cref="TypeOrNamespaceUsageInfo"/>.
/// For methods, fields, properties, events, locals and parameters, this corresponds to values from <see cref="ValueUsageInfo"/>.
/// </summary>
internal readonly struct SymbolUsageInfo : IEquatable<SymbolUsageInfo>
{
public static readonly SymbolUsageInfo None = Create(ValueUsageInfo.None);
public static readonly ImmutableArray<string> LocalizableStringsForAllAllowedValues = CreateLocalizableStringsForAllAllowedValues();

public ValueUsageInfo? ValueUsageInfoOpt { get; }
public TypeOrNamespaceUsageInfo? TypeOrNamespaceUsageInfoOpt { get; }

private SymbolUsageInfo(ValueUsageInfo? valueUsageInfoOpt, TypeOrNamespaceUsageInfo? typeOrNamespaceUsageInfoOpt)
{
Debug.Assert(valueUsageInfoOpt.HasValue ^ typeOrNamespaceUsageInfoOpt.HasValue);

ValueUsageInfoOpt = valueUsageInfoOpt;
TypeOrNamespaceUsageInfoOpt = typeOrNamespaceUsageInfoOpt;
}

public static SymbolUsageInfo Create(ValueUsageInfo valueUsageInfo)
=> new SymbolUsageInfo(valueUsageInfo, typeOrNamespaceUsageInfoOpt: null);

public static SymbolUsageInfo Create(TypeOrNamespaceUsageInfo typeOrNamespaceUsageInfo)
=> new SymbolUsageInfo(valueUsageInfoOpt: null, typeOrNamespaceUsageInfo);

private static ImmutableArray<string> CreateLocalizableStringsForAllAllowedValues()
{
var valueUsageInfoStrings = Enum.GetValues(typeof(ValueUsageInfo))
.Cast<ValueUsageInfo>()
.Where(value => value.IsSingleBitSet())
.Select(v => v.ToLocalizableString());
var typeOrNamespaceUsageInfoStrings = Enum.GetValues(typeof(TypeOrNamespaceUsageInfo))
.Cast<TypeOrNamespaceUsageInfo>()
.Where(value => value.IsSingleBitSet())
.Select(v => v.ToLocalizableString());
return valueUsageInfoStrings.Concat(typeOrNamespaceUsageInfoStrings).ToImmutableArray();
}

public bool IsReadFrom()
=> ValueUsageInfoOpt.HasValue && ValueUsageInfoOpt.Value.IsReadFrom();

public bool IsWrittenTo()
=> ValueUsageInfoOpt.HasValue && ValueUsageInfoOpt.Value.IsWrittenTo();

public bool IsNameOnly()
=> ValueUsageInfoOpt.HasValue && ValueUsageInfoOpt.Value.IsNameOnly();

public string ToLocalizableString()
=> ValueUsageInfoOpt.HasValue ? ValueUsageInfoOpt.Value.ToLocalizableString() : TypeOrNamespaceUsageInfoOpt.Value.ToLocalizableString();

public ImmutableArray<string> ToLocalizableValues()
=> ValueUsageInfoOpt.HasValue ? ValueUsageInfoOpt.Value.ToLocalizableValues() : TypeOrNamespaceUsageInfoOpt.Value.ToLocalizableValues();

public override bool Equals(object obj)
=> obj is SymbolUsageInfo && Equals((SymbolUsageInfo)obj);

public bool Equals(SymbolUsageInfo other)
{
if (ValueUsageInfoOpt.HasValue)
{
return other.ValueUsageInfoOpt.HasValue &&
ValueUsageInfoOpt.Value == other.ValueUsageInfoOpt.Value;
}

return other.TypeOrNamespaceUsageInfoOpt.HasValue &&
TypeOrNamespaceUsageInfoOpt.Value == other.TypeOrNamespaceUsageInfoOpt.Value;
}

public override int GetHashCode()
=> Hash.Combine(ValueUsageInfoOpt?.GetHashCode() ?? 0, TypeOrNamespaceUsageInfoOpt?.GetHashCode() ?? 0);
}
}
120 changes: 120 additions & 0 deletions src/Workspaces/Core/Portable/Extensions/TypeOrNamespaceUsageInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 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.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.PooledObjects;

namespace Microsoft.CodeAnalysis
{
[Flags]
internal enum TypeOrNamespaceUsageInfo
{
/// <summary>
/// Represents default value indicating no usage.
/// </summary>
None = 0x0,

/// <summary>
/// Represents a reference to a namespace or type on the left side of a dotted name (qualified name or member access).
/// For example, 'NS' in <code>NS.Type x = new NS.Type();</code> or <code>NS.Type.StaticMethod();</code> or
/// 'Type' in <code>Type.NestedType x = new Type.NestedType();</code> or <code>Type.StaticMethod();</code>
/// </summary>
Qualified = 0x01,

/// <summary>
/// Represents a generic type argument reference.
/// For example, 'Type' in <code>Generic{Type} x = ...;</code> or <code>class Derived : Base{Type} { }</code>
/// </summary>
TypeArgument = 0x02,

/// <summary>
/// Represents a type parameter constraint that is a type.
/// For example, 'Type' in <code>class Derived{T} where T : Type { }</code>
/// </summary>
TypeConstraint = 0x04,

/// <summary>
/// Represents a base type or interface reference in the base list of a named type.
/// For example, 'Base' in <code>class Derived : Base { }</code>.
/// </summary>
Base = 0x08,

/// <summary>
/// Represents a reference to a type whose instance is being created.
/// For example, 'C' in <code>var x = new C();</code>, where 'C' is a named type.
/// </summary>
ObjectCreation = 0x10,

/// <summary>
/// Represents a reference to a namespace or type within a using or imports directive.
/// For example, <code>using NS;</code> or <code>using static NS.Extensions</code> or <code>using Alias = MyType</code>.
/// </summary>
Import = 0x20,

/// <summary>
/// Represents a reference to a namespace name in a namespace declaration context.
/// For example, 'N1' or <code>namespaces N1.N2 { }</code>.
/// </summary>
NamespaceDeclaration = 0x40,
}

internal static class TypeOrNamespaceUsageInfoExtensions
{
public static string ToLocalizableString(this TypeOrNamespaceUsageInfo info)
{
// We don't support localizing value combinations.
Debug.Assert(info.IsSingleBitSet());

switch (info)
{
case TypeOrNamespaceUsageInfo.Qualified:
return WorkspacesResources.TypeOrNamespaceUsageInfo_Qualify;

case TypeOrNamespaceUsageInfo.TypeArgument:
return WorkspacesResources.TypeOrNamespaceUsageInfo_TypeArgument;

case TypeOrNamespaceUsageInfo.TypeConstraint:
return WorkspacesResources.TypeOrNamespaceUsageInfo_TypeConstraint;

case TypeOrNamespaceUsageInfo.Base:
return WorkspacesResources.TypeOrNamespaceUsageInfo_BaseType;

case TypeOrNamespaceUsageInfo.ObjectCreation:
return WorkspacesResources.TypeOrNamespaceUsageInfo_Construct;

case TypeOrNamespaceUsageInfo.Import:
return WorkspacesResources.TypeOrNamespaceUsageInfo_Import;

case TypeOrNamespaceUsageInfo.NamespaceDeclaration:
return WorkspacesResources.TypeOrNamespaceUsageInfo_Declare;

default:
Debug.Fail($"Unhandled value: '{info.ToString()}'");
return info.ToString();
}
}

public static bool IsSingleBitSet(this TypeOrNamespaceUsageInfo usageInfo)
=> usageInfo != TypeOrNamespaceUsageInfo.None && (usageInfo & (usageInfo - 1)) == 0;

public static ImmutableArray<string> ToLocalizableValues(this TypeOrNamespaceUsageInfo usageInfo)
{
if (usageInfo == TypeOrNamespaceUsageInfo.None)
{
return ImmutableArray<string>.Empty;
}

var builder = ArrayBuilder<string>.GetInstance();
foreach (TypeOrNamespaceUsageInfo value in Enum.GetValues(typeof(TypeOrNamespaceUsageInfo)))
{
if (value.IsSingleBitSet() && (usageInfo & value) != 0)
{
builder.Add(value.ToLocalizableString());
}
}

return builder.ToImmutableAndFree();
}
}
}
Loading