@@ -3,7 +3,15 @@ pub struct Scope {
33 pub ancestors : AncestorTracker ,
44}
55
6- static SCOPE_BOUNDARIES : & [ & str ] = & [ "statement" , "ERROR" , "program" ] ;
6+ static SCOPE_BOUNDARIES : & [ & str ] = & [
7+ "statement" ,
8+ "ERROR" ,
9+ "program" ,
10+ "block" ,
11+ "transaction" ,
12+ "psql_meta_command" ,
13+ "copy_data_stream" ,
14+ ] ;
715
816#[ derive( Debug ) ]
917pub struct ScopeTracker {
@@ -22,7 +30,7 @@ impl ScopeTracker {
2230
2331 self . scopes
2432 . last_mut ( )
25- . unwrap_or_else ( || panic ! ( "Unhandled node kind: {}" , node . kind ( ) ) )
33+ . unwrap_or_else ( || panic ! ( "No top-level grammar-rule found. Please create an issue with the entire Postgres file, noting cursor/hover position." ) )
2634 . ancestors
2735 . register ( node, position) ;
2836 }
@@ -105,3 +113,104 @@ impl AncestorTracker {
105113 true
106114 }
107115}
116+
117+ #[ cfg( test) ]
118+ mod tests {
119+ use crate :: context:: { TreeSitterContextParams , TreesitterContext } ;
120+
121+ fn get_tree ( input : & str ) -> tree_sitter:: Tree {
122+ let mut parser = tree_sitter:: Parser :: new ( ) ;
123+ parser
124+ . set_language ( & pgls_treesitter_grammar:: LANGUAGE . into ( ) )
125+ . expect ( "Couldn't set language" ) ;
126+
127+ parser. parse ( input, None ) . expect ( "Unable to parse tree" )
128+ }
129+
130+ fn assert_no_panic_for_all_positions ( sql : & str ) {
131+ let tree = get_tree ( sql) ;
132+ for pos in 0 ..sql. len ( ) {
133+ let params = TreeSitterContextParams {
134+ position : ( pos as u32 ) . into ( ) ,
135+ text : sql,
136+ tree : & tree,
137+ } ;
138+ let _ = TreesitterContext :: new ( params) ;
139+ }
140+ }
141+
142+ #[ test]
143+ fn scope_boundary_block ( ) {
144+ assert_no_panic_for_all_positions ( "BEGIN; SELECT 1; END;" ) ;
145+ }
146+
147+ #[ test]
148+ fn scope_boundary_transaction ( ) {
149+ assert_no_panic_for_all_positions ( "BEGIN TRANSACTION; SELECT 1; COMMIT;" ) ;
150+ assert_no_panic_for_all_positions ( "BEGIN; INSERT INTO t VALUES (1); ROLLBACK;" ) ;
151+ }
152+
153+ #[ test]
154+ fn scope_boundary_psql_meta_command ( ) {
155+ assert_no_panic_for_all_positions ( "\\ dt\n \\ d users" ) ;
156+ }
157+
158+ #[ test]
159+ fn scope_boundary_copy_data_stream ( ) {
160+ assert_no_panic_for_all_positions ( "COPY t FROM STDIN;\n 1\t Alice\n \\ .\n " ) ;
161+ }
162+
163+ #[ test]
164+ fn scope_boundary_comment ( ) {
165+ assert_no_panic_for_all_positions ( "-- a comment\n SELECT 1;" ) ;
166+ }
167+
168+ #[ test]
169+ fn issue_704_regression ( ) {
170+ let statements = vec ! [
171+ r#"
172+ CREATE OR REPLACE FUNCTION my_schema.my_function1(
173+ pi_1 character varying,
174+ pi_2 character varying,
175+ pi_3 jsonb,
176+ OUT po_1 integer,
177+ OUT po_2 integer,
178+ OUT result integer
179+ )
180+ RETURNS record
181+ LANGUAGE plpgsql
182+ AS $function$
183+ "#
184+ . trim( ) ,
185+
186+ r#"
187+ CREATE OR REPLACE FUNCTION my_schema.my_function2(
188+ pi_1 character varying,
189+ pi_2 character varying,
190+ pi_3 jsonb,
191+ OUT po_1 integer,
192+ OUT po_2 integer,
193+ OUT result integer
194+ )
195+ RETURNS record
196+ LANGUAGE plpgsql
197+ AS $function$
198+ DECLARE
199+ BEGIN
200+ -- Function logic goes here
201+ -- For example, you can perform some operations using the input parameters and set the output parameters accordingly
202+
203+ -- Example logic (replace with actual implementation):
204+ po_1 := length(pi_1); -- Set po_1 to the length of pi_1
205+ po_2 := length(pi_2); -- Set po_2 to the length of pi_2
206+ result := po_1 + po_2; -- Set result to the sum of po_1 and po_2
207+ END;
208+ $function$;
209+ "# . trim( ) ,
210+ ] ;
211+
212+ for stmt in statements {
213+ assert_no_panic_for_all_positions ( stmt) ;
214+ }
215+ }
216+ }
0 commit comments