瀏覽代碼

Use property types to infer accessor types (#10569)

* [typer] use property types to infer accessor types

see #10568

* fix test
Simon Krajewski 3 年之前
父節點
當前提交
add042a117

+ 3 - 3
src/typing/functionArguments.ml

@@ -54,15 +54,15 @@ let type_function_arg_value ctx t c do_display =
 
 
 class function_arguments
 class function_arguments
 	(ctx : typer)
 	(ctx : typer)
-	(type_arg : bool -> type_hint option -> pos -> Type.t)
+	(type_arg : int -> bool -> type_hint option -> pos -> Type.t)
 	(is_extern : bool)
 	(is_extern : bool)
 	(do_display : bool)
 	(do_display : bool)
 	(abstract_this : Type.t option)
 	(abstract_this : Type.t option)
 	(syntax : (placed_name * bool * metadata * type_hint option * expr option) list)
 	(syntax : (placed_name * bool * metadata * type_hint option * expr option) list)
 =
 =
 	let with_default =
 	let with_default =
-		let l = List.map (fun ((name,pn),opt,m,t,eo) ->
-			let t = type_arg opt t pn in
+		let l = List.mapi (fun i ((name,pn),opt,m,t,eo) ->
+			let t = type_arg i opt t pn in
 			let t,eo = type_function_arg ctx t eo opt pn in
 			let t,eo = type_function_arg ctx t eo opt pn in
 			(name,eo,t)
 			(name,eo,t)
 		) syntax in
 		) syntax in

+ 54 - 5
src/typing/typeloadFields.ml

@@ -85,6 +85,11 @@ type field_init_ctx = {
 	mutable expr_presence_matters : bool;
 	mutable expr_presence_matters : bool;
 }
 }
 
 
