fix(parser): fix incorrectly identified directives (#1885)

Parser incorrectly identifies string literals as directives if they
follow after `import`s, `export`s, or decorators.

In all of these cases, `'use strict'` produces a directive in the AST,
where it should be parsed as an `ExpressionStatement` containing a
`StringLiteral`:

```js
import x from 'foo';
'use strict';
```

```js
export {x};
'use strict';
```

```js
@foo
'use strict';
```


[Playground](https://oxc-project.github.io/oxc/playground/?code=3YCAAIC0gICAgICAgIC0G8rnONK89ITJ3zrK%2FUP7OmSZPgHQzStr3yMtwFTU%2BD1WPt09JgqZJLoYooydbGsM5vGcf34BnIA%3D)

This PR should fix that.

I'm not sure about the decorator case, though. I assume it's not a
directive. But is prefixing a string literal with a decorator even legal
syntax anyway?

And a side nit: If I'm reading it right, I don't think the `continue`
statement in the decorator arm of the match does anything. Do I have
that right?

Last question: Where does one go about putting a test? I guess these
silly cases aren't covered by Babel etc's tests.

---------

Co-authored-by: Boshen <boshenc@gmail.com>
This commit is contained in:
overlookmotel 2024-01-04 13:39:15 +00:00 committed by GitHub
parent 11ca5c2bad
commit eb2966c512
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 0 deletions

View file

@ -44,13 +44,16 @@ impl<'a> Parser<'a> {
Kind::Import if !matches!(self.peek_kind(), Kind::Dot | Kind::LParen) => {
let stmt = self.parse_import_declaration()?;
statements.push(stmt);
expecting_directives = false;
}
Kind::Export => {
let stmt = self.parse_export_declaration()?;
statements.push(stmt);
expecting_directives = false;
}
Kind::At => {
self.eat_decorators()?;
expecting_directives = false;
continue;
}
_ => {

View file

@ -300,6 +300,22 @@ mod test {
assert_eq!(ret.errors.first().unwrap().to_string(), "Flow is not supported");
}
#[test]
fn directives() {
let allocator = Allocator::default();
let source_type = SourceType::default();
let sources = [
("import x from 'foo'; 'use strict';", 2),
("export {x} from 'foo'; 'use strict';", 2),
("@decorator 'use strict';", 1),
];
for (source, body_length) in sources {
let ret = Parser::new(&allocator, source, source_type).parse();
assert!(ret.program.directives.is_empty(), "{source}");
assert_eq!(ret.program.body.len(), body_length, "{source}");
}
}
// Source with length u32::MAX + 1 fails to parse
#[test]
fn overlong_source() {