Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion src/fsharp/AttributeChecking.fs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,11 @@ let CheckFSharpAttributesForHidden g attribs =
let CheckFSharpAttributesForObsolete g attribs =
not (isNil attribs) && (HasFSharpAttribute g g.attrib_SystemObsolete attribs)


/// Indicate if a list of F# attributes contains 'RequireArgumentNameAttribute'. Used to error on call site if arguments aren't explicitly named.
let CheckFSharpAttributesForRequireNamedParametersAttribute g attribs =
not (isNil attribs) && (HasFSharpAttribute g g.attrib_RequireArgumentNameAttribute attribs)

/// Indicate if a list of F# attributes contains 'ObsoleteAttribute'. Used to suppress the item in intellisense.
/// Also check the attributes for CompilerMessageAttribute, which has an IsHidden argument that allows
/// items to be suppressed from intellisense.
Expand Down Expand Up @@ -390,7 +395,7 @@ let CheckILFieldAttributes g (finfo:ILFieldInfo) m =
#endif

/// Check the attributes associated with a method, returning warnings and errors as data.
let CheckMethInfoAttributes g m tyargsOpt minfo =
let CheckMethInfoAttributes g m tyargsOpt minfo hasUnammedCallerArgs =
let search =
BindMethInfoAttributes m minfo
(fun ilAttribs -> Some(CheckILAttributes g false ilAttribs m))
Expand All @@ -399,6 +404,8 @@ let CheckMethInfoAttributes g m tyargsOpt minfo =
CheckFSharpAttributes g fsAttribs m ++ (fun () ->
if Option.isNone tyargsOpt && HasFSharpAttribute g g.attrib_RequiresExplicitTypeArgumentsAttribute fsAttribs then
ErrorD(Error(FSComp.SR.tcFunctionRequiresExplicitTypeArguments(minfo.LogicalName), m))
elif Option.isSome hasUnammedCallerArgs && hasUnammedCallerArgs.Value && HasFSharpAttribute g g.attrib_RequireArgumentNameAttribute fsAttribs then
ErrorD(Error(FSComp.SR.tcMethodRequireNamedArgumentAttribute(minfo.LogicalName), m))
else
CompleteD)
Some res)
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/AttributeChecking.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ val CheckPropInfoAttributes: pinfo:PropInfo -> m:range -> OperationResult<unit>

val CheckILFieldAttributes: g:TcGlobals -> finfo:ILFieldInfo -> m:range -> unit

val CheckMethInfoAttributes: g:TcGlobals -> m:range -> tyargsOpt:'a option -> minfo:MethInfo -> OperationResult<unit>
val CheckMethInfoAttributes: g:TcGlobals -> m:range -> tyargsOpt:'a option -> minfo:MethInfo -> hasUnammedCallerArgs:bool option -> OperationResult<unit>

val MethInfoIsUnseen: g:TcGlobals -> m:range -> ty:TType -> minfo:MethInfo -> bool

