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
25 changes: 25 additions & 0 deletions crates/squawk_ide/src/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,31 @@ pub(crate) fn classify_name_ref(node: &SyntaxNode) -> Option<NameRefClass> {
}
}

// %type clause paths (max 3 segments):
// column%type, table.column%type, schema.table.column%type
if let Some(parent) = node.parent()
&& let Some(mut path) = ast::PathSegment::cast(parent)
.and_then(|p| p.syntax().parent().and_then(ast::Path::cast))
{
let mut hops_up = 0;
while let Some(next) = path.syntax().parent().and_then(ast::Path::cast) {
path = next;
hops_up += 1;
}
if path
.syntax()
.parent()
.is_some_and(|p| ast::PercentType::can_cast(p.kind()))
{
return match hops_up {
0 => Some(NameRefClass::QualifiedColumn),
1 => Some(NameRefClass::Table),
2 => Some(NameRefClass::Schema),
_ => None,
};
}
}

if let Some(parent) = node.parent()
&& let Some(inner_path) = ast::PathSegment::cast(parent)
.and_then(|p| p.syntax().parent().and_then(ast::Path::cast))
Expand Down
80 changes: 80 additions & 0 deletions crates/squawk_ide/src/goto_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10032,4 +10032,84 @@ select 1 from graph_table (myshop$0
╰╴ ─ 1. source
");
}

