From ba37e5006f94c822ccb70bdcecb06152d4f4ad0a Mon Sep 17 00:00:00 2001 From: RblSb Date: Tue, 10 Jun 2025 07:47:30 +0300 Subject: [PATCH 1/4] Class meta to inherit doc for all class fields --- src-json/meta.json | 6 +++++ src/core/inheritDoc.ml | 46 +++++++++++++++++++++++++++--------- src/typing/typeloadFields.ml | 5 ++-- 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src-json/meta.json b/src-json/meta.json index 5c236f1d950..daf1ce37f07 100644 --- a/src-json/meta.json +++ b/src-json/meta.json @@ -489,6 +489,12 @@ "doc": "Append documentation from a parent field or class (if used without an argument) or from a specified class or field (if used like @:inheritDoc(pack.Some.field)).", "targets": ["TClass", "TEnum", "TAbstract", "TAnyField"] }, + { + "name": "InheritDocFields", + "metadata": ":inheritDocFields", + "doc": "Adds `@:inheritDoc` metadata to all class fields that don't have their own documentation.", + "targets": ["TClass"] + }, { "name": "InitPackage", "metadata": ":initPackage", diff --git a/src/core/inheritDoc.ml b/src/core/inheritDoc.ml index 3c96ae0f619..3e1128d142d 100644 --- a/src/core/inheritDoc.ml +++ b/src/core/inheritDoc.ml @@ -111,25 +111,49 @@ and build_abstract_doc ctx a = (** Populates `doc_inherited` field of `cf.cf_doc` *) -and build_class_field_doc ctx c_opt cf = +and build_class_field_doc ctx c_opt ?(inherit_fields = false) cf = (match cf.cf_doc with | None | Some { doc_inherited = [] } -> () | Some d -> d.doc_inherited <- [] ); let doc = ref cf.cf_doc in let no_args_cb add = + let rec find_in_parents classes = + match classes with + | [] -> () + | cl :: rest -> + try + let parent_cl, parent_cf = + if cf.cf_name = "new" then get_constructor cl + else get_class_field cl cf.cf_name + in + build_class_field_doc ctx parent_cl parent_cf; + add parent_cf.cf_doc + with Not_found -> find_in_parents rest + in match c_opt with - | Some { cl_super = Some (csup,_) } -> - (try - let c_opt, cf_sup = - if cf.cf_name = "new" then get_constructor csup - else get_class_field csup cf.cf_name - in - build_class_field_doc ctx c_opt cf_sup; - add cf_sup.cf_doc - with Not_found -> ()) - | _ -> () + | Some c -> + let interfaces = List.map (fun (cl, _) -> cl) c.cl_implements in + begin match c.cl_super with + | Some (csup, _) -> find_in_parents (csup :: interfaces) + | None -> find_in_parents interfaces + end + | None -> () in + (* + If class has `@:InheritDocFields`, add `@:InheritDoc` to all class fields + to get default doc from class parents + *) + if (inherit_fields) then begin + (* Do not inherit doc from parent field if there is own doc or `@:inheritDoc` on field *) + let has_own_doc = match cf.cf_doc with + | Some doc -> Option.is_some doc.doc_own + | None -> false + in + let has_cf_meta = Meta.has Meta.InheritDoc cf.cf_meta in + if (not has_own_doc && not has_cf_meta) then + cf.cf_meta <- (Meta.InheritDoc,[],Globals.null_pos) :: cf.cf_meta; + end; build_doc ctx ~no_args_cb doc cf.cf_meta; cf.cf_doc <- !doc diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index f003230ff23..fb9376114d0 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -1557,8 +1557,9 @@ let init_field (ctx,cctx,fctx) f cf = create_property (ctx,cctx,fctx) c f cf (get,set,t,eo) p in (if (fctx.is_static || fctx.is_macro && ctx.com.is_macro_context) then add_class_field_flag cf CfStatic); - if Meta.has Meta.InheritDoc cf.cf_meta then - delay ctx.g PTypeField (fun() -> InheritDoc.build_class_field_doc ctx (Some c) cf) + let inherit_fields = Meta.has Meta.InheritDocFields c.cl_meta in + if Meta.has Meta.InheritDoc cf.cf_meta || inherit_fields then + delay ctx.g PTypeField (fun() -> InheritDoc.build_class_field_doc ctx (Some c) ~inherit_fields:inherit_fields cf) let check_overload ctx f fs is_extern_class = try From 2f233905199dc0aa1962c9c85d2505502a5ddd30 Mon Sep 17 00:00:00 2001 From: RblSb Date: Wed, 11 Jun 2025 10:38:37 +0300 Subject: [PATCH 2/4] Only support for finding doc in interfaces too --- src-json/meta.json | 6 ------ src/core/inheritDoc.ml | 22 +++++----------------- src/typing/typeloadFields.ml | 5 ++--- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/src-json/meta.json b/src-json/meta.json index daf1ce37f07..5c236f1d950 100644 --- a/src-json/meta.json +++ b/src-json/meta.json @@ -489,12 +489,6 @@ "doc": "Append documentation from a parent field or class (if used without an argument) or from a specified class or field (if used like @:inheritDoc(pack.Some.field)).", "targets": ["TClass", "TEnum", "TAbstract", "TAnyField"] }, - { - "name": "InheritDocFields", - "metadata": ":inheritDocFields", - "doc": "Adds `@:inheritDoc` metadata to all class fields that don't have their own documentation.", - "targets": ["TClass"] - }, { "name": "InitPackage", "metadata": ":initPackage", diff --git a/src/core/inheritDoc.ml b/src/core/inheritDoc.ml index 3e1128d142d..740a98463c8 100644 --- a/src/core/inheritDoc.ml +++ b/src/core/inheritDoc.ml @@ -111,7 +111,7 @@ and build_abstract_doc ctx a = (** Populates `doc_inherited` field of `cf.cf_doc` *) -and build_class_field_doc ctx c_opt ?(inherit_fields = false) cf = +and build_class_field_doc ctx c_opt cf = (match cf.cf_doc with | None | Some { doc_inherited = [] } -> () | Some d -> d.doc_inherited <- [] @@ -127,8 +127,10 @@ and build_class_field_doc ctx c_opt ?(inherit_fields = false) cf = if cf.cf_name = "new" then get_constructor cl else get_class_field cl cf.cf_name in - build_class_field_doc ctx parent_cl parent_cf; - add parent_cf.cf_doc + if Option.is_some parent_cf.cf_doc then begin + build_class_field_doc ctx parent_cl parent_cf; + add parent_cf.cf_doc + end else raise Not_found with Not_found -> find_in_parents rest in match c_opt with @@ -140,20 +142,6 @@ and build_class_field_doc ctx c_opt ?(inherit_fields = false) cf = end | None -> () in - (* - If class has `@:InheritDocFields`, add `@:InheritDoc` to all class fields - to get default doc from class parents - *) - if (inherit_fields) then begin - (* Do not inherit doc from parent field if there is own doc or `@:inheritDoc` on field *) - let has_own_doc = match cf.cf_doc with - | Some doc -> Option.is_some doc.doc_own - | None -> false - in - let has_cf_meta = Meta.has Meta.InheritDoc cf.cf_meta in - if (not has_own_doc && not has_cf_meta) then - cf.cf_meta <- (Meta.InheritDoc,[],Globals.null_pos) :: cf.cf_meta; - end; build_doc ctx ~no_args_cb doc cf.cf_meta; cf.cf_doc <- !doc diff --git a/src/typing/typeloadFields.ml b/src/typing/typeloadFields.ml index fb9376114d0..f003230ff23 100644 --- a/src/typing/typeloadFields.ml +++ b/src/typing/typeloadFields.ml @@ -1557,9 +1557,8 @@ let init_field (ctx,cctx,fctx) f cf = create_property (ctx,cctx,fctx) c f cf (get,set,t,eo) p in (if (fctx.is_static || fctx.is_macro && ctx.com.is_macro_context) then add_class_field_flag cf CfStatic); - let inherit_fields = Meta.has Meta.InheritDocFields c.cl_meta in - if Meta.has Meta.InheritDoc cf.cf_meta || inherit_fields then - delay ctx.g PTypeField (fun() -> InheritDoc.build_class_field_doc ctx (Some c) ~inherit_fields:inherit_fields cf) + if Meta.has Meta.InheritDoc cf.cf_meta then + delay ctx.g PTypeField (fun() -> InheritDoc.build_class_field_doc ctx (Some c) cf) let check_overload ctx f fs is_extern_class = try From dbe6214a3130ef50588dc7903d49566596eda49c Mon Sep 17 00:00:00 2001 From: RblSb Date: Wed, 11 Jun 2025 10:59:32 +0300 Subject: [PATCH 3/4] check if no doc after building --- src/core/inheritDoc.ml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/inheritDoc.ml b/src/core/inheritDoc.ml index 740a98463c8..9539a068d64 100644 --- a/src/core/inheritDoc.ml +++ b/src/core/inheritDoc.ml @@ -127,10 +127,9 @@ and build_class_field_doc ctx c_opt cf = if cf.cf_name = "new" then get_constructor cl else get_class_field cl cf.cf_name in - if Option.is_some parent_cf.cf_doc then begin - build_class_field_doc ctx parent_cl parent_cf; - add parent_cf.cf_doc - end else raise Not_found + build_class_field_doc ctx parent_cl parent_cf; + add parent_cf.cf_doc; + if Option.is_none parent_cf.cf_doc then raise Not_found with Not_found -> find_in_parents rest in match c_opt with From 84373508cb9f6c131590db7ab2d9626b2ad78ecf Mon Sep 17 00:00:00 2001 From: RblSb Date: Wed, 11 Jun 2025 12:23:56 +0300 Subject: [PATCH 4/4] test --- src/core/inheritDoc.ml | 2 +- tests/server/src/cases/display/InheritDoc.hx | 39 +++++++++++++++++- .../server/test/templates/InheritDocTypes.hx | 41 ++++++++++++++++++- 3 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/core/inheritDoc.ml b/src/core/inheritDoc.ml index 9539a068d64..fe4052f6b86 100644 --- a/src/core/inheritDoc.ml +++ b/src/core/inheritDoc.ml @@ -134,7 +134,7 @@ and build_class_field_doc ctx c_opt cf = in match c_opt with | Some c -> - let interfaces = List.map (fun (cl, _) -> cl) c.cl_implements in + let interfaces = List.rev (List.map (fun (cl, _) -> cl) c.cl_implements) in begin match c.cl_super with | Some (csup, _) -> find_in_parents (csup :: interfaces) | None -> find_in_parents interfaces diff --git a/tests/server/src/cases/display/InheritDoc.hx b/tests/server/src/cases/display/InheritDoc.hx index e74c409e68c..8cd859ccc21 100644 --- a/tests/server/src/cases/display/InheritDoc.hx +++ b/tests/server/src/cases/display/InheritDoc.hx @@ -9,6 +9,15 @@ class InheritDoc extends DisplayTestCase { var c = new Chi{-1-}ld(); c.te{-2-}st(); Child.tes{-3-}t2(); + + final foo = new Foo(); + foo.te{-4-}st(); + final foo2 = new Foo2(); + foo2.te{-5-}st(); + final foo3 = new Foo3(); + foo3.te{-6-}st(); + final foo3inv = new Foo3Inv(); + foo3inv.te{-7-}st(); } } **/ @@ -35,5 +44,33 @@ class InheritDoc extends DisplayTestCase { }); var result = parseHover(); Assert.equals(' Child field 2 doc \n unrelated field doc ', result.result.item.args.field.doc); + + runHaxeJson([], DisplayMethods.Hover, { + file: file, + offset: offset(4) + }); + var result = parseHover(); + Assert.equals(' Foo doc \n GrandParent field doc ', result.result.item.args.field.doc); + + runHaxeJson([], DisplayMethods.Hover, { + file: file, + offset: offset(5) + }); + var result = parseHover(); + Assert.equals(' Foo doc \n IFoo doc ', result.result.item.args.field.doc); + + runHaxeJson([], DisplayMethods.Hover, { + file: file, + offset: offset(6) + }); + var result = parseHover(); + Assert.equals(' IFoo doc ', result.result.item.args.field.doc); + + runHaxeJson([], DisplayMethods.Hover, { + file: file, + offset: offset(7) + }); + var result = parseHover(); + Assert.equals(' IFoo doc ', result.result.item.args.field.doc); } -} \ No newline at end of file +} diff --git a/tests/server/test/templates/InheritDocTypes.hx b/tests/server/test/templates/InheritDocTypes.hx index 2047dceab12..36f88978cfc 100644 --- a/tests/server/test/templates/InheritDocTypes.hx +++ b/tests/server/test/templates/InheritDocTypes.hx @@ -16,6 +16,7 @@ class Parent extends GrandParent { class Child extends Parent { /** Child field doc **/ @:inheritDoc override public function test() {} + /** Child field 2 doc **/ @:inheritDoc(InheritDocTypes.Unrelated.unrelated) static public function test2() {} @@ -25,4 +26,42 @@ class Child extends Parent { class Unrelated { /** unrelated field doc */ static public function unrelated() {} -} \ No newline at end of file +} + +class Foo implements IFoo implements IFoo2 extends Parent { + /** Foo doc **/ + @:inheritDoc override public function test():Void {} +} + +class Foo2 implements IFoo implements IFoo2 { + public function new() {} + + /** Foo doc **/ + @:inheritDoc public function test():Void {} +} + +class Foo3 implements IFoo implements IEmptyFoo { + public function new() {} + + @:inheritDoc public function test():Void {} +} + +class Foo3Inv implements IEmptyFoo implements IFoo { + public function new() {} + + @:inheritDoc public function test():Void {} +} + +interface IEmptyFoo { + function test():Void; +} + +interface IFoo { + /** IFoo doc **/ + function test():Void; +} + +interface IFoo2 { + /** IFoo2 doc **/ + function test():Void; +}