From b09d8dce93431847949076e1b6572f641df9c8ea Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 13 Jul 2023 18:01:27 +0200 Subject: [PATCH 01/23] Better anonymous record error reporting --- src/Compiler/Checking/NameResolution.fs | 11 +++++-- src/Compiler/FSComp.txt | 3 +- src/Compiler/xlf/FSComp.txt.cs.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.de.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.es.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.fr.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.it.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ja.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ko.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.pl.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.ru.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.tr.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 5 +++ src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 5 +++ .../Types/RecordTypes/AnonymousRecords.fs | 32 ++++++++++++++++++- 16 files changed, 107 insertions(+), 4 deletions(-) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index b01e074229c..3e892a434e9 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3716,8 +3716,15 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi error(ErrorWithSuggestions(errorText, m, id.idText, suggestLabels)) else lookup() - | _ -> - lookup() + | ValueNone -> + if isAnonRecdTy g ty || isStructAnonRecdTy g ty then + match TryFindAnonRecdFieldOfType g ty id.idText with + | Some(Item.AnonRecdField _) -> + error(Error(FSComp.SR.chkAnonymousRecordFields(id.idText, id.idText), id.idRange)) + | _ -> + error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, id, NoSuggestions)) + else + lookup() | _ -> let lid = (mp@[id]) let tyconSearch ad () = diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 8e49696ba72..b3f2df7af60 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1699,4 +1699,5 @@ featureInformationalObjInferenceDiagnostic,"Diagnostic 3559 (warn when obj infer 3566,tcMultipleRecdTypeChoice,"Multiple type matches were found:\n%s\nThe type '%s' was used. Due to the overlapping field names\n%s\nconsider using type annotations or change the order of open statements." 3567,parsMissingMemberBody,"Expecting member body" 3568,parsMissingKeyword,"Missing keyword '%s'" -3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." \ No newline at end of file +3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." +3578,chkAnonymousRecordFields,"Label '%s' is part of anonymous record. Use {{| expr with %s = ... |}} instead." \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index dbf7c3dc89d..aa6163e96a3 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -32,6 +32,11 @@ Pokud typ používá atribut [<Sealed>] i [<AbstractClass>], znamená to, že je statický. Další konstruktor není povolený. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute by neměl mít alias. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index d883e6a3a9d..4f3f514cade 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -32,6 +32,11 @@ Wenn ein Typ sowohl das Attribute [<Sealed>] wie auch [<AbstractClass>] verwendet, bedeutet dies, dass er statisch ist. Ein zusätzlicher Konstruktor ist nicht zulässig. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute darf kein Alias sein. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index f3e0e6ffb2c..5320483ab38 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -32,6 +32,11 @@ Si un tipo usa los atributos [<Sealed>] y [<AbstractClass>], significa que es estático. No se permite un constructor adicional. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute no debe tener alias. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index fcc144579f3..d70486f2013 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -32,6 +32,11 @@ Si un type utilise les attributs [<Sealed>] et [<AbstractClass>], cela signifie qu’il est statique. Un constructeur supplémentaire n’est pas autorisé. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute ne doit pas avoir d'alias. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 2bdef80be67..08d36125fd7 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -32,6 +32,11 @@ Se un tipo usa entrambi gli attributi [<Sealed>] e [<AbstractClass>], significa che è statico. Non sono ammessi costruttori aggiuntivi. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. Non sono consentiti alias per FSharp.Core.AutoOpenAttribute. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index c1d1522d793..fd79529e7a0 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -32,6 +32,11 @@ 型が [<Sealed>] と [<AbstractClass>] の両方の属性を使用する場合、それは静的であることを意味します。追加のコンストラクターは許可されていません。 + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute にエイリアスを設定することはできません。 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 207e46daeec..5ea0ef5ead2 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -32,6 +32,11 @@ 형식이 [<Sealed>] 및 [<AbstractClass>] 특성을 모두 사용하는 경우 정적임을 의미합니다. 추가 생성자는 허용되지 않습니다. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute는 별칭을 지정할 수 없습니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index d891ca542b8..2b7c9d3a387 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -32,6 +32,11 @@ Jeśli typ używa obu [<Sealed>] i [< AbstractClass>] atrybutów, oznacza to, że jest statyczny. Konstruktor jest również niedozwolony. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. Element FSharp.Core.AutoOpenAttribute nie powinien mieć aliasu. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 82e94682905..4c1c0df0e2a 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -32,6 +32,11 @@ Se um tipo usa os atributos [<Sealed>] e [<AbstractClass>], significa que é estático. Construtor adicional não é permitido. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute não deve ter alias. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 4f6c6cc0244..13a25723588 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -32,6 +32,11 @@ Если тип использует атрибуты [<Sealed>] и [<AbstractClass>], это означает, что он статический. Дополнительный конструктор не разрешен. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute не должен быть псевдонимом. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index 8f3ff9088ae..d94b804fa24 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -32,6 +32,11 @@ Bir tür, hem [<Sealed>] hem de [< AbstractClass>] özniteliklerini kullanıyorsa bu statik olduğu anlamına gelir. Ek oluşturucuya izin verilmez. + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute diğer adlı olamaz. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 5659e17b622..8e5e6a2551c 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -32,6 +32,11 @@ 如果类型同时使用 [<Sealed>] 和 [<AbstractClass>] 属性,则表示它是静态的。不允许使用其他构造函数。 + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute 不应为别名。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index dfe9aabb1d8..49dbac6b0b3 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -32,6 +32,11 @@ 如果類型同時使用 [<Sealed>] 和 [<AbstractClass>] 屬性,表示其為靜態。不允許其他建構函式。 + + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. + + FSharp.Core.AutoOpenAttribute should not be aliased. 不應別名化 FSharp.Core.AutoOpenAttribute。 diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index e9dbe8ffd43..89eb7182672 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -64,4 +64,34 @@ type ErrorResponse = Error 10, Line 5, Col 42, Line 5, Col 43, "Unexpected integer literal in field declaration. Expected ':' or other token." Error 10, Line 7, Col 12, Line 7, Col 14, "Unexpected symbol '|}' in field declaration. Expected identifier or other token." Error 10, Line 10, Col 17, Line 10, Col 21, "Incomplete structured construct at or before this point in field declaration. Expected identifier or other token." - ] \ No newline at end of file + ] + + [] + let ```Anonymous Record type annotation with with fields defined in a record`` () = + Fsx """ +type T = { ff : int } + +let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } + """ + |> ignoreWarnings + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 1, Line 4, Col 42, Line 4, Col 60, "This expression was expected to have type + '{| ff: int; gu: string |}' +but here has type + 'T' ") + ] + + [] + let ```This expression was expected to have an anonymous Record but has a record`` () = + Fsx """ +let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } + """ + |> ignoreWarnings + |> typecheck + |> shouldFail + |> withDiagnostics [ + (Error 3578, Line 2, Col 51, Line 2, Col 53, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") + ] + \ No newline at end of file From 229192ec2c218e2fb859c8c7e3235d9841ce4a72 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 13 Jul 2023 18:10:18 +0200 Subject: [PATCH 02/23] remove extra ticks --- .../Conformance/Types/RecordTypes/AnonymousRecords.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index 440c7cb42cb..2c04c836046 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -67,7 +67,7 @@ type ErrorResponse = ] [] - let ```Anonymous Record type annotation with with fields defined in a record`` () = + let ``Anonymous Record type annotation with with fields defined in a record`` () = Fsx """ type T = { ff : int } @@ -84,7 +84,7 @@ but here has type ] [] - let ```This expression was expected to have an anonymous Record but has a record`` () = + let ``This expression was expected to have an anonymous Record but has a record`` () = Fsx """ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } """ From 216a6e2ab1cafc1d77b121cc6e4326fab2d97294 Mon Sep 17 00:00:00 2001 From: edgardev Date: Wed, 19 Jul 2023 16:51:06 +0200 Subject: [PATCH 03/23] Another possible approach --- src/Compiler/Checking/NameResolution.fs | 33 ++++++++++++------- .../Types/RecordTypes/AnonymousRecords.fs | 7 ++-- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 3e892a434e9..caa0b6e07f2 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3685,8 +3685,9 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi | [] -> let lookup() = let frefs = - try Map.find id.idText nenv.eFieldLabels - with :? KeyNotFoundException -> + match Map.tryFind id.idText nenv.eFieldLabels with + | Some fields -> fields + | None -> // record label is unknown -> suggest related labels and give a hint to the user error(SuggestLabelsOfRelatedRecords g nenv id allFields) @@ -3756,16 +3757,26 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi errorR(Error(FSComp.SR.nrInvalidFieldLabel(), (List.head rest).idRange)) [(resInfo, item)] + +let ResolveAnonRecField (g: TcGlobals) ty (fldId: Ident) = + if (TryFindAnonRecdFieldOfType g ty fldId.idText).IsSome then + error(Error(FSComp.SR.chkAnonymousRecordFields(fldId.idText, fldId.idText), fldId.idRange)) + else + error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, fldId, NoSuggestions)) -let ResolveField sink ncenv nenv ad ty mp id allFields = - let res = ResolveFieldPrim sink ncenv nenv ad ty (mp, id) allFields - // Register the results of any field paths "Module.Type" in "Module.Type.field" as a name resolution. (Note, the path resolution - // info is only non-empty if there was a unique resolution of the field) - let checker = ResultTyparChecker(fun () -> true) - res - |> List.map (fun (resInfo, rfref) -> - ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, resInfo, checker) - rfref) +let ResolveField sink (ncenv: NameResolver) nenv ad ty mp id allFields = + if isAnonRecdTy ncenv.g ty || isStructAnonRecdTy ncenv.g ty then + ResolveAnonRecField ncenv.g ty id + [] + else + let res = ResolveFieldPrim sink ncenv nenv ad ty (mp, id) allFields + // Register the results of any field paths "Module.Type" in "Module.Type.field" as a name resolution. (Note, the path resolution + // info is only non-empty if there was a unique resolution of the field) + let checker = ResultTyparChecker(fun () -> true) + res + |> List.map (fun (resInfo, rfref) -> + ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, resInfo, checker) + rfref) /// Resolve a long identifier representing a nested record field. /// diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index 2c04c836046..1b86c53a0e6 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -77,10 +77,7 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 1, Line 4, Col 42, Line 4, Col 60, "This expression was expected to have type - '{| ff: int; gu: string |}' -but here has type - 'T' ") + (Error 3578, Line 4, Col 51, Line 4, Col 53, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") ] [] @@ -89,7 +86,7 @@ but here has type let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } """ |> ignoreWarnings - |> typecheck + |> compile |> shouldFail |> withDiagnostics [ (Error 3578, Line 2, Col 51, Line 2, Col 53, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") From b075dbb16040370ef3752e621cc07c3e2913c009 Mon Sep 17 00:00:00 2001 From: edgardev Date: Wed, 19 Jul 2023 17:27:25 +0200 Subject: [PATCH 04/23] Remove previous approach and update tests --- src/Compiler/Checking/NameResolution.fs | 10 +--------- .../Conformance/Types/RecordTypes/AnonymousRecords.fs | 4 ++-- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index caa0b6e07f2..2ba58bf0773 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3717,15 +3717,7 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi error(ErrorWithSuggestions(errorText, m, id.idText, suggestLabels)) else lookup() - | ValueNone -> - if isAnonRecdTy g ty || isStructAnonRecdTy g ty then - match TryFindAnonRecdFieldOfType g ty id.idText with - | Some(Item.AnonRecdField _) -> - error(Error(FSComp.SR.chkAnonymousRecordFields(id.idText, id.idText), id.idRange)) - | _ -> - error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, id, NoSuggestions)) - else - lookup() + | ValueNone -> lookup() | _ -> let lid = (mp@[id]) let tyconSearch ad () = diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index 1b86c53a0e6..54de4fb7268 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -77,7 +77,7 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 4, Col 51, Line 4, Col 53, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") + (Error 3578, Line 4, Col 52, Line 4, Col 54, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") ] [] @@ -89,7 +89,7 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 51, Line 2, Col 53, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") + (Error 3578, Line 2, Col 52, Line 2, Col 54, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") ] [] From 9900e7828b94330704a7cc982a59d3270c2977ec Mon Sep 17 00:00:00 2001 From: edgardev Date: Wed, 19 Jul 2023 18:41:44 +0200 Subject: [PATCH 05/23] Don't change unrelated lines --- src/Compiler/FSComp.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index dc6bbf1ae36..a9b666f5570 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1700,6 +1700,6 @@ featureInformationalObjInferenceDiagnostic,"Diagnostic 3559 (warn when obj infer 3566,tcMultipleRecdTypeChoice,"Multiple type matches were found:\n%s\nThe type '%s' was used. Due to the overlapping field names\n%s\nconsider using type annotations or change the order of open statements." 3567,parsMissingMemberBody,"Expecting member body" 3568,parsMissingKeyword,"Missing keyword '%s'" +3569,chkNotTailRecursive,"The member or function '%s' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way." 3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." -3578,chkAnonymousRecordFields,"Label '%s' is part of anonymous record. Use {{| expr with %s = ... |}} instead." -3569,chkNotTailRecursive,"The member or function '%s' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way." \ No newline at end of file +3578,chkAnonymousRecordFields,"Label '%s' is part of anonymous record. Use {{| expr with %s = ... |}} instead." \ No newline at end of file From b9213fc97957b44d6e61a3f6f970bf8c2e1edb50 Mon Sep 17 00:00:00 2001 From: edgardev Date: Wed, 19 Jul 2023 19:33:05 +0200 Subject: [PATCH 06/23] revert unrelated change to make easy to review --- src/Compiler/Checking/NameResolution.fs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 2ba58bf0773..0f7fd4840dd 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3685,9 +3685,8 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi | [] -> let lookup() = let frefs = - match Map.tryFind id.idText nenv.eFieldLabels with - | Some fields -> fields - | None -> + try Map.find id.idText nenv.eFieldLabels + with :? KeyNotFoundException -> // record label is unknown -> suggest related labels and give a hint to the user error(SuggestLabelsOfRelatedRecords g nenv id allFields) From f62fc28d39cec76746633b8a1a643043ac2a6d76 Mon Sep 17 00:00:00 2001 From: edgardev Date: Wed, 19 Jul 2023 21:07:35 +0200 Subject: [PATCH 07/23] SendEntityPathToSink --- src/Compiler/Checking/NameResolution.fs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 0f7fd4840dd..dff7b2159e8 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3756,14 +3756,15 @@ let ResolveAnonRecField (g: TcGlobals) ty (fldId: Ident) = error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, fldId, NoSuggestions)) let ResolveField sink (ncenv: NameResolver) nenv ad ty mp id allFields = + let checker = ResultTyparChecker(fun () -> true) if isAnonRecdTy ncenv.g ty || isStructAnonRecdTy ncenv.g ty then ResolveAnonRecField ncenv.g ty id + ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, ResolutionInfo.Empty, checker) [] else let res = ResolveFieldPrim sink ncenv nenv ad ty (mp, id) allFields // Register the results of any field paths "Module.Type" in "Module.Type.field" as a name resolution. (Note, the path resolution // info is only non-empty if there was a unique resolution of the field) - let checker = ResultTyparChecker(fun () -> true) res |> List.map (fun (resInfo, rfref) -> ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, resInfo, checker) From 3e9200bd39321060a10309c160c7ce801875c12b Mon Sep 17 00:00:00 2001 From: edgardev Date: Wed, 19 Jul 2023 21:08:07 +0200 Subject: [PATCH 08/23] Add struct anon record test --- .../Types/RecordTypes/AnonymousRecords.fs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index 76af65c59ee..7300b9ac46f 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -91,6 +91,18 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } |> withDiagnostics [ (Error 3578, Line 2, Col 52, Line 2, Col 54, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") ] + + [] + let ``This expression was expected to have an struct anonymous Record but has a record`` () = + Fsx """ +let t3 (t1: struct {| gu: string; ff: int |}) = { t1 with ff = 3 } + """ + |> ignoreWarnings + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3578, Line 2, Col 59, Line 2, Col 61, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") + ] [] let ``Nested anonymous records where outer label = concatenated inner labels (see secondary issue reported in 6411)`` () = From 372c4c13f2207139721e76fd5d6ee10c743fe3ec Mon Sep 17 00:00:00 2001 From: edgardev Date: Wed, 19 Jul 2023 21:38:59 +0200 Subject: [PATCH 09/23] Fix merge --- src/Compiler/FSComp.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index ee4a2e31995..26f6b907a2b 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1702,7 +1702,6 @@ featureStaticLetInRecordsDusEmptyTypes,"Allow static let bindings in union, reco 3567,parsMissingMemberBody,"Expecting member body" 3568,parsMissingKeyword,"Missing keyword '%s'" 3569,chkNotTailRecursive,"The member or function '%s' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way." -3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." 3570,tcStaticBindingInExtrinsicAugmentation,"Static bindings cannot be added to extrinsic augmentations. Consider using a 'static member' instead." 3571,pickleFsharpCoreBackwardsCompatible,"Newly added pickle state cannot be used in FSharp.Core, since it must be working in older compilers+tooling as well. The time window is at least 3 years after feature introduction. Violation: %s . Context: \n %s " 3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." From a13e971cc080219a97a186757921043f2d228cf3 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 21 Jul 2023 15:46:27 +0200 Subject: [PATCH 10/23] Update error name and message --- src/Compiler/FSComp.txt | 2 +- src/Compiler/xlf/FSComp.txt.cs.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.de.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.es.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.fr.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.it.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.ja.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.ko.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.pl.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.ru.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.tr.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 10 +++++----- src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 10 +++++----- 14 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 4d20e575867..9e63a55195a 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1708,4 +1708,4 @@ featureAccessorFunctionShorthand,"underscore dot shorthand for accessor only fun 3570,tcStaticBindingInExtrinsicAugmentation,"Static bindings cannot be added to extrinsic augmentations. Consider using a 'static member' instead." 3571,pickleFsharpCoreBackwardsCompatible,"Newly added pickle state cannot be used in FSharp.Core, since it must be working in older compilers+tooling as well. The time window is at least 3 years after feature introduction. Violation: %s . Context: \n %s " 3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." -3578,chkAnonymousRecordFields,"Label '%s' is part of anonymous record. Use {{| expr with %s = ... |}} instead." \ No newline at end of file +3578,chkCopyUpdateSyntaxInAnonRecords,"Label '%s' is part of anonymous record. Use {{| %s with %s = ... |}} instead." \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 6baef6ae4b2..ea577c1cf3d 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -32,11 +32,6 @@ Pokud typ používá atribut [<Sealed>] i [<AbstractClass>], znamená to, že je statický. Další konstruktor není povolený. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute by neměl mít alias. @@ -47,6 +42,11 @@ Pokud typ používá atribut [<Sealed>] i [<AbstractClass>], znamená to, že je statický. Konstruktor s argumenty není povolený. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Duplicitní parametr Parametr {0} byl v této metodě použit vícekrát. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 802b5aa5edc..d424f73a5f4 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -32,11 +32,6 @@ Wenn ein Typ sowohl das Attribute [<Sealed>] wie auch [<AbstractClass>] verwendet, bedeutet dies, dass er statisch ist. Ein zusätzlicher Konstruktor ist nicht zulässig. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute darf kein Alias sein. @@ -47,6 +42,11 @@ Wenn ein Typ sowohl das Attribute [<Sealed>] wie auch [<AbstractClass>] verwendet, bedeutet dies, dass er statisch ist. Der Konstruktor mit Argumenten ist nicht zulässig. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Doppelter Parameter. Der Parameter „{0}“ wurde in dieser Methode mehrmals verwendet. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index c629d359325..0d2b8931a47 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -32,11 +32,6 @@ Si un tipo usa los atributos [<Sealed>] y [<AbstractClass>], significa que es estático. No se permite un constructor adicional. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute no debe tener alias. @@ -47,6 +42,11 @@ Si un tipo usa los atributos [<Sealed>] y [<AbstractClass>], significa que es estático. No se permite un constructor con argumentos. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Parámetro duplicado. El parámetro '{0}' se ha usado más una vez en este método. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 1b55621e1e8..8fdbceb7834 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -32,11 +32,6 @@ Si un type utilise les attributs [<Sealed>] et [<AbstractClass>], cela signifie qu’il est statique. Un constructeur supplémentaire n’est pas autorisé. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute ne doit pas avoir d'alias. @@ -47,6 +42,11 @@ Si un type utilise les attributs [<Sealed>] et [<AbstractClass>], cela signifie qu’il est statique. Le constructeur avec des arguments n’est pas autorisé. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Paramètre dupliqué. Le paramètre « {0} » a été utilisé une fois de plus dans cette méthode. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 0e3cea82ec7..e9370f6ffb2 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -32,11 +32,6 @@ Se un tipo usa entrambi gli attributi [<Sealed>] e [<AbstractClass>], significa che è statico. Non sono ammessi costruttori aggiuntivi. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. Non sono consentiti alias per FSharp.Core.AutoOpenAttribute. @@ -47,6 +42,11 @@ Se un tipo usa entrambi gli attributi [<Sealed>] e [<AbstractClass>], significa che è statico. Costruttore con argomenti non consentito. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Parametro duplicato. Il parametro '{0}' è stato utilizzato più volte in questo metodo. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 5b3f33073f5..59b1340a643 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -32,11 +32,6 @@ 型が [<Sealed>] と [<AbstractClass>] の両方の属性を使用する場合、それは静的であることを意味します。追加のコンストラクターは許可されていません。 - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute にエイリアスを設定することはできません。 @@ -47,6 +42,11 @@ 型が [<Sealed>] と [<AbstractClass>] の両方の属性を使用する場合、それは静的であることを意味します。引数を持つコンストラクターは許可されていません。 + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. パラメーターが重複しています。パラメーター '{0}' は、このメソッドで 1 回以上使用されています。 diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 5dcd232981b..8bda3152844 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -32,11 +32,6 @@ 형식이 [<Sealed>] 및 [<AbstractClass>] 특성을 모두 사용하는 경우 정적임을 의미합니다. 추가 생성자는 허용되지 않습니다. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute는 별칭을 지정할 수 없습니다. @@ -47,6 +42,11 @@ 형식이 [<Sealed>] 및 [<AbstractClass>] 특성을 모두 사용하는 경우 정적임을 의미합니다. 인수가 있는 생성자는 허용되지 않습니다. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. 매개 변수가 중복되었습니다. 이 메소드에서 매개 변수 '{0}'이(가) 두 번 이상 사용되었습니다. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 07cd6f16425..1d94a53274f 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -32,11 +32,6 @@ Jeśli typ używa obu [<Sealed>] i [< AbstractClass>] atrybutów, oznacza to, że jest statyczny. Konstruktor jest również niedozwolony. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. Element FSharp.Core.AutoOpenAttribute nie powinien mieć aliasu. @@ -47,6 +42,11 @@ Jeśli typ używa obu [<Sealed>] i [< AbstractClass>] atrybutów, oznacza to, że jest statyczny. Konstruktor z argumentami jest niedozwolony. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Zduplikowany parametr. Parametr „{0}” został użyty więcej niż raz w tej metodzie. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 3adf88a2ff8..2897bc5fa0c 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -32,11 +32,6 @@ Se um tipo usa os atributos [<Sealed>] e [<AbstractClass>], significa que é estático. Construtor adicional não é permitido. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute não deve ter alias. @@ -47,6 +42,11 @@ Se um tipo usa os atributos [<Sealed>] e [<AbstractClass>], significa que é estático. Construtor com argumentos não é permitido. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Parâmetro duplicado. O parâmetro '{0}' foi usado mais de uma vez neste método. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 512ebe1cc07..cd9a0dd9a2c 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -32,11 +32,6 @@ Если тип использует атрибуты [<Sealed>] и [<AbstractClass>], это означает, что он статический. Дополнительный конструктор не разрешен. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute не должен быть псевдонимом. @@ -47,6 +42,11 @@ Если тип использует атрибуты [<Sealed>] и [<AbstractClass>], это означает, что он статический. Конструктор с аргументами не разрешен. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Повторяющийся параметр. Параметр "{0}" использовался в этом методе несколько раз. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index e509bcaf719..07e1497b87c 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -32,11 +32,6 @@ Bir tür, hem [<Sealed>] hem de [< AbstractClass>] özniteliklerini kullanıyorsa bu statik olduğu anlamına gelir. Ek oluşturucuya izin verilmez. - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute diğer adlı olamaz. @@ -47,6 +42,11 @@ Bir tür, hem [<Sealed>] hem de [< AbstractClass>] özniteliklerini kullanıyorsa bu statik olduğu anlamına gelir.Bağımsız değişkenlere sahip oluşturucuya izin verilmez. + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. Yinelenen parametre. '{0}' parametresi bu metotta bir kereden fazla kullanıldı. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index ce486b1687a..5fcf4bc170d 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -32,11 +32,6 @@ 如果类型同时使用 [<Sealed>] 和 [<AbstractClass>] 属性,则表示它是静态的。不允许使用其他构造函数。 - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. FSharp.Core.AutoOpenAttribute 不应为别名。 @@ -47,6 +42,11 @@ 如果类型同时使用 [<Sealed>] 和 [<AbstractClass>] 属性,则表示它是静态的。不允许使用带参数的构造函数。 + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. 参数重复。此方法中多次使用了参数“{0}”。 diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 8f527e64e09..b2d5f6b34de 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -32,11 +32,6 @@ 如果類型同時使用 [<Sealed>] 和 [<AbstractClass>] 屬性,表示其為靜態。不允許其他建構函式。 - - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| expr with {1} = ... |}} instead. - - FSharp.Core.AutoOpenAttribute should not be aliased. 不應別名化 FSharp.Core.AutoOpenAttribute。 @@ -47,6 +42,11 @@ 如果類型同時使用 [<Sealed>] 和 [<AbstractClass>] 屬性,表示其為靜態。不允許具有引數的建構函式。 + + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + + Duplicate parameter. The parameter '{0}' has been used more that once in this method. 重複的參數。參數 '{0}' 在此方法中使用多次。 From 73f3a9bf417181250e2fccad18745a6108460892 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 21 Jul 2023 15:47:17 +0200 Subject: [PATCH 11/23] Use the type name as part of the error message --- src/Compiler/Checking/CheckExpressions.fs | 10 +++++++--- src/Compiler/Checking/CheckExpressions.fsi | 1 + src/Compiler/Checking/CheckPatterns.fs | 2 +- src/Compiler/Checking/NameResolution.fs | 15 +++++++-------- src/Compiler/Checking/NameResolution.fsi | 1 + 5 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 99dec97bcf7..7e03e680876 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -1799,7 +1799,7 @@ let FreshenAbstractSlot g amap m synTyparDecls absMethInfo = //------------------------------------------------------------------------- /// Helper used to check record expressions and record patterns -let BuildFieldMap (cenv: cenv) env isPartial ty (flds: ((Ident list * Ident) * 'T) list) m = +let BuildFieldMap (cenv: cenv) env isPartial ty (tyIdent: Ident option) (flds: ((Ident list * Ident) * 'T) list) m = let g = cenv.g let ad = env.eAccessRights @@ -1813,7 +1813,7 @@ let BuildFieldMap (cenv: cenv) env isPartial ty (flds: ((Ident list * Ident) * ' |> List.choose (fun (fld, fldExpr) -> try let fldPath, fldId = fld - let frefSet = ResolveField cenv.tcSink cenv.nameResolver env.eNameResEnv ad ty fldPath fldId allFields + let frefSet = ResolveField cenv.tcSink cenv.nameResolver env.eNameResEnv ad ty tyIdent fldPath fldId allFields Some(fld, frefSet, fldExpr) with e -> errorRecoveryNoRange e @@ -7386,7 +7386,11 @@ and TcRecdExpr cenv overallTy env tpenv (inherits, withExprOpt, synRecdFields, m match flds with | [] -> [] | _ -> - match BuildFieldMap cenv env hasOrigExpr overallTy flds mWholeExpr with + let tyIdent = + match withExprOpt with + | Some (SynExpr.Ident ident, _) -> Some ident + | _ -> None + match BuildFieldMap cenv env hasOrigExpr overallTy tyIdent flds mWholeExpr with | None -> [] | Some(tinst, tcref, _, fldsList) -> diff --git a/src/Compiler/Checking/CheckExpressions.fsi b/src/Compiler/Checking/CheckExpressions.fsi index b26381b6b02..8c44d752fea 100644 --- a/src/Compiler/Checking/CheckExpressions.fsi +++ b/src/Compiler/Checking/CheckExpressions.fsi @@ -893,6 +893,7 @@ val BuildFieldMap: env: TcEnv -> isPartial: bool -> ty: TType -> + tyIdent : Ident option -> flds: ((Ident list * Ident) * 'T) list -> m: range -> (TypeInst * TyconRef * Map * (string * 'T) list) option diff --git a/src/Compiler/Checking/CheckPatterns.fs b/src/Compiler/Checking/CheckPatterns.fs index 16e082f551b..4598e86ada5 100644 --- a/src/Compiler/Checking/CheckPatterns.fs +++ b/src/Compiler/Checking/CheckPatterns.fs @@ -441,7 +441,7 @@ and TcPatArrayOrList warnOnUpper cenv env vFlags patEnv ty isArray args m = and TcRecordPat warnOnUpper cenv env vFlags patEnv ty fieldPats m = let fieldPats = fieldPats |> List.map (fun (fieldId, _, fieldPat) -> fieldId, fieldPat) - match BuildFieldMap cenv env true ty fieldPats m with + match BuildFieldMap cenv env true ty None fieldPats m with | None -> (fun _ -> TPat_error m), patEnv | Some(tinst, tcref, fldsmap, _fldsList) -> diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 39c9a434f18..4c9e57d7ce9 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3749,17 +3749,16 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi [(resInfo, item)] -let ResolveAnonRecField (g: TcGlobals) ty (fldId: Ident) = - if (TryFindAnonRecdFieldOfType g ty fldId.idText).IsSome then - error(Error(FSComp.SR.chkAnonymousRecordFields(fldId.idText, fldId.idText), fldId.idRange)) - else - error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, fldId, NoSuggestions)) +let ResolveAnonRecField (g: TcGlobals) ty (tyIdent: Ident option) (fldId: Ident) = + match TryFindAnonRecdFieldOfType g ty fldId.idText, tyIdent with + | Some item, Some tpId -> + error(Error(FSComp.SR.chkCopyUpdateSyntaxInAnonRecords(item.DisplayNameCore, tpId.idText, fldId.idText), fldId.idRange)) + | _, _ -> error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, fldId, NoSuggestions)) -let ResolveField sink (ncenv: NameResolver) nenv ad ty mp id allFields = +let ResolveField sink (ncenv: NameResolver) nenv ad ty (tyIdent: Ident option) mp id allFields = let checker = ResultTyparChecker(fun () -> true) if isAnonRecdTy ncenv.g ty || isStructAnonRecdTy ncenv.g ty then - ResolveAnonRecField ncenv.g ty id - ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, ResolutionInfo.Empty, checker) + ResolveAnonRecField ncenv.g ty tyIdent id [] else let res = ResolveFieldPrim sink ncenv nenv ad ty (mp, id) allFields diff --git a/src/Compiler/Checking/NameResolution.fsi b/src/Compiler/Checking/NameResolution.fsi index 0ed9dc1a3e4..e9929d29102 100755 --- a/src/Compiler/Checking/NameResolution.fsi +++ b/src/Compiler/Checking/NameResolution.fsi @@ -750,6 +750,7 @@ val internal ResolveField: nenv: NameResolutionEnv -> ad: AccessorDomain -> ty: TType -> + tyIdent: Ident option -> mp: Ident list -> id: Ident -> allFields: Ident list -> From b1a40377752cfbc68f5a8f98e06d9b16a6d69861 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 21 Jul 2023 15:47:30 +0200 Subject: [PATCH 12/23] More tests --- .../Types/RecordTypes/AnonymousRecords.fs | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index 7300b9ac46f..af07841ea6f 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -19,7 +19,18 @@ module AnonRecd = |> shouldFail |> withErrorCode 3522 |> withMessage "The field 'A' appears multiple times in this record expression." - + + [] + let ``Anonymous Records incomplete`` () = + Fsx """ +let x () : {| A: int; B: string |} = {| A = 123 |} +""" + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 1, Line 2, Col 40, Line 2, Col 53, "This anonymous record does not have enough fields. Add the missing fields [B].") + ] + [] let ``Anonymous Records with duplicate labels - Copy and update expression`` () = FSharp """ @@ -73,11 +84,10 @@ type T = { ff : int } let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } """ - |> ignoreWarnings |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 4, Col 52, Line 4, Col 54, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") + (Error 3578, Line 4, Col 52, Line 4, Col 54, "Label 'ff' is part of anonymous record. Use {| t1 with ff = ... |} instead.") ] [] @@ -85,11 +95,10 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } Fsx """ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } """ - |> ignoreWarnings |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 52, Line 2, Col 54, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") + (Error 3578, Line 2, Col 52, Line 2, Col 54, "Label 'ff' is part of anonymous record. Use {| t1 with ff = ... |} instead.") ] [] @@ -97,11 +106,23 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } Fsx """ let t3 (t1: struct {| gu: string; ff: int |}) = { t1 with ff = 3 } """ - |> ignoreWarnings |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 59, Line 2, Col 61, "Label 'ff' is part of anonymous record. Use {| expr with ff = ... |} instead.") + (Error 3578, Line 2, Col 59, Line 2, Col 61, "Label 'ff' is part of anonymous record. Use {| t1 with ff = ... |} instead.") + ] + + [] + let ``This expression was expected to have an anonymous with various properties Record but has a record`` () = + Fsx """ +let f (r: {| A: int; C: int |}) = { r with A = 1; B = 2; C = 3 } + """ + |> compile + |> shouldFail + |> withDiagnostics [ + (Error 3578, Line 2, Col 44, Line 2, Col 45, "Label 'A' is part of anonymous record. Use {| r with A = ... |} instead.") + (Error 39, Line 2, Col 51, Line 2, Col 52, "The record label 'B' is not defined.") + (Error 3578, Line 2, Col 58, Line 2, Col 59, "Label 'C' is part of anonymous record. Use {| r with C = ... |} instead.") ] [] From d26aed5852d40ead3417434c2f24c1e311d77b3d Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Fri, 21 Jul 2023 16:12:37 +0200 Subject: [PATCH 13/23] format code --- src/Compiler/Checking/CheckExpressions.fsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compiler/Checking/CheckExpressions.fsi b/src/Compiler/Checking/CheckExpressions.fsi index 8c44d752fea..31c1096c6b6 100644 --- a/src/Compiler/Checking/CheckExpressions.fsi +++ b/src/Compiler/Checking/CheckExpressions.fsi @@ -893,7 +893,7 @@ val BuildFieldMap: env: TcEnv -> isPartial: bool -> ty: TType -> - tyIdent : Ident option -> + tyIdent: Ident option -> flds: ((Ident list * Ident) * 'T) list -> m: range -> (TypeInst * TyconRef * Map * (string * 'T) list) option From adc45f107c373f6d38bacbfdd67605cd359098f5 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Sun, 23 Jul 2023 10:18:16 +0200 Subject: [PATCH 14/23] Move to CheckExpressions to avoid poluting sig file --- src/Compiler/Checking/CheckExpressions.fs | 18 +++++++++++++--- src/Compiler/Checking/NameResolution.fs | 26 +++++++---------------- src/Compiler/Checking/NameResolution.fsi | 1 - 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 5699b5e7322..73bde92addc 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -1796,6 +1796,14 @@ let FreshenAbstractSlot g amap m synTyparDecls absMethInfo = let retTyFromAbsSlot = retTy |> GetFSharpViewOfReturnType g |> instType typarInstFromAbsSlot typarsFromAbsSlotAreRigid, typarsFromAbsSlot, argTysFromAbsSlot, retTyFromAbsSlot +let private CheckCopyUpdateSyntaxInAnonRecords sink (g: TcGlobals) ty (tyIdent: Ident option) (fldId: Ident) nenv ad = + match TryFindAnonRecdFieldOfType g ty fldId.idText, tyIdent with + | Some item, Some tpId -> + CallNameResolutionSink sink (fldId.idRange, nenv, item, emptyTyparInst, ItemOccurence.UseInType, ad) + error(Error(FSComp.SR.chkCopyUpdateSyntaxInAnonRecords(item.DisplayNameCore, tpId.idText, fldId.idText), fldId.idRange)) + | _, _ -> + error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, fldId, NoSuggestions)) + //------------------------------------------------------------------------- // Helpers to typecheck expressions and patterns //------------------------------------------------------------------------- @@ -1813,10 +1821,14 @@ let BuildFieldMap (cenv: cenv) env isPartial ty (tyIdent: Ident option) (flds: ( let allFields = flds |> List.map (fun ((_, ident), _) -> ident) flds |> List.choose (fun (fld, fldExpr) -> + let fldPath, fldId = fld try - let fldPath, fldId = fld - let frefSet = ResolveField cenv.tcSink cenv.nameResolver env.eNameResEnv ad ty tyIdent fldPath fldId allFields - Some(fld, frefSet, fldExpr) + if isAnonRecdTy cenv.g ty || isStructAnonRecdTy cenv.g ty then + CheckCopyUpdateSyntaxInAnonRecords cenv.tcSink cenv.g ty tyIdent fldId env.eNameResEnv ad + None + else + let frefSet = ResolveField cenv.tcSink cenv.nameResolver env.eNameResEnv ad ty fldPath fldId allFields + Some(fld, frefSet, fldExpr) with e -> errorRecoveryNoRange e None diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 4c9e57d7ce9..1f1195cc1e9 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3748,26 +3748,16 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi errorR(Error(FSComp.SR.nrInvalidFieldLabel(), (List.head rest).idRange)) [(resInfo, item)] - -let ResolveAnonRecField (g: TcGlobals) ty (tyIdent: Ident option) (fldId: Ident) = - match TryFindAnonRecdFieldOfType g ty fldId.idText, tyIdent with - | Some item, Some tpId -> - error(Error(FSComp.SR.chkCopyUpdateSyntaxInAnonRecords(item.DisplayNameCore, tpId.idText, fldId.idText), fldId.idRange)) - | _, _ -> error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, fldId, NoSuggestions)) -let ResolveField sink (ncenv: NameResolver) nenv ad ty (tyIdent: Ident option) mp id allFields = +let ResolveField sink (ncenv: NameResolver) nenv ad ty mp (id: Ident) allFields = let checker = ResultTyparChecker(fun () -> true) - if isAnonRecdTy ncenv.g ty || isStructAnonRecdTy ncenv.g ty then - ResolveAnonRecField ncenv.g ty tyIdent id - [] - else - let res = ResolveFieldPrim sink ncenv nenv ad ty (mp, id) allFields - // Register the results of any field paths "Module.Type" in "Module.Type.field" as a name resolution. (Note, the path resolution - // info is only non-empty if there was a unique resolution of the field) - res - |> List.map (fun (resInfo, rfref) -> - ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, resInfo, checker) - rfref) + let res = ResolveFieldPrim sink ncenv nenv ad ty (mp, id) allFields + // Register the results of any field paths "Module.Type" in "Module.Type.field" as a name resolution. (Note, the path resolution + // info is only non-empty if there was a unique resolution of the field) + res + |> List.map (fun (resInfo, rfref) -> + ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, resInfo, checker) + rfref) /// Resolve a long identifier representing a nested record field. /// diff --git a/src/Compiler/Checking/NameResolution.fsi b/src/Compiler/Checking/NameResolution.fsi index e9929d29102..0ed9dc1a3e4 100755 --- a/src/Compiler/Checking/NameResolution.fsi +++ b/src/Compiler/Checking/NameResolution.fsi @@ -750,7 +750,6 @@ val internal ResolveField: nenv: NameResolutionEnv -> ad: AccessorDomain -> ty: TType -> - tyIdent: Ident option -> mp: Ident list -> id: Ident -> allFields: Ident list -> From 1556f767d1a517ea20e5f9a6142acfbee82bdb6c Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Sun, 23 Jul 2023 10:18:28 +0200 Subject: [PATCH 15/23] Add Symbol tests --- tests/service/Symbols.fs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 6dbfd2bbaf9..7c97126a45d 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -8,6 +8,7 @@ module Tests.Service.Symbols #endif open System +open FSharp.Compiler.CodeAnalysis open FSharp.Compiler.Service.Tests.Common open FSharp.Compiler.Symbols open FSharp.Compiler.Syntax @@ -751,3 +752,17 @@ type Foo() = and set (a: int) (b: float) = () """ (5, 14, " member _.X", "X") + +module AnonymousRecord = + [] + let ``Anonymous record copy-and-update symbols`` () = + let _, checkResults = getParseAndCheckResults """ +let f (r: {| A: int; C: int |}) = + { r with A = 1; B = 2; C = 3 } +""" + assertHasSymbolUsages [ "A"; "C" ] checkResults + dumpDiagnostics checkResults |> shouldEqual [ + "(3,13--3,14): Label 'A' is part of anonymous record. Use {| r with A = ... |} instead." + "(3,20--3,21): The record label 'B' is not defined." + "(3,27--3,28): Label 'C' is part of anonymous record. Use {| r with C = ... |} instead." + ] \ No newline at end of file From 622f56f0eeb7fd8f0c72d4859792f458d8901342 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Sun, 23 Jul 2023 15:34:30 +0200 Subject: [PATCH 16/23] Update to minimize diff --- src/Compiler/Checking/NameResolution.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 1f1195cc1e9..138cefe2687 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3749,11 +3749,11 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi [(resInfo, item)] -let ResolveField sink (ncenv: NameResolver) nenv ad ty mp (id: Ident) allFields = - let checker = ResultTyparChecker(fun () -> true) +let ResolveField sink ncenv nenv ad ty mp id allFields = let res = ResolveFieldPrim sink ncenv nenv ad ty (mp, id) allFields // Register the results of any field paths "Module.Type" in "Module.Type.field" as a name resolution. (Note, the path resolution // info is only non-empty if there was a unique resolution of the field) + let checker = ResultTyparChecker(fun () -> true) res |> List.map (fun (resInfo, rfref) -> ResolutionInfo.SendEntityPathToSink(sink, ncenv, nenv, ItemOccurence.UseInType, ad, resInfo, checker) From 4ef7e80bc7854e27b8ac4bed1c912b0ece37e774 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 24 Jul 2023 14:58:41 +0200 Subject: [PATCH 17/23] Update ConvertToAnonymousRecordQuickFix and Tests --- .../CodeFixes/ConvertToAnonymousRecord.fs | 2 +- .../ConvertToAnonymousRecordTests.fs | 63 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs index 17e5653daf1..72e6360da41 100644 --- a/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs +++ b/vsintegration/src/FSharp.Editor/CodeFixes/ConvertToAnonymousRecord.fs @@ -16,7 +16,7 @@ type internal ConvertToAnonymousRecordCodeFixProvider [] ( static let title = SR.ConvertToAnonymousRecord() - override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039") + override _.FixableDiagnosticIds = ImmutableArray.Create("FS0039", "FS3578") override this.RegisterCodeFixesAsync context = context.RegisterFsharpFix(this) diff --git a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs index 55af0a5e040..0e33f6af4e8 100644 --- a/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs +++ b/vsintegration/tests/FSharp.Editor.Tests/CodeFixes/ConvertToAnonymousRecordTests.fs @@ -31,6 +31,69 @@ let band = {| Name = "The Velvet Underground" |} Assert.Equal(expected, actual) +[] +let ``Fixes FS3578 for anon records`` () = + let code = + """ +let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } +""" + + let expected = + Some + { + Message = "Convert to Anonymous Record" + FixedCode = + """ +let t3 (t1: {| gu: string; ff: int |}) = {| t1 with ff = 3 |} +""" + } + + let actual = codeFix |> tryFix code 3578 + + Assert.Equal(expected, actual) + +[] +let ``Fixes FS3578 for struct anon records`` () = + let code = + """ +let t3 (t1: struct {| gu: string; ff: int |}) = { t1 with ff = 3 } +""" + + let expected = + Some + { + Message = "Convert to Anonymous Record" + FixedCode = + """ +let t3 (t1: struct {| gu: string; ff: int |}) = {| t1 with ff = 3 |} +""" + } + + let actual = codeFix |> tryFix code 3578 + + Assert.Equal(expected, actual) + +[] +let ``Fixes FS3578 for anon records with multiple fields`` () = + let code = + """ +let f (r: {| A: int; C: int |}) = { r with A = 1; B = 2; C = 3 } +""" + + let expected = + Some + { + Message = "Convert to Anonymous Record" + FixedCode = + """ +let f (r: {| A: int; C: int |}) = {| r with A = 1; B = 2; C = 3 |} +""" + } + + let actual = codeFix |> tryFix code 3578 + + Assert.Equal(expected, actual) + [] let ``Doesn't fix FS0039 for random undefined identifiers`` () = let code = From f16ef8ea4dcb5ea8263a118544b464e63e0a40d0 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Mon, 24 Jul 2023 15:01:55 +0200 Subject: [PATCH 18/23] revert changes to minimize diff --- src/Compiler/Checking/NameResolution.fs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Checking/NameResolution.fs b/src/Compiler/Checking/NameResolution.fs index 138cefe2687..9b14ee4dc29 100644 --- a/src/Compiler/Checking/NameResolution.fs +++ b/src/Compiler/Checking/NameResolution.fs @@ -3685,8 +3685,8 @@ let ResolveFieldPrim sink (ncenv: NameResolver) nenv ad ty (mp, id: Ident) allFi | [] -> let lookup() = let frefs = - try Map.find id.idText nenv.eFieldLabels - with :? KeyNotFoundException -> + try Map.find id.idText nenv.eFieldLabels + with :? KeyNotFoundException -> // record label is unknown -> suggest related labels and give a hint to the user error(SuggestLabelsOfRelatedRecords g nenv id allFields) From 53a93597c00b1ad0e8001a9f53458c7596ae02d2 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 25 Jul 2023 15:50:38 +0200 Subject: [PATCH 19/23] Fix PR comments --- src/Compiler/Checking/CheckExpressions.fs | 20 +++++------- src/Compiler/Checking/CheckExpressions.fsi | 1 - src/Compiler/Checking/CheckPatterns.fs | 2 +- src/Compiler/FSComp.txt | 2 +- src/Compiler/xlf/FSComp.txt.cs.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.de.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.es.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.fr.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.it.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.ja.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.ko.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.pl.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.ru.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.tr.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 4 +-- src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 4 +-- .../Types/RecordTypes/AnonymousRecords.fs | 10 +++--- tests/service/Symbols.fs | 31 ++++++++++++++----- 19 files changed, 64 insertions(+), 54 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index 73bde92addc..dc32eaff9f0 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -1796,12 +1796,12 @@ let FreshenAbstractSlot g amap m synTyparDecls absMethInfo = let retTyFromAbsSlot = retTy |> GetFSharpViewOfReturnType g |> instType typarInstFromAbsSlot typarsFromAbsSlotAreRigid, typarsFromAbsSlot, argTysFromAbsSlot, retTyFromAbsSlot -let private CheckCopyUpdateSyntaxInAnonRecords sink (g: TcGlobals) ty (tyIdent: Ident option) (fldId: Ident) nenv ad = - match TryFindAnonRecdFieldOfType g ty fldId.idText, tyIdent with - | Some item, Some tpId -> +let private CheckCopyUpdateSyntaxInAnonRecords sink (g: TcGlobals) ty (fldId: Ident) nenv ad = + match TryFindAnonRecdFieldOfType g ty fldId.idText with + | Some item -> CallNameResolutionSink sink (fldId.idRange, nenv, item, emptyTyparInst, ItemOccurence.UseInType, ad) - error(Error(FSComp.SR.chkCopyUpdateSyntaxInAnonRecords(item.DisplayNameCore, tpId.idText, fldId.idText), fldId.idRange)) - | _, _ -> + error(Error(FSComp.SR.chkCopyUpdateSyntaxInAnonRecords(fldId.idText, fldId.idText), fldId.idRange)) + | None -> error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, fldId, NoSuggestions)) //------------------------------------------------------------------------- @@ -1809,7 +1809,7 @@ let private CheckCopyUpdateSyntaxInAnonRecords sink (g: TcGlobals) ty (tyIdent: //------------------------------------------------------------------------- /// Helper used to check record expressions and record patterns -let BuildFieldMap (cenv: cenv) env isPartial ty (tyIdent: Ident option) (flds: ((Ident list * Ident) * 'T) list) m = +let BuildFieldMap (cenv: cenv) env isPartial ty (flds: ((Ident list * Ident) * 'T) list) m = let g = cenv.g let ad = env.eAccessRights @@ -1824,7 +1824,7 @@ let BuildFieldMap (cenv: cenv) env isPartial ty (tyIdent: Ident option) (flds: ( let fldPath, fldId = fld try if isAnonRecdTy cenv.g ty || isStructAnonRecdTy cenv.g ty then - CheckCopyUpdateSyntaxInAnonRecords cenv.tcSink cenv.g ty tyIdent fldId env.eNameResEnv ad + CheckCopyUpdateSyntaxInAnonRecords cenv.tcSink cenv.g ty fldId env.eNameResEnv ad None else let frefSet = ResolveField cenv.tcSink cenv.nameResolver env.eNameResEnv ad ty fldPath fldId allFields @@ -7400,11 +7400,7 @@ and TcRecdExpr cenv overallTy env tpenv (inherits, withExprOpt, synRecdFields, m match flds with | [] -> [] | _ -> - let tyIdent = - match withExprOpt with - | Some (SynExpr.Ident ident, _) -> Some ident - | _ -> None - match BuildFieldMap cenv env hasOrigExpr overallTy tyIdent flds mWholeExpr with + match BuildFieldMap cenv env hasOrigExpr overallTy flds mWholeExpr with | None -> [] | Some(tinst, tcref, _, fldsList) -> diff --git a/src/Compiler/Checking/CheckExpressions.fsi b/src/Compiler/Checking/CheckExpressions.fsi index 31c1096c6b6..b26381b6b02 100644 --- a/src/Compiler/Checking/CheckExpressions.fsi +++ b/src/Compiler/Checking/CheckExpressions.fsi @@ -893,7 +893,6 @@ val BuildFieldMap: env: TcEnv -> isPartial: bool -> ty: TType -> - tyIdent: Ident option -> flds: ((Ident list * Ident) * 'T) list -> m: range -> (TypeInst * TyconRef * Map * (string * 'T) list) option diff --git a/src/Compiler/Checking/CheckPatterns.fs b/src/Compiler/Checking/CheckPatterns.fs index 4598e86ada5..16e082f551b 100644 --- a/src/Compiler/Checking/CheckPatterns.fs +++ b/src/Compiler/Checking/CheckPatterns.fs @@ -441,7 +441,7 @@ and TcPatArrayOrList warnOnUpper cenv env vFlags patEnv ty isArray args m = and TcRecordPat warnOnUpper cenv env vFlags patEnv ty fieldPats m = let fieldPats = fieldPats |> List.map (fun (fieldId, _, fieldPat) -> fieldId, fieldPat) - match BuildFieldMap cenv env true ty None fieldPats m with + match BuildFieldMap cenv env true ty fieldPats m with | None -> (fun _ -> TPat_error m), patEnv | Some(tinst, tcref, fldsmap, _fldsList) -> diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 6eec92a26fb..758beb7e954 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1709,4 +1709,4 @@ featureAccessorFunctionShorthand,"underscore dot shorthand for accessor only fun 3570,tcStaticBindingInExtrinsicAugmentation,"Static bindings cannot be added to extrinsic augmentations. Consider using a 'static member' instead." 3571,pickleFsharpCoreBackwardsCompatible,"Newly added pickle state cannot be used in FSharp.Core, since it must be working in older compilers+tooling as well. The time window is at least 3 years after feature introduction. Violation: %s . Context: \n %s " 3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." -3578,chkCopyUpdateSyntaxInAnonRecords,"Label '%s' is part of anonymous record. Use {{| %s with %s = ... |}} instead." \ No newline at end of file +3578,chkCopyUpdateSyntaxInAnonRecords,"'%s' is part of an anonymous record. Use {{| expr with %s = ... |}} instead." \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 5c870d4bfa8..30de7b976dd 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index d86c5f18f84..34a2d5ea78e 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index 1795ba9b75e..ed3a52933d3 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 065ec85dd8a..796bfc0e06f 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 834d014b198..0718fbdc85f 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 18cb7da66dc..23943c69c90 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 5771409d0ae..5e3440cd509 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 1ccc409734a..28e546841c4 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 53a71e34469..31f4dcc1bd6 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index 7cd660e756f..e95f0f5c33d 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index e443d70b84d..d862608459b 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index 394eac2c072..b7885e5b19b 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index 435e52237c1..ab82fdd076c 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -43,8 +43,8 @@ - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. - Label '{0}' is part of anonymous record. Use {{| {1} with {2} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index af07841ea6f..1f4907c93f5 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -87,7 +87,7 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 4, Col 52, Line 4, Col 54, "Label 'ff' is part of anonymous record. Use {| t1 with ff = ... |} instead.") + (Error 3578, Line 4, Col 52, Line 4, Col 54, "'ff' is part of an anonymous record. Use {| expr with ff = ... |} instead.") ] [] @@ -98,7 +98,7 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 52, Line 2, Col 54, "Label 'ff' is part of anonymous record. Use {| t1 with ff = ... |} instead.") + (Error 3578, Line 2, Col 52, Line 2, Col 54, "'ff' is part of an anonymous record. Use {| expr with ff = ... |} instead.") ] [] @@ -109,7 +109,7 @@ let t3 (t1: struct {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 59, Line 2, Col 61, "Label 'ff' is part of anonymous record. Use {| t1 with ff = ... |} instead.") + (Error 3578, Line 2, Col 59, Line 2, Col 61, "'ff' is part of an anonymous record. Use {| expr with ff = ... |} instead.") ] [] @@ -120,9 +120,9 @@ let f (r: {| A: int; C: int |}) = { r with A = 1; B = 2; C = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 44, Line 2, Col 45, "Label 'A' is part of anonymous record. Use {| r with A = ... |} instead.") + (Error 3578, Line 2, Col 44, Line 2, Col 45, "'A' is part of an anonymous record. Use {| expr with A = ... |} instead.") (Error 39, Line 2, Col 51, Line 2, Col 52, "The record label 'B' is not defined.") - (Error 3578, Line 2, Col 58, Line 2, Col 59, "Label 'C' is part of anonymous record. Use {| r with C = ... |} instead.") + (Error 3578, Line 2, Col 58, Line 2, Col 59, "'C' is part of an anonymous record. Use {| expr with C = ... |} instead.") ] [] diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 7c97126a45d..0cbd5ca5b60 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -755,14 +755,29 @@ type Foo() = module AnonymousRecord = [] - let ``Anonymous record copy-and-update symbols`` () = + let ``Anonymous record copy-and-update symbols usage`` () = + let _, checkResults = getParseAndCheckResults """ +let f (x: {| A: int |}) = + {| x with A = 1 |} +""" + + let getSymbolUses = + checkResults.GetAllUsesOfAllSymbolsInFile() + |> Seq.filter (fun su -> su.IsFromType) + |> Array.ofSeq + + Assert.AreEqual(2, getSymbolUses.Length) + + [] + let ``Anonymous record copy-and-update symbols usages`` () = let _, checkResults = getParseAndCheckResults """ let f (r: {| A: int; C: int |}) = - { r with A = 1; B = 2; C = 3 } + {| r with A = 1; B = 2; C = 3 |} """ - assertHasSymbolUsages [ "A"; "C" ] checkResults - dumpDiagnostics checkResults |> shouldEqual [ - "(3,13--3,14): Label 'A' is part of anonymous record. Use {| r with A = ... |} instead." - "(3,20--3,21): The record label 'B' is not defined." - "(3,27--3,28): Label 'C' is part of anonymous record. Use {| r with C = ... |} instead." - ] \ No newline at end of file + + let getSymbolUses = + checkResults.GetAllUsesOfAllSymbolsInFile() + |> Seq.filter(fun su -> su.IsFromType) + |> Array.ofSeq + + Assert.AreEqual(4, getSymbolUses.Length) \ No newline at end of file From 1d111fbf351b6cb536d023a6b8be5bc7bfb9e18e Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 25 Jul 2023 17:50:39 +0200 Subject: [PATCH 20/23] Symbols test take 3 --- tests/service/Symbols.fs | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 0cbd5ca5b60..3484f703d72 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -757,27 +757,47 @@ module AnonymousRecord = [] let ``Anonymous record copy-and-update symbols usage`` () = let _, checkResults = getParseAndCheckResults """ +module X let f (x: {| A: int |}) = {| x with A = 1 |} """ - let getSymbolUses = - checkResults.GetAllUsesOfAllSymbolsInFile() - |> Seq.filter (fun su -> su.IsFromType) + let fstSymbol = + checkResults.GetSymbolUsesAtLocation(3, 14, " {| x with A = 1 |}", ["A"]) |> Array.ofSeq - - Assert.AreEqual(2, getSymbolUses.Length) + |> Array.filter(fun su -> + match su.Symbol with + | :? FSharpField as f when f.IsAnonRecordField -> true + | _ -> false) + |> Array.head + + let sndSymbol = + checkResults.GetSymbolUsesAtLocation(4, 15, " let f (x: {| A: int |})", ["A"]) + |> Array.ofSeq + |> Array.filter(fun su -> + match su.Symbol with + | :? FSharpField as f when f.IsAnonRecordField -> true + | _ -> false) + |> Array.head + + Assert.NotNull fstSymbol + Assert.NotNull sndSymbol [] let ``Anonymous record copy-and-update symbols usages`` () = let _, checkResults = getParseAndCheckResults """ + +module X let f (r: {| A: int; C: int |}) = {| r with A = 1; B = 2; C = 3 |} """ let getSymbolUses = checkResults.GetAllUsesOfAllSymbolsInFile() - |> Seq.filter(fun su -> su.IsFromType) |> Array.ofSeq + |> Array.filter(fun su -> + match su.Symbol with + | :? FSharpField as f when f.IsAnonRecordField -> true + | _ -> false) - Assert.AreEqual(4, getSymbolUses.Length) \ No newline at end of file + Assert.AreEqual(5, getSymbolUses.Length) \ No newline at end of file From 6e8e08b0d09fba2eac5df5810ba520c981f141f7 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 25 Jul 2023 19:17:44 +0200 Subject: [PATCH 21/23] take 4 --- tests/service/Symbols.fs | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 3484f703d72..3ccdc0a3945 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -761,27 +761,15 @@ module X let f (x: {| A: int |}) = {| x with A = 1 |} """ - - let fstSymbol = - checkResults.GetSymbolUsesAtLocation(3, 14, " {| x with A = 1 |}", ["A"]) - |> Array.ofSeq - |> Array.filter(fun su -> - match su.Symbol with - | :? FSharpField as f when f.IsAnonRecordField -> true - | _ -> false) - |> Array.head - - let sndSymbol = - checkResults.GetSymbolUsesAtLocation(4, 15, " let f (x: {| A: int |})", ["A"]) + let getSymbolUses = + checkResults.GetAllUsesOfAllSymbolsInFile() |> Array.ofSeq |> Array.filter(fun su -> match su.Symbol with | :? FSharpField as f when f.IsAnonRecordField -> true | _ -> false) - |> Array.head - - Assert.NotNull fstSymbol - Assert.NotNull sndSymbol + + Assert.AreEqual(2, getSymbolUses.Length) [] let ``Anonymous record copy-and-update symbols usages`` () = From ccefaedf2481c9029f20dd1b13273fcafccc8799 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Tue, 25 Jul 2023 19:33:42 +0200 Subject: [PATCH 22/23] take 5 --- tests/service/Symbols.fs | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/service/Symbols.fs b/tests/service/Symbols.fs index 3ccdc0a3945..9f98d3d35f1 100644 --- a/tests/service/Symbols.fs +++ b/tests/service/Symbols.fs @@ -758,6 +758,23 @@ module AnonymousRecord = let ``Anonymous record copy-and-update symbols usage`` () = let _, checkResults = getParseAndCheckResults """ module X +let f (x: {| A: int |}) = + { x with A = 1 } +""" + let getSymbolUses = + checkResults.GetAllUsesOfAllSymbolsInFile() + |> Array.ofSeq + |> Array.filter(fun su -> + match su.Symbol with + | :? FSharpField as f when f.IsAnonRecordField -> true + | _ -> false) + + Assert.AreEqual(2, getSymbolUses.Length) + + [] + let ``Anonymous anon record copy-and-update symbols usage`` () = + let _, checkResults = getParseAndCheckResults """ +module X let f (x: {| A: int |}) = {| x with A = 1 |} """ @@ -775,6 +792,25 @@ let f (x: {| A: int |}) = let ``Anonymous record copy-and-update symbols usages`` () = let _, checkResults = getParseAndCheckResults """ +module X +let f (r: {| A: int; C: int |}) = + { r with A = 1; B = 2; C = 3 } +""" + + let getSymbolUses = + checkResults.GetAllUsesOfAllSymbolsInFile() + |> Array.ofSeq + |> Array.filter(fun su -> + match su.Symbol with + | :? FSharpField as f when f.IsAnonRecordField -> true + | _ -> false) + + Assert.AreEqual(4, getSymbolUses.Length) + + [] + let ``Anonymous anon record copy-and-update symbols usages`` () = + let _, checkResults = getParseAndCheckResults """ + module X let f (r: {| A: int; C: int |}) = {| r with A = 1; B = 2; C = 3 |} From f5f27afeeb6d12953131dd0a8cbe27062e630f08 Mon Sep 17 00:00:00 2001 From: Edgar Gonzalez Date: Thu, 27 Jul 2023 21:18:38 +0200 Subject: [PATCH 23/23] Ok one more time --- src/Compiler/Checking/CheckExpressions.fs | 63 ++++++++++--------- src/Compiler/FSComp.txt | 2 +- src/Compiler/xlf/FSComp.txt.cs.xlf | 4 +- src/Compiler/xlf/FSComp.txt.de.xlf | 4 +- src/Compiler/xlf/FSComp.txt.es.xlf | 4 +- src/Compiler/xlf/FSComp.txt.fr.xlf | 4 +- src/Compiler/xlf/FSComp.txt.it.xlf | 4 +- src/Compiler/xlf/FSComp.txt.ja.xlf | 4 +- src/Compiler/xlf/FSComp.txt.ko.xlf | 4 +- src/Compiler/xlf/FSComp.txt.pl.xlf | 4 +- src/Compiler/xlf/FSComp.txt.pt-BR.xlf | 4 +- src/Compiler/xlf/FSComp.txt.ru.xlf | 4 +- src/Compiler/xlf/FSComp.txt.tr.xlf | 4 +- src/Compiler/xlf/FSComp.txt.zh-Hans.xlf | 4 +- src/Compiler/xlf/FSComp.txt.zh-Hant.xlf | 4 +- .../Types/RecordTypes/AnonymousRecords.fs | 14 +++-- 16 files changed, 70 insertions(+), 61 deletions(-) diff --git a/src/Compiler/Checking/CheckExpressions.fs b/src/Compiler/Checking/CheckExpressions.fs index dc32eaff9f0..cf17d1a053a 100644 --- a/src/Compiler/Checking/CheckExpressions.fs +++ b/src/Compiler/Checking/CheckExpressions.fs @@ -1796,14 +1796,6 @@ let FreshenAbstractSlot g amap m synTyparDecls absMethInfo = let retTyFromAbsSlot = retTy |> GetFSharpViewOfReturnType g |> instType typarInstFromAbsSlot typarsFromAbsSlotAreRigid, typarsFromAbsSlot, argTysFromAbsSlot, retTyFromAbsSlot -let private CheckCopyUpdateSyntaxInAnonRecords sink (g: TcGlobals) ty (fldId: Ident) nenv ad = - match TryFindAnonRecdFieldOfType g ty fldId.idText with - | Some item -> - CallNameResolutionSink sink (fldId.idRange, nenv, item, emptyTyparInst, ItemOccurence.UseInType, ad) - error(Error(FSComp.SR.chkCopyUpdateSyntaxInAnonRecords(fldId.idText, fldId.idText), fldId.idRange)) - | None -> - error(UndefinedName(0, FSComp.SR.undefinedNameRecordLabel, fldId, NoSuggestions)) - //------------------------------------------------------------------------- // Helpers to typecheck expressions and patterns //------------------------------------------------------------------------- @@ -1816,19 +1808,14 @@ let BuildFieldMap (cenv: cenv) env isPartial ty (flds: ((Ident list * Ident) * ' if isNil flds then invalidArg "flds" "BuildFieldMap" let fldCount = flds.Length - let fldResolutions = let allFields = flds |> List.map (fun ((_, ident), _) -> ident) flds |> List.choose (fun (fld, fldExpr) -> - let fldPath, fldId = fld try - if isAnonRecdTy cenv.g ty || isStructAnonRecdTy cenv.g ty then - CheckCopyUpdateSyntaxInAnonRecords cenv.tcSink cenv.g ty fldId env.eNameResEnv ad - None - else - let frefSet = ResolveField cenv.tcSink cenv.nameResolver env.eNameResEnv ad ty fldPath fldId allFields - Some(fld, frefSet, fldExpr) + let fldPath, fldId = fld + let frefSet = ResolveField cenv.tcSink cenv.nameResolver env.eNameResEnv ad ty fldPath fldId allFields + Some(fld, frefSet, fldExpr) with e -> errorRecoveryNoRange e None @@ -7396,21 +7383,41 @@ and TcRecdExpr cenv overallTy env tpenv (inherits, withExprOpt, synRecdFields, m | _ -> List.frontAndBack synLongId.LongIdent, exprBeingAssigned) let flds = if hasOrigExpr then GroupUpdatesToNestedFields flds else flds + // Check if the overall type is an anon record type and if so raise an copy-update syntax error + // let f (r: {| A: int; C: int |}) = { r with A = 1; B = 2; C = 3 } + if isAnonRecdTy cenv.g overallTy || isStructAnonRecdTy cenv.g overallTy then + for fld, _ in flds do + let _, fldId = fld + match TryFindAnonRecdFieldOfType g overallTy fldId.idText with + | Some item -> + CallNameResolutionSink cenv.tcSink (fldId.idRange, env.eNameResEnv, item, emptyTyparInst, ItemOccurence.UseInType, env.eAccessRights) + | None -> () - match flds with - | [] -> [] - | _ -> - match BuildFieldMap cenv env hasOrigExpr overallTy flds mWholeExpr with - | None -> [] - | Some(tinst, tcref, _, fldsList) -> + try + let firstPartRange = mkRange mWholeExpr.FileName mWholeExpr.Start (mkPos mWholeExpr.StartLine (mWholeExpr.StartColumn + 1)) + // Use the left { in the expression + error(Error(FSComp.SR.chkCopyUpdateSyntaxInAnonRecords(), firstPartRange)) + with _ -> + // Use the right } in the expression + let lastPartRange = mkRange mWholeExpr.FileName (mkPos mWholeExpr.StartLine (mWholeExpr.EndColumn - 1)) (mkPos mWholeExpr.StartLine (mWholeExpr.EndColumn)) + error(Error(FSComp.SR.chkCopyUpdateSyntaxInAnonRecords(), lastPartRange)) + [] + else + // If the overall type is a record type build a map of the fields + match flds with + | [] -> [] + | _ -> + match BuildFieldMap cenv env hasOrigExpr overallTy flds mWholeExpr with + | None -> [] + | Some(tinst, tcref, _, fldsList) -> - let gtyp = mkAppTy tcref tinst - UnifyTypes cenv env mWholeExpr overallTy gtyp + let gtyp = mkAppTy tcref tinst + UnifyTypes cenv env mWholeExpr overallTy gtyp - [ for n, v in fldsList do - match v with - | Some v -> yield n, v - | None -> () ] + [ for n, v in fldsList do + match v with + | Some v -> yield n, v + | None -> () ] let withExprInfoOpt = match withExprOptChecked with diff --git a/src/Compiler/FSComp.txt b/src/Compiler/FSComp.txt index 758beb7e954..805008df352 100644 --- a/src/Compiler/FSComp.txt +++ b/src/Compiler/FSComp.txt @@ -1709,4 +1709,4 @@ featureAccessorFunctionShorthand,"underscore dot shorthand for accessor only fun 3570,tcStaticBindingInExtrinsicAugmentation,"Static bindings cannot be added to extrinsic augmentations. Consider using a 'static member' instead." 3571,pickleFsharpCoreBackwardsCompatible,"Newly added pickle state cannot be used in FSharp.Core, since it must be working in older compilers+tooling as well. The time window is at least 3 years after feature introduction. Violation: %s . Context: \n %s " 3577,tcOverrideUsesMultipleArgumentsInsteadOfTuple,"This override takes a tuple instead of multiple arguments. Try to add an additional layer of parentheses at the method definition (e.g. 'member _.Foo((x, y))'), or remove parentheses at the abstract method declaration (e.g. 'abstract member Foo: 'a * 'b -> 'c')." -3578,chkCopyUpdateSyntaxInAnonRecords,"'%s' is part of an anonymous record. Use {{| expr with %s = ... |}} instead." \ No newline at end of file +3578,chkCopyUpdateSyntaxInAnonRecords,"This expression is an anonymous record, use {{|...|}} instead of {{...}}." \ No newline at end of file diff --git a/src/Compiler/xlf/FSComp.txt.cs.xlf b/src/Compiler/xlf/FSComp.txt.cs.xlf index 30de7b976dd..7995df52a89 100644 --- a/src/Compiler/xlf/FSComp.txt.cs.xlf +++ b/src/Compiler/xlf/FSComp.txt.cs.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.de.xlf b/src/Compiler/xlf/FSComp.txt.de.xlf index 34a2d5ea78e..c903c06018b 100644 --- a/src/Compiler/xlf/FSComp.txt.de.xlf +++ b/src/Compiler/xlf/FSComp.txt.de.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.es.xlf b/src/Compiler/xlf/FSComp.txt.es.xlf index ed3a52933d3..4aadf0f1d66 100644 --- a/src/Compiler/xlf/FSComp.txt.es.xlf +++ b/src/Compiler/xlf/FSComp.txt.es.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.fr.xlf b/src/Compiler/xlf/FSComp.txt.fr.xlf index 796bfc0e06f..3f60d994b39 100644 --- a/src/Compiler/xlf/FSComp.txt.fr.xlf +++ b/src/Compiler/xlf/FSComp.txt.fr.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.it.xlf b/src/Compiler/xlf/FSComp.txt.it.xlf index 0718fbdc85f..d066f7c4765 100644 --- a/src/Compiler/xlf/FSComp.txt.it.xlf +++ b/src/Compiler/xlf/FSComp.txt.it.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.ja.xlf b/src/Compiler/xlf/FSComp.txt.ja.xlf index 23943c69c90..37b493ac2f7 100644 --- a/src/Compiler/xlf/FSComp.txt.ja.xlf +++ b/src/Compiler/xlf/FSComp.txt.ja.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.ko.xlf b/src/Compiler/xlf/FSComp.txt.ko.xlf index 5e3440cd509..ff2be7514ed 100644 --- a/src/Compiler/xlf/FSComp.txt.ko.xlf +++ b/src/Compiler/xlf/FSComp.txt.ko.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.pl.xlf b/src/Compiler/xlf/FSComp.txt.pl.xlf index 28e546841c4..e26d6657bb9 100644 --- a/src/Compiler/xlf/FSComp.txt.pl.xlf +++ b/src/Compiler/xlf/FSComp.txt.pl.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf index 31f4dcc1bd6..0c26fa7a79d 100644 --- a/src/Compiler/xlf/FSComp.txt.pt-BR.xlf +++ b/src/Compiler/xlf/FSComp.txt.pt-BR.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.ru.xlf b/src/Compiler/xlf/FSComp.txt.ru.xlf index e95f0f5c33d..21200bc7564 100644 --- a/src/Compiler/xlf/FSComp.txt.ru.xlf +++ b/src/Compiler/xlf/FSComp.txt.ru.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.tr.xlf b/src/Compiler/xlf/FSComp.txt.tr.xlf index d862608459b..036800eeebc 100644 --- a/src/Compiler/xlf/FSComp.txt.tr.xlf +++ b/src/Compiler/xlf/FSComp.txt.tr.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf index b7885e5b19b..ed3d23b7dea 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hans.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf index ab82fdd076c..f26c6c2562c 100644 --- a/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/Compiler/xlf/FSComp.txt.zh-Hant.xlf @@ -43,8 +43,8 @@ - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. - '{0}' is part of an anonymous record. Use {{| expr with {1} = ... |}} instead. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. + This expression is an anonymous record, use {{|...|}} instead of {{...}}. diff --git a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs index 1f4907c93f5..438311c83fe 100644 --- a/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs +++ b/tests/FSharp.Compiler.ComponentTests/Conformance/Types/RecordTypes/AnonymousRecords.fs @@ -87,7 +87,8 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 4, Col 52, Line 4, Col 54, "'ff' is part of an anonymous record. Use {| expr with ff = ... |} instead.") + (Error 3578, Line 4, Col 42, Line 4, Col 43, "This expression is an anonymous record, use {|...|} instead of {...}.") + (Error 3578, Line 4, Col 59, Line 4, Col 60, "This expression is an anonymous record, use {|...|} instead of {...}.") ] [] @@ -98,7 +99,8 @@ let t3 (t1: {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 52, Line 2, Col 54, "'ff' is part of an anonymous record. Use {| expr with ff = ... |} instead.") + (Error 3578, Line 2, Col 42, Line 2, Col 43, "This expression is an anonymous record, use {|...|} instead of {...}.") + (Error 3578, Line 2, Col 59, Line 2, Col 60, "This expression is an anonymous record, use {|...|} instead of {...}.") ] [] @@ -109,7 +111,8 @@ let t3 (t1: struct {| gu: string; ff: int |}) = { t1 with ff = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 59, Line 2, Col 61, "'ff' is part of an anonymous record. Use {| expr with ff = ... |} instead.") + (Error 3578, Line 2, Col 49, Line 2, Col 50, "This expression is an anonymous record, use {|...|} instead of {...}.") + (Error 3578, Line 2, Col 66, Line 2, Col 67, "This expression is an anonymous record, use {|...|} instead of {...}.") ] [] @@ -120,9 +123,8 @@ let f (r: {| A: int; C: int |}) = { r with A = 1; B = 2; C = 3 } |> compile |> shouldFail |> withDiagnostics [ - (Error 3578, Line 2, Col 44, Line 2, Col 45, "'A' is part of an anonymous record. Use {| expr with A = ... |} instead.") - (Error 39, Line 2, Col 51, Line 2, Col 52, "The record label 'B' is not defined.") - (Error 3578, Line 2, Col 58, Line 2, Col 59, "'C' is part of an anonymous record. Use {| expr with C = ... |} instead.") + (Error 3578, Line 2, Col 35, Line 2, Col 36, "This expression is an anonymous record, use {|...|} instead of {...}.") + (Error 3578, Line 2, Col 64, Line 2, Col 65, "This expression is an anonymous record, use {|...|} instead of {...}.") ] []