Expand Down
14 changes: 10 additions & 4 deletions src/fsharp/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -8322,7 +8322,7 @@ and TcEventValueThen cenv overallTy env tpenv mItem mExprAndItem objDetails (ein
let delegateType = einfo.GetDelegateType(cenv.amap, mItem)
let (SigOfFunctionForDelegate(invokeMethInfo, compiledViewOfDelArgTys, _, _)) = GetSigOfFunctionForDelegate cenv.infoReader delegateType mItem ad
let objArgs = Option.toList (Option.map fst objDetails)
MethInfoChecks cenv.g cenv.amap true None objArgs env.eAccessRights mItem invokeMethInfo
MethInfoChecks cenv.g cenv.amap true None objArgs env.eAccessRights mItem invokeMethInfo None

// This checks for and drops the 'object' sender
let argsTy = ArgsTypOfEventInfo cenv.infoReader mItem ad einfo
Expand Down Expand Up @@ -8788,7 +8788,13 @@ and TcMethodApplication
finalCalledMeth.AssociatedPropertyInfo |> Option.iter (fun pinfo -> CheckPropInfoAttributes pinfo mItem |> CommitOperationResult)

let isInstance = not (isNil objArgs)
MethInfoChecks cenv.g cenv.amap isInstance tyargsOpt objArgs ad mItem finalCalledMethInfo

let hasUnammedCallerArgs =
match unnamedCurriedCallerArgs with
| [] | [[]] -> false
| _ -> true

MethInfoChecks cenv.g cenv.amap isInstance tyargsOpt objArgs ad mItem finalCalledMethInfo (Some hasUnammedCallerArgs)

// Adhoc constraints on use of .NET methods
begin
Expand Down Expand Up @@ -8925,7 +8931,7 @@ and TcSetterArgExpr cenv env denv objExpr ad (AssignedItemSetter(id, setter, Cal
let argExprPrebinder, action, defnItem =
match setter with
| AssignedPropSetter (pinfo, pminfo, pminst) ->
MethInfoChecks cenv.g cenv.amap true None [objExpr] ad m pminfo
MethInfoChecks cenv.g cenv.amap true None [objExpr] ad m pminfo None
let calledArgTy = List.head (List.head (pminfo.GetParamTypes(cenv.amap, m, pminst)))
let argExprPrebinder, argExpr = MethodCalls.AdjustCallerArgExprForCoercions cenv.g cenv.amap cenv.infoReader ad false calledArgTy ReflectedArgInfo.None callerArgTy m argExpr
let mut = (if isStructTy cenv.g (tyOfExpr cenv.g objExpr) then DefinitelyMutates else PossiblyMutates)
Expand Down Expand Up @@ -9040,7 +9046,7 @@ and TcNewDelegateThen cenv overallTy env tpenv mDelTy mExprAndArg delegateTy arg
UnifyTypes cenv env mExprAndArg overallTy delegateTy
let (SigOfFunctionForDelegate(invokeMethInfo, delArgTys, _, fty)) = GetSigOfFunctionForDelegate cenv.infoReader delegateTy mDelTy ad
// We pass isInstance = true here because we're checking the rights to access the "Invoke" method
MethInfoChecks cenv.g cenv.amap true None [] env.eAccessRights mExprAndArg invokeMethInfo
MethInfoChecks cenv.g cenv.amap true None [] env.eAccessRights mExprAndArg invokeMethInfo None
let args = GetMethodArgs arg
match args with
| [farg], [] ->
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/ConstraintSolver.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1592,7 +1592,7 @@ and SolveMemberConstraint (csenv: ConstraintSolverEnv) ignoreUnresolvedOverload
else
ErrorD(ConstraintSolverError(FSComp.SR.csMethodFoundButIsStatic((NicePrint.minimalStringOfType denv minfo.ApparentEnclosingType), (DecompileOpName nm), nm), m, m2 ))
else
do! CheckMethInfoAttributes g m None minfo
do! CheckMethInfoAttributes g m None minfo None
return TTraitSolved (minfo, calledMeth.CalledTyArgs)

| _ ->
Expand Down
3 changes: 2 additions & 1 deletion src/fsharp/FSComp.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1556,4 +1556,5 @@ forFormatInvalidForInterpolated4,"Interpolated strings used as type IFormattable
3390,xmlDocMissingParameter,"This XML comment is incomplete: no documentation for parameter '%s'"
3391,tcLiteralAttributeCannotUseActivePattern,"A [<Literal>] declaration cannot use an active pattern for its identifier"
3392,containerDeprecated,"The 'AssemblyKeyNameAttribute' has been deprecated. Use 'AssemblyKeyFileAttribute' instead."
3393,containerSigningUnsupportedOnThisPlatform,"Key container signing is not supported on this platform."
3393,containerSigningUnsupportedOnThisPlatform,"Key container signing is not supported on this platform."
3394,tcMethodRequireNamedArgumentAttribute,"The method '%s' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)')."
7 changes: 6 additions & 1 deletion src/fsharp/FSharp.Core/prim-types.fs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,12 @@ namespace Microsoft.FSharp.Core
[<Sealed>]
type RequireQualifiedAccessAttribute() =
inherit System.Attribute()


[<AttributeUsage(AttributeTargets.Method, AllowMultiple=false)>]
[<Sealed>]
type RequireNamedArgumentAttribute() =
inherit System.Attribute()

[<AttributeUsage (AttributeTargets.Class ||| AttributeTargets.Assembly, AllowMultiple=true)>]
[<Sealed>]
type AutoOpenAttribute(path:string) =
Expand Down
13 changes: 13 additions & 0 deletions src/fsharp/FSharp.Core/prim-types.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,19 @@ namespace Microsoft.FSharp.Core
/// <returns>RequireQualifiedAccessAttribute</returns>
new : unit -> RequireQualifiedAccessAttribute

/// <summary>This attribute is used to mandate that call site to a method bearing the attribute
/// use named arguments argument syntax.</summary>
///
/// <category>Attributes</category>
[<AttributeUsage(AttributeTargets.Method, AllowMultiple=false)>]
[<Sealed>]
type RequireNamedArgumentAttribute =
inherit Attribute

/// <summary>Creates an instance of the attribute</summary>
/// <returns>RequireNamedArgumentAttribute</returns>
new : unit -> RequireNamedArgumentAttribute

/// <summary>Indicates a construct is automatically opened when brought into scope through
/// an assembly reference or then opening of the containing namespace or module.</summary>
///
Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/InfoReader.fs
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,7 @@ let GetSigOfFunctionForDelegate (infoReader: InfoReader) delty m ad =
| [] -> [g.unit_ty]
| _ -> compiledViewOfDelArgTys
let delRetTy = invokeMethInfo.GetFSharpReturnTy(amap, m, minst)
CheckMethInfoAttributes g m None invokeMethInfo |> CommitOperationResult
CheckMethInfoAttributes g m None invokeMethInfo None |> CommitOperationResult
let fty = mkIteratedFunTy fsharpViewOfDelArgTys delRetTy
SigOfFunctionForDelegate(invokeMethInfo, compiledViewOfDelArgTys, delRetTy, fty)

Expand Down
4 changes: 2 additions & 2 deletions src/fsharp/MethodCalls.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1766,7 +1766,7 @@ let ILFieldInstanceChecks g amap ad m (finfo : ILFieldInfo) =
CheckILFieldInfoAccessible g amap m ad finfo
CheckILFieldAttributes g finfo m

let MethInfoChecks g amap isInstance tyargsOpt objArgs ad m (minfo: MethInfo) =
let MethInfoChecks g amap isInstance tyargsOpt objArgs ad m (minfo: MethInfo) (hasUnammedCallerArgs: bool option) =
if minfo.IsInstance <> isInstance then
if isInstance then
error (Error (FSComp.SR.csMethodIsNotAnInstanceMethod(minfo.LogicalName), m))
Expand Down Expand Up @@ -1798,7 +1798,7 @@ let MethInfoChecks g amap isInstance tyargsOpt objArgs ad m (minfo: MethInfo) =
(minfo.LogicalName.StartsWithOrdinal("get_Item") || minfo.LogicalName.StartsWithOrdinal("get_Rest")) then
warning (Error (FSComp.SR.tcTupleMemberNotNormallyUsed(), m))

CheckMethInfoAttributes g m tyargsOpt minfo |> CommitOperationResult
CheckMethInfoAttributes g m tyargsOpt minfo hasUnammedCallerArgs |> CommitOperationResult

exception FieldNotMutable of DisplayEnv * RecdFieldRef * range

Expand Down
2 changes: 1 addition & 1 deletion src/fsharp/MethodCalls.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ val ILFieldStaticChecks: g:TcGlobals -> amap:ImportMap -> infoReader:InfoReader

val ILFieldInstanceChecks: g:TcGlobals -> amap:ImportMap -> ad:AccessorDomain -> m:range -> finfo:ILFieldInfo -> unit

val MethInfoChecks: g:TcGlobals -> amap:ImportMap -> isInstance:bool -> tyargsOpt:'a option -> objArgs:Expr list -> ad:AccessorDomain -> m:range -> minfo:MethInfo -> unit
val MethInfoChecks: g:TcGlobals -> amap:ImportMap -> isInstance:bool -> tyargsOpt:'a option -> objArgs:Expr list -> ad:AccessorDomain -> m:range -> minfo:MethInfo -> hasUnammedCallerArgs:bool option -> unit

exception FieldNotMutable of TypedTreeOps.DisplayEnv * RecdFieldRef * range

Expand Down
1 change: 1 addition & 0 deletions src/fsharp/TcGlobals.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1199,6 +1199,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d

member val attrib_AutoSerializableAttribute = mk_MFCore_attrib "AutoSerializableAttribute"
member val attrib_RequireQualifiedAccessAttribute = mk_MFCore_attrib "RequireQualifiedAccessAttribute"
member val attrib_RequireArgumentNameAttribute = mk_MFCore_attrib "RequireArgumentNameAttribute"
member val attrib_EntryPointAttribute = mk_MFCore_attrib "EntryPointAttribute"
member val attrib_DefaultAugmentationAttribute = mk_MFCore_attrib "DefaultAugmentationAttribute"
member val attrib_CompilerMessageAttribute = mk_MFCore_attrib "CompilerMessageAttribute"
Expand Down
1 change: 1 addition & 0 deletions tests/FSharp.Core.UnitTests/SurfaceArea.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2274,6 +2274,7 @@ Microsoft.FSharp.Core.ReflectedDefinitionAttribute: Boolean IncludeValue
Microsoft.FSharp.Core.ReflectedDefinitionAttribute: Boolean get_IncludeValue()
Microsoft.FSharp.Core.ReflectedDefinitionAttribute: Void .ctor()
Microsoft.FSharp.Core.ReflectedDefinitionAttribute: Void .ctor(Boolean)
Microsoft.FSharp.Core.RequireNamedArgumentAttribute: Void .ctor()
Microsoft.FSharp.Core.RequireQualifiedAccessAttribute: Void .ctor()
Microsoft.FSharp.Core.RequiresExplicitTypeArgumentsAttribute: Void .ctor()
Microsoft.FSharp.Core.ResultModule: Microsoft.FSharp.Core.FSharpResult`2[T,TResult] MapError[TError,TResult,T](Microsoft.FSharp.Core.FSharpFunc`2[TError,TResult], Microsoft.FSharp.Core.FSharpResult`2[T,TError])
Expand Down
5 changes: 5 additions & 0 deletions tests/fsharp/tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -3063,3 +3063,8 @@ module OverloadResolution =
let [<Test>] ``neg_generic_known_argument_types`` () = singleNegTest (testConfig "typecheck/overloads") "neg_generic_known_argument_types"
let [<Test>] ``neg_tupled_arguments`` () = singleNegTest (testConfig "typecheck/overloads") "neg_tupled_arguments"
#endif

module RequireNamedArgument =
[<Test>]
let ``type check neg_requirenamedargument same file`` () =
singleNegTest (testConfig "typecheck/requirenamedargument") "test1"
16 changes: 16 additions & 0 deletions tests/fsharp/typecheck/requirenamedargument/test1.bsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

test1.fsx(41,1,41,4): typecheck error FS3394: The method 'B' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)').

test1.fsx(43,1,43,4): typecheck error FS3394: The method 'C' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)').

test1.fsx(50,1,50,4): typecheck error FS3394: The method 'I' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)').

test1.fsx(54,1,54,5): typecheck error FS3394: The method 'I' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)').

test1.fsx(57,1,57,23): typecheck error FS3394: The method 'DefaultMethod' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)').

test1.fsx(58,1,58,16): typecheck error FS3394: The method 'Method' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)').

test1.fsx(60,1,60,24): typecheck error FS3394: The method 'DefaultMethod' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)').

test1.fsx(61,1,61,17): typecheck error FS3394: The method 'Method' has the 'RequireNamedArgumentAttribute' attribute specified, use the named arguments syntax (e.g. 'MethodName(x = value)').
61 changes: 61 additions & 0 deletions tests/fsharp/typecheck/requirenamedargument/test1.fsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
open System.Runtime.InteropServices


type I =
interface
[<RequireNamedArgument>]
abstract member I: i:int -> unit
end

[<AbstractClass>]
type Abstract() =
[<RequireNamedArgument>]
abstract Method: i:int -> unit
[<RequireNamedArgument>]
abstract DefaultMethod: i:int -> unit
default this.DefaultMethod(i) = printfn $"{i}"

type Concrete() =
inherit Abstract()
interface I with
member x.I(i:int) = printfn $"{i}"
override x.Method(_) = printfn "method!"
override x.DefaultMethod(a) = printfn "default method!"; base.DefaultMethod(a)

type A() =

[<RequireNamedArgument>]
member x.B(c:int, d:string) = ()

[<RequireNamedArgument>]
static member C(d:byte) = ()

[<RequireNamedArgument>]
static member C() = ()

type B() =
inherit A()
member x.Opt([<Optional;DefaultParameterValue(1)>]i: int, j: int) = ()
let a = A()

a.B(1, "")

A.C(1uy)

A.C()

let b = B()
b.Opt(j=2)
let i = Concrete() :> I
i.I(1)

let ii = { new I with member x.I(b:int) = printfn $"{b}"}

ii.I(2)

let concrete = Concrete()
concrete.DefaultMethod(1)
concrete.Method(1)
let baseClass = concrete :> Abstract
baseClass.DefaultMethod(1)
baseClass.Method(1)