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

Update FindRefs to find constructor references in a primary constructor base type #76097

Merged
merged 2 commits into from
Nov 26, 2024
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 @@ -1278,6 +1278,78 @@ Namespace Test
End Namespace]]>
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input, kind, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/73704")>
Public Async Function TestPrimaryConstructor1(kind As TestKind, host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class Program
{
public {|Definition:$$Program|}(int i)
{
}
}

class Derived() : [|Program|](0)
{
}
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input, kind, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/73704")>
Public Async Function TestPrimaryConstructor2(kind As TestKind, host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class Program
{
public {|Definition:$$Program|}(int i)
{
}
}

class Derived() : global::[|Program|](0)
{
}
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input, kind, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/73704")>
Public Async Function TestPrimaryConstructor3(kind As TestKind, host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
namespace N
{
class Program
{
public {|Definition:$$Program|}(int i)
{
}
}
}

class Derived() : N.[|Program|](0)
{
}
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input, kind, host)
End Function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ protected override void FindReferencesInDocument<TData>(
FindReferencesInImplicitObjectCreationExpression(
methodSymbol, state, processResult, processResultData, cancellationToken);

FindReferencesInPrimaryConstructorBaseType(
methodSymbol, state, processResult, processResultData, cancellationToken);

FindReferencesInDocumentInsideGlobalSuppressions(
methodSymbol, state, processResult, processResultData, cancellationToken);
}
Expand Down Expand Up @@ -257,4 +260,45 @@ private static void FindReferencesInImplicitObjectCreationExpression<TData>(
}
}
}

