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 }