diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 92b189c016d..6d34ab81624 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -7497,21 +7497,26 @@ and TcRecdExpr cenv overallTy env tpenv (inherits, withExprOpt, synRecdFields, m | None -> expr expr, tpenv +and CheckAnonRecdExprDuplicateFields (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.tcAnonRecdDuplicateFieldId(uc1.idText), uc1.idRange)))) // Check '{| .... |}' and TcAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, optOrigSynExpr, unsortedFieldIdsAndSynExprsGiven, mWholeExpr) = - - // Check for duplicate field IDs - unsortedFieldIdsAndSynExprsGiven - |> List.countBy (fun (fId, _, _) -> textOfLid fId.LongIdent) - |> List.iter (fun (label, count) -> - if count > 1 then error (Error (FSComp.SR.tcAnonRecdDuplicateFieldId(label), mWholeExpr))) - match optOrigSynExpr with | None -> TcNewAnonRecdExpr cenv overallTy env tpenv (isStruct, unsortedFieldIdsAndSynExprsGiven, mWholeExpr) | Some orig -> + // Ideally we should also check for duplicate field IDs in the TcCopyAndUpdateAnonRecdExpr case, but currently the logic is too complex to garante a proper error reporting + // So here we error instead errorR to avoid cascading internal errors + unsortedFieldIdsAndSynExprsGiven + |> List.countBy (fun (fId, _, _) -> textOfLid fId.LongIdent) + |> List.iter (fun (label, count) -> + if count > 1 then error (Error (FSComp.SR.tcAnonRecdDuplicateFieldId(label), mWholeExpr))) + TcCopyAndUpdateAnonRecdExpr cenv overallTy env tpenv (isStruct, orig, unsortedFieldIdsAndSynExprsGiven, mWholeExpr) and TcNewAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, unsortedFieldIdsAndSynExprsGiven, mWholeExpr) = @@ -7521,6 +7526,9 @@ and TcNewAnonRecdExpr cenv (overallTy: TType) env tpenv (isStruct, unsortedField let unsortedFieldIds = unsortedFieldIdsAndSynExprsGiven |> List.map (fun (synLongIdent, _, _) -> synLongIdent.LongIdent[0]) |> List.toArray let anonInfo, sortedFieldTys = UnifyAnonRecdTypeAndInferCharacteristics env.eContextInfo cenv env.DisplayEnv mWholeExpr overallTy isStruct unsortedFieldIds + if unsortedFieldIds.Length > 1 then + CheckAnonRecdExprDuplicateFields unsortedFieldIds + // Sort into canonical order let sortedIndexedArgs = unsortedFieldIdsAndSynExprsGiven diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index 32588c31f47..6fa51549e91 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -126,7 +126,7 @@ module AnonRecd = """ |> compile |> shouldFail - |> withErrorCode 3522 + |> withSingleDiagnostic (Error 3522, Line 5, Col 13, Line 5, Col 48, "The field 'y' appears multiple times in this record expression.") [] let ``Anonymous Record type annotation with duplicate labels`` () = @@ -138,7 +138,7 @@ module AnonRecd = """ |> compile |> shouldFail - |> withErrorCode 3523 + |> withSingleDiagnostic (Error 3523, Line 5, Col 17, Line 5, Col 18, "The field 'A' appears multiple times in this anonymous record type.") [] let ``Anonymous record types with parser errors or no fields do not produce overlapping diagnostics`` () = @@ -163,7 +163,7 @@ type ErrorResponse = ] [] - let ``Anonymous Record type annotation with with fields defined in a record`` () = + let ``Anonymous Record type annotation with fields defined in a record`` () = Fsx """ type T = { ff : int } @@ -303,3 +303,73 @@ let foo: {| A: int; C: string; A: int; B: int; A: int |} = failwith "foo" (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.") ] + + [] + let ``Anonymous Records field appears multiple times in this record expression`` () = + Fsx """ +let v = {| A = 1; A = 2 |} + """ + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3522, Line 2, Col 12, Line 2, Col 13, "The field 'A' appears multiple times in this record expression.") + ] + + [] + let ``Anonymous Records field appears multiple times in this record expression 2`` () = + Fsx """ +let v = {| A = 1; A = 2; A = 3 |} + """ + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3522, Line 2, Col 12, Line 2, Col 13, "The field 'A' appears multiple times in this record expression.") + (Error 3522, Line 2, Col 19, Line 2, Col 20, "The field 'A' appears multiple times in this record expression.") + ] + + [] + let ``Anonymous Records field appears multiple times in this record expression 3`` () = + Fsx """ +let v = {| A = 0; B = 2; A = 5; B = 6 |} + """ + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3522, Line 2, Col 12, Line 2, Col 13, "The field 'A' appears multiple times in this record expression.") + (Error 3522, Line 2, Col 19, Line 2, Col 20, "The field 'B' appears multiple times in this record expression.") + ] + + [] + let ``Anonymous Records field appears multiple times in this record expression 4`` () = + Fsx """ +let v = {| A = 2; C = "W"; A = 8; B = 6 |} + """ + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3522, Line 2, Col 12, Line 2, Col 13, "The field 'A' appears multiple times in this record expression.") + ] + + [] + let ``Anonymous Records field appears multiple times in this record expression 5`` () = + Fsx """ +let v = {| A = 0; C = ""; A = 1; B = 2; A = 5 |} + """ + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3522, Line 2, Col 12, Line 2, Col 13, "The field 'A' appears multiple times in this record expression.") + (Error 3522, Line 2, Col 27, Line 2, Col 28, "The field 'A' appears multiple times in this record expression.") + ] + + [] + let ``Anonymous Records field appears multiple times in this record expression 6`` () = + Fsx """ +let v = {| ``A`` = 0; B = 5; A = ""; B = 0 |} + """ + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3522, Line 2, Col 12, Line 2, Col 17, "The field 'A' appears multiple times in this record expression.") + (Error 3522, Line 2, Col 23, Line 2, Col 24, "The field 'B' appears multiple times in this record expression.") + ] \ No newline at end of file