Browse Source

fixed fields of @:structInit on completion inside of `{}` (fixes #9159)

Aleksandr Kuzmenko 5 years ago
parent
commit
87ceafc689
3 changed files with 96 additions and 1 deletions
  1. 51 0
      src/typing/fields.ml
  2. 1 1
      src/typing/typerDisplay.ml
  3. 44 0
      tests/server/src/DisplayTests.hx

+ 51 - 0
src/typing/fields.ml

@@ -623,3 +623,54 @@ let rec type_field cfg ctx e i p mode =
 		try using_field ctx mode e i p with Not_found -> no_field()
 
 let type_field_default_cfg = type_field TypeFieldConfig.default
+
+(**
+	Generates a list of fields for `@:structInit` class `c` with type params `tl`
+	as it's needed for anonymous object syntax.
+*)
+let get_struct_init_anon_fields c tl =
+	let args =
+		match c.cl_constructor with
+		| Some cf ->
+			(match follow cf.cf_type with
+			| TFun (args,_) ->
+				Some (match cf.cf_expr with
+					| Some { eexpr = TFunction fn } ->
+						List.map (fun (name,_,t) ->
+							let t = apply_params c.cl_params tl t in
+							try
+								let v,_ = List.find (fun (v,_) -> v.v_name = name) fn.tf_args in
+								name,t,v.v_pos
+							with Not_found ->
+								name,t,cf.cf_name_pos
+						) args
+					| _ ->
+						List.map
+							(fun (name,_,t) ->
+								let t = apply_params c.cl_params tl t in
+								try
+									let cf = PMap.find name c.cl_fields in
+									name,t,cf.cf_name_pos
+								with Not_found ->
+									name,t,cf.cf_name_pos
+							) args
+				)
+			| _ -> None
+			)
+		| _ -> None
+	in
+	match args with
+	| Some args ->
+		List.fold_left (fun fields (name,t,p) ->
+			let cf = mk_field name t p p in
+			PMap.add cf.cf_name cf fields
+		) PMap.empty args
+	| _ ->
+		PMap.fold (fun cf fields ->
+		match cf.cf_kind with
+		| Var _ ->
+			let cf = {cf with cf_type = apply_params c.cl_params tl cf.cf_type} in
+			PMap.add cf.cf_name cf fields
+		| _ ->
+			fields
+	) c.cl_fields PMap.empty

+ 1 - 1
src/typing/typerDisplay.ml

@@ -630,7 +630,7 @@ let handle_edisplay ?resume_typing ctx e dk with_type =
 						in
 						handle_structure_display ctx e an.a_fields origin
 					| TInst(c,tl) when Meta.has Meta.StructInit c.cl_meta ->
-						let fields = PMap.map (fun cf -> {cf with cf_type = apply_params c.cl_params tl cf.cf_type}) c.cl_fields in
+						let fields = get_struct_init_anon_fields c tl in
 						handle_structure_display ctx e fields (Self (TClassDecl c))
 					| _ -> handle_display ctx e dk with_type
 				end

+ 44 - 0
tests/server/src/DisplayTests.hx

@@ -468,4 +468,48 @@ typedef Foo = {
 		Assert.equals('Null<String>', strType(sigs[0].args[0].t));
 		Assert.equals('Null<Int>', strType(sigs[1].args[0].t));
 	}
+
+	function testIssue9159() {
+		var content = '
+			@:structInit
+			class CustomConstructor {
+				public var nope1:String;
+				public function new(x:Int = 0) {}
+				public function nope2() {}
+			}
+
+			@:structInit
+			class AutoConstructor {
+				public var y:Float;
+				public function nope() {}
+			}
+
+			class Main {
+				static function main() {
+					var a:CustomConstructor = {-1-}{};
+					var b:AutoConstructor = {-2-}{};
+				}
+			}
+		';
+		var transform = Marker.extractMarkers(content);
+		vfs.putContent("Main.hx", transform.source);
+
+		runHaxeJson([], DisplayMethods.Completion, {
+			file: new FsPath("Main.hx"),
+			offset: transform.markers[1],
+			wasAutoTriggered: true
+		});
+		var result = parseCompletion().result;
+		Assert.equals(1, result.items.length);
+		Assert.equals('x', result.items[0].args.field.name);
+
+		runHaxeJson([], DisplayMethods.Completion, {
+			file: new FsPath("Main.hx"),
+			offset: transform.markers[2],
+			wasAutoTriggered: true
+		});
+		var result = parseCompletion().result;
+		Assert.equals(1, result.items.length);
+		Assert.equals('y', result.items[0].args.field.name);
+	}
 }