Skip to content

Commit d35e871

Browse files
committed
Handle ref struct
fix #480
1 parent 8429387 commit d35e871

4 files changed

Lines changed: 82 additions & 24 deletions

File tree

IDisposableAnalyzers.Test/Helpers/DisposableTests.IsCreation.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,28 @@ public void Update()
755755
Assert.AreEqual(true, Disposable.IsCreation(value, semanticModel, CancellationToken.None));
756756
}
757757

758+
[TestCase("new S()")]
759+
public static void RefStruct(string expression)
760+
{
761+
var syntaxTree = CSharpSyntaxTree.ParseText("""
762+
namespace N;
763+
764+
public ref struct S
765+
{
766+
public static S M() => new S();
767+
768+
public void Dispose()
769+
{
770+
}
771+
}
772+
""".AssertReplace("new S()", expression));
773+
774+
var compilation = CSharpCompilation.Create("test", new[] { syntaxTree });
775+
var semanticModel = compilation.GetSemanticModel(syntaxTree);
776+
var value = syntaxTree.FindExpression(expression);
777+
Assert.AreEqual(true, Disposable.IsCreation(value, semanticModel, CancellationToken.None));
778+
}
779+
758780
[Ignore("Script")]
759781
[Test]
760782
public static void Dump()

IDisposableAnalyzers.Test/IDISP001DisposeCreatedTests/CodeFix.AddUsing.AddUsingDeclaration.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,5 +700,45 @@ private static Kernel Create()
700700
RoslynAssert.CodeFix(Analyzer, Fix, ExpectedDiagnostic, new[] { disposable, before }, after, fixTitle: "using");
701701
RoslynAssert.FixAll(Analyzer, Fix, ExpectedDiagnostic, new[] { disposable, before }, after, fixTitle: "using");
702702
}
703+
704+
[TestCase("new S()")]
705+
public static void LocalRefStructToUsingDeclaration(string expression)
706+
{
707+
var refStruct = """
708+
namespace N;
709+
710+
public ref struct S
711+
{
712+
public void Dispose()
713+
{
714+
}
715+
}
716+
""";
717+
var before = """
718+
namespace N;
719+
720+
public sealed class C
721+
{
722+
public void M()
723+
{
724+
↓S s = new S();
725+
}
726+
}
727+
""".AssertReplace("new S()", expression);
728+
729+
var after = """
730+
namespace N;
731+
732+
public sealed class C
733+
{
734+
public void M()
735+
{
736+
using S s = new S();
737+
}
738+
}
739+
""".AssertReplace("new S()", expression);
740+
741+
RoslynAssert.CodeFix(Analyzer, Fix, ExpectedDiagnostic, new[] { refStruct, before }, after, fixTitle: "using");
742+
}
703743
}
704744
}

IDisposableAnalyzers/Helpers/Disposable.cs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,16 @@
99

1010
internal static partial class Disposable
1111
{
12-
internal static bool IsPotentiallyAssignableFrom(ExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken)
12+
internal static bool IsPotentiallyAssignableFrom(ExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken) => candidate switch
1313
{
14-
return candidate switch
15-
{
16-
{ IsMissing: true } => false,
17-
LiteralExpressionSyntax _ => false,
18-
ObjectCreationExpressionSyntax objectCreation
19-
=> semanticModel.TryGetType(objectCreation, cancellationToken, out var type) &&
20-
IsAssignableFrom(type, semanticModel.Compilation),
21-
_ => semanticModel.TryGetType(candidate, cancellationToken, out var type) &&
22-
IsPotentiallyAssignableFrom(type, semanticModel.Compilation),
23-
};
24-
}
14+
{ IsMissing: true } => false,
15+
LiteralExpressionSyntax _ => false,
16+
ObjectCreationExpressionSyntax objectCreation
17+
=> semanticModel.TryGetType(objectCreation, cancellationToken, out var type) &&
18+
IsAssignableFrom(type, semanticModel.Compilation),
19+
_ => semanticModel.TryGetType(candidate, cancellationToken, out var type) &&
20+
IsPotentiallyAssignableFrom(type, semanticModel.Compilation),
21+
};
2522

2623
internal static bool IsPotentiallyAssignableFrom(ITypeSymbol type, Compilation compilation)
2724
{
@@ -45,18 +42,16 @@ internal static bool IsPotentiallyAssignableFrom(ITypeSymbol type, Compilation c
4542
return true;
4643
}
4744

48-
internal static bool IsAssignableFrom(ITypeSymbol type, Compilation compilation)
45+
internal static bool IsAssignableFrom(ITypeSymbol type, Compilation compilation) => type switch
4946
{
50-
return type switch
51-
{
52-
null => false,
53-
//// https://blogs.msdn.microsoft.com/pfxteam/2012/03/25/do-i-need-to-dispose-of-tasks/
54-
{ ContainingNamespace: { MetadataName: "Tasks", ContainingNamespace: { MetadataName: "Threading", ContainingNamespace.MetadataName: "System" } }, MetadataName: "Task" } => false,
55-
INamedTypeSymbol { ContainingNamespace: { MetadataName: "Tasks", ContainingNamespace: { MetadataName: "Threading", ContainingNamespace.MetadataName: "System" } }, MetadataName: "Task`1", TypeArguments: { Length: 1 } arguments }
56-
=> IsAssignableFrom(arguments[0], compilation),
57-
_ => type.IsAssignableTo(KnownSymbols.IDisposable, compilation),
58-
};
59-
}
47+
null => false,
48+
//// https://blogs.msdn.microsoft.com/pfxteam/2012/03/25/do-i-need-to-dispose-of-tasks/
49+
{ ContainingNamespace: { MetadataName: "Tasks", ContainingNamespace: { MetadataName: "Threading", ContainingNamespace.MetadataName: "System" } }, MetadataName: "Task" } => false,
50+
INamedTypeSymbol { ContainingNamespace: { MetadataName: "Tasks", ContainingNamespace: { MetadataName: "Threading", ContainingNamespace.MetadataName: "System" } }, MetadataName: "Task`1", TypeArguments: { Length: 1 } arguments }
51+
=> IsAssignableFrom(arguments[0], compilation),
52+
{ IsRefLikeType: true } => DisposeMethod.IsAccessibleOn(type, compilation),
53+
_ => type.IsAssignableTo(KnownSymbols.IDisposable, compilation),
54+
};
6055

6156
internal static bool IsNop(ExpressionSyntax candidate, SemanticModel semanticModel, CancellationToken cancellationToken)
6257
{

IDisposableAnalyzers/Helpers/DisposeMethod.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ internal static class DisposeMethod
1111
{
1212
internal static IMethodSymbol? Find(ITypeSymbol type, Compilation compilation, Search search)
1313
{
14-
if (!type.IsAssignableTo(KnownSymbols.IDisposable, compilation))
14+
if (!type.IsAssignableTo(KnownSymbols.IDisposable, compilation) &&
15+
!type.IsRefLikeType)
1516
{
1617
return null;
1718
}

0 commit comments

Comments
 (0)