@@ -5660,6 +5660,119 @@ type UseTheThings(i:int) =
5660
5660
((( 37 , 10 ), ( 37 , 21 )), " open type FSharpEnum2 // Unused, should appear." )]
5661
5661
unusedOpensData |> shouldEqual expected
5662
5662
5663
+ [<Theory>]
5664
+ [<InlineData( """ // 1. Auto-open type extension.
5665
+ // https://github.com/dotnet/fsharp/issues/17629
5666
+ module Module
5667
+
5668
+ type T =
5669
+ static member A = 99
5670
+
5671
+ [<AutoOpen>]
5672
+ module M =
5673
+ type T with
5674
+ static member Lol = 3
5675
+
5676
+ // Shows as unused; the unused opens analyzer will suggest removing it…
5677
+ open type T
5678
+
5679
+ // …Even though it's used.
5680
+ let lol = Lol
5681
+ """ ) >]
5682
+ [<InlineData( """ // 2. Open type on fully-qualified union type.
5683
+ // https://github.com/dotnet/fsharp/issues/17629
5684
+ module M =
5685
+ type E = A | B
5686
+
5687
+ open type M.E // Considered unused, even though it is.
5688
+
5689
+ let f x =
5690
+ match x with
5691
+ | A -> ()
5692
+ | B -> ()
5693
+ """ ) >]
5694
+ [<InlineData( """ // 3. Open namespace with auto-opened type.
5695
+ // https://github.com/dotnet/fsharp/issues/17929
5696
+ namespace SomeOtherNamespace
5697
+
5698
+ [<AutoOpen>]
5699
+ type SomeUnusedType =
5700
+ static member y = 1
5701
+
5702
+ namespace Test
5703
+
5704
+ open SomeOtherNamespace
5705
+
5706
+ type UseTheThings(i:int) =
5707
+ member x.UseSomeOtherNamespaceTypeMember() = y
5708
+ """ ) >]
5709
+ let ``Unused opens misc`` fileSource1Text =
5710
+ let fileName1 = Path.ChangeExtension( getTemporaryFileName (), " .fs" )
5711
+ let base2 = getTemporaryFileName ()
5712
+ let dllName = Path.ChangeExtension( base2, " .dll" )
5713
+ let projFileName = Path.ChangeExtension( base2, " .fsproj" )
5714
+ let fileSource1 = SourceText.ofString fileSource1Text
5715
+ FileSystem.OpenFileForWriteShim( fileName1) .Write( fileSource1Text)
5716
+
5717
+ let fileNames = [| fileName1|]
5718
+ let args = mkProjectCommandLineArgs ( dllName, [])
5719
+ let keepAssemblyContentsChecker = FSharpChecker.Create( keepAssemblyContents= true , useTransparentCompiler= CompilerAssertHelpers.UseTransparentCompiler)
5720
+ let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs ( projFileName, args) with SourceFiles = fileNames }
5721
+
5722
+ let fileCheckResults =
5723
+ keepAssemblyContentsChecker.ParseAndCheckFileInProject( fileName1, 0 , fileSource1, options) |> Async.RunImmediate
5724
+ |> function
5725
+ | _, FSharpCheckFileAnswer.Succeeded( res) -> res
5726
+ | _ -> failwithf " Parsing aborted unexpectedly..."
5727
+ let lines = FileSystem.OpenFileForReadShim( fileName1) .ReadAllLines()
5728
+ let unusedOpens = UnusedOpens.getUnusedOpens ( fileCheckResults, ( fun i -> lines[ i-1 ])) |> Async.RunImmediate
5729
+ let unusedOpensData = [ for uo in unusedOpens -> tups uo, lines[ uo.StartLine-1 ] ]
5730
+ unusedOpensData |> shouldEqual []
5731
+
5732
+ [<Fact>]
5733
+ let ``Unused opens shadowing`` () =
5734
+ let fileSource1Text =
5735
+ """
5736
+ // https://github.com/dotnet/fsharp/issues/16226
5737
+ namespace Case2
5738
+
5739
+ module RecordA =
5740
+ type Record = { Foo: string }
5741
+
5742
+ module RecordB =
5743
+ type Record = { Bar: string }
5744
+
5745
+ module Use =
5746
+ open RecordB // Not required.
5747
+ open RecordA // Required.
5748
+ open RecordB // Required.
5749
+
5750
+ let convertBToA (recordB: Record) =
5751
+ { Foo = recordB.Bar }
5752
+ """
5753
+
5754
+ let fileName1 = Path.ChangeExtension( getTemporaryFileName (), " .fs" )
5755
+ let base2 = getTemporaryFileName ()
5756
+ let dllName = Path.ChangeExtension( base2, " .dll" )
5757
+ let projFileName = Path.ChangeExtension( base2, " .fsproj" )
5758
+ let fileSource1 = SourceText.ofString fileSource1Text
5759
+ FileSystem.OpenFileForWriteShim( fileName1) .Write( fileSource1Text)
5760
+
5761
+ let fileNames = [| fileName1|]
5762
+ let args = mkProjectCommandLineArgs ( dllName, [])
5763
+ let keepAssemblyContentsChecker = FSharpChecker.Create( keepAssemblyContents= true , useTransparentCompiler= CompilerAssertHelpers.UseTransparentCompiler)
5764
+ let options = { keepAssemblyContentsChecker.GetProjectOptionsFromCommandLineArgs ( projFileName, args) with SourceFiles = fileNames }
5765
+
5766
+ let fileCheckResults =
5767
+ keepAssemblyContentsChecker.ParseAndCheckFileInProject( fileName1, 0 , fileSource1, options) |> Async.RunImmediate
5768
+ |> function
5769
+ | _, FSharpCheckFileAnswer.Succeeded( res) -> res
5770
+ | _ -> failwithf " Parsing aborted unexpectedly..."
5771
+ let lines = FileSystem.OpenFileForReadShim( fileName1) .ReadAllLines()
5772
+ let unusedOpens = UnusedOpens.getUnusedOpens ( fileCheckResults, ( fun i -> lines[ i-1 ])) |> Async.RunImmediate
5773
+ let unusedOpensData = [ for uo in unusedOpens -> tups uo, lines[ uo.StartLine-1 ] ]
5774
+ unusedOpensData |> shouldEqual [((( 12 , 9 ), ( 12 , 16 )), " open RecordB // Not required." )]
5775
+
5663
5776
[<Fact>]
5664
5777
let ``Unused opens smoke test auto open`` () =
5665
5778
0 commit comments