Permalink
Browse files

Fix elaboration of HH autoimport class names when HH syntax is disabled

Summary:
In Hack files, class names which are autoimported from the HH namespace are elaborated into the HH namespace. For example, Container -> \HH\Container; Traversable -> \HH\Traversable. Other class names are elaborated into the current namespace.

In PHP files, we do not autoimport any classes from the HH namespace, and should not elaborate any names into that namespace. However, we currently treat any name which would be autoimported from \HH (in a Hack file) as a name in the global namespace, instead of elaborating it into the current namespace as we do with any other name.

After this change, we always elaborate names into the current namespace in PHP files (unless the EnableHipHopSyntax option is set), matching HHVM's behavior for the added test case.

fixes #8204

Differential Revision: D8100689
  • Loading branch information...
Jake Bailey (Hacklang) authored and fredemmott committed May 24, 2018
1 parent 1574fa1 commit dd720bec27cdc90ba56c3f6b3fc6f3af7a38efa7
@@ -2784,7 +2784,10 @@ let scour_comments
* Front-end matter
)*****************************************************************************)
let elaborate_toplevel_and_std_constants ast env source_text =
let elaborate_toplevel_and_std_constants ast (env: env) source_text =
let autoimport =
env.is_hh_file || ParserOptions.enable_hh_syntax_for_hhvm env.parser_options
in
match env.elaborate_namespaces, env.saw_std_constant_redefinition with
| true, true ->
let elaborate_std_constants nsenv def =
@@ -2808,9 +2811,10 @@ let elaborate_toplevel_and_std_constants ast env source_text =
end in
visitor#on_def nsenv def in
let parser_options = env.parser_options in
NS.elaborate_map_toplevel_defs parser_options ast elaborate_std_constants
NS.elaborate_map_toplevel_defs
~autoimport parser_options ast elaborate_std_constants
| true, false ->
NS.elaborate_toplevel_defs env.parser_options ast
NS.elaborate_toplevel_defs ~autoimport env.parser_options ast
| _ -> ast
let elaborate_halt_compiler ast env source_text =
@@ -260,8 +260,8 @@ let elaborate_id_impl ~autoimport nsenv kind (p, id) =
(ParserOptions.auto_namespace_map nsenv.ns_popt) fully_qualified in
was_renamed, (p, translated)
let elaborate_id nsenv kind id =
let _, newid = elaborate_id_impl ~autoimport:true nsenv kind id in
let elaborate_id ?(autoimport=true) nsenv kind id =
let _, newid = elaborate_id_impl ~autoimport nsenv kind id in
newid
(* First pass of flattening namespaces, run super early in the pipeline, right
@@ -278,22 +278,22 @@ let elaborate_id nsenv kind id =
* allow us to fix those up during a second pass during naming.
*)
module ElaborateDefs = struct
let hint nsenv = function
let hint ~autoimport nsenv = function
| p, Happly (id, args) ->
p, Happly (elaborate_id nsenv ElaborateClass id, args)
p, Happly (elaborate_id ~autoimport nsenv ElaborateClass id, args)
| other -> other
let class_def nsenv = function
| ClassUse h -> ClassUse (hint nsenv h)
| XhpAttrUse h -> XhpAttrUse (hint nsenv h)
let class_def ~autoimport nsenv = function
| ClassUse h -> ClassUse (hint ~autoimport nsenv h)
| XhpAttrUse h -> XhpAttrUse (hint ~autoimport nsenv h)
| other -> other
let finish nsenv updated_nsenv stmt =
if updated_nsenv
then nsenv, [stmt; SetNamespaceEnv nsenv]
else nsenv, [stmt]
let rec def map_def nsenv = function
let rec def ~autoimport map_def nsenv = function
(*
The default namespace in php is the global namespace specified by
the empty string. In the case of an empty string, we model it as
@@ -311,7 +311,7 @@ module ElaborateDefs = struct
| "" -> None
| _ -> Some (parent_nsname ^ nsname) in
let new_nsenv = {nsenv with ns_name = nsname} in
nsenv, SetNamespaceEnv new_nsenv :: program map_def new_nsenv prog
nsenv, SetNamespaceEnv new_nsenv :: program ~autoimport map_def new_nsenv prog
end
| NamespaceUse l -> begin
let nsenv =
@@ -346,9 +346,9 @@ module ElaborateDefs = struct
elaborate_defined_id nsenv ElaborateClass c.c_name in
finish nsenv updated_nsenv @@ map_def nsenv (Class {c with
c_name = name;
c_extends = List.map c.c_extends (hint nsenv);
c_implements = List.map c.c_implements (hint nsenv);
c_body = List.map c.c_body (class_def nsenv);
c_extends = List.map c.c_extends (hint ~autoimport nsenv);
c_implements = List.map c.c_implements (hint ~autoimport nsenv);
c_body = List.map c.c_body (class_def ~autoimport nsenv);
c_namespace = nsenv;
})
| Fun f ->
@@ -386,25 +386,25 @@ module ElaborateDefs = struct
}]
| other -> nsenv, [map_def nsenv other]
and program f nsenv p =
and program ~autoimport f nsenv p =
let _, acc =
List.fold_left p ~init:(nsenv, []) ~f:begin fun (nsenv, acc) item ->
let nsenv, item = def f nsenv item in
let nsenv, item = def ~autoimport f nsenv item in
nsenv, item :: acc
end in
List.concat (List.rev acc)
end
let noop _ x = x
let elaborate_toplevel_defs_ ?(map_def = noop) popt ast =
ElaborateDefs.program map_def (Namespace_env.empty popt) ast
let elaborate_toplevel_defs_ ~autoimport ?(map_def = noop) popt ast =
ElaborateDefs.program ~autoimport map_def (Namespace_env.empty popt) ast
let elaborate_toplevel_defs popt ast =
elaborate_toplevel_defs_ popt ast
let elaborate_toplevel_defs ~autoimport popt ast =
elaborate_toplevel_defs_ ~autoimport popt ast
let elaborate_map_toplevel_defs popt ast map_def =
elaborate_toplevel_defs_ ~map_def popt ast
let elaborate_map_toplevel_defs ~autoimport popt ast map_def =
elaborate_toplevel_defs_ ~autoimport ~map_def popt ast
let elaborate_def nsenv def =
ElaborateDefs.def noop nsenv def
ElaborateDefs.def ~autoimport:true noop nsenv def
@@ -12,7 +12,7 @@ type elaborate_kind =
| ElaborateClass
| ElaborateConst
val elaborate_id : Namespace_env.env ->
val elaborate_id : ?autoimport:bool -> Namespace_env.env ->
elaborate_kind ->
Ast.id ->
Ast.id
@@ -26,8 +26,9 @@ val elaborate_id_impl : autoimport:bool -> Namespace_env.env ->
incur a perf hit that everybody will have to pay. For codegen purposed
namespaces are propagated to inline declarations
during closure conversion process *)
val elaborate_toplevel_defs : ParserOptions.t -> Ast.program -> Ast.program
val elaborate_toplevel_defs : autoimport:bool -> ParserOptions.t -> Ast.program -> Ast.program
val elaborate_map_toplevel_defs :
autoimport:bool ->
ParserOptions.t ->
Ast.program ->
(Namespace_env.env -> Ast.def -> Ast.def) ->
@@ -481,8 +481,10 @@ let rec program
Fixmes.HH_FIXMES.add env.file fixmes;
Option.iter (List.last !(env.errors)) Errors.parsing_error
end;
let autoimport =
is_hh_file || ParserOptions.enable_hh_syntax_for_hhvm env.popt in
let ast = if elaborate_namespaces
then Namespaces.elaborate_toplevel_defs env.popt ast
then Namespaces.elaborate_toplevel_defs ~autoimport env.popt ast
else ast in
{file_mode; is_hh_file; comments; ast; content}
@@ -0,0 +1,20 @@
<?php
namespace AutoimportTest;
include 'autoimport_defs.inc';
class ContainerExt extends Container {}
class TraversableExt extends Traversable {}
$a = new Container;
var_dump(get_class($a));
$a = new ContainerExt;
var_dump(get_class($a));
$a = new Traversable;
var_dump(get_class($a));
$a = new TraversableExt;
var_dump(get_class($a));
@@ -0,0 +1,4 @@
string(24) "AutoimportTest\Container"
string(27) "AutoimportTest\ContainerExt"
string(26) "AutoimportTest\Traversable"
string(29) "AutoimportTest\TraversableExt"
@@ -0,0 +1,6 @@
<?php
namespace AutoimportTest;
class Container {}
class Traversable {}

0 comments on commit dd720be

Please sign in to comment.