Skip to content

Commit eff6fe0

Browse files
authored
parser: fix nodes for casts so ast access works correctly (#711)
```sql select '{123}'::pg_catalog.varchar(10)[], '{123}'::pg_catalog.varchar(10), '{123}'::pg_catalog.varchar, '{123}'::varchar, cast('{123}' as pg_catalog.varchar(10)[]), pg_catalog.varchar(10) '{123}', interval '1' month; ``` Make sure we can call the `.ty()` and `.expr()` method on `ast::CastExpr` and have things just work.
1 parent 9c92005 commit eff6fe0

21 files changed

+2026
-1054
lines changed

crates/squawk_linter/src/rules/prefer_text_field.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ fn is_not_allowed_varchar(ty: &ast::Type) -> bool {
4747
ast::Type::DoubleType(_) => false,
4848
ast::Type::TimeType(_) => false,
4949
ast::Type::IntervalType(_) => false,
50+
ast::Type::ExprType(_) => false,
5051
}
5152
}
5253

crates/squawk_linter/src/rules/prefer_timestamptz.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub fn is_not_allowed_timestamp(ty: &ast::Type) -> bool {
4242
false
4343
}
4444
ast::Type::IntervalType(_) => false,
45+
ast::Type::ExprType(_) => false,
4546
}
4647
}
4748

crates/squawk_linter/src/visitors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pub(crate) fn is_not_valid_int_type(
3434
ast::Type::DoubleType(_) => false,
3535
ast::Type::TimeType(_) => false,
3636
ast::Type::IntervalType(_) => false,
37+
ast::Type::ExprType(_) => false,
3738
}
3839
}
3940