+type method_kind =
+	| MKNormal
+	| MKGetter
+	| MKSetter
+
 let dump_class_context cctx =
 let dump_class_context cctx =
 	Printer.s_record_fields "" [
 	Printer.s_record_fields "" [
 		"tclass",Printer.s_tclass "\t" cctx.tclass;
 		"tclass",Printer.s_tclass "\t" cctx.tclass;
@@ -1169,7 +1174,8 @@ let check_abstract (ctx,cctx,fctx) c cf fd t ret p =
 			()
 			()
 
 
 let create_method (ctx,cctx,fctx) c f fd p =
 let create_method (ctx,cctx,fctx) c f fd p =
-	let params = TypeloadFunction.type_function_params ctx fd (fst f.cff_name) p in
+	let name = fst f.cff_name in
+	let params = TypeloadFunction.type_function_params ctx fd name p in
 	if fctx.is_generic then begin
 	if fctx.is_generic then begin
 		if params = [] then typing_error (fst f.cff_name ^ ": Generic functions must have type parameters") p;
 		if params = [] then typing_error (fst f.cff_name ^ ": Generic functions must have type parameters") p;
 	end;
 	end;
@@ -1231,7 +1237,7 @@ let create_method (ctx,cctx,fctx) c f fd p =
 		| false,_ ->
 		| false,_ ->
 			()
 			()
 	end;
 	end;
-	let parent = (if not fctx.is_static then get_parent c (fst f.cff_name) else None) in
+	let parent = (if not fctx.is_static then get_parent c name else None) in
 	let dynamic = List.mem_assoc ADynamic f.cff_access || (match parent with Some { cf_kind = Method MethDynamic } -> true | _ -> false) in
 	let dynamic = List.mem_assoc ADynamic f.cff_access || (match parent with Some { cf_kind = Method MethDynamic } -> true | _ -> false) in
 	if fctx.is_abstract && dynamic then display_error ctx "Abstract methods may not be dynamic" p;
 	if fctx.is_abstract && dynamic then display_error ctx "Abstract methods may not be dynamic" p;
 	if fctx.is_inline && dynamic then typing_error (fst f.cff_name ^ ": 'inline' is not allowed on 'dynamic' functions") p;
 	if fctx.is_inline && dynamic then typing_error (fst f.cff_name ^ ": 'inline' is not allowed on 'dynamic' functions") p;
@@ -1240,7 +1246,45 @@ let create_method (ctx,cctx,fctx) c f fd p =
 
 
 	ctx.type_params <- if fctx.is_static && not fctx.is_abstract_member then params else params @ ctx.type_params;
 	ctx.type_params <- if fctx.is_static && not fctx.is_abstract_member then params else params @ ctx.type_params;
 	(* TODO is_lib: avoid forcing the return type to be typed *)
 	(* TODO is_lib: avoid forcing the return type to be typed *)
-	let ret = if fctx.field_kind = FKConstructor then ctx.t.tvoid else FunctionArguments.type_opt ctx cctx.is_core_api fctx.is_abstract p fd.f_type in
+	let mk = lazy (
+		if String.length name < 4 then
+			MKNormal
+		else match String.sub name 0 4 with
+		| "get_" ->
+			begin match fd.f_args with
+			| [] -> MKGetter
+			| _ -> MKNormal
+			end
+		| "set_" ->
+			begin match fd.f_args with
+			| [_] -> MKSetter
+			| _ -> MKNormal
+			end
+		| _ ->
+			MKNormal
+	) in
+	let try_find_property_type () =
+		let name = String.sub name 4 (String.length name - 4) in
+		let cf = if fctx.is_static then PMap.find name c.cl_statics else PMap.find name c.cl_fields (* TODO: inheritance? *) in
+		cf.cf_type
+	in
+	let maybe_use_property_type th check def =
+		if th = None && check() then
+			try
+				try_find_property_type()
+			with Not_found ->
+				def()
+		else
+			def()
+	in
+	let ret = if fctx.field_kind = FKConstructor then
+		ctx.t.tvoid
+	else begin
+		let def () =
+			FunctionArguments.type_opt ctx cctx.is_core_api fctx.is_abstract p fd.f_type
+		in
+		maybe_use_property_type fd.f_type (fun () -> match Lazy.force mk with MKGetter | MKSetter -> true | _ -> false) def
+	end in
 	let abstract_this = match cctx.abstract with
 	let abstract_this = match cctx.abstract with
 		| Some a when fctx.is_abstract_member && fst f.cff_name <> "_new" (* TODO: this sucks *) && not fctx.is_macro ->
 		| Some a when fctx.is_abstract_member && fst f.cff_name <> "_new" (* TODO: this sucks *) && not fctx.is_macro ->
 			Some a.a_this
 			Some a.a_this
@@ -1248,11 +1292,16 @@ let create_method (ctx,cctx,fctx) c f fd p =
 			None
 			None
 	in
 	in
 	let is_extern = fctx.is_extern || has_class_flag ctx.curclass CExtern in
 	let is_extern = fctx.is_extern || has_class_flag ctx.curclass CExtern in
-	let type_arg opt t p = FunctionArguments.type_opt ctx cctx.is_core_api fctx.is_abstract p t in
+	let type_arg i opt cto p =
+		let def () =
+			FunctionArguments.type_opt ctx cctx.is_core_api fctx.is_abstract p cto
+		in
+		if i = 0 then maybe_use_property_type cto (fun () -> match Lazy.force mk with MKSetter -> true | _ -> false) def else def()
+	in
 	let args = new FunctionArguments.function_arguments ctx type_arg is_extern fctx.is_display_field abstract_this fd.f_args in
 	let args = new FunctionArguments.function_arguments ctx type_arg is_extern fctx.is_display_field abstract_this fd.f_args in
 	let t = TFun (args#for_type,ret) in
 	let t = TFun (args#for_type,ret) in
 	let cf = {
 	let cf = {
-		(mk_field (fst f.cff_name) ~public:(is_public (ctx,cctx) f.cff_access parent) t f.cff_pos (pos f.cff_name)) with
+		(mk_field name ~public:(is_public (ctx,cctx) f.cff_access parent) t f.cff_pos (pos f.cff_name)) with
 		cf_doc = f.cff_doc;
 		cf_doc = f.cff_doc;
 		cf_meta = f.cff_meta;
 		cf_meta = f.cff_meta;
 		cf_kind = Method (if fctx.is_macro then MethMacro else if fctx.is_inline then MethInline else if dynamic then MethDynamic else MethNormal);
 		cf_kind = Method (if fctx.is_macro then MethMacro else if fctx.is_inline then MethInline else if dynamic then MethDynamic else MethNormal);

+ 1 - 1
src/typing/typer.ml

@@ -1194,7 +1194,7 @@ and type_local_function ctx kind f with_type p =
 	ctx.type_params <- params @ ctx.type_params;
 	ctx.type_params <- params @ ctx.type_params;
 	if not inline then ctx.in_loop <- false;
 	if not inline then ctx.in_loop <- false;
 	let rt = Typeload.load_type_hint ctx p f.f_type in
 	let rt = Typeload.load_type_hint ctx p f.f_type in
-	let type_arg opt t p = Typeload.load_type_hint ~opt ctx p t in
+	let type_arg _ opt t p = Typeload.load_type_hint ~opt ctx p t in
 	let args = new FunctionArguments.function_arguments ctx type_arg false ctx.in_display None f.f_args in
 	let args = new FunctionArguments.function_arguments ctx type_arg false ctx.in_display None f.f_args in
 	let targs = args#for_type in
 	let targs = args#for_type in
 	(match with_type with
 	(match with_type with

+ 2 - 2
tests/nullsafety/src/cases/TestStrict.hx

@@ -134,8 +134,8 @@ class TestStrict {
 	function get_str() {
 	function get_str() {
 		shouldFail(return (null:Null<String>));
 		shouldFail(return (null:Null<String>));
 	}
 	}
-	function set_str(v) {
-		shouldFail(return (v:Null<String>));
+	function set_str(v:Null<String>) {
+		shouldFail(return v);
 	}
 	}
 
 
 	/**
 	/**

+ 56 - 0
tests/unit/src/unit/issues/Issue10568.hx

@@ -0,0 +1,56 @@
+package unit.issues;
+
+private abstract A(String) {
+	var selected(get, set):Dynamic;
+
+	public function get_selected() {
+		return null;
+	}
+
+	public function set_selected(v) {
+		return null;
+	}
+
+	public function new() {
+		this = "";
+	}
+}
+
+private class C {
+	var selected(get, set):Dynamic;
+
+	public function get_selected() {
+		return null;
+	}
+
+	public function set_selected(v) {
+		return null;
+	}
+
+	public function new() {}
+}
+
+class Issue10568 extends Test {
+	static var selected(get, set):Dynamic;
+
+	static public function get_selected() {
+		return null;
+	}
+
+	static public function set_selected(v) {
+		return null;
+	}
+
+	function test() {
+		HelperMacros.typedAs(get_selected, (null : () -> Dynamic));
+		HelperMacros.typedAs(set_selected, (null : (v:Dynamic) -> Dynamic));
+
+		var c = new C();
+		HelperMacros.typedAs(c.get_selected, (null : () -> Dynamic));
+		HelperMacros.typedAs(c.set_selected, (null : (v:Dynamic) -> Dynamic));
+
+		var a = new A();
+		HelperMacros.typedAs(a.get_selected, (null : () -> Dynamic));
+		HelperMacros.typedAs(a.set_selected, (null : (v:Dynamic) -> Dynamic));
+	}
+}