Skip to content

Commit e0c27f3

Browse files
authored
ide: hover/goto def for subquery w/ alias (#970)
1 parent 6105c79 commit e0c27f3

4 files changed

Lines changed: 179 additions & 16 deletions

File tree

crates/squawk_ide/src/classify.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -917,6 +917,9 @@ pub(crate) fn classify_def_node(def_node: &SyntaxNode) -> Option<NameRefClass> {
917917
return Some(NameRefClass::Channel);
918918
}
919919
if ast::Alias::can_cast(ancestor.kind()) {
920+
if in_column {
921+
return Some(NameRefClass::SelectColumn);
922+
}
920923
return Some(NameRefClass::FromTable);
921924
}
922925
if ast::AsName::can_cast(ancestor.kind())

crates/squawk_ide/src/goto_definition.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4981,6 +4981,42 @@ select x, b$0 from (select 1 a, 2 b) t(x);
49814981
");
49824982
}
49834983

4984+
#[test]
4985+
fn goto_subquery_alias_with_column_list_table_ref() {
4986+
assert_snapshot!(goto("
4987+
with t as (select 1 a, 2 b)
4988+
select z$0 from (select * from t) as z(x, y);
4989+
"), @"
4990+
╭▸
4991+
3 │ select z from (select * from t) as z(x, y);
4992+
╰╴ ─ 1. source ─ 2. destination
4993+
");
4994+
}
4995+
4996+
#[test]
4997+
fn goto_subquery_alias_with_column_list_table_ref_shadows_column() {
4998+
assert_snapshot!(goto("
4999+
with t as (select 1 a, 2 b)
5000+
select z$0 from (select a as z, b from t) as z(x, y);
5001+
"), @"
5002+
╭▸
5003+
3 │ select z from (select a as z, b from t) as z(x, y);
5004+
╰╴ ─ 1. source ─ 2. destination
5005+
");
5006+
}
5007+
5008+
#[test]
5009+
fn goto_subquery_nested_paren_alias_with_column_list_table_ref() {
5010+
assert_snapshot!(goto("
5011+
with t as (select 1 a, 2 b, 3 c)
5012+
select z$0 from ((select * from t)) as z(x, y);
5013+
"), @"
5014+
╭▸
5015+
3 │ select z from ((select * from t)) as z(x, y);
5016+
╰╴ ─ 1. source ─ 2. destination
5017+
");
5018+
}
5019+
49845020
#[test]
49855021
fn goto_table_expr_values_cte_partial_alias() {
49865022
assert_snapshot!(goto("

crates/squawk_ide/src/hover.rs

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,18 @@ fn format_hover_for_column_node(
338338
return format_view_column(&create_view, column_name, binder);
339339
}
340340

341+
if let Some(alias) = ast::Alias::cast(a.clone())
342+
&& let Some(alias_name) = alias.name()
343+
&& alias.column_list().is_some()
344+
{
345+
let table_name = Name::from_node(&alias_name);
346+
let column_name = Name::from_string(column_name_node.text().to_string());
347+
return Some(ColumnHover::table_column(
348+
&table_name.to_string(),
349+
&column_name.to_string(),
350+
));
351+
}
352+
341353
if let Some(create_table_as) = ast::CreateTableAs::cast(a.clone()) {
342354
let column_name = if let Some(name) = ast::Name::cast(column_name_node.clone()) {
343355
Name::from_node(&name)
@@ -581,6 +593,9 @@ fn hover_unqualified_star_in_arg_list(
581593

582594
fn hover_subquery_table(def_node: &SyntaxNode) -> Option<String> {
583595
let alias = def_node.ancestors().find_map(ast::Alias::cast)?;
596+
if alias.column_list().is_some() {
597+
return None;
598+
}
584599
let name = Name::from_node(&alias.name()?);
585600
let from_item = alias.syntax().ancestors().find_map(ast::FromItem::cast)?;
586601
let paren_select = from_item.paren_select()?;
@@ -671,10 +686,15 @@ fn collect_star_column_names(
671686
let table_name_node = table_ptr.to_node(root);
672687

673688
if let Some(paren_select) = ast::ParenSelect::cast(table_name_node.clone()) {
674-
return resolve::collect_paren_select_columns_with_types(binder, root, &paren_select)
675-
.into_iter()
676-
.map(|(name, _ty)| name)
677-
.collect();
689+
let columns: Vec<Name> =
690+
resolve::collect_paren_select_columns_with_types(binder, root, &paren_select)
691+
.into_iter()
692+
.map(|(name, _ty)| name)
693+
.collect();
694+
if !columns.is_empty() {
695+
return columns;
696+
}
697+
return collect_star_column_names_from_paren_select(root, &paren_select, binder);
678698
}
679699

680700
match resolve::find_table_source(&table_name_node) {
@@ -720,6 +740,29 @@ fn collect_star_column_names(
720740
}
721741
}
722742

743+
fn collect_star_column_names_from_paren_select(
744+
root: &SyntaxNode,
745+
paren_select: &ast::ParenSelect,
746+
binder: &binder::Binder,
747+
) -> Vec<Name> {
748+
let Some(select_variant) = paren_select.select() else {
749+
return vec![];
750+
};
751+
let ast::SelectVariant::Select(select) = select_variant else {
752+
return vec![];
753+
};
754+
let Some(from_clause) = select.from_clause() else {
755+
return vec![];
756+
};
757+
let mut columns = vec![];
758+
for from_item in from_clause.from_items() {
759+
if let Some(table_ptr) = resolve::table_ptr_from_from_item(binder, &from_item) {
760+
columns.extend(collect_star_column_names(root, &table_ptr, binder));
761+
}
762+
}
763+
columns
764+
}
765+
723766
fn hover_qualified_star_columns_from_table(
724767
root: &SyntaxNode,
725768
create_table: &impl ast::HasCreateTable,
@@ -3235,6 +3278,19 @@ select u$0.x, u.y from t as u(x, y);
32353278
");
32363279
}
32373280

3281+
#[test]
3282+
fn hover_on_cte_table_alias_with_column_list_column_ref() {
3283+
assert_snapshot!(check_hover("
3284+
with t as (select 1 a, 2 b, 3 c)
3285+
select u.x$0 from t as u(x, y);
3286+
"), @"
3287+
hover: column u.x
3288+
╭▸
3289+
3 │ select u.x from t as u(x, y);
3290+
╰╴ ─ hover
3291+
");
3292+
}
3293+
32383294
#[test]
32393295
fn hover_on_cte_table_alias_with_column_list_table_ref() {
32403296
assert_snapshot!(check_hover("
@@ -3248,6 +3304,32 @@ select u$0 from t as u(x, y);
32483304
");
32493305
}
32503306

3307+
#[test]
3308+
fn hover_on_subquery_alias_with_column_list_table_ref() {
3309+
assert_snapshot!(check_hover("
3310+
with t as (select 1 a, 2 b, 3 c)
3311+
select z$0 from (select * from t) as z(x, y);
3312+
"), @"
3313+
hover: table z(x, y, c)
3314+
╭▸
3315+
3 │ select z from (select * from t) as z(x, y);
3316+
╰╴ ─ hover
3317+
");
3318+
}
3319+
3320+
#[test]
3321+
fn hover_on_subquery_nested_paren_alias_with_column_list_table_ref() {
3322+
assert_snapshot!(check_hover("
3323+
with t as (select 1 a, 2 b, 3 c)
3324+
select z$0 from ((select * from t)) as z(x, y);
3325+
"), @"
3326+
hover: table z(x, y, c)
3327+
╭▸
3328+
3 │ select z from ((select * from t)) as z(x, y);
3329+
╰╴ ─ hover
3330+
");
3331+
}
3332+
32513333
#[test]
32523334
fn hover_on_cte_table_alias_with_partial_column_list_star() {
32533335
assert_snapshot!(check_hover("

crates/squawk_ide/src/resolve.rs

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,14 +1285,23 @@ fn resolve_from_item_column_ptr(
12851285
let column_name = Name::from_node(column_name_ref);
12861286
if let Some(paren_select) = from_item.paren_select() {
12871287
let alias = from_item.alias();
1288-
return resolve_subquery_column_ptr(
1288+
if let Some(ptr) = resolve_subquery_column_ptr(
12891289
binder,
12901290
root,
12911291
&paren_select,
12921292
column_name_ref,
12931293
&column_name,
12941294
alias.as_ref(),
1295-
);
1295+
) {
1296+
return Some(ptr);
1297+
}
1298+
if let Some(alias) = alias
1299+
&& let Some(alias_name) = alias.name()
1300+
&& Name::from_node(&alias_name) == column_name
1301+
{
1302+
return Some(SyntaxNodePtr::new(alias_name.syntax()));
1303+
}
1304+
return None;
12961305
}
12971306

12981307
if let Some(paren_expr) = from_item.paren_expr() {
@@ -1308,13 +1317,18 @@ fn resolve_from_item_column_ptr(
13081317
}
13091318
}
13101319

1311-
return resolve_column_from_paren_expr(
1312-
binder,
1313-
root,
1314-
&paren_expr,
1315-
column_name_ref,
1316-
&column_name,
1317-
);
1320+
if let Some(ptr) =
1321+
resolve_column_from_paren_expr(binder, root, &paren_expr, column_name_ref, &column_name)
1322+
{
1323+
return Some(ptr);
1324+
}
1325+
if let Some(alias) = from_item.alias()
1326+
&& let Some(alias_name) = alias.name()
1327+
&& Name::from_node(&alias_name) == column_name
1328+
{
1329+
return Some(SyntaxNodePtr::new(alias_name.syntax()));
1330+
}
1331+
return None;
13181332
}
13191333

13201334
let alias_len = if let Some(alias) = from_item.alias()
@@ -2425,7 +2439,7 @@ fn resolve_subquery_column_ptr(
24252439
) -> Option<SyntaxNodePtr> {
24262440
let select_variant = paren_select.select()?;
24272441

2428-
if let Some(alias) = alias
2442+
let column_list_len = if let Some(alias) = alias
24292443
&& let Some(column_list) = alias.column_list()
24302444
{
24312445
for col in column_list.columns() {
@@ -2438,7 +2452,10 @@ fn resolve_subquery_column_ptr(
24382452
if matches!(select_variant, ast::SelectVariant::Values(_)) {
24392453
return None;
24402454
}
2441-
}
2455+
column_list.columns().count()
2456+
} else {
2457+
0
2458+
};
24422459

24432460
// TODO: this should just be a match stmt
24442461
if let ast::SelectVariant::Table(table) = select_variant {
@@ -2482,8 +2499,16 @@ fn resolve_subquery_column_ptr(
24822499
let select_clause = subquery_select.select_clause()?;
24832500
let target_list = select_clause.target_list()?;
24842501

2485-
for target in target_list.targets() {
2502+
for (i, target) in target_list.targets().enumerate() {
24862503
if let Some((col_name, node)) = ColumnName::from_target(target.clone()) {
2504+
// skip targets that have been renamed by the column list
2505+
if i < column_list_len {
2506+
if matches!(col_name, ColumnName::Star) {
2507+
// star expands to multiple columns, handled below
2508+
} else {
2509+
continue;
2510+
}
2511+
}
24872512
if let Some(col_name_str) = col_name.to_string()
24882513
&& Name::from_string(col_name_str) == *column_name
24892514
{
@@ -2759,6 +2784,10 @@ pub(crate) fn table_ptr_from_from_item(
27592784
return Some(SyntaxNodePtr::new(paren_select.syntax()));
27602785
}
27612786

2787+
if let Some(paren_expr) = from_item.paren_expr() {
2788+
return table_ptr_from_paren_expr(binder, &paren_expr);
2789+
}
2790+
27622791
let (table_name, schema) = table_and_schema_from_from_item(from_item)?;
27632792
let position = from_item.syntax().text_range().start();
27642793

@@ -2780,6 +2809,19 @@ pub(crate) fn table_ptr_from_from_item(
27802809
None
27812810
}
27822811

2812+
fn table_ptr_from_paren_expr(
2813+
binder: &Binder,
2814+
paren_expr: &ast::ParenExpr,
2815+
) -> Option<SyntaxNodePtr> {
2816+
if let Some(from_item) = paren_expr.from_item() {
2817+
return table_ptr_from_from_item(binder, &from_item);
2818+
}
2819+
if let Some(ast::Expr::ParenExpr(inner)) = paren_expr.expr() {
2820+
return table_ptr_from_paren_expr(binder, &inner);
2821+
}
2822+
None
2823+
}
2824+
27832825
fn collect_table_ptrs_from_join_expr(
27842826
binder: &Binder,
27852827
join_expr: &ast::JoinExpr,

0 commit comments

Comments
 (0)