swc_fast_ts_strip/
lib.rs

1use std::{cell::RefCell, fmt::Display, rc::Rc};
2
3use anyhow::Context;
4use serde::{Deserialize, Serialize};
5use swc_common::{
6    comments::SingleThreadedComments,
7    errors::{Handler, HANDLER},
8    source_map::DefaultSourceMapGenConfig,
9    sync::Lrc,
10    BytePos, FileName, Mark, SourceMap, Span, Spanned,
11};
12use swc_ecma_ast::{
13    ArrayPat, ArrowExpr, AutoAccessor, BindingIdent, Class, ClassDecl, ClassMethod, ClassProp,
14    Constructor, Decl, DefaultDecl, DoWhileStmt, EsVersion, ExportAll, ExportDecl,
15    ExportDefaultDecl, ExportSpecifier, FnDecl, ForInStmt, ForOfStmt, ForStmt, GetterProp, IfStmt,
16    ImportDecl, ImportSpecifier, ModuleDecl, ModuleItem, NamedExport, ObjectPat, Param, Pat,
17    PrivateMethod, PrivateProp, Program, ReturnStmt, SetterProp, Stmt, ThrowStmt, TsAsExpr,
18    TsConstAssertion, TsEnumDecl, TsExportAssignment, TsImportEqualsDecl, TsIndexSignature,
19    TsInstantiation, TsModuleDecl, TsModuleName, TsNamespaceBody, TsNonNullExpr, TsParamPropParam,
20    TsSatisfiesExpr, TsTypeAliasDecl, TsTypeAnn, TsTypeAssertion, TsTypeParamDecl,
21    TsTypeParamInstantiation, VarDeclarator, WhileStmt, YieldExpr,
22};
23use swc_ecma_parser::{
24    lexer::Lexer,
25    token::{BinOpToken, IdentLike, KnownIdent, Token, TokenAndSpan, Word},
26    Capturing, Parser, StringInput, Syntax, TsSyntax,
27};
28use swc_ecma_transforms_base::{
29    fixer::fixer,
30    helpers::{inject_helpers, Helpers, HELPERS},
31    hygiene::hygiene,
32    resolver,
33};
34use swc_ecma_transforms_typescript::typescript;
35use swc_ecma_visit::{Visit, VisitWith};
36#[cfg(feature = "wasm-bindgen")]
37use wasm_bindgen::prelude::*;
38
39#[derive(Default, Deserialize)]
40#[serde(rename_all = "camelCase")]
41pub struct Options {
42    #[serde(default)]
43    pub module: Option<bool>,
44    #[serde(default)]
45    pub filename: Option<String>,
46
47    #[serde(default = "default_ts_syntax")]
48    pub parser: TsSyntax,
49
50    #[serde(default)]
51    pub mode: Mode,
52
53    #[serde(default)]
54    pub transform: Option<typescript::Config>,
55
56    #[serde(default)]
57    pub deprecated_ts_module_as_error: Option<bool>,
58
59    #[serde(default)]
60    pub source_map: bool,
61}
62
63#[cfg(feature = "wasm-bindgen")]
64#[wasm_bindgen(typescript_custom_section)]
65const Type_Options: &'static str = r#"
66interface Options {
67    module?: boolean;
68    filename?: string;
69    mode?: Mode;
70    transform?: TransformConfig;
71    deprecatedTsModuleAsError?: boolean;
72    sourceMap?: boolean;
73}
74
75interface TransformConfig {
76    /**
77     * @see https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax
78     */
79    verbatimModuleSyntax?: boolean;
80    /**
81     * Native class properties support
82     */
83    nativeClassProperties?: boolean;
84    importNotUsedAsValues?: "remove" | "preserve";
85    /**
86     * Don't create `export {}`.
87     * By default, strip creates `export {}` for modules to preserve module
88     * context.
89     * 
90     * @see https://github.com/swc-project/swc/issues/1698
91     */
92    noEmptyExport?: boolean;
93    importExportAssignConfig?: "Classic" | "Preserve" | "NodeNext" | "EsNext";
94    /**
95     * Disables an optimization that inlines TS enum member values
96     * within the same module that assumes the enum member values
97     * are never modified.
98     * 
99     * Defaults to false.
100     */
101    tsEnumIsMutable?: boolean;
102}
103"#;
104
105#[derive(Debug, Default, Deserialize)]
106#[serde(rename_all = "kebab-case")]
107pub enum Mode {
108    #[default]
109    StripOnly,
110    Transform,
111}
112
113#[cfg(feature = "wasm-bindgen")]
114#[wasm_bindgen(typescript_custom_section)]
115const Type_Mode: &'static str = r#"
116type Mode = "strip-only" | "transform";
117"#;
118
119fn default_ts_syntax() -> TsSyntax {
120    TsSyntax {
121        decorators: true,
122        ..Default::default()
123    }
124}
125
126#[derive(Debug, Serialize)]
127pub struct TransformOutput {
128    pub code: String,
129    pub map: Option<String>,
130}
131
132#[cfg(feature = "wasm-bindgen")]
133#[wasm_bindgen(typescript_custom_section)]
134const Type_TransformOutput: &'static str = r#"
135interface TransformOutput {
136    code: string;
137    map?: string;
138}
139"#;
140
141#[derive(Debug, Serialize)]
142pub struct TsError {
143    pub message: String,
144    pub code: ErrorCode,
145}
146
147impl std::fmt::Display for TsError {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        write!(f, "[{}] {}", self.code, self.message)
150    }
151}
152
153impl std::error::Error for TsError {
154    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
155        None
156    }
157}
158
159#[non_exhaustive]
160#[derive(Debug, Clone, Copy, Serialize)]
161pub enum ErrorCode {
162    InvalidSyntax,
163    UnsupportedSyntax,
164    Unknown,
165}
166
167impl Display for ErrorCode {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        write!(f, "{:?}", self)
170    }
171}
172
173impl From<anyhow::Error> for TsError {
174    fn from(err: anyhow::Error) -> Self {
175        TsError {
176            message: err.to_string(),
177            code: ErrorCode::Unknown,
178        }
179    }
180}
181
182pub fn operate(
183    cm: &Lrc<SourceMap>,
184    handler: &Handler,
185    input: String,
186    options: Options,
187) -> Result<TransformOutput, TsError> {
188    let filename = options
189        .filename
190        .map_or(FileName::Anon, |f| FileName::Real(f.into()));
191
192    let fm = cm.new_source_file(filename.into(), input);
193
194    let syntax = Syntax::Typescript(options.parser);
195    let target = EsVersion::latest();
196
197    let comments = SingleThreadedComments::default();
198
199    let lexer = Capturing::new(Lexer::new(
200        syntax,
201        target,
202        StringInput::from(&*fm),
203        Some(&comments),
204    ));
205    let tokens = lexer.tokens().clone();
206
207    let mut parser = Parser::new_from(lexer);
208
209    let program = match options.module {
210        Some(true) => parser.parse_module().map(Program::Module),
211        Some(false) => parser.parse_script().map(Program::Script),
212        None => parser.parse_program(),
213    };
214    let errors = parser.take_errors();
215
216    let mut program = match program {
217        Ok(program) => program,
218        Err(err) => {
219            err.into_diagnostic(handler).emit();
220
221            for e in errors {
222                e.into_diagnostic(handler).emit();
223            }
224
225            return Err(TsError {
226                message: "Syntax error".to_string(),
227                code: ErrorCode::InvalidSyntax,
228            });
229        }
230    };
231
232    if !errors.is_empty() {
233        for e in errors {
234            e.into_diagnostic(handler).emit();
235        }
236
237        return Err(TsError {
238            message: "Syntax error".to_string(),
239            code: ErrorCode::InvalidSyntax,
240        });
241    }
242
243    drop(parser);
244
245    let deprecated_ts_module_as_error = options.deprecated_ts_module_as_error.unwrap_or_default();
246
247    match options.mode {
248        Mode::StripOnly => {
249            let mut tokens = RefCell::into_inner(Rc::try_unwrap(tokens).unwrap());
250
251            tokens.sort_by_key(|t| t.span);
252
253            if deprecated_ts_module_as_error {
254                program.visit_with(&mut ErrorOnTsModule {
255                    src: &fm.src,
256                    tokens: &tokens,
257                });
258            }
259
260            // Strip typescript types
261            let mut ts_strip = TsStrip::new(fm.src.clone(), tokens);
262
263            program.visit_with(&mut ts_strip);
264            if handler.has_errors() {
265                return Err(TsError {
266                    message: "Unsupported syntax".to_string(),
267                    code: ErrorCode::UnsupportedSyntax,
268                });
269            }
270
271            let replacements = ts_strip.replacements;
272            let overwrites = ts_strip.overwrites;
273
274            if replacements.is_empty() && overwrites.is_empty() {
275                return Ok(TransformOutput {
276                    code: fm.src.to_string(),
277                    map: Default::default(),
278                });
279            }
280
281            let source = fm.src.clone();
282            let mut code = fm.src.to_string().into_bytes();
283
284            for r in replacements {
285                let (start, end) = (r.0 .0 as usize - 1, r.1 .0 as usize - 1);
286
287                for (i, c) in source[start..end].char_indices() {
288                    let i = start + i;
289                    match c {
290                        // https://262.ecma-international.org/#sec-white-space
291                        '\u{0009}' | '\u{0000B}' | '\u{000C}' | '\u{FEFF}' => continue,
292                        // Space_Separator
293                        '\u{0020}' | '\u{00A0}' | '\u{1680}' | '\u{2000}' | '\u{2001}'
294                        | '\u{2002}' | '\u{2003}' | '\u{2004}' | '\u{2005}' | '\u{2006}'
295                        | '\u{2007}' | '\u{2008}' | '\u{2009}' | '\u{200A}' | '\u{202F}'
296                        | '\u{205F}' | '\u{3000}' => continue,
297                        // https://262.ecma-international.org/#sec-line-terminators
298                        '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}' => continue,
299                        _ => match c.len_utf8() {
300                            1 => {
301                                // Space 0020
302                                code[i] = 0x20;
303                            }
304                            2 => {
305                                // No-Break Space 00A0
306                                code[i] = 0xc2;
307                                code[i + 1] = 0xa0;
308                            }
309                            3 => {
310                                // En Space 2002
311                                code[i] = 0xe2;
312                                code[i + 1] = 0x80;
313                                code[i + 2] = 0x82;
314                            }
315                            4 => {
316                                // We do not have a 4-byte space character in the Unicode standard.
317
318                                // Space 0020
319                                code[i] = 0x20;
320                                // ZWNBSP FEFF
321                                code[i + 1] = 0xef;
322                                code[i + 2] = 0xbb;
323                                code[i + 3] = 0xbf;
324                            }
325                            _ => unreachable!(),
326                        },
327                    }
328                }
329            }
330
331            for (i, v) in overwrites {
332                code[i.0 as usize - 1] = v;
333            }
334
335            let code = if cfg!(debug_assertions) {
336                String::from_utf8(code).map_err(|err| TsError {
337                    message: format!("failed to convert to utf-8: {}", err),
338                    code: ErrorCode::Unknown,
339                })?
340            } else {
341                // SAFETY: We've already validated that the source is valid utf-8
342                // and our operations are limited to character-level string replacements.
343                unsafe { String::from_utf8_unchecked(code) }
344            };
345
346            Ok(TransformOutput {
347                code,
348                map: Default::default(),
349            })
350        }
351
352        Mode::Transform => {
353            let unresolved_mark = Mark::new();
354            let top_level_mark = Mark::new();
355
356            HELPERS.set(&Helpers::new(false), || {
357                program.mutate(&mut resolver(unresolved_mark, top_level_mark, true));
358
359                if deprecated_ts_module_as_error {
360                    let mut tokens = RefCell::into_inner(Rc::try_unwrap(tokens).unwrap());
361
362                    tokens.sort_by_key(|t| t.span);
363
364                    program.visit_with(&mut ErrorOnTsModule {
365                        src: &fm.src,
366                        tokens: &tokens,
367                    });
368                }
369
370                program.mutate(&mut typescript::typescript(
371                    options.transform.unwrap_or_default(),
372                    unresolved_mark,
373                    top_level_mark,
374                ));
375
376                program.mutate(&mut inject_helpers(unresolved_mark));
377
378                program.mutate(&mut hygiene());
379
380                program.mutate(&mut fixer(Some(&comments)));
381            });
382
383            let mut src = std::vec::Vec::new();
384            let mut src_map_buf = if options.source_map {
385                Some(Vec::new())
386            } else {
387                None
388            };
389
390            {
391                let mut emitter = swc_ecma_codegen::Emitter {
392                    cfg: swc_ecma_codegen::Config::default(),
393                    comments: if options.source_map {
394                        Some(&comments)
395                    } else {
396                        None
397                    },
398                    cm: cm.clone(),
399                    wr: swc_ecma_codegen::text_writer::JsWriter::new(
400                        cm.clone(),
401                        "\n",
402                        &mut src,
403                        src_map_buf.as_mut(),
404                    ),
405                };
406
407                emitter.emit_program(&program).unwrap();
408
409                let map = src_map_buf
410                    .map(|map| {
411                        let map =
412                            cm.build_source_map_with_config(&map, None, DefaultSourceMapGenConfig);
413
414                        let mut s = std::vec::Vec::new();
415                        map.to_writer(&mut s)
416                            .context("failed to write source map")?;
417
418                        String::from_utf8(s).context("source map was not utf8")
419                    })
420                    .transpose()?;
421
422                Ok(TransformOutput {
423                    code: String::from_utf8(src).context("generated code was not utf-8")?,
424                    map,
425                })
426            }
427        }
428    }
429}
430
431struct ErrorOnTsModule<'a> {
432    src: &'a str,
433    tokens: &'a [TokenAndSpan],
434}
435
436// All namespaces or modules are either at the top level or nested within
437// another namespace or module.
438impl Visit for ErrorOnTsModule<'_> {
439    fn visit_stmt(&mut self, n: &Stmt) {
440        if n.is_decl() {
441            n.visit_children_with(self);
442        }
443    }
444
445    fn visit_decl(&mut self, n: &Decl) {
446        if n.is_ts_module() {
447            n.visit_children_with(self);
448        }
449    }
450
451    fn visit_module_decl(&mut self, n: &ModuleDecl) {
452        if n.is_export_decl() {
453            n.visit_children_with(self);
454        }
455    }
456
457    fn visit_ts_module_decl(&mut self, n: &TsModuleDecl) {
458        n.visit_children_with(self);
459
460        if n.global || n.id.is_str() {
461            return;
462        }
463
464        let mut pos = n.span.lo;
465
466        if n.declare {
467            let declare_index = self
468                .tokens
469                .binary_search_by_key(&pos, |t| t.span.lo)
470                .unwrap();
471
472            debug_assert_eq!(
473                self.tokens[declare_index].token,
474                Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Declare)))
475            );
476
477            let TokenAndSpan { token, span, .. } = &self.tokens[declare_index + 1];
478            // declare global
479            // declare module
480            // declare namespace
481            if let Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Namespace))) = token {
482                return;
483            }
484
485            pos = span.lo;
486        } else if self.src.as_bytes()[pos.0 as usize - 1] != b'm' {
487            return;
488        }
489
490        HANDLER.with(|handler| {
491            handler
492                .struct_span_err(
493                    span(pos, pos + BytePos(6)),
494                    "`module` keyword is not supported. Use `namespace` instead.",
495                )
496                .emit();
497        });
498    }
499}
500
501struct TsStrip {
502    src: Lrc<String>,
503
504    /// Replaced with whitespace
505    replacements: Vec<(BytePos, BytePos)>,
506
507    // should be string, but we use u8 for only `)` usage.
508    overwrites: Vec<(BytePos, u8)>,
509
510    tokens: std::vec::Vec<TokenAndSpan>,
511}
512
513impl TsStrip {
514    fn new(src: Lrc<String>, tokens: std::vec::Vec<TokenAndSpan>) -> Self {
515        TsStrip {
516            src,
517            replacements: Default::default(),
518            overwrites: Default::default(),
519            tokens,
520        }
521    }
522}
523
524impl TsStrip {
525    fn add_replacement(&mut self, span: Span) {
526        self.replacements.push((span.lo, span.hi));
527    }
528
529    fn add_overwrite(&mut self, pos: BytePos, value: u8) {
530        self.overwrites.push((pos, value));
531    }
532
533    fn get_src_slice(&self, span: Span) -> &str {
534        &self.src[(span.lo.0 - 1) as usize..(span.hi.0 - 1) as usize]
535    }
536
537    fn get_next_token_index(&self, pos: BytePos) -> usize {
538        let index = self.tokens.binary_search_by_key(&pos, |t| t.span.lo);
539        match index {
540            Ok(index) => index,
541            Err(index) => index,
542        }
543    }
544
545    fn get_next_token(&self, pos: BytePos) -> &TokenAndSpan {
546        &self.tokens[self.get_next_token_index(pos)]
547    }
548
549    fn get_prev_token_index(&self, pos: BytePos) -> usize {
550        let index = self.tokens.binary_search_by_key(&pos, |t| t.span.lo);
551        match index {
552            Ok(index) => index,
553            Err(index) => index - 1,
554        }
555    }
556
557    fn get_prev_token(&self, pos: BytePos) -> &TokenAndSpan {
558        &self.tokens[self.get_prev_token_index(pos)]
559    }
560
561    fn fix_asi(&mut self, span: Span) {
562        let index = self.get_prev_token_index(span.lo);
563        if index == 0 {
564            // Skip if the token is the first token.
565            return;
566        }
567
568        let TokenAndSpan {
569            token: prev_token,
570            span: prev_span,
571            ..
572        } = &self.tokens[index - 1];
573
574        let index = self.get_prev_token_index(span.hi - BytePos(1));
575        if index == self.tokens.len() - 1 {
576            // Skip if the token is the last token.
577            return;
578        }
579
580        let TokenAndSpan {
581            token,
582            had_line_break,
583            ..
584        } = &self.tokens[index + 1];
585
586        if !*had_line_break {
587            return;
588        }
589
590        // https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#sec-asi-interesting-cases-in-statement-lists
591        // Add a semicolon if the next token is `[`, `(`, `/`, `+`, `-` or backtick.
592        match token {
593            Token::LParen
594            | Token::LBracket
595            | Token::BackQuote
596            | Token::BinOp(BinOpToken::Add | BinOpToken::Sub | BinOpToken::Div) => {
597                if prev_token == &Token::Semi {
598                    self.add_overwrite(prev_span.lo, b';');
599                    return;
600                }
601
602                self.add_overwrite(span.lo, b';');
603            }
604
605            _ => {}
606        }
607    }
608
609    fn fix_asi_in_expr(&mut self, span: Span) {
610        let index = self.get_prev_token_index(span.hi - BytePos(1));
611        if index == self.tokens.len() - 1 {
612            return;
613        }
614
615        if let TokenAndSpan {
616            // Only `(`, `[` and backtick affect ASI.
617            token: Token::LParen | Token::LBracket | Token::BackQuote,
618            had_line_break: true,
619            ..
620        } = &self.tokens[index + 1]
621        {
622            self.add_overwrite(span.lo, b';');
623        }
624    }
625
626    fn strip_class_modifier(&mut self, mut start_pos: BytePos, key_pos: BytePos) {
627        let mut index = self.get_next_token_index(start_pos);
628
629        while start_pos < key_pos {
630            let TokenAndSpan { token, span, .. } = &self.tokens[index];
631            start_pos = span.hi;
632            index += 1;
633
634            let next = &self.tokens[index];
635
636            if next.had_line_break {
637                return;
638            }
639
640            // see ts_next_token_can_follow_modifier
641            // class { public public() {} }
642            if !matches!(
643                next.token,
644                Token::LBracket
645                    | Token::LBrace
646                    | Token::BinOp(BinOpToken::Mul)
647                    | Token::DotDotDot
648                    | Token::Hash
649                    | Token::Word(_)
650                    | Token::Str { .. }
651                    | Token::Num { .. }
652                    | Token::BigInt { .. }
653            ) {
654                return;
655            }
656
657            match token {
658                Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Static))) => {
659                    continue;
660                }
661                Token::Word(Word::Ident(IdentLike::Known(
662                    KnownIdent::Readonly
663                    | KnownIdent::Public
664                    | KnownIdent::Protected
665                    | KnownIdent::Private,
666                ))) => {
667                    self.add_replacement(*span);
668                }
669                Token::Word(Word::Ident(IdentLike::Other(o))) if *o == "override" => {
670                    self.add_replacement(*span);
671                }
672                _ => {
673                    return;
674                }
675            }
676        }
677    }
678
679    fn strip_definite_mark(&mut self, index: usize) {
680        self.strip_token(index, Token::Bang);
681    }
682
683    fn strip_optional_mark(&mut self, index: usize) {
684        self.strip_token(index, Token::QuestionMark);
685    }
686
687    fn strip_token(&mut self, index: usize, expected: Token) {
688        let TokenAndSpan { token, span, .. } = &self.tokens[index];
689        debug_assert_eq!(*token, expected);
690
691        self.add_replacement(*span);
692    }
693
694    // ```TypeScript
695    // return/yield/throw <T>
696    //     (v: T) => v;
697    // ```
698    //
699    // ```TypeScript
700    // return/yield/throw (
701    //      v   ) => v;
702    // ```
703    fn fix_asi_in_arrow_expr(&mut self, arrow_expr: &ArrowExpr) {
704        if let Some(tp) = &arrow_expr.type_params {
705            let l_paren = self.get_next_token(tp.span.hi);
706            debug_assert_eq!(l_paren.token, Token::LParen);
707
708            let slice = self.get_src_slice(tp.span.with_hi(l_paren.span.lo));
709
710            if !slice.chars().any(is_new_line) {
711                return;
712            }
713
714            let l_paren_pos = l_paren.span.lo;
715            let l_lt_pos = tp.span.lo;
716
717            self.add_overwrite(l_paren_pos, b' ');
718            self.add_overwrite(l_lt_pos, b'(');
719        }
720    }
721}
722
723impl Visit for TsStrip {
724    fn visit_var_declarator(&mut self, n: &VarDeclarator) {
725        if n.definite {
726            if let Some(id) = n.name.as_ident() {
727                let mark_index = self.get_next_token_index(id.span.hi);
728                self.strip_definite_mark(mark_index);
729            };
730        }
731
732        n.visit_children_with(self);
733    }
734
735    fn visit_arrow_expr(&mut self, n: &ArrowExpr) {
736        'type_params: {
737            // ```TypeScript
738            // let f = async <
739            //    T
740            // >(v: T) => v;
741            // ```
742
743            // ```TypeScript
744            // let f = async (
745            //
746            //   v   ) => v;
747            // ```
748            if let Some(tp) = &n.type_params {
749                self.add_replacement(tp.span);
750
751                if !n.is_async {
752                    break 'type_params;
753                }
754
755                let slice = self.get_src_slice(tp.span);
756                if !slice.chars().any(is_new_line) {
757                    break 'type_params;
758                }
759
760                let l_paren = self.get_next_token(tp.span.hi);
761                debug_assert_eq!(l_paren.token, Token::LParen);
762                let l_paren_pos = l_paren.span.lo;
763                let l_lt_pos = tp.span.lo;
764
765                self.add_overwrite(l_paren_pos, b' ');
766                self.add_overwrite(l_lt_pos, b'(');
767            }
768        }
769
770        if let Some(ret) = &n.return_type {
771            self.add_replacement(ret.span);
772
773            let r_paren = self.get_prev_token(ret.span.lo - BytePos(1));
774            debug_assert_eq!(r_paren.token, Token::RParen);
775            let arrow = self.get_next_token(ret.span.hi);
776            debug_assert_eq!(arrow.token, Token::Arrow);
777            let span = span(r_paren.span.lo, arrow.span.lo);
778
779            let slice = self.get_src_slice(span);
780            if slice.chars().any(is_new_line) {
781                self.add_replacement(r_paren.span);
782
783                // Instead of moving the arrow mark, we shift the right parenthesis to the next
784                // line. This is because there might be a line break after the right
785                // parenthesis, and we wish to preserve the alignment of each line.
786                //
787                // ```TypeScript
788                // ()
789                //     : any =>
790                //     1;
791                // ```
792                //
793                // ```TypeScript
794                // (
795                //         ) =>
796                //     1;
797                // ```
798
799                let mut pos = ret.span.hi - BytePos(1);
800                while !self.src.as_bytes()[pos.0 as usize - 1].is_utf8_char_boundary() {
801                    self.add_overwrite(pos, b' ');
802                    pos = pos - BytePos(1);
803                }
804
805                self.add_overwrite(pos, b')');
806            }
807        }
808
809        n.params.visit_with(self);
810        n.body.visit_with(self);
811    }
812
813    fn visit_return_stmt(&mut self, n: &ReturnStmt) {
814        let Some(arg) = n.arg.as_deref() else {
815            return;
816        };
817
818        arg.visit_with(self);
819
820        let Some(arrow_expr) = arg.as_arrow() else {
821            return;
822        };
823
824        if arrow_expr.is_async {
825            // We have already handled type parameters in `visit_arrow_expr`.
826            return;
827        }
828
829        self.fix_asi_in_arrow_expr(arrow_expr);
830    }
831
832    fn visit_yield_expr(&mut self, n: &YieldExpr) {
833        let Some(arg) = &n.arg else {
834            return;
835        };
836
837        arg.visit_with(self);
838
839        let Some(arrow_expr) = arg.as_arrow() else {
840            return;
841        };
842
843        if arrow_expr.is_async {
844            // We have already handled type parameters in `visit_arrow_expr`.
845            return;
846        }
847
848        self.fix_asi_in_arrow_expr(arrow_expr);
849    }
850
851    fn visit_throw_stmt(&mut self, n: &ThrowStmt) {
852        let arg = &n.arg;
853
854        arg.visit_with(self);
855
856        let Some(arrow_expr) = arg.as_arrow() else {
857            return;
858        };
859
860        if arrow_expr.is_async {
861            // We have already handled type parameters in `visit_arrow_expr`.
862            return;
863        }
864
865        self.fix_asi_in_arrow_expr(arrow_expr);
866    }
867
868    fn visit_binding_ident(&mut self, n: &BindingIdent) {
869        n.visit_children_with(self);
870
871        if n.optional {
872            let mark_index = if let Some(type_ann) = &n.type_ann {
873                self.get_prev_token_index(type_ann.span.lo - BytePos(1))
874            } else {
875                self.get_next_token_index(n.span.hi)
876            };
877
878            self.strip_optional_mark(mark_index);
879        }
880    }
881
882    fn visit_class(&mut self, n: &Class) {
883        if n.is_abstract {
884            let mark_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
885            let r#abstract = self.get_next_token_index(mark_pos);
886
887            self.strip_token(
888                r#abstract,
889                Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Abstract))),
890            )
891        }
892
893        if !n.implements.is_empty() {
894            let implements =
895                self.get_prev_token(n.implements.first().unwrap().span.lo - BytePos(1));
896            debug_assert_eq!(
897                implements.token,
898                Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Implements)))
899            );
900
901            let last = n.implements.last().unwrap();
902            let span = span(implements.span.lo, last.span.hi);
903            self.add_replacement(span);
904        }
905
906        n.visit_children_with(self);
907    }
908
909    fn visit_constructor(&mut self, n: &Constructor) {
910        if n.body.is_none() {
911            self.add_replacement(n.span);
912            return;
913        }
914
915        // TODO(AST): constructor can not be optional
916        debug_assert!(!n.is_optional);
917
918        if n.accessibility.is_some() {
919            self.strip_class_modifier(n.span.lo, n.key.span_lo());
920        }
921
922        n.visit_children_with(self);
923    }
924
925    fn visit_class_method(&mut self, n: &ClassMethod) {
926        if n.function.body.is_none() || n.is_abstract {
927            self.add_replacement(n.span);
928            return;
929        }
930
931        let has_modifier = n.is_override || n.accessibility.is_some();
932
933        // @foo public m(): void {}
934        let start_pos = n
935            .function
936            .decorators
937            .last()
938            .map_or(n.span.lo, |d| d.span.hi);
939
940        if has_modifier {
941            self.strip_class_modifier(start_pos, n.key.span_lo());
942        }
943
944        if n.is_optional {
945            let mark_index = self.get_next_token_index(n.key.span_hi());
946            self.strip_optional_mark(mark_index);
947        }
948
949        // It's dangerous to strip TypeScript modifiers if the key is computed, a
950        // generator, or `in`/`instanceof` keyword. However, it is safe to do so
951        // if the key is preceded by a `static` keyword or decorators.
952        //
953        // `public [foo]()`
954        // `;      [foo]()`
955        //
956        // `public *foo()`
957        // `;      *foo()`
958        //
959        // `public in()`
960        // `;      in()`
961        if has_modifier
962            && !n.is_static
963            && n.function.decorators.is_empty()
964            && (n.key.is_computed()
965                || n.function.is_generator
966                || n.key
967                    .as_ident()
968                    .filter(|k| matches!(k.sym.as_ref(), "in" | "instanceof"))
969                    .is_some())
970        {
971            self.add_overwrite(start_pos, b';');
972        }
973
974        n.visit_children_with(self);
975    }
976
977    fn visit_class_prop(&mut self, n: &ClassProp) {
978        if n.declare || n.is_abstract {
979            self.add_replacement(n.span);
980            return;
981        }
982
983        let has_modifier = n.readonly || n.is_override || n.accessibility.is_some();
984        let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
985
986        if has_modifier {
987            self.strip_class_modifier(start_pos, n.key.span_lo());
988        }
989
990        if n.is_optional {
991            let mark_index = self.get_next_token_index(n.key.span_hi());
992            self.strip_optional_mark(mark_index);
993        }
994        if n.definite {
995            let mark_index = self.get_next_token_index(n.key.span_hi());
996            self.strip_definite_mark(mark_index);
997        }
998
999        // It's dangerous to strip types if the key is `get`, `set`, or `static`.
1000        if n.value.is_none() {
1001            if let Some(key) = n.key.as_ident() {
1002                if matches!(key.sym.as_ref(), "get" | "set" | "static") {
1003                    // `get: number`
1004                    // `get;       `
1005                    if let Some(type_ann) = &n.type_ann {
1006                        self.add_overwrite(type_ann.span.lo, b';');
1007                    }
1008                }
1009            }
1010        }
1011
1012        // `private [foo]`
1013        // `;       [foo]`
1014        //
1015        // `private in`
1016        // `;       in`
1017        if !n.is_static
1018            && has_modifier
1019            && n.decorators.is_empty()
1020            && (n.key.is_computed()
1021                || n.key
1022                    .as_ident()
1023                    .filter(|k| matches!(k.sym.as_ref(), "in" | "instanceof"))
1024                    .is_some())
1025        {
1026            self.add_overwrite(start_pos, b';');
1027        }
1028
1029        n.visit_children_with(self);
1030    }
1031
1032    fn visit_private_method(&mut self, n: &PrivateMethod) {
1033        debug_assert!(!n.is_override);
1034        debug_assert!(!n.is_abstract);
1035
1036        // Is `private #foo()` valid?
1037        if n.accessibility.is_some() {
1038            let start_pos = n
1039                .function
1040                .decorators
1041                .last()
1042                .map_or(n.span.lo, |d| d.span.hi);
1043
1044            self.strip_class_modifier(start_pos, n.key.span.lo);
1045        }
1046
1047        if n.is_optional {
1048            let mark_index = self.get_next_token_index(n.key.span.hi);
1049            self.strip_optional_mark(mark_index);
1050        }
1051
1052        n.visit_children_with(self);
1053    }
1054
1055    fn visit_private_prop(&mut self, n: &PrivateProp) {
1056        debug_assert!(!n.is_override);
1057
1058        if n.readonly || n.accessibility.is_some() {
1059            let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
1060            self.strip_class_modifier(start_pos, n.key.span.lo);
1061        }
1062
1063        if n.is_optional {
1064            let mark_index = self.get_next_token_index(n.key.span.hi);
1065            self.strip_optional_mark(mark_index);
1066        }
1067
1068        if n.definite {
1069            let mark_index = self.get_next_token_index(n.key.span.hi);
1070            self.strip_definite_mark(mark_index);
1071        }
1072
1073        n.visit_children_with(self);
1074    }
1075
1076    fn visit_auto_accessor(&mut self, n: &AutoAccessor) {
1077        if n.is_abstract {
1078            self.add_replacement(n.span);
1079            return;
1080        }
1081
1082        let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
1083
1084        self.strip_class_modifier(start_pos, n.key.span_lo());
1085
1086        if n.definite {
1087            let mark_index = self.get_next_token_index(n.key.span_hi());
1088            self.strip_definite_mark(mark_index);
1089        }
1090
1091        n.visit_children_with(self);
1092    }
1093
1094    fn visit_array_pat(&mut self, n: &ArrayPat) {
1095        if n.optional {
1096            let mark_index = if let Some(type_ann) = &n.type_ann {
1097                self.get_prev_token_index(type_ann.span.lo - BytePos(1))
1098            } else {
1099                self.get_next_token_index(n.span.hi)
1100            };
1101            self.strip_optional_mark(mark_index);
1102        }
1103
1104        n.visit_children_with(self);
1105    }
1106
1107    fn visit_object_pat(&mut self, n: &ObjectPat) {
1108        if n.optional {
1109            let mark_index = if let Some(type_ann) = &n.type_ann {
1110                self.get_prev_token_index(type_ann.span.lo - BytePos(1))
1111            } else {
1112                self.get_next_token_index(n.span.hi)
1113            };
1114            self.strip_optional_mark(mark_index);
1115        }
1116
1117        n.visit_children_with(self);
1118    }
1119
1120    fn visit_export_all(&mut self, n: &ExportAll) {
1121        if n.type_only {
1122            self.add_replacement(n.span);
1123            self.fix_asi(n.span);
1124            return;
1125        }
1126
1127        n.visit_children_with(self);
1128    }
1129
1130    fn visit_export_decl(&mut self, n: &ExportDecl) {
1131        if n.decl.is_ts_declare() || n.decl.is_uninstantiated() {
1132            self.add_replacement(n.span);
1133            self.fix_asi(n.span);
1134            return;
1135        }
1136
1137        n.visit_children_with(self);
1138    }
1139
1140    fn visit_export_default_decl(&mut self, n: &ExportDefaultDecl) {
1141        if n.decl.is_ts_declare() || n.decl.is_uninstantiated() {
1142            self.add_replacement(n.span);
1143            self.fix_asi(n.span);
1144            return;
1145        }
1146
1147        n.visit_children_with(self);
1148    }
1149
1150    fn visit_decl(&mut self, n: &Decl) {
1151        if n.is_ts_declare() || n.is_uninstantiated() {
1152            self.add_replacement(n.span());
1153            self.fix_asi(n.span());
1154            return;
1155        }
1156
1157        n.visit_children_with(self);
1158    }
1159
1160    fn visit_import_decl(&mut self, n: &ImportDecl) {
1161        if n.type_only {
1162            self.add_replacement(n.span);
1163            self.fix_asi(n.span);
1164            return;
1165        }
1166
1167        n.visit_children_with(self);
1168    }
1169
1170    fn visit_import_specifiers(&mut self, n: &[ImportSpecifier]) {
1171        for import in n {
1172            if let ImportSpecifier::Named(import) = import {
1173                if import.is_type_only {
1174                    let mut span = import.span;
1175                    let comma = self.get_next_token(import.span.hi);
1176                    if comma.token == Token::Comma {
1177                        span = span.with_hi(comma.span.hi);
1178                    } else {
1179                        debug_assert_eq!(comma.token, Token::RBrace);
1180                    }
1181                    self.add_replacement(span);
1182                }
1183            }
1184        }
1185    }
1186
1187    fn visit_named_export(&mut self, n: &NamedExport) {
1188        if n.type_only {
1189            self.add_replacement(n.span);
1190            self.fix_asi(n.span);
1191            return;
1192        }
1193
1194        for export in n.specifiers.iter() {
1195            if let ExportSpecifier::Named(e) = export {
1196                if e.is_type_only {
1197                    let mut span = e.span;
1198                    let comma = self.get_next_token(e.span.hi);
1199                    if comma.token == Token::Comma {
1200                        span = span.with_hi(comma.span.hi);
1201                    } else {
1202                        debug_assert_eq!(comma.token, Token::RBrace);
1203                    }
1204                    self.add_replacement(span);
1205                }
1206            }
1207        }
1208    }
1209
1210    fn visit_params(&mut self, n: &[Param]) {
1211        if let Some(p) = n.first().filter(|param| {
1212            matches!(
1213                &param.pat,
1214                Pat::Ident(id) if id.sym == "this"
1215            )
1216        }) {
1217            let mut span = p.span;
1218
1219            let comma = self.get_next_token(span.hi);
1220            if comma.token == Token::Comma {
1221                span = span.with_hi(comma.span.hi);
1222            } else {
1223                debug_assert_eq!(comma.token, Token::RParen);
1224            }
1225            self.add_replacement(span);
1226
1227            n[1..].visit_children_with(self);
1228
1229            return;
1230        }
1231
1232        n.visit_children_with(self);
1233    }
1234
1235    fn visit_ts_as_expr(&mut self, n: &TsAsExpr) {
1236        self.add_replacement(span(n.expr.span().hi, n.span.hi));
1237        let TokenAndSpan {
1238            token,
1239            span: as_span,
1240            ..
1241        } = self.get_next_token(n.expr.span_hi());
1242        debug_assert_eq!(
1243            token,
1244            &Token::Word(Word::Ident(IdentLike::Known(KnownIdent::As)))
1245        );
1246        self.fix_asi_in_expr(span(as_span.lo, n.span.hi));
1247
1248        n.expr.visit_children_with(self);
1249    }
1250
1251    fn visit_ts_const_assertion(&mut self, n: &TsConstAssertion) {
1252        self.add_replacement(span(n.expr.span().hi, n.span.hi));
1253
1254        n.expr.visit_children_with(self);
1255    }
1256
1257    fn visit_ts_export_assignment(&mut self, n: &TsExportAssignment) {
1258        HANDLER.with(|handler| {
1259            handler
1260                .struct_span_err(
1261                    n.span,
1262                    "TypeScript export assignment is not supported in strip-only mode",
1263                )
1264                .emit();
1265        });
1266    }
1267
1268    fn visit_ts_import_equals_decl(&mut self, n: &TsImportEqualsDecl) {
1269        if n.is_type_only {
1270            self.add_replacement(n.span);
1271            self.fix_asi(n.span);
1272            return;
1273        }
1274
1275        HANDLER.with(|handler| {
1276            handler
1277                .struct_span_err(
1278                    n.span,
1279                    "TypeScript import equals declaration is not supported in strip-only mode",
1280                )
1281                .emit();
1282        });
1283    }
1284
1285    fn visit_ts_index_signature(&mut self, n: &TsIndexSignature) {
1286        self.add_replacement(n.span);
1287    }
1288
1289    fn visit_ts_instantiation(&mut self, n: &TsInstantiation) {
1290        self.add_replacement(span(n.expr.span().hi, n.span.hi));
1291
1292        n.expr.visit_children_with(self);
1293    }
1294
1295    fn visit_ts_enum_decl(&mut self, e: &TsEnumDecl) {
1296        HANDLER.with(|handler| {
1297            handler
1298                .struct_span_err(
1299                    e.span,
1300                    "TypeScript enum is not supported in strip-only mode",
1301                )
1302                .emit();
1303        });
1304    }
1305
1306    fn visit_ts_module_decl(&mut self, n: &TsModuleDecl) {
1307        HANDLER.with(|handler| {
1308            handler
1309                .struct_span_err(
1310                    n.span(),
1311                    "TypeScript namespace declaration is not supported in strip-only mode",
1312                )
1313                .emit();
1314        });
1315    }
1316
1317    fn visit_ts_non_null_expr(&mut self, n: &TsNonNullExpr) {
1318        self.add_replacement(span(n.span.hi - BytePos(1), n.span.hi));
1319
1320        n.expr.visit_children_with(self);
1321    }
1322
1323    fn visit_ts_param_prop_param(&mut self, n: &TsParamPropParam) {
1324        HANDLER.with(|handler| {
1325            handler
1326                .struct_span_err(
1327                    n.span(),
1328                    "TypeScript parameter property is not supported in strip-only mode",
1329                )
1330                .emit();
1331        });
1332    }
1333
1334    fn visit_ts_satisfies_expr(&mut self, n: &TsSatisfiesExpr) {
1335        self.add_replacement(span(n.expr.span().hi, n.span.hi));
1336
1337        let TokenAndSpan {
1338            token,
1339            span: as_span,
1340            ..
1341        } = self.get_next_token(n.expr.span_hi());
1342        debug_assert_eq!(
1343            token,
1344            &Token::Word(Word::Ident(IdentLike::Known(KnownIdent::Satisfies)))
1345        );
1346        self.fix_asi_in_expr(span(as_span.lo, n.span.hi));
1347
1348        n.expr.visit_children_with(self);
1349    }
1350
1351    fn visit_ts_type_alias_decl(&mut self, n: &TsTypeAliasDecl) {
1352        self.add_replacement(n.span);
1353        self.fix_asi(n.span);
1354    }
1355
1356    fn visit_ts_type_ann(&mut self, n: &TsTypeAnn) {
1357        self.add_replacement(n.span);
1358    }
1359
1360    /// We do not strip type assertion because it's not safe.
1361    ///
1362    /// See https://github.com/swc-project/swc/issues/9295
1363    fn visit_ts_type_assertion(&mut self, n: &TsTypeAssertion) {
1364        HANDLER.with(|handler| {
1365            handler
1366                .struct_span_err(
1367                    n.span,
1368                    "The angle-bracket syntax for type assertions, `<T>expr`, is not supported in \
1369                     type strip mode. Instead, use the 'as' syntax: `expr as T`.",
1370                )
1371                .emit();
1372        });
1373
1374        n.expr.visit_children_with(self);
1375    }
1376
1377    fn visit_ts_type_param_decl(&mut self, n: &TsTypeParamDecl) {
1378        self.add_replacement(n.span);
1379    }
1380
1381    fn visit_ts_type_param_instantiation(&mut self, n: &TsTypeParamInstantiation) {
1382        self.add_replacement(span(n.span.lo, n.span.hi));
1383    }
1384
1385    fn visit_if_stmt(&mut self, n: &IfStmt) {
1386        n.visit_children_with(self);
1387
1388        if n.cons.is_ts_declare() {
1389            self.add_overwrite(n.cons.span_lo(), b';');
1390        }
1391
1392        if let Some(alt) = &n.alt {
1393            if alt.is_ts_declare() {
1394                self.add_overwrite(alt.span_lo(), b';');
1395            }
1396        }
1397    }
1398
1399    fn visit_for_stmt(&mut self, n: &ForStmt) {
1400        n.visit_children_with(self);
1401
1402        if n.body.is_ts_declare() {
1403            self.add_overwrite(n.body.span_lo(), b';');
1404        }
1405    }
1406
1407    fn visit_for_in_stmt(&mut self, n: &ForInStmt) {
1408        n.visit_children_with(self);
1409
1410        if n.body.is_ts_declare() {
1411            self.add_overwrite(n.body.span_lo(), b';');
1412        }
1413    }
1414
1415    fn visit_for_of_stmt(&mut self, n: &ForOfStmt) {
1416        n.visit_children_with(self);
1417
1418        if n.body.is_ts_declare() {
1419            self.add_overwrite(n.body.span_lo(), b';');
1420        }
1421    }
1422
1423    fn visit_while_stmt(&mut self, n: &WhileStmt) {
1424        n.visit_children_with(self);
1425
1426        if n.body.is_ts_declare() {
1427            self.add_overwrite(n.body.span_lo(), b';');
1428        }
1429    }
1430
1431    fn visit_do_while_stmt(&mut self, n: &DoWhileStmt) {
1432        n.visit_children_with(self);
1433
1434        if n.body.is_ts_declare() {
1435            self.add_overwrite(n.body.span_lo(), b';');
1436        }
1437    }
1438
1439    fn visit_getter_prop(&mut self, n: &GetterProp) {
1440        let l_parern_index = self.get_next_token_index(n.key.span_hi());
1441        let l_parern = &self.tokens[l_parern_index];
1442        debug_assert_eq!(l_parern.token, Token::LParen);
1443
1444        let r_parern_pos = n.type_ann.as_ref().map_or(n.body.span_lo(), |t| t.span.lo) - BytePos(1);
1445        let r_parern = self.get_prev_token(r_parern_pos);
1446        debug_assert_eq!(r_parern.token, Token::RParen);
1447
1448        let span = span(l_parern.span.lo + BytePos(1), r_parern.span.hi - BytePos(1));
1449        self.add_replacement(span);
1450
1451        n.visit_children_with(self);
1452    }
1453
1454    fn visit_setter_prop(&mut self, n: &SetterProp) {
1455        if let Some(this_param) = &n.this_param {
1456            self.add_replacement(this_param.span());
1457
1458            let comma = self.get_prev_token(n.param.span_lo() - BytePos(1));
1459            debug_assert_eq!(comma.token, Token::Comma);
1460
1461            self.add_replacement(comma.span);
1462        }
1463
1464        n.visit_children_with(self);
1465    }
1466}
1467
1468#[inline(always)]
1469fn is_new_line(c: char) -> bool {
1470    matches!(c, '\u{000A}' | '\u{000D}' | '\u{2028}' | '\u{2029}')
1471}
1472trait IsTsDecl {
1473    fn is_ts_declare(&self) -> bool;
1474}
1475
1476impl IsTsDecl for Decl {
1477    fn is_ts_declare(&self) -> bool {
1478        match self {
1479            Self::TsInterface(..) | Self::TsTypeAlias(..) => true,
1480
1481            Self::TsModule(module) => module.declare || matches!(module.id, TsModuleName::Str(..)),
1482            Self::TsEnum(ref r#enum) => r#enum.declare,
1483
1484            Self::Var(ref var) => var.declare,
1485            Self::Fn(FnDecl { declare: true, .. })
1486            | Self::Class(ClassDecl { declare: true, .. }) => true,
1487
1488            Self::Fn(FnDecl { function, .. }) => function.body.is_none(),
1489
1490            _ => false,
1491        }
1492    }
1493}
1494
1495impl IsTsDecl for Stmt {
1496    fn is_ts_declare(&self) -> bool {
1497        self.as_decl().map_or(false, IsTsDecl::is_ts_declare)
1498    }
1499}
1500
1501impl IsTsDecl for DefaultDecl {
1502    fn is_ts_declare(&self) -> bool {
1503        match self {
1504            Self::Class(..) => false,
1505            Self::Fn(r#fn) => r#fn.function.body.is_none(),
1506            Self::TsInterfaceDecl(..) => true,
1507        }
1508    }
1509}
1510
1511trait IsUninstantiated {
1512    fn is_uninstantiated(&self) -> bool;
1513}
1514
1515impl IsUninstantiated for TsNamespaceBody {
1516    fn is_uninstantiated(&self) -> bool {
1517        match self {
1518            Self::TsModuleBlock(block) => {
1519                block.body.iter().all(IsUninstantiated::is_uninstantiated)
1520            }
1521            Self::TsNamespaceDecl(decl) => decl.body.is_uninstantiated(),
1522        }
1523    }
1524}
1525
1526impl IsUninstantiated for ModuleItem {
1527    fn is_uninstantiated(&self) -> bool {
1528        match self {
1529            Self::Stmt(stmt) => stmt.is_uninstantiated(),
1530            Self::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl { decl, .. })) => {
1531                decl.is_uninstantiated()
1532            }
1533            _ => false,
1534        }
1535    }
1536}
1537
1538impl IsUninstantiated for Stmt {
1539    fn is_uninstantiated(&self) -> bool {
1540        matches!(self, Self::Decl(decl) if decl.is_uninstantiated())
1541    }
1542}
1543
1544impl IsUninstantiated for TsModuleDecl {
1545    fn is_uninstantiated(&self) -> bool {
1546        matches!(&self.body, Some(body) if body.is_uninstantiated())
1547    }
1548}
1549
1550impl IsUninstantiated for Decl {
1551    fn is_uninstantiated(&self) -> bool {
1552        match self {
1553            Self::TsInterface(..) | Self::TsTypeAlias(..) => true,
1554            Self::TsModule(module) => module.is_uninstantiated(),
1555            _ => false,
1556        }
1557    }
1558}
1559
1560impl IsUninstantiated for DefaultDecl {
1561    fn is_uninstantiated(&self) -> bool {
1562        matches!(self, Self::TsInterfaceDecl(..))
1563    }
1564}
1565
1566trait U8Helper {
1567    fn is_utf8_char_boundary(&self) -> bool;
1568}
1569
1570impl U8Helper for u8 {
1571    // Copy from std::core::num::u8
1572    #[inline]
1573    fn is_utf8_char_boundary(&self) -> bool {
1574        // This is bit magic equivalent to: b < 128 || b >= 192
1575        (*self as i8) >= -0x40
1576    }
1577}
1578
1579fn span(lo: BytePos, hi: BytePos) -> Span {
1580    Span::new(lo, hi)
1581}