Skip to content

Commit 042ac9f

Browse files
dlreevesfacebook-github-bot
authored andcommitted
Support top-level function in interpreter
Summary: This adds support for top-level functions. This combined with the other diffs in the stack allow for a nicer syntax for calling promptlets so it resembles top level function calls, i.e. ``` <<__SimpliHack(DeriveBuilderFor(Person::class))>> ``` # Implementation I evaluate `Id()` expressions to `FunctionPtr` values which wraps `Nast.fun_def`. I then defined a converter to a `Value.callable` which contains the common fields for `Nast.fun_` and `Nast.method_` that is used by `Call.eval` Reviewed By: patriciamckenzie Differential Revision: D72283936 fbshipit-source-id: 39fe53a6cf4468c94c59241318a645ea6771fef5
1 parent 0fecd7e commit 042ac9f

File tree

3 files changed

+96
-40
lines changed

3 files changed

+96
-40
lines changed

hphp/hack/src/client/ide_service/code_actions_services/simplihack/simplihack_interpreter.ml

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ open Hh_prelude
1212
module Value = struct
1313
type t =
1414
| Str of string (** String values *)
15-
| ClassPtr of Nast.class_ (** References to class definitions *)
16-
| MethodPtr of Nast.method_ (** References to method definitions *)
1715
| Error (** Represents evaluation errors *)
1816

