Skip to main content

polyglot_sql/dialects/
clickhouse.rs

1//! ClickHouse Dialect
2//!
3//! ClickHouse-specific transformations based on sqlglot patterns.
4//! ClickHouse is case-sensitive and has unique function naming conventions.
5
6use super::{DialectImpl, DialectType};
7#[cfg(feature = "transpile")]
8use crate::error::Result;
9#[cfg(feature = "transpile")]
10use crate::expressions::{
11    AggregateFunction, BinaryOp, Case, Cast, Expression, Function, In, IsNull, LikeOp,
12    MapConstructor, Paren, UnaryOp,
13};
14#[cfg(feature = "generate")]
15use crate::generator::GeneratorConfig;
16use crate::tokens::TokenizerConfig;
17
18/// ClickHouse dialect
19pub struct ClickHouseDialect;
20
21impl DialectImpl for ClickHouseDialect {
22    fn dialect_type(&self) -> DialectType {
23        DialectType::ClickHouse
24    }
25
26    fn tokenizer_config(&self) -> TokenizerConfig {
27        let mut config = TokenizerConfig::default();
28        // ClickHouse uses double quotes and backticks for identifiers
29        config.identifiers.insert('"', '"');
30        config.identifiers.insert('`', '`');
31        // ClickHouse supports nested comments
32        config.nested_comments = true;
33        // ClickHouse allows identifiers to start with digits
34        config.identifiers_can_start_with_digit = true;
35        // ClickHouse uses backslash escaping in strings
36        config.string_escapes.push('\\');
37        // ClickHouse supports # as single-line comment
38        config.hash_comments = true;
39        // ClickHouse allows $ in identifiers
40        config.dollar_sign_is_identifier = true;
41        // ClickHouse: INSERT ... FORMAT <name> is followed by raw data
42        config.insert_format_raw_data = true;
43        // ClickHouse supports 0xDEADBEEF hex integer literals
44        config.hex_number_strings = true;
45        config.hex_string_is_integer_type = true;
46        // ClickHouse allows underscores as digit separators in numeric literals
47        config.numbers_can_be_underscore_separated = true;
48        // SQLGlot tokenizes malformed SHOW LIKE probes such as `'a\' or 1=1`.
49        config.recover_terminal_backslash_quote = true;
50        // The ClickHouse corpus includes partial string probes extracted from shell tests.
51        config.recover_unterminated_string = true;
52        config
53    }
54
55    #[cfg(feature = "generate")]
56
57    fn generator_config(&self) -> GeneratorConfig {
58        use crate::generator::{IdentifierQuoteStyle, NormalizeFunctions};
59        let mut complexity_guard = crate::guard::ComplexityGuardOptions::default();
60        complexity_guard.max_ast_depth = Some(4_096);
61        complexity_guard.max_function_call_depth = Some(512);
62
63        GeneratorConfig {
64            identifier_quote: '"',
65            identifier_quote_style: IdentifierQuoteStyle::DOUBLE_QUOTE,
66            dialect: Some(DialectType::ClickHouse),
67            complexity_guard,
68            // ClickHouse uses uppercase keywords (matching Python SQLGlot behavior)
69            uppercase_keywords: true,
70            // ClickHouse function names are case-sensitive and typically camelCase
71            normalize_functions: NormalizeFunctions::None,
72            // ClickHouse identifiers are case-sensitive
73            case_sensitive_identifiers: true,
74            tablesample_keywords: "SAMPLE",
75            tablesample_requires_parens: false,
76            identifiers_can_start_with_digit: true,
77            // ClickHouse uses bracket-only notation for arrays: [1, 2, 3]
78            array_bracket_only: true,
79            ..Default::default()
80        }
81    }
82
83    #[cfg(feature = "transpile")]
84
85    fn transform_expr(&self, expr: Expression) -> Result<Expression> {
86        let wrap_predicate_left = |expr: Expression| -> Expression {
87            let needs_parens = matches!(
88                expr,
89                Expression::Add(_)
90                    | Expression::Sub(_)
91                    | Expression::Mul(_)
92                    | Expression::Div(_)
93                    | Expression::Mod(_)
94                    | Expression::Concat(_)
95                    | Expression::And(_)
96                    | Expression::Or(_)
97                    | Expression::Not(_)
98                    | Expression::Case(_)
99            );
100
101            if needs_parens {
102                Expression::Paren(Box::new(Paren {
103                    this: expr,
104                    trailing_comments: Vec::new(),
105                }))
106            } else {
107                expr
108            }
109        };
110
111        let wrap_not_target = |expr: Expression| -> Expression {
112            match expr {
113                Expression::Paren(_) => expr,
114                Expression::In(_)
115                | Expression::Between(_)
116                | Expression::Is(_)
117                | Expression::IsNull(_)
118                | Expression::IsTrue(_)
119                | Expression::IsFalse(_)
120                | Expression::IsJson(_)
121                | Expression::Like(_)
122                | Expression::ILike(_)
123                | Expression::SimilarTo(_)
124                | Expression::Glob(_)
125                | Expression::RegexpLike(_)
126                | Expression::RegexpILike(_)
127                | Expression::MemberOf(_) => Expression::Paren(Box::new(Paren {
128                    this: expr,
129                    trailing_comments: Vec::new(),
130                })),
131                _ => expr,
132            }
133        };
134
135        let unwrap_in_array = |mut expressions: Vec<Expression>,
136                               query: &Option<Expression>,
137                               unnest: &Option<Box<Expression>>|
138         -> Vec<Expression> {
139            if query.is_none() && unnest.is_none() && expressions.len() == 1 {
140                if matches!(expressions[0], Expression::ArrayFunc(_)) {
141                    if let Expression::ArrayFunc(arr) = expressions.remove(0) {
142                        return arr.expressions;
143                    }
144                }
145            }
146            expressions
147        };
148
149        match expr {
150            // TryCast stays as TryCast (ClickHouse doesn't have TRY_CAST by default)
151            // But we can emulate with toXOrNull functions
152            Expression::TryCast(c) => {
153                // For simplicity, just use regular CAST
154                // ClickHouse has toXOrNull/toXOrZero functions for safe casts
155                Ok(Expression::Cast(c))
156            }
157
158            // SafeCast -> CAST in ClickHouse
159            Expression::SafeCast(c) => Ok(Expression::Cast(c)),
160
161            // CountIf is native in ClickHouse (lowercase)
162            Expression::CountIf(f) => Ok(Expression::Function(Box::new(Function::new(
163                "countIf".to_string(),
164                vec![f.this],
165            )))),
166
167            // UNNEST -> arrayJoin in ClickHouse
168            Expression::Unnest(f) => Ok(Expression::Function(Box::new(Function::new(
169                "arrayJoin".to_string(),
170                vec![f.this],
171            )))),
172
173            // EXPLODE -> arrayJoin in ClickHouse
174            Expression::Explode(f) => Ok(Expression::Function(Box::new(Function::new(
175                "arrayJoin".to_string(),
176                vec![f.this],
177            )))),
178
179            // ExplodeOuter -> arrayJoin in ClickHouse
180            Expression::ExplodeOuter(f) => Ok(Expression::Function(Box::new(Function::new(
181                "arrayJoin".to_string(),
182                vec![f.this],
183            )))),
184
185            // RAND -> randCanonical() in ClickHouse
186            Expression::Rand(_) => Ok(Expression::Function(Box::new(Function::new(
187                "randCanonical".to_string(),
188                vec![],
189            )))),
190
191            // Random -> randCanonical() in ClickHouse
192            Expression::Random(_) => Ok(Expression::Function(Box::new(Function::new(
193                "randCanonical".to_string(),
194                vec![],
195            )))),
196
197            // startsWith -> startsWith
198            Expression::StartsWith(f) => Ok(Expression::Function(Box::new(Function::new(
199                "startsWith".to_string(),
200                vec![f.this, f.expression],
201            )))),
202
203            // endsWith -> endsWith
204            Expression::EndsWith(f) => Ok(Expression::Function(Box::new(Function::new(
205                "endsWith".to_string(),
206                vec![f.this, f.expression],
207            )))),
208
209            // ClickHouse prefers NOT (x IN (...)) over x NOT IN (...)
210            Expression::In(in_expr) if in_expr.not => {
211                if in_expr.global {
212                    return Ok(Expression::In(in_expr));
213                }
214                let In {
215                    this,
216                    expressions,
217                    query,
218                    unnest,
219                    global,
220                    is_field,
221                    ..
222                } = *in_expr;
223                let expressions = unwrap_in_array(expressions, &query, &unnest);
224                let base = Expression::In(Box::new(In {
225                    this: wrap_predicate_left(this),
226                    expressions,
227                    query,
228                    not: false,
229                    global,
230                    unnest,
231                    is_field,
232                }));
233                Ok(Expression::Not(Box::new(UnaryOp {
234                    this: wrap_not_target(base),
235                    inferred_type: None,
236                })))
237            }
238
239            // ClickHouse prefers NOT (x IS NULL) over x IS NOT NULL
240            Expression::IsNull(is_null) if is_null.not => {
241                let IsNull { this, .. } = *is_null;
242                let base = Expression::IsNull(Box::new(IsNull {
243                    this: wrap_predicate_left(this),
244                    not: false,
245                    postfix_form: false,
246                }));
247                Ok(Expression::Not(Box::new(UnaryOp {
248                    this: wrap_not_target(base),
249                    inferred_type: None,
250                })))
251            }
252
253            Expression::In(mut in_expr) => {
254                in_expr.expressions =
255                    unwrap_in_array(in_expr.expressions, &in_expr.query, &in_expr.unnest);
256                in_expr.this = wrap_predicate_left(in_expr.this);
257                Ok(Expression::In(in_expr))
258            }
259
260            Expression::IsNull(mut is_null) => {
261                is_null.this = wrap_predicate_left(is_null.this);
262                Ok(Expression::IsNull(is_null))
263            }
264
265            // IF(cond, true, false) -> CASE WHEN cond THEN true ELSE false END
266            Expression::IfFunc(f) => {
267                let f = *f;
268                let has_aliased_arg = matches!(f.condition, Expression::Alias(_))
269                    || matches!(f.true_value, Expression::Alias(_))
270                    || matches!(f.false_value.as_ref(), Some(Expression::Alias(_)));
271                if has_aliased_arg {
272                    return Ok(Expression::IfFunc(Box::new(f)));
273                }
274                Ok(Expression::Case(Box::new(Case {
275                    operand: None,
276                    whens: vec![(f.condition, f.true_value)],
277                    else_: f.false_value,
278                    comments: Vec::new(),
279                    inferred_type: None,
280                })))
281            }
282
283            Expression::Is(mut is_expr) => {
284                is_expr.left = wrap_predicate_left(is_expr.left);
285                Ok(Expression::Is(is_expr))
286            }
287
288            Expression::Or(op) => {
289                let BinaryOp {
290                    left,
291                    right,
292                    left_comments,
293                    operator_comments,
294                    trailing_comments,
295                    ..
296                } = *op;
297                let left = if matches!(left, Expression::And(_)) {
298                    Expression::Paren(Box::new(Paren {
299                        this: left,
300                        trailing_comments: Vec::new(),
301                    }))
302                } else {
303                    left
304                };
305                let right = if matches!(right, Expression::And(_)) {
306                    Expression::Paren(Box::new(Paren {
307                        this: right,
308                        trailing_comments: Vec::new(),
309                    }))
310                } else {
311                    right
312                };
313                Ok(Expression::Or(Box::new(BinaryOp {
314                    left,
315                    right,
316                    left_comments,
317                    operator_comments,
318                    trailing_comments,
319                    inferred_type: None,
320                })))
321            }
322
323            Expression::Not(op) => {
324                let inner = wrap_not_target(op.this);
325                Ok(Expression::Not(Box::new(UnaryOp {
326                    this: inner,
327                    inferred_type: None,
328                })))
329            }
330
331            Expression::MapFunc(map) if map.curly_brace_syntax => {
332                let MapConstructor { keys, values, .. } = *map;
333                let mut args = Vec::with_capacity(keys.len() * 2);
334                for (key, value) in keys.into_iter().zip(values.into_iter()) {
335                    args.push(key);
336                    args.push(value);
337                }
338                Ok(Expression::Function(Box::new(Function::new(
339                    "map".to_string(),
340                    args,
341                ))))
342            }
343
344            Expression::Insert(mut insert) => {
345                for row in insert.values.iter_mut() {
346                    for value in row.iter_mut() {
347                        if !matches!(value, Expression::Paren(_)) {
348                            let wrapped = Expression::Paren(Box::new(Paren {
349                                this: value.clone(),
350                                trailing_comments: Vec::new(),
351                            }));
352                            *value = wrapped;
353                        }
354                    }
355                }
356                Ok(Expression::Insert(insert))
357            }
358
359            // Generic function transformations
360            Expression::Function(f) => self.transform_function(*f),
361
362            // Generic aggregate function transformations
363            Expression::AggregateFunction(f) => self.transform_aggregate_function(f),
364
365            // Cast transformations
366            Expression::Cast(c) => self.transform_cast(*c),
367
368            // TYPEOF -> toTypeName in ClickHouse
369            Expression::Typeof(f) => Ok(Expression::Function(Box::new(Function::new(
370                "toTypeName".to_string(),
371                vec![f.this],
372            )))),
373
374            // Pass through everything else
375            _ => Ok(expr),
376        }
377    }
378}
379
380#[cfg(feature = "transpile")]
381impl ClickHouseDialect {}
382
383#[cfg(feature = "transpile")]
384impl ClickHouseDialect {
385    fn transform_function(&self, f: Function) -> Result<Expression> {
386        let name_upper = f.name.to_uppercase();
387        match name_upper.as_str() {
388            // UTCTimestamp() -> CURRENT_TIMESTAMP('UTC')
389            "UTCTIMESTAMP" => Ok(Expression::UtcTimestamp(Box::new(
390                crate::expressions::UtcTimestamp { this: None },
391            ))),
392
393            "CURRENTDATABASE" | "CURRENT_DATABASE" => Ok(Expression::Function(Box::new(
394                Function::new("CURRENT_DATABASE".to_string(), f.args),
395            ))),
396            "CURRENTSCHEMAS" | "CURRENT_SCHEMAS" => Ok(Expression::Function(Box::new(
397                Function::new("CURRENT_SCHEMAS".to_string(), f.args),
398            ))),
399            "LEVENSHTEIN" | "LEVENSHTEINDISTANCE" | "EDITDISTANCE" => Ok(Expression::Function(
400                Box::new(Function::new("editDistance".to_string(), f.args)),
401            )),
402            "CHAR" | "CHR" => Ok(Expression::Function(Box::new(Function::new(
403                "CHAR".to_string(),
404                f.args,
405            )))),
406            "STR_TO_DATE" => Ok(Expression::Function(Box::new(Function::new(
407                "STR_TO_DATE".to_string(),
408                f.args,
409            )))),
410            "JSONEXTRACTSTRING" => Ok(Expression::Function(Box::new(Function::new(
411                "JSONExtractString".to_string(),
412                f.args,
413            )))),
414            "MATCH" => Ok(Expression::Function(Box::new(Function::new(
415                "match".to_string(),
416                f.args,
417            )))),
418            "LIKE" if f.args.len() == 2 => {
419                let left = f.args[0].clone();
420                let right = f.args[1].clone();
421                Ok(Expression::Like(Box::new(LikeOp::new(left, right))))
422            }
423            "NOTLIKE" if f.args.len() == 2 => {
424                let left = f.args[0].clone();
425                let right = f.args[1].clone();
426                let like = Expression::Like(Box::new(LikeOp::new(left, right)));
427                Ok(Expression::Not(Box::new(UnaryOp {
428                    this: like,
429                    inferred_type: None,
430                })))
431            }
432            "ILIKE" if f.args.len() == 2 => {
433                let left = f.args[0].clone();
434                let right = f.args[1].clone();
435                Ok(Expression::ILike(Box::new(LikeOp::new(left, right))))
436            }
437            "AND" if f.args.len() >= 2 => {
438                let mut iter = f.args.into_iter();
439                let mut expr = iter.next().unwrap();
440                for arg in iter {
441                    expr = Expression::And(Box::new(BinaryOp::new(expr, arg)));
442                }
443                Ok(expr)
444            }
445            "OR" if f.args.len() >= 2 => {
446                let mut iter = f.args.into_iter();
447                let mut expr = iter.next().unwrap();
448                for arg in iter {
449                    expr = Expression::Or(Box::new(BinaryOp::new(expr, arg)));
450                }
451                self.transform_expr(expr)
452            }
453            "XOR" if f.args.len() >= 2 => {
454                let mut iter = f.args.into_iter().map(Self::wrap_nested_xor_arg);
455                let mut expr = iter.next().unwrap();
456                for arg in iter {
457                    expr = Expression::Function(Box::new(Function::new(
458                        f.name.clone(),
459                        vec![expr, arg],
460                    )));
461                }
462                Ok(expr)
463            }
464            // TYPEOF -> toTypeName in ClickHouse
465            "TYPEOF" => Ok(Expression::Function(Box::new(Function::new(
466                "toTypeName".to_string(),
467                f.args,
468            )))),
469
470            // DATE_TRUNC: ClickHouse uses dateTrunc (camelCase)
471            "DATE_TRUNC" | "DATETRUNC" => Ok(Expression::Function(Box::new(Function::new(
472                "dateTrunc".to_string(),
473                f.args,
474            )))),
475            "TOSTARTOFDAY" if f.args.len() == 1 => {
476                Ok(Expression::Function(Box::new(Function::new(
477                    "dateTrunc".to_string(),
478                    vec![Expression::string("DAY"), f.args[0].clone()],
479                ))))
480            }
481
482            // SUBSTRING_INDEX: preserve original case (substringIndex in ClickHouse)
483            "SUBSTRING_INDEX" => Ok(Expression::Function(Box::new(Function::new(
484                f.name.clone(),
485                f.args,
486            )))),
487
488            // IS_NAN / ISNAN -> isNaN (ClickHouse camelCase)
489            "IS_NAN" | "ISNAN" => Ok(Expression::Function(Box::new(Function::new(
490                "isNaN".to_string(),
491                f.args,
492            )))),
493
494            _ => Ok(Expression::Function(Box::new(f))),
495        }
496    }
497
498    fn transform_aggregate_function(
499        &self,
500        f: Box<crate::expressions::AggregateFunction>,
501    ) -> Result<Expression> {
502        let name_upper = f.name.to_uppercase();
503        match name_upper.as_str() {
504            // COUNT_IF -> countIf
505            "COUNT_IF" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
506                "countIf".to_string(),
507                f.args,
508            )))),
509
510            // SUM_IF -> sumIf
511            "SUM_IF" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
512                "sumIf".to_string(),
513                f.args,
514            )))),
515
516            // AVG_IF -> avgIf
517            "AVG_IF" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
518                "avgIf".to_string(),
519                f.args,
520            )))),
521
522            // ANY_VALUE -> any in ClickHouse
523            "ANY_VALUE" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
524                "any".to_string(),
525                f.args,
526            )))),
527
528            // GROUP_CONCAT -> groupArray + arrayStringConcat
529            "GROUP_CONCAT" if !f.args.is_empty() => {
530                let mut args = f.args;
531                let first = args.remove(0);
532                let separator = args.pop();
533                let group_array = Expression::Function(Box::new(Function::new(
534                    "groupArray".to_string(),
535                    vec![first],
536                )));
537                if let Some(sep) = separator {
538                    Ok(Expression::Function(Box::new(Function::new(
539                        "arrayStringConcat".to_string(),
540                        vec![group_array, sep],
541                    ))))
542                } else {
543                    Ok(Expression::Function(Box::new(Function::new(
544                        "arrayStringConcat".to_string(),
545                        vec![group_array],
546                    ))))
547                }
548            }
549
550            // STRING_AGG -> groupArray + arrayStringConcat
551            "STRING_AGG" if !f.args.is_empty() => {
552                let mut args = f.args;
553                let first = args.remove(0);
554                let separator = args.pop();
555                let group_array = Expression::Function(Box::new(Function::new(
556                    "groupArray".to_string(),
557                    vec![first],
558                )));
559                if let Some(sep) = separator {
560                    Ok(Expression::Function(Box::new(Function::new(
561                        "arrayStringConcat".to_string(),
562                        vec![group_array, sep],
563                    ))))
564                } else {
565                    Ok(Expression::Function(Box::new(Function::new(
566                        "arrayStringConcat".to_string(),
567                        vec![group_array],
568                    ))))
569                }
570            }
571
572            // LISTAGG -> groupArray + arrayStringConcat
573            "LISTAGG" if !f.args.is_empty() => {
574                let mut args = f.args;
575                let first = args.remove(0);
576                let separator = args.pop();
577                let group_array = Expression::Function(Box::new(Function::new(
578                    "groupArray".to_string(),
579                    vec![first],
580                )));
581                if let Some(sep) = separator {
582                    Ok(Expression::Function(Box::new(Function::new(
583                        "arrayStringConcat".to_string(),
584                        vec![group_array, sep],
585                    ))))
586                } else {
587                    Ok(Expression::Function(Box::new(Function::new(
588                        "arrayStringConcat".to_string(),
589                        vec![group_array],
590                    ))))
591                }
592            }
593
594            // ARRAY_AGG -> groupArray
595            "ARRAY_AGG" if !f.args.is_empty() => Ok(Expression::Function(Box::new(Function::new(
596                "groupArray".to_string(),
597                f.args,
598            )))),
599
600            // STDDEV -> stddevSamp in ClickHouse (sample stddev)
601            "STDDEV" if !f.args.is_empty() => {
602                Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
603                    name: "stddevSamp".to_string(),
604                    args: f.args,
605                    distinct: f.distinct,
606                    filter: f.filter,
607                    order_by: Vec::new(),
608                    limit: None,
609                    ignore_nulls: None,
610                    inferred_type: None,
611                })))
612            }
613
614            // STDDEV_POP -> stddevPop
615            "STDDEV_POP" if !f.args.is_empty() => {
616                Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
617                    name: "stddevPop".to_string(),
618                    args: f.args,
619                    distinct: f.distinct,
620                    filter: f.filter,
621                    order_by: Vec::new(),
622                    limit: None,
623                    ignore_nulls: None,
624                    inferred_type: None,
625                })))
626            }
627
628            // VARIANCE -> varSamp in ClickHouse
629            "VARIANCE" if !f.args.is_empty() => {
630                Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
631                    name: "varSamp".to_string(),
632                    args: f.args,
633                    distinct: f.distinct,
634                    filter: f.filter,
635                    order_by: Vec::new(),
636                    limit: None,
637                    ignore_nulls: None,
638                    inferred_type: None,
639                })))
640            }
641
642            // VAR_POP -> varPop
643            "VAR_POP" if !f.args.is_empty() => {
644                Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
645                    name: "varPop".to_string(),
646                    args: f.args,
647                    distinct: f.distinct,
648                    filter: f.filter,
649                    order_by: Vec::new(),
650                    limit: None,
651                    ignore_nulls: None,
652                    inferred_type: None,
653                })))
654            }
655
656            // MEDIAN -> median
657            "MEDIAN" if !f.args.is_empty() => {
658                Ok(Expression::AggregateFunction(Box::new(AggregateFunction {
659                    name: "median".to_string(),
660                    args: f.args,
661                    distinct: f.distinct,
662                    filter: f.filter,
663                    order_by: Vec::new(),
664                    limit: None,
665                    ignore_nulls: None,
666                    inferred_type: None,
667                })))
668            }
669
670            // APPROX_COUNT_DISTINCT -> uniq in ClickHouse
671            "APPROX_COUNT_DISTINCT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
672                Function::new("uniq".to_string(), f.args),
673            ))),
674
675            // APPROX_DISTINCT -> uniq
676            "APPROX_DISTINCT" if !f.args.is_empty() => Ok(Expression::Function(Box::new(
677                Function::new("uniq".to_string(), f.args),
678            ))),
679
680            _ => Ok(Expression::AggregateFunction(f)),
681        }
682    }
683
684    fn transform_cast(&self, c: Cast) -> Result<Expression> {
685        Ok(Expression::Cast(Box::new(c)))
686    }
687
688    fn wrap_nested_xor_arg(expr: Expression) -> Expression {
689        if matches!(&expr, Expression::Function(f) if f.name.eq_ignore_ascii_case("XOR")) {
690            Expression::Paren(Box::new(Paren {
691                this: expr,
692                trailing_comments: Vec::new(),
693            }))
694        } else {
695            expr
696        }
697    }
698}