Skip to content

Commit 353d659

Browse files
authored
docs: add documentation (#46)
* docs: add documentation * docs: add documentation * docs: add missing doc * docs(config): add documentation * docs: add documentation * docs: add documentation * docs: update readme * docs: update readme * docs: update readme * docs: add documentation * docs: update readme
1 parent 9c7dad9 commit 353d659

15 files changed

+192
-64
lines changed

README.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# 🔎 lintspec
22

3-
![lintspec screenshot](./screenshot.png)
3+
![lintspec screenshot](https://raw.githubusercontent.com/beeb/lintspec/refs/heads/main/screenshot.png)
44

55
<div align="center">
66
<a href="https://github.com/beeb/lintspec"><img
@@ -61,7 +61,7 @@ Head over to the [releases page](https://github.com/beeb/lintspec/releases)!
6161

6262
## Usage
6363

64-
```
64+
```text
6565
Usage: lintspec [OPTIONS] [PATH]...
6666
6767
Arguments:
@@ -101,16 +101,16 @@ that are displayed in the source files when viewed (e.g. in a PR's "Files" tab).
101101

102102
### Options
103103

104-
The following options are available for the action (all are optional):
104+
The following options are available for the action (all are optional if a config file is present):
105105

106106
| Input | Default Value | Description | Example |
107107
|---|---|---|---|
108108
| `working-directory` | `"./"` | Working directory path | `"./src"` |
109-
| `paths` | `"[]"` | Paths to scan, relative to the working directory, in square brackets and separated by commas. | `"[path/to/file.sol,test/test.sol]"` |
109+
| `paths` | `"[]"` | Paths to scan, relative to the working directory, in square brackets and separated by commas. Required unless a `.lintspec.toml` file is present in the working directory. | `"[path/to/file.sol,test/test.sol]"` |
110110
| `exclude` | `"[]"` | Paths to exclude, relative to the working directory, in square brackets and separated by commas | `"[path/to/exclude,other/path.sol]"` |
111111
| `extra-args` | | Extra arguments passed to the `lintspec` command | `"--constructor=true"` |
112112
| `version` | `"latest"` | Version of lintspec to use. For enhanced security, you can pin this to a fixed version | `"0.1.5"` |
113-
| `fail-on-problem` | `"true"` | Whether the action should fail when NatSpec problems have been found. Disabling this only creates annotations for found problems, but succeeds | `"false"` |
113+
| `fail-on-problem` | `"true"` | Whether the action should fail when `NatSpec` problems have been found. Disabling this only creates annotations for found problems, but succeeds | `"false"` |
114114

115115
### Example Workflow
116116

@@ -149,7 +149,7 @@ On an AMD Ryzen 9 7950X processor with 64GB of RAM, linting the
149149
[Uniswap/v4-core](https://github.com/Uniswap/v4-core) `src` folder on WSL2 (Ubuntu), lintspec is about 214x faster, or
150150
0.46% of the execution time:
151151

152-
```
152+
```text
153153
Benchmark 1: npx @defi-wonderland/natspec-smells --include "src/**/*.sol"
154154
Time (mean ± σ): 12.484 s ± 0.157 s [User: 13.581 s, System: 0.594 s]
155155
Range (min … max): 12.288 s … 12.817 s 10 runs

src/config.rs

+23
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//! Tool configuration parsing and validation
12
use std::path::PathBuf;
23

34
use anyhow::Result;
@@ -91,21 +92,43 @@ pub struct Args {
9192
pub sort: Option<bool>,
9293
}
9394

95+
/// The parsed and validated config for the tool
9496
#[derive(Debug, Clone, Serialize, Deserialize, bon::Builder)]
9597
#[non_exhaustive]
9698
#[builder(on(PathBuf, into))]
9799
#[allow(clippy::struct_excessive_bools)]
98100
pub struct Config {
101+
/// The paths to search for Solidity files
99102
pub paths: Vec<PathBuf>,
103+
104+
/// Some paths to ignore while searching for Solidity files
100105
pub exclude: Vec<PathBuf>,
106+
107+
/// The file where to output the diagnostics (if `None`, then stderr is used)
101108
pub out: Option<PathBuf>,
109+
110+
/// Whether to enforce the use of `@inheritdoc` on external/public/overridden items
102111
pub inheritdoc: bool,
112+
113+
/// Whether to enforce documentation of constructors
103114
pub constructor: bool,
115+
116+
/// Whether to enforce documentation of struct members
104117
pub struct_params: bool,
118+
119+
/// Whether to enforce documentation of enum variants
105120
pub enum_params: bool,
121+
122+
/// Whether to enforce documentation of items which have no params/returns/members
106123
pub enforce: Vec<ItemType>,
124+
125+
/// Output JSON diagnostics
107126
pub json: bool,
127+
128+
/// Output compact format (minified JSON or simple text output)
108129
pub compact: bool,
130+
131+
/// Sort diagnostics by file path
109132
pub sort: bool,
110133
}
111134

src/definitions/constructor.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//! Parsing and validation of constructors.
12
use slang_solidity::cst::{NonterminalKind, Query, QueryMatch, TextRange};
23

34
use crate::{
@@ -15,9 +16,16 @@ use super::{
1516
#[derive(Debug, Clone, bon::Builder)]
1617
#[non_exhaustive]
1718
pub struct ConstructorDefinition {
19+
/// The parent contract (should always be a [`Parent::Contract`])
1820
pub parent: Option<Parent>,
21+
22+
/// The span corresponding to the constructor definition, excluding the body
1923
pub span: TextRange,
24+
25+
/// The name and span of the constructor's parameters
2026
pub params: Vec<Identifier>,
27+
28+
/// The [`NatSpec`] associated with the constructor, if any
2129
pub natspec: Option<NatSpec>,
2230
}
2331

src/definitions/enumeration.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//! Parsing and validation of enum definitions.
12
use slang_solidity::cst::{Cursor, Query, QueryMatch, TerminalKind, TextRange};
23

34
use crate::{
@@ -16,10 +17,19 @@ use super::{
1617
#[non_exhaustive]
1718
#[builder(on(String, into))]
1819
pub struct EnumDefinition {
20+
/// The parent for the enum definition, if any
1921
pub parent: Option<Parent>,
22+
23+
/// The name of the enum
2024
pub name: String,
25+
26+
/// The span of the enum definition
2127
pub span: TextRange,
28+
29+
/// The name and span of the enum variants
2230
pub members: Vec<Identifier>,
31+
32+
/// The [`NatSpec`] associated with the enum definition, if any
2333
pub natspec: Option<NatSpec>,
2434
}
2535

@@ -94,6 +104,7 @@ impl Validate for EnumDefinition {
94104
}
95105
}
96106

107+
/// Extract the identifiers of each of an enum's variants
97108
fn extract_enum_members(cursor: &Cursor) -> Vec<Identifier> {
98109
let mut cursor = cursor.spawn().with_edges();
99110
let mut out = Vec::new();

src/definitions/error.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//! Parsing and validation of error definitions.
12
use slang_solidity::cst::{Query, QueryMatch, TextRange};
23

34
use crate::{
@@ -16,10 +17,19 @@ use super::{
1617
#[non_exhaustive]
1718
#[builder(on(String, into))]
1819
pub struct ErrorDefinition {
20+
/// The parent for the error definition, if any
1921
pub parent: Option<Parent>,
22+
23+
/// The name of the error
2024
pub name: String,
25+
26+
/// The span of the error definition
2127
pub span: TextRange,
28+
29+
/// The name and span of the error's parameters
2230
pub params: Vec<Identifier>,
31+
32+
/// The [`NatSpec`] associated with the error definition, if any
2333
pub natspec: Option<NatSpec>,
2434
}
2535

src/definitions/event.rs

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//! Parsing and validation of event definitions.
12
use slang_solidity::cst::{NonterminalKind, Query, QueryMatch, TextRange};
23

34
use crate::{
@@ -16,10 +17,19 @@ use super::{
1617
#[non_exhaustive]
1718
#[builder(on(String, into))]
1819
pub struct EventDefinition {
20+
/// The parent for the event definition, if any
1921
pub parent: Option<Parent>,
22+
23+
/// The name of the event
2024
pub name: String,
25+
26+
/// The span of the event definition
2127
pub span: TextRange,
28+
29+
/// The name and span of the event's parameters
2230
pub params: Vec<Identifier>,
31+
32+
/// The [`NatSpec`] associated with the event definition, if any
2333
pub natspec: Option<NatSpec>,
2434
}
2535

src/definitions/function.rs

+14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//! Parsing and validation of function definitions.
12
use slang_solidity::cst::{NonterminalKind, Query, QueryMatch, TextRange};
23

34
use crate::{
@@ -17,12 +18,25 @@ use super::{
1718
#[non_exhaustive]
1819
#[builder(on(String, into))]
1920
pub struct FunctionDefinition {
21+
/// The parent for the function definition (should always be `Some`)
2022
pub parent: Option<Parent>,
23+
24+
/// The name of the function
2125
pub name: String,
26+
27+
/// The span of the function definition, exluding the body
2228
pub span: TextRange,
29+
30+
/// The name and span of the function's parameters
2331
pub params: Vec<Identifier>,
32+
33+
/// The name and span of the function's returns
2434
pub returns: Vec<Identifier>,
35+
36+
/// The [`NatSpec`] associated with the function definition, if any
2537
pub natspec: Option<NatSpec>,
38+
39+
/// The attributes of the function (visibility and override)
2640
pub attributes: Attributes,
2741
}
2842

src/definitions/mod.rs

+63-56
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
//! Parsing and validation of source item definitions
2+
//!
3+
//! This module contains structs for each of the source item types that can be documented with `NatSpec`.
4+
//! The [`Definition`] type provides a unified interface to interact with the various types.
5+
//! The [`find_items`] function takes the tree root [`Cursor`] from a Solidity document and returns all the parsed
6+
//! definitions contained within.
7+
//! Some helper functions allow to extract useful information from [`Cursor`]s.
18
use constructor::ConstructorDefinition;
29
use derive_more::{Display, From};
310
use enumeration::EnumDefinition;
@@ -304,7 +311,7 @@ impl Definition {
304311
}
305312
}
306313

307-
/// Find source item definitions from a root CST cursor
314+
/// Find source item definitions from a root CST [`Cursor`]
308315
pub fn find_items(cursor: Cursor) -> Vec<Definition> {
309316
let mut out = Vec::new();
310317
for m in cursor.query(vec![
@@ -357,7 +364,7 @@ pub fn find_items(cursor: Cursor) -> Vec<Definition> {
357364

358365
/// Extract parameters from a function-like source item.
359366
///
360-
/// The node kind that holds the `Identifier` (`Parameter`, `EventParameter`) is provided as `kind`.
367+
/// The node kind that holds the `Identifier` (`Parameter`, `EventParameter`) must be provided with `kind`.
361368
#[must_use]
362369
pub fn extract_params(cursor: &Cursor, kind: NonterminalKind) -> Vec<Identifier> {
363370
let mut cursor = cursor.spawn();
@@ -474,6 +481,60 @@ pub fn extract_identifiers(cursor: &Cursor) -> Vec<Identifier> {
474481
out
475482
}
476483

484+
/// Extract the attributes (visibility and override) from a function-like item or state variable
485+
#[must_use]
486+
pub fn extract_attributes(cursor: &Cursor) -> Attributes {
487+
let mut cursor = cursor.spawn();
488+
let mut out = Attributes::default();
489+
while cursor.go_to_next_terminal_with_kinds(&[
490+
TerminalKind::ExternalKeyword,
491+
TerminalKind::InternalKeyword,
492+
TerminalKind::PrivateKeyword,
493+
TerminalKind::PublicKeyword,
494+
TerminalKind::OverrideKeyword,
495+
]) {
496+
match cursor
497+
.node()
498+
.as_terminal()
499+
.expect("should be terminal kind")
500+
.kind
501+
{
502+
TerminalKind::ExternalKeyword => out.visibility = Visibility::External,
503+
TerminalKind::InternalKeyword => out.visibility = Visibility::Internal,
504+
TerminalKind::PrivateKeyword => out.visibility = Visibility::Private,
505+
TerminalKind::PublicKeyword => out.visibility = Visibility::Public,
506+
TerminalKind::OverrideKeyword => out.r#override = true,
507+
_ => unreachable!(),
508+
}
509+
}
510+
out
511+
}
512+
513+
/// Find the parent's name (contract, interface, library), if any
514+
#[must_use]
515+
pub fn extract_parent_name(mut cursor: Cursor) -> Option<Parent> {
516+
while cursor.go_to_parent() {
517+
if let Some(parent) = cursor.node().as_nonterminal_with_kinds(&[
518+
NonterminalKind::ContractDefinition,
519+
NonterminalKind::InterfaceDefinition,
520+
NonterminalKind::LibraryDefinition,
521+
]) {
522+
for child in &parent.children {
523+
if child.is_terminal_with_kind(TerminalKind::Identifier) {
524+
let name = child.node.unparse().trim().to_string();
525+
return Some(match parent.kind {
526+
NonterminalKind::ContractDefinition => Parent::Contract(name),
527+
NonterminalKind::InterfaceDefinition => Parent::Interface(name),
528+
NonterminalKind::LibraryDefinition => Parent::Library(name),
529+
_ => unreachable!(),
530+
});
531+
}
532+
}
533+
}
534+
}
535+
None
536+
}
537+
477538
/// Check a list of params to see if they are documented with a corresponding item in the [`NatSpec`], and generate a
478539
/// diagnostic for each missing one or if there are more than 1 entry per param.
479540
#[must_use]
@@ -542,60 +603,6 @@ pub fn check_returns(
542603
res
543604
}
544605

545-
/// Extract the attributes (visibility and override) from a function-like item or state variable
546-
#[must_use]
547-
pub fn extract_attributes(cursor: &Cursor) -> Attributes {
548-
let mut cursor = cursor.spawn();
549-
let mut out = Attributes::default();
550-
while cursor.go_to_next_terminal_with_kinds(&[
551-
TerminalKind::ExternalKeyword,
552-
TerminalKind::InternalKeyword,
553-
TerminalKind::PrivateKeyword,
554-
TerminalKind::PublicKeyword,
555-
TerminalKind::OverrideKeyword,
556-
]) {
557-
match cursor
558-
.node()
559-
.as_terminal()
560-
.expect("should be terminal kind")
561-
.kind
562-
{
563-
TerminalKind::ExternalKeyword => out.visibility = Visibility::External,
564-
TerminalKind::InternalKeyword => out.visibility = Visibility::Internal,
565-
TerminalKind::PrivateKeyword => out.visibility = Visibility::Private,
566-
TerminalKind::PublicKeyword => out.visibility = Visibility::Public,
567-
TerminalKind::OverrideKeyword => out.r#override = true,
568-
_ => unreachable!(),
569-
}
570-
}
571-
out
572-
}
573-
574-
/// Find the parent's name (contract, interface, library), if any
575-
#[must_use]
576-
pub fn extract_parent_name(mut cursor: Cursor) -> Option<Parent> {
577-
while cursor.go_to_parent() {
578-
if let Some(parent) = cursor.node().as_nonterminal_with_kinds(&[
579-
NonterminalKind::ContractDefinition,
580-
NonterminalKind::InterfaceDefinition,
581-
NonterminalKind::LibraryDefinition,
582-
]) {
583-
for child in &parent.children {
584-
if child.is_terminal_with_kind(TerminalKind::Identifier) {
585-
let name = child.node.unparse().trim().to_string();
586-
return Some(match parent.kind {
587-
NonterminalKind::ContractDefinition => Parent::Contract(name),
588-
NonterminalKind::InterfaceDefinition => Parent::Interface(name),
589-
NonterminalKind::LibraryDefinition => Parent::Library(name),
590-
_ => unreachable!(),
591-
});
592-
}
593-
}
594-
}
595-
}
596-
None
597-
}
598-
599606
#[cfg(test)]
600607
mod tests {
601608
use similar_asserts::assert_eq;

0 commit comments

Comments
 (0)