Go to file
Aleksey Kladov a5c333c3ed Fix yet another parser infinite loop
This commit is an example of fixing a common parser error: infinite
loop due to error recovery.

This error typically happens when we parse a list of items and fail to
parse a specific item at the current position.

One choices is to skip a token and try to parse a list item at the
next position. This is a good, but not universal, default. When
parsing a list of arguments in a function call, you, for example,
don't want to skip over `fn`, because it's most likely that it is a
function declaration, and not a mistyped arg:

```
fn foo() {
    quux(1, 2

fn bar() {
}
```

Another choice is to bail out of the loop immediately, but it isn't
perfect either: sometimes skipping over garbage helps:

```
quux(1, foo:, 92) // should skip over `:`, b/c that's part of `foo::bar`
```

In general, parser tries to balance these two cases, though we don't
have a definitive strategy yet.

However, if the parser accidentally neither skips over a token, nor
breaks out of the loop, then it becomes stuck in the loop infinitely
(there's an internal counter to self-check this situation and panic
though), and that's exactly what is demonstrated by the test.

To fix such situation, first of all, add the test case to tests/data/parser/{err,fuzz-failures}.

Then, run

```
RUST_BACKTRACE=short cargo test --package libsyntax2
````

to verify that parser indeed panics, and to get an idea what grammar
production is the culprit (look for `_list` functions!).

In this case, I see

```
  10: libsyntax2::grammar::expressions::atom::match_arm_list
             at crates/libsyntax2/src/grammar/expressions/atom.rs:309
```

and that's look like it might be a culprit. I verify it by adding
`eprintln!("loopy {:?}", p.current());` and indeed I see that this is
printed repeatedly.

Diagnosing this a bit shows that the problem is that
`pattern::pattern` function does not consume anything if the next
token is `let`. That is a good default to make cases like

```
let
let foo = 92;
```

where the user hasn't typed the pattern yet, to parse in a reasonable
they correctly.

For match arms, pretty much the single thing we expect is a pattern,
so, for a fix, I introduce a special variant of pattern that does not
do recovery.
2018-09-08 19:10:40 +03:00
.cargo cmd to install code extension 2018-07-30 22:17:33 +03:00
code move 2018-08-30 12:52:21 +03:00
crates Fix yet another parser infinite loop 2018-09-08 19:10:40 +03:00
.gitignore More symbols 2018-08-11 16:20:37 +03:00
.travis.yml no time to explain, disable clippy checks 2018-08-29 11:16:28 +03:00
appveyor.yml Don\'t block on rustfmt 2018-07-30 15:07:41 +03:00
bors.toml Let bors to cleanup branches 2018-01-28 12:49:42 +03:00
Cargo.toml matching brace 2018-08-16 00:23:22 +03:00
CONTRIBUTING.md Fix link 2018-01-28 00:35:17 +03:00
LICENSE-APACHE Licenses 2018-01-10 22:47:04 +03:00
LICENSE-MIT Licenses 2018-01-10 22:47:04 +03:00
README.md perf-info 2018-08-24 18:32:48 +03:00
rustfmt.toml Enforce rustfmt format 2018-01-27 18:31:23 -05:00

libsyntax2.0

Build Status Build status

libsyntax2.0 is an experimental parser of the Rust language, intended for the use in IDEs. RFC.

Quick Start

$ cargo test
$ cargo parse < crates/libsyntax2/src/lib.rs

Trying It Out

This installs experimental VS Code plugin

$ cargo install-code

It's better to remove existing Rust plugins to avoid interference. Warning: plugin is not intended for general use, has a lot of rough edges and missing features (notably, no code completion). That said, while originally libsyntax2 was developed in IntelliJ, @matklad now uses this plugin (and thus, libsytax2) to develop libsyntax2, and it doesn't hurt too much :-)

Features:

  • syntax highlighting (LSP does not have API for it, so impl is hacky and sometimes fall-backs to the horrible built-in highlighting)

  • commands (ctrl+shift+p or keybindings)

    • Show Rust Syntax Tree (use it to verify that plugin works)
    • Rust Extend Selection (works with multiple cursors)
    • Rust Matching Brace (knows the difference between < and <)
    • Rust Parent Module
    • Rust Join Lines (deals with trailing commas)
  • Go to symbol in file

  • Go to symbol in workspace (no support for Cargo deps yet)

  • code actions:

    • Flip , in comma separated lists
    • Add #[derive] to struct/enum
    • Add impl block to struct/enum
    • Run tests at caret
  • Go to definition ("correct" for mod foo; decls, index-based for functions).

Code Walk-Through

crates/libsyntax2

  • yellow, red/green syntax tree, heavily inspired by this
  • grammar, the actual parser
  • parser_api/parser_impl bridges the tree-agnostic parser from grammar with yellow trees
  • grammar.ron RON description of the grammar, which is used to generate syntax_kinds and ast modules.
  • algo: generic tree algorithms, including walk for O(1) stack space tree traversal (this is cool) and visit for type-driven visiting the nodes (this is double plus cool, if you understand how Visitor works, you understand libsyntax2).

crates/libeditor

Most of IDE features leave here, unlike libanalysis, libeditor is single-file and is basically a bunch of pure functions.

crates/libanalysis

A stateful library for analyzing many Rust files as they change. WorldState is a mutable entity (clojure's atom) which holds current state, incorporates changes and handles out Worlds --- immutable consistent snapshots of WorldState, which actually power analysis.

crates/server

An LSP implementation which uses libanalysis for managing state and libeditor for actually doing useful stuff.

crates/cli

A CLI interface to libsyntax

crate/tools

Code-gen tasks, used to develop libsyntax2:

  • cargo gen-kinds -- generate ast and syntax_kinds
  • cargo gen-tests -- collect inline tests from grammar
  • cargo install-code -- build and install VS Code extension and server

code

VS Code plugin

Performance

Non-incremental, but seems pretty fast:

$ cargo build --release --package cli
$ wc -l ~/projects/rust/src/libsyntax/parse/parser.rs
7546 /home/matklad/projects/rust/src/libsyntax/parse/parser.rs
$ ./target/release/cli parse < ~/projects/rust/src/libsyntax/parse/parser.rs --no-dump  > /dev/null
parsing: 21.067065ms

Getting in touch

@matklad can be found at Rust discord, in #ides-and-editors.

License

libsyntax2 is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0).

See LICENSE-APACHE and LICENSE-MIT for details.