Permalink
Browse files

Correctly parse Hack constant declarations

Summary:
I added two new predicates. One checks for the absence of an initializer in a concrete const declaration, and the other for the presence of one in an abstract const declaration.

Correspondingly, I added two new syntax errors, 2049 and 2050, respectively.

Examples:
    class C {
      const int i; // error 2049
    }

    abstract class D {
      abstract const int i = 4; // error 2050
    }

Reviewed By: ericlippert

Differential Revision: D5705720

fbshipit-source-id: 5e98319ea75b5481905ce202a2cab9bfb78eec50
  • Loading branch information...
Henry Swanson authored and hhvm-bot committed Aug 29, 2017
1 parent d576f75 commit 9ccec7cf7f6abdb4cb48308eb43d064ff924f399
@@ -1081,6 +1081,9 @@ module WithExpressionAndStatementAndTypeParser
Note that if this logic is changed, it should be changed in
is_type_in_const above as well.
*)
(* This permits abstract variables to have an initializer, and vice-versa.
This is deliberate, and those errors will be detected after the syntax
tree is created. *)
let (parser, const_name) = require_name_allow_keywords parser in
let (parser, initializer_) = parse_simple_initializer_opt parser in
(parser, make_constant_declarator const_name initializer_)
@@ -572,6 +572,43 @@ let is_invalid_list_expression le_node parents =
| _ :: _ :: p3 :: _ when is_list_expression p3 -> false
| _ -> true (* All other deployments of list_expression are invalid *)
(* Given a node, checks if it is a concrete ConstDeclaration *)
let is_concrete_const declaration =
match syntax declaration with
| ConstDeclaration x -> is_missing x.const_abstract
| _ -> false
(* Given a node, checks if it is a abstract ConstDeclaration *)
let is_abstract_const declaration =
match syntax declaration with
| ConstDeclaration x -> not (is_missing x.const_abstract)
| _ -> false
(* Given a ConstDeclarator node, test whether it is concrete, but has no
initializer. *)
let concrete_no_initializer cd_node parents =
let is_concrete =
match parents with
| _ :: _ :: p3 :: _ when is_concrete_const p3 -> true
| _ -> false
in
let has_no_initializer =
is_missing cd_node.constant_declarator_initializer in
is_concrete && has_no_initializer
(* Given a ConstDeclarator node, test whether it is abstract, but has an
initializer. *)
let abstract_with_initializer cd_node parents =
let is_abstract =
match parents with
| _ :: _ :: p3 :: _ when is_abstract_const p3 -> true
| _ -> false
in
let has_initializer =
not (is_missing cd_node.constant_declarator_initializer) in
is_abstract && has_initializer
let methodish_errors node parents =
match syntax node with
(* TODO how to narrow the range of error *)
@@ -802,7 +839,7 @@ let type_errors node parents is_strict =
match syntax node with
| SimpleTypeSpecifier t ->
let acc = [ ] in
produce_error acc (type_contains_array_in_strict is_strict)
produce_error acc (type_contains_array_in_strict is_strict)
t.simple_type_specifier SyntaxError.error2032 t.simple_type_specifier
| _ -> [ ]
@@ -837,6 +874,19 @@ let group_use_errors node =
SyntaxError.error2048 prefix
| _ -> [ ]
let const_decl_errors node parents =
match syntax node with
| ConstantDeclarator cd ->
let errors = [ ] in
let errors =
produce_error_parents errors concrete_no_initializer cd parents
SyntaxError.error2050 cd.constant_declarator_initializer in
let errors =
produce_error_parents errors abstract_with_initializer cd parents
SyntaxError.error2051 cd.constant_declarator_initializer in
errors
| _ -> [ ]
let find_syntax_errors node is_strict is_hack =
let folder acc node parents =
let param_errs = parameter_errors node parents is_strict in
@@ -851,10 +901,11 @@ let find_syntax_errors node is_strict is_hack =
let type_errors = type_errors node parents is_strict in
let alias_errors = alias_errors node in
let group_use_errors = group_use_errors node in
let const_decl_errors = const_decl_errors node parents in
let errors = acc.errors @ param_errs @ func_errs @
xhp_errs @ statement_errs @ methodish_errs @ property_errs @
expr_errs @ require_errs @ classish_errors @ type_errors @ alias_errors @
group_use_errors in
group_use_errors @ const_decl_errors in
{ errors } in
let acc = SyntaxUtilities.parented_fold_pre folder { errors = [] } node in
List.sort SyntaxError.compare acc.errors
@@ -187,3 +187,5 @@ let error2047 visibility_modifier = "Methods inside of interfaces may not be " ^
"marked '" ^ visibility_modifier ^ "'; only 'public' visibility is allowed."
let error2048 = "Expected group use prefix to end with '\\'"
let error2049 = "A namespace use clause may not specify the kind here."
let error2050 = "A concrete constant declaration must have an initializer."
let error2051 = "An abstract constant declaration must not have an initializer."
@@ -0,0 +1,3 @@
(8,15)-(8,15) A concrete constant declaration must have an initializer.
(16,25)-(16,27) An abstract constant declaration must not have an initializer.
(24,15)-(24,15) A concrete constant declaration must have an initializer.
@@ -0,0 +1,25 @@
<?hh //strict
class ConcreteRight {
const int i1 = 4;
}
class ConcreteWrong {
const int i1;
}
abstract class AbstractRight {
abstract const int i1;
}
abstract class AbstractWrong {
abstract const int i1 = 3;
}
abstract class MixedRight {
const int i1 = 5;
}
abstract class MixedWrong {
const int i1;
}
@@ -209,6 +209,7 @@ let error_tests =
"test_misspelling_recovery";
"test_misspelling_recovery2";
"test_group_use_errors";
"test_abstract_initializers";
] ~f:mapper
let test_data = minimal_tests @ error_tests @

0 comments on commit 9ccec7c

Please sign in to comment.