private static void FindReferencesInPrimaryConstructorBaseType<TData>(
IMethodSymbol symbol,
FindReferencesDocumentState state,
Action<FinderLocation, TData> processResult,
TData processResultData,
CancellationToken cancellationToken)
{
if (!state.Cache.SyntaxTreeIndex.ContainsPrimaryConstructorBaseType)
return;

var syntaxFacts = state.SyntaxFacts;
foreach (var token in state.Cache.FindMatchingIdentifierTokens(symbol.ContainingType.Name, cancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();

var parent = token.GetRequiredParent();
if (!syntaxFacts.IsSimpleName(parent))
continue;

if (syntaxFacts.IsRightOfQualifiedName(parent) || syntaxFacts.IsRightOfAliasQualifiedName(parent))
parent = parent.GetRequiredParent();

var node = parent.GetRequiredParent();
if (!syntaxFacts.IsPrimaryConstructorBaseType(node))
continue;

var constructor = state.SemanticModel.GetSymbolInfo(node, cancellationToken).Symbol;
if (Matches(constructor, symbol))
{
var result = new FinderLocation(node, new ReferenceLocation(
state.Document,
alias: null,
token.GetLocation(),
isImplicit: false,
GetSymbolUsageInfo(node, state, cancellationToken),
GetAdditionalFindUsagesProperties(node, state), CandidateReason.None));
processResult(result, processResultData);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal abstract partial class AbstractSyntaxIndex<TIndex>
/// that we will not try to read previously cached data from a prior version of roslyn with a different format and
/// will instead regenerate all the indices with the new format.
/// </summary>
private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("43");
private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("44");

/// <summary>
/// Cache of ParseOptions to a checksum for the <see cref="ParseOptions.PreprocessorSymbolNames"/> contained
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public ContextInfo(
bool containsGlobalKeyword,
bool containsCollectionInitializer,
bool containsAttribute,
bool containsDirective)
bool containsDirective,
bool containsPrimaryConstructorBaseType)
: this(predefinedTypes, predefinedOperators,
ConvertToContainingNodeFlag(
containsForEachStatement,
Expand All @@ -56,7 +57,8 @@ public ContextInfo(
containsGlobalKeyword,
containsCollectionInitializer,
containsAttribute,
containsDirective))
containsDirective,
containsPrimaryConstructorBaseType))
{
}

Expand Down Expand Up @@ -85,7 +87,8 @@ private static ContainingNodes ConvertToContainingNodeFlag(
bool containsGlobalKeyword,
bool containsCollectionInitializer,
bool containsAttribute,
bool containsDirective)
bool containsDirective,
bool containsPrimaryConstructorBaseType)
{
var containingNodes = ContainingNodes.None;

Expand All @@ -107,6 +110,7 @@ private static ContainingNodes ConvertToContainingNodeFlag(
containingNodes |= containsCollectionInitializer ? ContainingNodes.ContainsCollectionInitializer : 0;
containingNodes |= containsAttribute ? ContainingNodes.ContainsAttribute : 0;
containingNodes |= containsDirective ? ContainingNodes.ContainsDirective : 0;
containingNodes |= containsPrimaryConstructorBaseType ? ContainingNodes.ContainsPrimaryConstructorBaseType : 0;

return containingNodes;
}
Expand Down Expand Up @@ -171,6 +175,9 @@ public bool ContainsAttribute
public bool ContainsDirective
=> (_containingNodes & ContainingNodes.ContainsDirective) == ContainingNodes.ContainsDirective;

public bool ContainsPrimaryConstructorBaseType
=> (_containingNodes & ContainingNodes.ContainsPrimaryConstructorBaseType) == ContainingNodes.ContainsPrimaryConstructorBaseType;

public void WriteTo(ObjectWriter writer)
{
writer.WriteInt32(_predefinedTypes);
Expand Down Expand Up @@ -217,6 +224,7 @@ private enum ContainingNodes
ContainsCollectionInitializer = 1 << 15,
ContainsAttribute = 1 << 16,
ContainsDirective = 1 << 17,
ContainsPrimaryConstructorBaseType = 1 << 18,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ private static SyntaxTreeIndex CreateIndex(
var containsCollectionInitializer = false;
var containsAttribute = false;
var containsDirective = root.ContainsDirectives;
var containsPrimaryConstructorBaseType = false;

var predefinedTypes = (int)PredefinedType.None;
var predefinedOperators = (int)PredefinedOperator.None;
Expand Down Expand Up @@ -105,6 +106,7 @@ private static SyntaxTreeIndex CreateIndex(
containsConversion = containsConversion || syntaxFacts.IsConversionExpression(node);
containsCollectionInitializer = containsCollectionInitializer || syntaxFacts.IsObjectCollectionInitializer(node);
containsAttribute = containsAttribute || syntaxFacts.IsAttribute(node);
containsPrimaryConstructorBaseType = containsPrimaryConstructorBaseType || syntaxFacts.IsPrimaryConstructorBaseType(node);

TryAddAliasInfo(syntaxFacts, ref aliasInfo, node);

Expand Down Expand Up @@ -198,7 +200,8 @@ private static SyntaxTreeIndex CreateIndex(
containsGlobalKeyword,
containsCollectionInitializer,
containsAttribute,
containsDirective),
containsDirective,
containsPrimaryConstructorBaseType),
aliasInfo,
interceptsLocationInfo);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ internal sealed partial class SyntaxTreeIndex
public bool ContainsCollectionInitializer => _contextInfo.ContainsCollectionInitializer;
public bool ContainsAttribute => _contextInfo.ContainsAttribute;
public bool ContainsDirective => _contextInfo.ContainsDirective;
public bool ContainsPrimaryConstructorBaseType => _contextInfo.ContainsPrimaryConstructorBaseType;

/// <summary>
/// Gets the set of global aliases that point to something with the provided name and arity.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1585,6 +1585,14 @@ public bool IsObjectCollectionInitializer([NotNullWhen(true)] SyntaxNode? node)

#region GetPartsOfXXX members

public void GetPartsOfAliasQualifiedName(SyntaxNode node, out SyntaxNode alias, out SyntaxToken colonColonToken, out SyntaxNode name)
{
var qualifiedName = (AliasQualifiedNameSyntax)node;
alias = qualifiedName.Alias;
colonColonToken = qualifiedName.ColonColonToken;
name = qualifiedName.Name;
}

public void GetPartsOfArgumentList(SyntaxNode node, out SyntaxToken openParenToken, out SeparatedSyntaxList<SyntaxNode> arguments, out SyntaxToken closeParenToken)
{
var argumentListNode = (ArgumentListSyntax)node;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ public int Convert<TSyntaxKind>(TSyntaxKind kind) where TSyntaxKind : struct
public int TrueKeyword => (int)SyntaxKind.TrueKeyword;
public int UsingKeyword => (int)SyntaxKind.UsingKeyword;

public int? AliasQualifiedName => (int)SyntaxKind.AliasQualifiedName;
public int GenericName => (int)SyntaxKind.GenericName;
public int IdentifierName => (int)SyntaxKind.IdentifierName;
public int QualifiedName => (int)SyntaxKind.QualifiedName;
Expand Down Expand Up @@ -175,4 +176,5 @@ public int Convert<TSyntaxKind>(TSyntaxKind kind) where TSyntaxKind : struct
public int InterpolatedStringExpression => (int)SyntaxKind.InterpolatedStringExpression;
public int InterpolatedStringText => (int)SyntaxKind.InterpolatedStringText;
public int? IndexerMemberCref => (int)SyntaxKind.IndexerMemberCref;
public int? PrimaryConstructorBaseType => (int)SyntaxKind.PrimaryConstructorBaseType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ void GetPartsOfTupleExpression<TArgumentSyntax>(SyntaxNode node,

#region GetPartsOfXXX members

void GetPartsOfAliasQualifiedName(SyntaxNode node, out SyntaxNode alias, out SyntaxToken colonColonToken, out SyntaxNode name);
void GetPartsOfAnyIsTypeExpression(SyntaxNode node, out SyntaxNode expression, out SyntaxNode type);
void GetPartsOfArgumentList(SyntaxNode node, out SyntaxToken openParenToken, out SeparatedSyntaxList<SyntaxNode> arguments, out SyntaxToken closeParenToken);
void GetPartsOfAttribute(SyntaxNode node, out SyntaxNode name, out SyntaxNode? argumentList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,16 @@ public static bool IsRightOfQualifiedName(this ISyntaxFacts syntaxFacts, [NotNul
return node == right;
}

public static bool IsRightOfAliasQualifiedName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
{
var parent = node?.Parent;
if (!syntaxFacts.IsAliasQualifiedName(parent))
return false;

syntaxFacts.GetPartsOfAliasQualifiedName(parent, out _, out _, out var right);
return node == right;
}

public static bool IsTypeOfObjectCreationExpression(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
{
var parent = node?.Parent;
Expand Down Expand Up @@ -732,6 +742,9 @@ public static bool IsInterpolatedStringTextToken(this ISyntaxFacts syntaxFacts,

#region names

public static bool IsAliasQualifiedName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
=> node?.RawKind == syntaxFacts.SyntaxKinds.AliasQualifiedName;

public static bool IsGenericName(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
=> node?.RawKind == syntaxFacts.SyntaxKinds.GenericName;

Expand Down Expand Up @@ -1000,6 +1013,9 @@ public static bool IsImplicitElementAccess(this ISyntaxFacts syntaxFacts, [NotNu
public static bool IsIndexerMemberCref(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
=> node?.RawKind == syntaxFacts.SyntaxKinds.IndexerMemberCref;

public static bool IsPrimaryConstructorBaseType(this ISyntaxFacts syntaxFacts, [NotNullWhen(true)] SyntaxNode? node)
=> node?.RawKind == syntaxFacts.SyntaxKinds.PrimaryConstructorBaseType;

#endregion

#endregion
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ internal interface ISyntaxKinds

#region names

int? AliasQualifiedName { get; }
int GenericName { get; }
int IdentifierName { get; }
int QualifiedName { get; }
Expand Down Expand Up @@ -231,6 +232,7 @@ internal interface ISyntaxKinds
int Interpolation { get; }
int InterpolatedStringText { get; }
int? IndexerMemberCref { get; }
int? PrimaryConstructorBaseType { get; }

#endregion
}
Original file line number Diff line number Diff line change
Expand Up @@ -1822,6 +1822,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService

#Region "GetPartsOfXXX members"

Public Sub GetPartsOfAliasQualifiedName(node As SyntaxNode, ByRef [alias] As SyntaxNode, ByRef colonColonToken As SyntaxToken, ByRef name As SyntaxNode) Implements ISyntaxFacts.GetPartsOfAliasQualifiedName
Throw New InvalidOperationException(DoesNotExistInVBErrorMessage)
End Sub

Public Sub GetPartsOfArgumentList(node As SyntaxNode, ByRef openParenToken As SyntaxToken, ByRef arguments As SeparatedSyntaxList(Of SyntaxNode), ByRef closeParenToken As SyntaxToken) Implements ISyntaxFacts.GetPartsOfArgumentList
Dim argumentList = DirectCast(node, ArgumentListSyntax)
openParenToken = argumentList.OpenParenToken
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService
Public ReadOnly Property TrueKeyword As Integer = SyntaxKind.TrueKeyword Implements ISyntaxKinds.TrueKeyword
Public ReadOnly Property UsingKeyword As Integer = SyntaxKind.UsingKeyword Implements ISyntaxKinds.UsingKeyword

Public ReadOnly Property AliasQualifiedName As Integer? Implements ISyntaxKinds.AliasQualifiedName
Public ReadOnly Property GenericName As Integer = SyntaxKind.GenericName Implements ISyntaxKinds.GenericName
Public ReadOnly Property IdentifierName As Integer = SyntaxKind.IdentifierName Implements ISyntaxKinds.IdentifierName
Public ReadOnly Property QualifiedName As Integer = SyntaxKind.QualifiedName Implements ISyntaxKinds.QualifiedName
Expand Down Expand Up @@ -179,5 +180,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageService
Public ReadOnly Property InterpolatedStringExpression As Integer = SyntaxKind.InterpolatedStringExpression Implements ISyntaxKinds.InterpolatedStringExpression
Public ReadOnly Property InterpolatedStringText As Integer = SyntaxKind.InterpolatedStringText Implements ISyntaxKinds.InterpolatedStringText
Public ReadOnly Property IndexerMemberCref As Integer? Implements ISyntaxKinds.IndexerMemberCref
Public ReadOnly Property PrimaryConstructorBaseType As Integer? Implements ISyntaxKinds.PrimaryConstructorBaseType
End Class
End Namespace
Loading