1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use itertools::Itertools;
7 use lazy_static::lazy_static;
9 use std::collections::{BTreeMap, HashMap, HashSet};
10 use std::str::FromStr;
12 use strum::IntoEnumIterator;
13 use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
15 use naming_special_names_rust as sn;
17 use oxidized::parser_options::ParserOptions;
18 use parser_core_types::{
19 indexed_source_text::IndexedSourceText,
20 lexable_token::LexableToken,
21 positioned_syntax::PositionedSyntax,
22 syntax::{ListItemChildren, Syntax, SyntaxValueType, SyntaxVariant, SyntaxVariant::*},
23 syntax_error::{self as errors, Error, ErrorType, SyntaxError},
24 syntax_trait::SyntaxTrait,
25 syntax_tree::SyntaxTree,
26 token_kind::TokenKind,
29 use hh_autoimport_rust as hh_autoimport;
31 #[derive(Clone, PartialEq, Debug)]
37 #[derive(Clone, PartialEq, Debug)]
39 NameUse, // `use` construct
40 NameDef, // definition e.g. `class` or `trait`
41 NameImplicitUse, // implicit `use` e.g. HH type in type hint
48 LvalTypeNonFinalInout,
53 enum BinopAllowsAwaitInPositions {
71 #[strum(serialize_all = "snake_case")]
72 enum UnstableFeatures {
73 UnionIntersectionTypeHints,
77 use BinopAllowsAwaitInPositions::*;
80 #[derive(Clone, Debug)]
81 struct FirstUseOrDef {
92 Unbracketed(Location),
97 // TODO: is there a more Rust idiomatic way to write this?
98 #[derive(Clone, Debug)]
100 YesCase(HashMap<String, X>),
101 NoCase(HashMap<String, X>),
105 fn mem(&self, k: &str) -> bool {
107 NoCase(m) => m.contains_key(&k.to_ascii_lowercase()),
108 YesCase(m) => m.contains_key(k),
112 fn add(&mut self, k: &str, v: X) {
114 NoCase(m) => m.insert(k.to_ascii_lowercase(), v),
115 YesCase(m) => m.insert(k.to_string(), v),
119 fn get(&self, k: &str) -> Option<&X> {
121 NoCase(m) => m.get(&k.to_ascii_lowercase()),
122 YesCase(m) => m.get(k),
126 fn filter<F>(self, f: F) -> Self
131 NoCase(m) => NoCase(m.into_iter().filter(|(_, x)| f(x)).collect()),
132 YesCase(m) => YesCase(m.into_iter().filter(|(_, x)| f(x)).collect()),
136 fn into_iter(self) -> std::collections::hash_map::IntoIter<String, X> {
138 NoCase(m) => m.into_iter(),
139 YesCase(m) => m.into_iter(),
144 use crate::Strmap::*;
146 fn empty_trait_require_clauses() -> Strmap<TokenKind> {
147 NoCase(HashMap::new())
150 #[derive(Clone, Debug)]
152 classes: Strmap<FirstUseOrDef>, // NoCase
153 namespaces: Strmap<FirstUseOrDef>, // NoCase
154 functions: Strmap<FirstUseOrDef>, // NoCase
155 constants: Strmap<FirstUseOrDef>, // YesCase
156 attributes: Strmap<FirstUseOrDef>, // YesCase
162 classes: NoCase(HashMap::new()),
163 namespaces: NoCase(HashMap::new()),
164 functions: NoCase(HashMap::new()),
165 constants: YesCase(HashMap::new()),
166 attributes: YesCase(HashMap::new()),
171 struct Context<'a, Syntax> {
172 pub active_classish: Option<&'a Syntax>,
173 pub active_methodish: Option<&'a Syntax>,
174 pub active_callable: Option<&'a Syntax>,
175 pub active_callable_attr_spec: Option<&'a Syntax>,
176 // true if active callable is reactive if it is a function or method, or there is a reactive
177 // proper ancestor (including lambdas) but not beyond the enclosing function or method
178 pub active_is_rx_or_enclosing_for_lambdas: bool,
179 pub active_const: Option<&'a Syntax>,
180 pub active_unstable_features: HashSet<UnstableFeatures>,
183 // TODO: why can't this be auto-derived?
184 impl<'a, Syntax> std::clone::Clone for Context<'a, Syntax> {
185 fn clone(&self) -> Self {
187 active_classish: self.active_classish,
188 active_methodish: self.active_methodish,
189 active_callable: self.active_callable,
190 active_callable_attr_spec: self.active_callable_attr_spec,
191 active_is_rx_or_enclosing_for_lambdas: self.active_is_rx_or_enclosing_for_lambdas,
192 active_const: self.active_const,
193 active_unstable_features: self.active_unstable_features.clone(),
198 struct Env<'a, Syntax, SyntaxTree> {
199 parser_options: ParserOptions,
200 syntax_tree: &'a SyntaxTree,
201 text: IndexedSourceText<'a>,
202 context: Context<'a, Syntax>,
203 hhvm_compat_mode: bool,
208 impl<'a, Syntax, SyntaxTree> Env<'a, Syntax, SyntaxTree> {
209 fn is_hhvm_compat(&self) -> bool {
210 self.hhvm_compat_mode
213 fn is_typechecker(&self) -> bool {
217 fn is_hhi_mode(&self) -> bool {
222 const GLOBAL_NAMESPACE_NAME: &str = "\\";
224 fn combine_names(n1: &str, n2: &str) -> String {
225 let has_leading_slash = n2.starts_with('\\');
226 let has_trailing_slash = n1.ends_with('\\');
227 match (has_leading_slash, has_trailing_slash) {
228 (true, true) => n1.to_string() + &n2[1..],
229 (false, false) => n1.to_string() + "\\" + n2,
230 _ => n1.to_string() + n2,
234 fn make_first_use_or_def(
238 namespace_name: &str,
241 let res = FirstUseOrDef {
244 name: combine_names(namespace_name, name),
245 global: !is_method && namespace_name == GLOBAL_NAMESPACE_NAME,
250 struct ParserErrors<'a, Token, Value, State> {
251 phantom: std::marker::PhantomData<(*const Token, *const Value, *const State)>,
253 env: Env<'a, Syntax<Token, Value>, SyntaxTree<'a, Syntax<Token, Value>, State>>,
254 errors: Vec<SyntaxError>,
255 parents: Vec<&'a Syntax<Token, Value>>,
257 trait_require_clauses: Strmap<TokenKind>,
258 is_in_concurrent_block: bool,
260 // Named (not anonymous) namespaces that the current expression is enclosed within.
261 nested_namespaces: Vec<&'a Syntax<Token, Value>>,
262 namespace_type: NamespaceType,
263 namespace_name: String,
266 // TODO: why do we need :'a everywhere?
267 impl<'a, Token: 'a, Value: 'a, State: 'a> ParserErrors<'a, Token, Value, State>
269 Syntax<Token, Value>: SyntaxTrait,
270 Token: LexableToken<'a> + std::fmt::Debug,
271 Value: SyntaxValueType<Token> + std::fmt::Debug,
275 env: Env<'a, Syntax<Token, Value>, SyntaxTree<'a, Syntax<Token, Value>, State>>,
281 names: UsedNames::empty(),
282 trait_require_clauses: empty_trait_require_clauses(),
283 namespace_name: GLOBAL_NAMESPACE_NAME.to_string(),
284 namespace_type: Unspecified,
285 is_in_concurrent_block: false,
286 nested_namespaces: vec![],
287 phantom: std::marker::PhantomData,
291 fn text(&self, node: &'a Syntax<Token, Value>) -> &'a str {
292 node.extract_text(self.env.syntax_tree.text())
293 .expect("<text_extraction_failure>")
296 fn make_location(s: &'a Syntax<Token, Value>, e: &'a Syntax<Token, Value>) -> Location {
297 let start_offset = Self::start_offset(s);
298 let end_offset = Self::end_offset(e);
305 fn make_location_of_node(n: &'a Syntax<Token, Value>) -> Location {
306 Self::make_location(n, n)
309 fn start_offset(n: &'a Syntax<Token, Value>) -> usize {
310 // TODO: this logic should be moved to SyntaxTrait::position, when implemented
311 n.leading_start_offset() + n.leading_width()
314 fn end_offset(n: &'a Syntax<Token, Value>) -> usize {
315 // TODO: this logic should be moved to SyntaxTrait::position, when implemented
317 n.leading_start_offset() + n.leading_width() + w
320 fn get_short_name_from_qualified_name<'b>(name: &'b str, alias: &'b str) -> &'b str {
321 if !alias.is_empty() {
324 match name.rfind('\\') {
325 Some(i) => &name[i + 1..],
330 // Turns a syntax node into a list of nodes; if it is a separated syntax
331 // list then the separators are filtered from the resulting list.
333 include_separators: bool,
334 node: &'a Syntax<Token, Value>,
335 ) -> impl DoubleEndedIterator<Item = &'a Syntax<Token, Value>> {
336 use itertools::Either::{Left, Right};
337 use std::iter::{empty, once};
338 let on_list_item = move |x: &'a ListItemChildren<Token, Value>| {
339 if include_separators {
340 vec![&x.list_item, &x.list_separator].into_iter()
342 vec![&x.list_item].into_iter()
346 Missing => Left(Left(empty())),
347 SyntaxList(s) => Left(Right(
349 .map(move |x| match &x.syntax {
350 ListItem(x) => Left(on_list_item(x)),
355 ListItem(x) => Right(Left(on_list_item(x))),
356 _ => Right(Right(once(node))),
360 fn syntax_to_list_no_separators(
361 node: &'a Syntax<Token, Value>,
362 ) -> impl DoubleEndedIterator<Item = &'a Syntax<Token, Value>> {
363 Self::syntax_to_list(false, node)
366 fn syntax_to_list_with_separators(
367 node: &'a Syntax<Token, Value>,
368 ) -> impl DoubleEndedIterator<Item = &'a Syntax<Token, Value>> {
369 Self::syntax_to_list(true, node)
372 fn assert_last_in_list<F>(
374 node: &'a Syntax<Token, Value>,
375 ) -> Option<&'a Syntax<Token, Value>>
377 F: Fn(&'a Syntax<Token, Value>) -> bool,
379 let mut iter = Self::syntax_to_list_no_separators(node);
381 iter.find(|x| assert_fun(*x))
384 fn enable_unstable_feature(
386 _node: &'a Syntax<Token, Value>,
387 arg: &'a Syntax<Token, Value>,
389 let error_invalid_argument = |self_: &mut Self, message| {
390 self_.errors.push(Self::make_error_from_node(
392 errors::invalid_use_of_enable_unstable_feature(message),
397 LiteralExpression(x) => {
398 let text = self.text(&x.literal_expression);
399 match UnstableFeatures::from_str(escaper::unquote_str(text)) {
401 self.env.context.active_unstable_features.insert(feature);
403 Err(_) => error_invalid_argument(
406 "there is no feature named {}.\nAvailable features are:\n\t{}",
408 UnstableFeatures::iter().join("\n\t")
414 _ => error_invalid_argument(self, "this is not a literal string expression"),
418 fn check_can_use_feature(
420 node: &'a Syntax<Token, Value>,
421 feature: &UnstableFeatures,
423 let parser_options = &self.env.parser_options;
424 let enabled = match feature {
425 UnstableFeatures::UnionIntersectionTypeHints => {
426 parser_options.tco_union_intersection_type_hints
428 UnstableFeatures::ClassLevelWhere => parser_options.po_enable_class_level_where_clauses,
429 } || self.env.context.active_unstable_features.contains(feature);
431 self.errors.push(Self::make_error_from_node(
433 errors::cannot_use_feature(feature.into()),
438 fn attr_spec_to_node_list(
439 node: &'a Syntax<Token, Value>,
440 ) -> impl DoubleEndedIterator<Item = &'a Syntax<Token, Value>> {
441 use itertools::Either::{Left, Right};
442 let f = |attrs| Left(Self::syntax_to_list_no_separators(attrs));
444 AttributeSpecification(x) => f(&x.attribute_specification_attributes),
445 OldAttributeSpecification(x) => f(&x.old_attribute_specification_attributes),
446 FileAttributeSpecification(x) => f(&x.file_attribute_specification_attributes),
447 _ => Right(std::iter::empty()),
451 fn attr_constructor_call(node: &'a Syntax<Token, Value>) -> &'a SyntaxVariant<Token, Value> {
453 ConstructorCall(_) => &node.syntax,
454 Attribute(x) => &x.attribute_attribute_name.syntax,
459 fn attr_name(&self, node: &'a Syntax<Token, Value>) -> Option<&'a str> {
460 if let ConstructorCall(x) = Self::attr_constructor_call(node) {
461 Some(self.text(&x.constructor_call_type))
469 node: &'a Syntax<Token, Value>,
470 ) -> Option<impl DoubleEndedIterator<Item = &'a Syntax<Token, Value>>> {
471 if let ConstructorCall(x) = Self::attr_constructor_call(node) {
472 Some(Self::syntax_to_list_no_separators(
473 &x.constructor_call_argument_list,
480 fn attribute_specification_contains(&self, node: &'a Syntax<Token, Value>, name: &str) -> bool {
482 AttributeSpecification(_)
483 | OldAttributeSpecification(_)
484 | FileAttributeSpecification(_) => {
485 Self::attr_spec_to_node_list(node).any(|node| self.attr_name(node) == Some(name))
491 fn methodish_contains_attribute(
493 node: &'a Syntax<Token, Value>,
497 MethodishDeclaration(x) => {
498 self.attribute_specification_contains(&x.methodish_attribute, attribute)
504 fn is_decorated_expression<F>(node: &'a Syntax<Token, Value>, f: F) -> bool
506 F: Fn(&'a Syntax<Token, Value>) -> bool,
509 DecoratedExpression(x) => f(&x.decorated_expression_decorator),
514 fn test_decorated_expression_child<F>(node: &'a Syntax<Token, Value>, f: F) -> bool
516 F: Fn(&'a Syntax<Token, Value>) -> bool,
519 DecoratedExpression(x) => f(&x.decorated_expression_expression),
524 fn is_variadic_expression(node: &'a Syntax<Token, Value>) -> bool {
525 Self::is_decorated_expression(node, |x| x.is_ellipsis())
526 || Self::test_decorated_expression_child(node, &Self::is_variadic_expression)
529 fn is_double_variadic(node: &'a Syntax<Token, Value>) -> bool {
530 Self::is_decorated_expression(node, |x| x.is_ellipsis())
531 && Self::test_decorated_expression_child(node, &Self::is_variadic_expression)
534 fn is_variadic_parameter_variable(node: &'a Syntax<Token, Value>) -> bool {
535 // TODO: This shouldn't be a decorated *expression* because we are not
536 // expecting an expression at all. We're expecting a declaration.
537 Self::is_variadic_expression(node)
540 fn is_variadic_parameter_declaration(node: &'a Syntax<Token, Value>) -> bool {
542 VariadicParameter(_) => true,
543 ParameterDeclaration(x) => Self::is_variadic_parameter_variable(&x.parameter_name),
547 fn misplaced_variadic_param(
548 param: &'a Syntax<Token, Value>,
549 ) -> Option<&'a Syntax<Token, Value>> {
550 Self::assert_last_in_list(&Self::is_variadic_parameter_declaration, param)
552 fn misplaced_variadic_arg(args: &'a Syntax<Token, Value>) -> Option<&'a Syntax<Token, Value>> {
553 Self::assert_last_in_list(&Self::is_variadic_expression, args)
555 // If a list ends with a variadic parameter followed by a comma, return it
556 fn ends_with_variadic_comma(
557 params: &'a Syntax<Token, Value>,
558 ) -> Option<&'a Syntax<Token, Value>> {
559 let mut iter = Self::syntax_to_list_with_separators(params).rev();
563 (Some(x), Some(y)) if Self::is_variadic_parameter_declaration(x) && y.is_comma() => {
570 // Extract variadic parameter from a parameter list
571 fn variadic_param(params: &'a Syntax<Token, Value>) -> Option<&'a Syntax<Token, Value>> {
572 Self::syntax_to_list_with_separators(params)
573 .find(|&x| Self::is_variadic_parameter_declaration(x))
576 fn is_parameter_with_default_value(param: &'a Syntax<Token, Value>) -> bool {
577 match ¶m.syntax {
578 ParameterDeclaration(x) => !x.parameter_default_value.is_missing(),
583 // test a node is a syntaxlist and that the list contains an element
584 // satisfying a given predicate
585 fn list_contains_predicate<P>(p: P, node: &'a Syntax<Token, Value>) -> bool
587 P: Fn(&'a Syntax<Token, Value>) -> bool,
589 if let SyntaxList(lst) = &node.syntax {
596 fn list_first_duplicate_token(
597 node: &'a Syntax<Token, Value>,
598 ) -> Option<&'a Syntax<Token, Value>> {
599 if let SyntaxList(lst) = &node.syntax {
600 let mut seen = BTreeMap::new();
601 for node in lst.iter().rev() {
602 if let Token(t) = &node.syntax {
603 if let Some(dup) = seen.insert(t.kind(), node) {
612 fn is_empty_list_or_missing(node: &'a Syntax<Token, Value>) -> bool {
614 SyntaxList(x) if x.is_empty() => true,
620 fn token_kind(node: &'a Syntax<Token, Value>) -> Option<TokenKind> {
621 if let Token(t) = &node.syntax {
622 return Some(t.kind());
627 // Helper function for common code pattern
628 fn is_token_kind(node: &'a Syntax<Token, Value>, kind: TokenKind) -> bool {
629 Self::token_kind(node) == Some(kind)
632 fn active_classish_kind(&self) -> Option<TokenKind> {
636 .and_then(|x| match &x.syntax {
637 ClassishDeclaration(cd) => Self::token_kind(&cd.classish_keyword),
642 fn modifiers_of_function_decl_header_exn(
643 node: &'a Syntax<Token, Value>,
644 ) -> &'a Syntax<Token, Value> {
646 FunctionDeclarationHeader(x) => &x.function_modifiers,
647 _ => panic!("expected to get FunctionDeclarationHeader"),
651 fn get_modifiers_of_declaration(
652 node: &'a Syntax<Token, Value>,
653 ) -> Option<&'a Syntax<Token, Value>> {
655 MethodishDeclaration(x) => Some(Self::modifiers_of_function_decl_header_exn(
656 &x.methodish_function_decl_header,
658 FunctionDeclaration(x) => Some(Self::modifiers_of_function_decl_header_exn(
659 &x.function_declaration_header,
661 PropertyDeclaration(x) => Some(&x.property_modifiers),
662 ConstDeclaration(x) => Some(&x.const_modifiers),
663 TypeConstDeclaration(x) => Some(&x.type_const_modifiers),
664 ClassishDeclaration(x) => Some(&x.classish_modifiers),
665 TraitUseAliasItem(x) => Some(&x.trait_use_alias_item_modifiers),
670 // tests whether the node's modifiers contain one that satisfies [p]
671 fn has_modifier_helper<P>(p: P, node: &'a Syntax<Token, Value>) -> bool
673 P: Fn(&'a Syntax<Token, Value>) -> bool,
675 match Self::get_modifiers_of_declaration(node) {
676 Some(x) => Self::list_contains_predicate(p, x),
681 // does the node contain the Abstract keyword in its modifiers
682 fn has_modifier_abstract(node: &'a Syntax<Token, Value>) -> bool {
683 Self::has_modifier_helper(|x| x.is_abstract(), node)
686 // does the node contain the Static keyword in its modifiers
687 fn has_modifier_static(node: &'a Syntax<Token, Value>) -> bool {
688 Self::has_modifier_helper(|x| x.is_static(), node)
691 // does the node contain the Private keyword in its modifiers
692 fn has_modifier_private(node: &'a Syntax<Token, Value>) -> bool {
693 Self::has_modifier_helper(|x| x.is_private(), node)
696 fn get_modifier_final(modifiers: &'a Syntax<Token, Value>) -> Option<&'a Syntax<Token, Value>> {
697 Self::syntax_to_list_no_separators(modifiers).find(|x| x.is_final())
700 fn is_visibility(x: &'a Syntax<Token, Value>) -> bool {
701 x.is_public() || x.is_private() || x.is_protected()
704 fn contains_async_not_last(mods: &'a Syntax<Token, Value>) -> bool {
705 let mut mod_list = Self::syntax_to_list_no_separators(mods);
706 match mod_list.next_back() {
707 Some(x) if x.is_async() => false,
708 _ => mod_list.any(|x| x.is_async()),
712 fn has_static<F>(&self, node: &'a Syntax<Token, Value>, f: F) -> bool
714 F: Fn(&'a Syntax<Token, Value>) -> bool,
717 FunctionDeclarationHeader(node) => {
718 let label = &node.function_name;
725 .any(|&x| Self::has_modifier_static(x))
731 fn is_clone(&self, label: &'a Syntax<Token, Value>) -> bool {
732 self.text(label).eq_ignore_ascii_case(sn::members::__CLONE)
735 fn class_constructor_has_static(&self, node: &'a Syntax<Token, Value>) -> bool {
736 self.has_static(node, |x| x.is_construct())
739 fn clone_cannot_be_static(&self, node: &'a Syntax<Token, Value>) -> bool {
740 self.has_static(node, |x| self.is_clone(x))
744 params: impl DoubleEndedIterator<Item = &'a Syntax<Token, Value>>,
745 ) -> impl DoubleEndedIterator<Item = &'a Syntax<Token, Value>> {
746 params.filter(|node| match &node.syntax {
747 ParameterDeclaration(x) => !x.parameter_visibility.is_missing(),
752 // Given a function declaration header, confirm that it is NOT a constructor
753 // and that the header containing it has visibility modifiers in parameters
754 fn class_non_constructor_has_visibility_param(node: &'a Syntax<Token, Value>) -> bool {
756 FunctionDeclarationHeader(node) => {
757 let params = Self::syntax_to_list_no_separators(&node.function_parameter_list);
758 (!&node.function_name.is_construct())
759 && Self::promoted_params(params).next().is_some()
765 // Don't allow a promoted parameter in a constructor if the class
766 // already has a property with the same name. Return the clashing name found.
767 fn class_constructor_param_promotion_clash(
769 node: &'a Syntax<Token, Value>,
771 use itertools::Either::{Left, Right};
772 let class_elts = |node: Option<&'a Syntax<Token, Value>>| match node.map(|x| &x.syntax) {
773 Some(ClassishDeclaration(cd)) => match &cd.classish_body.syntax {
774 ClassishBody(cb) => Left(Self::syntax_to_list_no_separators(
775 &cb.classish_body_elements,
777 _ => Right(std::iter::empty()),
779 _ => Right(std::iter::empty()),
782 // A property declaration may include multiple property names:
783 // public int $x, $y;
784 let prop_names = |elt: &'a Syntax<Token, Value>| match &elt.syntax {
785 PropertyDeclaration(x) => Left(
786 Self::syntax_to_list_no_separators(&x.property_declarators).filter_map(|decl| {
788 PropertyDeclarator(x) => Some(self.text(&x.property_name)),
793 _ => Right(std::iter::empty()),
796 let param_name = |p: &'a Syntax<Token, Value>| match &p.syntax {
797 ParameterDeclaration(x) => Some(self.text(&x.parameter_name)),
802 FunctionDeclarationHeader(node) if node.function_name.is_construct() => {
803 let class_var_names: Vec<_> = class_elts(self.env.context.active_classish)
804 .map(|x| prop_names(x))
807 let params = Self::syntax_to_list_no_separators(&node.function_parameter_list);
808 let mut promoted_param_names = Self::promoted_params(params).filter_map(param_name);
809 promoted_param_names.find(|name| class_var_names.contains(name))
815 // Ban parameter promotion in abstract constructors.
816 fn abstract_class_constructor_has_visibility_param(node: &'a Syntax<Token, Value>) -> bool {
818 FunctionDeclarationHeader(node) => {
819 let label = &node.function_name;
820 let params = Self::syntax_to_list_no_separators(&node.function_parameter_list);
822 && Self::list_contains_predicate(|x| x.is_abstract(), &node.function_modifiers)
823 && Self::promoted_params(params).next().is_some()
829 // Ban parameter promotion in interfaces and traits.
830 fn interface_or_trait_has_visibility_param(&self, node: &'a Syntax<Token, Value>) -> bool {
832 FunctionDeclarationHeader(node) => {
833 let is_interface_or_trait =
837 .map_or(false, |parent_classish| match &parent_classish.syntax {
838 ClassishDeclaration(cd) => {
839 let kind = Self::token_kind(&cd.classish_keyword);
840 kind == Some(TokenKind::Interface) || kind == Some(TokenKind::Trait)
845 let params = Self::syntax_to_list_no_separators(&node.function_parameter_list);
846 Self::promoted_params(params).next().is_some() && is_interface_or_trait
852 // check that a constructor is type annotated
853 fn class_constructor_has_non_void_type(&self, node: &'a Syntax<Token, Value>) -> bool {
854 if !self.env.is_typechecker() {
858 FunctionDeclarationHeader(node) => {
859 let label = &node.function_name;
860 let type_ano = &node.function_type;
861 let function_colon = &node.function_colon;
862 let is_missing = type_ano.is_missing() && function_colon.is_missing();
863 let is_void = match &type_ano.syntax {
864 SimpleTypeSpecifier(spec) => spec.simple_type_specifier.is_void(),
867 label.is_construct() && !(is_missing || is_void)
874 fn unsupported_magic_method_errors(&mut self, node: &'a Syntax<Token, Value>) {
875 if let FunctionDeclarationHeader(x) = &node.syntax {
876 let name = self.text(&x.function_name).to_ascii_lowercase();
877 let unsupported = sn::members::UNSUPPORTED_MAP.get(&name);
879 if let Some(unsupported) = unsupported {
880 self.errors.push(Self::make_error_from_node(
882 errors::unsupported_magic_method(unsupported),
888 fn async_magic_method(&self, node: &'a Syntax<Token, Value>) -> bool {
890 FunctionDeclarationHeader(node) => {
891 let name = self.text(&node.function_name).to_ascii_lowercase();
893 _ if name.eq_ignore_ascii_case(sn::members::__DISPOSE_ASYNC) => false,
894 _ if sn::members::AS_LOWERCASE_SET.contains(&name) => {
895 Self::list_contains_predicate(|x| x.is_async(), &node.function_modifiers)
904 fn clone_takes_no_arguments(&self, node: &'a Syntax<Token, Value>) -> bool {
906 FunctionDeclarationHeader(x) => {
907 let mut params = Self::syntax_to_list_no_separators(&x.function_parameter_list);
908 self.is_clone(&x.function_name) && params.next().is_some()
914 // whether a methodish decl has body
915 fn methodish_has_body(node: &'a Syntax<Token, Value>) -> bool {
917 MethodishDeclaration(syntax) => !syntax.methodish_function_body.is_missing(),
922 // whether a methodish decl is native
923 fn methodish_is_native(&self, node: &'a Syntax<Token, Value>) -> bool {
924 self.methodish_contains_attribute(node, "__Native")
927 // By checking the third parent of a methodish node, tests whether the methodish
928 // node is inside an interface.
929 fn methodish_inside_interface(&self) -> bool {
934 .any(|parent_classish| match &parent_classish.syntax {
935 ClassishDeclaration(cd) => {
936 Self::token_kind(&cd.classish_keyword) == Some(TokenKind::Interface)
942 // Test whether node is a non-abstract method without a body and not native.
943 // Here node is the methodish node
944 // And methods inside interfaces are inherently considered abstract *)
945 fn methodish_non_abstract_without_body_not_native(
947 node: &'a Syntax<Token, Value>,
950 !(Self::has_modifier_abstract(node) || self.methodish_inside_interface());
951 let not_has_body = !Self::methodish_has_body(node);
952 let not_native = !self.methodish_is_native(node);
953 let not_hhi = !self.env.is_hhi_mode();
955 not_hhi && non_abstract && not_has_body && not_native
958 // Test whether node is a method that is both abstract and private
959 fn methodish_abstract_conflict_with_private(node: &'a Syntax<Token, Value>) -> bool {
960 let is_abstract = Self::has_modifier_abstract(node);
961 let has_private = Self::has_modifier_private(node);
962 is_abstract && has_private
965 fn methodish_abstract_inside_interface(&self, node: &'a Syntax<Token, Value>) -> bool {
966 let is_abstract = Self::has_modifier_abstract(node);
967 let is_in_interface = self.methodish_inside_interface();
968 is_abstract && is_in_interface
971 fn using_statement_function_scoped_is_legal(&self) -> bool {
972 // using is allowed in the toplevel, and also in toplevel async blocks
973 let len = self.parents.len();
977 match (self.parents.get(len - 2), self.parents.get(len - 3)) {
980 syntax: CompoundStatement(_),
984 ) => match &x.syntax {
985 FunctionDeclaration(_)
986 | MethodishDeclaration(_)
987 | AnonymousFunction(_)
988 | LambdaExpression(_)
989 | AwaitableCreationExpression(_) => true,
996 fn make_error_from_nodes(
997 child: Option<SyntaxError>,
998 start_node: &'a Syntax<Token, Value>,
999 end_node: &'a Syntax<Token, Value>,
1000 error_type: ErrorType,
1001 error: errors::Error,
1003 let s = Self::start_offset(start_node);
1004 let e = Self::end_offset(end_node);
1005 SyntaxError::make_with_child_and_type(child, s, e, error_type, error)
1008 fn make_error_from_node(node: &'a Syntax<Token, Value>, error: errors::Error) -> SyntaxError {
1009 Self::make_error_from_nodes(None, node, node, ErrorType::ParseError, error)
1012 fn make_error_from_node_with_type(
1013 node: &'a Syntax<Token, Value>,
1014 error: errors::Error,
1015 error_type: ErrorType,
1017 Self::make_error_from_nodes(None, node, node, error_type, error)
1020 fn is_invalid_xhp_attr_enum_item_literal(literal_expression: &'a Syntax<Token, Value>) -> bool {
1021 if let Token(t) = &literal_expression.syntax {
1023 TokenKind::DecimalLiteral
1024 | TokenKind::SingleQuotedStringLiteral
1025 | TokenKind::DoubleQuotedStringLiteral => false,
1033 fn is_invalid_xhp_attr_enum_item(node: &'a Syntax<Token, Value>) -> bool {
1034 if let LiteralExpression(x) = &node.syntax {
1035 Self::is_invalid_xhp_attr_enum_item_literal(&x.literal_expression)
1041 fn xhp_errors(&mut self, node: &'a Syntax<Token, Value>) {
1042 match &node.syntax {
1043 XHPEnumType(enum_type) => {
1044 if self.env.is_typechecker() && enum_type.xhp_enum_values.is_missing() {
1045 self.errors.push(Self::make_error_from_node(
1046 &enum_type.xhp_enum_values,
1049 } else if self.env.is_typechecker() {
1050 Self::syntax_to_list_no_separators(&enum_type.xhp_enum_values)
1051 .filter(|&x| Self::is_invalid_xhp_attr_enum_item(x))
1054 .push(Self::make_error_from_node(item, errors::error2063))
1058 XHPExpression(x) => {
1059 if let XHPOpen(xhp_open) = &x.xhp_open.syntax {
1060 if let XHPClose(xhp_close) = &x.xhp_close.syntax {
1061 let open_tag = self.text(&xhp_open.xhp_open_name);
1062 let close_tag = self.text(&xhp_close.xhp_close_name);
1063 if open_tag != close_tag {
1064 self.errors.push(Self::make_error_from_node(
1066 errors::error2070(&open_tag, &close_tag),
1076 fn invalid_modifier_errors<F>(&mut self, decl_name: &str, node: &'a Syntax<Token, Value>, ok: F)
1078 F: Fn(TokenKind) -> bool,
1080 if let Some(modifiers) = Self::get_modifiers_of_declaration(node) {
1081 for modifier in Self::syntax_to_list_no_separators(modifiers) {
1082 if let Some(kind) = Self::token_kind(modifier) {
1084 self.errors.push(Self::make_error_from_node(
1086 errors::invalid_modifier_for_declaration(
1088 self.text(modifier),
1094 if let Some(duplicate) = Self::list_first_duplicate_token(modifiers) {
1095 self.errors.push(Self::make_error_from_node(
1097 errors::duplicate_modifiers_for_declaration(decl_name),
1100 if let SyntaxList(modifiers) = &modifiers.syntax {
1101 let modifiers: Vec<&'a Syntax<Token, Value>> = modifiers
1103 .filter(|x: &&'a Syntax<Token, Value>| Self::is_visibility(*x))
1105 if modifiers.len() > 1 {
1106 self.errors.push(Self::make_error_from_node(
1107 modifiers.last().unwrap(),
1108 errors::multiple_visibility_modifiers_for_declaration(decl_name),
1115 // helper since there are so many kinds of errors
1116 fn produce_error<'b, F, E, X>(
1120 error: E, // closure to avoid constant premature concatenation of error strings
1121 error_node: &'a Syntax<Token, Value>,
1123 F: Fn(&mut Self, &'b X) -> bool,
1126 if check(self, node) {
1128 .push(Self::make_error_from_node(error_node, error()))
1132 // helper since there are so many kinds of errors
1133 fn produce_error_from_check<'b, F, E, X>(&mut self, check: F, node: &'b X, error: E)
1135 F: Fn(&'b X) -> Option<&'a Syntax<Token, Value>>,
1138 if let Some(error_node) = check(node) {
1140 .push(Self::make_error_from_node(error_node, error()))
1144 fn cant_be_classish_name(name: &str) -> bool {
1145 match name.to_ascii_lowercase().as_ref() {
1146 "callable" | "classname" | "darray" | "false" | "null" | "parent" | "self" | "this"
1147 | "true" | "varray" => true,
1152 // Given a function_declaration_header node, returns its function_name
1154 fn extract_function_name(&self, header_node: &'a Syntax<Token, Value>) -> Option<&'a str> {
1155 // The '_' arm of this match will never be reached, but the type checker
1156 // doesn't allow a direct extraction of function_name from
1157 // function_declaration_header. *)
1158 match &header_node.syntax {
1159 FunctionDeclarationHeader(fdh) => Some(self.text(&fdh.function_name)),
1164 // Return, as a string opt, the name of the function or method given the context *)
1165 fn first_parent_function_name(&self) -> Option<&str> {
1166 // Note: matching on either is sound because functions and/or methods cannot be nested
1168 match self.env.context.active_methodish {
1170 syntax: FunctionDeclaration(x),
1172 }) => self.extract_function_name(&x.function_declaration_header),
1174 syntax: MethodishDeclaration(x),
1176 }) => self.extract_function_name(&x.methodish_function_decl_header),
1181 // Given a particular TokenKind::(Trait/Interface), tests if a given
1182 // classish_declaration node is both of that kind and declared abstract.
1183 fn is_classish_kind_declared_abstract(&self, cd_node: &'a Syntax<Token, Value>) -> bool {
1184 match &cd_node.syntax {
1185 ClassishDeclaration(x)
1186 if Self::is_token_kind(&x.classish_keyword, TokenKind::Trait)
1187 || Self::is_token_kind(&x.classish_keyword, TokenKind::Interface) =>
1189 Self::list_contains_predicate(|x| x.is_abstract(), &x.classish_modifiers)
1195 fn is_immediately_in_lambda(&self) -> bool {
1199 .map_or(false, |node| match &node.syntax {
1200 AnonymousFunction(_) | LambdaExpression(_) | AwaitableCreationExpression(_) => true,
1205 // Returns the whether the current context is in an active class scope
1206 fn is_in_active_class_scope(&self) -> bool {
1207 self.env.context.active_classish.is_some()
1210 // Returns the first ClassishDeclaration node or
1211 // None if there isn't one or classish_kind does not match. *)
1212 fn first_parent_classish_node(
1214 classish_kind: TokenKind,
1215 ) -> Option<&'a Syntax<Token, Value>> {
1219 .and_then(|node| match &node.syntax {
1220 ClassishDeclaration(cd)
1221 if Self::is_token_kind(&cd.classish_keyword, classish_kind) =>
1229 // Return, as a string opt, the name of the closest enclosing classish entity in
1230 // the given context (not just Classes )
1231 fn active_classish_name(&self) -> Option<&'a str> {
1232 self.env.context.active_classish.and_then(|node| {
1233 if let ClassishDeclaration(cd) = &node.syntax {
1234 cd.classish_name.extract_text(self.env.syntax_tree.text())
1241 // Return, as a string opt, the name of the Class in the given context
1242 fn first_parent_class_name(&self) -> Option<&'a str> {
1247 .and_then(|parent_classish| {
1248 if let ClassishDeclaration(cd) = &parent_classish.syntax {
1249 if Self::token_kind(&cd.classish_keyword) == Some(TokenKind::Class) {
1250 return self.active_classish_name();
1252 return None; // This arm is never reached
1259 // Given a declaration node, returns the modifier node matching the given
1260 // predicate from its list of modifiers, or None if there isn't one.
1261 fn extract_keyword<F>(
1263 declaration_node: &'a Syntax<Token, Value>,
1264 ) -> Option<&'a Syntax<Token, Value>>
1266 F: Fn(&'a Syntax<Token, Value>) -> bool,
1268 Self::get_modifiers_of_declaration(declaration_node).and_then(|modifiers_list| {
1269 Self::syntax_to_list_no_separators(modifiers_list)
1270 .find(|x: &&'a Syntax<Token, Value>| modifier(*x))
1274 // Wrapper function that uses above extract_keyword function to test if node
1275 // contains is_abstract keyword
1276 fn is_abstract_declaration(declaration_node: &'a Syntax<Token, Value>) -> bool {
1277 Self::extract_keyword(|x| x.is_abstract(), declaration_node).is_some()
1280 // Tests if the immediate classish parent is an interface.
1281 fn is_inside_interface(&self) -> bool {
1282 self.first_parent_classish_node(TokenKind::Interface)
1286 // Tests if the immediate classish parent is a trait.
1287 fn is_inside_trait(&self) -> bool {
1288 self.first_parent_classish_node(TokenKind::Trait).is_some()
1291 fn is_abstract_and_async_method(md_node: &'a Syntax<Token, Value>) -> bool {
1292 Self::is_abstract_declaration(md_node)
1293 && Self::extract_keyword(|x| x.is_async(), md_node).is_some()
1296 fn is_interface_and_async_method(&self, md_node: &'a Syntax<Token, Value>) -> bool {
1297 self.is_inside_interface() && Self::extract_keyword(|x| x.is_async(), md_node).is_some()
1300 fn get_params_for_enclosing_callable(&self) -> Option<&'a Syntax<Token, Value>> {
1301 let from_header = |header: &'a Syntax<Token, Value>| match &header.syntax {
1302 FunctionDeclarationHeader(fdh) => Some(&fdh.function_parameter_list),
1308 .and_then(|callable| match &callable.syntax {
1309 FunctionDeclaration(x) => from_header(&x.function_declaration_header),
1310 MethodishDeclaration(x) => from_header(&x.methodish_function_decl_header),
1311 LambdaExpression(x) => match &x.lambda_signature.syntax {
1312 LambdaSignature(x) => Some(&x.lambda_parameters),
1319 fn first_parent_function_attributes_contains(&self, name: &str) -> bool {
1320 let from_attr_spec = |attr_spec| {
1321 Self::attr_spec_to_node_list(attr_spec).any(|node| self.attr_name(node) == Some(name))
1323 match self.env.context.active_methodish {
1325 syntax: FunctionDeclaration(x),
1327 }) => from_attr_spec(&x.function_attribute_spec),
1329 syntax: MethodishDeclaration(x),
1331 }) => from_attr_spec(&x.methodish_attribute),
1336 fn parameter_callconv(param: &'a Syntax<Token, Value>) -> Option<&'a Syntax<Token, Value>> {
1337 (match ¶m.syntax {
1338 ParameterDeclaration(x) => Some(&x.parameter_call_convention),
1339 ClosureParameterTypeSpecifier(x) => Some(&x.closure_parameter_call_convention),
1340 VariadicParameter(x) => Some(&x.variadic_parameter_call_convention),
1343 .filter(|node| !node.is_missing())
1346 fn is_parameter_with_callconv(param: &'a Syntax<Token, Value>) -> bool {
1347 Self::parameter_callconv(param).is_some()
1350 fn has_inout_params(&self) -> bool {
1351 self.get_params_for_enclosing_callable()
1352 .map_or(false, |function_parameter_list| {
1353 Self::syntax_to_list_no_separators(function_parameter_list)
1354 .any(|x| Self::is_parameter_with_callconv(x))
1358 fn is_inside_async_method(&self) -> bool {
1359 let from_header = |header: &'a Syntax<Token, Value>| match &header.syntax {
1360 FunctionDeclarationHeader(fdh) => {
1361 Self::syntax_to_list_no_separators(&fdh.function_modifiers).any(|x| x.is_async())
1368 .map_or(false, |node| match &node.syntax {
1369 FunctionDeclaration(x) => from_header(&x.function_declaration_header),
1370 MethodishDeclaration(x) => from_header(&x.methodish_function_decl_header),
1371 AnonymousFunction(x) => !x.anonymous_async_keyword.is_missing(),
1372 LambdaExpression(x) => !x.lambda_async.is_missing(),
1373 AwaitableCreationExpression(_) => true,
1378 fn make_name_already_used_error(
1379 node: &'a Syntax<Token, Value>,
1382 original_location: &Location,
1383 report_error: &dyn Fn(&str, &str) -> Error,
1385 let name = Self::strip_ns(name);
1386 let original_location_error = SyntaxError::make(
1387 original_location.start_offset,
1388 original_location.end_offset,
1389 errors::original_definition,
1392 let s = Self::start_offset(node);
1393 let e = Self::end_offset(node);
1394 SyntaxError::make_with_child_and_type(
1395 Some(original_location_error),
1398 ErrorType::ParseError,
1399 report_error(&name, short_name),
1403 fn check_type_name_reference(&mut self, name_text: &str, location: Location) {
1404 if hh_autoimport::is_hh_autoimport(name_text) && !self.names.classes.mem(name_text) {
1405 let def = make_first_use_or_def(false, NameImplicitUse, location, "HH", name_text);
1406 self.names.classes.add(name_text, def)
1410 fn check_type_hint(&mut self, node: &'a Syntax<Token, Value>) {
1411 for x in node.iter_children() {
1412 self.check_type_hint(x)
1414 let check_type_name = |self_: &mut Self, s| {
1415 self_.check_type_name_reference(self_.text(s), Self::make_location_of_node(node))
1417 match &node.syntax {
1418 SimpleTypeSpecifier(x) => check_type_name(self, &x.simple_type_specifier),
1419 GenericTypeSpecifier(x) => check_type_name(self, &x.generic_class_type),
1424 fn extract_callconv_node(node: &'a Syntax<Token, Value>) -> Option<&'a Syntax<Token, Value>> {
1425 match &node.syntax {
1426 ParameterDeclaration(x) => Some(&x.parameter_call_convention),
1427 ClosureParameterTypeSpecifier(x) => Some(&x.closure_parameter_call_convention),
1428 VariadicParameter(x) => Some(&x.variadic_parameter_call_convention),
1433 // Given a node, checks if it is a abstract ConstDeclaration
1434 fn is_abstract_const(declaration: &'a Syntax<Token, Value>) -> bool {
1435 match &declaration.syntax {
1436 ConstDeclaration(_) => Self::has_modifier_abstract(declaration),
1441 // Given a ConstDeclarator node, test whether it is abstract, but has an
1443 fn constant_abstract_with_initializer(&self, init: &'a Syntax<Token, Value>) -> bool {
1444 let is_abstract = match self.env.context.active_const {
1445 Some(p_const_declaration) if Self::is_abstract_const(p_const_declaration) => true,
1448 let has_initializer = !init.is_missing();
1449 is_abstract && has_initializer
1452 // Given a node, checks if it is a concrete ConstDeclaration *)
1453 fn is_concrete_const(declaration: &'a Syntax<Token, Value>) -> bool {
1454 match &declaration.syntax {
1455 ConstDeclaration(_) => !Self::has_modifier_abstract(declaration),
1460 // Given a ConstDeclarator node, test whether it is concrete, but has no
1462 fn constant_concrete_without_initializer(&self, init: &'a Syntax<Token, Value>) -> bool {
1463 let is_concrete = match self.env.context.active_const {
1464 Some(p_const_declaration) if Self::is_concrete_const(p_const_declaration) => true,
1467 is_concrete && init.is_missing()
1470 fn methodish_memoize_lsb_on_non_static(&mut self, node: &'a Syntax<Token, Value>) {
1471 if self.methodish_contains_attribute(node, sn::user_attributes::MEMOIZE_LSB)
1472 && !Self::has_modifier_static(node)
1474 self.errors.push(Self::make_error_from_node(
1476 errors::memoize_lsb_on_non_static,
1481 fn function_declaration_contains_attribute(
1483 node: &'a Syntax<Token, Value>,
1486 match &node.syntax {
1487 FunctionDeclaration(x) => {
1488 self.attribute_specification_contains(&x.function_attribute_spec, attribute)
1494 fn methodish_contains_memoize(&self, node: &'a Syntax<Token, Value>) -> bool {
1495 self.env.is_typechecker()
1496 && self.is_inside_interface()
1497 && self.methodish_contains_attribute(node, sn::user_attributes::MEMOIZE)
1500 fn is_some_reactivity_attribute_name(name: &str) -> bool {
1501 name == sn::user_attributes::REACTIVE
1502 || name == sn::user_attributes::SHALLOW_REACTIVE
1503 || name == sn::user_attributes::LOCAL_REACTIVE
1504 || name == sn::user_attributes::NON_RX
1505 || name == sn::user_attributes::PURE
1508 fn is_some_reactivity_attribute(&self, node: &'a Syntax<Token, Value>) -> bool {
1509 match self.attr_name(node) {
1511 Some(name) => Self::is_some_reactivity_attribute_name(name),
1515 fn attribute_first_reactivity_annotation(
1517 node: &'a Syntax<Token, Value>,
1518 ) -> Option<&'a Syntax<Token, Value>> {
1519 match &node.syntax {
1520 AttributeSpecification(_) | OldAttributeSpecification(_) => {
1521 Self::attr_spec_to_node_list(node).find(|x| self.is_some_reactivity_attribute(x))
1527 fn attribute_has_reactivity_annotation(&self, attr_spec: &'a Syntax<Token, Value>) -> bool {
1528 self.attribute_first_reactivity_annotation(attr_spec)
1532 fn attribute_missing_reactivity_for_condition(
1534 attr_spec: &'a Syntax<Token, Value>,
1536 let has_attr = |attr| self.attribute_specification_contains(attr_spec, attr);
1537 !(self.attribute_has_reactivity_annotation(attr_spec))
1538 && (has_attr(sn::user_attributes::ONLY_RX_IF_IMPL)
1539 || has_attr(sn::user_attributes::AT_MOST_RX_AS_ARGS))
1542 fn error_if_memoize_function_returns_mutable(&mut self, attrs: &'a Syntax<Token, Value>) {
1543 let mut has_memoize = false;
1544 let mut mutable_node = None;
1545 let mut mut_return_node = None;
1546 for node in Self::attr_spec_to_node_list(attrs) {
1547 match self.attr_name(node) {
1548 Some(n) if n == sn::user_attributes::MUTABLE_RETURN => mut_return_node = Some(node),
1550 if n == sn::user_attributes::MEMOIZE
1551 || n == sn::user_attributes::MEMOIZE_LSB =>
1555 Some(n) if n == sn::user_attributes::MUTABLE => mutable_node = Some(node),
1561 if let Some(n) = mutable_node {
1562 self.errors.push(Self::make_error_from_node(
1564 errors::mutable_parameter_in_memoize_function(true),
1567 if let Some(n) = mut_return_node {
1568 self.errors.push(Self::make_error_from_node(
1570 errors::mutable_return_in_memoize_function,
1576 fn methodish_missing_reactivity_for_condition(&self, node: &'a Syntax<Token, Value>) -> bool {
1577 match &node.syntax {
1578 MethodishDeclaration(x) => {
1579 self.attribute_missing_reactivity_for_condition(&x.methodish_attribute)
1585 fn methodish_contains_owned_mutable_attribute(&self, node: &'a Syntax<Token, Value>) -> bool {
1586 self.methodish_contains_attribute(node, sn::user_attributes::OWNED_MUTABLE)
1589 fn check_nonrx_annotation(&mut self, node: &'a Syntax<Token, Value>) {
1590 let err_decl = |self_: &mut Self| {
1591 self_.errors.push(Self::make_error_from_node(
1593 errors::invalid_non_rx_argument_for_declaration,
1596 let err_lambda = |self_: &mut Self| {
1597 self_.errors.push(Self::make_error_from_node(
1599 errors::invalid_non_rx_argument_for_lambda,
1602 let attr_spec = match &node.syntax {
1603 MethodishDeclaration(x) => Some((&x.methodish_attribute, true)),
1604 FunctionDeclaration(x) => Some((&x.function_attribute_spec, true)),
1605 AnonymousFunction(x) => Some((&x.anonymous_attribute_spec, false)),
1606 LambdaExpression(x) => Some((&x.lambda_attribute_spec, false)),
1607 AwaitableCreationExpression(x) => Some((&x.awaitable_attribute_spec, false)),
1611 if let Some((node, is_decl)) = attr_spec {
1612 // try find argument list
1613 let args_opt = Self::attr_spec_to_node_list(node)
1614 .find(|node| self.attr_name(node) == Some(sn::user_attributes::NON_RX))
1615 .and_then(|x| self.attr_args(x));
1617 let is_string_argument = |x: &'a Syntax<Token, Value>| match &x.syntax {
1618 LiteralExpression(x) => match &x.literal_expression.syntax {
1620 token.kind() == TokenKind::DoubleQuotedStringLiteral
1621 || token.kind() == TokenKind::SingleQuotedStringLiteral
1628 if let Some(mut args_opt) = args_opt {
1629 let first_arg = args_opt.next();
1630 let second_arg = args_opt.next();
1631 match (first_arg, second_arg) {
1632 // __NonRx attribute is found and argument list is empty.
1633 // This is ok for lambdas but error for declarations
1639 // __NonRx attribute is found with single string argument.
1640 // This is ok for declarations for not allowed for lambdas *)
1641 (Some(arg), None) if is_string_argument(arg) => {
1646 // __NonRx attribute is found but argument list is not suitable
1647 // nor for declarations, neither for lambdas
1658 // __NonRx attribute not found
1662 fn function_missing_reactivity_for_condition(&self, node: &'a Syntax<Token, Value>) -> bool {
1663 match &node.syntax {
1664 FunctionDeclaration(x) => {
1665 self.attribute_missing_reactivity_for_condition(&x.function_attribute_spec)
1671 fn function_declaration_contains_only_rx_if_impl_attribute(
1673 node: &'a Syntax<Token, Value>,
1675 self.function_declaration_contains_attribute(node, sn::user_attributes::ONLY_RX_IF_IMPL)
1678 fn function_declaration_contains_owned_mutable_attribute(
1680 node: &'a Syntax<Token, Value>,
1682 self.function_declaration_contains_attribute(node, sn::user_attributes::OWNED_MUTABLE)
1685 fn attribute_multiple_reactivity_annotations(
1687 attr_spec: &'a Syntax<Token, Value>,
1689 match &attr_spec.syntax {
1690 OldAttributeSpecification(_) | AttributeSpecification(_) => {
1691 Self::attr_spec_to_node_list(attr_spec)
1692 .filter(|x| self.is_some_reactivity_attribute(x))
1701 fn methodish_multiple_reactivity_annotations(&self, node: &'a Syntax<Token, Value>) -> bool {
1702 match &node.syntax {
1703 MethodishDeclaration(x) => {
1704 self.attribute_multiple_reactivity_annotations(&x.methodish_attribute)
1710 fn function_multiple_reactivity_annotations(&self, node: &'a Syntax<Token, Value>) -> bool {
1711 match &node.syntax {
1712 FunctionDeclaration(x) => {
1713 self.attribute_multiple_reactivity_annotations(&x.function_attribute_spec)
1719 fn function_declaration_header_memoize_lsb(&mut self) {
1720 if let (Some(node), None) = (
1721 self.env.context.active_methodish,
1722 self.env.context.active_classish,
1724 // a function, not a method
1725 if self.function_declaration_contains_attribute(node, sn::user_attributes::MEMOIZE_LSB)
1727 self.errors.push(Self::make_error_from_node(
1729 errors::memoize_lsb_on_non_method,
1735 fn is_in_reified_class(&self) -> bool {
1736 let active_classish = match self.env.context.active_classish {
1740 if let ClassishDeclaration(x) = &active_classish.syntax {
1741 if let TypeParameters(x) = &x.classish_type_parameters.syntax {
1742 return Self::syntax_to_list_no_separators(&x.type_parameters_parameters).any(
1743 |p| match &p.syntax {
1744 TypeParameter(x) => !x.type_reified.is_missing(),
1753 fn methodish_errors(&mut self, node: &'a Syntax<Token, Value>) {
1754 match &node.syntax {
1755 FunctionDeclarationHeader(x) => {
1756 let function_parameter_list = &x.function_parameter_list;
1757 let function_type = &x.function_type;
1760 |self_, x| Self::class_constructor_has_non_void_type(self_, x),
1762 || errors::error2018,
1767 |_, x| Self::class_non_constructor_has_visibility_param(x),
1769 || errors::error2010,
1770 function_parameter_list,
1773 if let Some(clashing_name) = self.class_constructor_param_promotion_clash(node) {
1774 let class_name = self.active_classish_name().unwrap_or("");
1775 let error_msg = errors::error2025(class_name, &clashing_name);
1776 self.errors.push(Self::make_error_from_node(
1777 function_parameter_list,
1783 |_, x| Self::abstract_class_constructor_has_visibility_param(x),
1785 || errors::error2023,
1786 function_parameter_list,
1790 |self_, x| Self::interface_or_trait_has_visibility_param(self_, x),
1792 || errors::error2024,
1793 function_parameter_list,
1795 self.function_declaration_header_memoize_lsb();
1797 FunctionDeclaration(fd) => {
1798 let function_attrs = &fd.function_attribute_spec;
1800 |self_, x| Self::function_multiple_reactivity_annotations(self_, x),
1802 || errors::multiple_reactivity_annotations,
1805 self.error_if_memoize_function_returns_mutable(function_attrs);
1809 Self::function_declaration_contains_only_rx_if_impl_attribute(self_, x)
1812 || errors::functions_cannot_implement_reactive,
1815 self.check_nonrx_annotation(node);
1818 |self_, x| Self::function_missing_reactivity_for_condition(self_, x),
1820 || errors::missing_reactivity_for_condition,
1826 Self::function_declaration_contains_owned_mutable_attribute(self_, x)
1829 || errors::misplaced_owned_mutable,
1833 self.invalid_modifier_errors("Top-level functions", node, |kind| {
1834 kind == TokenKind::Async || kind == TokenKind::Coroutine
1837 MethodishDeclaration(md) => {
1838 let header_node = &md.methodish_function_decl_header;
1839 let modifiers = Self::modifiers_of_function_decl_header_exn(header_node);
1840 let class_name = self.active_classish_name().unwrap_or("");
1841 let method_name = self
1842 .extract_function_name(&md.methodish_function_decl_header)
1844 let method_attrs = &md.methodish_attribute;
1845 self.error_if_memoize_function_returns_mutable(method_attrs);
1847 |self_, x| self_.methodish_contains_memoize(x),
1849 || errors::interface_with_memoize,
1853 |self_, x| self_.class_constructor_has_static(x),
1855 || errors::error2009(class_name, method_name),
1858 self.unsupported_magic_method_errors(header_node);
1860 |self_, x| self_.async_magic_method(x),
1862 || errors::async_magic_method(method_name),
1866 |self_, x| self_.clone_takes_no_arguments(x),
1868 || errors::clone_takes_no_arguments(class_name, method_name),
1872 |self_, x| self_.clone_cannot_be_static(x),
1874 || errors::clone_cannot_be_static(class_name, method_name),
1877 self.invalid_modifier_errors("Methods", node, |kind| {
1878 kind == TokenKind::Abstract
1879 || kind == TokenKind::Final
1880 || kind == TokenKind::Static
1881 || kind == TokenKind::Private
1882 || kind == TokenKind::Protected
1883 || kind == TokenKind::Public
1884 || kind == TokenKind::Async
1885 || kind == TokenKind::Coroutine
1888 if self.is_inside_interface() {
1889 self.invalid_modifier_errors("Interface methods", node, |kind| {
1890 kind != TokenKind::Final
1894 if Self::has_modifier_static(node)
1895 && (self.attribute_specification_contains(
1897 sn::user_attributes::MUTABLE,
1898 ) || self.attribute_specification_contains(
1900 sn::user_attributes::MAYBE_MUTABLE,
1903 self.errors.push(Self::make_error_from_node(
1905 errors::mutability_annotation_on_static_method,
1909 if method_name.eq_ignore_ascii_case(sn::members::__CONSTRUCT)
1910 && (self.attribute_specification_contains(
1912 sn::user_attributes::MUTABLE,
1913 ) || self.attribute_specification_contains(
1915 sn::user_attributes::MAYBE_MUTABLE,
1916 ) || self.attribute_specification_contains(
1918 sn::user_attributes::MUTABLE_RETURN,
1921 self.errors.push(Self::make_error_from_node(
1923 errors::mutability_annotation_on_constructor,
1927 let fun_semicolon = &md.methodish_semicolon;
1930 |self_, x| self_.methodish_non_abstract_without_body_not_native(x),
1932 || errors::error2015(class_name, method_name),
1936 |_, x| Self::methodish_abstract_conflict_with_private(x),
1938 || errors::error2016(class_name, method_name),
1942 if let Some(modifier) = Self::get_modifier_final(modifiers) {
1944 |_, x| Self::has_modifier_abstract(x),
1946 || errors::error2019(class_name, method_name),
1952 |self_, x| self_.methodish_abstract_inside_interface(x),
1954 || errors::error2045,
1957 self.methodish_memoize_lsb_on_non_static(node);
1958 let async_annotation =
1959 Self::extract_keyword(|x| x.is_async(), node).unwrap_or(node);
1962 |self_, x| self_.is_interface_and_async_method(x),
1964 || errors::error2046("a method in an interface"),
1969 |_, x| Self::is_abstract_and_async_method(x),
1971 || errors::error2046("an abstract method"),
1975 if self.env.is_typechecker() {
1977 |_, x| Self::contains_async_not_last(x),
1979 || errors::async_not_last,
1984 |self_, x| self_.methodish_multiple_reactivity_annotations(x),
1986 || errors::multiple_reactivity_annotations,
1989 self.check_nonrx_annotation(node);
1991 |self_, x| self_.methodish_missing_reactivity_for_condition(x),
1993 || errors::missing_reactivity_for_condition,
1997 |self_, x| self_.methodish_contains_owned_mutable_attribute(x),
1999 || errors::misplaced_owned_mutable,
2007 fn is_hashbang(&self, node: &'a Syntax<Token, Value>) -> bool {
2008 let text = self.text(node);
2010 static ref RE: Regex = Regex::new("^#!.*\n").unwrap();
2012 text.lines().nth(1).is_none() && // only one line
2016 fn is_in_construct_method(&self) -> bool {
2017 if self.is_immediately_in_lambda() {
2020 self.first_parent_function_name()
2021 .map_or(false, |s| s.eq_ignore_ascii_case(sn::members::__CONSTRUCT))
2025 // If a variadic parameter has a default value, return it
2026 fn variadic_param_with_default_value(
2027 params: &'a Syntax<Token, Value>,
2028 ) -> Option<&'a Syntax<Token, Value>> {
2029 Self::variadic_param(params).filter(|x| Self::is_parameter_with_default_value(x))
2032 // If a variadic parameter is marked inout, return it
2033 fn variadic_param_with_callconv(
2034 params: &'a Syntax<Token, Value>,
2035 ) -> Option<&'a Syntax<Token, Value>> {
2036 Self::variadic_param(params).filter(|x| Self::is_parameter_with_callconv(x))
2039 // If an inout parameter has a default, return the default
2040 fn param_with_callconv_has_default(
2041 node: &'a Syntax<Token, Value>,
2042 ) -> Option<&'a Syntax<Token, Value>> {
2043 match &node.syntax {
2044 ParameterDeclaration(x)
2045 if Self::is_parameter_with_callconv(node)
2046 && Self::is_parameter_with_default_value(&node) =>
2048 Some(&x.parameter_default_value)
2054 fn params_errors(&mut self, params: &'a Syntax<Token, Value>) {
2055 self.produce_error_from_check(&Self::ends_with_variadic_comma, params, || {
2058 self.produce_error_from_check(&Self::misplaced_variadic_param, params, || {
2062 self.produce_error_from_check(&Self::variadic_param_with_default_value, params, || {
2066 self.produce_error_from_check(&Self::variadic_param_with_callconv, params, || {
2071 fn decoration_errors(&mut self, node: &'a Syntax<Token, Value>) {
2073 |_, x| Self::is_double_variadic(x),
2075 || errors::double_variadic,
2080 fn parameter_rx_errors(&mut self, node: &'a Syntax<Token, Value>) {
2081 if let ParameterDeclaration(x) = &node.syntax {
2082 let spec = &x.parameter_attribute;
2083 let name = &x.parameter_name;
2084 let has_owned_mutable =
2085 self.attribute_specification_contains(spec, sn::user_attributes::OWNED_MUTABLE);
2088 self.attribute_specification_contains(spec, sn::user_attributes::MUTABLE);
2090 let has_maybemutable =
2091 self.attribute_specification_contains(spec, sn::user_attributes::MAYBE_MUTABLE);
2093 match (has_mutable, has_owned_mutable, has_maybemutable) {
2094 (true, true, _) => self.errors.push(Self::make_error_from_node(
2096 errors::conflicting_mutable_and_owned_mutable_attributes,
2098 (true, _, true) => self.errors.push(Self::make_error_from_node(
2100 errors::conflicting_mutable_and_maybe_mutable_attributes,
2102 (_, true, true) => self.errors.push(Self::make_error_from_node(
2104 errors::conflicting_owned_mutable_and_maybe_mutable_attributes,
2108 if (has_mutable || has_owned_mutable || has_maybemutable)
2109 && Self::is_variadic_expression(name)
2112 .push(Self::make_error_from_node(name, errors::vararg_and_mutable))
2114 let is_inout = Self::is_parameter_with_callconv(node);
2115 if is_inout && (has_mutable || has_maybemutable || has_owned_mutable) {
2116 self.errors.push(Self::make_error_from_node(
2118 errors::mutability_annotation_on_inout_parameter,
2121 if has_owned_mutable || has_mutable {
2122 let attrs = self.env.context.active_callable_attr_spec;
2123 let active_is_rx = self.env.context.active_is_rx_or_enclosing_for_lambdas;
2125 let parent_func_is_memoize = attrs
2127 self.attribute_specification_contains(spec, sn::user_attributes::MEMOIZE)
2128 || self.attribute_specification_contains(
2130 sn::user_attributes::MEMOIZE,
2135 if has_owned_mutable && !active_is_rx {
2136 self.errors.push(Self::make_error_from_node(
2138 errors::mutably_owned_attribute_on_non_rx_function,
2141 if has_mutable && parent_func_is_memoize {
2142 self.errors.push(Self::make_error_from_node(
2144 errors::mutable_parameter_in_memoize_function(false),
2151 fn does_unop_create_write(token_kind: Option<TokenKind>) -> bool {
2152 token_kind.map_or(false, |x| match x {
2153 TokenKind::PlusPlus | TokenKind::MinusMinus => true,
2158 fn does_decorator_create_write(token_kind: Option<TokenKind>) -> bool {
2160 Some(TokenKind::Inout) => true,
2165 fn node_lval_type<'b>(
2166 node: &'a Syntax<Token, Value>,
2167 parents: &'b [&'a Syntax<Token, Value>],
2169 let is_in_final_lval_position = |mut node, parents: &'b [&'a Syntax<Token, Value>]| {
2170 for &parent in parents.iter().rev() {
2171 match &parent.syntax {
2172 SyntaxList(_) | ListItem(_) => {
2176 ExpressionStatement(_) => return true,
2178 if node as *const _ == &x.for_initializer as *const _
2179 || node as *const _ == &x.for_end_of_loop as *const _ =>
2183 UsingStatementFunctionScoped(x)
2184 if node as *const _ == &x.using_function_expression as *const _ =>
2188 UsingStatementBlockScoped(x)
2189 if node as *const _ == &x.using_block_expressions as *const _ =>
2198 let get_arg_call_node_with_parents = |mut node, parents: &'b [&'a Syntax<Token, Value>]| {
2199 for i in (0..parents.len()).rev() {
2200 let parent = parents[i];
2201 match &parent.syntax {
2202 SyntaxList(_) | ListItem(_) => {
2206 FunctionCallExpression(x)
2207 if node as *const _ == &x.function_call_argument_list as *const _ =>
2210 // probably unreachable, but just in case to avoid crashing on 0-1
2211 return Some((parent, &parents[0..0]));
2213 let grandparent = parents.get(i - 1).unwrap();
2214 return match &grandparent.syntax {
2215 PrefixUnaryExpression(x)
2216 if Self::token_kind(&x.prefix_unary_operator)
2217 == Some(TokenKind::At) =>
2219 Some((grandparent, &parents[..i - 1]))
2221 _ => Some((parent, &parents[..i])),
2230 let lval_ness_of_function_arg_for_inout =
2231 |node, parents| match get_arg_call_node_with_parents(node, parents) {
2232 None => LvalTypeNone,
2233 Some((call_node, parents)) => {
2234 if is_in_final_lval_position(call_node, parents) {
2235 return LvalTypeFinal;
2237 match parents.last() {
2238 None => LvalTypeNonFinalInout,
2239 Some(parent) => match &parent.syntax {
2241 if call_node as *const _ == &x.binary_right_operand as *const _
2242 && Self::does_binop_create_write_on_left(Self::token_kind(
2246 if is_in_final_lval_position(parent, &parents[..parents.len() - 1])
2250 LvalTypeNonFinalInout
2253 _ => LvalTypeNonFinalInout,
2259 let unary_expression_operator = |x: &'a Syntax<Token, Value>| match &x.syntax {
2260 PrefixUnaryExpression(x) => &x.prefix_unary_operator,
2261 PostfixUnaryExpression(x) => &x.postfix_unary_operator,
2262 _ => panic!("expected expression operator"),
2265 let unary_expression_operand = |x: &'a Syntax<Token, Value>| match &x.syntax {
2266 PrefixUnaryExpression(x) => &x.prefix_unary_operand,
2267 PostfixUnaryExpression(x) => &x.postfix_unary_operand,
2268 _ => panic!("expected expression operator"),
2271 if let Some(next_node) = parents.last() {
2272 let parents = &parents[..parents.len() - 1];
2273 match &next_node.syntax {
2274 DecoratedExpression(x)
2275 if node as *const _ == &x.decorated_expression_expression as *const _
2276 && Self::does_decorator_create_write(Self::token_kind(
2277 &x.decorated_expression_decorator,
2280 lval_ness_of_function_arg_for_inout(next_node, parents)
2282 PrefixUnaryExpression(_) | PostfixUnaryExpression(_)
2283 if node as *const _ == unary_expression_operand(next_node) as *const _
2284 && Self::does_unop_create_write(Self::token_kind(
2285 unary_expression_operator(next_node),
2288 if is_in_final_lval_position(next_node, parents) {
2295 if node as *const _ == &x.binary_left_operand as *const _
2296 && Self::does_binop_create_write_on_left(Self::token_kind(
2300 if is_in_final_lval_position(next_node, parents) {
2307 if node as *const _ == &x.foreach_key as *const _
2308 || node as *const _ == &x.foreach_value as *const _ =>
2319 fn lval_errors(&mut self, syntax_node: &'a Syntax<Token, Value>) {
2320 if self.env.parser_options.po_disable_lval_as_an_expression {
2321 if let LvalTypeNonFinal = Self::node_lval_type(syntax_node, &self.parents) {
2322 self.errors.push(Self::make_error_from_node(
2324 errors::lval_as_expression,
2330 fn parameter_errors(&mut self, node: &'a Syntax<Token, Value>) {
2331 let param_errors = |self_: &mut Self, params| {
2332 for x in Self::syntax_to_list_no_separators(params) {
2333 self_.parameter_rx_errors(x)
2335 self_.params_errors(params)
2337 match &node.syntax {
2338 ParameterDeclaration(p) => {
2339 let callconv_text = self.text(Self::extract_callconv_node(node).unwrap_or(node));
2340 self.produce_error_from_check(&Self::param_with_callconv_has_default, node, || {
2341 errors::error2074(callconv_text)
2343 self.parameter_rx_errors(node);
2344 self.check_type_hint(&p.parameter_type);
2346 if let Some(inout_modifier) = Self::parameter_callconv(node) {
2347 if self.is_inside_async_method() {
2348 self.errors.push(Self::make_error_from_node_with_type(
2350 errors::inout_param_in_async,
2351 ErrorType::RuntimeError,
2354 if self.is_in_construct_method() {
2355 self.errors.push(Self::make_error_from_node(
2357 errors::inout_param_in_construct,
2360 let in_memoize = self
2361 .first_parent_function_attributes_contains(sn::user_attributes::MEMOIZE);
2362 let in_memoize_lsb = self.first_parent_function_attributes_contains(
2363 sn::user_attributes::MEMOIZE_LSB,
2366 if (in_memoize || in_memoize_lsb) && !self.is_immediately_in_lambda() {
2367 self.errors.push(Self::make_error_from_node_with_type(
2369 errors::memoize_with_inout,
2370 ErrorType::RuntimeError,
2375 FunctionDeclarationHeader(x) => param_errors(self, &x.function_parameter_list),
2376 AnonymousFunction(x) => param_errors(self, &x.anonymous_parameters),
2377 ClosureTypeSpecifier(x) => param_errors(self, &x.closure_parameter_list),
2378 LambdaExpression(x) => {
2379 if let LambdaSignature(x) = &x.lambda_signature.syntax {
2380 param_errors(self, &x.lambda_parameters)
2383 DecoratedExpression(_) => self.decoration_errors(node),
2388 // Only check the functions; invalid attributes on methods (like <<__EntryPoint>>) are caught elsewhere
2389 fn multiple_entrypoint_attribute_errors(&mut self, node: &'a Syntax<Token, Value>) {
2390 match &node.syntax {
2391 FunctionDeclaration(f)
2392 if self.attribute_specification_contains(
2393 &f.function_attribute_spec,
2394 sn::user_attributes::ENTRY_POINT,
2397 // Get the location of the <<...>> annotation
2398 let location = match &f.function_attribute_spec.syntax {
2399 AttributeSpecification(x) => {
2400 Self::make_location_of_node(&x.attribute_specification_attributes)
2402 OldAttributeSpecification(x) => {
2403 Self::make_location_of_node(&x.old_attribute_specification_attributes)
2405 _ => panic!("Expected attribute specification node"),
2407 let def = make_first_use_or_def(
2411 &self.namespace_name,
2412 sn::user_attributes::ENTRY_POINT,
2414 match self.names.attributes.get(sn::user_attributes::ENTRY_POINT) {
2416 let (line_num, _) = self
2419 .offset_to_position(prev_def.location.start_offset as isize);
2421 let path = self.env.text.source_text().file_path().path_str();
2422 let loc = String::from(path) + ":" + &line_num.to_string();
2423 let err = errors::multiple_entrypoints(&loc);
2424 let err_type = ErrorType::ParseError;
2426 .push(Self::make_error_from_node_with_type(node, err, err_type))
2432 .add(sn::user_attributes::ENTRY_POINT, def)
2438 fn redeclaration_errors(&mut self, node: &'a Syntax<Token, Value>) {
2439 match &node.syntax {
2440 FunctionDeclarationHeader(f) if !f.function_name.is_missing() => {
2441 let mut it = self.parents.iter().rev();
2446 match (p1, p3, p4) {
2449 syntax: FunctionDeclaration(_),
2453 syntax: NamespaceBody(_),
2460 syntax: FunctionDeclaration(_),
2468 syntax: MethodishDeclaration(_),
2476 syntax: MethodishTraitResolution(_),
2482 let function_name = self.text(&f.function_name);
2483 let location = Self::make_location_of_node(&f.function_name);
2484 let is_method = match p1 {
2486 syntax: MethodishDeclaration(_),
2491 let def = make_first_use_or_def(
2495 &self.namespace_name,
2498 match self.names.functions.get(function_name) {
2500 if prev_def.global == def.global && prev_def.kind == NameDef =>
2502 let (line_num, _) = self
2505 .offset_to_position(prev_def.location.start_offset as isize);
2507 let path = self.env.text.source_text().file_path().path_str();
2508 let loc = String::from(path) + ":" + &line_num.to_string();
2509 let (err, err_type) = match self.first_parent_class_name() {
2511 errors::redeclaration_of_function(function_name, &loc),
2512 ErrorType::RuntimeError,
2514 Some(class_name) => {
2516 String::from(class_name) + "::" + function_name;
2518 errors::redeclaration_of_method(&full_name),
2519 ErrorType::ParseError,
2524 .push(Self::make_error_from_node_with_type(node, err, err_type))
2526 Some(prev_def) if (prev_def.kind != NameDef) => {
2527 let (line_num, _) = self
2530 .offset_to_position(prev_def.location.start_offset as isize);
2531 let line_num = line_num as usize;
2533 self.errors.push(Self::make_name_already_used_error(
2535 &combine_names(&self.namespace_name, &function_name),
2538 &|x, y| errors::declared_name_is_already_in_use(line_num, x, y),
2543 self.names.functions.add(function_name, def)
2545 _ if self.env.is_typechecker() => self.errors.push(Self::make_error_from_node(
2547 errors::decl_outside_global_scope,
2556 fn is_foreach_in_for(for_initializer: &'a Syntax<Token, Value>) -> bool {
2557 if let Some(Syntax {
2558 syntax: ListItem(x),
2560 }) = for_initializer.syntax_node_to_list().next()
2562 x.list_item.is_as_expression()
2568 fn statement_errors(&mut self, node: &'a Syntax<Token, Value>) {
2569 let expect_colon = |colon: &'a Syntax<Token, Value>| match &colon.syntax {
2570 Token(m) if self.env.is_typechecker() && m.kind() != TokenKind::Colon => {
2571 Some((colon, errors::error1020))
2575 (match &node.syntax {
2577 if x.try_catch_clauses.is_missing() && x.try_finally_clause.is_missing() =>
2579 Some((node, errors::error2007))
2581 UsingStatementFunctionScoped(_) if !self.using_statement_function_scoped_is_legal() => {
2582 Some((node, errors::using_st_function_scoped_top_level))
2584 ForStatement(x) if Self::is_foreach_in_for(&x.for_initializer) => {
2585 Some((node, errors::for_with_as_expression))
2587 CaseLabel(x) => expect_colon(&x.case_colon),
2589 DefaultLabel(x) => expect_colon(&x.default_colon),
2593 .for_each(|(error_node, error_message)| {
2595 .push(Self::make_error_from_node(error_node, error_message))
2599 fn invalid_shape_initializer_name(&mut self, node: &'a Syntax<Token, Value>) {
2600 match &node.syntax {
2601 LiteralExpression(x) => {
2602 let is_str = match Self::token_kind(&x.literal_expression) {
2603 Some(TokenKind::SingleQuotedStringLiteral) => true,
2605 // TODO: Double quoted string are only legal
2606 // if they contain no encapsulated expressions.
2607 Some(TokenKind::DoubleQuotedStringLiteral) => true,
2611 self.errors.push(Self::make_error_from_node(
2613 errors::invalid_shape_field_name,
2617 ScopeResolutionExpression(_) => (),
2618 QualifiedName(_) => {
2619 if self.env.is_typechecker() {
2620 self.errors.push(Self::make_error_from_node(
2622 errors::invalid_shape_field_name,
2626 Token(_) if node.is_name() => {
2627 if self.env.is_typechecker() {
2628 self.errors.push(Self::make_error_from_node(
2630 errors::invalid_shape_field_name,
2634 _ => self.errors.push(Self::make_error_from_node(
2636 errors::invalid_shape_field_name,
2641 fn invalid_shape_field_check(&mut self, node: &'a Syntax<Token, Value>) {
2642 if let FieldInitializer(x) = &node.syntax {
2643 self.invalid_shape_initializer_name(&x.field_initializer_name)
2645 self.errors.push(Self::make_error_from_node(
2647 errors::invalid_shape_field_name,
2652 fn is_in_unyieldable_magic_method(&self) -> bool {
2653 self.first_parent_function_name().map_or(false, |s| {
2654 let s = s.to_ascii_lowercase();
2656 _ if s == sn::members::__INVOKE => false,
2657 _ => sn::members::AS_LOWERCASE_SET.contains(&s),
2662 fn function_call_argument_errors(
2664 in_constructor_call: bool,
2665 node: &'a Syntax<Token, Value>,
2667 if let Some(e) = match &node.syntax {
2668 VariableExpression(x)
2669 if self.text(&x.variable_expression) == sn::superglobals::GLOBALS =>
2671 Some(errors::globals_without_subscript)
2673 DecoratedExpression(x) => {
2674 if let Token(token) = &x.decorated_expression_decorator.syntax {
2675 if token.kind() == TokenKind::Inout {
2676 let expression = &x.decorated_expression_expression;
2677 match &expression.syntax {
2678 _ if in_constructor_call => Some(errors::inout_param_in_construct),
2679 VariableExpression(x)
2680 if sn::superglobals::is_any_global(
2681 self.text(&x.variable_expression),
2684 Some(errors::fun_arg_invalid_arg)
2686 BinaryExpression(_) => Some(errors::fun_arg_inout_set),
2687 QualifiedName(_) => Some(errors::fun_arg_inout_const),
2688 Token(_) if expression.is_name() => Some(errors::fun_arg_inout_const),
2689 // TODO: Maybe be more descriptive in error messages
2690 ScopeResolutionExpression(_)
2691 | FunctionCallExpression(_)
2692 | MemberSelectionExpression(_)
2693 | SafeMemberSelectionExpression(_) => Some(errors::fun_arg_invalid_arg),
2694 SubscriptExpression(x) => match &x.subscript_receiver.syntax {
2695 MemberSelectionExpression(_) | ScopeResolutionExpression(_) => {
2696 Some(errors::fun_arg_invalid_arg)
2699 let text = self.text(&x.subscript_receiver);
2700 if sn::superglobals::is_any_global(text) {
2701 Some(errors::fun_arg_inout_containers)
2718 self.errors.push(Self::make_error_from_node(node, e))
2722 fn function_call_on_xhp_name_errors(&mut self, node: &'a Syntax<Token, Value>) {
2723 let check = |self_: &mut Self,
2724 member_object: &'a Syntax<Token, Value>,
2725 name: &'a Syntax<Token, Value>| {
2726 if let XHPExpression(_) = &member_object.syntax {
2727 if self_.env.is_typechecker() {
2728 self_.errors.push(Self::make_error_from_node(
2730 errors::method_calls_on_xhp_expression,
2735 if let Token(token) = &name.syntax {
2736 if token.kind() == TokenKind::XHPClassName {
2737 self_.errors.push(Self::make_error_from_node(
2739 errors::method_calls_on_xhp_attributes,
2744 match &node.syntax {
2745 MemberSelectionExpression(x) => check(self, &x.member_object, &x.member_name),
2746 SafeMemberSelectionExpression(x) => {
2747 check(self, &x.safe_member_object, &x.safe_member_name)
2753 fn no_async_before_lambda_body(&mut self, body_node: &'a Syntax<Token, Value>) {
2754 if let AwaitableCreationExpression(_) = &body_node.syntax {
2755 if self.env.is_typechecker() {
2756 self.errors.push(Self::make_error_from_node(
2758 errors::no_async_before_lambda_body,
2764 fn no_memoize_attribute_on_lambda(&mut self, node: &'a Syntax<Token, Value>) {
2765 match &node.syntax {
2766 OldAttributeSpecification(_) | AttributeSpecification(_) => {
2767 for node in Self::attr_spec_to_node_list(node) {
2768 match self.attr_name(node) {
2770 if n == sn::user_attributes::MEMOIZE
2771 || n == sn::user_attributes::MEMOIZE_LSB =>
2774 .push(Self::make_error_from_node(node, errors::memoize_on_lambda))
2786 fn is_good_scope_resolution_qualifier(node: &'a Syntax<Token, Value>) -> bool {
2787 match &node.syntax {
2788 QualifiedName(_) => true,
2789 Token(token) => match token.kind() {
2790 TokenKind::XHPClassName
2792 | TokenKind::SelfToken
2794 | TokenKind::Static => true,
2801 fn new_variable_errors_(
2803 node: &'a Syntax<Token, Value>,
2804 inside_scope_resolution: bool,
2806 match &node.syntax {
2807 SimpleTypeSpecifier(_)
2808 | VariableExpression(_)
2809 | GenericTypeSpecifier(_)
2810 | PipeVariableExpression(_) => (),
2811 SubscriptExpression(x) if x.subscript_index.is_missing() => self.errors.push(
2812 Self::make_error_from_node(node, errors::instanceof_missing_subscript_index),
2814 SubscriptExpression(x) => {
2815 self.new_variable_errors_(&x.subscript_receiver, inside_scope_resolution)
2817 MemberSelectionExpression(x) => {
2818 if inside_scope_resolution {
2819 self.errors.push(Self::make_error_from_node(
2821 errors::instanceof_memberselection_inside_scoperesolution,
2824 self.new_variable_errors_(&x.member_object, inside_scope_resolution)
2827 ScopeResolutionExpression(x) => {
2828 if let Token(name) = &x.scope_resolution_name.syntax {
2829 if Self::is_good_scope_resolution_qualifier(&x.scope_resolution_qualifier)
2830 && name.kind() == TokenKind::Variable
2833 } else if name.kind() == TokenKind::Variable {
2834 self.new_variable_errors_(&x.scope_resolution_qualifier, true)
2836 self.errors.push(Self::make_error_from_node(
2838 errors::instanceof_invalid_scope_resolution,
2842 self.errors.push(Self::make_error_from_node(
2844 errors::instanceof_invalid_scope_resolution,
2849 self.errors.push(Self::make_error_from_node(
2851 errors::instanceof_new_unknown_node(node.kind().to_string()),
2857 fn new_variable_errors(&mut self, node: &'a Syntax<Token, Value>) {
2858 self.new_variable_errors_(node, false)
2861 fn class_type_designator_errors(&mut self, node: &'a Syntax<Token, Value>) {
2862 if !Self::is_good_scope_resolution_qualifier(node) {
2863 match &node.syntax {
2864 ParenthesizedExpression(_) => (),
2865 _ => self.new_variable_errors(node),
2870 fn rec_walk_impl<F, X>(
2872 parents: &mut Vec<&'a Syntax<Token, Value>>,
2874 node: &'a Syntax<Token, Value>,
2878 F: Fn(&'a Syntax<Token, Value>, &Vec<&'a Syntax<Token, Value>>, X) -> (bool, X),
2880 let (continue_walk, new_acc) = f(node, parents, acc);
2884 for child in node.iter_children() {
2885 acc = self.rec_walk_impl(parents, f, child, acc);
2892 fn rec_walk<F, X>(&self, f: F, node: &'a Syntax<Token, Value>, acc: X) -> X
2894 F: Fn(&'a Syntax<Token, Value>, &Vec<&'a Syntax<Token, Value>>, X) -> (bool, X),
2896 self.rec_walk_impl(&mut vec![], &f, node, acc)
2899 fn find_invalid_lval_usage(&self, node: &'a Syntax<Token, Value>) -> Vec<SyntaxError> {
2901 |node, parents, mut acc| match &node.syntax {
2902 AnonymousFunction(_) | LambdaExpression(_) | AwaitableCreationExpression(_) => {
2906 match Self::node_lval_type(node, parents) {
2907 LvalTypeFinal | LvalTypeNone => (),
2908 LvalTypeNonFinalInout | LvalTypeNonFinal => {
2909 acc.push(Self::make_error_from_node(node, errors::lval_as_expression))
2920 fn does_binop_create_write_on_left(token_kind: Option<TokenKind>) -> bool {
2921 token_kind.map_or(false, |x| match x {
2923 | TokenKind::BarEqual
2924 | TokenKind::PlusEqual
2925 | TokenKind::StarEqual
2926 | TokenKind::StarStarEqual
2927 | TokenKind::SlashEqual
2928 | TokenKind::DotEqual
2929 | TokenKind::MinusEqual
2930 | TokenKind::PercentEqual
2931 | TokenKind::CaratEqual
2932 | TokenKind::AmpersandEqual
2933 | TokenKind::LessThanLessThanEqual
2934 | TokenKind::GreaterThanGreaterThanEqual
2935 | TokenKind::QuestionQuestionEqual => true,
2940 fn get_positions_binop_allows_await(
2941 t: &'a Syntax<Token, Value>,
2942 ) -> BinopAllowsAwaitInPositions {
2944 match Self::token_kind(t) {
2945 None => BinopAllowAwaitNone,
2946 Some(t) => match t {
2947 BarBar | AmpersandAmpersand | QuestionColon | QuestionQuestion | BarGreaterThan => {
2961 | LessThanLessThanEqual
2962 | GreaterThanGreaterThanEqual => BinopAllowAwaitRight,
2975 | ExclamationEqualEqual
2977 | LessThanEqualGreaterThan
2982 | GreaterThanGreaterThan
2983 | Carat => BinopAllowAwaitBoth,
2984 QuestionQuestionEqual | _ => BinopAllowAwaitNone,
2989 fn unop_allows_await(t: &'a Syntax<Token, Value>) -> bool {
2991 Self::token_kind(t).map_or(false, |t| match t {
2992 Exclamation | Tilde | Plus | Minus | At | Clone | Print => true,
2997 fn await_as_an_expression_errors(&mut self, await_node: &'a Syntax<Token, Value>) {
2998 let mut prev = None;
2999 let mut node = await_node;
3000 for n in self.parents.iter().rev() {
3001 if let Some(prev) = prev {
3006 // statements that root for the concurrently executed await expressions
3007 ExpressionStatement(_)
3008 | ReturnStatement(_)
3011 | ThrowStatement(_) => break,
3012 IfStatement(x) if node as *const _ == &x.if_condition as *const _ => break,
3013 ForStatement(x) if node as *const _ == &x.for_initializer as *const _ => break,
3014 SwitchStatement(x) if node as *const _ == &x.switch_expression as *const _ => break,
3015 ForeachStatement(x) if node as *const _ == &x.foreach_collection as *const _ => {
3018 UsingStatementBlockScoped(x)
3019 if node as *const _ == &x.using_block_expressions as *const _ =>
3023 UsingStatementFunctionScoped(x)
3024 if node as *const _ == &x.using_function_expression as *const _ =>
3028 LambdaExpression(x) if node as *const _ == &x.lambda_body as *const _ => break,
3029 // Dependent awaits are not allowed currently
3030 PrefixUnaryExpression(x)
3031 if Self::token_kind(&x.prefix_unary_operator) == Some(TokenKind::Await) =>
3033 self.errors.push(Self::make_error_from_node(
3035 errors::invalid_await_position_dependent,
3039 // Unary based expressions have their own custom fanout
3040 PrefixUnaryExpression(x) if Self::unop_allows_await(&x.prefix_unary_operator) => {
3043 PostfixUnaryExpression(x) if Self::unop_allows_await(&x.postfix_unary_operator) => {
3046 DecoratedExpression(x)
3047 if Self::unop_allows_await(&x.decorated_expression_decorator) =>
3051 // Special case the pipe operator error message
3053 if node as *const _ == &x.binary_right_operand as *const _
3054 && Self::token_kind(&x.binary_operator)
3055 == Some(TokenKind::BarGreaterThan) =>
3057 self.errors.push(Self::make_error_from_node(
3059 errors::invalid_await_position_pipe,
3063 // left or right operand of binary expressions are considered legal locations
3064 // if operator is not short-circuiting and containing expression
3065 // is in legal location
3067 if (match Self::get_positions_binop_allows_await(&x.binary_operator) {
3068 BinopAllowAwaitBoth => true,
3069 BinopAllowAwaitLeft => {
3070 node as *const _ == &x.binary_left_operand as *const _
3072 BinopAllowAwaitRight => {
3073 node as *const _ == &x.binary_right_operand as *const _
3075 BinopAllowAwaitNone => false,
3080 // test part of conditional expression is considered legal location if
3081 // onditional expression itself is in legal location
3082 ConditionalExpression(x) if node as *const _ == &x.conditional_test as *const _ => {
3085 FunctionCallExpression(x)
3086 if node as *const _ == &x.function_call_receiver as *const _
3087 || node as *const _ == &x.function_call_argument_list as *const _
3089 .function_call_receiver
3090 .is_safe_member_selection_expression() =>
3095 // object of member selection expression or safe member selection expression
3096 // is in legal position if member selection expression itself is in legal position
3097 SafeMemberSelectionExpression(x)
3098 if node as *const _ == &x.safe_member_object as *const _ =>
3103 // These are nodes where any position is valid
3105 | MemberSelectionExpression(_)
3106 | ScopeResolutionExpression(_)
3109 | NullableAsExpression(_)
3110 | IssetExpression(_)
3111 | ParenthesizedExpression(_)
3112 | BracedExpression(_)
3113 | EmbeddedBracedExpression(_)
3114 | CollectionLiteralExpression(_)
3115 | ObjectCreationExpression(_)
3116 | ConstructorCall(_)
3117 | ShapeExpression(_)
3118 | TupleExpression(_)
3119 | ArrayIntrinsicExpression(_)
3120 | DarrayIntrinsicExpression(_)
3121 | DictionaryIntrinsicExpression(_)
3122 | KeysetIntrinsicExpression(_)
3123 | VarrayIntrinsicExpression(_)
3124 | VectorIntrinsicExpression(_)
3125 | ElementInitializer(_)
3126 | FieldInitializer(_)
3127 | SimpleInitializer(_)
3128 | SubscriptExpression(_)
3129 | EmbeddedSubscriptExpression(_)
3130 | YieldExpression(_)
3133 | XHPSimpleAttribute(_)
3134 | XHPSpreadAttribute(_)
3136 | ListItem(_) => continue,
3137 // otherwise report error and bail out
3139 self.errors.push(Self::make_error_from_node(
3141 errors::invalid_await_position,
3147 let is_in_concurrent = self
3151 .any(|parent| match &parent.syntax {
3152 ConcurrentStatement(_) => true,
3155 if !is_in_concurrent {
3156 let await_node_statement_parent =
3160 .find(|parent| match &parent.syntax {
3161 ExpressionStatement(_)
3162 | ReturnStatement(_)
3168 | SwitchStatement(_)
3169 | ForeachStatement(_) => true,
3172 if let Some(x) = await_node_statement_parent {
3173 for error in self.find_invalid_lval_usage(x) {
3174 self.errors.push(error)
3177 // We must have already errored in for loop
3182 fn check_prefix_unary_dollar(node: &'a Syntax<Token, Value>) -> bool {
3183 match &node.syntax {
3184 PrefixUnaryExpression(x)
3185 if Self::token_kind(&x.prefix_unary_operator) == Some(TokenKind::Dollar) =>
3187 Self::check_prefix_unary_dollar(&x.prefix_unary_operand)
3189 BracedExpression(_) | SubscriptExpression(_) | VariableExpression(_) => false, // these ones are valid
3190 LiteralExpression(_) | PipeVariableExpression(_) => false, // these ones get caught later
3195 fn node_has_await_child(&mut self, node: &'a Syntax<Token, Value>) -> bool {
3197 |node, _parents, acc| {
3198 let is_new_scope = match &node.syntax {
3199 AnonymousFunction(_) | LambdaExpression(_) | AwaitableCreationExpression(_) => {
3207 let is_await = |n: &'a Syntax<Token, Value>| match &n.syntax {
3208 PrefixUnaryExpression(x)
3209 if Self::token_kind(&x.prefix_unary_operator)
3210 == Some(TokenKind::Await) =>
3216 let found_await = acc || is_await(node);
3217 (!found_await, found_await)
3225 fn expression_errors(&mut self, node: &'a Syntax<Token, Value>) {
3226 let check_is_as_expression = |self_: &mut Self, hint: &'a Syntax<Token, Value>| {
3227 let n = match &node.syntax {
3228 IsExpression(_) => "is",
3231 match &hint.syntax {
3232 ClosureTypeSpecifier(_) if self_.env.is_hhvm_compat() => {
3233 self_.errors.push(Self::make_error_from_node(
3235 errors::invalid_is_as_expression_hint(n, "Callable"),
3238 SoftTypeSpecifier(_) => {
3239 self_.errors.push(Self::make_error_from_node(
3241 errors::invalid_is_as_expression_hint(n, "Soft"),
3244 AttributizedSpecifier(x)
3245 if self_.attribute_specification_contains(
3246 &x.attributized_specifier_attribute_spec,
3250 self_.errors.push(Self::make_error_from_node(
3252 errors::invalid_is_as_expression_hint(n, "Soft"),
3258 match &node.syntax {
3259 // We parse the right hand side of `new` as a generic expression, but PHP
3260 // (and therefore Hack) only allow a certain subset of expressions, so we
3261 // should verify here that the expression we parsed is in that subset.
3262 // Refer: https://github.com/php/php-langspec/blob/master/spec/10-expressions.md#instanceof-operator*)
3263 ConstructorCall(ctr_call) => {
3265 Self::syntax_to_list_no_separators(&ctr_call.constructor_call_argument_list)
3267 self.function_call_argument_errors(true, p);
3269 self.class_type_designator_errors(&ctr_call.constructor_call_type);
3270 if self.env.is_typechecker() {
3271 // attr or list item -> syntax list -> attribute
3272 match self.parents.iter().rev().nth(2) {
3274 if a.is_attribute_specification()
3275 || a.is_old_attribute_specification()
3276 || a.is_file_attribute_specification() =>
3281 if ctr_call.constructor_call_left_paren.is_missing()
3282 || ctr_call.constructor_call_right_paren.is_missing()
3284 let node = &ctr_call.constructor_call_type;
3285 let constructor_name = self.text(&ctr_call.constructor_call_type);
3286 self.errors.push(Self::make_error_from_node(
3288 errors::error2038(constructor_name),
3295 LiteralExpression(x) => {
3296 if let Token(token) = &x.literal_expression.syntax {
3297 if token.kind() == TokenKind::DecimalLiteral
3298 || token.kind() == TokenKind::DecimalLiteral
3300 let text = self.text(&x.literal_expression);
3301 if text.parse::<i64>().is_err() {
3302 let error_text = if token.kind() == TokenKind::DecimalLiteral {
3303 errors::error2071(text)
3305 errors::error2072(text)
3308 .push(Self::make_error_from_node(node, error_text))
3314 SubscriptExpression(x)
3315 if self.env.is_typechecker() && x.subscript_left_bracket.is_left_brace() =>
3318 .push(Self::make_error_from_node(node, errors::error2020))
3321 FunctionCallExpression(x) => {
3322 let arg_list = &x.function_call_argument_list;
3323 if let Some(h) = Self::misplaced_variadic_arg(arg_list) {
3325 .push(Self::make_error_from_node(h, errors::error2033))
3328 for p in Self::syntax_to_list_no_separators(arg_list) {
3329 self.function_call_argument_errors(false, p)
3331 self.function_call_on_xhp_name_errors(&x.function_call_receiver);
3333 ListExpression(x) if x.list_members.is_missing() && self.env.is_hhvm_compat() => {
3334 if let Some(Syntax {
3335 syntax: ForeachStatement(x),
3337 }) = self.parents.last()
3339 if node as *const _ == &x.foreach_value as *const _ {
3340 self.errors.push(Self::make_error_from_node_with_type(
3343 ErrorType::RuntimeError,
3349 ListExpression(_) => {
3353 .map_or(false, |e| e.is_return_statement())
3356 .push(Self::make_error_from_node(node, errors::list_must_be_lvar))
3359 ShapeExpression(x) => {
3360 for f in Self::syntax_to_list_no_separators(&x.shape_expression_fields).rev() {
3361 self.invalid_shape_field_check(f)
3364 DecoratedExpression(x) => {
3365 let decorator = &x.decorated_expression_decorator;
3366 if Self::token_kind(decorator) == Some(TokenKind::Await) {
3367 self.await_as_an_expression_errors(node)
3370 YieldExpression(_) => {
3371 if self.is_in_unyieldable_magic_method() {
3372 self.errors.push(Self::make_error_from_node(
3374 errors::yield_in_magic_methods,
3377 if self.env.context.active_callable.is_none() {
3378 self.errors.push(Self::make_error_from_node(
3380 errors::yield_outside_function,
3384 if self.has_inout_params() {
3385 let e = if self.is_inside_async_method() {
3386 errors::inout_param_in_async_generator
3388 errors::inout_param_in_generator
3390 self.errors.push(Self::make_error_from_node_with_type(
3393 ErrorType::RuntimeError,
3397 ScopeResolutionExpression(x) => {
3398 let qualifier = &x.scope_resolution_qualifier;
3399 let name = &x.scope_resolution_name;
3401 let (is_dynamic_name, is_self_or_parent, is_valid) =
3402 // PHP langspec allows string literals, variables
3403 // qualified names, static, self and parent as valid qualifiers
3404 // We do not allow string literals in hack
3405 match (&qualifier.syntax , Self::token_kind(qualifier)) {
3406 | (LiteralExpression (_), _) => (false, false, false),
3407 | (QualifiedName (_), _) => (false, false, true),
3408 | (_, Some (TokenKind::Name))
3409 | (_, Some (TokenKind::XHPClassName))
3410 | (_, Some (TokenKind::Static)) =>
3411 (false, false, true),
3412 | (_, Some (TokenKind::SelfToken))
3413 | (_, Some (TokenKind::Parent)) =>
3414 (false, true, true),
3416 | (PrefixUnaryExpression (x), _) if Self::token_kind(&x.prefix_unary_operator) == Some (TokenKind::Dollar) =>
3417 (true, false, true),
3418 | (PipeVariableExpression (_), _)
3419 | (VariableExpression (_), _)
3420 | (SimpleTypeSpecifier (_), _)
3421 | (GenericTypeSpecifier (_), _) =>
3422 (true, false, true),
3423 | _ => (true, false, false),
3425 if !is_valid && self.env.is_typechecker() {
3426 self.errors.push(Self::make_error_from_node(
3428 errors::invalid_scope_resolution_qualifier,
3431 let is_name_class = self.text(name).eq_ignore_ascii_case("class");
3432 if (is_dynamic_name || !is_valid) && is_name_class {
3433 self.errors.push(Self::make_error_from_node(
3435 errors::coloncolonclass_on_dynamic,
3438 let text_name = self.text(qualifier);
3439 let is_name_namespace = text_name.eq_ignore_ascii_case("namespace");
3440 if is_name_namespace {
3441 self.errors.push(Self::make_error_from_node(
3443 errors::namespace_not_a_classname,
3446 if is_self_or_parent && is_name_class && !self.is_in_active_class_scope() {
3447 self.errors.push(Self::make_error_from_node_with_type(
3449 errors::self_or_parent_colon_colon_class_outside_of_class(text_name),
3450 ErrorType::RuntimeError,
3455 PrefixUnaryExpression(x)
3456 if Self::token_kind(&x.prefix_unary_operator) == Some(TokenKind::Dollar) =>
3458 if Self::check_prefix_unary_dollar(node) {
3460 .push(Self::make_error_from_node(node, errors::dollar_unary))
3464 // TODO(T21285960): Remove this bug-port, stemming from T22184312
3466 if self.env.is_hhvm_compat()
3467 && !x.lambda_async.is_missing()
3468 && x.lambda_async.trailing_width() == 0
3469 && x.lambda_signature.leading_width() == 0 =>
3472 .push(Self::make_error_from_node(node, errors::error1057("==>")))
3475 IsExpression(x) => check_is_as_expression(self, &x.is_right_operand),
3476 AsExpression(x) => check_is_as_expression(self, &x.as_right_operand),
3478 ConditionalExpression(x) => {
3479 if x.conditional_consequence.is_missing() && self.env.is_typechecker() {
3480 self.errors.push(Self::make_error_from_node(
3482 errors::elvis_operator_space,
3485 if x.conditional_test.is_conditional_expression() && self.env.is_typechecker() {
3487 .push(Self::make_error_from_node(node, errors::nested_ternary))
3489 match &x.conditional_alternative.syntax {
3491 if x.lambda_body.is_conditional_expression()
3492 && self.env.is_typechecker() =>
3495 .push(Self::make_error_from_node(node, errors::nested_ternary))
3500 LambdaExpression(x) => {
3501 self.no_memoize_attribute_on_lambda(&x.lambda_attribute_spec);
3502 self.no_async_before_lambda_body(&x.lambda_body);
3504 AnonymousFunction(x) => {
3505 self.no_memoize_attribute_on_lambda(&x.anonymous_attribute_spec)
3507 AwaitableCreationExpression(x) => {
3508 self.no_memoize_attribute_on_lambda(&x.awaitable_attribute_spec)
3511 CollectionLiteralExpression(x) => {
3519 let n = &x.collection_literal_name;
3520 let initializers = &x.collection_literal_initializers;
3522 let is_standard_collection = |lc_name: &str| {
3523 lc_name.eq_ignore_ascii_case("pair")
3524 || lc_name.eq_ignore_ascii_case("vector")
3525 || lc_name.eq_ignore_ascii_case("map")
3526 || lc_name.eq_ignore_ascii_case("set")
3527 || lc_name.eq_ignore_ascii_case("immvector")
3528 || lc_name.eq_ignore_ascii_case("immmap")
3529 || lc_name.eq_ignore_ascii_case("immset")
3531 let use_key_value_initializers = |lc_name: &str| {
3532 lc_name.eq_ignore_ascii_case("map") || lc_name.eq_ignore_ascii_case("immmap")
3534 let is_qualified_std_collection = |l, r| {
3535 Self::token_kind(l) == Some(TokenKind::Name)
3536 && Self::token_kind(r) == Some(TokenKind::Name)
3537 && self.text(l).eq_ignore_ascii_case("hh")
3538 && is_standard_collection(&self.text(r))
3541 let check_type_specifier = |n, t: &Token| {
3542 if t.kind() == TokenKind::Name {
3543 match self.text(n).to_ascii_lowercase().as_ref() {
3544 "dict" | "vec" | "keyset" => InvalidBraceKind,
3546 if is_standard_collection(n) {
3547 ValidClass(n.to_string())
3558 let check_qualified_name = |parts| {
3559 let mut parts = Self::syntax_to_list(false, parts);
3560 let p1 = parts.next();
3561 let p2 = parts.next();
3562 let p3 = parts.next();
3563 let p4 = parts.next();
3564 match (p1, p2, p3, p4) {
3565 (Some(l), Some(r), None, None)
3566 if self.namespace_name == GLOBAL_NAMESPACE_NAME
3567 && is_qualified_std_collection(l, r) =>
3569 // HH\Vector in global namespace
3570 ValidClass(self.text(r).to_ascii_lowercase())
3572 (Some(missing), Some(l), Some(r), None)
3573 if missing.is_missing() && is_qualified_std_collection(l, r) =>
3576 ValidClass(self.text(r).to_ascii_lowercase())
3581 let status = match &n.syntax {
3582 // non-qualified name
3583 SimpleTypeSpecifier(x) => match &x.simple_type_specifier.syntax {
3584 Token(t) => check_type_specifier(&x.simple_type_specifier, t),
3585 QualifiedName(x) => check_qualified_name(&x.qualified_name_parts),
3588 GenericTypeSpecifier(x) => match &x.generic_class_type.syntax {
3589 Token(t) => check_type_specifier(&x.generic_class_type, t),
3590 QualifiedName(x) => check_qualified_name(&x.qualified_name_parts),
3596 let is_key_value = |s: &Syntax<Token, Value>| {
3597 if let ElementInitializer(_) = s.syntax {
3603 let initializer_list = || Self::syntax_to_list_no_separators(initializers);
3604 let num_initializers = initializer_list().count();
3607 if use_key_value_initializers(name)
3608 && initializer_list().any(|i| !is_key_value(i)) =>
3610 self.errors.push(Self::make_error_from_node(
3612 errors::invalid_value_initializer(self.text(n)),
3617 if !use_key_value_initializers(name)
3618 && initializer_list().any(|i| is_key_value(i)) =>
3620 self.errors.push(Self::make_error_from_node(
3622 errors::invalid_key_value_initializer(self.text(n)),
3626 ValidClass(pair) if pair == "pair" && num_initializers != 2 => {
3627 let msg = if num_initializers == 0 {
3628 errors::pair_initializer_needed
3630 errors::pair_initializer_arity
3632 self.errors.push(Self::make_error_from_node_with_type(
3635 ErrorType::RuntimeError,
3639 ValidClass(_) => (),
3640 InvalidBraceKind => self.errors.push(Self::make_error_from_node(
3642 errors::invalid_brace_kind_in_collection_initializer,
3644 InvalidClass => self.errors.push(Self::make_error_from_node(
3646 errors::invalid_class_in_collection_initializer,
3650 PrefixUnaryExpression(x)
3651 if Self::token_kind(&x.prefix_unary_operator) == Some(TokenKind::Await) =>
3653 self.await_as_an_expression_errors(node)
3655 // Other kinds of expressions currently produce no expr errors.
3660 fn check_repeated_properties_tconst_const(
3663 prop: &'a Syntax<Token, Value>,
3664 p_names: &mut HashSet<String>,
3665 c_names: &mut HashSet<String>,
3667 let mut check = |sname, names: &mut HashSet<String>| {
3668 let name = self.text(sname);
3669 // If the name is empty, then there was an earlier
3670 // parsing error that should supercede this one.
3672 } else if names.contains(name) {
3673 self.errors.push(Self::make_error_from_node(
3675 errors::redeclaration_error(
3676 &(Self::strip_ns(&full_name).to_string() + "::" + name),
3680 names.insert(name.to_owned());
3684 match &prop.syntax {
3685 PropertyDeclaration(x) => {
3686 for prop in Self::syntax_to_list_no_separators(&x.property_declarators) {
3687 if let PropertyDeclarator(x) = &prop.syntax {
3688 check(&x.property_name, p_names)
3692 ConstDeclaration(x) => {
3693 for prop in Self::syntax_to_list_no_separators(&x.const_declarators) {
3694 if let ConstantDeclarator(x) = &prop.syntax {
3695 check(&x.constant_declarator_name, c_names)
3699 TypeConstDeclaration(x) => check(&x.type_const_name, c_names),
3704 fn require_errors(&mut self, node: &'a Syntax<Token, Value>) {
3705 if let RequireClause(p) = &node.syntax {
3706 let name = self.text(&p.require_name);
3707 let req_kind = Self::token_kind(&p.require_kind);
3708 match (self.trait_require_clauses.get(name), req_kind) {
3709 (None, Some(tk)) => self.trait_require_clauses.add(name, tk),
3710 (Some(tk1), Some(tk2)) if *tk1 == tk2 =>
3711 // duplicate, it is okay
3714 // Conflicting entry
3715 self.errors.push(Self::make_error_from_node(
3717 errors::conflicting_trait_require_clauses(name),
3721 match (self.active_classish_kind(), req_kind) {
3722 (Some(TokenKind::Interface), Some(TokenKind::Implements))
3723 | (Some(TokenKind::Class), Some(TokenKind::Implements)) => self
3725 .push(Self::make_error_from_node(node, errors::error2030)),
3733 name: &'a Syntax<Token, Value>,
3737 match self.names.classes.get(name_text) {
3738 Some(FirstUseOrDef {
3743 }) if &combine_names(&self.namespace_name, name_text) != def_name
3744 && *kind != NameDef =>
3746 let (line_num, _) = self
3749 .offset_to_position(location.start_offset as isize);
3750 let line_num = line_num as usize;
3751 let long_name_text = combine_names(&self.namespace_name, name_text);
3752 self.errors.push(Self::make_name_already_used_error(
3757 &|x, y| match kind {
3758 NameImplicitUse => {
3759 errors::declared_name_is_already_in_use_implicit_hh(line_num, x, y)
3761 NameUse => errors::declared_name_is_already_in_use(line_num, x, y),
3762 NameDef => errors::type_name_is_already_in_use(x, y),
3767 let def = make_first_use_or_def(
3771 &self.namespace_name,
3774 self.names.classes.add(&name_text, def)
3779 fn get_type_params_and_emit_shadowing_errors(
3781 l: &'a Syntax<Token, Value>,
3782 ) -> (HashSet<&'a str>, HashSet<&'a str>) {
3783 let mut res: HashSet<&'a str> = HashSet::new();
3784 let mut notreified: HashSet<&'a str> = HashSet::new();
3785 for p in Self::syntax_to_list_no_separators(l).rev() {
3787 TypeParameter(x) => {
3788 let name = self.text(&x.type_name);
3789 if !x.type_reified.is_missing() {
3790 if res.contains(&name) {
3792 .push(Self::make_error_from_node(p, errors::shadowing_reified))
3797 notreified.insert(name);
3806 fn reified_parameter_errors(&mut self, node: &'a Syntax<Token, Value>) {
3807 if let FunctionDeclarationHeader(x) = &node.syntax {
3808 if let TypeParameters(x) = &x.function_type_parameter_list.syntax {
3809 self.get_type_params_and_emit_shadowing_errors(&x.type_parameters_parameters)
3815 fn is_method_declaration(node: &'a Syntax<Token, Value>) -> bool {
3816 if let MethodishDeclaration(_) = &node.syntax {
3823 fn class_reified_param_errors(&mut self, node: &'a Syntax<Token, Value>) {
3824 match &node.syntax {
3825 ClassishDeclaration(cd) => {
3826 let (reified, non_reified) = match &cd.classish_type_parameters.syntax {
3827 TypeParameters(x) => self
3828 .get_type_params_and_emit_shadowing_errors(&x.type_parameters_parameters),
3829 _ => (HashSet::new(), HashSet::new()),
3832 let tparams: HashSet<&'a str> = reified
3833 .union(&non_reified)
3835 .collect::<HashSet<&'a str>>();
3837 let add_error = |self_: &mut Self, e: &'a Syntax<Token, Value>| {
3838 if let TypeParameter(x) = &e.syntax {
3839 if !x.type_reified.is_missing()
3840 && tparams.contains(&self_.text(&x.type_name))
3844 .push(Self::make_error_from_node(e, errors::shadowing_reified))
3848 let check_method = |e: &'a Syntax<Token, Value>| {
3849 if let MethodishDeclaration(x) = &e.syntax {
3850 if let FunctionDeclarationHeader(x) =
3851 &x.methodish_function_decl_header.syntax
3853 if let TypeParameters(x) = &x.function_type_parameter_list.syntax {
3854 Self::syntax_to_list_no_separators(&x.type_parameters_parameters)
3856 .for_each(|x| add_error(self, x))
3861 if let ClassishBody(x) = &cd.classish_body.syntax {
3862 Self::syntax_to_list_no_separators(&x.classish_body_elements)
3864 .for_each(check_method)
3867 if !reified.is_empty() {
3868 if Self::is_token_kind(&cd.classish_keyword, TokenKind::Interface) {
3869 self.errors.push(Self::make_error_from_node(
3871 errors::reified_in_invalid_classish("an interface"),
3873 } else if Self::is_token_kind(&cd.classish_keyword, TokenKind::Trait) {
3874 self.errors.push(Self::make_error_from_node(
3876 errors::reified_in_invalid_classish("a trait"),
3881 PropertyDeclaration(_) => {
3882 if Self::has_modifier_static(node) && self.is_in_reified_class() {
3883 self.errors.push(Self::make_error_from_node(
3885 errors::static_property_in_reified_class,
3893 fn attr_spec_contains_sealed(&self, node: &'a Syntax<Token, Value>) -> bool {
3894 self.attribute_specification_contains(node, sn::user_attributes::SEALED)
3897 fn attr_spec_contains_const(&self, node: &'a Syntax<Token, Value>) -> bool {
3898 self.attribute_specification_contains(node, sn::user_attributes::CONST)
3901 // If there's more than one XHP category, report an error on the last one.
3902 fn duplicate_xhp_category_errors<I>(&mut self, elts: I)
3904 I: Iterator<Item = &'a Syntax<Token, Value>>,
3906 let mut iter = elts.filter(|x| match &x.syntax {
3907 XHPCategoryDeclaration(_) => true,
3911 if let Some(node) = iter.last() {
3912 self.errors.push(Self::make_error_from_node(
3914 errors::xhp_class_multiple_category_decls,
3919 // If there's more than one XHP children declaration, report an error
3921 fn duplicate_xhp_children_errors<I>(&mut self, elts: I)
3923 I: Iterator<Item = &'a Syntax<Token, Value>>,
3925 let mut iter = elts.filter(|x| match &x.syntax {
3926 XHPChildrenDeclaration(_) => true,
3930 if let Some(node) = iter.last() {
3931 self.errors.push(Self::make_error_from_node(
3933 errors::xhp_class_multiple_children_decls,
3938 fn interface_private_method_errors<I>(&mut self, elts: I)
3940 I: Iterator<Item = &'a Syntax<Token, Value>>,
3943 if let Some(modifiers) = Self::get_modifiers_of_declaration(elt) {
3944 for modifier in Self::syntax_to_list_no_separators(modifiers) {
3945 if modifier.is_private() {
3946 self.errors.push(Self::make_error_from_node(
3948 errors::interface_has_private_method,
3956 fn classish_errors(&mut self, node: &'a Syntax<Token, Value>) {
3957 if let ClassishDeclaration(cd) = &node.syntax {
3958 // Given a ClassishDeclaration node, test whether or not it's a trait
3959 // invoking the 'extends' keyword.
3960 let classish_invalid_extends_keyword = |_|
3961 // Invalid if uses 'extends' and is a trait.
3962 Self::token_kind(&cd.classish_extends_keyword) == Some(TokenKind::Extends)
3963 && Self::token_kind(&cd.classish_keyword) == Some(TokenKind::Trait);
3965 let abstract_keyword = Self::extract_keyword(|x| x.is_abstract(), node).unwrap_or(node);
3968 |self_, x| self_.is_classish_kind_declared_abstract(x),
3970 || errors::error2042,
3974 // Given a sealed ClassishDeclaration node, test whether all the params
3976 let classish_sealed_arg_not_classname = |self_: &mut Self| {
3977 Self::attr_spec_to_node_list(&cd.classish_attribute).any(|node| {
3978 self_.attr_name(node) == Some(sn::user_attributes::SEALED)
3979 && self_.attr_args(node).map_or(false, |mut args| {
3980 args.any(|arg_node| match &arg_node.syntax {
3981 ScopeResolutionExpression(x) => {
3982 self_.text(&x.scope_resolution_name) != "class"
3990 let classish_is_sealed = self.attr_spec_contains_sealed(&cd.classish_attribute);
3992 // Given a ClassishDeclaration node, test whether or not length of
3993 // extends_list is appropriate for the classish_keyword. *)
3994 let classish_invalid_extends_list = |self_: &mut Self| {
3995 // Invalid if is a class and has list of length greater than one.
3996 self_.env.is_typechecker()
3997 && Self::token_kind(&cd.classish_keyword) == Some(TokenKind::Class)
3998 && Self::token_kind(&cd.classish_extends_keyword) == Some(TokenKind::Extends)
3999 && Self::syntax_to_list_no_separators(&cd.classish_extends_list).count() != 1
4002 // Given a ClassishDeclaration node, test whether it is sealed and final.
4003 let classish_sealed_final = |_| {
4004 Self::list_contains_predicate(|x| x.is_final(), &cd.classish_modifiers)
4005 && classish_is_sealed
4009 |self_, _| classish_invalid_extends_list(self_),
4011 || errors::error2037,
4012 &cd.classish_extends_list,
4015 if let Some(n) = self.attribute_first_reactivity_annotation(&cd.classish_attribute) {
4016 self.errors.push(Self::make_error_from_node(
4018 errors::misplaced_reactivity_annotation,
4022 self.invalid_modifier_errors("Classes, interfaces, and traits", node, |kind| {
4023 kind == TokenKind::Abstract || kind == TokenKind::Final || kind == TokenKind::XHP
4027 |self_, _| classish_sealed_arg_not_classname(self_),
4029 || errors::sealed_val_not_classname,
4030 &cd.classish_attribute,
4034 |_, x| classish_invalid_extends_keyword(x),
4036 || errors::error2036,
4037 &cd.classish_extends_keyword,
4041 |_, x| classish_sealed_final(x),
4043 || errors::sealed_final,
4044 &cd.classish_attribute,
4047 let classish_name = self.text(&cd.classish_name);
4049 |_, x| Self::cant_be_classish_name(x),
4051 || errors::reserved_keyword_as_class_name(&classish_name),
4054 if Self::is_token_kind(&cd.classish_keyword, TokenKind::Interface)
4055 && !cd.classish_implements_keyword.is_missing()
4057 self.errors.push(Self::make_error_from_node(
4059 errors::interface_implements,
4062 if self.attr_spec_contains_const(&cd.classish_attribute)
4063 && (Self::is_token_kind(&cd.classish_keyword, TokenKind::Interface)
4064 || Self::is_token_kind(&cd.classish_keyword, TokenKind::Trait))
4066 self.errors.push(Self::make_error_from_node(
4068 errors::no_const_interfaces_traits_enums,
4071 if self.attr_spec_contains_const(&cd.classish_attribute)
4072 && Self::is_token_kind(&cd.classish_keyword, TokenKind::Class)
4073 && Self::list_contains_predicate(|x| x.is_abstract(), &cd.classish_modifiers)
4074 && Self::list_contains_predicate(|x| x.is_final(), &cd.classish_modifiers)
4076 self.errors.push(Self::make_error_from_node(
4078 errors::no_const_abstract_final_class,
4082 if Self::list_contains_predicate(|x| x.is_final(), &cd.classish_modifiers) {
4083 match Self::token_kind(&cd.classish_keyword) {
4084 Some(TokenKind::Interface) => self.errors.push(Self::make_error_from_node(
4086 errors::declared_final("Interfaces"),
4088 Some(TokenKind::Trait) => self.errors.push(Self::make_error_from_node(
4090 errors::declared_final("Traits"),
4096 if Self::token_kind(&cd.classish_xhp) == Some(TokenKind::XHP) {
4097 match Self::token_kind(&cd.classish_keyword) {
4098 Some(TokenKind::Interface) => self.errors.push(Self::make_error_from_node(
4100 errors::invalid_xhp_classish("Interfaces"),
4102 Some(TokenKind::Trait) => self.errors.push(Self::make_error_from_node(
4104 errors::invalid_xhp_classish("Traits"),
4106 Some(TokenKind::Enum) => self.errors.push(Self::make_error_from_node(
4108 errors::invalid_xhp_classish("Enums"),
4114 let name = self.text(&cd.classish_name);
4115 if let ClassishBody(cb) = &cd.classish_body.syntax {
4116 let declared_name_str = self.text(&cd.classish_name);
4117 let full_name = combine_names(&self.namespace_name, declared_name_str);
4119 let class_body_elts =
4120 || Self::syntax_to_list_no_separators(&cb.classish_body_elements);
4121 let class_body_methods =
4122 || class_body_elts().filter(|x| Self::is_method_declaration(x));
4124 let mut p_names = HashSet::<String>::new();
4125 let mut c_names = HashSet::<String>::new();
4126 for elt in class_body_elts() {
4127 self.check_repeated_properties_tconst_const(
4134 let has_abstract_fn = class_body_methods().any(&Self::has_modifier_abstract);
4136 && Self::is_token_kind(&cd.classish_keyword, TokenKind::Class)
4137 && !Self::list_contains_predicate(|x| x.is_abstract(), &cd.classish_modifiers)
4139 self.errors.push(Self::make_error_from_node(
4141 errors::class_with_abstract_method(name),
4145 if Self::is_token_kind(&cd.classish_keyword, TokenKind::Interface) {
4146 self.interface_private_method_errors(class_body_elts());
4149 self.duplicate_xhp_category_errors(class_body_elts());
4150 self.duplicate_xhp_children_errors(class_body_elts());
4153 match Self::token_kind(&cd.classish_keyword) {
4154 Some(TokenKind::Class) | Some(TokenKind::Trait)
4155 if !cd.classish_name.is_missing() =>
4157 let location = Self::make_location_of_node(&cd.classish_name);
4158 self.check_type_name(&cd.classish_name, name, location)
4165 // Checks for modifiers on class constants
4166 fn class_constant_modifier_errors(&mut self, node: &'a Syntax<Token, Value>) {
4167 if self.is_inside_trait() {
4169 .push(Self::make_error_from_node(node, errors::const_in_trait))
4171 self.invalid_modifier_errors("Constants", node, |kind| kind == TokenKind::Abstract);
4174 fn type_const_modifier_errors(&mut self, node: &'a Syntax<Token, Value>) {
4175 self.invalid_modifier_errors("Type constants", node, |kind| kind == TokenKind::Abstract);
4178 fn alias_errors(&mut self, node: &'a Syntax<Token, Value>) {
4179 if let AliasDeclaration(ad) = &node.syntax {
4180 if Self::token_kind(&ad.alias_keyword) == Some(TokenKind::Type)
4181 && !ad.alias_constraint.is_missing()
4183 self.errors.push(Self::make_error_from_node(
4188 if !ad.alias_name.is_missing() {
4189 let name = self.text(&ad.alias_name);
4190 let location = Self::make_location_of_node(&ad.alias_name);
4191 if let TypeConstant(_) = &ad.alias_type.syntax {
4192 if self.env.is_typechecker() {
4193 self.errors.push(Self::make_error_from_node(
4195 errors::type_alias_to_type_constant,
4200 self.check_type_name(&ad.alias_name, name, location)
4205 fn is_invalid_group_use_clause(
4206 kind: &'a Syntax<Token, Value>,
4207 clause: &'a Syntax<Token, Value>,
4209 if let NamespaceUseClause(x) = &clause.syntax {
4210 let clause_kind = &x.namespace_use_clause_kind;
4211 if kind.is_missing() {
4212 match &clause_kind.syntax {
4215 if token.kind() == TokenKind::Function
4216 || token.kind() == TokenKind::Const =>
4223 !clause_kind.is_missing()
4230 fn is_invalid_group_use_prefix(prefix: &'a Syntax<Token, Value>) -> bool {
4231 !prefix.is_namespace_prefix()
4234 fn group_use_errors(&mut self, node: &'a Syntax<Token, Value>) {
4235 if let NamespaceGroupUseDeclaration(x) = &node.syntax {
4236 let prefix = &x.namespace_group_use_prefix;
4237 let clauses = &x.namespace_group_use_clauses;
4238 let kind = &x.namespace_group_use_kind;
4239 Self::syntax_to_list_no_separators(clauses)
4240 .filter(|x| Self::is_invalid_group_use_clause(kind, x))
4241 .for_each(|clause| {
4243 .push(Self::make_error_from_node(clause, errors::error2049))
4246 |_, x| Self::is_invalid_group_use_prefix(&x),
4248 || errors::error2048,
4254 fn use_class_or_namespace_clause_errors(
4256 namespace_prefix: Option<&str>,
4258 kind: &'a Syntax<Token, Value>,
4259 cl: &'a Syntax<Token, Value>,
4262 NamespaceUseClause(x) if !&x.namespace_use_name.is_missing() => {
4263 let name = &x.namespace_use_name;
4265 let kind = if kind.is_missing() {
4266 &x.namespace_use_clause_kind
4271 let name_text = self.text(name);
4272 let qualified_name = match namespace_prefix {
4273 None => combine_names(GLOBAL_NAMESPACE_NAME, name_text),
4274 Some(p) => combine_names(p, name_text),
4276 let short_name = Self::get_short_name_from_qualified_name(
4278 self.text(&x.namespace_use_alias),
4283 error_on_global_redefinition,
4284 get_names: &dyn Fn(&mut UsedNames) -> &mut Strmap<FirstUseOrDef>,
4286 let is_global_namespace = self_.is_global_namespace();
4287 let names = get_names(&mut self_.names);
4288 match names.get(&short_name) {
4289 Some(FirstUseOrDef {
4296 || error_on_global_redefinition
4297 && (is_global_namespace || *global)
4299 self_.errors.push(Self::make_name_already_used_error(
4309 let new_use = make_first_use_or_def(
4312 Self::make_location_of_node(name),
4313 GLOBAL_NAMESPACE_NAME,
4316 names.add(&short_name, new_use)
4321 match &kind.syntax {
4322 Token(token) => match token.kind() {
4323 TokenKind::Namespace => do_check(
4326 &|x: &mut UsedNames| &mut x.namespaces,
4327 &errors::namespace_name_is_already_in_use,
4330 TokenKind::Type => do_check(
4333 &|x: &mut UsedNames| &mut x.classes,
4334 &errors::type_name_is_already_in_use,
4337 TokenKind::Function => do_check(
4340 &|x: &mut UsedNames| &mut x.functions,
4341 &errors::function_name_is_already_in_use,
4344 TokenKind::Const => do_check(
4347 &|x: &mut UsedNames| &mut x.constants,
4348 &errors::const_name_is_already_in_use,
4353 if name_text == "strict" {
4354 self.errors.push(Self::make_error_from_node(
4356 errors::strict_namespace_hh,
4359 let location = Self::make_location_of_node(name);
4361 match self.names.classes.get(&short_name) {
4362 Some(FirstUseOrDef {
4368 if &qualified_name != def_name || kind != &NameDef {
4370 self.env.text.offset_to_position(loc.start_offset as isize);
4371 let err_msg = |x: &str, y: &str| -> Error {
4372 if kind != &NameDef {
4373 if kind == &NameImplicitUse {
4374 errors::name_is_already_in_use_implicit_hh(
4378 errors::name_is_already_in_use_hh(line_num, x, y)
4381 errors::name_is_already_in_use_php(x, y)
4385 self.errors.push(Self::make_name_already_used_error(
4395 let new_use = make_first_use_or_def(
4399 GLOBAL_NAMESPACE_NAME,
4403 if !self.names.namespaces.mem(&short_name) {
4404 self.names.namespaces.add(&short_name, new_use.clone());
4405 self.names.classes.add(&short_name, new_use);
4407 self.names.classes.add(&short_name, new_use);
4420 fn is_global_in_const_decl(&self, init: &'a Syntax<Token, Value>) -> bool {
4421 if let SimpleInitializer(x) = &init.syntax {
4422 if let VariableExpression(x) = &x.simple_initializer_value.syntax {
4423 return sn::superglobals::is_any_global(self.text(&x.variable_expression));
4429 fn namespace_use_declaration_errors(&mut self, node: &'a Syntax<Token, Value>) {
4430 match &node.syntax {
4431 NamespaceUseDeclaration(x) => {
4432 Self::syntax_to_list_no_separators(&x.namespace_use_clauses).for_each(|clause| {
4433 self.use_class_or_namespace_clause_errors(None, &x.namespace_use_kind, clause)
4436 NamespaceGroupUseDeclaration(x) => {
4437 Self::syntax_to_list_no_separators(&x.namespace_group_use_clauses).for_each(
4439 match &clause.syntax {
4440 NamespaceUseClause(x) if !x.namespace_use_name.is_missing() => self
4441 .check_preceding_backslashes_qualified_name(&x.namespace_use_name),
4444 self.use_class_or_namespace_clause_errors(
4445 Some(self.text(&x.namespace_group_use_prefix)),
4446 &x.namespace_group_use_kind,
4456 fn token_text<'b>(&'b self, token: &Token) -> &'b str {
4457 self.env.text.source_text().sub_as_str(
4458 token.leading_start_offset().unwrap() + token.leading_width(),
4463 fn check_constant_expression(&mut self, node: &'a Syntax<Token, Value>) {
4464 // __FUNCTION_CREDENTIAL__ emits an object,
4465 // so it cannot be used in a constant expression
4466 let not_function_credential = |self_: &Self, token: &Token| {
4469 .eq_ignore_ascii_case("__FUNCTION_CREDENTIAL__")
4472 let is_whitelisted_function = |self_: &Self, receiver_token| {
4473 let text = self_.text(receiver_token);
4475 (!self_.env.parser_options.po_disallow_func_ptrs_in_constants
4476 && (text == Self::strip_hh_ns(sn::autoimported_functions::FUN_)
4477 || text == Self::strip_hh_ns(sn::autoimported_functions::CLASS_METH)))
4478 || (text == sn::std_lib_functions::ARRAY_MARK_LEGACY)
4479 || (text == Self::strip_ns(sn::std_lib_functions::ARRAY_MARK_LEGACY))
4482 let is_namey = |self_: &Self, token: &Token| -> bool {
4483 token.kind() == TokenKind::Name && not_function_credential(self_, token)
4486 let is_good_scope_resolution_name = |node: &'a Syntax<Token, Value>| match &node.syntax {
4487 QualifiedName(_) => true,
4490 match token.kind() {
4491 Name | Trait | Extends | Implements | Static | Abstract | Final | Private
4492 | Protected | Public | Global | Goto | Instanceof | Insteadof | Interface
4493 | Namespace | New | Try | Use | Var | List | Clone | Include | Include_once
4494 | Throw | Array | Tuple | Print | Echo | Require | Require_once | Return
4495 | Else | Elseif | Default | Break | Continue | Switch | Yield | Function
4496 | If | Finally | For | Foreach | Case | Do | While | As | Catch | Empty
4497 | Using | Class | NullLiteral | Super | Where => true,
4504 let default = |self_: &mut Self| {
4505 self_.errors.push(Self::make_error_from_node(
4507 errors::invalid_constant_initializer,
4511 let check_type_specifier = |self_: &mut Self, x: &'a Syntax<Token, Value>, initializer| {
4512 if let Token(token) = &x.syntax {
4513 if is_namey(self_, &token) {
4514 return Self::syntax_to_list_no_separators(initializer)
4515 .for_each(|x| self_.check_constant_expression(x));
4521 let check_collection_members = |self_: &mut Self, x| {
4522 Self::syntax_to_list_no_separators(x).for_each(|x| self_.check_constant_expression(&x))
4524 match &node.syntax {
4525 Missing | QualifiedName(_) | LiteralExpression(_) => {}
4527 if !is_namey(self, token) {
4531 PrefixUnaryExpression(x) => {
4532 if let Token(token) = &x.prefix_unary_operator.syntax {
4534 match token.kind() {
4535 Exclamation | Plus | Minus | Tilde => {
4536 self.check_constant_expression(&x.prefix_unary_operand)
4544 BinaryExpression(x) => {
4545 if let Token(token) = &x.binary_operator.syntax {
4547 match token.kind() {
4549 | AmpersandAmpersand
4560 | GreaterThanGreaterThan
4565 | ExclamationEqualEqual
4570 | LessThanEqualGreaterThan
4571 | QuestionColon => {
4572 self.check_constant_expression(&x.binary_left_operand);
4573 self.check_constant_expression(&x.binary_right_operand);
4581 ConditionalExpression(x) => {
4582 self.check_constant_expression(&x.conditional_test);
4583 self.check_constant_expression(&x.conditional_consequence);
4584 self.check_constant_expression(&x.conditional_alternative);
4586 SimpleInitializer(x) => {
4587 if let LiteralExpression(y) = &x.simple_initializer_value.syntax {
4588 if let SyntaxList(_) = &y.literal_expression.syntax {
4589 self.errors.push(Self::make_error_from_node(
4591 errors::invalid_constant_initializer,
4594 self.check_constant_expression(&x.simple_initializer_value)
4596 self.check_constant_expression(&x.simple_initializer_value)
4600 ParenthesizedExpression(x) => {
4601 self.check_constant_expression(&x.parenthesized_expression_expression)
4603 CollectionLiteralExpression(x) => {
4604 if let SimpleTypeSpecifier(y) = &x.collection_literal_name.syntax {
4605 check_type_specifier(
4607 &y.simple_type_specifier,
4608 &x.collection_literal_initializers,
4610 } else if let GenericTypeSpecifier(y) = &x.collection_literal_name.syntax {
4611 check_type_specifier(
4613 &y.generic_class_type,
4614 &x.collection_literal_initializers,
4621 TupleExpression(x) => check_collection_members(self, &x.tuple_expression_items),
4622 KeysetIntrinsicExpression(x) => {
4623 check_collection_members(self, &x.keyset_intrinsic_members)
4625 VarrayIntrinsicExpression(x) => {
4626 check_collection_members(self, &x.varray_intrinsic_members)
4628 DarrayIntrinsicExpression(x) => {
4629 check_collection_members(self, &x.darray_intrinsic_members)
4631 VectorIntrinsicExpression(x) => {
4632 check_collection_members(self, &x.vector_intrinsic_members)
4634 DictionaryIntrinsicExpression(x) => {
4635 check_collection_members(self, &x.dictionary_intrinsic_members)
4637 ArrayIntrinsicExpression(x) => {
4638 check_collection_members(self, &x.array_intrinsic_members)
4640 ShapeExpression(x) => check_collection_members(self, &x.shape_expression_fields),
4641 ElementInitializer(x) => {
4642 self.check_constant_expression(&x.element_key);
4643 self.check_constant_expression(&x.element_value);
4645 FieldInitializer(x) => {
4646 self.check_constant_expression(&x.field_initializer_name);
4647 self.check_constant_expression(&x.field_initializer_value);
4649 ScopeResolutionExpression(x)
4650 if Self::is_good_scope_resolution_qualifier(&x.scope_resolution_qualifier)
4651 && is_good_scope_resolution_name(&x.scope_resolution_name) => {}
4652 AsExpression(x) => match &x.as_right_operand.syntax {
4653 LikeTypeSpecifier(_) => self.check_constant_expression(&x.as_left_operand),
4654 GenericTypeSpecifier(y)
4655 if self.text(&y.generic_class_type) == sn::fb::INCORRECT_TYPE
4656 || self.text(&y.generic_class_type)
4657 == Self::strip_ns(sn::fb::INCORRECT_TYPE) =>
4659 self.check_constant_expression(&x.as_left_operand)
4663 FunctionCallExpression(x) => {
4664 let mut check_receiver_and_arguments = |receiver| {
4665 if is_whitelisted_function(self, receiver) {
4667 Self::syntax_to_list_no_separators(&x.function_call_argument_list)
4669 self.check_constant_expression(node)
4676 match &x.function_call_receiver.syntax {
4677 Token(tok) if tok.kind() == TokenKind::Name => {
4678 check_receiver_and_arguments(&x.function_call_receiver)
4680 QualifiedName(_) => check_receiver_and_arguments(&x.function_call_receiver),
4684 FunctionPointerExpression(_) => {
4685 // Bans the equivalent of inst_meth as well as class_meth and fun
4686 if self.env.parser_options.po_disallow_func_ptrs_in_constants {
4694 fn check_static_in_initializer(&mut self, initializer: &'a Syntax<Token, Value>) -> bool {
4695 if let SimpleInitializer(x) = &initializer.syntax {
4696 if let ScopeResolutionExpression(x) = &x.simple_initializer_value.syntax {
4697 if let Token(t) = &x.scope_resolution_qualifier.syntax {
4699 TokenKind::Static => return true,
4700 TokenKind::Parent => {
4702 .text(&x.scope_resolution_name)
4703 .eq_ignore_ascii_case("class")
4713 fn const_decl_errors(&mut self, node: &'a Syntax<Token, Value>) {
4714 if let ConstantDeclarator(cd) = &node.syntax {
4716 |self_, x| self_.constant_abstract_with_initializer(x),
4717 &cd.constant_declarator_initializer,
4718 || errors::error2051,
4719 &cd.constant_declarator_initializer,
4723 |self_, x| self_.constant_concrete_without_initializer(x),
4724 &cd.constant_declarator_initializer,
4725 || errors::error2050,
4726 &cd.constant_declarator_initializer,
4730 |self_, x| self_.is_global_in_const_decl(x),
4731 &cd.constant_declarator_initializer,
4732 || errors::global_in_const_decl,
4733 &cd.constant_declarator_initializer,
4735 self.check_constant_expression(&cd.constant_declarator_initializer);
4738 |self_, x| self_.check_static_in_initializer(x),
4739 &cd.constant_declarator_initializer,
4740 || errors::parent_static_const_decl,
4741 &cd.constant_declarator_initializer,
4744 if !cd.constant_declarator_name.is_missing() {
4745 let constant_name = self.text(&cd.constant_declarator_name);
4746 let location = Self::make_location_of_node(&cd.constant_declarator_name);
4747 let def = make_first_use_or_def(
4751 &self.namespace_name,
4756 self.names.constants.get(constant_name),
4757 self.first_parent_class_name(),
4759 // Only error if this is inside a class
4760 (Some(_), Some(class_name)) => {
4761 let full_name = class_name.to_string() + "::" + constant_name;
4762 self.errors.push(Self::make_error_from_node(
4764 errors::redeclaration_error(&full_name),
4767 (Some(prev_def), None) if prev_def.kind != NameDef => {
4768 let (line_num, _) = self
4771 .offset_to_position(prev_def.location.start_offset as isize);
4772 let line_num = line_num as usize;
4774 self.errors.push(Self::make_name_already_used_error(
4775 &cd.constant_declarator_name,
4776 &combine_names(&self.namespace_name, &constant_name),
4779 &|x, y| errors::declared_name_is_already_in_use(line_num, x, y),
4784 self.names.constants.add(constant_name, def)
4789 fn class_property_modifiers_errors(&mut self, node: &'a Syntax<Token, Value>) {
4790 if let PropertyDeclaration(x) = &node.syntax {
4791 let property_modifiers = &x.property_modifiers;
4793 let abstract_static_props = self.env.parser_options.po_abstract_static_props;
4794 self.invalid_modifier_errors("Properties", node, |kind| {
4795 if kind == TokenKind::Abstract {
4796 return abstract_static_props;
4798 kind == TokenKind::Static
4799 || kind == TokenKind::Private
4800 || kind == TokenKind::Protected
4801 || kind == TokenKind::Public
4805 |_, x| Self::is_empty_list_or_missing(x),
4807 || errors::property_requires_visibility,
4811 if self.env.parser_options.po_abstract_static_props {
4813 |_, n| Self::has_modifier_abstract(n) && !Self::has_modifier_static(n),
4815 || errors::abstract_instance_property,
4820 if Self::has_modifier_abstract(node) && Self::has_modifier_private(node) {
4821 self.errors.push(Self::make_error_from_node(
4823 errors::elt_abstract_private("properties"),
4829 fn class_property_const_errors(&mut self, node: &'a Syntax<Token, Value>) {
4830 if let PropertyDeclaration(x) = &node.syntax {
4831 if self.attr_spec_contains_const(&x.property_attribute_spec)
4832 && self.attribute_specification_contains(
4833 &x.property_attribute_spec,
4834 sn::user_attributes::LATE_INIT,
4837 // __LateInit together with const just doesn't make sense.
4838 self.errors.push(Self::make_error_from_node(
4840 errors::no_const_late_init_props,
4846 fn class_property_declarator_errors(&mut self, node: &'a Syntax<Token, Value>) {
4847 let check_decls = |self_: &mut Self,
4848 f: &dyn Fn(&'a Syntax<Token, Value>) -> bool,
4849 error: errors::Error,
4850 property_declarators| {
4851 Self::syntax_to_list_no_separators(property_declarators).for_each(|decl| {
4852 if let PropertyDeclarator(x) = &decl.syntax {
4853 if f(&x.property_initializer) {
4856 .push(Self::make_error_from_node(node, error.clone()))
4861 if let PropertyDeclaration(x) = &node.syntax {
4862 if self.env.parser_options.tco_const_static_props && Self::has_modifier_static(node) {
4863 if self.env.parser_options.po_abstract_static_props
4864 && Self::has_modifier_abstract(node)
4868 &|n| !n.is_missing(),
4869 errors::abstract_prop_init,
4870 &x.property_declarators,
4872 } else if self.attr_spec_contains_const(&x.property_attribute_spec) {
4875 &|n| n.is_missing(),
4876 errors::const_static_prop_init,
4877 &x.property_declarators,
4884 fn trait_use_alias_item_modifier_errors(&mut self, node: &'a Syntax<Token, Value>) {
4885 self.invalid_modifier_errors("Trait use aliases", node, |kind| {
4886 kind == TokenKind::Final
4887 || kind == TokenKind::Private
4888 || kind == TokenKind::Protected
4889 || kind == TokenKind::Public
4893 fn mixed_namespace_errors(&mut self, node: &'a Syntax<Token, Value>) {
4894 match &node.syntax {
4895 NamespaceBody(x) => {
4896 let s = Self::start_offset(&x.namespace_left_brace);
4897 let e = Self::end_offset(&x.namespace_right_brace);
4898 if let NamespaceType::Unbracketed(Location {
4901 }) = self.namespace_type
4903 let child = Some(SyntaxError::make(
4908 self.errors.push(SyntaxError::make_with_child_and_type(
4912 ErrorType::ParseError,
4917 NamespaceEmptyBody(x) => {
4918 let s = Self::start_offset(&x.namespace_semicolon);
4919 let e = Self::end_offset(&x.namespace_semicolon);
4920 if let NamespaceType::Bracketed(Location {
4923 }) = self.namespace_type
4925 let child = Some(SyntaxError::make(
4930 self.errors.push(SyntaxError::make_with_child_and_type(
4934 ErrorType::ParseError,
4939 NamespaceDeclaration(x) => {
4940 let mut is_first_decl = true;
4941 let mut has_code_outside_namespace = false;
4944 syntax: Script(_), ..
4945 }, syntax_list] = self.parents.as_slice()
4947 if let SyntaxList(_) = syntax_list.syntax {
4948 is_first_decl = false;
4949 for decl in Self::syntax_to_list_no_separators(syntax_list) {
4950 match &decl.syntax {
4951 MarkupSection(x) => {
4952 if x.markup_text.width() == 0
4953 || self.is_hashbang(&x.markup_text)
4960 NamespaceUseDeclaration(_) | FileAttributeSpecification(_) => (),
4961 NamespaceDeclaration(_) => {
4962 is_first_decl = true;
4969 has_code_outside_namespace = !(x.namespace_body.is_namespace_empty_body())
4970 && Self::syntax_to_list_no_separators(syntax_list).any(|decl| {
4971 match &decl.syntax {
4972 MarkupSection(x) => {
4973 !(x.markup_text.width() == 0
4974 || self.is_hashbang(&x.markup_text))
4976 NamespaceDeclaration(_)
4977 | FileAttributeSpecification(_)
4979 | NamespaceUseDeclaration(_) => false,
4987 self.errors.push(Self::make_error_from_node(
4989 errors::namespace_decl_first_statement,
4992 if has_code_outside_namespace {
4993 self.errors.push(Self::make_error_from_node(
4995 errors::code_outside_namespace,
5003 fn enumerator_errors(&mut self, node: &'a Syntax<Token, Value>) {
5004 if let Enumerator(x) = &node.syntax {
5005 if self.text(&x.enumerator_name).eq_ignore_ascii_case("class") {
5006 self.errors.push(Self::make_error_from_node(
5008 errors::enum_elem_name_is_class,
5011 self.check_constant_expression(&x.enumerator_value)
5015 fn enum_decl_errors(&mut self, node: &'a Syntax<Token, Value>) {
5016 if let EnumDeclaration(x) = &node.syntax {
5017 let attrs = &x.enum_attribute_spec;
5018 if self.attr_spec_contains_sealed(attrs) {
5020 .push(Self::make_error_from_node(node, errors::sealed_enum))
5021 } else if self.attr_spec_contains_const(attrs) {
5022 self.errors.push(Self::make_error_from_node(
5024 errors::no_const_interfaces_traits_enums,
5028 if !x.enum_name.is_missing() {
5029 let name = self.text(&x.enum_name);
5030 let location = Self::make_location_of_node(&x.enum_name);
5031 self.check_type_name(&x.enum_name, name, location)
5036 fn check_lvalue(&mut self, allow_reassign: bool, loperand: &'a Syntax<Token, Value>) {
5037 let append_errors = |self_: &mut Self, node, error| {
5038 self_.errors.push(Self::make_error_from_node(node, error))
5041 let err = |self_: &mut Self, error| append_errors(self_, loperand, error);
5043 let check_unary_expression = |self_: &mut Self, op| match Self::token_kind(op) {
5044 Some(TokenKind::At) | Some(TokenKind::Dollar) => {}
5045 _ => err(self_, errors::not_allowed_in_write("Unary expression")),
5048 match &loperand.syntax {
5049 ListExpression(x) => Self::syntax_to_list_no_separators(&x.list_members)
5050 .for_each(|n| self.check_lvalue(false, n)),
5051 SafeMemberSelectionExpression(_) => {
5052 err(self, errors::not_allowed_in_write("?-> operator"))
5054 MemberSelectionExpression(x) => {
5055 if Self::token_kind(&x.member_name) == Some(TokenKind::XHPClassName) {
5056 err(self, errors::not_allowed_in_write("->: operator"))
5059 VariableExpression(x) => {
5060 if !allow_reassign {
5061 let text = self.text(&x.variable_expression);
5062 if text == sn::special_idents::THIS {
5063 err(self, errors::reassign_this)
5064 } else if text == sn::superglobals::GLOBALS {
5065 err(self, errors::not_allowed_in_write("$GLOBALS"))
5069 DecoratedExpression(x) => match Self::token_kind(&x.decorated_expression_decorator) {
5070 Some(TokenKind::Clone) => err(self, errors::not_allowed_in_write("Clone")),
5071 Some(TokenKind::Await) => err(self, errors::not_allowed_in_write("Await")),
5072 Some(TokenKind::Suspend) => err(self, errors::not_allowed_in_write("Suspend")),
5073 Some(TokenKind::QuestionQuestion) => {
5074 err(self, errors::not_allowed_in_write("?? operator"))
5076 Some(TokenKind::BarGreaterThan) => {
5077 err(self, errors::not_allowed_in_write("|> operator"))
5079 Some(TokenKind::Inout) => err(self, errors::not_allowed_in_write("Inout")),
5082 ParenthesizedExpression(x) => {
5083 self.check_lvalue(allow_reassign, &x.parenthesized_expression_expression)
5085 SubscriptExpression(x) => self.check_lvalue(true, &x.subscript_receiver),
5087 | AnonymousFunction(_)
5088 | AwaitableCreationExpression(_)
5089 | ArrayIntrinsicExpression(_)
5090 | DarrayIntrinsicExpression(_)
5091 | VarrayIntrinsicExpression(_)
5092 | ShapeExpression(_)
5093 | CollectionLiteralExpression(_)
5094 | GenericTypeSpecifier(_)
5095 | YieldExpression(_)
5097 | BinaryExpression(_)
5098 | ConditionalExpression(_)
5101 | NullableAsExpression(_)
5102 | ConstructorCall(_)
5105 | InclusionExpression(_)
5106 | TupleExpression(_)
5107 | LiteralExpression(_) => err(
5109 errors::not_allowed_in_write(loperand.kind().to_string()),
5111 PrefixUnaryExpression(x) => check_unary_expression(self, &x.prefix_unary_operator),
5112 PostfixUnaryExpression(x) => check_unary_expression(self, &x.postfix_unary_operator),
5114 // FIXME: Array_get ((_, Class_const _), _) is not a valid lvalue. *)
5115 _ => {} // Ideally we should put all the rest of the syntax here so everytime
5116 // a new syntax is added people need to consider whether the syntax
5117 // can be a valid lvalue or not. However, there are too many of them.
5121 fn assignment_errors(&mut self, node: &'a Syntax<Token, Value>) {
5122 let check_rvalue = |self_: &mut Self, roperand: &'a Syntax<Token, Value>| {
5123 let append_errors = |self_: &mut Self, node, error| {
5124 self_.errors.push(Self::make_error_from_node(node, error))
5126 match &roperand.syntax {
5127 VariableExpression(x)
5128 if self_.text(&x.variable_expression) == sn::superglobals::GLOBALS =>
5130 append_errors(self_, roperand, errors::globals_without_subscript)
5137 let check_unary_expression = |self_: &mut Self, op, loperand: &'a Syntax<Token, Value>| {
5138 if Self::does_unop_create_write(Self::token_kind(op)) {
5139 self_.check_lvalue(true, loperand)
5142 match &node.syntax {
5143 PrefixUnaryExpression(x) => {
5144 check_unary_expression(self, &x.prefix_unary_operator, &x.prefix_unary_operand)
5146 PostfixUnaryExpression(x) => {
5147 check_unary_expression(self, &x.postfix_unary_operator, &x.postfix_unary_operand)
5149 DecoratedExpression(x) => {
5150 let loperand = &x.decorated_expression_expression;
5151 if Self::does_decorator_create_write(Self::token_kind(
5152 &x.decorated_expression_decorator,
5154 self.check_lvalue(true, &loperand)
5157 BinaryExpression(x) => {
5158 let loperand = &x.binary_left_operand;
5159 let roperand = &x.binary_right_operand;
5160 if Self::does_binop_create_write_on_left(Self::token_kind(&x.binary_operator)) {
5161 self.check_lvalue(false, &loperand);
5162 check_rvalue(self, &roperand);
5165 ForeachStatement(x) => {
5166 self.check_lvalue(false, &x.foreach_value);
5167 self.check_lvalue(false, &x.foreach_key);
5168 check_rvalue(self, &x.foreach_collection);
5174 fn dynamic_method_call_errors(&mut self, node: &'a Syntax<Token, Value>) {
5175 match &node.syntax {
5176 FunctionCallExpression(x) if !x.function_call_type_args.is_missing() => {
5177 let is_variable = |x| Self::is_token_kind(x, TokenKind::Variable);
5178 let is_dynamic = match &x.function_call_receiver.syntax {
5179 ScopeResolutionExpression(x) => is_variable(&x.scope_resolution_name),
5180 MemberSelectionExpression(x) => is_variable(&x.member_name),
5181 SafeMemberSelectionExpression(x) => is_variable(&x.safe_member_name),
5185 self.errors.push(Self::make_error_from_node(
5187 errors::no_type_parameters_on_dynamic_method_calls,
5195 fn get_namespace_name(&self) -> String {
5196 if let Some(node) = self.nested_namespaces.last() {
5197 if let NamespaceDeclaration(x) = &node.syntax {
5198 if let NamespaceDeclarationHeader(x) = &x.namespace_header.syntax {
5199 let ns = &x.namespace_name;
5200 if !ns.is_missing() {
5201 return combine_names(&self.namespace_name, self.text(ns));
5206 return self.namespace_name.clone();
5209 fn is_invalid_hack_mode(&mut self) {
5210 if self.env.syntax_tree.mode().is_none() {
5211 let root = self.env.syntax_tree.root();
5212 let e = Self::make_error_from_node(root, errors::invalid_hack_mode);
5213 self.errors.push(e);
5217 fn disabled_legacy_soft_typehint_errors(&mut self, node: &'a Syntax<Token, Value>) {
5218 if let SoftTypeSpecifier(_) = node.syntax {
5219 if self.env.parser_options.po_disable_legacy_soft_typehints {
5220 self.errors.push(Self::make_error_from_node(
5222 errors::no_legacy_soft_typehints,
5228 fn disabled_legacy_attribute_syntax_errors(&mut self, node: &'a Syntax<Token, Value>) {
5230 OldAttributeSpecification(_)
5231 if self.env.parser_options.po_disable_legacy_attribute_syntax =>
5233 self.errors.push(Self::make_error_from_node(
5235 errors::no_legacy_attribute_syntax,
5242 fn param_default_decl_errors(&mut self, node: &'a Syntax<Token, Value>) {
5243 if let ParameterDeclaration(x) = &node.syntax {
5244 if self.env.parser_options.po_const_default_lambda_args {
5245 match self.env.context.active_callable {
5246 Some(node) => match node.syntax {
5247 AnonymousFunction(_) | LambdaExpression(_) => {
5248 self.check_constant_expression(&x.parameter_default_value);
5255 if self.env.parser_options.po_const_default_func_args {
5256 self.check_constant_expression(&x.parameter_default_value)
5261 fn concurrent_statement_errors(&mut self, node: &'a Syntax<Token, Value>) {
5262 if let ConcurrentStatement(x) = &node.syntax {
5263 // issue error if concurrent blocks are nested
5264 if self.is_in_concurrent_block {
5265 self.errors.push(Self::make_error_from_node(
5267 errors::nested_concurrent_blocks,
5270 if let CompoundStatement(x) = &x.concurrent_statement.syntax {
5271 let statement_list = || Self::syntax_to_list_no_separators(&x.compound_statements);
5272 if statement_list().nth(1).is_none() {
5273 self.errors.push(Self::make_error_from_node(
5275 errors::fewer_than_two_statements_in_concurrent_block,
5278 for n in statement_list() {
5279 if let ExpressionStatement(x) = &n.syntax {
5280 if !self.node_has_await_child(&x.expression_statement_expression) {
5281 self.errors.push(Self::make_error_from_node(
5283 errors::statement_without_await_in_concurrent_block,
5287 self.errors.push(Self::make_error_from_node(
5289 errors::invalid_syntax_concurrent_block,
5293 for n in statement_list() {
5294 for error in self.find_invalid_lval_usage(n) {
5295 self.errors.push(error)
5299 self.errors.push(Self::make_error_from_node(
5301 errors::invalid_syntax_concurrent_block,
5307 fn disabled_function_pointer_expression_error(&mut self, node: &'a Syntax<Token, Value>) {
5308 if let FunctionPointerExpression(_) = &node.syntax {
5312 .po_enable_first_class_function_pointers
5314 self.errors.push(Self::make_error_from_node(
5316 errors::function_pointers_disabled,
5322 fn check_qualified_name(&mut self, node: &'a Syntax<Token, Value>) {
5323 // The last segment in a qualified name should not have a trailing backslash
5324 // i.e. `Foospace\Bar\` except as the prefix of a GroupUseClause
5325 if let Some(Syntax {
5326 syntax: NamespaceGroupUseDeclaration(_),
5328 }) = self.parents.last()
5331 if let QualifiedName(x) = &node.syntax {
5332 let name_parts = &x.qualified_name_parts;
5333 let mut parts = Self::syntax_to_list_with_separators(name_parts);
5334 let last_part = parts.nth_back(0);
5336 Some(t) if Self::token_kind(t) == Some(TokenKind::Backslash) => self
5338 .push(Self::make_error_from_node(t, errors::error0008)),
5345 fn check_preceding_backslashes_qualified_name(&mut self, node: &'a Syntax<Token, Value>) {
5346 // Qualified names as part of file level declarations
5347 // (group use, namespace use, namespace declarations) should not have preceding backslashes
5348 // `use namespace A\{\B}` will throw this error.
5349 if let QualifiedName(x) = &node.syntax {
5350 let name_parts = &x.qualified_name_parts;
5351 let mut parts = Self::syntax_to_list_with_separators(name_parts);
5352 let first_part = parts.find(|x| !x.is_missing());
5355 Some(t) if Self::token_kind(t) == Some(TokenKind::Backslash) => self.errors.push(
5356 Self::make_error_from_node(node, errors::preceding_backslash),
5363 fn strip_ns(name: &str) -> &str {
5364 match name.chars().next() {
5365 Some('\\') => &name[1..],
5370 fn strip_hh_ns(name: &str) -> &str {
5371 name.trim_start_matches("\\HH\\")
5374 fn is_global_namespace(&self) -> bool {
5375 self.namespace_name == GLOBAL_NAMESPACE_NAME
5378 fn folder(&mut self, node: &'a Syntax<Token, Value>) {
5379 let has_rx_attr_mutable_hack = |self_: &mut Self, attrs| {
5381 .attribute_first_reactivity_annotation(attrs)
5382 .map_or(false, |node| {
5383 self_.attr_name(node) != Some(sn::user_attributes::NON_RX)
5386 let mut prev_context = None;
5387 let mut pushed_nested_namespace = false;
5389 let named_function_context =
5390 |self_: &mut Self, node, s, prev_context: &mut Option<Context<'a, _>>| {
5391 *prev_context = Some(self_.env.context.clone());
5392 // a _single_ variable suffices as they cannot be nested
5393 self_.env.context.active_methodish = Some(node);
5394 // inspect the rx attribute directly.
5395 self_.env.context.active_is_rx_or_enclosing_for_lambdas =
5396 has_rx_attr_mutable_hack(self_, s);
5397 self_.env.context.active_callable = Some(node);
5398 self_.env.context.active_callable_attr_spec = Some(s);
5401 let lambda_context =
5402 |self_: &mut Self, node, s, prev_context: &mut Option<Context<'a, _>>| {
5403 *prev_context = Some(self_.env.context.clone());
5404 //preserve context when entering lambdas (and anonymous functions)
5405 self_.env.context.active_callable = Some(node);
5406 self_.env.context.active_callable_attr_spec = Some(s);
5409 match &node.syntax {
5410 ConstDeclaration(_) => {
5411 prev_context = Some(self.env.context.clone());
5412 self.env.context.active_const = Some(node)
5414 FunctionDeclaration(x) => {
5415 named_function_context(self, node, &x.function_attribute_spec, &mut prev_context)
5417 MethodishDeclaration(x) => {
5418 named_function_context(self, node, &x.methodish_attribute, &mut prev_context)
5420 NamespaceDeclaration(x) => {
5421 if let NamespaceDeclarationHeader(x) = &x.namespace_header.syntax {
5422 let namespace_name = &x.namespace_name;
5423 if !namespace_name.is_missing() && !self.text(namespace_name).is_empty() {
5424 pushed_nested_namespace = true;
5425 self.nested_namespaces.push(node)
5429 AnonymousFunction(x) => {
5430 lambda_context(self, node, &x.anonymous_attribute_spec, &mut prev_context)
5432 LambdaExpression(x) => {
5433 lambda_context(self, node, &x.lambda_attribute_spec, &mut prev_context)
5435 AwaitableCreationExpression(x) => {
5436 lambda_context(self, node, &x.awaitable_attribute_spec, &mut prev_context)
5438 ClassishDeclaration(_) => {
5439 prev_context = Some(self.env.context.clone());
5440 self.env.context.active_classish = Some(node)
5442 FileAttributeSpecification(_) => Self::attr_spec_to_node_list(node).for_each(|node| {
5443 if self.attr_name(node).as_deref()
5444 == Some(sn::user_attributes::ENABLE_UNSTABLE_FEATURES)
5446 if !self.env.parser_options.po_allow_unstable_features {
5447 self.errors.push(Self::make_error_from_node(
5449 errors::invalid_use_of_enable_unstable_feature(
5450 "unstable features are disabled",
5453 } else if let Some(args) = self.attr_args(node) {
5454 let mut args = args.peekable();
5455 if args.peek().is_none() {
5456 self.errors.push(Self::make_error_from_node(
5458 errors::invalid_use_of_enable_unstable_feature(
5460 "you didn't select a feature. Available features are:\n\t{}",
5461 UnstableFeatures::iter().join("\n\t")
5467 args.for_each(|arg| self.enable_unstable_feature(node, arg))
5476 self.parameter_errors(node);
5478 match &node.syntax {
5480 | UsingStatementFunctionScoped(_)
5483 | DefaultLabel(_) => self.statement_errors(node),
5484 MethodishDeclaration(_) | FunctionDeclaration(_) | FunctionDeclarationHeader(_) => {
5485 self.reified_parameter_errors(node);
5486 self.redeclaration_errors(node);
5487 self.multiple_entrypoint_attribute_errors(node);
5488 self.methodish_errors(node);
5491 ArrayIntrinsicExpression(_) => self.expression_errors(node),
5492 LiteralExpression(_)
5493 | SafeMemberSelectionExpression(_)
5494 | FunctionCallExpression(_)
5496 | ShapeExpression(_)
5497 | DecoratedExpression(_)
5498 | VectorIntrinsicExpression(_)
5499 | DictionaryIntrinsicExpression(_)
5500 | KeysetIntrinsicExpression(_)
5501 | VarrayIntrinsicExpression(_)
5502 | DarrayIntrinsicExpression(_)
5503 | YieldExpression(_)
5504 | ScopeResolutionExpression(_)
5505 | PrefixUnaryExpression(_)
5506 | LambdaExpression(_)
5509 | AnonymousFunction(_)
5510 | SubscriptExpression(_)
5511 | ConstructorCall(_)
5512 | AwaitableCreationExpression(_)
5513 | PipeVariableExpression(_)
5514 | ConditionalExpression(_)
5515 | CollectionLiteralExpression(_)
5516 | VariableExpression(_) => {
5517 self.dynamic_method_call_errors(node);
5518 self.expression_errors(node);
5519 self.check_nonrx_annotation(node);
5520 self.assignment_errors(node);
5523 ParameterDeclaration(_) => self.param_default_decl_errors(node),
5524 RequireClause(_) => self.require_errors(node),
5525 ClassishDeclaration(_) => {
5526 self.classish_errors(node);
5527 self.class_reified_param_errors(node);
5530 ConstDeclaration(_) => self.class_constant_modifier_errors(node),
5532 TypeConstDeclaration(_) => self.type_const_modifier_errors(node),
5534 AliasDeclaration(_) => self.alias_errors(node),
5535 ConstantDeclarator(_) => self.const_decl_errors(node),
5536 NamespaceBody(_) | NamespaceEmptyBody(_) | NamespaceDeclaration(_) => {
5537 self.mixed_namespace_errors(node)
5539 NamespaceUseDeclaration(_) | NamespaceGroupUseDeclaration(_) => {
5540 self.group_use_errors(node);
5541 self.namespace_use_declaration_errors(node);
5543 PropertyDeclaration(_) => {
5544 self.class_property_modifiers_errors(node);
5545 self.class_reified_param_errors(node);
5546 self.class_property_const_errors(node);
5547 self.class_property_declarator_errors(node);
5549 TraitUseAliasItem(_) => self.trait_use_alias_item_modifier_errors(node),
5550 EnumDeclaration(_) => self.enum_decl_errors(node),
5551 Enumerator(_) => self.enumerator_errors(node),
5552 PostfixUnaryExpression(_) | BinaryExpression(_) | ForeachStatement(_) => {
5553 self.assignment_errors(node)
5555 XHPEnumType(_) | XHPExpression(_) => self.xhp_errors(node),
5556 PropertyDeclarator(x) => {
5557 let init = &x.property_initializer;
5560 |self_, x| self_.check_static_in_initializer(x),
5562 || errors::parent_static_prop_decl,
5565 self.check_constant_expression(&init)
5567 RecordField(x) => self.check_constant_expression(&x.record_field_init),
5568 XHPClassAttribute(x) => {
5569 self.check_constant_expression(&x.xhp_attribute_decl_initializer)
5571 OldAttributeSpecification(_) => self.disabled_legacy_attribute_syntax_errors(node),
5572 SoftTypeSpecifier(_) => self.disabled_legacy_soft_typehint_errors(node),
5573 FunctionPointerExpression(_) => self.disabled_function_pointer_expression_error(node),
5574 QualifiedName(_) => self.check_qualified_name(node),
5577 self.lval_errors(node);
5579 match &node.syntax {
5580 LambdaExpression(_) | AwaitableCreationExpression(_) | AnonymousFunction(_) => {
5581 let prev_is_in_concurrent_block = self.is_in_concurrent_block;
5582 // reset is_in_concurrent_block for functions
5583 self.is_in_concurrent_block = false;
5584 // analyze the body of lambda block
5585 self.fold_child_nodes(node);
5586 // adjust is_in_concurrent_block in final result
5587 self.is_in_concurrent_block = prev_is_in_concurrent_block;
5590 ConcurrentStatement(_) => {
5591 self.concurrent_statement_errors(node);
5592 // adjust is_in_concurrent_block in accumulator to dive into the
5593 let prev_is_in_concurrent_block = self.is_in_concurrent_block;
5594 self.is_in_concurrent_block = true;
5595 // analyze the body of concurrent block
5596 self.fold_child_nodes(node);
5597 // adjust is_in_concurrent_block in final result
5598 self.is_in_concurrent_block = prev_is_in_concurrent_block;
5601 NamespaceBody(x) => {
5602 if self.namespace_type == Unspecified {
5603 self.namespace_type = Bracketed(Self::make_location(
5604 &x.namespace_left_brace,
5605 &x.namespace_right_brace,
5609 let old_namespace_name = self.namespace_name.clone();
5610 let mut old_names = self.names.clone();
5611 // reset names before diving into namespace body,
5612 // keeping global function names
5613 self.namespace_name = self.get_namespace_name();
5614 let names_copy = std::mem::replace(&mut self.names, UsedNames::empty());
5615 self.names.functions = names_copy.functions.filter(|x| x.global);
5616 self.fold_child_nodes(node);
5618 // add newly declared global functions to the old set of names
5619 let names_copy = std::mem::replace(&mut self.names, UsedNames::empty());
5620 for (k, v) in names_copy.functions.into_iter().filter(|(_, x)| x.global) {
5621 old_names.functions.add(&k, v)
5623 // resume with old set of names and pull back
5624 // accumulated errors/last seen namespace type
5625 self.names = old_names;
5626 self.namespace_name = old_namespace_name;
5628 NamespaceEmptyBody(x) => {
5629 if self.namespace_type == Unspecified {
5630 self.namespace_type =
5631 Unbracketed(Self::make_location_of_node(&x.namespace_semicolon))
5633 self.namespace_name = self.get_namespace_name();
5634 self.names = UsedNames::empty();
5635 self.fold_child_nodes(node);
5637 ClassishDeclaration(_) | AnonymousClass(_) => {
5638 // Reset the trait require clauses
5639 // Reset the const declarations
5640 // Reset the function declarations
5643 std::mem::replace(&mut self.names.constants, YesCase(HashMap::new()));
5645 std::mem::replace(&mut self.names.functions, NoCase(HashMap::new()));
5646 let trait_require_clauses = std::mem::replace(
5647 &mut self.trait_require_clauses,
5648 empty_trait_require_clauses(),
5651 self.fold_child_nodes(node);
5653 self.trait_require_clauses = trait_require_clauses;
5654 self.names.functions = functions;
5655 self.names.constants = constants;
5657 _ => self.fold_child_nodes(node),
5660 match &node.syntax {
5661 UnionTypeSpecifier(_) | IntersectionTypeSpecifier(_) => {
5662 self.check_can_use_feature(node, &UnstableFeatures::UnionIntersectionTypeHints)
5664 ClassishDeclaration(x) => match &x.classish_where_clause.syntax {
5665 WhereClause(_) => self.check_can_use_feature(
5666 &x.classish_where_clause,
5667 &UnstableFeatures::ClassLevelWhere,
5674 if let Some(prev_context) = prev_context {
5675 self.env.context = prev_context;
5677 if pushed_nested_namespace {
5679 self.nested_namespaces.pop().map(|x| x as *const _),
5680 Some(node as *const _)
5685 fn fold_child_nodes(&mut self, node: &'a Syntax<Token, Value>) {
5686 self.parents.push(node);
5687 for c in node.iter_children() {
5691 self.parents.pop().map(|x| x as *const _),
5692 Some(node as *const _)
5696 fn parse_errors_impl(mut self) -> Vec<SyntaxError> {
5697 if self.env.is_typechecker() && !self.env.parser_options.po_disable_modes {
5698 self.is_invalid_hack_mode();
5700 self.fold_child_nodes(self.env.syntax_tree.root());
5701 self.errors.reverse();
5706 tree: &'a SyntaxTree<'a, Syntax<Token, Value>, State>,
5707 text: IndexedSourceText<'a>,
5708 parser_options: ParserOptions,
5709 hhvm_compat_mode: bool,
5712 ) -> Vec<SyntaxError> {
5718 active_classish: None,
5719 active_methodish: None,
5720 active_callable: None,
5721 active_callable_attr_spec: None,
5722 active_is_rx_or_enclosing_for_lambdas: false,
5724 active_unstable_features: HashSet::new(),
5731 match tree.required_stack_size() {
5732 None => Self::new(env).parse_errors_impl(),
5733 Some(stack_size) => {
5734 // We want to use new thread ONLY for it's capability of adjustable stack size.
5735 // Rust is against it because SyntaxTree is not a thread safe structure. Moreover,
5736 // spawned thread could outlive the 'a lifetime.
5737 // Since the only thing the main thread will do after spawning the child is to wait
5738 // for it to finish, there will be no actual concurrency, and we can "safely" unsafe
5739 // it. The usize cast is to fool the borrow checker about the thread lifetime and 'a.
5740 let raw_pointer = Box::leak(Box::new(Self::new(env))) as *mut Self as usize;
5741 std::thread::Builder::new()
5742 .stack_size(stack_size)
5744 let self_ = unsafe { Box::from_raw(raw_pointer as *mut Self) };
5745 self_.parse_errors_impl()
5747 .expect("ERROR: thread::spawn")
5749 .expect("ERROR: failed to wait on new thread")
5755 pub fn parse_errors<'a>(
5756 tree: &'a SyntaxTree<'a, PositionedSyntax, ()>,
5757 parser_options: ParserOptions,
5758 hhvm_compat_mode: bool,
5761 ) -> Vec<SyntaxError> {
5762 ParserErrors::parse_errors(
5764 IndexedSourceText::new(tree.text().clone()),
5772 pub fn parse_errors_with_text<'a>(
5773 tree: &'a SyntaxTree<'a, PositionedSyntax, ()>,
5774 text: IndexedSourceText<'a>,
5775 parser_options: ParserOptions,
5776 hhvm_compat_mode: bool,
5779 ) -> Vec<SyntaxError> {
5780 ParserErrors::parse_errors(