Skip to content
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 @@ -3969,6 +3969,276 @@ End Class
Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function CSharp_TestDisposeUsedInUsingDeclaration1(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
struct S
{
public void {|Definition:$$Dispose|}() { }
}

class C
{
void M()
{
[|using|] (var s = new S())
{
}
}
}
</Document>
</Project>
</Workspace>
Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function CSharp_TestDisposeUsedInUsingDeclaration2(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
struct S
{
public void {|Definition:Dispose|}() { }
}

class C
{
void M()
{
[|$$using|] (var s = new S())
{
}
}
}
</Document>
</Project>
</Workspace>
Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function CSharp_TestDisposeUsedInUsingDeclaration3(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
struct S
{
public void {|Definition:$$Dispose|}() { }
}

class C
{
void M()
{
[|using|] var s = new S();
}
}
</Document>
</Project>
</Workspace>
Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function CSharp_TestDisposeUsedInUsingDeclaration4(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
struct S
{
public void {|Definition:Dispose|}() { }
}

class C
{
void M()
{
[|$$using|] var s = new S();
}
}
</Document>
</Project>
</Workspace>
Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function CSharp_TestDisposeUsedInUsingDeclaration5(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
struct S
{
public void {|Definition:$$Dispose|}() { }
}

class C
{
void M()
{
[|using|] (new S())
{
}
}
}
</Document>
</Project>
</Workspace>
Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function CSharp_TestDisposeUsedInUsingDeclaration6(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
struct S
{
public void {|Definition:Dispose|}() { }
}

class C
{
void M()
{
[|$$using|] (new S())
{
}
}
}
</Document>
</Project>
</Workspace>
Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function VisualBasic_TestDisposeUsedInUsingDeclaration1(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
imports System
structure S
implements IDisposable

public sub {|Definition:$$Dispose|}() Implements IDisposable.[|Dispose|]
end sub
end structure

class C
sub M()
[|using|] (new S())
end using
end sub
end class
</Document>
</Project>
</Workspace>

Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function VisualBasic_TestDisposeUsedInUsingDeclaration2(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
imports System
structure S
implements IDisposable

public sub {|Definition:Dispose|}() Implements IDisposable.[|Dispose|]
end sub
end structure

class C
sub M()
[|$$using|] (new S())
end using
end sub
end class
</Document>
</Project>
</Workspace>

Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function VisualBasic_TestDisposeUsedInUsingDeclaration3(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
imports System
structure S
implements IDisposable

public sub {|Definition:$$Dispose|}() Implements IDisposable.[|Dispose|]
end sub
end structure

class C
sub M()
[|using|] x = new S()
end using
end sub
end class
</Document>
</Project>
</Workspace>

Await TestAPI(input, host)
End Function

<WpfTheory, CombinatorialData>
<WorkItem("https://github.com/dotnet/roslyn/issues/34107")>
Public Async Function VisualBasic_TestDisposeUsedInUsingDeclaration4(host As TestHost) As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
imports System
structure S
implements IDisposable

public sub {|Definition:Dispose|}() Implements IDisposable.[|Dispose|]
end sub
end structure

class C
sub M()
[|$$using|] x = new S()
end using
end sub
end class
</Document>
</Project>
</Workspace>

Await TestAPI(input, host)
End Function

#Region "Collection Initializers"

<WpfTheory, CombinatorialData>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,9 @@ protected static Task FindDocumentsWithPredicateAsync<TData>(
protected static Task FindDocumentsWithForEachStatementsAsync<TData>(Project project, IImmutableSet<Document>? documents, Action<Document, TData> processResult, TData processResultData, CancellationToken cancellationToken)
=> FindDocumentsWithPredicateAsync(project, documents, static index => index.ContainsForEachStatement, processResult, processResultData, cancellationToken);

protected static Task FindDocumentsWithUsingStatementsAsync<TData>(Project project, IImmutableSet<Document>? documents, Action<Document, TData> processResult, TData processResultData, CancellationToken cancellationToken)
=> FindDocumentsWithPredicateAsync(project, documents, static index => index.ContainsUsingStatement, processResult, processResultData, cancellationToken);

/// <summary>
/// If the `node` implicitly matches the `symbol`, then it will be added to `locations`.
/// </summary>
Expand Down Expand Up @@ -393,12 +396,9 @@ protected void FindReferencesInForEachStatements<TData>(
TData processResultData,
CancellationToken cancellationToken)
{
FindReferencesInDocument(state, IsRelevantDocument, CollectMatchingReferences, processResult, processResultData, cancellationToken);
FindReferencesInDocument(state, static index => index.ContainsForEachStatement, CollectMatchingReferences, processResult, processResultData, cancellationToken);
return;

static bool IsRelevantDocument(SyntaxTreeIndex syntaxTreeInfo)
=> syntaxTreeInfo.ContainsForEachStatement;

void CollectMatchingReferences(
SyntaxNode node, FindReferencesDocumentState state, Action<FinderLocation, TData> processResult, TData processResultData)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ await FindDocumentsWithGlobalSuppressMessageAttributeAsync(

if (IsAddMethod(methodSymbol))
await FindDocumentsWithCollectionInitializersAsync(project, documents, processResult, processResultData, cancellationToken).ConfigureAwait(false);

if (IsDisposeMethod(methodSymbol))
await FindDocumentsWithUsingStatementsAsync(project, documents, processResult, processResultData, cancellationToken).ConfigureAwait(false);
}

private static Task FindDocumentsWithDeconstructionAsync<TData>(Project project, IImmutableSet<Document>? documents, Action<Document, TData> processResult, TData processResultData, CancellationToken cancellationToken)
Expand All @@ -109,6 +112,9 @@ private static bool IsGetAwaiterMethod(IMethodSymbol methodSymbol)
private static bool IsAddMethod(IMethodSymbol methodSymbol)
=> methodSymbol.Name == WellKnownMemberNames.CollectionInitializerAddMethodName;

private static bool IsDisposeMethod(IMethodSymbol methodSymbol)
=> methodSymbol.Name == nameof(IDisposable.Dispose);

protected sealed override void FindReferencesInDocument<TData>(
IMethodSymbol symbol,
FindReferencesDocumentState state,
Expand All @@ -134,5 +140,44 @@ protected sealed override void FindReferencesInDocument<TData>(

if (IsAddMethod(symbol))
FindReferencesInCollectionInitializer(symbol, state, processResult, processResultData, cancellationToken);

if (IsDisposeMethod(symbol))
FindReferencesInUsingStatements(symbol, state, processResult, processResultData, cancellationToken);
}

private void FindReferencesInUsingStatements<TData>(
IMethodSymbol symbol,
FindReferencesDocumentState state,
Action<FinderLocation, TData> processResult,
TData processResultData,
CancellationToken cancellationToken)
{
FindReferencesInDocument(state, static index => index.ContainsUsingStatement, CollectMatchingReferences, processResult, processResultData, cancellationToken);
return;

void CollectMatchingReferences(
SyntaxNode node,
FindReferencesDocumentState state,
Action<FinderLocation, TData> processResult,
TData processResultData)
{
var disposeMethod = state.SemanticFacts.TryGetDisposeMethod(state.SemanticModel, node, cancellationToken);

if (Matches(disposeMethod, symbol))
{
var location = node.GetFirstToken().GetLocation();
var symbolUsageInfo = GetSymbolUsageInfo(node, state, cancellationToken);

var result = new FinderLocation(node, new ReferenceLocation(
state.Document,
alias: null,
location: location,
isImplicit: true,
symbolUsageInfo,
GetAdditionalFindUsagesProperties(node, state),
candidateReason: 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("44");
private static readonly Checksum s_serializationFormatChecksum = CodeAnalysis.Checksum.Create("45");

/// <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 @@ -90,7 +90,7 @@ private static SyntaxTreeIndex CreateIndex(

containsForEachStatement = containsForEachStatement || syntaxFacts.IsForEachStatement(node);
containsLockStatement = containsLockStatement || syntaxFacts.IsLockStatement(node);
containsUsingStatement = containsUsingStatement || syntaxFacts.IsUsingStatement(node);
containsUsingStatement = containsUsingStatement || syntaxFacts.IsUsingStatement(node) || syntaxFacts.IsUsingLocalDeclarationStatement(node);
Copy link
Contributor

Choose a reason for hiding this comment

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

IsUsingLocalDeclarationStatement

To clarify for me, this is tracking both "using namespace" and "using var foo = IDisposable" statements, right? Other than the name being the same, why are they grouped together?

Copy link
Member Author

Choose a reason for hiding this comment

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

To clarify for me, this is tracking both "using namespace" and "using var foo = IDisposable" statements, right?

Yes.

Other than the name being the same, why are they grouped together?

Because this is for FindRefs, which only cares about tthis info just to say "should i look for Dispose references here?". And for FindRefs, it doesn't care about which of the two cases it is.

I could do this as two different bools, but the consumption side would then have to check for both. So i just collapse here to one bool since htat's all we need.

containsQueryExpression = containsQueryExpression || syntaxFacts.IsQueryExpression(node);
containsElementAccess = containsElementAccess || (syntaxFacts.IsElementAccessExpression(node) || syntaxFacts.IsImplicitElementAccess(node));
containsIndexerMemberCref = containsIndexerMemberCref || syntaxFacts.IsIndexerMemberCref(node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ public void GetPartsOfUsingAliasDirective(SyntaxNode node, out SyntaxToken globa
public bool IsDeconstructionForEachStatement([NotNullWhen(true)] SyntaxNode? node)
=> node is ForEachVariableStatementSyntax;

public bool IsUsingLocalDeclarationStatement([NotNullWhen(true)] SyntaxNode? node)
=> node is LocalDeclarationStatementSyntax { UsingKeyword.RawKind: not (int)SyntaxKind.None };

public bool IsDeconstructionAssignment([NotNullWhen(true)] SyntaxNode? node)
=> node is AssignmentExpressionSyntax assignment && assignment.IsDeconstruction();

Expand Down
Loading
Loading