From 65b1c950bd7ac275869055738f565053661568ea Mon Sep 17 00:00:00 2001 From: Shayne Fletcher Date: Thu, 10 Mar 2022 14:44:18 -0800 Subject: [PATCH] report cyclic class errors Summary: arrange to report on cyclic class definition errors Differential Revision: D34800222 fbshipit-source-id: e82367158d71571c29206094baa568af26dafc2d --- .../rupro/lib/folded_decl_provider/fold.rs | 2 +- .../lib/folded_decl_provider/provider.rs | 34 ++++++---- .../rupro/lib/typing_error/error_primary.rs | 1 + .../test/rupro/folded_decls/cyclic_class.php | 4 ++ .../cyclic_class.php.folded_decls.exp | 66 +++++++++++++++++++ 5 files changed, 94 insertions(+), 13 deletions(-) create mode 100644 hphp/hack/test/rupro/folded_decls/cyclic_class.php create mode 100644 hphp/hack/test/rupro/folded_decls/cyclic_class.php.folded_decls.exp diff --git a/hphp/hack/src/rupro/lib/folded_decl_provider/fold.rs b/hphp/hack/src/rupro/lib/folded_decl_provider/fold.rs index 389d744210d44d..c031b5786f2690 100644 --- a/hphp/hack/src/rupro/lib/folded_decl_provider/fold.rs +++ b/hphp/hack/src/rupro/lib/folded_decl_provider/fold.rs @@ -759,6 +759,7 @@ impl DeclFolder { &self, sc: &ShallowClass, parents: &TypeNameIndexMap>>, + mut errors: Vec>, ) -> Arc> { let inh = Inherited::make(sc, parents); @@ -801,7 +802,6 @@ impl DeclFolder { .iter() .for_each(|tc| self.decl_type_const(&mut type_consts, &mut consts, sc, tc)); - let mut errors = vec![]; let extends = self.get_extends(sc, parents, &mut errors); let xhp_attr_deps = self.get_xhp_attr_deps(sc, parents, &mut errors); diff --git a/hphp/hack/src/rupro/lib/folded_decl_provider/provider.rs b/hphp/hack/src/rupro/lib/folded_decl_provider/provider.rs index 70d9634646956d..7d8fd5d6df086e 100644 --- a/hphp/hack/src/rupro/lib/folded_decl_provider/provider.rs +++ b/hphp/hack/src/rupro/lib/folded_decl_provider/provider.rs @@ -9,6 +9,7 @@ use crate::decl_defs::{ConstDecl, DeclTy, DeclTy_, FoldedClass, FunDecl, Shallow use crate::reason::Reason; use crate::shallow_decl_provider::{self, ShallowDeclProvider}; use crate::special_names::SpecialNames; +use crate::typing_error::{Primary, TypingError}; use oxidized::global_options::GlobalOptions; use pos::{ ConstName, FunName, MethodName, Positioned, PropName, TypeName, TypeNameIndexMap, @@ -133,24 +134,31 @@ impl LazyFoldedDeclProvider { fn detect_cycle( &self, stack: &mut TypeNameIndexSet, + errors: &mut Vec>, pos_id: &Positioned, ) -> bool { if stack.contains(&pos_id.id()) { - todo!("TODO(hrust): register error"); + errors.push(TypingError::primary(Primary::CyclicClassDef( + pos_id.pos().clone(), + stack.iter().copied().collect(), + ))); + true + } else { + false } - false } fn decl_class_type( &self, stack: &mut TypeNameIndexSet, acc: &mut TypeNameIndexMap>>, + errors: &mut Vec>, ty: &DeclTy, ) -> Result<()> { match &**ty.node() { DeclTy_::DTapply(id_and_args) => { let pos_id = &id_and_args.0; - if !self.detect_cycle(stack, pos_id) { + if !self.detect_cycle(stack, errors, pos_id) { if let Some(folded_decl) = self.get_folded_class_impl(stack, pos_id.id())? { acc.insert(pos_id.id(), folded_decl); } @@ -195,31 +203,32 @@ impl LazyFoldedDeclProvider { fn decl_class_parents( &self, stack: &mut TypeNameIndexSet, + errors: &mut Vec>, sc: &ShallowClass, ) -> Result>>> { let mut acc = Default::default(); for ty in sc.extends.iter() { - self.decl_class_type(stack, &mut acc, ty) + self.decl_class_type(stack, &mut acc, errors, ty) .map_err(|err| Self::parent_error(sc, ty, err))?; } for ty in sc.implements.iter() { - self.decl_class_type(stack, &mut acc, ty) + self.decl_class_type(stack, &mut acc, errors, ty) .map_err(|err| Self::parent_error(sc, ty, err))?; } for ty in sc.uses.iter() { - self.decl_class_type(stack, &mut acc, ty) + self.decl_class_type(stack, &mut acc, errors, ty) .map_err(|err| Self::parent_error(sc, ty, err))?; } for ty in sc.xhp_attr_uses.iter() { - self.decl_class_type(stack, &mut acc, ty) + self.decl_class_type(stack, &mut acc, errors, ty) .map_err(|err| Self::parent_error(sc, ty, err))?; } for ty in sc.req_extends.iter() { - self.decl_class_type(stack, &mut acc, ty) + self.decl_class_type(stack, &mut acc, errors, ty) .map_err(|err| Self::parent_error(sc, ty, err))?; } for ty in sc.req_implements.iter() { - self.decl_class_type(stack, &mut acc, ty) + self.decl_class_type(stack, &mut acc, errors, ty) .map_err(|err| Self::parent_error(sc, ty, err))?; } for ty in sc @@ -228,7 +237,7 @@ impl LazyFoldedDeclProvider { .map_or([].as_slice(), |et| &et.includes) .iter() { - self.decl_class_type(stack, &mut acc, ty) + self.decl_class_type(stack, &mut acc, errors, ty) .map_err(|err| Self::parent_error(sc, ty, err))?; } @@ -240,15 +249,16 @@ impl LazyFoldedDeclProvider { stack: &mut TypeNameIndexSet, name: TypeName, ) -> Result>>> { + let mut errors = vec![]; let shallow_class = match self.shallow_decl_provider.get_class(name)? { None => return Ok(None), Some(c) => c, }; stack.insert(name); - let parents = self.decl_class_parents(stack, &shallow_class)?; + let parents = self.decl_class_parents(stack, &mut errors, &shallow_class)?; stack.remove(&name); let folder = DeclFolder::new(Arc::clone(&self.opts), self.special_names); - Ok(Some(folder.decl_class(&shallow_class, &parents))) + Ok(Some(folder.decl_class(&shallow_class, &parents, errors))) } fn get_folded_class_impl( diff --git a/hphp/hack/src/rupro/lib/typing_error/error_primary.rs b/hphp/hack/src/rupro/lib/typing_error/error_primary.rs index 32699eaccc694c..7f1d6c34e2922e 100644 --- a/hphp/hack/src/rupro/lib/typing_error/error_primary.rs +++ b/hphp/hack/src/rupro/lib/typing_error/error_primary.rs @@ -10,6 +10,7 @@ pub enum Primary { InvalidTypeHint(R::Pos), ExpectingTypeHint(R::Pos), ExpectingReturnTypeHint(R::Pos), + CyclicClassDef(R::Pos, Vec), TraitReuse { parent_pos: R::Pos, parent_name: TypeName, diff --git a/hphp/hack/test/rupro/folded_decls/cyclic_class.php b/hphp/hack/test/rupro/folded_decls/cyclic_class.php new file mode 100644 index 00000000000000..7e154c94741349 --- /dev/null +++ b/hphp/hack/test/rupro/folded_decls/cyclic_class.php @@ -0,0 +1,4 @@ +, + origin: "\\A", + refs: [], + }, + }, + extends: { + "\\C", + "\\A", + }, +} +FoldedClass { + name: "\\C", + kind: Cclass( + Concrete, + ), + ancestors: { + "\\A": A, + }, + consts: { + "class": ClassConst { + is_synthesized: true, + kind: CCConcrete, + pos: NPos, + ty: HH\classname, + origin: "\\C", + refs: [], + }, + }, + extends: { + "\\A", + }, + decl_errors: [ + Primary( + CyclicClassDef( + NPos, + [ + "\\A", + "\\C", + ], + ), + ), + ], +}