Avoid growing type parameters in Visitor
[hiphop-php.git] / hphp / hack / src / parser / aast_check.rs
blob0bf5de5ba46a7da2091d7038a20c2e111862e47b
1 // Copyright (c) 2019, Facebook, Inc.
2 // All rights reserved.
3 //
4 // This source code is licensed under the MIT license found in the
5 // LICENSE file in the "hack" directory of this source tree.
7 use naming_special_names_rust::special_idents;
8 use oxidized::{
9     aast,
10     aast_visitor::{visit, AstParams, Node, Visitor},
11     pos::Pos,
13 use parser_core_types::{
14     syntax_error,
15     syntax_error::{Error as ErrorMsg, SyntaxError},
18 struct Context {
19     in_methodish: bool,
20     in_classish: bool,
21     in_static_methodish: bool,
24 struct Checker {
25     errors: Vec<SyntaxError>,
28 impl Checker {
29     fn new() -> Self {
30         Self { errors: vec![] }
31     }
33     fn add_error(&mut self, pos: &Pos, msg: ErrorMsg) {
34         let (start_offset, end_offset) = pos.info_raw();
35         self.errors
36             .push(SyntaxError::make(start_offset, end_offset, msg));
37     }
39     fn name_eq_this_and_in_static_method(c: &Context, name: impl AsRef<str>) -> bool {
40         c.in_classish
41             && c.in_static_methodish
42             && name.as_ref().eq_ignore_ascii_case(special_idents::THIS)
43     }
46 impl Visitor for Checker {
47     type P = AstParams<Context, ()>;
49     fn object(&mut self) -> &mut dyn Visitor<P = Self::P> {
50         self
51     }
53     fn visit_class_(
54         &mut self,
55         c: &mut Context,
56         p: &aast::Class_<Pos, (), (), ()>,
57     ) -> Result<(), ()> {
58         p.recurse(
59             &mut Context {
60                 in_classish: true,
61                 ..*c
62             },
63             self,
64         )
65     }
67     fn visit_method_(
68         &mut self,
69         c: &mut Context,
70         p: &aast::Method_<Pos, (), (), ()>,
71     ) -> Result<(), ()> {
72         p.recurse(
73             &mut Context {
74                 in_methodish: true,
75                 in_static_methodish: p.static_,
76                 ..*c
77             },
78             self,
79         )
80     }
82     fn visit_fun_(&mut self, c: &mut Context, p: &aast::Fun_<Pos, (), (), ()>) -> Result<(), ()> {
83         p.recurse(
84             &mut Context {
85                 in_methodish: true,
86                 in_static_methodish: p.static_ || c.in_static_methodish,
87                 ..*c
88             },
89             self,
90         )
91     }
93     fn visit_expr(&mut self, c: &mut Context, p: &aast::Expr<Pos, (), (), ()>) -> Result<(), ()> {
94         use aast::{ClassId, ClassId_::*, Expr, Expr_::*, Lid};
96         if let Await(_) = p.1 {
97             if !c.in_methodish {
98                 self.add_error(&p.0, syntax_error::toplevel_await_use)
99             }
100         } else if let Some((_, Expr(_, f), ..)) = p.1.as_call() {
101             if let Some((ClassId(_, CIexpr(Expr(pos, Id(id)))), ..)) = f.as_class_const() {
102                 if Self::name_eq_this_and_in_static_method(c, &id.1) {
103                     self.add_error(&pos, syntax_error::this_in_static);
104                 }
105             }
106         } else if let Some(Lid(pos, (_, name))) = p.1.as_lvar() {
107             if Self::name_eq_this_and_in_static_method(c, name) {
108                 self.add_error(pos, syntax_error::this_in_static);
109             }
110         }
111         p.recurse(c, self)
112     }
115 pub fn check_program(program: &aast::Program<Pos, (), (), ()>) -> Vec<SyntaxError> {
116     let mut checker = Checker::new();
117     let mut context = Context {
118         in_methodish: false,
119         in_classish: false,
120         in_static_methodish: false,
121     };
122     visit(&mut checker, &mut context, program).unwrap();
123     checker.errors