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
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.100.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* Fixed how the source ranges of warn directives are reported (as trivia) in the parser output (by not reporting leading spaces). ([Issue #19405](https://github.com/dotnet/fsharp/issues/19405), [PR #19408]((https://github.com/dotnet/fsharp/pull/19408)))
* Fix UoM value type `ToString()` returning garbage values when `--checknulls+` is enabled, caused by double address-taking in codegen. ([Issue #19435](https://github.com/dotnet/fsharp/issues/19435), [PR #19440](https://github.com/dotnet/fsharp/pull/19440))
* Fix completion inconsistently showing some obsolete members (fields and events) while hiding others (methods and properties). All obsolete members are now consistently hidden by default. ([Issue #13512](https://github.com/dotnet/fsharp/issues/13512), [PR #19506](https://github.com/dotnet/fsharp/pull/19506))
* Fix `AttributeUsage.AllowMultiple` not being inherited for attributes subclassed in C#. ([Issue #17107](https://github.com/dotnet/fsharp/issues/17107), [PR #19315](https://github.com/dotnet/fsharp/pull/19315))

### Added

Expand Down
15 changes: 13 additions & 2 deletions src/Compiler/Checking/PostInferenceChecks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2036,8 +2036,19 @@ and CheckAttribs cenv env (attribs: Attribs) =
|> Seq.filter (fun (_, count) -> count > 1)
|> Seq.map fst
|> Seq.toList
// Filter for allowMultiple = false
|> List.filter (fun (tcref, _, m) -> TryFindAttributeUsageAttribute cenv.g m tcref <> Some true)
// Filter for allowMultiple = false, walking the inheritance chain to find AttributeUsage
|> List.filter (fun (tcref, _, m) ->
let rec allowsMultiple (tcref: TyconRef) =
match TryFindAttributeUsageAttribute cenv.g m tcref with
| Some res -> res
| None ->
generalizedTyconRef cenv.g tcref
|> GetSuperTypeOfType cenv.g cenv.amap m
|> Option.bind (tryTcrefOfAppTy cenv.g >> ValueOption.toOption)
|> Option.map allowsMultiple
|> Option.defaultValue false

not (allowsMultiple tcref))

if cenv.reportErrors then
for tcref, _, m in duplicates do
Expand Down
44 changes: 21 additions & 23 deletions src/Compiler/TypedTree/TypedTreeOps.Attributes.fs
Original file line number Diff line number Diff line change
Expand Up @@ -812,30 +812,28 @@ module internal AttributeHelpers =
| [ Some(:? bool as v: obj) ], _ -> Some v
| _ -> None)

/// Try to find the resolved attributeusage for an type by walking its inheritance tree and picking the correct attribute usage value
/// Try to find the AllowMultiple value of the AttributeUsage attribute on a type definition.
let TryFindAttributeUsageAttribute g m tcref =
[| yield tcref; yield! supersOfTyconRef tcref |]
|> Array.tryPick (fun tcref ->
TryBindTyconRefAttribute
g
m
g.attrib_AttributeUsageAttribute
tcref
(fun (_, named) ->
named
|> List.tryPick (function
| "AllowMultiple", _, _, ILAttribElem.Bool res -> Some res
| _ -> None))
(fun (Attrib(_, _, _, named, _, _, _)) ->
named
|> List.tryPick (function
| AttribNamedArg("AllowMultiple", _, _, AttribBoolArg res) -> Some res
| _ -> None))
(fun (_, named) ->
named
|> List.tryPick (function
| "AllowMultiple", Some(:? bool as res: obj) -> Some res
| _ -> None)))
TryBindTyconRefAttribute
g
m
g.attrib_AttributeUsageAttribute
tcref
(fun (_, named) ->
named
|> List.tryPick (function
| "AllowMultiple", _, _, ILAttribElem.Bool res -> Some res
| _ -> None))
(fun (Attrib(_, _, _, named, _, _, _)) ->
named
|> List.tryPick (function
| AttribNamedArg("AllowMultiple", _, _, AttribBoolArg res) -> Some res
| _ -> None))
(fun (_, named) ->
named
|> List.tryPick (function
| "AllowMultiple", Some(:? bool as res: obj) -> Some res
| _ -> None))

/// Try to find a specific attribute on a type definition, where the attribute accepts a string argument.
///
Expand Down
2 changes: 1 addition & 1 deletion src/Compiler/TypedTree/TypedTreeOps.Attributes.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ module internal AttributeHelpers =
/// Check if a TyconRef has AllowNullLiteralAttribute, returning Some true/Some false/None.
val TyconRefAllowsNull: g: TcGlobals -> tcref: TyconRef -> bool option

/// Try to find the AttributeUsage attribute, looking for the value of the AllowMultiple named parameter
/// Try to find the AllowMultiple value of the AttributeUsage attribute on a type definition.
val TryFindAttributeUsageAttribute: TcGlobals -> range -> TyconRef -> bool option

val (|AttribBitwiseOrExpr|_|): TcGlobals -> Expr -> (Expr * Expr) voption
Expand Down
8 changes: 0 additions & 8 deletions src/Compiler/TypedTree/TypedTreeOps.FreeVars.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1542,11 +1542,3 @@ module internal MemberRepresentation =
match tycon.TypeContents.tcaug_super with
| None -> g.obj_ty_noNulls
| Some ty -> ty

/// walk a TyconRef's inheritance tree, yielding any parent types as an array
let supersOfTyconRef (tcref: TyconRef) =
tcref
|> Array.unfold (fun tcref ->
match tcref.TypeContents.tcaug_super with
| Some(TType_app(sup, _, _)) -> Some(sup, sup)
| _ -> None)
3 changes: 0 additions & 3 deletions src/Compiler/TypedTree/TypedTreeOps.FreeVars.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -357,9 +357,6 @@ module internal MemberRepresentation =

val superOfTycon: TcGlobals -> Tycon -> TType

/// walk a TyconRef's inheritance tree, yielding any parent types as an array
val supersOfTyconRef: TyconRef -> TyconRef array

val GetTraitConstraintInfosOfTypars: TcGlobals -> Typars -> TraitConstraintInfo list

val GetTraitWitnessInfosOfTypars: TcGlobals -> numParentTypars: int -> typars: Typars -> TraitWitnessInfos
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,3 +77,114 @@ type C() =
|> withReferences [csharpBaseClass]
|> compile
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPP>]
let ``C# attribute subclass inherits AllowMultiple true from base`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BaseAttribute : Attribute { }
public class ChildAttribute : BaseAttribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

[<Child; Child>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPP>]
let ``C# attribute subclass inherits AllowMultiple false from base`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class BaseAttribute : Attribute { }
public class ChildAttribute : BaseAttribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

[<Child; Child>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldFail
|> withSingleDiagnostic (Error 429, Line 4, Col 10, Line 4, Col 15, "The attribute type 'ChildAttribute' has 'AllowMultiple=false'. Multiple instances of this attribute cannot be attached to a single language element.")

[<FSharp.Test.FactForNETCOREAPP>]
let ``C# attribute multi-level inheritance inherits AllowMultiple true`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BaseAttribute : Attribute { }
public class MiddleAttribute : BaseAttribute { }
public class LeafAttribute : MiddleAttribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

[<Leaf; Leaf>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldSucceed

[<FSharp.Test.FactForNETCOREAPP>]
let ``C# attribute subclass with own AttributeUsage overrides base AllowMultiple`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BaseAttribute : Attribute { }

[AttributeUsage(AttributeTargets.All, AllowMultiple = false)]
public class ChildAttribute : BaseAttribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

[<Child; Child>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldFail
|> withSingleDiagnostic (Error 429, Line 4, Col 10, Line 4, Col 15, "The attribute type 'ChildAttribute' has 'AllowMultiple=false'. Multiple instances of this attribute cannot be attached to a single language element.")

[<FSharp.Test.FactForNETCOREAPP>]
let ``F# attribute subclass of C# base inherits AllowMultiple true`` () =
let csharpLib =
CSharp """
using System;

[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class BaseAttribute : Attribute { }
""" |> withName "csAttrLib"

FSharp """
module Test

type ChildAttribute() = inherit BaseAttribute()

[<Child; Child>]
type C() = class end
"""
|> withReferences [csharpLib]
|> compile
|> shouldSucceed
Loading