From 55b1d21281af58f5e254f9fe0df2ced4c4649808 Mon Sep 17 00:00:00 2001 From: Sam Zhou Date: Sat, 30 Dec 2023 22:46:11 -0800 Subject: [PATCH] [parser][printer] Consistent comment attachment and printing for classes and interfaces --- crates/samlang-ast/src/source.rs | 99 +++- crates/samlang-ast/src/source_tests.rs | 182 +++--- .../src/checker_integration_tests.rs | 2 +- crates/samlang-checker/src/checker_tests.rs | 2 +- .../samlang-checker/src/global_signature.rs | 20 +- crates/samlang-checker/src/main_checker.rs | 46 +- crates/samlang-checker/src/ssa_analysis.rs | 28 +- .../samlang-checker/src/ssa_analysis_tests.rs | 2 +- crates/samlang-checker/src/type_.rs | 2 +- crates/samlang-compiler/src/hir_lowering.rs | 545 +++++++++--------- .../src/hir_type_conversion.rs | 35 +- crates/samlang-parser/src/lib.rs | 8 +- crates/samlang-parser/src/source_parser.rs | 218 ++++--- crates/samlang-printer/src/source_printer.rs | 175 ++++-- crates/samlang-services/src/api_tests.rs | 2 +- crates/samlang-services/src/ast_differ.rs | 12 +- crates/samlang-services/src/gc.rs | 25 +- .../samlang-services/src/global_searcher.rs | 13 +- crates/samlang-services/src/lib.rs | 2 +- crates/samlang-services/src/location_cover.rs | 4 +- .../src/variable_definition.rs | 103 ++-- 21 files changed, 922 insertions(+), 603 deletions(-) diff --git a/crates/samlang-ast/src/source.rs b/crates/samlang-ast/src/source.rs index 52c9b7b5..2584f63c 100644 --- a/crates/samlang-ast/src/source.rs +++ b/crates/samlang-ast/src/source.rs @@ -160,17 +160,18 @@ pub mod annotation { } #[derive(Clone, PartialEq, Eq)] - pub struct FunctionParameters { + pub struct ParenthesizedAnnotationList { pub location: Location, + pub start_associated_comments: CommentReference, pub ending_associated_comments: CommentReference, - pub parameters: Vec, + pub annotations: Vec, } #[derive(Clone, PartialEq, Eq)] pub struct Function { pub location: Location, pub associated_comments: CommentReference, - pub parameters: FunctionParameters, + pub parameters: ParenthesizedAnnotationList, pub return_type: Box, } @@ -685,6 +686,21 @@ pub struct ClassMemberDefinition { pub body: expr::E, } +/// The node after colon, interpreted as extends in interfaces and implements in classes. +#[derive(Clone, PartialEq, Eq)] +pub struct ExtendsOrImplementsNodes { + pub location: Location, + pub associated_comments: CommentReference, + pub nodes: Vec, +} + +#[derive(Clone, PartialEq, Eq)] +pub struct InterfaceMembersCommon { + pub loc: Location, + pub members: Vec, + pub ending_associated_comments: CommentReference, +} + #[derive(Clone, PartialEq, Eq)] pub struct InterfaceDeclarationCommon { pub loc: Location, @@ -692,10 +708,9 @@ pub struct InterfaceDeclarationCommon { pub private: bool, pub name: Id, pub type_parameters: Option, - /** The node after colon, interpreted as extends in interfaces and implements in classes. */ - pub extends_or_implements_nodes: Vec, + pub extends_or_implements_nodes: Option, pub type_definition: D, - pub members: Vec, + pub members: InterfaceMembersCommon, } pub type InterfaceDeclaration = InterfaceDeclarationCommon<(), ClassMemberDeclaration>; @@ -710,16 +725,63 @@ pub struct FieldDefinition { #[derive(Clone, PartialEq, Eq)] pub struct VariantDefinition { pub name: Id, - pub associated_data_types: Vec, + pub associated_data_types: Option, } #[derive(Clone, EnumAsInner, PartialEq, Eq)] pub enum TypeDefinition { - Struct { loc: Location, fields: Vec }, - Enum { loc: Location, variants: Vec }, + Struct { + loc: Location, + start_associated_comments: CommentReference, + ending_associated_comments: CommentReference, + fields: Vec, + }, + Enum { + loc: Location, + start_associated_comments: CommentReference, + ending_associated_comments: CommentReference, + variants: Vec, + }, } -pub type ClassDefinition = InterfaceDeclarationCommon>; +impl TypeDefinition { + pub fn loc(&self) -> &Location { + match self { + TypeDefinition::Struct { + loc, + start_associated_comments: _, + ending_associated_comments: _, + fields: _, + } + | TypeDefinition::Enum { + loc, + start_associated_comments: _, + ending_associated_comments: _, + variants: _, + } => loc, + } + } + + pub fn loc_mut(&mut self) -> &mut Location { + match self { + TypeDefinition::Struct { + loc, + start_associated_comments: _, + ending_associated_comments: _, + fields: _, + } + | TypeDefinition::Enum { + loc, + start_associated_comments: _, + ending_associated_comments: _, + variants: _, + } => loc, + } + } +} + +pub type ClassDefinition = + InterfaceDeclarationCommon, ClassMemberDefinition>; #[derive(Clone, PartialEq, Eq)] pub enum Toplevel { @@ -786,24 +848,24 @@ impl Toplevel { } } - pub fn extends_or_implements_nodes(&self) -> &Vec { + pub fn extends_or_implements_nodes(&self) -> Option<&ExtendsOrImplementsNodes> { match self { - Toplevel::Interface(i) => &i.extends_or_implements_nodes, - Toplevel::Class(c) => &c.extends_or_implements_nodes, + Toplevel::Interface(i) => i.extends_or_implements_nodes.as_ref(), + Toplevel::Class(c) => c.extends_or_implements_nodes.as_ref(), } } pub fn type_definition(&self) -> Option<&TypeDefinition> { match self { Toplevel::Interface(_) => None, - Toplevel::Class(c) => Some(&c.type_definition), + Toplevel::Class(c) => c.type_definition.as_ref(), } } pub fn members_iter(&self) -> MemberDeclarationsIterator { match self { - Toplevel::Interface(i) => MemberDeclarationsIterator::Interface(i.members.iter()), - Toplevel::Class(c) => MemberDeclarationsIterator::Class(c.members.iter()), + Toplevel::Interface(i) => MemberDeclarationsIterator::Interface(i.members.members.iter()), + Toplevel::Class(c) => MemberDeclarationsIterator::Class(c.members.members.iter()), } } } @@ -911,10 +973,11 @@ pub mod test_builder { annotation::Function { location: Location::dummy(), associated_comments: NO_COMMENT_REFERENCE, - parameters: annotation::FunctionParameters { + parameters: annotation::ParenthesizedAnnotationList { location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, ending_associated_comments: NO_COMMENT_REFERENCE, - parameters, + annotations: parameters, }, return_type: Box::new(return_type), } diff --git a/crates/samlang-ast/src/source_tests.rs b/crates/samlang-ast/src/source_tests.rs index 8e3994c6..c173a099 100644 --- a/crates/samlang-ast/src/source_tests.rs +++ b/crates/samlang-ast/src/source_tests.rs @@ -1,5 +1,7 @@ #[cfg(test)] mod tests { + use crate::source::annotation::ParenthesizedAnnotationList; + use super::super::loc::Location; use super::super::source::expr::*; use super::super::source::*; @@ -484,10 +486,11 @@ mod tests { annotation: Some(annotation::T::Fn(annotation::Function { location: Location::dummy(), associated_comments: NO_COMMENT_REFERENCE, - parameters: annotation::FunctionParameters { + parameters: annotation::ParenthesizedAnnotationList { location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: vec![ + annotations: vec![ annotation::T::Id(annotation::Id { location: Location::dummy(), module_reference: ModuleReference::DUMMY, @@ -565,32 +568,40 @@ mod tests { private: false, name: Id::from(PStr::LOWER_A), type_parameters: None, - extends_or_implements_nodes: vec![], + extends_or_implements_nodes: Some(ExtendsOrImplementsNodes { + location: Location::dummy(), + associated_comments: NO_COMMENT_REFERENCE, + nodes: vec![], + }), type_definition: (), - members: vec![ClassMemberDeclaration { + members: InterfaceMembersCommon { loc: Location::dummy(), - associated_comments: NO_COMMENT_REFERENCE, - is_public: true, - is_method: true, - name: Id::from(PStr::LOWER_A), - type_parameters: Some(annotation::TypeParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: vec![] - }), - parameters: FunctionParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: Rc::new(vec![AnnotatedId { - name: Id::from(PStr::LOWER_A), - type_: (), - annotation: builder.int_annot() - }]) - }, - return_type: builder.int_annot(), - }] + members: vec![ClassMemberDeclaration { + loc: Location::dummy(), + associated_comments: NO_COMMENT_REFERENCE, + is_public: true, + is_method: true, + name: Id::from(PStr::LOWER_A), + type_parameters: Some(annotation::TypeParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: vec![] + }), + parameters: FunctionParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: Rc::new(vec![AnnotatedId { + name: Id::from(PStr::LOWER_A), + type_: (), + annotation: builder.int_annot() + }]) + }, + return_type: builder.int_annot(), + }], + ending_associated_comments: NO_COMMENT_REFERENCE + } } .clone() .type_parameters @@ -606,6 +617,8 @@ mod tests { .is_empty()); let _ = TypeDefinition::Struct { loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, fields: vec![FieldDefinition { name: Id::from(heap.alloc_str_for_test("str")), annotation: builder.bool_annot(), @@ -615,9 +628,16 @@ mod tests { .clone(); let enum_type_def = TypeDefinition::Enum { loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, variants: vec![VariantDefinition { name: Id::from(heap.alloc_str_for_test("str")), - associated_data_types: vec![builder.bool_annot()], + associated_data_types: Some(ParenthesizedAnnotationList { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + annotations: vec![builder.bool_annot()], + }), }], }; assert!(enum_type_def.clone().eq(&enum_type_def)); @@ -654,38 +674,48 @@ mod tests { ending_associated_comments: NO_COMMENT_REFERENCE, parameters: vec![], }), - extends_or_implements_nodes: vec![], - type_definition: TypeDefinition::Struct { + extends_or_implements_nodes: Some(ExtendsOrImplementsNodes { + location: Location::dummy(), + associated_comments: NO_COMMENT_REFERENCE, + nodes: vec![], + }), + type_definition: Some(TypeDefinition::Struct { loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, fields: vec![FieldDefinition { name: Id::from(heap.alloc_str_for_test("str")), annotation: builder.bool_annot(), is_public: true, }], - }, - members: vec![ClassMemberDefinition { - decl: ClassMemberDeclaration { - loc: Location::dummy(), - associated_comments: NO_COMMENT_REFERENCE, - is_public: true, - is_method: true, - name: Id::from(PStr::LOWER_A), - type_parameters: Some(annotation::TypeParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: vec![], - }), - parameters: FunctionParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: Rc::new(vec![]), + }), + members: InterfaceMembersCommon { + loc: Location::dummy(), + members: vec![ClassMemberDefinition { + decl: ClassMemberDeclaration { + loc: Location::dummy(), + associated_comments: NO_COMMENT_REFERENCE, + is_public: true, + is_method: true, + name: Id::from(PStr::LOWER_A), + type_parameters: Some(annotation::TypeParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: vec![], + }), + parameters: FunctionParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: Rc::new(vec![]), + }, + return_type: builder.int_annot(), }, - return_type: builder.int_annot(), - }, - body: expr::E::Literal(expr::ExpressionCommon::dummy(()), Literal::Int(0)), - }], + body: expr::E::Literal(expr::ExpressionCommon::dummy(()), Literal::Int(0)), + }], + ending_associated_comments: NO_COMMENT_REFERENCE, + }, }); class.members_iter().next(); class.loc(); @@ -703,28 +733,36 @@ mod tests { ending_associated_comments: NO_COMMENT_REFERENCE, parameters: vec![], }), - extends_or_implements_nodes: vec![], + extends_or_implements_nodes: Some(ExtendsOrImplementsNodes { + location: Location::dummy(), + associated_comments: NO_COMMENT_REFERENCE, + nodes: vec![], + }), type_definition: (), - members: vec![ClassMemberDeclaration { + members: InterfaceMembersCommon { loc: Location::dummy(), - associated_comments: NO_COMMENT_REFERENCE, - is_public: true, - is_method: true, - name: Id::from(PStr::LOWER_A), - type_parameters: Some(annotation::TypeParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: vec![], - }), - parameters: FunctionParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: Rc::new(vec![]), - }, - return_type: builder.int_annot(), - }], + members: vec![ClassMemberDeclaration { + loc: Location::dummy(), + associated_comments: NO_COMMENT_REFERENCE, + is_public: true, + is_method: true, + name: Id::from(PStr::LOWER_A), + type_parameters: Some(annotation::TypeParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: vec![], + }), + parameters: FunctionParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: Rc::new(vec![]), + }, + return_type: builder.int_annot(), + }], + ending_associated_comments: NO_COMMENT_REFERENCE, + }, }); assert!(interface.clone().eq(&interface)); interface.members_iter().next(); diff --git a/crates/samlang-checker/src/checker_integration_tests.rs b/crates/samlang-checker/src/checker_integration_tests.rs index 943ac743..38bcacda 100644 --- a/crates/samlang-checker/src/checker_integration_tests.rs +++ b/crates/samlang-checker/src/checker_integration_tests.rs @@ -138,7 +138,7 @@ interface Conflicting1 { interface Conflicting2 { method foo(): bool } -interface ExtendingConfliting : Conflicting1, Conflicting2 +interface ExtendingConfliting : Conflicting1, Conflicting2 {} class ImplItself : ImplItself {} // error: expect interface type class ImplTArg : T {} // error: T not resolved "#, diff --git a/crates/samlang-checker/src/checker_tests.rs b/crates/samlang-checker/src/checker_tests.rs index 831135e8..61b588c2 100644 --- a/crates/samlang-checker/src/checker_tests.rs +++ b/crates/samlang-checker/src/checker_tests.rs @@ -3907,7 +3907,7 @@ interface Conflicting1 { interface Conflicting2 { method foo(): bool } -interface ExtendingConfliting : Conflicting1, Conflicting2 // no error, we only complain if it's used by classes +interface ExtendingConfliting : Conflicting1, Conflicting2 {} // no error, we only complain if it's used by classes class ImplItself : ImplItself {} // error: expect interface type class ImplTArg : T {} // error: T not resolved class NoBoundMethodCall { diff --git a/crates/samlang-checker/src/global_signature.rs b/crates/samlang-checker/src/global_signature.rs index 0ddc7150..d44683fd 100644 --- a/crates/samlang-checker/src/global_signature.rs +++ b/crates/samlang-checker/src/global_signature.rs @@ -54,8 +54,13 @@ pub fn build_module_signature( .map(|it| Rc::new(Type::Generic(Reason::new(it.loc, Some(it.loc)), it.name.name))) .collect_vec(), })); - match &class.type_definition { - TypeDefinition::Struct { loc, fields } => { + match class.type_definition.as_ref() { + Some(TypeDefinition::Struct { + loc, + start_associated_comments: _, + ending_associated_comments: _, + fields, + }) => { let type_def_reason = Reason::new(*loc, Some(*loc)); let ctor_fn = MemberSignature { is_public: true, @@ -85,7 +90,12 @@ pub fn build_module_signature( .collect(), )) } - TypeDefinition::Enum { loc, variants } => { + Some(TypeDefinition::Enum { + loc, + start_associated_comments: _, + ending_associated_comments: _, + variants, + }) => { let type_def_reason = Reason::new(*loc, Some(*loc)); for variant in variants { let ctor_fn = MemberSignature { @@ -96,6 +106,7 @@ pub fn build_module_signature( argument_types: variant .associated_data_types .iter() + .flat_map(|it| &it.annotations) .map(|annot| Rc::new(Type::from_annotation(annot))) .collect(), return_type: class_type.clone(), @@ -111,12 +122,14 @@ pub fn build_module_signature( types: variant .associated_data_types .iter() + .flat_map(|it| &it.annotations) .map(|it| Rc::new(Type::from_annotation(it))) .collect(), }) .collect(), )) } + None => Some(TypeDefinitionSignature::Enum(vec![])), } } else { None @@ -132,6 +145,7 @@ pub fn build_module_signature( super_types: toplevel .extends_or_implements_nodes() .iter() + .flat_map(|it| &it.nodes) .map(NominalType::from_annotation) .collect(), }, diff --git a/crates/samlang-checker/src/main_checker.rs b/crates/samlang-checker/src/main_checker.rs index 87016128..576a9ac8 100644 --- a/crates/samlang-checker/src/main_checker.rs +++ b/crates/samlang-checker/src/main_checker.rs @@ -13,7 +13,8 @@ use itertools::Itertools; use samlang_ast::{ source::{ annotation, expr, pattern, ClassMemberDeclaration, ClassMemberDefinition, Id, - InterfaceDeclarationCommon, Literal, Module, OptionallyAnnotatedId, Toplevel, TypeDefinition, + InterfaceDeclarationCommon, InterfaceMembersCommon, Literal, Module, OptionallyAnnotatedId, + Toplevel, TypeDefinition, }, Description, Location, Reason, }; @@ -1644,20 +1645,33 @@ pub fn type_check_module( toplevel_tparams_sig.clone(), ); validate_tparams_signature_type_instantiation(&mut cx, &toplevel_tparams_sig); - for bound in toplevel.extends_or_implements_nodes() { + for bound in toplevel.extends_or_implements_nodes().iter().flat_map(|it| &it.nodes) { cx.validate_type_instantiation_allow_abstract_types(&Type::Nominal( NominalType::from_annotation(bound), )); } if let Some(type_definition) = toplevel.type_definition() { match type_definition { - TypeDefinition::Struct { loc: _, fields } => { + TypeDefinition::Struct { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + fields, + } => { for field in fields { cx.validate_type_instantiation_strictly(&Type::from_annotation(&field.annotation)) } } - TypeDefinition::Enum { loc: _, variants } => { - for t in variants.iter().flat_map(|it| it.associated_data_types.iter()) { + TypeDefinition::Enum { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + variants, + } => { + for t in variants + .iter() + .flat_map(|it| it.associated_data_types.iter().flat_map(|it| &it.annotations)) + { cx.validate_type_instantiation_strictly(&Type::from_annotation(t)) } } @@ -1724,7 +1738,7 @@ pub fn type_check_module( global_signature::resolve_all_member_names(global_cx, &resolved_super_types, false); let mut missing_method_members = global_signature::resolve_all_member_names(global_cx, &resolved_super_types, true); - for member in &c.members { + for member in &c.members.members { let n = member.decl.name.name; if member.decl.is_method { missing_method_members.remove(&n); @@ -1732,15 +1746,21 @@ pub fn type_check_module( missing_function_members.remove(&n); } } - match &c.type_definition { - TypeDefinition::Struct { .. } => { + match c.type_definition.as_ref() { + Some(TypeDefinition::Struct { .. }) => { missing_function_members.remove(&PStr::INIT); } - TypeDefinition::Enum { loc: _, variants } => { + Some(TypeDefinition::Enum { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + variants, + }) => { for variant in variants { missing_function_members.remove(&variant.name.name); } } + None => {} } missing_function_members.extend(&missing_method_members); if !missing_function_members.is_empty() { @@ -1752,7 +1772,7 @@ pub fn type_check_module( local_cx.write(c.loc, Rc::new(Type::Nominal(nominal_type))); let mut checked_members = vec![]; - for member in &c.members { + for member in &c.members.members { let tparam_sigs = if member.decl.is_method { let mut sigs = TypeParameterSignature::from_list(toplevel.type_parameters()); let mut local_sigs = @@ -1784,7 +1804,11 @@ pub fn type_check_module( type_parameters: c.type_parameters.clone(), extends_or_implements_nodes: c.extends_or_implements_nodes.clone(), type_definition: c.type_definition.clone(), - members: checked_members, + members: InterfaceMembersCommon { + loc: c.members.loc, + members: checked_members, + ending_associated_comments: c.members.ending_associated_comments, + }, }) } }; diff --git a/crates/samlang-checker/src/ssa_analysis.rs b/crates/samlang-checker/src/ssa_analysis.rs index 222db22e..ffdb7d4d 100644 --- a/crates/samlang-checker/src/ssa_analysis.rs +++ b/crates/samlang-checker/src/ssa_analysis.rs @@ -103,7 +103,7 @@ impl<'a> SsaAnalysisState<'a> { let type_parameters = toplevel.type_parameters(); let type_definition = toplevel.type_definition(); - for t in toplevel.extends_or_implements_nodes() { + for t in toplevel.extends_or_implements_nodes().iter().flat_map(|it| &it.nodes) { self.use_id(&t.id.name, t.id.loc, true); } @@ -112,7 +112,7 @@ impl<'a> SsaAnalysisState<'a> { self.context.push_scope(); { self.visit_type_parameters_with_bounds(type_parameters); - for t in toplevel.extends_or_implements_nodes() { + for t in toplevel.extends_or_implements_nodes().iter().flat_map(|it| &it.nodes) { for annot in t.type_arguments.iter().flat_map(|it| &it.arguments) { self.visit_annot(annot); } @@ -121,16 +121,26 @@ impl<'a> SsaAnalysisState<'a> { let mut names = vec![]; let mut annots = vec![]; match type_def { - TypeDefinition::Struct { loc: _, fields } => { + TypeDefinition::Struct { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + fields, + } => { for field in fields { names.push(&field.name); annots.push(&field.annotation); } } - TypeDefinition::Enum { loc: _, variants } => { + TypeDefinition::Enum { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + variants, + } => { for variant in variants { names.push(&variant.name); - for annot in &variant.associated_data_types { + for annot in variant.associated_data_types.iter().flat_map(|it| &it.annotations) { annots.push(annot); } } @@ -156,7 +166,7 @@ impl<'a> SsaAnalysisState<'a> { self.context.pop_scope(); // Visit instance methods self.context.push_scope(); - if type_definition.is_some() { + if toplevel.is_class() { self.define_id(PStr::THIS, toplevel.loc()); } for tparam in type_parameters.iter().flat_map(|it| &it.parameters) { @@ -177,14 +187,14 @@ impl<'a> SsaAnalysisState<'a> { fn visit_members(&mut self, toplevel: &Toplevel<()>, is_method: bool) { match toplevel { Toplevel::Class(c) => { - for m in &c.members { + for m in &c.members.members { if m.decl.is_method == is_method { self.visit_member_declaration(&m.decl, Some(&m.body)); } } } Toplevel::Interface(d) => { - for m in &d.members { + for m in &d.members.members { if m.is_method == is_method { self.visit_member_declaration(m, None); } @@ -395,7 +405,7 @@ impl<'a> SsaAnalysisState<'a> { parameters, return_type, }) => { - for arg in ¶meters.parameters { + for arg in ¶meters.annotations { self.visit_annot(arg); } self.visit_annot(return_type); diff --git a/crates/samlang-checker/src/ssa_analysis_tests.rs b/crates/samlang-checker/src/ssa_analysis_tests.rs index db7cfbe5..39105d0a 100644 --- a/crates/samlang-checker/src/ssa_analysis_tests.rs +++ b/crates/samlang-checker/src/ssa_analysis_tests.rs @@ -119,7 +119,7 @@ def_to_use_map: let program_str = r#" import { Pair } from stdlib.utils -interface Useless +interface Useless {} interface Comparable : Useless { method compare(other: T): int diff --git a/crates/samlang-checker/src/type_.rs b/crates/samlang-checker/src/type_.rs index 1a43e32a..d60cc70e 100644 --- a/crates/samlang-checker/src/type_.rs +++ b/crates/samlang-checker/src/type_.rs @@ -160,7 +160,7 @@ impl FunctionType { reason: Reason::new(annotation.location, Some(annotation.location)), argument_types: annotation .parameters - .parameters + .annotations .iter() .map(|annot| Rc::new(Type::from_annotation(annot))) .collect(), diff --git a/crates/samlang-compiler/src/hir_lowering.rs b/crates/samlang-compiler/src/hir_lowering.rs index 69b4d8dd..87eeda15 100644 --- a/crates/samlang-compiler/src/hir_lowering.rs +++ b/crates/samlang-compiler/src/hir_lowering.rs @@ -1186,10 +1186,10 @@ fn compile_sources_with_generics_preserved( heap, mod_ref, c.name.name, - &c.type_definition, + c.type_definition.as_ref(), )); if c.name.name == PStr::MAIN_TYPE - && c.members.iter().any(|source::ClassMemberDefinition { decl, .. }| { + && c.members.members.iter().any(|source::ClassMemberDefinition { decl, .. }| { decl.name.name == PStr::MAIN_FN && decl.parameters.parameters.is_empty() && decl.type_parameters.is_none() @@ -1221,7 +1221,7 @@ fn compile_sources_with_generics_preserved( c.name.name, &type_def_mappings, )); - for member in &c.members { + for member in &c.members.members { let function_name = hir::FunctionName { type_name: hir::TypeName { module_reference: Some(*module_reference), @@ -3339,9 +3339,13 @@ return (_t2: int);"#, private: false, name: source::Id::from(heap.alloc_str_for_test("I")), type_parameters: None, - extends_or_implements_nodes: vec![], + extends_or_implements_nodes: None, type_definition: (), - members: vec![], + members: source::InterfaceMembersCommon { + loc: Location::dummy(), + members: vec![], + ending_associated_comments: NO_COMMENT_REFERENCE, + }, }), source::Toplevel::Class(source::InterfaceDeclarationCommon { loc: Location::dummy(), @@ -3349,116 +3353,117 @@ return (_t2: int);"#, private: false, name: source::Id::from(PStr::MAIN_TYPE), type_parameters: None, - extends_or_implements_nodes: vec![], - type_definition: source::TypeDefinition::Struct { + extends_or_implements_nodes: None, + type_definition: None, + members: source::InterfaceMembersCommon { loc: Location::dummy(), - fields: vec![], - }, - members: vec![ - source::ClassMemberDefinition { - decl: source::ClassMemberDeclaration { - loc: Location::dummy(), - associated_comments: NO_COMMENT_REFERENCE, - is_public: true, - is_method: false, - name: source::Id::from(PStr::MAIN_FN), - type_parameters: None, - parameters: source::FunctionParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: Rc::new(vec![]), - }, - return_type: annot_builder.unit_annot(), - }, - body: source::expr::E::Call(source::expr::Call { - common: source::expr::ExpressionCommon::dummy(builder.unit_type()), - callee: Box::new(source::expr::E::MethodAccess(source::expr::MethodAccess { - common: source::expr::ExpressionCommon::dummy( - builder.fun_type(vec![], builder.int_type()), - ), - explicit_type_arguments: None, - inferred_type_arguments: vec![], - object: Box::new(source::expr::E::ClassId( - source::expr::ExpressionCommon::dummy(Rc::new(type_::Type::Nominal( - type_::NominalType { - reason: Reason::dummy(), - is_class_statics: true, - module_reference: ModuleReference::DUMMY, - id: heap.alloc_str_for_test("Class1"), - type_arguments: vec![], - }, - ))), - ModuleReference::DUMMY, - source::Id::from(heap.alloc_str_for_test("Class1")), - )), - method_name: source::Id::from(heap.alloc_str_for_test("infiniteLoop")), - })), - arguments: source::expr::ParenthesizedExpressionList { + members: vec![ + source::ClassMemberDefinition { + decl: source::ClassMemberDeclaration { loc: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - expressions: vec![], + associated_comments: NO_COMMENT_REFERENCE, + is_public: true, + is_method: false, + name: source::Id::from(PStr::MAIN_FN), + type_parameters: None, + parameters: source::FunctionParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: Rc::new(vec![]), + }, + return_type: annot_builder.unit_annot(), }, - }), - }, - source::ClassMemberDefinition { - decl: source::ClassMemberDeclaration { - loc: Location::dummy(), - associated_comments: NO_COMMENT_REFERENCE, - is_public: true, - is_method: false, - name: source::Id::from(heap.alloc_str_for_test("loopy")), - type_parameters: Some(source::annotation::TypeParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: vec![source::annotation::TypeParameter { + body: source::expr::E::Call(source::expr::Call { + common: source::expr::ExpressionCommon::dummy(builder.unit_type()), + callee: Box::new(source::expr::E::MethodAccess(source::expr::MethodAccess { + common: source::expr::ExpressionCommon::dummy( + builder.fun_type(vec![], builder.int_type()), + ), + explicit_type_arguments: None, + inferred_type_arguments: vec![], + object: Box::new(source::expr::E::ClassId( + source::expr::ExpressionCommon::dummy(Rc::new(type_::Type::Nominal( + type_::NominalType { + reason: Reason::dummy(), + is_class_statics: true, + module_reference: ModuleReference::DUMMY, + id: heap.alloc_str_for_test("Class1"), + type_arguments: vec![], + }, + ))), + ModuleReference::DUMMY, + source::Id::from(heap.alloc_str_for_test("Class1")), + )), + method_name: source::Id::from(heap.alloc_str_for_test("infiniteLoop")), + })), + arguments: source::expr::ParenthesizedExpressionList { loc: Location::dummy(), - name: source::Id::from(heap.alloc_str_for_test("T")), - bound: None, - }], + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + expressions: vec![], + }, }), - parameters: source::FunctionParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: Rc::new(vec![]), - }, - return_type: annot_builder.unit_annot(), }, - body: source::expr::E::Call(source::expr::Call { - common: source::expr::ExpressionCommon::dummy(builder.unit_type()), - callee: Box::new(source::expr::E::MethodAccess(source::expr::MethodAccess { - common: source::expr::ExpressionCommon::dummy( - builder.fun_type(vec![], builder.int_type()), - ), - explicit_type_arguments: None, - inferred_type_arguments: vec![], - object: Box::new(source::expr::E::ClassId( - source::expr::ExpressionCommon::dummy(Rc::new(type_::Type::Nominal( - type_::NominalType { - reason: Reason::dummy(), - is_class_statics: true, - module_reference: ModuleReference::DUMMY, - id: heap.alloc_str_for_test("T"), - type_arguments: vec![], - }, - ))), - ModuleReference::DUMMY, - source::Id::from(heap.alloc_str_for_test("T")), - )), - method_name: source::Id::from(heap.alloc_str_for_test("loopy")), - })), - arguments: source::expr::ParenthesizedExpressionList { + source::ClassMemberDefinition { + decl: source::ClassMemberDeclaration { loc: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - expressions: vec![], + associated_comments: NO_COMMENT_REFERENCE, + is_public: true, + is_method: false, + name: source::Id::from(heap.alloc_str_for_test("loopy")), + type_parameters: Some(source::annotation::TypeParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: vec![source::annotation::TypeParameter { + loc: Location::dummy(), + name: source::Id::from(heap.alloc_str_for_test("T")), + bound: None, + }], + }), + parameters: source::FunctionParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: Rc::new(vec![]), + }, + return_type: annot_builder.unit_annot(), }, - }), - }, - ], + body: source::expr::E::Call(source::expr::Call { + common: source::expr::ExpressionCommon::dummy(builder.unit_type()), + callee: Box::new(source::expr::E::MethodAccess(source::expr::MethodAccess { + common: source::expr::ExpressionCommon::dummy( + builder.fun_type(vec![], builder.int_type()), + ), + explicit_type_arguments: None, + inferred_type_arguments: vec![], + object: Box::new(source::expr::E::ClassId( + source::expr::ExpressionCommon::dummy(Rc::new(type_::Type::Nominal( + type_::NominalType { + reason: Reason::dummy(), + is_class_statics: true, + module_reference: ModuleReference::DUMMY, + id: heap.alloc_str_for_test("T"), + type_arguments: vec![], + }, + ))), + ModuleReference::DUMMY, + source::Id::from(heap.alloc_str_for_test("T")), + )), + method_name: source::Id::from(heap.alloc_str_for_test("loopy")), + })), + arguments: source::expr::ParenthesizedExpressionList { + loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + expressions: vec![], + }, + }), + }, + ], + ending_associated_comments: NO_COMMENT_REFERENCE, + }, }), source::Toplevel::Class(source::InterfaceDeclarationCommon { loc: Location::dummy(), @@ -3466,136 +3471,63 @@ return (_t2: int);"#, private: false, name: source::Id::from(heap.alloc_str_for_test("Class1")), type_parameters: None, - extends_or_implements_nodes: vec![], - type_definition: source::TypeDefinition::Struct { + extends_or_implements_nodes: None, + type_definition: Some(source::TypeDefinition::Struct { loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, fields: vec![source::FieldDefinition { name: source::Id::from(PStr::LOWER_A), annotation: annot_builder.int_annot(), is_public: true, }], - }, - members: vec![ - source::ClassMemberDefinition { - decl: source::ClassMemberDeclaration { - loc: Location::dummy(), - associated_comments: NO_COMMENT_REFERENCE, - is_public: true, - is_method: true, - name: source::Id::from(heap.alloc_str_for_test("foo")), - type_parameters: None, - parameters: source::FunctionParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: Rc::new(vec![source::AnnotatedId { - name: source::Id::from(PStr::LOWER_A), - type_: (), // builder.int_type(), - annotation: annot_builder.int_annot(), - }]), - }, - return_type: annot_builder.int_annot(), - }, - body: this_expr.clone(), - }, - source::ClassMemberDefinition { - decl: source::ClassMemberDeclaration { - loc: Location::dummy(), - associated_comments: NO_COMMENT_REFERENCE, - is_public: true, - is_method: false, - name: source::Id::from(heap.alloc_str_for_test("infiniteLoop")), - type_parameters: None, - parameters: source::FunctionParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: Rc::new(vec![]), - }, - return_type: annot_builder.unit_annot(), - }, - body: source::expr::E::Call(source::expr::Call { - common: source::expr::ExpressionCommon::dummy(builder.unit_type()), - callee: Box::new(source::expr::E::MethodAccess(source::expr::MethodAccess { - common: source::expr::ExpressionCommon::dummy( - builder.fun_type(vec![], builder.int_type()), - ), - explicit_type_arguments: None, - inferred_type_arguments: vec![], - object: Box::new(source::expr::E::ClassId( - source::expr::ExpressionCommon::dummy(Rc::new(type_::Type::Nominal( - type_::NominalType { - reason: Reason::dummy(), - is_class_statics: true, - module_reference: ModuleReference::DUMMY, - id: heap.alloc_str_for_test("Class1"), - type_arguments: vec![], - }, - ))), - ModuleReference::DUMMY, - source::Id::from(heap.alloc_str_for_test("Class1")), - )), - method_name: source::Id::from(heap.alloc_str_for_test("infiniteLoop")), - })), - arguments: source::expr::ParenthesizedExpressionList { + }), + members: source::InterfaceMembersCommon { + loc: Location::dummy(), + members: vec![ + source::ClassMemberDefinition { + decl: source::ClassMemberDeclaration { loc: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - expressions: vec![], - }, - }), - }, - source::ClassMemberDefinition { - decl: source::ClassMemberDeclaration { - loc: Location::dummy(), - associated_comments: NO_COMMENT_REFERENCE, - is_public: true, - is_method: false, - name: source::Id::from(heap.alloc_str_for_test("factorial")), - type_parameters: None, - parameters: source::FunctionParameters { - location: Location::dummy(), - start_associated_comments: NO_COMMENT_REFERENCE, - ending_associated_comments: NO_COMMENT_REFERENCE, - parameters: Rc::new(vec![ - source::AnnotatedId { - name: source::Id::from(heap.alloc_str_for_test("n")), - type_: (), // builder.int_type(), - annotation: annot_builder.int_annot(), - }, - source::AnnotatedId { - name: source::Id::from(heap.alloc_str_for_test("acc")), + associated_comments: NO_COMMENT_REFERENCE, + is_public: true, + is_method: true, + name: source::Id::from(heap.alloc_str_for_test("foo")), + type_parameters: None, + parameters: source::FunctionParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: Rc::new(vec![source::AnnotatedId { + name: source::Id::from(PStr::LOWER_A), type_: (), // builder.int_type(), annotation: annot_builder.int_annot(), - }, - ]), + }]), + }, + return_type: annot_builder.int_annot(), }, - return_type: annot_builder.int_annot(), + body: this_expr.clone(), }, - body: source::expr::E::IfElse(source::expr::IfElse { - common: source::expr::ExpressionCommon::dummy(builder.int_type()), - condition: Box::new(source::expr::IfElseCondition::Expression( - source::expr::E::Binary(source::expr::Binary { - common: source::expr::ExpressionCommon::dummy(builder.int_type()), - operator_preceding_comments: NO_COMMENT_REFERENCE, - operator: source::expr::BinaryOperator::EQ, - e1: Box::new(id_expr(heap.alloc_str_for_test("n"), builder.int_type())), - e2: Box::new(source::expr::E::Literal( - source::expr::ExpressionCommon::dummy(builder.int_type()), - source::Literal::Int(0), - )), - }), - )), - e1: Box::new(source::expr::E::Literal( - source::expr::ExpressionCommon::dummy(builder.int_type()), - source::Literal::Int(1), - )), - e2: Box::new(source::expr::E::Call(source::expr::Call { - common: source::expr::ExpressionCommon::dummy(builder.int_type()), + source::ClassMemberDefinition { + decl: source::ClassMemberDeclaration { + loc: Location::dummy(), + associated_comments: NO_COMMENT_REFERENCE, + is_public: true, + is_method: false, + name: source::Id::from(heap.alloc_str_for_test("infiniteLoop")), + type_parameters: None, + parameters: source::FunctionParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: Rc::new(vec![]), + }, + return_type: annot_builder.unit_annot(), + }, + body: source::expr::E::Call(source::expr::Call { + common: source::expr::ExpressionCommon::dummy(builder.unit_type()), callee: Box::new(source::expr::E::MethodAccess(source::expr::MethodAccess { common: source::expr::ExpressionCommon::dummy( - builder - .fun_type(vec![builder.int_type(), builder.int_type()], builder.int_type()), + builder.fun_type(vec![], builder.int_type()), ), explicit_type_arguments: None, inferred_type_arguments: vec![], @@ -3612,36 +3544,115 @@ return (_t2: int);"#, ModuleReference::DUMMY, source::Id::from(heap.alloc_str_for_test("Class1")), )), - method_name: source::Id::from(heap.alloc_str_for_test("factorial")), + method_name: source::Id::from(heap.alloc_str_for_test("infiniteLoop")), })), arguments: source::expr::ParenthesizedExpressionList { loc: Location::dummy(), start_associated_comments: NO_COMMENT_REFERENCE, ending_associated_comments: NO_COMMENT_REFERENCE, - expressions: vec![ - source::expr::E::Binary(source::expr::Binary { - common: source::expr::ExpressionCommon::dummy(builder.int_type()), - operator_preceding_comments: NO_COMMENT_REFERENCE, - operator: source::expr::BinaryOperator::MINUS, - e1: Box::new(id_expr(heap.alloc_str_for_test("n"), builder.int_type())), - e2: Box::new(source::expr::E::Literal( - source::expr::ExpressionCommon::dummy(builder.int_type()), - source::Literal::Int(1), - )), - }), - source::expr::E::Binary(source::expr::Binary { - common: source::expr::ExpressionCommon::dummy(builder.int_type()), - operator_preceding_comments: NO_COMMENT_REFERENCE, - operator: source::expr::BinaryOperator::MUL, - e1: Box::new(id_expr(heap.alloc_str_for_test("n"), builder.int_type())), - e2: Box::new(id_expr(heap.alloc_str_for_test("acc"), builder.int_type())), - }), - ], + expressions: vec![], }, - })), - }), - }, - ], + }), + }, + source::ClassMemberDefinition { + decl: source::ClassMemberDeclaration { + loc: Location::dummy(), + associated_comments: NO_COMMENT_REFERENCE, + is_public: true, + is_method: false, + name: source::Id::from(heap.alloc_str_for_test("factorial")), + type_parameters: None, + parameters: source::FunctionParameters { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + parameters: Rc::new(vec![ + source::AnnotatedId { + name: source::Id::from(heap.alloc_str_for_test("n")), + type_: (), // builder.int_type(), + annotation: annot_builder.int_annot(), + }, + source::AnnotatedId { + name: source::Id::from(heap.alloc_str_for_test("acc")), + type_: (), // builder.int_type(), + annotation: annot_builder.int_annot(), + }, + ]), + }, + return_type: annot_builder.int_annot(), + }, + body: source::expr::E::IfElse(source::expr::IfElse { + common: source::expr::ExpressionCommon::dummy(builder.int_type()), + condition: Box::new(source::expr::IfElseCondition::Expression( + source::expr::E::Binary(source::expr::Binary { + common: source::expr::ExpressionCommon::dummy(builder.int_type()), + operator_preceding_comments: NO_COMMENT_REFERENCE, + operator: source::expr::BinaryOperator::EQ, + e1: Box::new(id_expr(heap.alloc_str_for_test("n"), builder.int_type())), + e2: Box::new(source::expr::E::Literal( + source::expr::ExpressionCommon::dummy(builder.int_type()), + source::Literal::Int(0), + )), + }), + )), + e1: Box::new(source::expr::E::Literal( + source::expr::ExpressionCommon::dummy(builder.int_type()), + source::Literal::Int(1), + )), + e2: Box::new(source::expr::E::Call(source::expr::Call { + common: source::expr::ExpressionCommon::dummy(builder.int_type()), + callee: Box::new(source::expr::E::MethodAccess(source::expr::MethodAccess { + common: source::expr::ExpressionCommon::dummy(builder.fun_type( + vec![builder.int_type(), builder.int_type()], + builder.int_type(), + )), + explicit_type_arguments: None, + inferred_type_arguments: vec![], + object: Box::new(source::expr::E::ClassId( + source::expr::ExpressionCommon::dummy(Rc::new(type_::Type::Nominal( + type_::NominalType { + reason: Reason::dummy(), + is_class_statics: true, + module_reference: ModuleReference::DUMMY, + id: heap.alloc_str_for_test("Class1"), + type_arguments: vec![], + }, + ))), + ModuleReference::DUMMY, + source::Id::from(heap.alloc_str_for_test("Class1")), + )), + method_name: source::Id::from(heap.alloc_str_for_test("factorial")), + })), + arguments: source::expr::ParenthesizedExpressionList { + loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + expressions: vec![ + source::expr::E::Binary(source::expr::Binary { + common: source::expr::ExpressionCommon::dummy(builder.int_type()), + operator_preceding_comments: NO_COMMENT_REFERENCE, + operator: source::expr::BinaryOperator::MINUS, + e1: Box::new(id_expr(heap.alloc_str_for_test("n"), builder.int_type())), + e2: Box::new(source::expr::E::Literal( + source::expr::ExpressionCommon::dummy(builder.int_type()), + source::Literal::Int(1), + )), + }), + source::expr::E::Binary(source::expr::Binary { + common: source::expr::ExpressionCommon::dummy(builder.int_type()), + operator_preceding_comments: NO_COMMENT_REFERENCE, + operator: source::expr::BinaryOperator::MUL, + e1: Box::new(id_expr(heap.alloc_str_for_test("n"), builder.int_type())), + e2: Box::new(id_expr(heap.alloc_str_for_test("acc"), builder.int_type())), + }), + ], + }, + })), + }), + }, + ], + ending_associated_comments: NO_COMMENT_REFERENCE, + }, }), source::Toplevel::Class(source::InterfaceDeclarationCommon { loc: Location::dummy(), @@ -3649,15 +3660,26 @@ return (_t2: int);"#, private: false, name: source::Id::from(heap.alloc_str_for_test("Class2")), type_parameters: None, - extends_or_implements_nodes: vec![], - type_definition: source::TypeDefinition::Enum { + extends_or_implements_nodes: None, + type_definition: Some(source::TypeDefinition::Enum { loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, variants: vec![source::VariantDefinition { name: source::Id::from(heap.alloc_str_for_test("Tag")), - associated_data_types: vec![annot_builder.int_annot()], + associated_data_types: Some(source::annotation::ParenthesizedAnnotationList { + location: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, + annotations: vec![annot_builder.int_annot()], + }), }], + }), + members: source::InterfaceMembersCommon { + loc: Location::dummy(), + members: vec![], + ending_associated_comments: NO_COMMENT_REFERENCE, }, - members: vec![], }), source::Toplevel::Class(source::InterfaceDeclarationCommon { loc: Location::dummy(), @@ -3674,9 +3696,11 @@ return (_t2: int);"#, bound: None, }], }), - extends_or_implements_nodes: vec![], - type_definition: source::TypeDefinition::Struct { + extends_or_implements_nodes: None, + type_definition: Some(source::TypeDefinition::Struct { loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, fields: vec![source::FieldDefinition { name: source::Id::from(PStr::LOWER_A), annotation: annot_builder.fn_annot( @@ -3688,8 +3712,12 @@ return (_t2: int);"#, ), is_public: true, }], + }), + members: source::InterfaceMembersCommon { + loc: Location::dummy(), + members: vec![], + ending_associated_comments: NO_COMMENT_REFERENCE, }, - members: vec![], }), ], trailing_comments: NO_COMMENT_REFERENCE, @@ -3708,16 +3736,11 @@ return (_t2: int);"#, ]); let generics_preserved_expected = r#"closure type _$SyntheticIDType0 = (DUMMY_A, T) -> int -object type DUMMY_Main = [] +variant type DUMMY_Main = [] object type DUMMY_Class1 = [int] variant type DUMMY_Class2 = [(Tag: [int])] object type DUMMY_Class3 = [_$SyntheticIDType0] variant type _Str = [] -function DUMMY_Main$init(_this: int): DUMMY_Main { - let o: DUMMY_Main = []; - return (o: DUMMY_Main); -} - function DUMMY_Main$main(_this: int): int { DUMMY_Class1$infiniteLoop(0); return 0; diff --git a/crates/samlang-compiler/src/hir_type_conversion.rs b/crates/samlang-compiler/src/hir_type_conversion.rs index 6708292c..3900b13b 100644 --- a/crates/samlang-compiler/src/hir_type_conversion.rs +++ b/crates/samlang-compiler/src/hir_type_conversion.rs @@ -215,14 +215,20 @@ impl TypeLoweringManager { heap: &mut Heap, module_reference: &ModuleReference, identifier: PStr, - source_type_def: &source::TypeDefinition, + source_type_def: Option<&source::TypeDefinition>, ) -> TypeDefinition { let type_parameters = Vec::from_iter( self.generic_types.iter().cloned().sorted_by(|x, y| x.as_str(heap).cmp(y.as_str(heap))), ); + let name = TypeName { module_reference: Some(*module_reference), type_name: identifier }; match source_type_def { - source::TypeDefinition::Struct { loc: _, fields } => TypeDefinition { - name: TypeName { module_reference: Some(*module_reference), type_name: identifier }, + Some(source::TypeDefinition::Struct { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + fields, + }) => TypeDefinition { + name, type_parameters, mappings: TypeDefinitionMappings::Struct( fields @@ -233,8 +239,13 @@ impl TypeLoweringManager { .collect(), ), }, - source::TypeDefinition::Enum { loc: _, variants } => TypeDefinition { - name: TypeName { module_reference: Some(*module_reference), type_name: identifier }, + Some(source::TypeDefinition::Enum { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + variants, + }) => TypeDefinition { + name, type_parameters, mappings: TypeDefinitionMappings::Enum( variants @@ -245,6 +256,7 @@ impl TypeLoweringManager { variant .associated_data_types .iter() + .flat_map(|it| &it.annotations) .map(|t| self.lower_source_type(heap, &type_::Type::from_annotation(t))) .collect_vec(), ) @@ -252,6 +264,9 @@ impl TypeLoweringManager { .collect(), ), }, + None => { + TypeDefinition { name, type_parameters, mappings: TypeDefinitionMappings::Enum(vec![]) } + } } } @@ -276,7 +291,11 @@ impl TypeLoweringManager { mod tests { use super::*; use pretty_assertions::assert_eq; - use samlang_ast::{hir::INT_TYPE, source::test_builder, Location, Reason}; + use samlang_ast::{ + hir::INT_TYPE, + source::{test_builder, NO_COMMENT_REFERENCE}, + Location, Reason, + }; use samlang_checker::type_::test_type_builder; use samlang_heap::PStr; @@ -529,6 +548,8 @@ mod tests { let type_def = source::TypeDefinition::Struct { loc: Location::dummy(), + start_associated_comments: NO_COMMENT_REFERENCE, + ending_associated_comments: NO_COMMENT_REFERENCE, fields: vec![ source::FieldDefinition { name: source::Id::from(PStr::LOWER_A), @@ -556,7 +577,7 @@ mod tests { }; let foo_str = heap.alloc_str_for_test("Foo"); let type_def = - manager.lower_source_type_definition(heap, &ModuleReference::ROOT, foo_str, &type_def); + manager.lower_source_type_definition(heap, &ModuleReference::ROOT, foo_str, Some(&type_def)); let SynthesizedTypes { closure_types, mut tuple_types } = manager.type_synthesizer.synthesized_types(); assert_eq!( diff --git a/crates/samlang-parser/src/lib.rs b/crates/samlang-parser/src/lib.rs index 13c17d74..b0576504 100644 --- a/crates/samlang-parser/src/lib.rs +++ b/crates/samlang-parser/src/lib.rs @@ -271,12 +271,12 @@ mod tests { class Util {} - class Util + class Util {} - class Util + class Util {} - class A(val a: () -> int) : Baz - class A(val a: (Str) -> int) : Baz + class A(val a: () -> int) : Baz {} + class A(val a: (Str) -> int) : Baz {} class TParamFixParserTest : A, A<(A) -> int> { function , R: Bar<(A) -> int>> f(): unit = {} diff --git a/crates/samlang-parser/src/source_parser.rs b/crates/samlang-parser/src/source_parser.rs index e6606a1a..f4e55f4b 100644 --- a/crates/samlang-parser/src/source_parser.rs +++ b/crates/samlang-parser/src/source_parser.rs @@ -367,7 +367,7 @@ mod toplevel_parser { super::lexer::{Keyword, Token, TokenContent, TokenOp}, MAX_STRUCT_SIZE, MAX_VARIANT_SIZE, }; - use samlang_ast::{source::*, Location}; + use samlang_ast::source::*; use std::collections::HashSet; pub(super) fn parse_toplevel(parser: &mut super::SourceParser) -> Toplevel<()> { @@ -390,13 +390,16 @@ mod toplevel_parser { } pub(super) fn parse_class(parser: &mut super::SourceParser) -> ClassDefinition<()> { - let associated_comments = parser.collect_preceding_comments(); + let mut associated_comments = vec![]; let (mut loc, private) = if let Token(loc, TokenContent::Keyword(Keyword::PRIVATE)) = parser.peek() { + associated_comments.append(&mut parser.collect_preceding_comments()); parser.consume(); + associated_comments.append(&mut parser.collect_preceding_comments()); parser.assert_and_consume_keyword(Keyword::CLASS); (loc, true) } else { + associated_comments.append(&mut parser.collect_preceding_comments()); (parser.assert_and_consume_keyword(Keyword::CLASS), false) }; let name = parser.parse_upper_id(); @@ -404,61 +407,49 @@ mod toplevel_parser { parser.available_tparams = HashSet::new(); let type_parameters = super::type_parser::parse_type_parameters(parser); let (type_definition, extends_or_implements_nodes) = match parser.peek().1 { - TokenContent::Operator(TokenOp::LBRACE | TokenOp::COLON) - | TokenContent::Keyword(Keyword::CLASS | Keyword::INTERFACE | Keyword::PRIVATE) => { - let nodes = if let TokenContent::Operator(TokenOp::COLON) = parser.peek().1 { - parser.consume(); - let nodes = parse_extends_or_implements_nodes(parser); - loc = loc.union(&nodes.last().unwrap().location); - nodes - } else { - vec![] - }; + TokenContent::Operator(TokenOp::LBRACE | TokenOp::COLON) => { loc = if let Some(tparams_node) = &type_parameters { loc.union(&tparams_node.location) } else { loc }; - let type_def = TypeDefinition::Struct { loc: parser.peek().0, fields: vec![] }; - (type_def, nodes) + let extends_or_implements_nodes = parse_extends_or_implements_nodes(parser); + if let Some(node) = &extends_or_implements_nodes { + loc = loc.union(&node.location); + } + (None, extends_or_implements_nodes) } _ => { - let type_def_loc_start = parser.assert_and_consume_operator(TokenOp::LPAREN); let mut type_def = parse_type_definition_inner(parser); - let type_def_loc_end = parser.assert_and_consume_operator(TokenOp::RPAREN); let type_def_loc = type_parameters .as_ref() .map(|it| it.location) - .unwrap_or(type_def_loc_start) - .union(&type_def_loc_end); - match &mut type_def { - TypeDefinition::Struct { loc, fields: _ } => *loc = type_def_loc, - TypeDefinition::Enum { loc, variants: _ } => *loc = type_def_loc, + .unwrap_or(*type_def.loc()) + .union(type_def.loc()); + *type_def.loc_mut() = type_def_loc; + loc = loc.union(&type_def_loc); + let extends_or_implements_nodes = parse_extends_or_implements_nodes(parser); + if let Some(node) = &extends_or_implements_nodes { + loc = loc.union(&node.location); } - loc = loc.union(&type_def_loc_end); - let nodes = if let TokenContent::Operator(TokenOp::COLON) = parser.peek().1 { - parser.consume(); - let nodes = parse_extends_or_implements_nodes(parser); - loc = loc.union(&nodes.last().unwrap().location); - nodes - } else { - vec![] - }; - (type_def, nodes) + (Some(type_def), extends_or_implements_nodes) } }; let mut members = vec![]; - if !peeked_class_or_interface_start(parser) { - parser.assert_and_consume_operator(TokenOp::LBRACE); - while let TokenContent::Keyword(Keyword::FUNCTION | Keyword::METHOD | Keyword::PRIVATE) = - parser.peek().1 - { - let saved_upper_type_parameters = parser.available_tparams.clone(); - members.push(parse_class_member_definition(parser)); - parser.available_tparams = saved_upper_type_parameters; - } - loc = loc.union(&parser.assert_and_consume_operator(TokenOp::RBRACE)); + let members_start_loc = parser.assert_and_consume_operator(TokenOp::LBRACE); + while let TokenContent::Keyword(Keyword::FUNCTION | Keyword::METHOD | Keyword::PRIVATE) = + parser.peek().1 + { + let saved_upper_type_parameters = parser.available_tparams.clone(); + members.push(parse_class_member_definition(parser)); + parser.available_tparams = saved_upper_type_parameters; } + let ending_associated_comments = { + let comments = parser.collect_preceding_comments(); + parser.comments_store.create_comment_reference(comments) + }; + let end_loc = parser.assert_and_consume_operator(TokenOp::RBRACE); + loc = loc.union(&end_loc); InterfaceDeclarationCommon { loc, associated_comments: parser.comments_store.create_comment_reference(associated_comments), @@ -467,44 +458,46 @@ mod toplevel_parser { type_parameters, extends_or_implements_nodes, type_definition, - members, + members: InterfaceMembersCommon { + loc: members_start_loc.union(&end_loc), + members, + ending_associated_comments, + }, } } pub(super) fn parse_interface(parser: &mut super::SourceParser) -> InterfaceDeclaration { - let associated_comments = parser.collect_preceding_comments(); + let mut associated_comments = vec![]; let (mut loc, private) = if let Token(loc, TokenContent::Keyword(Keyword::PRIVATE)) = parser.peek() { + associated_comments.append(&mut parser.collect_preceding_comments()); parser.consume(); + associated_comments.append(&mut parser.collect_preceding_comments()); parser.assert_and_consume_keyword(Keyword::INTERFACE); (loc, true) } else { + associated_comments.append(&mut parser.collect_preceding_comments()); (parser.assert_and_consume_keyword(Keyword::INTERFACE), false) }; let name = parser.parse_upper_id(); parser.available_tparams = HashSet::new(); let type_parameters = super::type_parser::parse_type_parameters(parser); - let extends_or_implements_nodes = - if let TokenContent::Operator(TokenOp::COLON) = parser.peek().1 { - parser.consume(); - let nodes = parse_extends_or_implements_nodes(parser); - loc = loc.union(&nodes.last().unwrap().location); - nodes - } else { - vec![] - }; + let extends_or_implements_nodes = parse_extends_or_implements_nodes(parser); let mut members = vec![]; - if let TokenContent::Operator(TokenOp::LBRACE) = parser.peek().1 { - parser.consume(); - while let TokenContent::Keyword(Keyword::FUNCTION | Keyword::METHOD | Keyword::PRIVATE) = - parser.peek().1 - { - let saved_upper_type_parameters = parser.available_tparams.clone(); - members.push(parse_class_member_declaration(parser)); - parser.available_tparams = saved_upper_type_parameters; - } - loc = loc.union(&parser.assert_and_consume_operator(TokenOp::RBRACE)); + let members_start_loc = parser.assert_and_consume_operator(TokenOp::LBRACE); + while let TokenContent::Keyword(Keyword::FUNCTION | Keyword::METHOD | Keyword::PRIVATE) = + parser.peek().1 + { + let saved_upper_type_parameters = parser.available_tparams.clone(); + members.push(parse_class_member_declaration(parser)); + parser.available_tparams = saved_upper_type_parameters; } + let ending_associated_comments = { + let comments = parser.collect_preceding_comments(); + parser.comments_store.create_comment_reference(comments) + }; + let end_loc = parser.assert_and_consume_operator(TokenOp::RBRACE); + loc = loc.union(&end_loc); InterfaceDeclarationCommon { loc, associated_comments: parser.comments_store.create_comment_reference(associated_comments), @@ -513,28 +506,54 @@ mod toplevel_parser { type_parameters, extends_or_implements_nodes, type_definition: (), - members, + members: InterfaceMembersCommon { + loc: members_start_loc.union(&end_loc), + members, + ending_associated_comments, + }, } } - fn parse_extends_or_implements_nodes(parser: &mut super::SourceParser) -> Vec { - let id = parser.parse_upper_id(); - let mut collector = vec![super::type_parser::parse_identifier_annot(parser, id)]; - while let Token(_, TokenContent::Operator(TokenOp::COMMA)) = parser.peek() { - parser.consume(); + fn parse_extends_or_implements_nodes( + parser: &mut super::SourceParser, + ) -> Option { + if let TokenContent::Operator(TokenOp::COLON) = parser.peek().1 { + let comments = parser.collect_preceding_comments(); + let mut location = parser.assert_and_consume_operator(TokenOp::COLON); let id = parser.parse_upper_id(); - collector.push(super::type_parser::parse_identifier_annot(parser, id)); + let mut nodes = vec![super::type_parser::parse_identifier_annot(parser, id)]; + while let Token(_, TokenContent::Operator(TokenOp::COMMA)) = parser.peek() { + let comments = parser.collect_preceding_comments(); + parser.consume(); + let id = parser.parse_upper_id_with_comments(comments); + nodes.push(super::type_parser::parse_identifier_annot(parser, id)); + } + location = location.union(&nodes.last().unwrap().location); + Some(ExtendsOrImplementsNodes { + location, + associated_comments: parser.comments_store.create_comment_reference(comments), + nodes, + }) + } else { + None } - collector } fn parse_type_definition_inner(parser: &mut super::SourceParser) -> TypeDefinition { + let start_comments = parser.collect_preceding_comments(); + let loc_start = parser.assert_and_consume_operator(TokenOp::LPAREN); + let end_comments = parser.collect_preceding_comments(); if let Token(_, TokenContent::UpperId(_)) = parser.peek() { let mut variants = parser .parse_comma_separated_list_with_end_token(TokenOp::RPAREN, &mut parse_variant_definition); variants.truncate(MAX_VARIANT_SIZE); - // Location is later patched by the caller - TypeDefinition::Enum { loc: Location::dummy(), variants } + let loc_end = parser.assert_and_consume_operator(TokenOp::RPAREN); + TypeDefinition::Enum { + loc: loc_start.union(&loc_end), + start_associated_comments: parser.comments_store.create_comment_reference(start_comments), + ending_associated_comments: parser.comments_store.create_comment_reference(end_comments), + variants, + } } else { let mut fields = parser .parse_comma_separated_list_with_end_token(TokenOp::RPAREN, &mut parse_field_definition); @@ -545,52 +564,63 @@ mod toplevel_parser { ); } fields.truncate(MAX_STRUCT_SIZE); - // Location is later patched by the caller - TypeDefinition::Struct { loc: Location::dummy(), fields } + let loc_end = parser.assert_and_consume_operator(TokenOp::RPAREN); + TypeDefinition::Struct { + loc: loc_start.union(&loc_end), + start_associated_comments: parser.comments_store.create_comment_reference(start_comments), + ending_associated_comments: parser.comments_store.create_comment_reference(end_comments), + fields, + } } } fn parse_field_definition(parser: &mut super::SourceParser) -> FieldDefinition { let mut is_public = true; + let mut comments = vec![]; if let TokenContent::Keyword(Keyword::PRIVATE) = parser.peek().1 { is_public = false; + comments.append(&mut parser.collect_preceding_comments()); parser.consume(); } + comments.append(&mut parser.collect_preceding_comments()); parser.assert_and_consume_keyword(Keyword::VAL); - let name = parser.parse_lower_id(); + let name = parser.parse_lower_id_with_comments(comments); let annotation = super::type_parser::parse_annotation_with_colon(parser); FieldDefinition { name, annotation, is_public } } fn parse_variant_definition(parser: &mut super::SourceParser) -> VariantDefinition { let name = parser.parse_upper_id(); - if let Token(_, TokenContent::Operator(TokenOp::LPAREN)) = parser.peek() { + if let Token(left_paren_loc, TokenContent::Operator(TokenOp::LPAREN)) = parser.peek() { + let start_comments = parser.collect_preceding_comments(); parser.consume(); - let associated_data_types = parser.parse_comma_separated_list_with_end_token( + let annotations = parser.parse_comma_separated_list_with_end_token( TokenOp::RPAREN, &mut super::type_parser::parse_annotation, ); - if let Some(node) = associated_data_types.get(MAX_VARIANT_SIZE) { + if let Some(node) = annotations.get(MAX_VARIANT_SIZE) { parser.error_set.report_invalid_syntax_error( node.location(), format!("Maximum allowed field size is {MAX_VARIANT_SIZE}"), ); } - parser.assert_and_consume_operator(TokenOp::RPAREN); - VariantDefinition { name, associated_data_types } + let end_comments = parser.collect_preceding_comments(); + let right_paren_loc = parser.assert_and_consume_operator(TokenOp::RPAREN); + VariantDefinition { + name, + associated_data_types: Some(annotation::ParenthesizedAnnotationList { + location: left_paren_loc.union(&right_paren_loc), + start_associated_comments: parser.comments_store.create_comment_reference(start_comments), + ending_associated_comments: parser.comments_store.create_comment_reference(end_comments), + annotations, + }), + } } else { - VariantDefinition { name, associated_data_types: vec![] } + VariantDefinition { name, associated_data_types: None } } } - fn peeked_class_or_interface_start(parser: &mut super::SourceParser) -> bool { - matches!( - parser.peek().1, - TokenContent::Keyword(Keyword::CLASS | Keyword::INTERFACE | Keyword::PRIVATE) - ) - } - pub(super) fn parse_class_member_definition( parser: &mut super::SourceParser, ) -> ClassMemberDefinition<()> { @@ -1855,10 +1885,11 @@ mod type_parser { let location = peeked.0.union(&parser.assert_and_consume_operator(TokenOp::RPAREN)); comments.append(&mut parser.collect_preceding_comments()); parser.assert_and_consume_operator(TokenOp::ARROW); - annotation::FunctionParameters { + annotation::ParenthesizedAnnotationList { location, + start_associated_comments: NO_COMMENT_REFERENCE, ending_associated_comments: parser.comments_store.create_comment_reference(comments), - parameters: Vec::with_capacity(0), + annotations: Vec::with_capacity(0), } } else { let parameters = parser @@ -1867,10 +1898,11 @@ mod type_parser { let location = peeked.0.union(&parser.assert_and_consume_operator(TokenOp::RPAREN)); comments.append(&mut parser.collect_preceding_comments()); parser.assert_and_consume_operator(TokenOp::ARROW); - annotation::FunctionParameters { + annotation::ParenthesizedAnnotationList { location, + start_associated_comments: NO_COMMENT_REFERENCE, ending_associated_comments: parser.comments_store.create_comment_reference(comments), - parameters, + annotations: parameters, } }; let return_type = parse_annotation(parser); @@ -1924,7 +1956,7 @@ mod type_parser { } } annotation::T::Fn(t) => { - for annot in &mut t.parameters.parameters { + for annot in &mut t.parameters.annotations { fix_annot_with_generic_annot(parser, annot); } fix_annot_with_generic_annot(parser, &mut t.return_type); diff --git a/crates/samlang-printer/src/source_printer.rs b/crates/samlang-printer/src/source_printer.rs index 5ff475a7..9dc3afe7 100644 --- a/crates/samlang-printer/src/source_printer.rs +++ b/crates/samlang-printer/src/source_printer.rs @@ -2,8 +2,8 @@ use super::prettier::{rc_string, rcs, Document, Str}; use itertools::Itertools; use samlang_ast::source::{ annotation, expr, pattern, ClassDefinition, ClassMemberDeclaration, CommentKind, - CommentReference, CommentStore, Id, InterfaceDeclaration, Module, Toplevel, TypeDefinition, - NO_COMMENT_REFERENCE, + CommentReference, CommentStore, ExtendsOrImplementsNodes, Id, InterfaceDeclaration, Module, + Toplevel, TypeDefinition, NO_COMMENT_REFERENCE, }; use samlang_heap::{Heap, ModuleReference, PStr}; use std::{collections::HashMap, ops::Deref, rc::Rc}; @@ -162,7 +162,7 @@ pub(super) fn annotation_to_doc( parenthesis_surrounded_doc(comma_sep_list( heap, comment_store, - ¶meters.parameters, + ¶meters.annotations, parameters.ending_associated_comments, |annot| annotation_to_doc(heap, comment_store, annot), )), @@ -964,13 +964,11 @@ fn create_doc_for_interface_member( fn extends_or_implements_node_to_doc( heap: &Heap, comment_store: &CommentStore, - nodes: &Vec, + nodes: Option<&ExtendsOrImplementsNodes>, ) -> Document { - if nodes.is_empty() { - Document::Nil - } else { + if let Some(nodes) = nodes { let mut expanded_ids = vec![Document::Line]; - for id in nodes { + for id in &nodes.nodes { expanded_ids.push(id_annot_to_doc(heap, comment_store, id)); expanded_ids.push(Document::Text(rcs(","))); expanded_ids.push(Document::Line); @@ -981,7 +979,14 @@ fn extends_or_implements_node_to_doc( Rc::new(Document::Text(rcs(" :"))), Rc::new(Document::Nest(2, Rc::new(Document::concat(expanded_ids)))), ); - Document::group(expanded) + create_opt_preceding_comment_doc( + heap, + comment_store, + nodes.associated_comments, + Document::group(expanded), + ) + } else { + Document::Nil } } @@ -1005,11 +1010,15 @@ fn interface_to_doc( interface.name.name.as_str(heap) ))), type_parameters_to_doc(heap, comment_store, false, interface.type_parameters.as_ref()), - extends_or_implements_node_to_doc(heap, comment_store, &interface.extends_or_implements_nodes), + extends_or_implements_node_to_doc( + heap, + comment_store, + interface.extends_or_implements_nodes.as_ref(), + ), ]; documents.push(Document::Text(rcs(" {"))); - for member in &interface.members { + for member in &interface.members.members { documents.push(Document::Nest( 2, Rc::new(Document::concat( @@ -1021,6 +1030,16 @@ fn interface_to_doc( )); documents.push(Document::Line); } + if let Some(comments) = associated_comments_doc( + heap, + comment_store, + interface.members.ending_associated_comments, + DocumentGrouping::Expanded, + true, + ) { + documents.push(Document::Nest(2, Rc::new(Document::concat(vec![Document::Line, comments])))); + documents.push(Document::Line); + }; documents.push(Document::Text(rcs("}"))); documents @@ -1046,52 +1065,81 @@ fn class_to_doc( class.name.name.as_str(heap) ))), type_parameters_to_doc(heap, comment_store, false, class.type_parameters.as_ref()), - match &class.type_definition { - TypeDefinition::Struct { loc: _, fields } if fields.is_empty() => Document::Nil, - TypeDefinition::Struct { loc: _, fields } => parenthesis_surrounded_doc(comma_sep_list( - heap, - comment_store, + match class.type_definition.as_ref() { + None => Document::Nil, + Some(TypeDefinition::Struct { + loc: _, + start_associated_comments, + ending_associated_comments, fields, - NO_COMMENT_REFERENCE, - |field| { - Document::Concat( - Rc::new(Document::Text(rc_string(format!( - "{}val {}: ", - if field.is_public { "" } else { "private " }, - field.name.name.as_str(heap) - )))), - Rc::new(annotation_to_doc(heap, comment_store, &field.annotation)), - ) - }, - )), - TypeDefinition::Enum { loc: _, variants } => parenthesis_surrounded_doc(comma_sep_list( + }) => create_opt_preceding_comment_doc( heap, comment_store, + *start_associated_comments, + parenthesis_surrounded_doc(comma_sep_list( + heap, + comment_store, + fields, + *ending_associated_comments, + |field| { + Document::Concat( + Rc::new(Document::Text(rc_string(format!( + "{}val {}: ", + if field.is_public { "" } else { "private " }, + field.name.name.as_str(heap) + )))), + Rc::new(annotation_to_doc(heap, comment_store, &field.annotation)), + ) + }, + )), + ), + Some(TypeDefinition::Enum { + loc: _, + start_associated_comments, + ending_associated_comments, variants, - NO_COMMENT_REFERENCE, - |variant| { - if variant.associated_data_types.is_empty() { - Document::Text(rc_pstr(heap, variant.name.name)) - } else { - Document::concat(vec![ - Document::Text(rc_pstr(heap, variant.name.name)), - parenthesis_surrounded_doc(comma_sep_list( - heap, - comment_store, - &variant.associated_data_types, - NO_COMMENT_REFERENCE, - |annot| annotation_to_doc(heap, comment_store, annot), - )), - ]) - } - }, - )), + }) => create_opt_preceding_comment_doc( + heap, + comment_store, + *start_associated_comments, + parenthesis_surrounded_doc(comma_sep_list( + heap, + comment_store, + variants, + *ending_associated_comments, + |variant| { + if let Some(annotations) = &variant.associated_data_types { + Document::concat(vec![ + Document::Text(rc_pstr(heap, variant.name.name)), + create_opt_preceding_comment_doc( + heap, + comment_store, + annotations.start_associated_comments, + parenthesis_surrounded_doc(comma_sep_list( + heap, + comment_store, + &annotations.annotations, + annotations.ending_associated_comments, + |annot| annotation_to_doc(heap, comment_store, annot), + )), + ), + ]) + } else { + Document::Text(rc_pstr(heap, variant.name.name)) + } + }, + )), + ), }, - extends_or_implements_node_to_doc(heap, comment_store, &class.extends_or_implements_nodes), + extends_or_implements_node_to_doc( + heap, + comment_store, + class.extends_or_implements_nodes.as_ref(), + ), ]; documents.push(Document::Text(rcs(" {"))); - for member in &class.members { + for member in &class.members.members { documents.push(Document::Nest( 2, Rc::new(Document::concat( @@ -1108,6 +1156,16 @@ fn class_to_doc( )); documents.push(Document::Line); } + if let Some(comments) = associated_comments_doc( + heap, + comment_store, + class.members.ending_associated_comments, + DocumentGrouping::Expanded, + true, + ) { + documents.push(Document::Nest(2, Rc::new(Document::concat(vec![Document::Line, comments])))); + documents.push(Document::Line); + }; documents.push(Document::Text(rcs("}"))); documents @@ -1231,7 +1289,7 @@ mod tests { match n { Toplevel::Interface(_) => {} Toplevel::Class(c) => { - for member in &c.members { + for member in &c.members.members { for p in member.decl.parameters.parameters.as_ref().iter() { pretty_print_annotation(&heap, 40, &m.comment_store, &p.annotation); } @@ -1599,17 +1657,22 @@ ClassName /* b */ /* c */.classMember< assert_reprint_module( r#" interface Foo {} -interface Foo2 : Foo {} +interface Foo2 : Foo { + + // f +} private interface Bar { function baz(): int } -class Empty -class Empty2 : Foo +class Empty {} +class Empty2 : Foo {} class Main { function main(): unit = {} } "#, r#" interface Foo {} -interface Foo2 : Foo {} +interface Foo2 : Foo { + // f +} private interface Bar< /* comment */ @@ -1655,6 +1718,8 @@ class Obj(private val d: int, val e: int) { let b: int = 2; /* a */ // sss } + + // f } /** short line */ @@ -1750,6 +1815,8 @@ class Obj( /* a */ // sss } + + // f } /** short line */ diff --git a/crates/samlang-services/src/api_tests.rs b/crates/samlang-services/src/api_tests.rs index 64ed864b..e9031988 100644 --- a/crates/samlang-services/src/api_tests.rs +++ b/crates/samlang-services/src/api_tests.rs @@ -1027,7 +1027,7 @@ Str [kind=Class, detail=class Str] .join("\n") ); assert_eq!( - "init [kind=Function, detail=init(): Developer]", + "", completion::auto_complete(&state, &mod_ref, Position(3, 42)) .iter() .map(completion::AutoCompletionItem::to_string) diff --git a/crates/samlang-services/src/ast_differ.rs b/crates/samlang-services/src/ast_differ.rs index 518eafba..6fef9f85 100644 --- a/crates/samlang-services/src/ast_differ.rs +++ b/crates/samlang-services/src/ast_differ.rs @@ -430,7 +430,9 @@ pub(super) fn compute_module_diff_edits( mod tests { use super::*; use pretty_assertions::assert_eq; - use samlang_ast::source::{test_builder, Id, InterfaceDeclarationCommon, NO_COMMENT_REFERENCE}; + use samlang_ast::source::{ + test_builder, Id, InterfaceDeclarationCommon, InterfaceMembersCommon, NO_COMMENT_REFERENCE, + }; use samlang_errors::ErrorSet; use samlang_heap::PStr; @@ -531,9 +533,13 @@ mod tests { private: false, name: Id::from(PStr::UPPER_A), type_parameters: None, - extends_or_implements_nodes: vec![], + extends_or_implements_nodes: None, type_definition: (), - members: vec![], + members: InterfaceMembersCommon { + loc: Location::dummy(), + members: vec![], + ending_associated_comments: NO_COMMENT_REFERENCE, + }, }); assert_eq!( "interface A {}", diff --git a/crates/samlang-services/src/gc.rs b/crates/samlang-services/src/gc.rs index cbfe041f..c1615198 100644 --- a/crates/samlang-services/src/gc.rs +++ b/crates/samlang-services/src/gc.rs @@ -26,7 +26,7 @@ fn mark_id_annot(heap: &mut Heap, annot: &annotation::Id) { } fn mark_fn_annot(heap: &mut Heap, annot: &annotation::Function) { - mark_annotations(heap, &annot.parameters.parameters); + mark_annotations(heap, &annot.parameters.annotations); mark_annot(heap, &annot.return_type); } @@ -206,7 +206,7 @@ fn mark_module(heap: &mut Heap, module: &Module>) { for toplevel in &module.toplevels { mark_id(heap, toplevel.name()); mark_type_parameters(heap, toplevel.type_parameters()); - for t in toplevel.extends_or_implements_nodes() { + for t in toplevel.extends_or_implements_nodes().iter().flat_map(|it| &it.nodes) { mark_id_annot(heap, t); } for m in toplevel.members_iter() { @@ -215,23 +215,34 @@ fn mark_module(heap: &mut Heap, module: &Module>) { mark_annot(heap, &m.return_type); } if let Toplevel::Class(c) = toplevel { - match &c.type_definition { - TypeDefinition::Struct { loc: _, fields } => { + match c.type_definition.as_ref() { + Some(TypeDefinition::Struct { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + fields, + }) => { for field in fields { mark_id(heap, &field.name); mark_annot(heap, &field.annotation); } } - TypeDefinition::Enum { loc: _, variants } => { + Some(TypeDefinition::Enum { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + variants, + }) => { for variant in variants { mark_id(heap, &variant.name); - for annot in &variant.associated_data_types { + for annot in variant.associated_data_types.iter().flat_map(|it| &it.annotations) { mark_annot(heap, annot); } } } + None => {} } - for m in &c.members { + for m in &c.members.members { mark_expression(heap, &m.body); } } diff --git a/crates/samlang-services/src/global_searcher.rs b/crates/samlang-services/src/global_searcher.rs index a948f409..5317a18d 100644 --- a/crates/samlang-services/src/global_searcher.rs +++ b/crates/samlang-services/src/global_searcher.rs @@ -21,7 +21,7 @@ fn search_annot( annotation::T::Primitive(_, _, _) | annotation::T::Generic(_, _) => {} annotation::T::Id(annot) => search_id_annot(annot, request, collector), annotation::T::Fn(annot) => { - for a in &annot.parameters.parameters { + for a in &annot.parameters.annotations { search_annot(a, request, collector); } search_annot(&annot.return_type, request, collector); @@ -231,7 +231,12 @@ pub(super) fn search_modules_globally( if let Toplevel::Class(c) = toplevel { match (&c.type_definition, request) { ( - TypeDefinition::Struct { loc: _, fields }, + Some(TypeDefinition::Struct { + loc: _, + start_associated_comments: _, + ending_associated_comments: _, + fields, + }), GlobalNameSearchRequest::Property(req_mod_ref, req_toplevel_name, req_prop), ) if mod_ref.eq(req_mod_ref) && toplevel_name.name.eq(req_toplevel_name) => { for field in fields { @@ -242,7 +247,7 @@ pub(super) fn search_modules_globally( } _ => {} } - for member in &c.members { + for member in &c.members.members { search_expression(&member.body, request, &mut collector); } } @@ -304,7 +309,7 @@ mod tests { } } - interface Interface + interface Interface {} class Main { function identity(a: int): int = a diff --git a/crates/samlang-services/src/lib.rs b/crates/samlang-services/src/lib.rs index 988a94e3..5cd2d001 100644 --- a/crates/samlang-services/src/lib.rs +++ b/crates/samlang-services/src/lib.rs @@ -84,7 +84,7 @@ mod state_searcher_utils { ) -> Option<&'a FieldDefinition> { find_type_def(state, module_reference, class_name) .and_then(|it| it.as_struct()) - .and_then(|(_, fields)| fields.iter().find(|it| it.name.name.eq(field_name))) + .and_then(|(_, _, _, fields)| fields.iter().find(|it| it.name.name.eq(field_name))) } pub(super) fn find_class_member<'a>( diff --git a/crates/samlang-services/src/location_cover.rs b/crates/samlang-services/src/location_cover.rs index c1b41a08..6d9fdd4b 100644 --- a/crates/samlang-services/src/location_cover.rs +++ b/crates/samlang-services/src/location_cover.rs @@ -245,7 +245,7 @@ pub(super) fn search_module_locally( } } if let Toplevel::Class(c) = toplevel { - for member in &c.members { + for member in &c.members.members { if !member.decl.loc.contains_position(position) { continue; } @@ -345,7 +345,7 @@ mod tests { } } - interface Interface + interface Interface {} class Main { function identity(a: int): int = a diff --git a/crates/samlang-services/src/variable_definition.rs b/crates/samlang-services/src/variable_definition.rs index 6c3a3f34..d7d68d8c 100644 --- a/crates/samlang-services/src/variable_definition.rs +++ b/crates/samlang-services/src/variable_definition.rs @@ -1,7 +1,7 @@ use samlang_ast::{ source::{ expr, pattern, AnnotatedId, ClassDefinition, ClassMemberDeclaration, ClassMemberDefinition, Id, - Module, OptionallyAnnotatedId, Toplevel, + InterfaceMembersCommon, Module, OptionallyAnnotatedId, Toplevel, }, Location, }; @@ -352,55 +352,60 @@ pub(super) fn apply_renaming( type_parameters: c.type_parameters.clone(), extends_or_implements_nodes: c.extends_or_implements_nodes.clone(), type_definition: c.type_definition.clone(), - members: c - .members - .iter() - .map( - |ClassMemberDefinition { - decl: - ClassMemberDeclaration { - loc, - associated_comments, - is_public, - is_method, - name, - type_parameters, - return_type, - parameters, - }, - body, - }| { - ClassMemberDefinition { - decl: ClassMemberDeclaration { - loc: *loc, - associated_comments: *associated_comments, - is_public: *is_public, - is_method: *is_method, - name: *name, - type_parameters: type_parameters.clone(), - parameters: samlang_ast::source::FunctionParameters { - location: parameters.location, - start_associated_comments: parameters.start_associated_comments, - ending_associated_comments: parameters.ending_associated_comments, - parameters: Rc::new( - parameters - .parameters - .iter() - .map(|AnnotatedId { name, type_, annotation }| AnnotatedId { - name: mod_def_id(name, definition_and_uses, new_name), - type_: *type_, - annotation: annotation.clone(), - }) - .collect(), - ), + members: InterfaceMembersCommon { + loc: c.members.loc, + members: c + .members + .members + .iter() + .map( + |ClassMemberDefinition { + decl: + ClassMemberDeclaration { + loc, + associated_comments, + is_public, + is_method, + name, + type_parameters, + return_type, + parameters, + }, + body, + }| { + ClassMemberDefinition { + decl: ClassMemberDeclaration { + loc: *loc, + associated_comments: *associated_comments, + is_public: *is_public, + is_method: *is_method, + name: *name, + type_parameters: type_parameters.clone(), + parameters: samlang_ast::source::FunctionParameters { + location: parameters.location, + start_associated_comments: parameters.start_associated_comments, + ending_associated_comments: parameters.ending_associated_comments, + parameters: Rc::new( + parameters + .parameters + .iter() + .map(|AnnotatedId { name, type_, annotation }| AnnotatedId { + name: mod_def_id(name, definition_and_uses, new_name), + type_: *type_, + annotation: annotation.clone(), + }) + .collect(), + ), + }, + return_type: return_type.clone(), }, - return_type: return_type.clone(), - }, - body: apply_expr_renaming(body, definition_and_uses, new_name), - } - }, - ) - .collect(), + body: apply_expr_renaming(body, definition_and_uses, new_name), + } + }, + ) + .collect(), + ending_associated_comments: c.members.ending_associated_comments, + }, }), }) .collect(),