#[test]
fn goto_create_function_param_percent_type_column() {
assert_snapshot!(goto("
create schema s;
create table s.t (a int, b text);
create function f(x s.t.a$0%type) returns s.t.b%type
as $$ select 'hello'::text $$ language sql;
"), @"
╭▸
3 │ create table s.t (a int, b text);
│ ─ 2. destination
4 │ create function f(x s.t.a%type) returns s.t.b%type
╰╴ ─ 1. source
");
}

#[test]
fn goto_create_function_param_percent_type_table() {
assert_snapshot!(goto("
create schema s;
create table s.t (a int, b text);
create function f(x s.t$0.a%type) returns s.t.b%type
as $$ select 'hello'::text $$ language sql;
"), @"
╭▸
3 │ create table s.t (a int, b text);
│ ─ 2. destination
4 │ create function f(x s.t.a%type) returns s.t.b%type
╰╴ ─ 1. source
");
}

#[test]
fn goto_create_function_param_percent_type_schema() {
assert_snapshot!(goto("
create schema s;
create table s.t (a int, b text);
create function f(x s$0.t.a%type) returns s.t.b%type
as $$ select 'hello'::text $$ language sql;
"), @"
╭▸
2 │ create schema s;
│ ─ 2. destination
3 │ create table s.t (a int, b text);
4 │ create function f(x s.t.a%type) returns s.t.b%type
╰╴ ─ 1. source
");
}

#[test]
fn goto_create_function_returns_percent_type_column() {
assert_snapshot!(goto("
create schema s;
create table s.t (a int, b text);
create function f(x s.t.a%type) returns s.t.b$0%type
as $$ select 'hello'::text $$ language sql;
"), @"
╭▸
3 │ create table s.t (a int, b text);
│ ─ 2. destination
4 │ create function f(x s.t.a%type) returns s.t.b%type
╰╴ ─ 1. source
");
}

#[test]
fn goto_create_function_param_percent_type_two_part() {
assert_snapshot!(goto("
create table t (a int, b text);
create function f(x t.a$0%type) returns t.b%type
as $$ select 'hello'::text $$ language sql;
"), @"
╭▸
2 │ create table t (a int, b text);
│ ─ 2. destination
3 │ create function f(x t.a%type) returns t.b%type
╰╴ ─ 1. source
");
}
}
2 changes: 1 addition & 1 deletion crates/squawk_ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,9 @@ fn hover_literal(literal: &ast::Literal) -> Option<Hover> {
}
None => format!("value of literal: {}", markdown_inline_code(&value)),
},
LitKind::FloatNumber(_) => return None,
LitKind::IntNumber(_) => return None,
LitKind::Null(_) => return None,
LitKind::NumericNumber(_) => return None,
LitKind::PositionalParam(_) => return None,
};

Expand Down
2 changes: 1 addition & 1 deletion crates/squawk_ide/src/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ pub(crate) fn infer_type_from_literal(literal: &ast::Literal) -> Option<Type> {
let token = literal.syntax().first_token()?;
match token.kind() {
SyntaxKind::INT_NUMBER => Some(infer_int_type(token.text())),
SyntaxKind::FLOAT_NUMBER => Some(Type::Numeric),
SyntaxKind::NUMERIC_NUMBER => Some(Type::Numeric),
// TODO: this isn't necessarily text, e.g., select 1 + '1';
// We need to look at the context of the string's usage to be sure.
SyntaxKind::STRING
Expand Down
75 changes: 40 additions & 35 deletions crates/squawk_lexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ impl Cursor<'_> {

fn number(&mut self, first_digit: char) -> LiteralKind {
let mut base = Base::Decimal;
if first_digit == '.' {
return self.eat_fractional(base);
}
if first_digit == '0' {
// Attempt to parse encoding base.
match self.first() {
Expand Down Expand Up @@ -306,43 +309,13 @@ impl Cursor<'_> {
};

match self.first() {
'.' => {
// might have stuff after the ., and if it does, it needs to start
// with a number
self.bump();
let mut empty_exponent = false;
if self.first().is_ascii_digit() {
self.eat_decimal_digits();
match self.first() {
'e' | 'E' => {
self.bump();
empty_exponent = !self.eat_float_exponent();
}
_ => (),
}
} else {
match self.first() {
'e' | 'E' => {
self.bump();
empty_exponent = !self.eat_float_exponent();
}
_ => (),
}
}
let trailing_junk_start = self.pos_within_token();
self.eat_identifier();
LiteralKind::Float {
base,
empty_exponent,
trailing_junk_start,
}
}
'.' => self.eat_fractional(base),
'e' | 'E' => {
self.bump();
let empty_exponent = !self.eat_float_exponent();
let empty_exponent = !self.eat_numeric_exponent();
let trailing_junk_start = self.pos_within_token();
self.eat_identifier();
LiteralKind::Float {
LiteralKind::Numeric {
base,
empty_exponent,
trailing_junk_start,
Expand Down Expand Up @@ -509,9 +482,9 @@ impl Cursor<'_> {
has_digits
}

/// Eats the float exponent. Returns true if at least one digit was met,
/// Eats the numeric exponent. Returns true if at least one digit was met,
/// and returns false otherwise.
fn eat_float_exponent(&mut self) -> bool {
fn eat_numeric_exponent(&mut self) -> bool {
if self.first() == '_' {
return false;
}
Expand All @@ -526,6 +499,38 @@ impl Cursor<'_> {
self.eat_while(is_ident_cont);
}
}

pub(crate) fn eat_fractional(&mut self, base: Base) -> crate::LiteralKind {
// might have stuff after the ., and if it does, it needs to start
// with a number
self.bump();
let mut empty_exponent = false;
if self.first().is_ascii_digit() {
self.eat_decimal_digits();
match self.first() {
'e' | 'E' => {
self.bump();
empty_exponent = !self.eat_numeric_exponent();
}
_ => (),
}
} else {
match self.first() {
'e' | 'E' => {
self.bump();
empty_exponent = !self.eat_numeric_exponent();
}
_ => (),
}
}
let trailing_junk_start = self.pos_within_token();
self.eat_identifier();
LiteralKind::Numeric {
base,
empty_exponent,
trailing_junk_start,
}
}
}

/// Creates an iterator that produces tokens from the input string.
Expand Down
20 changes: 10 additions & 10 deletions crates/squawk_lexer/src/snapshots/squawk_lexer__tests__numeric.snap
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ expression: "lex(r#\"\n42\n3.5\n4.\n.001\n.123e10\n5e2\n1.925e-3\n1e-10\n1e+10\n
"\n" @ Whitespace,
"42" @ Literal { kind: Int { base: Decimal, empty_int: false, trailing_junk_start: 2 } },
"\n" @ Whitespace,
"3.5" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 3 } },
"3.5" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 3 } },
"\n" @ Whitespace,
"4." @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 2 } },
"4." @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 2 } },
"\n" @ Whitespace,
".001" @ Literal { kind: Int { base: Decimal, empty_int: false, trailing_junk_start: 4 } },
".001" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 4 } },
"\n" @ Whitespace,
".123e10" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 7 } },
".123e10" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 7 } },
"\n" @ Whitespace,
"5e2" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 3 } },
"5e2" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 3 } },
"\n" @ Whitespace,
"1.925e-3" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 8 } },
"1.925e-3" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 8 } },
"\n" @ Whitespace,
"1e-10" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 5 } },
"1e-10" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 5 } },
"\n" @ Whitespace,
"1e+10" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 5 } },
"1e+10" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 5 } },
"\n" @ Whitespace,
"1e10" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 4 } },
"1e10" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 4 } },
"\n" @ Whitespace,
"4664.E+5" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 8 } },
"4664.E+5" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 8 } },
"\n" @ Whitespace,
]
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ expression: "lex(r#\"\n1_500_000_000\n0b10001000_00000000\n0o_1_755\n0xFFFF_FFFF
"\n" @ Whitespace,
"0xFFFF_FFFF" @ Literal { kind: Int { base: Hexadecimal, empty_int: false, trailing_junk_start: 11 } },
"\n" @ Whitespace,
"1.618_034" @ Literal { kind: Float { base: Decimal, empty_exponent: false, trailing_junk_start: 9 } },
"1.618_034" @ Literal { kind: Numeric { base: Decimal, empty_exponent: false, trailing_junk_start: 9 } },
"\n" @ Whitespace,
]
6 changes: 3 additions & 3 deletions crates/squawk_lexer/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub enum TokenKind {
/// Used when there's an error of some sort while lexing.
Unknown,
/// Examples: `12u8`, `1.0e-40`, `b"123"`. Note that `_` is an invalid
/// suffix, but may be present here on string and float literals. Users of
/// suffix, but may be present here on string and numeric literals. Users of
/// this type will need to check for and reject that case.
///
/// See [`LiteralKind`] for more details.
Expand Down Expand Up @@ -132,10 +132,10 @@ pub enum LiteralKind {
empty_int: bool,
trailing_junk_start: u32,
},
/// Float Numeric, e.g., `1.925e-3`
/// Numeric literal with a decimal point or exponent, e.g., `1.925e-3`
///
/// see: <https://www.postgresql.org/docs/16/sql-syntax-lexical.html#SQL-SYNTAX-CONSTANTS-NUMERIC>
Float {
Numeric {
base: Base,
empty_exponent: bool,
trailing_junk_start: u32,
Expand Down
10 changes: 5 additions & 5 deletions crates/squawk_parser/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,11 @@ pub(crate) enum Event {
kind: SyntaxKind,
n_raw_tokens: u8,
},
/// When we parse `foo.0.0` or `foo. 0. 0` the lexer will hand us a float literal
/// When we parse `foo.0.0` or `foo. 0. 0` the lexer will hand us a numeric literal
/// instead of an integer literal followed by a dot as the lexer has no contextual knowledge.
/// This event instructs whatever consumes the events to split the float literal into
/// This event instructs whatever consumes the events to split the numeric literal into
/// the corresponding parts.
FloatSplitHack {
NumericSplitHack {
ends_in_dot: bool,
},
Error {
Expand Down Expand Up @@ -159,8 +159,8 @@ pub(super) fn process(mut events: Vec<Event>) -> Output {
Event::Token { kind, n_raw_tokens } => {
res.token(kind, n_raw_tokens);
}
Event::FloatSplitHack { ends_in_dot } => {
res.float_split_hack(ends_in_dot);
Event::NumericSplitHack { ends_in_dot } => {
res.numeric_split_hack(ends_in_dot);
let ev = mem::replace(&mut events[i + 1], Event::tombstone());
assert!(matches!(ev, Event::Finish), "{ev:?}");
}
Expand Down
2 changes: 1 addition & 1 deletion crates/squawk_parser/src/generated/syntax_kind.rs

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

6 changes: 3 additions & 3 deletions crates/squawk_parser/src/grammar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2278,8 +2278,8 @@ fn field_expr(
p.bump(DOT);
if p.at(IDENT) || p.at_ts(TYPE_KEYWORDS) || p.at(INT_NUMBER) || p.at_ts(ALL_KEYWORDS) {
name_ref_or_index(p);
} else if p.at(FLOAT_NUMBER) {
return match p.split_float(m) {
} else if p.at(NUMERIC_NUMBER) {
return match p.split_numeric(m) {
(true, m) => {
let lhs = m.complete(p, FIELD_EXPR);
postfix_dot_expr(p, lhs, allow_calls)
Expand Down Expand Up @@ -4901,7 +4901,7 @@ const LITERAL_FIRST: TokenSet = TokenSet::new(&[TRUE_KW, FALSE_KW, NULL_KW, DEFA
.union(NUMERIC_FIRST)
.union(STRING_FIRST);

const NUMERIC_FIRST: TokenSet = TokenSet::new(&[INT_NUMBER, FLOAT_NUMBER]);
const NUMERIC_FIRST: TokenSet = TokenSet::new(&[INT_NUMBER, NUMERIC_NUMBER]);

const STRING_FIRST: TokenSet = TokenSet::new(&[
STRING,
Expand Down
4 changes: 2 additions & 2 deletions crates/squawk_parser/src/lexed_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ impl<'a> Converter<'a> {
}
SyntaxKind::INT_NUMBER
}
squawk_lexer::LiteralKind::Float {
squawk_lexer::LiteralKind::Numeric {
empty_exponent,
base: _,
trailing_junk_start,
Expand All @@ -256,7 +256,7 @@ impl<'a> Converter<'a> {
} else if (trailing_junk_start as usize) < token_text.len() {
err = Some("trailing junk after numeric literal".into());
}
SyntaxKind::FLOAT_NUMBER
SyntaxKind::NUMERIC_NUMBER
}
squawk_lexer::LiteralKind::Str { terminated } => {
if !terminated {
Expand Down
Loading
Loading