From e5d1b75544001ecd6737df0484d2edcf1e2c5372 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Fri, 29 May 2026 23:03:05 -0400 Subject: [PATCH 1/3] ide: fix cte & alias resolution --- crates/squawk_ide/src/ast_nav.rs | 28 +++++------- crates/squawk_ide/src/goto_definition.rs | 54 ++++++++++++++++++++++++ crates/squawk_ide/src/resolve.rs | 20 ++++----- 3 files changed, 74 insertions(+), 28 deletions(-) diff --git a/crates/squawk_ide/src/ast_nav.rs b/crates/squawk_ide/src/ast_nav.rs index b01d97ab..37511728 100644 --- a/crates/squawk_ide/src/ast_nav.rs +++ b/crates/squawk_ide/src/ast_nav.rs @@ -18,24 +18,18 @@ pub(crate) fn find_cte_with_table( .ancestors() .find_map(|query| ast::WithQuery::cast(query)?.with_clause())?; + let ref_start = name_ref.syntax().text_range().start(); let is_recursive = with_clause.recursive_token().is_some(); - for with_table in with_clause.with_tables() { - if let Some(name) = with_table.name() - && Name::from_node(&name) == *cte_name - { - // Skip if we're inside this CTE's definition (CTE doesn't shadow itself) - if !is_recursive - && with_table - .syntax() - .text_range() - .contains_range(name_ref.syntax().text_range()) - { - continue; - } - return Some(with_table); - } - } - None + + with_clause + .with_tables() + // Without RECURSIVE, only CTEs before the reference are visible. + .filter(|with_table| is_recursive || with_table.syntax().text_range().end() <= ref_start) + .find(|with_table| { + with_table + .name() + .is_some_and(|name| Name::from_node(&name) == *cte_name) + }) } pub(crate) fn iter_values_columns(values: &ast::Values) -> impl Iterator { diff --git a/crates/squawk_ide/src/goto_definition.rs b/crates/squawk_ide/src/goto_definition.rs index 705f9e6d..37c93b9c 100644 --- a/crates/squawk_ide/src/goto_definition.rs +++ b/crates/squawk_ide/src/goto_definition.rs @@ -694,6 +694,20 @@ from ); } + #[test] + fn goto_cte_forward_ref_not_found() { + // b is defined after a, so a can't reference it in a non-recursive WITH + // ERROR: relation "b" does not exist + goto_not_found( + " + with + a as (select * from b$0), + b as (select 1 x) + select * from a; +", + ); + } + #[test] fn goto_drop_sequence() { assert_snapshot!(goto(" @@ -4930,6 +4944,36 @@ select a from x$0; "); } + #[test] + fn goto_cte_shadows_table_in_from() { + assert_snapshot!(goto(" +create table x(a int); +with x as (select 1 a) +select a from x$0; +"), @" + ╭▸ + 3 │ with x as (select 1 a) + │ ─ 2. destination + 4 │ select a from x; + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_cte_shadows_view_column() { + assert_snapshot!(goto(" +create view x as select 1 a; +with x as (select 2 a) +select a$0 from x; +"), @" + ╭▸ + 3 │ with x as (select 2 a) + │ ─ 2. destination + 4 │ select a from x; + ╰╴ ─ 1. source + "); + } + #[test] fn goto_cte_column() { assert_snapshot!(goto(" @@ -7195,6 +7239,16 @@ select m.message$0 from users as u join messages as m on u.id = m.user_id; "); } + #[test] + fn goto_alias_hides_table_name() { + goto_not_found( + " +create table t(a int); +select t$0.a from t as u; +", + ); + } + #[test] fn goto_join_unqualified_column() { assert_snapshot!(goto(" diff --git a/crates/squawk_ide/src/resolve.rs b/crates/squawk_ide/src/resolve.rs index ce10bde5..0821c295 100644 --- a/crates/squawk_ide/src/resolve.rs +++ b/crates/squawk_ide/src/resolve.rs @@ -777,6 +777,13 @@ pub(crate) fn resolve_table_like( schemas: &ResolvedSchemas, file: File, ) -> Option<(SyntaxNodePtr, LocationKind)> { + if schemas.unqualified() + && let Some(name_ref) = name_ref + && let Some(cte_ptr) = resolve_cte_table(name_ref, table_name) + { + return Some((cte_ptr, LocationKind::Table)); + } + if let Some(view_name_ptr) = resolve_view_name_ptr(db, table_name, schemas, file) { return Some((view_name_ptr, LocationKind::View)); } @@ -785,13 +792,6 @@ pub(crate) fn resolve_table_like( return Some((table_name_ptr, LocationKind::Table)); } - if schemas.unqualified() - && let Some(name_ref) = name_ref - && let Some(cte_ptr) = resolve_cte_table(name_ref, table_name) - { - return Some((cte_ptr, LocationKind::Table)); - } - None } @@ -1742,10 +1742,8 @@ fn resolve_fn_call_column( } fn is_from_item_match(from_item: &ast::FromItem, qualifier: &Name) -> bool { - if let Some(alias_name) = from_item.alias().and_then(|a| a.name()) - && Name::from_node(&alias_name) == *qualifier - { - return true; + if let Some(alias_name) = from_item.alias().and_then(|a| a.name()) { + return Name::from_node(&alias_name) == *qualifier; } if let Some((_schema, function_name)) = from_item From f0ff9b113fe029678f85b6d0506f7e8252b5a7a6 Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Fri, 29 May 2026 23:44:49 -0400 Subject: [PATCH 2/3] add some tests --- crates/squawk_ide/src/goto_definition.rs | 92 ++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/crates/squawk_ide/src/goto_definition.rs b/crates/squawk_ide/src/goto_definition.rs index 37c93b9c..265fa83b 100644 --- a/crates/squawk_ide/src/goto_definition.rs +++ b/crates/squawk_ide/src/goto_definition.rs @@ -708,6 +708,98 @@ from ); } + #[test] + fn goto_cte_forward_ref_ignored() { + assert_snapshot!(goto(" +create table b(c int); +with + a as (select c$0 from b), + b as (select 1 c) +select c from a; +"), @" + ╭▸ + 2 │ create table b(c int); + │ ─ 2. destination + 3 │ with + 4 │ a as (select c from b), + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_cte_forward_ref_ignored_inside_table_query() { + assert_snapshot!(goto(" +create table b(c int); +with + a as (table b), + b as (select 1 c) +select c$0 from a; +"), @" + ╭▸ + 2 │ create table b(c int); + │ ─ 2. destination + ‡ + 6 │ select c from a; + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_cte_forward_ref_ignored_inside_create_table_as_star() { + assert_snapshot!(goto(" +create table b(c int); +create table ct as + with + a as (select * from b), + b as (select 1 c) + select * from a; +select c$0 from ct; +"), @" + ╭▸ + 2 │ create table b(c int); + │ ─ 2. destination + ‡ + 8 │ select c from ct; + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_cte_forward_ref_ignored_inside_create_table_as_table() { + assert_snapshot!(goto(" +create table b(c int); +create table made as + with + a as (table b), + b as (select 1 c) + table a; +select c$0 from made; +"), @" + ╭▸ + 2 │ create table b(c int); + │ ─ 2. destination + ‡ + 8 │ select c from made; + ╰╴ ─ 1. source + "); + } + + #[test] + fn goto_cte_star_over_subquery_from_item() { + assert_snapshot!(goto(" +create table t(c int); +with + a as (select * from (select c from t)) +select c$0 from a; +"), @" + ╭▸ + 4 │ a as (select * from (select c from t)) + │ ─ 2. destination + 5 │ select c from a; + ╰╴ ─ 1. source + "); + } + #[test] fn goto_drop_sequence() { assert_snapshot!(goto(" From 251f48120fbf62f016e495f8e6d75a97bbd0053e Mon Sep 17 00:00:00 2001 From: Steve Dignam Date: Sat, 30 May 2026 00:01:35 -0400 Subject: [PATCH 3/3] make tests pass --- crates/squawk_ide/src/collect.rs | 126 ++++++++++++------------------ crates/squawk_ide/src/resolve.rs | 128 +++++++++++++++++++------------ 2 files changed, 132 insertions(+), 122 deletions(-) diff --git a/crates/squawk_ide/src/collect.rs b/crates/squawk_ide/src/collect.rs index 8df4f2f8..903ff6c6 100644 --- a/crates/squawk_ide/src/collect.rs +++ b/crates/squawk_ide/src/collect.rs @@ -280,45 +280,7 @@ fn select_columns_with_types( target_list_columns_with_types_in_file(db, file, &target_list, from_clause.as_ref()) } ast::SelectVariant::Values(values) => columns_from_values(values), - ast::SelectVariant::Table(table) => { - let Some(path) = table.relation_name().and_then(|r| r.path()) else { - return vec![]; - }; - let Some((schema, table_name)) = name::schema_and_name_path(&path) else { - return vec![]; - }; - let name_ref = path.segment().and_then(|s| s.name_ref()); - let position = table.syntax().text_range().start(); - let schemas = bind(db, file).resolved_schemas(position, schema.as_ref()); - // Try CTE resolution first since resolve_table_name doesn't handle CTEs - if let Some((ptr, kind)) = - resolve_table_like(db, name_ref.as_ref(), &table_name, &schemas, file) - { - let tree = parse(db, file).tree(); - let node = ptr.to_node(tree.syntax()); - match kind { - LocationKind::Table => { - if let Some(with_table) = node.ancestors().find_map(ast::WithTable::cast) { - return with_table_columns_with_types(db, file, with_table); - } - if let Some(t) = node.ancestors().find_map(ast::CreateTableLike::cast) { - return table_columns(db, file, &t); - } - } - LocationKind::View => { - if let Some(v) = node.ancestors().find_map(ast::CreateViewLike::cast) { - return view_like_columns_with_types(db, file, &v); - } - } - _ => (), - } - } - // Fall back to builtins for schema-qualified names - if let Some(resolved) = resolve_table_name(db, &table_name, &schemas, file) { - return resolved_to_columns_with_types(db, resolved.file_id, resolved.value, 0); - } - vec![] - } + ast::SelectVariant::Table(table) => table_query_columns_with_types(db, file, table), ast::SelectVariant::SelectInto(select_into) => { let Some(target_list) = select_into.select_clause().and_then(|c| c.target_list()) else { @@ -336,6 +298,51 @@ fn select_columns_with_types( } } +fn table_query_columns_with_types( + db: &dyn Db, + file: File, + table: &ast::Table, +) -> Vec<(Name, Option)> { + let Some(path) = table.relation_name().and_then(|r| r.path()) else { + return vec![]; + }; + let Some((schema, table_name)) = name::schema_and_name_path(&path) else { + return vec![]; + }; + let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) else { + return vec![]; + }; + let position = name_ref.syntax().text_range().start(); + let schemas = bind(db, file).resolved_schemas(position, schema.as_ref()); + // Try CTE resolution first since resolve_table_name doesn't handle CTEs + if let Some((ptr, kind)) = resolve_table_like(db, Some(&name_ref), &table_name, &schemas, file) + { + let tree = parse(db, file).tree(); + let node = ptr.to_node(tree.syntax()); + match kind { + LocationKind::Table => { + if let Some(with_table) = node.ancestors().find_map(ast::WithTable::cast) { + return with_table_columns_with_types(db, file, with_table); + } + if let Some(t) = node.ancestors().find_map(ast::CreateTableLike::cast) { + return table_columns(db, file, &t); + } + } + LocationKind::View => { + if let Some(v) = node.ancestors().find_map(ast::CreateViewLike::cast) { + return view_like_columns_with_types(db, file, &v); + } + } + _ => (), + } + } + // Fall back to builtins for schema-qualified names + if let Some(resolved) = resolve_table_name(db, &table_name, &schemas, file) { + return resolved_to_columns_with_types(db, resolved.file_id, resolved.value, 0); + } + vec![] +} + fn columns_from_returning_clause_with_types( db: &dyn Db, file: File, @@ -497,6 +504,10 @@ fn with_table_query_columns_with_types( return columns_from_values(&values); } + if let ast::WithQuery::Table(table) = query { + return table_query_columns_with_types(db, file, &table); + } + if let Some(columns) = columns_from_returning_clause_with_types(db, file, &query) { return columns; } @@ -734,8 +745,6 @@ fn select_variant_columns_with_types( file: File, select_variant: &ast::SelectVariant, ) -> Vec<(Name, Option)> { - let tree = parse(db, file).tree(); - let root = tree.syntax(); match select_variant { ast::SelectVariant::Values(values) => columns_from_values(values), ast::SelectVariant::Select(select) => { @@ -767,40 +776,7 @@ fn select_variant_columns_with_types( }; select_variant_columns_with_types(db, file, &lhs) } - ast::SelectVariant::Table(table) => { - let Some(path) = table.relation_name().and_then(|r| r.path()) else { - return vec![]; - }; - let Some((schema, table_name)) = name::schema_and_name_path(&path) else { - return vec![]; - }; - let name_ref = path.segment().and_then(|segment| segment.name_ref()); - let position = table.syntax().text_range().start(); - let schemas = bind(db, file).resolved_schemas(position, schema.as_ref()); - let Some((ptr, kind)) = - resolve_table_like(db, name_ref.as_ref(), &table_name, &schemas, file) - else { - return vec![]; - }; - let node = ptr.to_node(root); - match kind { - LocationKind::View => node - .ancestors() - .find_map(ast::CreateViewLike::cast) - .map(|v| view_like_columns_with_types(db, file, &v)) - .unwrap_or_default(), - LocationKind::Table => { - if let Some(with_table) = node.ancestors().find_map(ast::WithTable::cast) { - return with_table_columns_with_types(db, file, with_table); - } - node.ancestors() - .find_map(ast::CreateTableLike::cast) - .map(|t| table_columns(db, file, &t)) - .unwrap_or_default() - } - _ => vec![], - } - } + ast::SelectVariant::Table(table) => table_query_columns_with_types(db, file, table), } } diff --git a/crates/squawk_ide/src/resolve.rs b/crates/squawk_ide/src/resolve.rs index 0821c295..2b426a72 100644 --- a/crates/squawk_ide/src/resolve.rs +++ b/crates/squawk_ide/src/resolve.rs @@ -1356,23 +1356,32 @@ fn resolve_from_item_column_ptr( db: &dyn Db, from_item: InFile<&ast::FromItem>, column_name_ref: &ast::NameRef, +) -> Option> { + let column_name = Name::from_node(column_name_ref); + resolve_from_item_column_by_name(db, from_item, column_name_ref, &column_name) +} + +fn resolve_from_item_column_by_name( + db: &dyn Db, + from_item: InFile<&ast::FromItem>, + scope_name_ref: &ast::NameRef, + column_name: &Name, ) -> Option> { let file = from_item.file_id; let from_item = from_item.value; - let column_name = Name::from_node(column_name_ref); if let Some(paren_select) = from_item.paren_select() { let alias = from_item.alias(); if let Some(ptr) = resolve_subquery_column_ptr( db, InFile::new(file, &paren_select), - column_name_ref, - &column_name, + scope_name_ref, + column_name, alias.as_ref(), ) { return Some(ptr); } if let Some(alias_name) = alias.and_then(|x| x.name()) - && Name::from_node(&alias_name) == column_name + && Name::from_node(&alias_name) == *column_name { return Some(smallvec![Location::new( file, @@ -1387,7 +1396,7 @@ fn resolve_from_item_column_ptr( if let Some(column_list) = from_item.alias().and_then(|x| x.column_list()) { for col in column_list.columns() { if let Some(col_name) = col.name() - && Name::from_node(&col_name) == column_name + && Name::from_node(&col_name) == *column_name { return Some(smallvec![Location::new( file, @@ -1401,13 +1410,13 @@ fn resolve_from_item_column_ptr( if let Some(ptr) = resolve_column_from_paren_expr( db, InFile::new(file, &paren_expr), - column_name_ref, - &column_name, + scope_name_ref, + column_name, ) { return Some(ptr); } if let Some(alias_name) = from_item.alias().and_then(|x| x.name()) - && Name::from_node(&alias_name) == column_name + && Name::from_node(&alias_name) == *column_name { return Some(smallvec![Location::new( file, @@ -1421,7 +1430,7 @@ fn resolve_from_item_column_ptr( let alias_len = if let Some(column_list) = from_item.alias().and_then(|x| x.column_list()) { for col in column_list.columns() { if let Some(col_name) = col.name() - && Name::from_node(&col_name) == column_name + && Name::from_node(&col_name) == *column_name { return Some(smallvec![Location::new( file, @@ -1439,8 +1448,8 @@ fn resolve_from_item_column_ptr( && let Some(ptr) = resolve_column_from_call_expr_return_table( db, InFile::new(file, &call_expr), - column_name_ref, - &column_name, + scope_name_ref, + column_name, alias_len, ) { @@ -1448,19 +1457,20 @@ fn resolve_from_item_column_ptr( } let (schema, table_name) = name::schema_and_table_from_from_item(from_item)?; + let scope_name_ref = relation_name_ref_from_from_item(from_item)?; if let Some(ptr) = resolve_column_from_table_or_view_or_cte( db, - InFile::new(file, column_name_ref), + InFile::new(file, &scope_name_ref), &table_name, schema.as_ref(), - &column_name, + column_name, ) { return Some(ptr); } if let Some(alias_name) = from_item.alias().and_then(|x| x.name()) - && Name::from_node(&alias_name) == column_name + && Name::from_node(&alias_name) == *column_name { return Some(smallvec![Location::new( file, @@ -1626,12 +1636,13 @@ fn resolve_from_item_for_cte_star( if let Some((schema, table_name)) = name::schema_and_table_from_from_item(from_item.value) && table_name == *cte_name { - let position = name_ref.syntax().text_range().start(); + let scope_name_ref = relation_name_ref_from_from_item(from_item.value)?; + let position = scope_name_ref.syntax().text_range().start(); let schemas = bind(db, file).resolved_schemas(position, schema.as_ref()); return resolve_column_for_table(db, &table_name, &schemas, column_name, file); } - resolve_from_item_column_ptr(db, from_item, name_ref) + resolve_from_item_column_by_name(db, from_item, name_ref, column_name) } fn resolve_select_column_ptr( @@ -1726,9 +1737,10 @@ fn resolve_fn_call_column( for from_item in ast_nav::iter_from_clause(&from_clause) { if let Some((schema, table_name)) = name::schema_and_table_from_from_item(&from_item) + && let Some(scope_name_ref) = relation_name_ref_from_from_item(&from_item) && let Some(result) = resolve_column_from_table_or_view_or_cte( db, - InFile::new(file, name_ref), + InFile::new(file, &scope_name_ref), &table_name, schema.as_ref(), &column_name, @@ -1967,10 +1979,14 @@ fn find_column_in_create_table_as( if let ast::SelectVariant::Table(table) = &query { let path = table.relation_name()?.path()?; let (schema, table_name) = name::schema_and_name_path(&path)?; - let position = table.syntax().text_range().start(); - let schemas = bind(db, file).resolved_schemas(position, schema.as_ref()); - let resolved = resolve_table_name(db, &table_name, &schemas, file)?; - return find_column_in_resolved(db, resolved, column_name, 0); + let table_name_ref = relation_name_ref_from_table(table)?; + return resolve_column_from_table_or_view_or_cte( + db, + InFile::new(file, &table_name_ref), + &table_name, + schema.as_ref(), + column_name, + ); } let select = ast_nav::select_from_variant(query)?; @@ -2013,12 +2029,16 @@ fn find_column_in_from_clause( let Some((schema, table_name)) = name::schema_and_table_from_from_item(&from_item) else { continue; }; - let position = from_item.syntax().text_range().start(); - let schemas = bind(db, file).resolved_schemas(position, schema.as_ref()); - let Some(resolved) = resolve_table_name(db, &table_name, &schemas, file) else { + let Some(relation_name_ref) = relation_name_ref_from_from_item(&from_item) else { continue; }; - if let Some(result) = find_column_in_resolved(db, resolved, column_name, 0) { + if let Some(result) = resolve_column_from_table_or_view_or_cte( + db, + InFile::new(file, &relation_name_ref), + &table_name, + schema.as_ref(), + column_name, + ) { return Some(result); } } @@ -2046,6 +2066,18 @@ fn find_column_in_select_into( None } +fn relation_name_ref_from_table(table: &ast::Table) -> Option { + table.relation_name()?.path()?.segment()?.name_ref() +} + +fn relation_name_ref_from_from_item(from_item: &ast::FromItem) -> Option { + from_item.name_ref().or_else(|| { + from_item + .field_expr() + .and_then(|field_expr| field_expr.field()) + }) +} + fn resolve_cte_table(name_ref: &ast::NameRef, cte_name: &Name) -> Option { let with_table = ast_nav::find_cte_with_table(name_ref, cte_name)?; Some(SyntaxNodePtr::new(with_table.name()?.syntax())) @@ -2210,10 +2242,11 @@ fn resolve_cte_column( if let ast::WithQuery::Table(table) = query { let path = table.relation_name()?.path()?; let (schema, table_name) = name::schema_and_name_path(&path)?; + let table_name_ref = relation_name_ref_from_table(&table)?; return resolve_column_from_table_or_view_or_cte( db, - InFile::new(file, name_ref), + InFile::new(file, &table_name_ref), &table_name, schema.as_ref(), column_name, @@ -2236,7 +2269,7 @@ fn resolve_cte_column( let target_column_count = from_clause .as_ref() .and_then(|from_clause| { - count_columns_for_target(db, InFile::new(file, &target), name_ref, from_clause) + count_columns_for_target(db, InFile::new(file, &target), from_clause) }) .unwrap_or(1); let column_list_end = column_index.saturating_add(target_column_count); @@ -2396,10 +2429,11 @@ fn resolve_subquery_column_ptr( if let ast::SelectVariant::Table(table) = select_variant { let path = table.relation_name()?.path()?; let (schema, table_name) = name::schema_and_name_path(&path)?; + let table_name_ref = relation_name_ref_from_table(&table)?; return resolve_column_from_table_or_view_or_cte( db, - InFile::new(file, name_ref), + InFile::new(file, &table_name_ref), &table_name, schema.as_ref(), column_name, @@ -2462,9 +2496,12 @@ fn resolve_column_from_select_targets( && let Some(from_clause) = &from_clause { for from_item in ast_nav::iter_from_clause(from_clause) { - if let Some(result) = - resolve_from_item_column_ptr(db, InFile::new(file, &from_item), name_ref) - { + if let Some(result) = resolve_from_item_column_by_name( + db, + InFile::new(file, &from_item), + name_ref, + column_name, + ) { return Some(result); } } @@ -2475,8 +2512,12 @@ fn resolve_column_from_select_targets( && let Some(table_name) = qualified_star_table_name(&field_expr) && let Some(from_clause) = &from_clause && let Some(from_item) = find_from_item_in_from_clause(from_clause, &table_name) - && let Some(result) = - resolve_from_item_column_ptr(db, InFile::new(file, &from_item), name_ref) + && let Some(result) = resolve_from_item_column_by_name( + db, + InFile::new(file, &from_item), + name_ref, + column_name, + ) { return Some(result); } @@ -2579,20 +2620,19 @@ fn table_ptr_from_paren_expr( fn count_columns_for_target( db: &dyn Db, target: InFile<&ast::Target>, - name_ref: &ast::NameRef, from_clause: &ast::FromClause, ) -> Option { let file = target.file_id; let target = target.value; if target.star_token().is_some() { - return count_columns_for_from_clause(db, InFile::new(file, from_clause), name_ref); + return count_columns_for_from_clause(db, InFile::new(file, from_clause)); } if let Some(ast::Expr::FieldExpr(field_expr)) = target.expr() && let Some(table_name) = qualified_star_table_name(&field_expr) && let Some(from_item) = find_from_item_in_from_clause(from_clause, &table_name) { - return count_columns_for_from_item(db, InFile::new(file, &from_item), name_ref); + return count_columns_for_from_item(db, InFile::new(file, &from_item)); } Some(1) @@ -2601,7 +2641,6 @@ fn count_columns_for_target( fn count_columns_for_from_clause( db: &dyn Db, from_clause: InFile<&ast::FromClause>, - name_ref: &ast::NameRef, ) -> Option { let file = from_clause.file_id; let from_clause = from_clause.value; @@ -2609,9 +2648,7 @@ fn count_columns_for_from_clause( let mut found = false; for from_item in ast_nav::iter_from_clause(from_clause) { - if let Some(count) = - count_columns_for_from_item(db, InFile::new(file, &from_item), name_ref) - { + if let Some(count) = count_columns_for_from_item(db, InFile::new(file, &from_item)) { total = total.saturating_add(count); found = true; } @@ -2620,17 +2657,14 @@ fn count_columns_for_from_clause( found.then_some(total) } -fn count_columns_for_from_item( - db: &dyn Db, - from_item: InFile<&ast::FromItem>, - name_ref: &ast::NameRef, -) -> Option { +fn count_columns_for_from_item(db: &dyn Db, from_item: InFile<&ast::FromItem>) -> Option { let file = from_item.file_id; let from_item = from_item.value; let (schema, table_name) = name::schema_and_table_from_from_item(from_item)?; - let position = name_ref.syntax().text_range().start(); + let scope_name_ref = relation_name_ref_from_from_item(from_item)?; + let position = scope_name_ref.syntax().text_range().start(); let schemas = bind(db, file).resolved_schemas(position, schema.as_ref()); - count_columns_for_table_name(db, &table_name, &schemas, file, Some(name_ref)) + count_columns_for_table_name(db, &table_name, &schemas, file, Some(&scope_name_ref)) } fn resolve_from_clause_for_cte_star(