-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
> I removed them because mkdocs will error out if they are not included in navigation. > But looks like they introduced a new [not_in_nav](https://www.mkdocs.org/user-guide/configuration/#not_in_nav) option. I will try using it and if that works! > That would mean they are still built/validated/served, but won't exist in the navigation sidebar. Context: #1121 (comment)
- Loading branch information
1 parent
be9d497
commit df12184
Showing
8 changed files
with
377 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
- [Rust Crate](./index.md) | ||
- [Installation](./installation.md) | ||
- [Using the CLI](./using-the-cli.md) | ||
- [Using the Parser](./using-the-parser.md) | ||
- [Using the Cursor](./using-the-cursor.md) | ||
- [Using Queries](./using-queries.md) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Rust Crate | ||
|
||
- [Installation](./installation.md) | ||
- [Using the CLI](./using-the-cli.md) | ||
- [Using the Parser](./using-the-parser.md) | ||
- [Using the Cursor](./using-the-cursor.md) | ||
- [Using Queries](./using-queries.md) |
16 changes: 16 additions & 0 deletions
16
documentation/public/user-guide/rust-crate/installation.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Installation | ||
|
||
The Rust package is published to crates.io as [`slang_solidity`](https://crates.io/crates/slang_solidity) ([docs](https://docs.rs/slang_solidity/latest/slang_solidity/)). | ||
It can be used both as a regular Rust dependency and as a standalone CLI (installable with Cargo). | ||
|
||
You can install the CLI as a cargo binary using: | ||
|
||
```bash | ||
cargo install "slang_solidity_cli" | ||
``` | ||
|
||
Or you can add the API as a dependency to your project: | ||
|
||
```bash | ||
cargo add "slang_solidity" | ||
``` |
83 changes: 83 additions & 0 deletions
83
documentation/public/user-guide/rust-crate/using-queries.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# Using Queries | ||
|
||
It's often more convenient to use the declarative `Query` API to traverse the CST, as they allow you to express your intent more concisely and can largely replace the need for both internal (cursor), and external (visitor) iterator patterns. | ||
|
||
The [query language](./../tree-query-language.md) is based on pattern matching, and the execution semantics are closer to unification than to regular expression matching. A query returns all possible matches, not just the longest/shortest/first/last match. | ||
|
||
If not specified otherwise, let's assume we already parsed a Solidity source and have a `cursor` pointing to the root node of the CST (created with `create_tree_cursor`, see [Using the Cursor](./using-the-cursor.md)). | ||
|
||
## Creating and executing queries | ||
|
||
You can create a `Query` struct using `Query::parse`, which accepts a `&str`. These can be then used by `Cursor::query` to execute it. | ||
|
||
You can pass multiple queries to a cursor to and efficiently traverse the tree looking for matches. They will be executed concurrently, returning matches in the order they appear in input. | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_queries.rs:creating-a-query" | ||
``` | ||
|
||
## Iterating over node patterns | ||
|
||
Queries allow you to iterate over all node patterns that match the query, which can replace your need for manual iteration via cursors or visitors. In order to get a `Cursor` that points to the matched node, you need to capture them with a name capture (`@capture_name`) to a specific node in the query pattern. | ||
|
||
Let's use this to list all the contract definitions in the source file: | ||
|
||
```solidity title="input.sol" | ||
--8<-- "documentation/public/user-guide/inputs/using-the-cursor.sol" | ||
``` | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_queries.rs:visiting-contracts" | ||
``` | ||
|
||
### Multiple patterns simultaneously | ||
|
||
We can also intersperse multiple patterns in a single query, which will return all the matches for each pattern. This can be useful when you want to match multiple types of nodes in a single pass. | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_queries.rs:multiple-patterns" | ||
``` | ||
|
||
## Matching on node's label | ||
|
||
We can match not only on the node's kind, but also on its label. This can be useful if there may be two children with the same kind but different labels or to be more declarative. | ||
|
||
To do so, we use `[label: _]` syntax. Here, we also use `_` to allow matching any kind of node, as long as it matches the given label. | ||
|
||
```solidity title="input.sol" | ||
--8<-- "documentation/public/user-guide/inputs/typed-tuple.sol" | ||
``` | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_queries.rs:matching-on-label" | ||
``` | ||
|
||
## Matching on node's literal content | ||
|
||
Lastly, we can also match on the node's literal content. This can be useful when you want to match a specific identifier, string, or number. | ||
|
||
Let's say we prefer our code to be explicit and prefer using `uint256` instead of `uint`. To find all instances of the `uint` alias we could do the following: | ||
|
||
```solidity title="input.sol" | ||
--8<-- "documentation/public/user-guide/inputs/typed-tuple.sol" | ||
``` | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_queries.rs:matching-on-literal-value" | ||
``` | ||
|
||
## Example: Finding `tx.origin` patterns | ||
|
||
As a more realistic example, let's say we want to write a linter that unconditionally lints against all [`tx.origin`](https://docs.soliditylang.org/en/latest/security-considerations.html#tx-origin) accesses. | ||
|
||
Let's use the motivating example from [https://soliditylang.org](https://docs.soliditylang.org/en/latest/security-considerations.html#tx-origin): | ||
|
||
```solidity title="input.sol" | ||
--8<-- "documentation/public/user-guide/inputs/tx-origin.sol" | ||
``` | ||
|
||
Now, we can above features to write a query that matches all `tx.origin` patterns: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_queries.rs:tx-origin" | ||
``` |
132 changes: 132 additions & 0 deletions
132
documentation/public/user-guide/rust-crate/using-the-cli.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
# Using the CLI | ||
|
||
## Parsing Source Files | ||
|
||
The `parse` command will take a path to a Solidity file, and a `--version` flag. | ||
Specifying the correct version is important, as it will affect the grammar used to parse inputs. | ||
|
||
All parse errors are printed in a human-readable format; the command will succeed if there are no parse errors, and fail otherwise. | ||
|
||
```bash | ||
$ slang_solidity parse --help | ||
|
||
Usage: slang_solidity parse [OPTIONS] --version <VERSION> <FILE_PATH> | ||
|
||
Arguments: | ||
<FILE_PATH> | ||
File path to the Solidity (*.sol) source file to parse | ||
|
||
Options: | ||
-v, --version <VERSION> | ||
The Solidity language version to use for parsing | ||
--json | ||
Print the concrete syntax tree as JSON | ||
-h, --help | ||
Print help | ||
``` | ||
|
||
Here is an example of the JSON output it can print: | ||
|
||
```json | ||
// A Nonterminal node | ||
"Nonterminal": { | ||
// Name of the nonterminal kind | ||
"kind": "SourceUnit", | ||
// Length of the nonterminal in Unicode code points, depending on the encoding used | ||
"text_len": { | ||
"utf8": 24, | ||
"utf16": 24, | ||
"char": 24 // de facto utf32 | ||
}, | ||
"children": [/* Nonterminal or Terminal nodes */] | ||
} | ||
// A Terminal node | ||
"Terminal": { | ||
// Name of the terminal kind | ||
"kind": "PragmaKeyword", | ||
// Literal value, taken from the source code | ||
"text": "pragma" | ||
} | ||
``` | ||
|
||
## Inspecting JSON Output | ||
|
||
Now let's try to use that command to parse the following Solidity file, and inspect its contents: | ||
|
||
```solidity title="input.sol" | ||
pragma solidity ^0.8.0; | ||
``` | ||
|
||
```bash | ||
slang_solidity parse --json --version "0.8.0" "input.sol" > "output.json" | ||
``` | ||
|
||
Because the resulting structure is well-defined and recursive, we can use the popular `jq` tool to quickly analyze the resulting output: | ||
|
||
```bash | ||
JQ_QUERY='recurse | select(.Terminal?) | .Terminal' | ||
cat output.json | jq "$JQ_QUERY" | ||
``` | ||
|
||
This gives us a flat list of the Terminal nodes: | ||
|
||
```json | ||
{ | ||
"kind": "PragmaKeyword", | ||
"text": "pragma" | ||
} | ||
{ | ||
"kind": "Whitespace", | ||
"text": " " | ||
} | ||
{ | ||
"kind": "SolidityKeyword", | ||
"text": "solidity" | ||
} | ||
{ | ||
"kind": "Whitespace", | ||
"text": " " | ||
} | ||
{ | ||
"kind": "Caret", | ||
"text": "^" | ||
} | ||
{ | ||
"kind": "VersionPragmaValue", | ||
"text": "0" | ||
} | ||
{ | ||
"kind": "Period", | ||
"text": "." | ||
} | ||
{ | ||
"kind": "VersionPragmaValue", | ||
"text": "8" | ||
} | ||
{ | ||
"kind": "Period", | ||
"text": "." | ||
} | ||
{ | ||
"kind": "VersionPragmaValue", | ||
"text": "0" | ||
} | ||
{ | ||
"kind": "Semicolon", | ||
"text": ";" | ||
} | ||
{ | ||
"kind": "EndOfLine", | ||
"text": "\n" | ||
} | ||
``` | ||
|
||
Now, we can adapt the query to select the `text` fields of the nodes and concatenate them, | ||
which gives us back the reconstructed source code! 🎉 | ||
|
||
```bash | ||
$ JQ_QUERY='[recurse | select(.Terminal?) | .Terminal.text] | join("")' | ||
$ cat output.json | jq "$JQ_QUERY" | ||
|
||
"pragma solidity ^0.8.0;\n" | ||
``` |
75 changes: 75 additions & 0 deletions
75
documentation/public/user-guide/rust-crate/using-the-cursor.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Using the Cursor | ||
|
||
This guide will walk you through the basics of using a [CST cursor](../concepts.md#cst-cursors) in your project. | ||
Let's start with this source file, that contains three contracts: | ||
|
||
```solidity title="input.sol" | ||
--8<-- "documentation/public/user-guide/inputs/using-the-cursor.sol" | ||
``` | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_cursor.rs:imports" | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_cursor.rs:parse-input" | ||
``` | ||
|
||
## Listing Contract Names | ||
|
||
The below example uses a cursor to list the names of all contracts in a source file: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_cursor.rs:listing-contract-names" | ||
``` | ||
|
||
## Visiting Only a Sub-tree | ||
|
||
In cases like the above, we needed to visit a sub-tree of the CST (to get the contract name). | ||
But we also need to remember to return the cursor to its original position after each read, | ||
which is inconvenient, and can lead to subtle bugs. | ||
|
||
To avoid this, we can use the `spawn()` API, | ||
which cheaply creates a new cursor that starts at the given node, without copying the previous path history. | ||
This lets us visit the sub-tree of each contract, without modifying the original cursor: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_cursor.rs:visiting-sub-tree" | ||
``` | ||
|
||
## Accessing Node Positions | ||
|
||
The `Cursor` API also tracks the position and range of the current node it is visiting. | ||
Here is an example that records the source range of each contract, along with its text: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_cursor.rs:accessing-node-positions" | ||
``` | ||
|
||
## Using Iterator API | ||
|
||
In addition to the procedural-style methods, the `Cursor` struct also implements the `Iterator` trait, which allows you to use it in a functional style. | ||
|
||
Let's use that to extract all `Identifier` nodes from the source text using that API: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_cursor.rs:using-iterator-api" | ||
``` | ||
|
||
!!! note | ||
|
||
It's important to note that `Iterator::next` first visits the current node, | ||
yields it, and then moves the cursor to the next node. | ||
As such, accessor associated functions called on the `Cursor` that reference | ||
the "current" will point to the one that is not yet yielded by the iterator. | ||
This might be an important, when mixing the two styles. | ||
|
||
## Using a Cursor with Names | ||
|
||
In addition to the basic `Cursor`, there's also a `CursorWithLabels` type | ||
that keeps track of the names of the nodes it visits. | ||
You can create a `CursorWithLabels` from a `Cursor` by using the `with_labels()` API. | ||
|
||
Let's use that to extract all nodes that are labeled `Name`: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_cursor.rs:using-labeled-cursors" | ||
``` |
48 changes: 48 additions & 0 deletions
48
documentation/public/user-guide/rust-crate/using-the-parser.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Using the Parser | ||
|
||
Using the API directly provides us with a more fine-grained control over the parsing process. It allows us to parse not just the input as a top-level source unit, but also individual nonterminals like contracts, various definitions, and even expressions. | ||
|
||
## Parsing Source Files | ||
|
||
Let's start with this simple source file, that contains a single contract: | ||
|
||
```solidity title="input.sol" | ||
--8<-- "documentation/public/user-guide/inputs/using-the-parser.sol" | ||
``` | ||
|
||
We begin by creating a `Parser` object with a specified version. This is an entry point for our parser API. | ||
Then we can use it to parse the source file, specifying the top-level nonterminal to parse: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_parser.rs:imports" | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_parser.rs:parse-input" | ||
``` | ||
|
||
## Checking for Syntax Errors | ||
|
||
If the file has errors, we can get them from the `ParseOutput` type, and print them out: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_parser.rs:print-errors" | ||
``` | ||
|
||
Otherwise, we can check if input is valid using this helpful utility: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_parser.rs:assert-is-valid" | ||
``` | ||
|
||
## Inspecting the Parse Tree | ||
|
||
Now, let's try to inspect the resulting CST, and iterate on its children: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_parser.rs:inspect-tree" | ||
``` | ||
|
||
Additionally, we can convert the CST node back into the input string: | ||
|
||
```{ .rust } | ||
--8<-- "crates/solidity/outputs/cargo/tests/src/doc_examples/using_the_parser.rs:unparse-node" | ||
``` |