1917
(* Helper functions to safely extract values from the Value.t type.
@@ -25,19 +23,6 @@ module Value = struct
2523
let str = function
2624
| Str s -> Some s
2725
| _ -> None
28-
29-
let str_ish = function
30-
| Str s -> Some s
31-
| ClassPtr c -> Some (Utils.strip_ns @@ snd c.Aast.c_name)
32-
| _ -> None
33-
34-
let class_ptr = function
35-
| ClassPtr c -> Some c
36-
| _ -> None
37-
38-
let method_ptr = function
39-
| MethodPtr m -> Some m
40-
| _ -> None
4126
end
4227

4328
(* Context module maintains the interpreter's state during evaluation *)
@@ -123,6 +108,24 @@ module Context = struct
123108
let find_method ~ctx:_ class_ name =
124109
List.find class_.Aast.c_methods ~f:(fun meth ->
125110
String.equal (snd meth.Aast.m_name) name)
111+
112+
(** [find_function ~ctx name] looks up a function definition by name using the context's provider
113+
Returns [None] if the function is not found
114+
115+
Example:
116+
{[
117+
match Context.find_function ~ctx "myFunction" with
118+
| Some func_def -> (* Use function definition *)
119+
| None -> (* Handle missing function *)
120+
]}
121+
*)
122+
let find_function ~ctx name =
123+
let open Option.Let_syntax in
124+
let* path = Naming_provider.get_fun_path ctx.provider name in
125+
let* fun_def =
126+
Ast_provider.find_fun_in_file ~full:true ctx.provider path name
127+
in
128+
return @@ Naming.fun_def ctx.provider fun_def
126129
end
127130

128131
(* ControlFlow module represents the possible outcomes of statement evaluation *)
@@ -157,28 +160,18 @@ end = struct
157160
~init:(Some "")
158161
~f:(fun acc e ->
159162
let* lhs = acc in
160-
let* rhs = Value.str_ish @@ expr ctx e in
163+
let* rhs = Value.str @@ expr ctx e in
161164
return @@ lhs ^ rhs)
162165
exprs
163166
in
164167
return @@ Value.Str value
168+
| Aast.Nameof (_, _, Aast.CI (_, name)) -> return @@ Value.Str name
165169
| Aast.Lvar (_, name) ->
166170
(* Looks up value of a local variable *)
167171
Context.load ~ctx name
168-
| Aast.Class_const (cid, (_, id))
169-
when String.equal id Naming_special_names.Members.mClass ->
170-
(* Handles class constant access for getting class pointer *)
171-
let* class_ = ClassId.eval ctx cid in
172-
return @@ Value.ClassPtr class_
173-
| Aast.Class_const (cid, (_, id)) ->
174-
(* Handles method reference through class constant access *)
175-
let* class_ = ClassId.eval ctx cid in
176-
let* meth = Context.find_method ~ctx class_ id in
177-
return @@ Value.MethodPtr meth
178172
| Aast.(Call { func; targs = _; args; unpacked_arg = None }) ->
179173
(* Evaluates a function/method call *)
180-
let* meth = Value.method_ptr @@ expr ctx func in
181-
Call.eval ctx meth args
174+
Call.eval ctx func args
182175
| Aast.(Binop { bop; lhs; rhs }) ->
183176
(* Handles binary operations *)
184177
Binop.eval ctx bop lhs rhs
@@ -239,21 +232,20 @@ end = struct
239232

240233
let class_id (ctx : Context.t) (ClassId cid) =
241234
match cid with
242-
| Aast.(CIparent | CIself | CIstatic) ->
235+
| Aast.(CIparent | CIself | CIstatic | CIexpr _) ->
243236
None (* Special class refs not supported *)
244237
| Aast.CI (_, name) -> Context.find_class ~ctx name (* Named class *)
245-
| Aast.CIexpr e -> Value.class_ptr @@ Expr.eval ctx e (* Class expression *)
246238

247239
let eval (ctx : Context.t) (_, _, cid) = class_id ctx (ClassId cid)
248240
end
249241

250242
(* Call module handles function/method calls *)
251243
and Call : sig
252244
val eval :
253-
Context.t -> Nast.method_ -> (_, _) Aast.argument list -> Value.t option
245+
Context.t -> (_, _) Aast.expr -> (_, _) Aast.argument list -> Value.t option
254246
end = struct
255247
(* Build new context for function call with arguments *)
256-
let build_ctx ~ctx func args =
248+
let build_ctx ~ctx params args =
257249
let eval = Expr.eval ctx in
258250
let rec go args params ctx =
259251
match (args, params) with
@@ -262,15 +254,33 @@ end = struct
262254
@@ Context.store ~ctx (Local_id.make_unscoped param_name) (eval arg)
263255
| _ -> ctx
264256
in
265-
go args func.Aast.m_params @@ Context.fresh ctx
257+
go args params @@ Context.fresh ctx
258+
259+
let invoke ctx (params, body) args =
260+
let ctx = build_ctx ~ctx params args in
261+
let { Aast.fb_ast } = body in
262+
Block.eval ctx fb_ast
266263

267264
(* Evaluate function call *)
268265
let eval
269-
(ctx : Context.t) (func : Nast.method_) (args : (_, _) Aast.argument list)
270-
=
271-
let ctx = build_ctx ~ctx func args in
272-
let { Aast.fb_ast } = func.Aast.m_body in
273-
Block.eval ctx fb_ast
266+
(ctx : Context.t)
267+
(func : (_, _) Aast.expr)
268+
(args : (_, _) Aast.argument list) =
269+
let open Option.Let_syntax in
270+
let (_, _, func) = func in
271+
match func with
272+
| Aast.Id (_, name) ->
273+
let* Aast.{ fd_fun = { f_params = params; f_body = body; _ }; _ } =
274+
Context.find_function ~ctx name
275+
in
276+
invoke ctx (params, body) args
277+
| Aast.Class_const (cid, (_, name)) ->
278+
let* class_ = ClassId.eval ctx cid in
279+
let* Aast.{ m_params = params; m_body = body; _ } =
280+
Context.find_method ~ctx class_ name
281+
in
282+
invoke ctx (params, body) args
283+
| _ -> None
274284
end
275285

276286
(* Binop module handles binary operations *)
@@ -286,8 +296,8 @@ end = struct
286296
match bop with
287297
| Ast_defs.Dot ->
288298
let open Option.Let_syntax in
289-
let* lhs_str = Value.str_ish @@ Expr.eval ctx lhs_expr in
290-
let* rhs_str = Value.str_ish @@ Expr.eval ctx rhs_expr in
299+
let* lhs_str = Value.str @@ Expr.eval ctx lhs_expr in
300+
let* rhs_str = Value.str @@ Expr.eval ctx rhs_expr in
291301
Some (Value.Str (lhs_str ^ rhs_str))
292302
| _ -> None
293303
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?hh
2+
<<__SimpliHack(deriveGetters())>>
3+
// ^ at-caret
4+
class SomeClass {
5+
public int $one;
6+
public string $word;
7+
public function foo(): void {}
8+
}
9+
10+
function deriveGetters(): string {
11+
return "generate getters for all fields";
12+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Code actions available:
2+
------------------------------------------
3+
Generate Code for SimpliHack (CodeActionKind: "") SELECTED
4+
5+
JSON for selected code action:
6+
------------------------------------------
7+
{
8+
"command":{
9+
"arguments":[
10+
{
11+
"entrypoint":"HandleUserAttributeCodeAction",
12+
"extras":{},
13+
"overrideSelection":{"end":{"character":1,"line":7},"start":{"character":0,"line":1}},
14+
"predefinedPrompt":{
15+
"command":"SimpliHack",
16+
"description":"Handle __SimpliHack attribute",
17+
"displayPrompt":"generate getters for all fields",
18+
"model":"iCodeLlama 3.1 70B",
19+
"userPrompt":" Edit <selection_to_edit> in the following way:\n generate getters for all fields\n\n Do exactly as instructed above. Do not make any assumptions on what to do unless specifically instructed to do so.\n "
20+
},
21+
"webviewStartLine":1
22+
}
23+
],
24+
"command":"code-compose.show-inline-chat",
25+
"title":"Generate Code for SimpliHack"
26+
},
27+
"data":{"isAI":true},
28+
"diagnostics":[],
29+
"edit":{"changes":{}},
30+
"kind":"",
31+
"title":"Generate Code for SimpliHack"
32+
}
33+
34+
The code action edit was a no-op

0 commit comments

Comments
 (0)