Skip to content

Improve error reporting for anonymous records declarations #15824

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 21, 2023
Merged
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
16 changes: 9 additions & 7 deletions src/Compiler/Checking/CheckExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -4511,20 +4511,22 @@ and TcTupleType kindOpt (cenv: cenv) newOk checkConstraints occ env tpenv isStru
else
let argsR,tpenv = TcTypesAsTuple cenv newOk checkConstraints occ env tpenv args m
TType_tuple(tupInfo, argsR), tpenv

and CheckAnonRecdTypeDuplicateFields (elems: Ident array) =
elems |> Array.iteri (fun i (uc1: Ident) ->
elems |> Array.iteri (fun j (uc2: Ident) ->
if j > i && uc1.idText = uc2.idText then
errorR(Error(FSComp.SR.tcAnonRecdTypeDuplicateFieldId(uc1.idText), uc1.idRange))))

and TcAnonRecdType (cenv: cenv) newOk checkConstraints occ env tpenv isStruct args m =
let tupInfo = mkTupInfo isStruct
let unsortedFieldIds = args |> List.map fst |> List.toArray
if unsortedFieldIds.Length > 1 then
CheckAnonRecdTypeDuplicateFields unsortedFieldIds
let tup = args |> List.map (fun (_, t) -> SynTupleTypeSegment.Type t)
let argsR,tpenv = TcTypesAsTuple cenv newOk checkConstraints occ env tpenv tup m
let unsortedFieldIds = args |> List.map fst |> List.toArray
let anonInfo = AnonRecdTypeInfo.Create(cenv.thisCcu, tupInfo, unsortedFieldIds)

// Check for duplicate field IDs
unsortedFieldIds
|> Array.countBy (fun fieldId -> fieldId.idText)
|> Array.iter (fun (idText, count) ->
if count > 1 then error (Error (FSComp.SR.tcAnonRecdTypeDuplicateFieldId(idText), m)))

// Sort into canonical order
let sortedFieldTys, sortedCheckedArgTys = List.zip args argsR |> List.indexed |> List.sortBy (fun (i,_) -> unsortedFieldIds[i].idText) |> List.map snd |> List.unzip

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,85 @@ let x = {| abcd = {| ab = 4; cd = 1 |} |}
"""
|> compile
|> shouldSucceed

[<Fact>]
let ``Anonymous Records field appears multiple times in this anonymous record definition`` () =
Fsx """
let x(f: {| A: int; A: int |}) = ()
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Error 3523, Line 2, Col 13, Line 2, Col 14, "The field 'A' appears multiple times in this anonymous record type.")
]

[<Fact>]
let ``Anonymous Records field appears multiple times in this anonymous record definition 2`` () =
Fsx """
let x(f: {| A: int; A: int; A:int |}) = ()
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Error 3523, Line 2, Col 13, Line 2, Col 14, "The field 'A' appears multiple times in this anonymous record type.")
(Error 3523, Line 2, Col 21, Line 2, Col 22, "The field 'A' appears multiple times in this anonymous record type.")
]

[<Fact>]
let ``Anonymous Records field appears multiple times in this anonymous record declaration 3`` () =
Fsx """
let f(x:{| A: int; B: int; A:string; B: int |}) = ()
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Error 3523, Line 2, Col 12, Line 2, Col 13, "The field 'A' appears multiple times in this anonymous record type.")
(Error 3523, Line 2, Col 20, Line 2, Col 21, "The field 'B' appears multiple times in this anonymous record type.")
]

[<Fact>]
let ``Anonymous Records field appears multiple times in this anonymous record declaration 4`` () =
Fsx """
let f(x:{| A: int; C: string; A: int; B: int |}) = ()
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Error 3523, Line 2, Col 12, Line 2, Col 13, "The field 'A' appears multiple times in this anonymous record type.")
]

[<Fact>]
let ``Anonymous Records field appears multiple times in this anonymous record declaration 5`` () =
Fsx """
let f(x:{| A: int; C: string; A: int; B: int; A: int |}) = ()
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Error 3523, Line 2, Col 12, Line 2, Col 13, "The field 'A' appears multiple times in this anonymous record type.")
(Error 3523, Line 2, Col 31, Line 2, Col 32, "The field 'A' appears multiple times in this anonymous record type.")
]

[<Fact>]
let ``Anonymous Records field with in double backticks appears multiple times in this anonymous record declaration`` () =
Fsx """
let f(x:{| ``A``: int; B: int; A:string; B: int |}) = ()
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Error 3523, Line 2, Col 12, Line 2, Col 17, "The field 'A' appears multiple times in this anonymous record type.")
(Error 3523, Line 2, Col 24, Line 2, Col 25, "The field 'B' appears multiple times in this anonymous record type.")
]

[<Fact>]
let ``Anonymous Records field appears multiple times in this anonymous record declaration 6`` () =
Fsx """
let foo: {| A: int; C: string; A: int; B: int; A: int |} = failwith "foo"
"""
|> compile
|> shouldFail
|> withDiagnostics [
(Error 3523, Line 2, Col 13, Line 2, Col 14, "The field 'A' appears multiple times in this anonymous record type.")
(Error 3523, Line 2, Col 32, Line 2, Col 33, "The field 'A' appears multiple times in this anonymous record type.")
]