From b3c5a23dd67fdcbd337414d233ff996b6537615f Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Mon, 8 Sep 2025 19:36:26 +0300 Subject: [PATCH 1/5] wip --- src/Compiler/TypedTree/TcGlobals.fs | 41 ++++++++++--------- .../EditorTests.fs | 6 +-- .../FSharp.Compiler.Service.Tests/Symbols.fs | 12 ++++++ 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index e97e774d7b8..b563c89448b 100644 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -966,27 +966,28 @@ type TcGlobals( let mkDebuggerTypeProxyAttribute (ty : ILType) = mkILCustomAttribute (findSysILTypeRef tname_DebuggerTypeProxyAttribute, [ilg.typ_Type], [ILAttribElem.TypeRef (Some ty.TypeRef)], []) let betterTyconEntries = - [| "Int32" , v_int_tcr - "IntPtr" , v_nativeint_tcr - "UIntPtr" , v_unativeint_tcr - "Int16" , v_int16_tcr - "Int64" , v_int64_tcr - "UInt16" , v_uint16_tcr - "UInt32" , v_uint32_tcr - "UInt64" , v_uint64_tcr - "SByte" , v_sbyte_tcr - "Decimal" , v_decimal_tcr - "Byte" , v_byte_tcr - "Boolean" , v_bool_tcr - "String" , v_string_tcr - "Object" , v_obj_tcr - "Exception", v_exn_tcr - "Char" , v_char_tcr - "Double" , v_float_tcr - "Single" , v_float32_tcr |] - |> Array.map (fun (nm, tcr) -> + [| sys, "Int32" , v_int_tcr + sys, "IntPtr" , v_nativeint_tcr + sys, "UIntPtr" , v_unativeint_tcr + sys, "Int16" , v_int16_tcr + sys, "Int64" , v_int64_tcr + sys, "UInt16" , v_uint16_tcr + sys, "UInt32" , v_uint32_tcr + sys, "UInt64" , v_uint64_tcr + sys, "SByte" , v_sbyte_tcr + sys, "Decimal" , v_decimal_tcr + sys, "Byte" , v_byte_tcr + sys, "Boolean" , v_bool_tcr + sys, "String" , v_string_tcr + sys, "Object" , v_obj_tcr + sys, "Exception" , v_exn_tcr + sys, "Char" , v_char_tcr + sys, "Double" , v_float_tcr + sys, "Single" , v_float32_tcr + sysGenerics, "IEnumerable`1", v_seq_tcr |] + |> Array.map (fun (sysLib, nm, tcr) -> let ty = mkNonGenericTy tcr - nm, findSysTyconRef sys nm, (fun _ nullness -> + nm, findSysTyconRef sysLib nm, (fun _ nullness -> match nullness with | Nullness.Known NullnessInfo.WithoutNull -> ty | _ -> mkNonGenericTyWithNullness tcr nullness)) diff --git a/tests/FSharp.Compiler.Service.Tests/EditorTests.fs b/tests/FSharp.Compiler.Service.Tests/EditorTests.fs index 6ac0789cd9c..6df9a1dc4a9 100644 --- a/tests/FSharp.Compiler.Service.Tests/EditorTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/EditorTests.fs @@ -130,15 +130,15 @@ let ``GetMethodsAsSymbols should return all overloads of a method as FSharpSymbo yield ms.Symbol.DisplayName, extractCurriedParams ms ] |> List.sortBy (fun (_name, parameters) -> parameters.Length, (parameters |> List.map snd )) let expected = - [("Concat", [("values", "Collections.Generic.IEnumerable<'T>")]); - ("Concat", [("values", "Collections.Generic.IEnumerable")]); + [("Concat", [("values", "'T seq")]); #if NETCOREAPP ("Concat", [("args", "ReadOnlySpan")]); ("Concat", [("values", "ReadOnlySpan")]); #endif ("Concat", [("arg0", "obj")]); ("Concat", [("args", "obj array")]); - ("Concat", [("values", "string array")]); + ("Concat", [("values", "string array")]) + ("Concat", [("values", "string seq")]) #if NETCOREAPP ("Concat", [("str0", "ReadOnlySpan");("str1", "ReadOnlySpan")]); #endif diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs index b5126bf1ca6..cc83a98cb1a 100644 --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs @@ -521,6 +521,18 @@ let f2 b1 b2 b3 b4 b5 = let ``Nullable types`` declaredType formattedType = let _, checkResults = getParseAndCheckResults $""" let f (x: {declaredType}) = () +""" + let symbolUse = findSymbolUseByName "x" checkResults + let symbol = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue + let typeArg = symbol.FullType + typeArg.Format(symbolUse.DisplayContext) |> shouldEqual formattedType + + [] + [", "int seq")>] + let ``Format IEnumerable as seq`` declaredType formattedType = + let _, checkResults = getParseAndCheckResults $""" +open System.Collections.Generic +let f (x: {declaredType}) = () """ let symbolUse = findSymbolUseByName "x" checkResults let symbol = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue From 53de20972fde6f8efc1b1b5b6f536f5bfc184348 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Mon, 8 Sep 2025 20:06:01 +0300 Subject: [PATCH 2/5] wip --- src/Compiler/TypedTree/TcGlobals.fs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index b563c89448b..aea2c81e0d6 100644 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -987,10 +987,11 @@ type TcGlobals( sysGenerics, "IEnumerable`1", v_seq_tcr |] |> Array.map (fun (sysLib, nm, tcr) -> let ty = mkNonGenericTy tcr - nm, findSysTyconRef sysLib nm, (fun _ nullness -> - match nullness with - | Nullness.Known NullnessInfo.WithoutNull -> ty - | _ -> mkNonGenericTyWithNullness tcr nullness)) + nm, findSysTyconRef sysLib nm, (fun typars nullness -> + match typars, nullness with + | [], Nullness.Known NullnessInfo.WithoutNull -> ty + | [], nullness -> mkNonGenericTyWithNullness tcr nullness + | _ -> TType_app(tcr, typars, nullness))) let decompileTyconEntries = [| From c05b6dd62ee2ac3e3d9f3734d290556fbba95872 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Mon, 8 Sep 2025 20:18:51 +0300 Subject: [PATCH 3/5] wip --- tests/FSharp.Compiler.Service.Tests/Symbols.fs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs index cc83a98cb1a..b31c0f7e360 100644 --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs @@ -528,11 +528,24 @@ let f (x: {declaredType}) = () typeArg.Format(symbolUse.DisplayContext) |> shouldEqual formattedType [] - [", "int seq")>] - let ``Format IEnumerable as seq`` declaredType formattedType = + [", "IEnumerable")>] + let ``Format explicit IEnumerable as IEnumerable`` declaredType formattedType = let _, checkResults = getParseAndCheckResults $""" open System.Collections.Generic let f (x: {declaredType}) = () +""" + let symbolUse = findSymbolUseByName "x" checkResults + let symbol = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue + let typeArg = symbol.FullType + typeArg.Format(symbolUse.DisplayContext) |> shouldEqual formattedType + + [] + [", "int seq")>] + let ``Format implicit IEnumerable as seq`` declaredType formattedType = + let _, checkResults = getParseAndCheckResults $""" +open System.Linq + +let x = [1].AsEnumerable() """ let symbolUse = findSymbolUseByName "x" checkResults let symbol = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue From 827807b257a63039faa324b8738f9c725bd11656 Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Tue, 16 Sep 2025 01:11:12 +0300 Subject: [PATCH 4/5] wip --- src/Compiler/TypedTree/TcGlobals.fs | 43 ++++++++--------- .../ProjectAnalysisTests.fs | 2 +- .../FSharp.Compiler.Service.Tests/Symbols.fs | 19 ++------ .../QuickInfoProviderTests.fs | 2 +- .../Refactors/AddReturnTypeTests.fs | 47 +++++++++++++++---- 5 files changed, 66 insertions(+), 47 deletions(-) diff --git a/src/Compiler/TypedTree/TcGlobals.fs b/src/Compiler/TypedTree/TcGlobals.fs index aea2c81e0d6..ee5a3c38670 100644 --- a/src/Compiler/TypedTree/TcGlobals.fs +++ b/src/Compiler/TypedTree/TcGlobals.fs @@ -966,28 +966,29 @@ type TcGlobals( let mkDebuggerTypeProxyAttribute (ty : ILType) = mkILCustomAttribute (findSysILTypeRef tname_DebuggerTypeProxyAttribute, [ilg.typ_Type], [ILAttribElem.TypeRef (Some ty.TypeRef)], []) let betterTyconEntries = - [| sys, "Int32" , v_int_tcr - sys, "IntPtr" , v_nativeint_tcr - sys, "UIntPtr" , v_unativeint_tcr - sys, "Int16" , v_int16_tcr - sys, "Int64" , v_int64_tcr - sys, "UInt16" , v_uint16_tcr - sys, "UInt32" , v_uint32_tcr - sys, "UInt64" , v_uint64_tcr - sys, "SByte" , v_sbyte_tcr - sys, "Decimal" , v_decimal_tcr - sys, "Byte" , v_byte_tcr - sys, "Boolean" , v_bool_tcr - sys, "String" , v_string_tcr - sys, "Object" , v_obj_tcr - sys, "Exception" , v_exn_tcr - sys, "Char" , v_char_tcr - sys, "Double" , v_float_tcr - sys, "Single" , v_float32_tcr - sysGenerics, "IEnumerable`1", v_seq_tcr |] - |> Array.map (fun (sysLib, nm, tcr) -> + [| yield sys, "Int32" , v_int_tcr + yield sys, "IntPtr" , v_nativeint_tcr + yield sys, "UIntPtr" , v_unativeint_tcr + yield sys, "Int16" , v_int16_tcr + yield sys, "Int64" , v_int64_tcr + yield sys, "UInt16" , v_uint16_tcr + yield sys, "UInt32" , v_uint32_tcr + yield sys, "UInt64" , v_uint64_tcr + yield sys, "SByte" , v_sbyte_tcr + yield sys, "Decimal" , v_decimal_tcr + yield sys, "Byte" , v_byte_tcr + yield sys, "Boolean" , v_bool_tcr + yield sys, "String" , v_string_tcr + yield sys, "Object" , v_obj_tcr + yield sys, "Exception", v_exn_tcr + yield sys, "Char" , v_char_tcr + yield sys, "Double" , v_float_tcr + yield sys, "Single" , v_float32_tcr + if not compilingFSharpCore then + yield sysGenerics, "IEnumerable`1", v_seq_tcr |] + |> Array.map (fun (qualifier, nm, tcr) -> let ty = mkNonGenericTy tcr - nm, findSysTyconRef sysLib nm, (fun typars nullness -> + nm, findSysTyconRef qualifier nm, (fun typars nullness -> match typars, nullness with | [], Nullness.Known NullnessInfo.WithoutNull -> ty | [], nullness -> mkNonGenericTyWithNullness tcr nullness diff --git a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs index 5c4aba97324..df1a064ae9d 100644 --- a/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs +++ b/tests/FSharp.Compiler.Service.Tests/ProjectAnalysisTests.fs @@ -3216,7 +3216,7 @@ let ``Test Project22 IList contents`` () = set [ for x in ilistTypeDefn.AllInterfaces -> x.TypeDefinition.DisplayName, attribsOfSymbol x.TypeDefinition ] |> shouldEqual (set [("IList", ["interface"]); ("ICollection", ["interface"]); - ("IEnumerable", ["interface"]); ("IEnumerable", ["interface"])]) + ("IEnumerable", ["interface"]); ("seq", ["abbrev"])]) arrayTypes |> shouldEqual [|("[]", 1); ("[,,]", 3)|] diff --git a/tests/FSharp.Compiler.Service.Tests/Symbols.fs b/tests/FSharp.Compiler.Service.Tests/Symbols.fs index b31c0f7e360..06a84651fb6 100644 --- a/tests/FSharp.Compiler.Service.Tests/Symbols.fs +++ b/tests/FSharp.Compiler.Service.Tests/Symbols.fs @@ -528,24 +528,13 @@ let f (x: {declaredType}) = () typeArg.Format(symbolUse.DisplayContext) |> shouldEqual formattedType [] - [", "IEnumerable")>] - let ``Format explicit IEnumerable as IEnumerable`` declaredType formattedType = + [ = []", "IEnumerable")>] + [] + let ``Format IEnumerable`` code formattedType = let _, checkResults = getParseAndCheckResults $""" open System.Collections.Generic -let f (x: {declaredType}) = () -""" - let symbolUse = findSymbolUseByName "x" checkResults - let symbol = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue - let typeArg = symbol.FullType - typeArg.Format(symbolUse.DisplayContext) |> shouldEqual formattedType - - [] - [", "int seq")>] - let ``Format implicit IEnumerable as seq`` declaredType formattedType = - let _, checkResults = getParseAndCheckResults $""" open System.Linq - -let x = [1].AsEnumerable() +{code} """ let symbolUse = findSymbolUseByName "x" checkResults let symbol = symbolUse.Symbol :?> FSharpMemberOrFunctionOrValue diff --git a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs index 6c3287d3dcc..18cff0490e9 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/QuickInfoProviderTests.fs @@ -226,7 +226,7 @@ let res8 = abs 5.0 [ mkDesc "GroupBy" - "(extension) System.Collections.Generic.IEnumerable.GroupBy<'TSource,'TKey>(keySelector: System.Func<'TSource,'TKey>) : System.Collections.Generic.IEnumerable> + "(extension) System.Collections.Generic.IEnumerable.GroupBy<'TSource,'TKey>(keySelector: System.Func<'TSource,'TKey>) : IGrouping<'TKey,'TSource> seq 'TSource is int * string 'TKey is int" mkDesc diff --git a/vsintegration/tests/FSharp.Editor.Tests/Refactors/AddReturnTypeTests.fs b/vsintegration/tests/FSharp.Editor.Tests/Refactors/AddReturnTypeTests.fs index 839fe5b963d..b5d6ef23e41 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/Refactors/AddReturnTypeTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/Refactors/AddReturnTypeTests.fs @@ -298,7 +298,7 @@ let ``Binding linq function doesnt crash`` () = let code = $""" let skip1 elements = - System.Linq.Enumerable.Skip(elements, 1) + System.Linq.Enumerable.Skip(elements, 1).GetEnumerator() """ use context = TestContext.CreateWithCode code @@ -309,8 +309,8 @@ let skip1 elements = let expectedCode = $""" -let skip1 elements : System.Collections.Generic.IEnumerable<'a> = - System.Linq.Enumerable.Skip(elements, 1) +let skip1 elements : System.Collections.Generic.IEnumerator<'a> = + System.Linq.Enumerable.Skip(elements, 1).GetEnumerator() """ let resultText = newDoc.GetTextAsync() |> GetTaskResult @@ -325,7 +325,7 @@ let ``Handle already existing opens on Linq`` () = open System let skip1 elements = - Linq.Enumerable.Skip(elements, 1) + Linq.Enumerable.Skip(elements, 1).GetEnumerator() """ use context = TestContext.CreateWithCode code @@ -338,8 +338,8 @@ let skip1 elements = $""" open System -let skip1 elements : Collections.Generic.IEnumerable<'a> = - Linq.Enumerable.Skip(elements, 1) +let skip1 elements : Collections.Generic.IEnumerator<'a> = + Linq.Enumerable.Skip(elements, 1).GetEnumerator() """ let resultText = newDoc.GetTextAsync() |> GetTaskResult @@ -355,7 +355,7 @@ open System open System.Linq let skip1 elements = - Enumerable.Skip(elements, 1) + Enumerable.Skip(elements, 1).GetEnumerator() """ use context = TestContext.CreateWithCode code @@ -369,8 +369,37 @@ let skip1 elements = open System open System.Linq -let skip1 elements : Collections.Generic.IEnumerable<'a> = - Enumerable.Skip(elements, 1) +let skip1 elements : Collections.Generic.IEnumerator<'a> = + Enumerable.Skip(elements, 1).GetEnumerator() + """ + + let resultText = newDoc.GetTextAsync() |> GetTaskResult + Assert.Equal(expectedCode, resultText.ToString()) + +[] +let ``Handle seq`` () = + let symbolName = "skip1" + + let code = + $""" +open System + +let skip1 elements = + Linq.Enumerable.Skip(elements, 1) + """ + + use context = TestContext.CreateWithCode code + + let spanStart = code.IndexOf symbolName + + let newDoc = tryRefactor code spanStart context (new AddReturnType()) + + let expectedCode = + $""" +open System + +let skip1 elements : 'a seq = + Linq.Enumerable.Skip(elements, 1) """ let resultText = newDoc.GetTextAsync() |> GetTaskResult From 5c0ac65f7a0d55d62335b943f1844a5d30ea024b Mon Sep 17 00:00:00 2001 From: "Alexey.Berezhnykh" Date: Tue, 16 Sep 2025 01:14:43 +0300 Subject: [PATCH 5/5] readme --- docs/release-notes/.FSharp.Compiler.Service/10.0.100.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md index 06b39cc019d..9c7216891de 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/10.0.100.md @@ -29,6 +29,7 @@ * Use `errorR` instead of `error` in `CheckDeclarations.fs` when possible. ([PR #18645](https://github.com/dotnet/fsharp/pull/18645)) * Parser: Capture named fields block separators. ([PR #18857](https://github.com/dotnet/fsharp/pull/18857)) * Type checker: use inner expr range in upcast constraints errors ([PR #18850](https://github.com/dotnet/fsharp/pull/18850)) +* Import `IEnumerable` as `seq`. ([PR #18865](https://github.com/dotnet/fsharp/pull/18865)) ### Breaking Changes