diff --git a/src/fsharp/NameResolution.fs b/src/fsharp/NameResolution.fs index 368b6c3fb03..58b110ad25e 100644 --- a/src/fsharp/NameResolution.fs +++ b/src/fsharp/NameResolution.fs @@ -775,36 +775,108 @@ let AddStaticContentOfTyconRefToNameEnv (g:TcGlobals) (amap: Import.ImportMap) a if amap.g.langVersion.SupportsFeature LanguageFeature.OpenStaticClasses then let ty = generalizedTyconRef tcref let infoReader = InfoReader(g,amap) - let items = - [| let methGroups = - AllMethInfosOfTypeInScope ResultCollectionSettings.AllResults infoReader nenv None ad PreferOverrides m ty - |> List.groupBy (fun m -> m.LogicalName) - - for (methName, methGroup) in methGroups do - let methGroup = methGroup |> List.filter (fun m -> not m.IsInstance && not m.IsClassConstructor) - if not methGroup.IsEmpty then - yield KeyValuePair(methName, Item.MethodGroup(methName, methGroup, None)) - - let propInfos = - AllPropInfosOfTypeInScope ResultCollectionSettings.AllResults infoReader nenv None ad PreferOverrides m ty - |> List.groupBy (fun m -> m.PropertyName) - - for (propName, propInfos) in propInfos do - let propInfos = propInfos |> List.filter (fun m -> m.IsStatic) - for propInfo in propInfos do - yield KeyValuePair(propName , Item.Property(propName,[propInfo])) + + let getMethItems () = + [ let methGroups = + AllMethInfosOfTypeInScope ResultCollectionSettings.AllResults infoReader nenv None AccessorDomain.AccessibleFromSomeFSharpCode PreferOverrides m ty + |> List.groupBy (fun m -> m.LogicalName) + + for (methName, methGroup) in methGroups do + let methGroup = methGroup |> List.filter (fun m -> not m.IsInstance && not m.IsClassConstructor) + if not methGroup.IsEmpty then + yield KeyValuePair(methName, Item.MethodGroup(methName, methGroup, None)) + ] + + let getPropItems () = + [ let propInfos = + AllPropInfosOfTypeInScope ResultCollectionSettings.AllResults infoReader nenv None AccessorDomain.AccessibleFromSomeFSharpCode PreferOverrides m ty + |> List.groupBy (fun m -> m.PropertyName) - let fields = + for (propName, propInfos) in propInfos do + let propInfos = propInfos |> List.filter (fun m -> m.IsStatic) + for propInfo in propInfos do + yield KeyValuePair(propName , Item.Property(propName,[propInfo])) + ] + + let getFieldItems () = + [ let fields = infoReader.GetILFieldInfosOfType(None, ad, m, ty) |> List.groupBy (fun f -> f.FieldName) - for (fieldName, fieldInfos) in fields do - let fieldInfos = fieldInfos |> List.filter (fun fi -> fi.IsStatic) - for fieldInfo in fieldInfos do - yield KeyValuePair(fieldName, Item.ILField(fieldInfo)) - |] + for (fieldName, fieldInfos) in fields do + let fieldInfos = fieldInfos |> List.filter (fun fi -> fi.IsStatic) + for fieldInfo in fieldInfos do + yield KeyValuePair(fieldName, Item.ILField(fieldInfo)) + ] + + let getEventItems () = + [ let events = + infoReader.GetEventInfosOfType(None, ad, m, ty) + |> List.groupBy (fun e -> e.EventName) - { nenv with eUnqualifiedItems = nenv.eUnqualifiedItems.AddAndMarkAsCollapsible items } + for (eventName, eventInfos) in events do + let eventInfos = eventInfos |> List.filter (fun e -> e.IsStatic) + for eventInfo in eventInfos do + yield KeyValuePair(eventName, Item.Event(eventInfo)) + ] + + let items = + [ + let propAndMethGroups = + getPropItems () @ getMethItems () + |> List.groupBy (fun pair -> pair.Key) + + for (_, items) in propAndMethGroups do + // This is to handle the current behavior with qualified extension members: + // Extension properties will shadow all extension methods that share the same name no matter the order in which the extensions are opened from modules. + // TODO: What happens with CSharp-style extension methods? + let sortedItems = + items + |> List.sortBy (fun pair -> + match pair.Value with + | Item.Property (_, [propInfo]) -> not propInfo.IsExtensionMember + | Item.MethodGroup (_, methGroup, _) -> methGroup |> List.exists (fun x -> not x.IsExtensionMember) + | _ -> false) + match sortedItems with + | item :: _ -> yield item + | _ -> () + + yield! getFieldItems () + yield! getEventItems () + ] + + { nenv with + eUnqualifiedItems = + (nenv.eUnqualifiedItems, items) + ||> List.fold (fun x (KeyValue(k, v)) -> + match x.TryGetValue k with + | true, existingItem -> + match existingItem, v with + + // Combine methods groups. This allows overloading on methods from different classes. Mimics C# behavior. + | Item.MethodGroup (_, minfosCurrent, None), Item.MethodGroup (_, minfosNew, None) -> + let minfos = + (minfosNew, minfosCurrent) + ||> List.fold (fun minfos minfo1 -> + // Shadow methods with the same signature. + // F# specific behavior; the order of opening is significant and ambiguities are shadowed. + // In C#, an error would occur and asks the developer to resolve the ambiguity. + // We allow UoM (units of measure) to be significant in overloading here. + if minfosCurrent |> List.exists (fun minfo2 -> MethInfosEquivByNameAndSig Erasure.EraseNone true g amap m minfo1 minfo2) then + minfos + else + minfo1 :: minfos + ) + x.Add(k, Item.MethodGroup (k, minfos, None)) + + // Methods will always shadow properties, events, and fields with the same name; order of opening does not matter in this case. + // This is to mimic the C# behavior of opening multiple static classes. + | Item.MethodGroup _, Item.Property _ + | Item.MethodGroup _, Item.Event _ + | Item.MethodGroup _, Item.ILField _ -> x + | _ -> x.Add(k, v) + | _ -> x.Add(k, v)) + } else nenv