Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

74 changes: 36 additions & 38 deletions crates/squawk_ide/src/code_actions/rewrite_integer_radix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,42 @@ use num_bigint::BigUint;
use rowan::TextSize;
use salsa::Database as Db;
use squawk_linter::Edit;
use squawk_syntax::SyntaxKind;

use crate::{
file::InFile,
literals::{IntegerRadix, normalize_integer_literal},
offsets::token_from_offset,
use squawk_syntax::{
SyntaxKind,
ast::{self, AstNode},
};

use super::{ActionKind, CodeAction};
use crate::{file::InFile, offsets::token_from_offset};

impl IntegerRadix {
const ALL: [Self; 4] = [Self::Binary, Self::Octal, Self::Decimal, Self::Hexadecimal];
use super::{ActionKind, CodeAction};

fn format(self, value: &BigUint) -> String {
let mut digits = value.to_str_radix(self.base());
if self == Self::Hexadecimal {
digits.make_ascii_uppercase();
}
let prefix = match self {
Self::Binary => "0b",
Self::Decimal => "",
Self::Hexadecimal => "0x",
Self::Octal => "0o",
};
format!("{prefix}{digits}")
const INTEGER_RADIXES: [ast::IntegerRadix; 4] = [
ast::IntegerRadix::Binary,
ast::IntegerRadix::Octal,
ast::IntegerRadix::Decimal,
ast::IntegerRadix::Hexadecimal,
];

fn format_integer(radix: ast::IntegerRadix, value: &BigUint) -> String {
let mut digits = value.to_str_radix(radix.base());
if radix == ast::IntegerRadix::Hexadecimal {
digits.make_ascii_uppercase();
}
let prefix = match radix {
ast::IntegerRadix::Binary => "0b",
ast::IntegerRadix::Decimal => "",
ast::IntegerRadix::Hexadecimal => "0x",
ast::IntegerRadix::Octal => "0o",
};
format!("{prefix}{digits}")
}

fn name(self) -> &'static str {
match self {
Self::Binary => "binary",
Self::Decimal => "decimal",
Self::Hexadecimal => "hexadecimal",
Self::Octal => "octal",
}
fn integer_radix_name(radix: ast::IntegerRadix) -> &'static str {
match radix {
ast::IntegerRadix::Binary => "binary",
ast::IntegerRadix::Decimal => "decimal",
ast::IntegerRadix::Hexadecimal => "hexadecimal",
ast::IntegerRadix::Octal => "octal",
}
}

Expand All @@ -49,15 +51,17 @@ pub(super) fn rewrite_integer_radix(
return None;
}

let (source_radix, value) = parse_integer_literal(token.text())?;
for target_radix in IntegerRadix::ALL {
let literal = token.parent().and_then(ast::Literal::cast)?;
let source_radix = literal.integer_radix()?;
let value = literal.integer_value()?;
for target_radix in INTEGER_RADIXES {
if target_radix == source_radix {
continue;
}

let replacement = target_radix.format(&value);
let replacement = format_integer(target_radix, &value);
actions.push(CodeAction {
title: format!("Rewrite integer as {}", target_radix.name()),
title: format!("Rewrite integer as {}", integer_radix_name(target_radix)),
edits: vec![Edit::replace(token.text_range(), replacement)],
kind: ActionKind::RefactorRewrite,
});
Expand All @@ -66,12 +70,6 @@ pub(super) fn rewrite_integer_radix(
Some(())
}

fn parse_integer_literal(text: &str) -> Option<(IntegerRadix, BigUint)> {
let (radix, digits) = normalize_integer_literal(text);
let value = BigUint::parse_bytes(digits.as_bytes(), radix.base())?;
Some((radix, value))
}

#[cfg(test)]
mod tests {
use insta::assert_snapshot;
Expand Down
4 changes: 2 additions & 2 deletions crates/squawk_ide/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ fn with_table_query_columns_with_types(
target_list_columns_with_types_in_file(db, file, &target_list, from_clause.as_ref())
}

fn target_expr_type(db: &dyn Db, file: FileId, target: &ast::Target) -> Option<Type> {
pub(crate) fn target_expr_type(db: &dyn Db, file: FileId, target: &ast::Target) -> Option<Type> {
let expr = target.expr()?;
if let Some(ty) = infer_type_from_expr(&expr) {
return Some(ty);
Expand Down Expand Up @@ -653,7 +653,7 @@ fn columns_for_star_from_clause(
columns
}

fn columns_for_star_from_from_item(
pub(crate) fn columns_for_star_from_from_item(
db: &dyn Db,
file: FileId,
from_item: &ast::FromItem,
Expand Down
211 changes: 211 additions & 0 deletions crates/squawk_ide/src/goto_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8592,6 +8592,217 @@ select * from t group by t.b$0;
");
}

#[test]
fn goto_select_target_ordinal_in_group_by() {
assert_snapshot!(goto("
with t as (select 1 a, 2 b)
select a, max(b) from t group by 1$0
"), @"
╭▸
3 │ select a, max(b) from t group by 1
╰╴ ─ 2. destination ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_order_by() {
assert_snapshot!(goto("
select a, max(b) as maximum from t order by 2$0
"), @"
╭▸
2 │ select a, max(b) as maximum from t order by 2
╰╴ ───────────────── 2. destination ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_parenthesized() {
assert_snapshot!(goto("
select a from t order by ((1$0))
"), @"
╭▸
2 │ select a from t order by ((1))
╰╴ ─ 2. destination ─ 1. source
");
assert_snapshot!(goto("
select a from t group by ((1$0))
"), @"
╭▸
2 │ select a from t group by ((1))
╰╴ ─ 2. destination ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_distinct_on() {
assert_snapshot!(goto("
select distinct on (1$0) a from t
"), @"
╭▸
2 │ select distinct on (1) a from t
│ ┬ ─ 2. destination
│ │
╰╴ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_group_by_set() {
assert_snapshot!(goto("
select a from t group by cube (1$0)
"), @"
╭▸
2 │ select a from t group by cube (1)
╰╴ ─ 2. destination ─ 1. source
");
assert_snapshot!(goto("
select a from t group by rollup (1$0)
"), @"
╭▸
2 │ select a from t group by rollup (1)
╰╴ ─ 2. destination ─ 1. source
");
assert_snapshot!(goto("
select a from t group by grouping sets (1$0)
"), @"
╭▸
2 │ select a from t group by grouping sets (1)
╰╴ ─ 2. destination ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_out_of_range() {
goto_not_found("select a from t order by 2$0");
}

#[test]
fn goto_select_target_ordinal_in_star() {
assert_snapshot!(goto("
with t as (select 1 a, 2 b)
select *, a from t order by 2$0
"), @"
╭▸
2 │ with t as (select 1 a, 2 b)
│ ─ 2. destination
3 │ select *, a from t order by 2
╰╴ ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_after_star() {
assert_snapshot!(goto("
with t as (select 1 a, 2 b)
select *, a from t order by 3$0
"), @"
╭▸
3 │ select *, a from t order by 3
╰╴ ─ 2. destination ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_qualified_star() {
assert_snapshot!(goto("
with t as (select 1 a, 2 b),
u as (select 3 c, 4 d)
select u.*, t.* from t, u group by 3$0
"), @"
╭▸
2 │ with t as (select 1 a, 2 b),
│ ─ 2. destination
3 │ u as (select 3 c, 4 d)
4 │ select u.*, t.* from t, u group by 3
╰╴ ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_star_across_from_items() {
assert_snapshot!(goto("
with t as (select 1 a, 2 b),
u as (select 3 c, 4 d)
select * from t, u order by 4$0
"), @"
╭▸
3 │ u as (select 3 c, 4 d)
│ ─ 2. destination
4 │ select * from t, u order by 4
╰╴ ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_star_through_cte_star() {
assert_snapshot!(goto("
with t as (select 1 a, 2 b),
u as (select * from t)
select * from u order by 2$0
"), @"
╭▸
2 │ with t as (select 1 a, 2 b),
│ ─ 2. destination
3 │ u as (select * from t)
4 │ select * from u order by 2
╰╴ ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_star_with_duplicate_names() {
assert_snapshot!(goto("
with t as (select 1 a, 2 a)
select * from t order by 2$0
"), @"
╭▸
2 │ with t as (select 1 a, 2 a)
│ ─ 2. destination
3 │ select * from t order by 2
╰╴ ─ 1. source
");
}

#[test]
fn goto_select_target_ordinal_in_star_with_alias_columns() {
assert_snapshot!(goto("
with t as (select 1 a, 2 b)
select * from t as x(y) order by 1$0
"), @"
╭▸
3 │ select * from t as x(y) order by 1
│ ┬ ─ 1. source
│ │
╰╴ 2. destination
");
}

#[test]
fn goto_select_target_ordinal_in_star_out_of_range() {
goto_not_found(
"
with t as (select 1 a, 2 b)
select *, a from t order by 4$0
",
);
}

#[test]
fn goto_select_target_ordinal_in_paren_select() {
assert_snapshot!(goto("
(select a, b from t) order by 2$0
"), @"
╭▸
2 │ (select a, b from t) order by 2
╰╴ ─ 2. destination ─ 1. source
");
}

#[test]
fn goto_window_order_by_integer_is_not_a_target_ordinal() {
goto_not_found("select a, row_number() over (order by 1$0) from t");
}

#[test]
fn goto_select_alias_order_by_column_name_conflict() {
// If an ORDER BY expression is a simple name that matches both an
Expand Down
Loading
Loading