From 67b09db0a00ad3c42e556111c5f37321e9c30c23 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 3 Nov 2022 12:22:50 +0100 Subject: [PATCH 1/6] Using obsolete record field makes whole record construction expression obsolete (#14211) * Use more accurate range to only report obsolete field as a obsolete rather that making the whole record construction expression obsolete * Use record expression range for CheckRecdFieldAccessible --- src/Compiler/Checking/CheckExpressions.fs | 6 +++--- .../Language/ObsoleteAttributeCheckingTests.fs | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 02cdd360e86..7d9cff68b37 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -1822,19 +1822,19 @@ let BuildFieldMap (cenv: cenv) env isPartial ty (flds: ((Ident list * Ident) * ' rfinfo1.TypeInst, rfinfo1.TyconRef let fldsmap, rfldsList = - ((Map.empty, []), fldResolutions) ||> List.fold (fun (fs, rfldsList) (fld, frefs, fldExpr) -> + ((Map.empty, []), fldResolutions) ||> List.fold (fun (fs, rfldsList) ((_, ident), frefs, fldExpr) -> match frefs |> List.filter (fun (FieldResolution(rfinfo2, _)) -> tyconRefEq g tcref rfinfo2.TyconRef) with | [FieldResolution(rfinfo2, showDeprecated)] -> // Record the precise resolution of the field for intellisense let item = Item.RecdField(rfinfo2) - CallNameResolutionSink cenv.tcSink ((snd fld).idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, ad) + CallNameResolutionSink cenv.tcSink (ident.idRange, env.NameEnv, item, emptyTyparInst, ItemOccurence.Use, ad) let fref2 = rfinfo2.RecdFieldRef CheckRecdFieldAccessible cenv.amap m env.eAccessRights fref2 |> ignore - CheckFSharpAttributes g fref2.PropertyAttribs m |> CommitOperationResult + CheckFSharpAttributes g fref2.PropertyAttribs ident.idRange |> CommitOperationResult if Map.containsKey fref2.FieldName fs then errorR (Error(FSComp.SR.tcFieldAppearsTwiceInRecord(fref2.FieldName), m)) diff --git a/tests/FSharp.Compiler.ComponentTests/Language/ObsoleteAttributeCheckingTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/ObsoleteAttributeCheckingTests.fs index 20e785370a3..8ccbe23126c 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/ObsoleteAttributeCheckingTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/ObsoleteAttributeCheckingTests.fs @@ -165,7 +165,7 @@ let c = { X = 0 } |> compile |> shouldFail |> withDiagnostics [ - (Error 101, Line 7, Col 9, Line 7, Col 18, "This construct is deprecated. Use B instead") + (Error 101, Line 7, Col 11, Line 7, Col 12, "This construct is deprecated. Use B instead") ] [] @@ -373,7 +373,7 @@ type ButtonExtensions = |> compile |> shouldFail |> withDiagnostics [ - (Error 101, Line 13, Col 9, Line 13, Col 34, "This construct is deprecated. Use B instead") + (Error 101, Line 13, Col 21, Line 13, Col 25, "This construct is deprecated. Use B instead") ] [] @@ -397,7 +397,7 @@ type ButtonExtensions = |> shouldFail |> withDiagnostics [ (Error 101, Line 12, Col 37, Line 12, Col 43, "This construct is deprecated. Use B instead"); - (Error 101, Line 13, Col 9, Line 13, Col 34, "This construct is deprecated. Use B instead") + (Error 101, Line 13, Col 21, Line 13, Col 25, "This construct is deprecated. Use B instead") ] [] @@ -416,7 +416,7 @@ module Button = |> compile |> shouldFail |> withDiagnostics [ - (Error 101, Line 9, Col 20, Line 9, Col 36, "This construct is deprecated. Use B instead") + (Error 101, Line 9, Col 22, Line 9, Col 26, "This construct is deprecated. Use B instead") ] @@ -437,7 +437,7 @@ module Button = |> compile |> shouldFail |> withDiagnostics [ - (Error 101, Line 10, Col 20, Line 10, Col 36, "This construct is deprecated. Use B instead") + (Error 101, Line 10, Col 22, Line 10, Col 26, "This construct is deprecated. Use B instead") ] [] @@ -462,7 +462,7 @@ type ButtonExtensions = |> compile |> shouldFail |> withDiagnostics [ - (Error 101, Line 9, Col 20, Line 9, Col 36, "This construct is deprecated. Use B instead") + (Error 101, Line 9, Col 22, Line 9, Col 26, "This construct is deprecated. Use B instead") ] [] @@ -607,9 +607,10 @@ let a = { DeprecatedField= "23" ; JustField = "" } |> compile |> shouldFail |> withDiagnostics [ - (Error 101, Line 4, Col 9, Line 4, Col 51, "This construct is deprecated. Deprecated Field") + (Error 101, Line 4, Col 11, Line 4, Col 26, "This construct is deprecated. Deprecated Field") ] + // This should only report one warning but instead show two. Related issue https://github.com/dotnet/fsharp/issues/14203 [] let ``Obsolete attribute warning is taken into account when used in one the record properties`` () = Fsx """ @@ -620,6 +621,7 @@ let a = { DeprecatedField= "23" ; JustField = "" } |> compile |> shouldFail |> withDiagnostics [ + (Warning 44, Line 4, Col 11, Line 4, Col 26, "This construct is deprecated. Deprecated Field") (Warning 44, Line 4, Col 9, Line 4, Col 51, "This construct is deprecated. Deprecated Field") ] From 9d8259212f1d7aaa0dbd2725571613176f07a7d0 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 3 Nov 2022 12:23:09 +0100 Subject: [PATCH 2/6] Use more a more accurate range when reporing an error on delegate type declaration (#14208) --- src/Compiler/Checking/CheckDeclarations.fs | 5 ++++- .../Conformance/DelegateTypes/DelegateDefinition.fs | 11 +++++++---- .../DelegateTypes/invalid_delegate_definition.fs | 5 ----- .../invalid_delegate_definition.fs.err.bsl | 3 --- 4 files changed, 11 insertions(+), 13 deletions(-) delete mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/invalid_delegate_definition.fs delete mode 100644 tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/invalid_delegate_definition.fs.err.bsl diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index e9272db3bc0..1b85fada4c6 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -3161,7 +3161,10 @@ module EstablishTypeDefinitionCores = | None -> () | Some spats -> let ctorArgNames, _ = TcSimplePatsOfUnknownType cenv true CheckCxs envinner tpenv spats - if not ctorArgNames.IsEmpty then errorR (Error(FSComp.SR.parsOnlyClassCanTakeValueArguments(), m)) + if not ctorArgNames.IsEmpty then + match spats with + | SynSimplePats.SimplePats(_, m) -> errorR (Error(FSComp.SR.parsOnlyClassCanTakeValueArguments(), m)) + | SynSimplePats.Typed(_, _, m) -> errorR (Error(FSComp.SR.parsOnlyClassCanTakeValueArguments(), m)) let envinner = AddDeclaredTypars CheckForDuplicateTypars (tycon.Typars m) envinner let envinner = MakeInnerEnvForTyconRef envinner thisTyconRef false diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/DelegateDefinition.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/DelegateDefinition.fs index df0857b3f28..3e01c1723fd 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/DelegateDefinition.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/DelegateDefinition.fs @@ -17,8 +17,9 @@ namespace FSharpTest """ |> compile |> shouldFail - |> withErrorCode 552 - |> withErrorMessage "Only class types may take value arguments" + |> withDiagnostics [ + (Error 552, Line 3, Col 11, Line 3, Col 19, "Only class types may take value arguments") + ] [] let ``Delegate definition with primary constructor no argument.`` () = @@ -30,8 +31,10 @@ namespace FSharpTest """ |> compile |> shouldFail - |> withErrorCode 552 - |> withErrorMessage "Only class types may take value arguments" + |> shouldFail + |> withDiagnostics [ + (Error 552, Line 3, Col 11, Line 3, Col 13, "Only class types may take value arguments") + ] [] let ``Delegate definition`` () = diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/invalid_delegate_definition.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/invalid_delegate_definition.fs deleted file mode 100644 index dd55759ff61..00000000000 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/invalid_delegate_definition.fs +++ /dev/null @@ -1,5 +0,0 @@ -type T(x: int) = - delegate of int -> int - -type T() = - delegate of int -> int \ No newline at end of file diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/invalid_delegate_definition.fs.err.bsl b/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/invalid_delegate_definition.fs.err.bsl deleted file mode 100644 index c804d430211..00000000000 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/DelegateTypes/invalid_delegate_definition.fs.err.bsl +++ /dev/null @@ -1,3 +0,0 @@ -invalid_delegate_definition.fs (1,6)-(1,15) Only class types may take value arguments -invalid_delegate_definition.fs (4,6)-(1,9) Only class types may take value arguments - From d9fed819b16d089ad1452dabdac7e18a35ec5c1b Mon Sep 17 00:00:00 2001 From: kerams Date: Thu, 3 Nov 2022 12:32:45 +0100 Subject: [PATCH 3/6] Disallow explicit type parameters on getset properties (#14212) --- src/Compiler/Checking/CheckExpressions.fs | 3 ++- tests/fsharp/typecheck/sigs/neg32.bsl | 18 ++++++++++++------ tests/fsharp/typecheck/sigs/neg32.fs | 3 +++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 7d9cff68b37..60a7d9b364e 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -2169,7 +2169,8 @@ module GeneralizationHelpers = match memberFlags.MemberKind with // can't infer extra polymorphism for properties | SynMemberKind.PropertyGet - | SynMemberKind.PropertySet -> + | SynMemberKind.PropertySet + | SynMemberKind.PropertyGetSet -> if not (isNil declaredTypars) then errorR(Error(FSComp.SR.tcPropertyRequiresExplicitTypeParameters(), m)) | SynMemberKind.Constructor -> diff --git a/tests/fsharp/typecheck/sigs/neg32.bsl b/tests/fsharp/typecheck/sigs/neg32.bsl index be532860a7a..09e6050c790 100644 --- a/tests/fsharp/typecheck/sigs/neg32.bsl +++ b/tests/fsharp/typecheck/sigs/neg32.bsl @@ -37,14 +37,20 @@ neg32.fs(55,18,55,20): typecheck error FS0039: The type parameter 'T is not defi neg32.fs(56,18,56,20): typecheck error FS0039: The type parameter 'T is not defined. -neg32.fs(59,10,59,12): typecheck error FS0039: The type parameter 'T is not defined. +neg32.fs(57,4,57,38): typecheck error FS0671: A property cannot have explicit type parameters. Consider using a method instead. -neg32.fs(59,10,59,12): typecheck error FS0039: The type parameter 'T is not defined. +neg32.fs(58,4,58,33): typecheck error FS0671: A property cannot have explicit type parameters. Consider using a method instead. -neg32.fs(62,11,62,13): typecheck error FS0039: The type parameter 'T is not defined. +neg32.fs(59,4,59,33): typecheck error FS0671: A property cannot have explicit type parameters. Consider using a method instead. -neg32.fs(62,11,62,13): typecheck error FS0039: The type parameter 'T is not defined. +neg32.fs(62,10,62,12): typecheck error FS0039: The type parameter 'T is not defined. -neg32.fs(66,65,66,86): typecheck error FS0033: The non-generic type 'System.EventArgs' does not expect any type arguments, but here is given 1 type argument(s) +neg32.fs(62,10,62,12): typecheck error FS0039: The type parameter 'T is not defined. -neg32.fs(66,21,66,27): typecheck error FS1091: The event 'Event1' has a non-standard type. If this event is declared in another CLI language, you may need to access this event using the explicit add_Event1 and remove_Event1 methods for the event. If this event is declared in F#, make the type of the event an instantiation of either 'IDelegateEvent<_>' or 'IEvent<_,_>'. +neg32.fs(65,11,65,13): typecheck error FS0039: The type parameter 'T is not defined. + +neg32.fs(65,11,65,13): typecheck error FS0039: The type parameter 'T is not defined. + +neg32.fs(69,65,69,86): typecheck error FS0033: The non-generic type 'System.EventArgs' does not expect any type arguments, but here is given 1 type argument(s) + +neg32.fs(69,21,69,27): typecheck error FS1091: The event 'Event1' has a non-standard type. If this event is declared in another CLI language, you may need to access this event using the explicit add_Event1 and remove_Event1 methods for the event. If this event is declared in F#, make the type of the event an instantiation of either 'IDelegateEvent<_>' or 'IEvent<_,_>'. diff --git a/tests/fsharp/typecheck/sigs/neg32.fs b/tests/fsharp/typecheck/sigs/neg32.fs index a8893b86bbe..915336d7eb5 100644 --- a/tests/fsharp/typecheck/sigs/neg32.fs +++ b/tests/fsharp/typecheck/sigs/neg32.fs @@ -54,6 +54,9 @@ type NegativeClass() = abstract M2 : 'T with get, set abstract M3 : 'T with set abstract M4 : 'T with get + abstract M5<'T> : 'T with get, set + abstract M6<'T> : 'T with set + abstract M7<'T> : 'T with get type NegativeRecord = { v : 'T } From eaefd0204f07cb7b89ced204e3d1ceaa3f76b940 Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 3 Nov 2022 15:38:42 +0100 Subject: [PATCH 4/6] Revert "Activate Server mode for .NET Core compiler (#13740)" (#14223) --- FSharp.Profiles.props | 4 ---- 1 file changed, 4 deletions(-) diff --git a/FSharp.Profiles.props b/FSharp.Profiles.props index 66e46a32b08..4222cace487 100644 --- a/FSharp.Profiles.props +++ b/FSharp.Profiles.props @@ -14,8 +14,4 @@ - - - true - From 4ffb1ccb2ac6308421a581877efcb0f4795bc71c Mon Sep 17 00:00:00 2001 From: Vlad Zarytovskii Date: Thu, 3 Nov 2022 16:24:12 +0100 Subject: [PATCH 5/6] Update add_to_project.yml --- .github/workflows/add_to_project.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/add_to_project.yml b/.github/workflows/add_to_project.yml index a4625caa71d..01b509e1584 100644 --- a/.github/workflows/add_to_project.yml +++ b/.github/workflows/add_to_project.yml @@ -5,9 +5,10 @@ on: types: - opened - transferred - pull_request: + pull_request_target: types: - opened + branches: ['main'] permissions: issues: write @@ -16,7 +17,7 @@ permissions: jobs: cleanup_old_runs: runs-on: ubuntu-20.04 - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request_target' permissions: actions: write env: @@ -42,7 +43,7 @@ jobs: github-token: ${{ secrets.REPO_PROJECT_PAT }} apply-label: runs-on: ubuntu-latest - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request_target' steps: - uses: actions/github-script@v6 with: @@ -55,7 +56,7 @@ jobs: }) apply-milestone: runs-on: ubuntu-latest - if: github.event_name != 'pull_request' + if: github.event_name != 'pull_request_target' steps: - uses: actions/github-script@v6 with: From df393d08fef239cf9644e8af1fb57d4ae517f7ad Mon Sep 17 00:00:00 2001 From: Florian Verdonck Date: Thu, 3 Nov 2022 16:54:17 +0100 Subject: [PATCH 6/6] Allow extension methods without type attribute. (#13839) * Allow extension methods without type attribute. * Add Extension attribute to type if any member has the attribute. * Handle C# extensions as let bindings in a module. * Add test for recursive types. * Add tests for types in recursive modules. * Only add attribute to module when module declaration has attribute. * Add additional test. * Add CSharp Extension Attribute Not Required as a Preview Language Feature * Passing propper Fsharp.Core version to executed tests in legacy FsharpSuite * Fix for tests that use NetFramework types (like winforms brushes etc.) Co-authored-by: Edgar Gonzalez Co-authored-by: Tomas Grosup Co-authored-by: Vlad Zarytovskii --- src/Compiler/Checking/CheckDeclarations.fs | 83 +++ src/Compiler/Checking/NameResolution.fs | 71 +- src/Compiler/FSComp.txt | 1 + src/Compiler/Facilities/LanguageFeatures.fs | 4 + src/Compiler/Facilities/LanguageFeatures.fsi | 1 + src/Compiler/TypedTree/TypedTreeOps.fs | 20 +- src/Compiler/TypedTree/TypedTreeOps.fsi | 4 + src/Compiler/xlf/FSComp.txt.cs.xlf | 5 + src/Compiler/xlf/FSComp.txt.de.xlf | 5 + src/Compiler/xlf/FSComp.txt.es.xlf | 5 + src/Compiler/xlf/FSComp.txt.fr.xlf | 5 + src/Compiler/xlf/FSComp.txt.it.xlf | 5 + src/Compiler/xlf/FSComp.txt.ja.xlf | 5 + src/Compiler/xlf/FSComp.txt.ko.xlf | 5 + src/Compiler/xlf/FSComp.txt.pl.xlf | 5 + src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 + src/Compiler/xlf/FSComp.txt.ru.xlf | 5 + src/Compiler/xlf/FSComp.txt.tr.xlf | 5 + src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 + src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 + .../FSharp.Compiler.ComponentTests.fsproj | 1 + .../Language/ExtensionMethodTests.fs | 611 ++++++++++++++++++ tests/fsharp/single-test.fs | 2 +- 23 files changed, 853 insertions(+), 10 deletions(-) create mode 100644 tests/FSharp.Compiler.ComponentTests/Language/ExtensionMethodTests.fs diff --git a/src/Compiler/Checking/CheckDeclarations.fs b/src/Compiler/Checking/CheckDeclarations.fs index 1b85fada4c6..07573e3f7f3 100644 --- a/src/Compiler/Checking/CheckDeclarations.fs +++ b/src/Compiler/Checking/CheckDeclarations.fs @@ -969,6 +969,20 @@ module MutRecBindingChecking = | rest -> rest let prelimRecValues = [ for x in defnAs do match x with Phase2AMember bind -> yield bind.RecBindingInfo.Val | _ -> () ] + + let tyconOpt = + if cenv.g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) then + tyconOpt + |> Option.map (fun tycon -> + tryAddExtensionAttributeIfNotAlreadyPresent + (fun tryFindExtensionAttribute -> + tycon.MembersOfFSharpTyconSorted + |> Seq.tryPick (fun m -> tryFindExtensionAttribute m.Attribs) + ) + tycon + ) + else + tyconOpt let defnAs = MutRecShape.Tycon(TyconBindingsPhase2A(tyconOpt, declKind, prelimRecValues, tcref, copyOfTyconTypars, thisTy, defnAs)) defnAs, (tpenv, recBindIdx, uncheckedBindsRev)) @@ -4228,6 +4242,50 @@ module TcDeclarations = // Check the members and decide on representations for types with implicit constructors. let withBindings, envFinal = TcMutRecDefns_Phase2 cenv envInitial m scopem mutRecNSInfo envMutRecPrelimWithReprs withEnvs isMutRec + let withBindings = + if cenv.g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) then + // If any of the types has a member with the System.Runtime.CompilerServices.ExtensionAttribute, + // or a recursive module has a binding with the System.Runtime.CompilerServices.ExtensionAttribute, + // that type/recursive module should also received the ExtensionAttribute if it is not yet present. + // Example: + // open System.Runtime.CompilerServices + // + // type Int32Extensions = + // [] + // static member PlusOne (a:int) : int = a + 1 + // + // or + // + // module rec Foo + // + // [] + // let PlusOne (a:int) = a + 1 + withBindings + |> List.map (function + | MutRecShape.Tycon (Some tycon, bindings) -> + let tycon = + tryAddExtensionAttributeIfNotAlreadyPresent + (fun tryFindExtensionAttribute -> + tycon.MembersOfFSharpTyconSorted + |> Seq.tryPick (fun m -> tryFindExtensionAttribute m.Attribs) + ) + tycon + MutRecShape.Tycon (Some tycon, bindings) + | MutRecShape.Module ((MutRecDefnsPhase2DataForModule(moduleOrNamespaceType, entity), env), shapes) -> + let entity = + tryAddExtensionAttributeIfNotAlreadyPresent + (fun tryFindExtensionAttribute -> + moduleOrNamespaceType.Value.AllValsAndMembers + |> Seq.filter(fun v -> v.IsModuleBinding) + |> Seq.tryPick (fun v -> tryFindExtensionAttribute v.Attribs) + ) + entity + + MutRecShape.Module ((MutRecDefnsPhase2DataForModule(moduleOrNamespaceType, entity), env), shapes) + | shape -> shape) + else + withBindings + // Generate the hash/compare/equality bindings for all tycons. // // Note: generating these bindings must come after generating the members, since some in the case of structs some fields @@ -4766,6 +4824,31 @@ let rec TcModuleOrNamespaceElementNonMutRec (cenv: cenv) parent typeNames scopem // Get the inferred type of the decls and record it in the modul. moduleEntity.entity_modul_type <- MaybeLazy.Strict moduleTyAcc.Value + + let moduleEntity = + if cenv.g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) then + // If any of the let bindings inside the module has the System.Runtime.CompilerServices.ExtensionAttribute, + // that module should also received the ExtensionAttribute if it is not yet present. + // Example: + // module Foo + // + //[] + //let PlusOne (a:int) = a + 1 + tryAddExtensionAttributeIfNotAlreadyPresent + (fun tryFindExtensionAttribute -> + match moduleContents with + | ModuleOrNamespaceContents.TMDefs(defs) -> + defs + |> Seq.tryPick (function + | ModuleOrNamespaceContents.TMDefLet (Binding.TBind(var = v),_) -> + tryFindExtensionAttribute v.Attribs + | _ -> None) + | _ -> None + ) + moduleEntity + else + moduleEntity + let moduleDef = TMDefRec(false, [], [], [ModuleOrNamespaceBinding.Module(moduleEntity, moduleContents)], m) PublishModuleDefn cenv env moduleEntity diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 13b0c7c3c14..ad78b5ca5c5 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -530,14 +530,18 @@ let IsMethInfoPlainCSharpStyleExtensionMember g m isEnclExtTy (minfo: MethInfo) /// Get the info for all the .NET-style extension members listed as static members in the type. let private GetCSharpStyleIndexedExtensionMembersForTyconRef (amap: Import.ImportMap) m (tcrefOfStaticClass: TyconRef) = let g = amap.g - - if IsTyconRefUsedForCSharpStyleExtensionMembers g m tcrefOfStaticClass then - let pri = NextExtensionMethodPriority() + + if g.langVersion.SupportsFeature(LanguageFeature.CSharpExtensionAttributeNotRequired) then let ty = generalizedTyconRef g tcrefOfStaticClass + + let minfos = + GetImmediateIntrinsicMethInfosOfType (None, AccessorDomain.AccessibleFromSomeFSharpCode) g amap m ty + |> List.filter (IsMethInfoPlainCSharpStyleExtensionMember g m true) - let minfos = GetImmediateIntrinsicMethInfosOfType (None, AccessorDomain.AccessibleFromSomeFSharpCode) g amap m ty - [ for minfo in minfos do - if IsMethInfoPlainCSharpStyleExtensionMember g m true minfo then + if IsTyconRefUsedForCSharpStyleExtensionMembers g m tcrefOfStaticClass || not minfos.IsEmpty then + let pri = NextExtensionMethodPriority() + + [ for minfo in minfos do let ilExtMem = ILExtMem (tcrefOfStaticClass, minfo, pri) // The results are indexed by the TyconRef of the first 'this' argument, if any. @@ -584,9 +588,60 @@ let private GetCSharpStyleIndexedExtensionMembersForTyconRef (amap: Import.Impor | None -> () | Some (Some tcref) -> yield Choice1Of2(tcref, ilExtMem) | Some None -> yield Choice2Of2 ilExtMem ] + else + [] else - [] - + if IsTyconRefUsedForCSharpStyleExtensionMembers g m tcrefOfStaticClass then + let pri = NextExtensionMethodPriority() + let ty = generalizedTyconRef g tcrefOfStaticClass + let minfos = GetImmediateIntrinsicMethInfosOfType (None, AccessorDomain.AccessibleFromSomeFSharpCode) g amap m ty + + [ for minfo in minfos do + if IsMethInfoPlainCSharpStyleExtensionMember g m true minfo then + let ilExtMem = ILExtMem (tcrefOfStaticClass, minfo, pri) + // The results are indexed by the TyconRef of the first 'this' argument, if any. + // So we need to go and crack the type of the 'this' argument. + // + // This is convoluted because we only need the ILTypeRef of the first argument, and we don't + // want to read any other metadata as it can trigger missing-assembly errors. It turns out ImportILTypeRef + // is less eager in reading metadata than GetParamTypes. + // + // We don't use the index for the IL extension method for tuple of F# function types (e.g. if extension + // methods for tuple occur in C# code) + let thisTyconRef = + try + let rs = + match metadataOfTycon tcrefOfStaticClass.Deref, minfo with + | ILTypeMetadata (TILObjectReprData(scoref, _, _)), ILMeth(_, ILMethInfo(_, _, _, ilMethod, _), _) -> + match ilMethod.ParameterTypes with + | firstTy :: _ -> + match firstTy with + | ILType.Boxed tspec | ILType.Value tspec -> + let tref = (tspec |> rescopeILTypeSpec scoref).TypeRef + if Import.CanImportILTypeRef amap m tref then + let tcref = tref |> Import.ImportILTypeRef amap m + if isCompiledTupleTyconRef g tcref || tyconRefEq g tcref g.fastFunc_tcr then None + else Some tcref + else None + | _ -> None + | _ -> None + | _ -> + // The results are indexed by the TyconRef of the first 'this' argument, if any. + // So we need to go and crack the type of the 'this' argument. + let thisTy = minfo.GetParamTypes(amap, m, generalizeTypars minfo.FormalMethodTypars).Head.Head + match thisTy with + | AppTy g (tcrefOfTypeExtended, _) when not (isByrefTy g thisTy) -> Some tcrefOfTypeExtended + | _ -> None + Some rs + with e -> // Import of the ILType may fail, if so report the error and skip on + errorRecovery e m + None + match thisTyconRef with + | None -> () + | Some (Some tcref) -> yield Choice1Of2(tcref, ilExtMem) + | Some None -> yield Choice2Of2 ilExtMem ] + else + [] /// Query the declared properties of a type (including inherited properties) let IntrinsicPropInfosOfTypeInScope (infoReader: InfoReader) optFilter ad findFlag m ty = diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 7e460abeb92..9c9da192081 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1560,6 +1560,7 @@ featureRequiredProperties,"support for required properties" featureInitProperties,"support for consuming init properties" featureLowercaseDUWhenRequireQualifiedAccess,"Allow lowercase DU when RequireQualifiedAccess attribute" featureMatchNotAllowedForUnionCaseWithNoData,"Pattern match discard is not allowed for union case that takes no data." +featureCSharpExtensionAttributeNotRequired,"Allow implicit Extension attribute on declaring types, modules" 3353,fsiInvalidDirective,"Invalid directive '#%s %s'" 3354,tcNotAFunctionButIndexerNamedIndexingNotYetEnabled,"This value supports indexing, e.g. '%s.[index]'. The syntax '%s[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." 3354,tcNotAFunctionButIndexerIndexingNotYetEnabled,"This expression supports indexing, e.g. 'expr.[index]'. The syntax 'expr[index]' requires /langversion:preview. See https://aka.ms/fsharp-index-notation." diff --git a/src/Compiler/Facilities/LanguageFeatures.fs b/src/Compiler/Facilities/LanguageFeatures.fs index 8644fbe9971..00ddd74b6c5 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fs +++ b/src/Compiler/Facilities/LanguageFeatures.fs @@ -55,6 +55,7 @@ type LanguageFeature = | InterfacesWithAbstractStaticMembers | SelfTypeConstraints | MatchNotAllowedForUnionCaseWithNoData + | CSharpExtensionAttributeNotRequired /// LanguageVersion management type LanguageVersion(versionText) = @@ -126,6 +127,8 @@ type LanguageVersion(versionText) = // F# preview LanguageFeature.FromEndSlicing, previewVersion LanguageFeature.MatchNotAllowedForUnionCaseWithNoData, previewVersion + LanguageFeature.CSharpExtensionAttributeNotRequired, previewVersion + ] static let defaultLanguageVersion = LanguageVersion("default") @@ -233,6 +236,7 @@ type LanguageVersion(versionText) = | LanguageFeature.InterfacesWithAbstractStaticMembers -> FSComp.SR.featureInterfacesWithAbstractStaticMembers () | LanguageFeature.SelfTypeConstraints -> FSComp.SR.featureSelfTypeConstraints () | LanguageFeature.MatchNotAllowedForUnionCaseWithNoData -> FSComp.SR.featureMatchNotAllowedForUnionCaseWithNoData () + | LanguageFeature.CSharpExtensionAttributeNotRequired -> FSComp.SR.featureCSharpExtensionAttributeNotRequired () /// Get a version string associated with the given feature. static member GetFeatureVersionString feature = diff --git a/src/Compiler/Facilities/LanguageFeatures.fsi b/src/Compiler/Facilities/LanguageFeatures.fsi index 694a6ae73fd..1b3d68e8f2a 100644 --- a/src/Compiler/Facilities/LanguageFeatures.fsi +++ b/src/Compiler/Facilities/LanguageFeatures.fsi @@ -45,6 +45,7 @@ type LanguageFeature = | InterfacesWithAbstractStaticMembers | SelfTypeConstraints | MatchNotAllowedForUnionCaseWithNoData + | CSharpExtensionAttributeNotRequired /// LanguageVersion management type LanguageVersion = diff --git a/src/Compiler/TypedTree/TypedTreeOps.fs b/src/Compiler/TypedTree/TypedTreeOps.fs index f57954f7bf5..6fa70a5903e 100644 --- a/src/Compiler/TypedTree/TypedTreeOps.fs +++ b/src/Compiler/TypedTree/TypedTreeOps.fs @@ -10410,4 +10410,22 @@ let (|EmptyModuleOrNamespaces|_|) (moduleOrNamespaceContents: ModuleOrNamespaceC Some emptyModuleOrNamespaces else None - | _ -> None \ No newline at end of file + | _ -> None + +let tryAddExtensionAttributeIfNotAlreadyPresent + (tryFindExtensionAttributeIn: (Attrib list -> Attrib option) -> Attrib option) + (entity: Entity) + : Entity + = + let tryFindExtensionAttribute (attribs: Attrib list): Attrib option = + List.tryFind + (fun (a: Attrib) -> + a.TyconRef.CompiledRepresentationForNamedType.BasicQualifiedName = "System.Runtime.CompilerServices.ExtensionAttribute") + attribs + + if Option.isSome (tryFindExtensionAttribute entity.Attribs) then + entity + else + match tryFindExtensionAttributeIn tryFindExtensionAttribute with + | None -> entity + | Some extensionAttrib -> { entity with entity_attribs = extensionAttrib :: entity.Attribs } diff --git a/src/Compiler/TypedTree/TypedTreeOps.fsi b/src/Compiler/TypedTree/TypedTreeOps.fsi index 00d838e04d4..ae58019683a 100755 --- a/src/Compiler/TypedTree/TypedTreeOps.fsi +++ b/src/Compiler/TypedTree/TypedTreeOps.fsi @@ -2687,3 +2687,7 @@ type TraitConstraintInfo with /// This will match anything that does not have any types or bindings. val (|EmptyModuleOrNamespaces|_|): moduleOrNamespaceContents: ModuleOrNamespaceContents -> (ModuleOrNamespace list) option + +/// Add an System.Runtime.CompilerServices.ExtensionAttribute to the Entity if found via predicate and not already present. +val tryAddExtensionAttributeIfNotAlreadyPresent: + tryFindExtensionAttributeIn: ((Attrib list -> Attrib option) -> Attrib option) -> entity: Entity -> Entity diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 2d981896ab9..8c4a1300a7f 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -152,6 +152,11 @@ automatické generování vlastnosti Message pro deklarace exception + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption využití člena výchozího rozhraní diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 7b2e86ccd81..688592092e3 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -152,6 +152,11 @@ Automatische Generierung der Eigenschaft „Message“ für „exception“-Deklarationen + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption standardmäßige Schnittstellenmembernutzung diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 8aad3d6f781..a06daa1f7f1 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -152,6 +152,11 @@ generación automática de la propiedad 'Message' para declaraciones 'exception' + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption consumo de miembros de interfaz predeterminados diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index ffc5e832198..f8a89904bfa 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -152,6 +152,11 @@ génération automatique de la propriété « Message » pour les déclarations « exception » + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption consommation par défaut des membres d'interface diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 29240e0d98c..37d11b74636 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -152,6 +152,11 @@ generazione automatica della proprietà 'Messaggio' per le dichiarazioni 'eccezione' + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption utilizzo predefinito dei membri di interfaccia diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index c509bc1328e..c42700c1d18 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -152,6 +152,11 @@ `exception` 宣言の `Message` プロパティの自動生成 + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption 既定のインターフェイス メンバーの消費 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 9767ff521a6..39e9a33001e 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -152,6 +152,11 @@ 'exception' 선언에 대한 'Message' 속성 자동 생성 + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption 기본 인터페이스 멤버 사용 diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 4a565f29eaf..e3a61d7dd58 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -152,6 +152,11 @@ Automatyczne generowanie właściwości „Wiadomość“ dla deklaracji „Wyjątek“ + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption domyślne użycie składowej interfejsu diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 140f330414d..78742938012 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -152,6 +152,11 @@ geração automática da propriedade 'Message' para declarações de 'exception' + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption consumo de membro da interface padrão diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 5c8c1ad44e7..837271832bf 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -152,6 +152,11 @@ автоматическое создание свойства “Message” для объявлений “exception” + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption использование элемента интерфейса по умолчанию diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index fb8d5406e68..59d04a3fe94 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -152,6 +152,11 @@ 'exception' bildirimleri için 'Message' özelliğinin otomatik olarak oluşturulması + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption varsayılan arabirim üyesi tüketimi diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index baf4e53d041..ebcafbf1852 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -152,6 +152,11 @@ 自动生成“异常”声明的“消息”属性 + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption 默认接口成员消耗 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 3711de1d214..0606b9b48c4 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -152,6 +152,11 @@ 自動產生 'exception' 宣告的 'Message' 屬性 + + Allow implicit Extension attribute on declaring types, modules + Allow implicit Extension attribute on declaring types, modules + + default interface member consumption 預設介面成員使用 diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 29cca6690d0..b6ea2254387 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -174,6 +174,7 @@ + diff --git a/tests/FSharp.Compiler.ComponentTests/Language/ExtensionMethodTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/ExtensionMethodTests.fs new file mode 100644 index 00000000000..1ad91f999c3 --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/Language/ExtensionMethodTests.fs @@ -0,0 +1,611 @@ +namespace FSharp.Compiler.ComponentTests.Language + +open FSharp.Test +open Xunit +open FSharp.Test.Compiler + +module ExtensionMethodTests = + + [] + let ``Extension method with toplevel attribute on type`` () = + Fsx + """ +open System.Runtime.CompilerServices + +[] +type Foo = + [] + static member PlusOne (a:int) : int = a + 1 + +let f (b:int) = b.PlusOne() + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``Extension method without toplevel attribute on type`` () = + Fsx + """ +open System.Runtime.CompilerServices + +type Foo = + [] + static member PlusOne (a:int) : int = a + 1 + +let f (b:int) = b.PlusOne() + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``Extension method without toplevel attribute on type lang version 7`` () = + Fsx + """ +open System.Runtime.CompilerServices + +type Foo = + [] + static member PlusOne (a:int) : int = a + 1 + +let f (b:int) = b.PlusOne() + """ + |> withLangVersion70 + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 39, Line 8, Col 19, Line 8, Col 26, "The type 'Int32' does not define the field, constructor or member 'PlusOne'.") + ] + + [] + let ``Extension method without toplevel attribute on recursive type`` () = + Fsx + """ +open System.Runtime.CompilerServices + +type Foo = + class + end +and Bar = + [] + static member PlusOne (a:int) : int = a + 1 + +let f (b:int) = b.PlusOne() + """ + |> withLangVersionPreview + |> compile + |> shouldSucceed + + [] + let ``F# CSharpStyleExtensionMethod consumed in C#`` () = + let fsharp = + FSharp + """ +module Hello + +open System.Runtime.CompilerServices + +type Foo = + [] + static member PlusOne (a:int) : int = a + 1 +""" + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp + """ +namespace Consumer +{ + using static Hello.Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } +} +""" + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``F# lang version 7 CSharpStyleExtensionMethod consumed in C#`` () = + let fsharp = + FSharp + """ +module Hello + +open System.Runtime.CompilerServices + +type Foo = + [] + static member PlusOne (a:int) : int = a + 1 +""" + |> withLangVersion70 + |> withName "FSLib" + + let csharp = + CSharp + """ +namespace Consumer +{ + using static Hello.Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } +} +""" + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 1061, Line 9, Col 25, Line 9, Col 32, "'int' does not contain a definition for 'PlusOne' and no accessible extension method 'PlusOne' accepting a first argument of type 'int' could be found (are you missing a using directive or an assembly reference?)") + ] + + [] + let ``F# CSharpStyleExtensionMethod in recursive type consumed in C#`` () = + let fsharp = + FSharp + """ +module Hello + +open System.Runtime.CompilerServices + +type Foo = + class + end +and Bar = + [] + static member PlusOne (a:int) : int = a + 1 +""" + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp + """ +namespace Consumer +{ + using static Hello.Bar; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } +} +""" + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``F# CSharpStyleExtensionMethod defined in top level module with attribute consumed in C#`` () = + let fsharp = + FSharp + """ +namespace Hello + +open System.Runtime.CompilerServices + +[] +module Foo = + [] + let PlusOne (a:int) : int = a + 1 +""" + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp + """ +namespace Consumer +{ + using static Hello.Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } +} +""" + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``F# CSharpStyleExtensionMethod defined in top level module without attribute consumed in C#`` () = + let fsharp = + FSharp + """ +namespace Hello + +open System.Runtime.CompilerServices + +module Foo = + [] + let PlusOne (a:int) : int = a + 1 +""" + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp + """ +namespace Consumer +{ + using static Hello.Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } +} +""" + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``Toplevel named module with Extension attribute and top level let binding with Extension attribute`` () = + let fsharp = + FSharp """ + [] + module Foo + + [] + let PlusOne (a:int) = a + 1 + """ + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } + } + """ + + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``Toplevel named module without Extension attribute and top level let binding with Extension attribute`` () = + let fsharp = + FSharp """ + module Foo + + [] + let PlusOne (a:int) = a + 1 + """ + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } + } + """ + + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``Recursive toplevel named module with Extension attribute and top level let binding with Extension attribute`` () = + let fsharp = + FSharp """ + [] + module rec Foo + + [] + let PlusOne (a:int) = a + 1 + """ + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } + } + """ + + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``Recursive toplevel named module without Extension attribute and top level let binding with Extension attribute`` () = + let fsharp = + FSharp """ + module rec Foo + + [] + let PlusOne (a:int) = a + 1 + """ + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } + } + """ + + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``Foobar `` () = + let fsharp = + FSharp """ +module rec Foo + +[] +type Bar = + [] + static member PlusOne (a:int) = a + 1 + """ + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo.Bar; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } + } + """ + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``Recursive named module with type with CSharp style extension can be consumed in CSharp`` () = + let fsharp = + FSharp """ +module rec Foo + +type Bar = + [] + static member PlusOne (a:int) = a + 1 + """ + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo.Bar; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } + } + """ + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``CSharp style extension method in F# type backed by a signature`` () = + let implementation = + SourceCodeFileKind.Create( + "Source.fs", + """ +module Foo + +open System.Runtime.CompilerServices + +type Bar = + [] + static member PlusOne (a:int) : int = a + 1 +""" + ) + + let fsharp = + Fsi """ +module Foo + +open System.Runtime.CompilerServices + +[] +type Bar = + [] + static member PlusOne: a: int -> int +""" + |> withLangVersionPreview + |> withAdditionalSourceFile implementation + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo.Bar; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } + } + """ + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``CSharp style extension method in F# type backed by a signature in a recursive module`` () = + let implementation = + SourceCodeFileKind.Create( + "Source.fs", + """ +module rec Foo + +open System.Runtime.CompilerServices + +type Bar = + [] + static member PlusOne (a:int) : int = a + 1 +""" + ) + + let fsharp = + Fsi """ +module rec Foo + +open System.Runtime.CompilerServices + +[] +type Bar = + [] + static member PlusOne: a: int -> int +""" + |> withLangVersionPreview + |> withAdditionalSourceFile implementation + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo.Bar; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne(); + } + } + } + """ + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed + + [] + let ``Multiple top level let binding with Extension attribute`` () = + let fsharp = + FSharp """ + module Foo + + [] + let PlusOne (a:int) = a + 1 + + [] + let MinusOne (a:int) = a - 1 + """ + |> withLangVersionPreview + |> withName "FSLib" + + let csharp = + CSharp """ + namespace Consumer + { + using static Foo; + + public class Class1 + { + public Class1() + { + var meh = 1.PlusOne().MinusOne(); + } + } + } + """ + + |> withName "CSLib" + |> withReferences [ fsharp ] + + csharp |> compile |> shouldSucceed diff --git a/tests/fsharp/single-test.fs b/tests/fsharp/single-test.fs index 3e1ab4390bc..6d7b401e003 100644 --- a/tests/fsharp/single-test.fs +++ b/tests/fsharp/single-test.fs @@ -392,7 +392,7 @@ let singleVersionedNegTest (cfg: TestConfig) version testname = let cfg = { cfg with - fsc_flags = sprintf "%s %s --preferreduilang:en-US --define:NEGATIVE" cfg.fsc_flags options + fsc_flags = sprintf """%s %s --preferreduilang:en-US --define:NEGATIVE --simpleresolution /r:"%s" """ cfg.fsc_flags options cfg.FSCOREDLLPATH fsi_flags = sprintf "%s --preferreduilang:en-US %s" cfg.fsi_flags options }