diff --git a/src/fsharp/NameResolution.fs b/src/fsharp/NameResolution.fs index d085460b95e..50587d55eab 100644 --- a/src/fsharp/NameResolution.fs +++ b/src/fsharp/NameResolution.fs @@ -1909,6 +1909,19 @@ let DecodeFSharpEvent (pinfos:PropInfo list) ad g (ncenv:NameResolver) m = | _ -> None +/// Returns all record label names for the given type. +let GetRecordLabelsForType g nenv typ = + if isRecdTy g typ then + let typeName = NicePrint.minimalStringOfType nenv.eDisplayEnv typ + nenv.eFieldLabels + |> Seq.filter (fun kv -> + kv.Value + |> List.map (fun r -> r.TyconRef.DisplayName) + |> List.exists ((=) typeName)) + |> Seq.map (fun kv -> kv.Key) + |> Set.ofSeq + else + Set.empty // REVIEW: this shows up on performance logs. Consider for example endless resolutions of "List.map" to // the empty set of results, or "x.Length" for a list or array type. This indicates it could be worth adding a cache here. @@ -2011,12 +2024,25 @@ let rec ResolveLongIdentInTypePrim (ncenv:NameResolver) nenv lookupKind (resInfo |> List.filter (fun m -> not m.IsClassConstructor && not m.IsConstructor) |> List.map (fun m -> m.DisplayName) |> Set.ofList - - + let suggestions5 = GetRecordLabelsForType g nenv typ + let suggestions6 = + match lookupKind with + | LookupKind.Expr | LookupKind.Pattern -> + if isAppTy g typ then + let tcref,_ = destAppTy g typ + tcref.UnionCasesArray + |> Array.map (fun uc -> uc.DisplayName) + |> Set.ofArray + else + Set.empty + | _ -> Set.empty + suggestions1 |> Set.union suggestions2 |> Set.union suggestions3 |> Set.union suggestions4 + |> Set.union suggestions5 + |> Set.union suggestions6 raze (UndefinedName (depth,FSComp.SR.undefinedNameFieldConstructorOrMember, id, suggestMembers)) @@ -2140,8 +2166,21 @@ let rec ResolveExprLongIdentInModuleOrNamespace (ncenv:NameResolver) nenv (typeN |> Seq.filter (fun kv -> IsEntityAccessible ncenv.amap m ad (modref.NestedTyconRef kv.Value)) |> Seq.map (fun e -> e.Value.DisplayName) |> Set.ofSeq + + let unions = + modref.ModuleOrNamespaceType.AllEntities + |> Seq.collect (fun tycon -> + let hasRequireQualifiedAccessAttribute = HasFSharpAttribute ncenv.g ncenv.g.attrib_RequireQualifiedAccessAttribute tycon.Attribs + if hasRequireQualifiedAccessAttribute then + [||] + else + tycon.UnionCasesArray) + |> Seq.map (fun uc -> uc.DisplayName) + |> Set.ofSeq - Set.union types submodules // TODO: Add unions + types + |> Set.union submodules + |> Set.union unions | _ -> Set.empty raze (UndefinedName(depth,FSComp.SR.undefinedNameValueConstructorNamespaceOrType,id,suggestPossibleTypes)) @@ -2757,15 +2796,8 @@ let rec ResolveFieldInModuleOrNamespace (ncenv:NameResolver) nenv ad (resInfo:Re error(InternalError("ResolveFieldInModuleOrNamespace",m)) /// Suggest other labels of the same record -let SuggestOtherLabelsOfSameRecordType (nenv:NameResolutionEnv) typeName (id:Ident) (allFields:Ident list) = - let labelsOfPossibleRecord = - nenv.eFieldLabels - |> Seq.filter (fun kv -> - kv.Value - |> List.map (fun r -> r.TyconRef.DisplayName) - |> List.exists ((=) typeName)) - |> Seq.map (fun kv -> kv.Key) - |> Set.ofSeq +let SuggestOtherLabelsOfSameRecordType g (nenv:NameResolutionEnv) typ (id:Ident) (allFields:Ident list) = + let labelsOfPossibleRecord = GetRecordLabelsForType g nenv typ let givenFields = allFields @@ -2830,10 +2862,10 @@ let ResolveFieldPrim (ncenv:NameResolver) nenv ad typ (mp,id:Ident) allFields = match ncenv.InfoReader.TryFindRecdOrClassFieldInfoOfType(id.idText,m,typ) with | Some (RecdFieldInfo(_,rfref)) -> [ResolutionInfo.Empty, FieldResolution(rfref,false)] | None -> - let typeName = NicePrint.minimalStringOfType nenv.eDisplayEnv typ if isRecdTy g typ then // record label doesn't belong to record type -> suggest other labels of same record - let suggestLabels() = SuggestOtherLabelsOfSameRecordType nenv typeName id allFields + let suggestLabels() = SuggestOtherLabelsOfSameRecordType g nenv typ id allFields + let typeName = NicePrint.minimalStringOfType nenv.eDisplayEnv typ let errorText = FSComp.SR.nrRecordDoesNotContainSuchLabel(typeName,id.idText) error(ErrorWithSuggestions(errorText, m, id.idText, suggestLabels)) else diff --git a/tests/fsharp/typecheck/sigs/neg01.bsl b/tests/fsharp/typecheck/sigs/neg01.bsl index a0c3aec5bb4..069e0862725 100644 --- a/tests/fsharp/typecheck/sigs/neg01.bsl +++ b/tests/fsharp/typecheck/sigs/neg01.bsl @@ -3,4 +3,4 @@ neg01a.fsi(24,8,25,7): typecheck error FS0913: Types cannot contain nested type neg01a.fs(22,8,23,7): typecheck error FS0913: Types cannot contain nested type definitions -neg01b.fs(2,13,2,14): typecheck error FS0039: The value, constructor, namespace or type 'X' is not defined. Maybe you want one of the following: fieldsInWrongOrder, missingConstructorInSignature, missingFieldInSignature, missingInterfaceInImplementation, missingInterfaceInSignature +neg01b.fs(2,13,2,14): typecheck error FS0039: The value, constructor, namespace or type 'X' is not defined. Maybe you want one of the following: A, B, fieldsInWrongOrder, missingConstructorInSignature, missingFieldInSignature diff --git a/tests/fsharp/typecheck/sigs/neg14.bsl b/tests/fsharp/typecheck/sigs/neg14.bsl index d677a803339..224682fff16 100644 --- a/tests/fsharp/typecheck/sigs/neg14.bsl +++ b/tests/fsharp/typecheck/sigs/neg14.bsl @@ -1,4 +1,4 @@ neg14a.fs(9,6,9,33): typecheck error FS0343: The type 'missingInterfaceInSignature' implements 'System.IComparable' explicitly but provides no corresponding override for 'Object.Equals'. An implementation of 'Object.Equals' has been automatically provided, implemented via 'System.IComparable'. Consider implementing the override 'Object.Equals' explicitly -neg14b.fs(2,13,2,14): typecheck error FS0039: The value, constructor, namespace or type 'X' is not defined. Maybe you want one of the following: fieldsInWrongOrder, missingConstructorInSignature, missingFieldInSignature, missingInterfaceInImplementation, missingInterfaceInSignature +neg14b.fs(2,13,2,14): typecheck error FS0039: The value, constructor, namespace or type 'X' is not defined. Maybe you want one of the following: A, B, fieldsInWrongOrder, missingConstructorInSignature, missingFieldInSignature diff --git a/tests/fsharp/typecheck/sigs/neg15.bsl b/tests/fsharp/typecheck/sigs/neg15.bsl index 0726e5b5314..d7419af9bb5 100644 --- a/tests/fsharp/typecheck/sigs/neg15.bsl +++ b/tests/fsharp/typecheck/sigs/neg15.bsl @@ -29,11 +29,11 @@ neg15.fs(115,19,115,48): typecheck error FS0072: Lookup on object of indetermina neg15.fs(116,20,116,73): typecheck error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. -neg15.fs(122,32,122,57): typecheck error FS0039: The value, constructor, namespace or type 'InternalTagOfInternalType' is not defined. Maybe you want one of the following: InternalUnionType, InternalRecordType, UnionTypeWithPrivateRepresentation +neg15.fs(122,32,122,57): typecheck error FS0039: The value, constructor, namespace or type 'InternalTagOfInternalType' is not defined. Maybe you want one of the following: InternalUnionType, InternalRecordType, DefaultTagOfInternalType, DefaultTagOfPrivateType, UnionTypeWithPrivateRepresentation neg15.fs(128,31,128,61): typecheck error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. -neg15.fs(135,31,135,56): typecheck error FS0039: The value, constructor, namespace or type 'InternalTagOfInternalType' is not defined. Maybe you want one of the following: InternalUnionType, InternalRecordType, UnionTypeWithPrivateRepresentation +neg15.fs(135,31,135,56): typecheck error FS0039: The value, constructor, namespace or type 'InternalTagOfInternalType' is not defined. Maybe you want one of the following: InternalUnionType, InternalRecordType, DefaultTagOfInternalType, DefaultTagOfPrivateType, UnionTypeWithPrivateRepresentation neg15.fs(141,30,141,60): typecheck error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. diff --git a/tests/fsharp/typecheck/sigs/neg17.bsl b/tests/fsharp/typecheck/sigs/neg17.bsl index dd78a8273b6..cddb2c38d61 100644 --- a/tests/fsharp/typecheck/sigs/neg17.bsl +++ b/tests/fsharp/typecheck/sigs/neg17.bsl @@ -21,7 +21,7 @@ neg17b.fs(16,19,16,48): typecheck error FS0072: Lookup on object of indeterminat neg17b.fs(17,19,17,47): typecheck error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. -neg17b.fs(21,31,21,77): typecheck error FS0039: The value, constructor, namespace or type 'DefaultTagOfUnionTypeWithPrivateRepresentation' is not defined. Maybe you want one of the following: UnionTypeWithPrivateRepresentation, RecordTypeWithPrivateRepresentation +neg17b.fs(21,31,21,77): typecheck error FS0039: The value, constructor, namespace or type 'DefaultTagOfUnionTypeWithPrivateRepresentation' is not defined. Maybe you want one of the following: DefaultTagOfInternalType, DefaultTagOfPrivateType, UnionTypeWithPrivateRepresentation, RecordTypeWithPrivateRepresentation neg17b.fs(29,31,29,61): typecheck error FS0072: Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved. diff --git a/tests/fsharpqa/Source/Warnings/SuggestRecordLabels.fs b/tests/fsharpqa/Source/Warnings/SuggestRecordLabels.fs new file mode 100644 index 00000000000..5bb65ee9da5 --- /dev/null +++ b/tests/fsharpqa/Source/Warnings/SuggestRecordLabels.fs @@ -0,0 +1,10 @@ +// #Warnings +//The field, constructor or member 'ello' is not defined. Maybe you want one of the following: Hello + +type MyRecord = { Hello: int; World: bool} + +let r = { Hello = 2 ; World = true} + +let x = r.ello + +exit 0 \ No newline at end of file diff --git a/tests/fsharpqa/Source/Warnings/SuggestUnionCases.fs b/tests/fsharpqa/Source/Warnings/SuggestUnionCases.fs new file mode 100644 index 00000000000..edc0c3819fb --- /dev/null +++ b/tests/fsharpqa/Source/Warnings/SuggestUnionCases.fs @@ -0,0 +1,11 @@ +// #Warnings +//The field, constructor or member 'AntherCase' is not defined. Maybe you want one of the following: AnotherCase + +type MyUnion = +| ASimpleCase +| AnotherCase of int + +let u = MyUnion.AntherCase + + +exit 0 \ No newline at end of file diff --git a/tests/fsharpqa/Source/Warnings/env.lst b/tests/fsharpqa/Source/Warnings/env.lst index 55bbfc61675..5a35a41373a 100644 --- a/tests/fsharpqa/Source/Warnings/env.lst +++ b/tests/fsharpqa/Source/Warnings/env.lst @@ -28,6 +28,8 @@ SOURCE=SuggestModules.fs # SuggestModules.fs SOURCE=SuggestMethods.fs # SuggestMethods.fs SOURCE=SuggestAttributes.fs # SuggestAttributes.fs + SOURCE=SuggestRecordLabels.fs SCFLAGS="--vserrors" # SuggestRecordLabels.fs + SOURCE=SuggestUnionCases.fs SCFLAGS="--vserrors" # SuggestUnionCases.fs SOURCE=SuggestTypesInNamespace.fs # SuggestTypesInNamespace.fs SOURCE=DontSuggestCompletelyWrongStuff.fs SCFLAGS="--vserrors" # DontSuggestCompletelyWrongStuff.fs SOURCE=SuggestTypesInNamespaceVS.fs SCFLAGS="--vserrors" # SuggestTypesInNamespaceVS.fs