11use std:: fmt:: Write ;
22
33use crate :: {
4- DbIndex , LuaType , RenderLevel , TypeMismatch , TypeMismatchKind , TypePathInfo , TypePathSegment ,
5- humanize_type,
4+ DbIndex , LuaMemberKey , LuaType , RenderLevel , TypeMismatch , TypeMismatchKind , TypePathInfo ,
5+ TypePathSegment , humanize_type,
66} ;
77
8+ use super :: humanize_lint_type;
9+
810pub fn render_diagnostic_detail (
911 db : & DbIndex ,
1012 mismatch : & TypeMismatch ,
@@ -52,9 +54,11 @@ fn render_type_mismatch_reason<'a>(
5254 & mut last_relation,
5355 ) ,
5456 TypeMismatchKind :: Message ( message) => push_text_line ( & mut output, & mut depth, message) ,
55- TypeMismatchKind :: MissingMember { key } => {
56- start_line ( & mut output, depth) ;
57- let _ = write ! ( output, "Property '{}' is missing." , key. to_path( ) ) ;
57+ TypeMismatchKind :: MissingMembers { keys } => {
58+ let ( source, target) = last_relation. unwrap_or ( ( root_source, root_target) ) ;
59+ if let Some ( text) = format_missing_fields ( db, source, target, keys) {
60+ push_text_line ( & mut output, & mut depth, & text) ;
61+ }
5862 }
5963 TypeMismatchKind :: MissingTupleElement { index } => {
6064 start_line ( & mut output, depth) ;
@@ -65,6 +69,61 @@ fn render_type_mismatch_reason<'a>(
6569 ( !output. is_empty ( ) ) . then_some ( output)
6670}
6771
72+ pub fn format_missing_fields (
73+ db : & DbIndex ,
74+ source : & LuaType ,
75+ target : & LuaType ,
76+ keys : & [ LuaMemberKey ] ,
77+ ) -> Option < String > {
78+ let mut names = keys
79+ . iter ( )
80+ . filter_map ( member_key_to_field_name)
81+ . collect :: < Vec < _ > > ( ) ;
82+ names. sort_unstable ( ) ;
83+ names. dedup ( ) ;
84+ let first = names. first ( ) ?;
85+
86+ if names. len ( ) == 1 {
87+ return Some (
88+ t ! (
89+ "Type `%{source}` is missing the `%{field}` field from type `%{target}`." ,
90+ source = humanize_lint_type( db, source) ,
91+ field = first. clone( ) ,
92+ target = humanize_lint_type( db, target) ,
93+ )
94+ . to_string ( ) ,
95+ ) ;
96+ }
97+
98+ let total_count = names. len ( ) ;
99+ let mut fields = names. into_iter ( ) . take ( 4 ) . collect :: < Vec < _ > > ( ) . join ( ", " ) ;
100+ if total_count > 4 {
101+ let more_count = total_count - 4 ;
102+ fields. push_str ( & format ! (
103+ " {}" ,
104+ t!( "and %{count} more." , count = more_count)
105+ ) ) ;
106+ }
107+
108+ Some (
109+ t ! (
110+ "Type `%{source}` is missing the following fields from type `%{target}`: %{fields}" ,
111+ source = humanize_lint_type( db, source) ,
112+ target = humanize_lint_type( db, target) ,
113+ fields = fields,
114+ )
115+ . to_string ( ) ,
116+ )
117+ }
118+
119+ fn member_key_to_field_name ( key : & LuaMemberKey ) -> Option < String > {
120+ match key {
121+ LuaMemberKey :: Name ( name) => Some ( name. to_string ( ) ) ,
122+ LuaMemberKey :: Integer ( index) => Some ( format ! ( "[{}]" , index) ) ,
123+ LuaMemberKey :: None | LuaMemberKey :: TypeKey ( _) => None ,
124+ }
125+ }
126+
68127fn render_path_title (
69128 output : & mut String ,
70129 depth : & mut usize ,
0 commit comments