crates/squawk_parser/src/generated/syntax_kind.rs

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/squawk_parser/src/grammar.rs

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,7 +2017,18 @@ fn name_ref_(p: &mut Parser<'_>) -> Option<CompletedMarker> {
20172017
// A type name followed by a string is a type cast so we insert a CAST_EXPR
20182018
// preceding it to wrap the previously parsed data.
20192019
// e.g., `select numeric '12312'`
2020-
if opt_string_literal(p).is_some() {
2020+
if p.at_ts(STRING_FIRST) {
2021+
// Wrap expr in type.
2022+
// TODO: can we unify types & exprs?
2023+
let cm = if kind == NAME_REF {
2024+
let path_segment = cm.precede(p).complete(p, PATH_SEGMENT);
2025+
let path = path_segment.precede(p).complete(p, PATH);
2026+
path.precede(p).complete(p, PATH_TYPE)
2027+
} else {
2028+
cm
2029+
};
2030+
2031+
string_literal(p);
20212032
if kind == INTERVAL_TYPE {
20222033
opt_interval_trailing(p);
20232034
}
@@ -2045,11 +2056,18 @@ fn between_expr(p: &mut Parser<'_>) -> CompletedMarker {
20452056

20462057
fn call_expr_args(p: &mut Parser<'_>, lhs: CompletedMarker) -> CompletedMarker {
20472058
assert!(p.at(L_PAREN));
2059+
let prev_kind = lhs.kind();
20482060
let m = lhs.precede(p);
20492061
arg_list(p);
20502062
opt_agg_clauses(p);
2051-
let cm = m.complete(p, CALL_EXPR);
2052-
if opt_string_literal(p).is_some() {
2063+
let mut cm = m.complete(p, CALL_EXPR);
2064+
if p.at_ts(STRING_FIRST) {
2065+
// Wrap expr in type.
2066+
// TODO: can we unify types & exprs?
2067+
if prev_kind == FIELD_EXPR {
2068+
cm = cm.precede(p).complete(p, EXPR_TYPE);
2069+
}
2070+
string_literal(p);
20532071
cm.precede(p).complete(p, CAST_EXPR)
20542072
} else {
20552073
cm
@@ -2199,9 +2217,13 @@ fn postfix_dot_expr(
21992217
) -> Result<CompletedMarker, CompletedMarker> {
22002218
assert!(p.at(DOT));
22012219
field_expr(p, Some(lhs), allow_calls).map(|cm| {
2202-
// A field followed by a literal is a type cast so we insert a CAST_EXPR
2203-
// preceding it to wrap the previously parsed data.
2204-
if opt_string_literal(p).is_some() {
2220+
if p.at_ts(STRING_FIRST) {
2221+
// wrap our previous expression in a type
2222+
// TODO: can we unify types & exprs?
2223+
let cm = cm.precede(p).complete(p, EXPR_TYPE);
2224+
string_literal(p);
2225+
// A field followed by a literal is a type cast so we insert a CAST_EXPR
2226+
// preceding it to wrap the previously parsed data.
22052227
cm.precede(p).complete(p, CAST_EXPR)
22062228
} else {
22072229
cm
@@ -2433,17 +2455,29 @@ fn expr_bp(p: &mut Parser<'_>, bp: u8, r: &Restrictions) -> Option<CompletedMark
24332455
Associativity::Left => op_bp + 1,
24342456
Associativity::Right => op_bp,
24352457
};
2436-
let _ = expr_bp(p, op_bp, r);
2437-
lhs = m.complete(
2438-
p,
2439-
if matches!(op, COLON_COLON) {
2440-
CAST_EXPR
2441-
} else if matches!(op, FAT_ARROW | COLON_EQ) {
2442-
NAMED_ARG
2443-
} else {
2444-
BIN_EXPR
2445-
},
2446-
);
2458+
let rhs = expr_bp(p, op_bp, r);
2459+
lhs = if matches!(op, COLON_COLON) {
2460+
if let Some(rhs) = rhs {
2461+
match rhs.kind() {
2462+
NAME_REF => {
2463+
// wrap our previous expression in a type
2464+
// TODO: can we unify types & exprs?
2465+
let path_segment = rhs.precede(p).complete(p, PATH_SEGMENT);
2466+
let path = path_segment.precede(p).complete(p, PATH);
2467+
path.precede(p).complete(p, PATH_TYPE);
2468+
}
2469+
FIELD_EXPR | CALL_EXPR | INDEX_EXPR => {
2470+
rhs.precede(p).complete(p, EXPR_TYPE);
2471+
}
2472+
_ => {}
2473+
}
2474+
};
2475+
m.complete(p, CAST_EXPR)
2476+
} else if matches!(op, FAT_ARROW | COLON_EQ) {
2477+
m.complete(p, NAMED_ARG)
2478+
} else {
2479+
m.complete(p, BIN_EXPR)
2480+
};
24472481
}
24482482
Some(lhs)
24492483
}

crates/squawk_parser/tests/data/ok/select_casts.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ select int8('1234');
77
select 44::bit(3);
88
select cast(-44 as bit(12));
99
select '1110'::bit(4)::integer;
10+
select '{1}'::pg_catalog.varchar(1)[];
1011

1112
select '{1,2,3}'::int[];
1213
select foo::int;
@@ -206,6 +207,8 @@ select '{1}'::pg_catalog.int8[];
206207

207208
-- cast
208209
select cast(a as foo.bar);
210+
select cast('{1}' as pg_catalog.varchar(1)[]);
211+
209212

210213
-- treat
211214
select treat(a as foo.b);
@@ -268,3 +271,4 @@ select boolean 'false';
268271

269272
select foo.bar '100';
270273
select foo.bar(10, 2) '100';
274+
select pg_catalog.varchar(100) '{123}';

crates/squawk_parser/tests/snapshots/tests__create_view_ok.snap

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,11 @@ SOURCE_FILE
4646
TARGET_LIST
4747
TARGET
4848
CAST_EXPR
49-
NAME_REF
50-
TEXT_KW "text"
49+
PATH_TYPE
50+
PATH
51+
PATH_SEGMENT
52+
NAME_REF
53+
TEXT_KW "text"
5154
WHITESPACE " "
5255
LITERAL
5356
STRING "'Hello World'"

crates/squawk_parser/tests/snapshots/tests__explain_ok.snap

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -538,8 +538,11 @@ SOURCE_FILE
538538
COLON_COLON
539539
COLON ":"
540540
COLON ":"
541-
NAME_REF
542-
INTEGER_KW "integer"
541+
PATH_TYPE
542+
PATH
543+
PATH_SEGMENT
544+
NAME_REF
545+
INTEGER_KW "integer"
543546
WHITESPACE " "
544547
AND_KW "AND"
545548
WHITESPACE " "
@@ -555,8 +558,11 @@ SOURCE_FILE
555558
COLON_COLON
556559
COLON ":"
557560
COLON ":"
558-
NAME_REF
559-
INTEGER_KW "integer"
561+
PATH_TYPE
562+
PATH
563+
PATH_SEGMENT
564+
NAME_REF
565+
INTEGER_KW "integer"
560566
WHITESPACE "\n "
561567
GROUP_BY_CLAUSE
562568
GROUP_KW "GROUP"

0 commit comments

Comments
 (0)