Skip to content

Commit b5d99b6

Browse files
authored
chore: standardize lint help + validate docs existance (#10639)
1 parent 5e20961 commit b5d99b6

16 files changed

+110
-43
lines changed

crates/forge/tests/cli/lint.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use forge_lint::{linter::Lint, sol::med::REGISTERED_LINTS};
12
use foundry_config::{LintSeverity, LinterConfig};
23

34
const CONTRACT: &str = r#"
@@ -53,6 +54,7 @@ warning[divide-before-multiply]: multiplication should occur before division to
5354
16 | (1 / 2) * 3;
5455
| -----------
5556
|
57+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply
5658
5759
5860
"#]]);
@@ -75,6 +77,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase
7577
6 | uint256 VARIABLE_MIXED_CASE_INFO;
7678
| ------------------------
7779
|
80+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable
7881
7982
8083
"#]]);
@@ -109,6 +112,7 @@ note[mixed-case-variable]: mutable variables should use mixedCase
109112
6 | uint256 VARIABLE_MIXED_CASE_INFO;
110113
| ------------------------
111114
|
115+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#mixed-case-variable
112116
113117
114118
"#]]);
@@ -134,6 +138,7 @@ warning[divide-before-multiply]: multiplication should occur before division to
134138
16 | (1 / 2) * 3;
135139
| -----------
136140
|
141+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply
137142
138143
139144
"#]]);
@@ -160,8 +165,56 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect
160165
13 | result = 8 >> localValue;
161166
| ---------------
162167
|
168+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift
163169
164170
165171
"#
166172
]]);
167173
});
174+
175+
#[tokio::test]
176+
async fn ensure_lint_rule_docs() {
177+
const FOUNDRY_BOOK_LINT_PAGE_URL: &str =
178+
"https://book.getfoundry.sh/reference/forge/forge-lint";
179+
180+
// Fetch the content of the lint reference
181+
let content = match reqwest::get(FOUNDRY_BOOK_LINT_PAGE_URL).await {
182+
Ok(resp) => {
183+
if !resp.status().is_success() {
184+
panic!(
185+
"Failed to fetch Foundry Book lint page ({FOUNDRY_BOOK_LINT_PAGE_URL}). Status: {status}",
186+
status = resp.status()
187+
);
188+
}
189+
match resp.text().await {
190+
Ok(text) => text,
191+
Err(e) => {
192+
panic!("Failed to read response text: {e}");
193+
}
194+
}
195+
}
196+
Err(e) => {
197+
panic!("Failed to fetch Foundry Book lint page ({FOUNDRY_BOOK_LINT_PAGE_URL}): {e}",);
198+
}
199+
};
200+
201+
// Ensure no missing lints
202+
let mut missing_lints = Vec::new();
203+
for lint in REGISTERED_LINTS {
204+
let selector = format!("#{}", lint.id());
205+
if !content.contains(&selector) {
206+
missing_lints.push(lint.id());
207+
}
208+
}
209+
210+
if !missing_lints.is_empty() {
211+
let mut msg = String::from(
212+
"Foundry Book lint validation failed. The following lints must be added to the docs:\n",
213+
);
214+
for lint in missing_lints {
215+
msg.push_str(&format!(" - {lint}\n"));
216+
}
217+
msg.push_str("Please open a PR: https://github.com/foundry-rs/book");
218+
panic!("{msg}");
219+
}
220+
}

crates/lint/src/linter.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ pub trait Lint {
3131
fn id(&self) -> &'static str;
3232
fn severity(&self) -> Severity;
3333
fn description(&self) -> &'static str;
34-
fn help(&self) -> Option<&'static str> {
35-
None
36-
}
34+
fn help(&self) -> &'static str;
3735
}
3836

3937
pub struct LintContext<'s> {
@@ -48,19 +46,14 @@ impl<'s> LintContext<'s> {
4846

4947
// Helper method to emit diagnostics easily from passes
5048
pub fn emit<L: Lint>(&self, lint: &'static L, span: Span) {
51-
let (desc, help) = match (self.desc, lint.help()) {
52-
(true, Some(help)) => (lint.description(), help),
53-
(true, None) => (lint.description(), ""),
54-
(false, _) => ("", ""),
55-
};
56-
49+
let desc = if self.desc { lint.description() } else { "" };
5750
let diag: DiagBuilder<'_, ()> = self
5851
.sess
5952
.dcx
6053
.diag(lint.severity().into(), desc)
6154
.code(DiagId::new_str(lint.id()))
6255
.span(MultiSpan::from_span(span))
63-
.help(help);
56+
.help(lint.help());
6457

6558
diag.emit();
6659
}

crates/lint/src/sol/gas/keccak.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ declare_forge_lint!(
1111
ASM_KECCAK256,
1212
Severity::Gas,
1313
"asm-keccak256",
14-
"hash using inline assembly to save gas",
15-
""
14+
"hash using inline assembly to save gas"
1615
);
1716

1817
impl<'ast> EarlyLintPass<'ast> for AsmKeccak256 {

crates/lint/src/sol/high/incorrect_shift.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ declare_forge_lint!(
1010
INCORRECT_SHIFT,
1111
Severity::High,
1212
"incorrect-shift",
13-
"the order of args in a shift operation is incorrect",
14-
""
13+
"the order of args in a shift operation is incorrect"
1514
);
1615

1716
impl<'ast> EarlyLintPass<'ast> for IncorrectShift {

crates/lint/src/sol/info/mixed_case.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ declare_forge_lint!(
1010
MIXED_CASE_FUNCTION,
1111
Severity::Info,
1212
"mixed-case-function",
13-
"function names should use mixedCase",
14-
"https://docs.soliditylang.org/en/latest/style-guide.html#function-names"
13+
"function names should use mixedCase"
1514
);
1615

1716
impl<'ast> EarlyLintPass<'ast> for MixedCaseFunction {

crates/lint/src/sol/info/pascal_case.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ declare_forge_lint!(
1010
PASCAL_CASE_STRUCT,
1111
Severity::Info,
1212
"pascal-case-struct",
13-
"structs should use PascalCase",
14-
"https://docs.soliditylang.org/en/latest/style-guide.html#struct-names"
13+
"structs should use PascalCase"
1514
);
1615

1716
impl<'ast> EarlyLintPass<'ast> for PascalCaseStruct {

crates/lint/src/sol/info/screaming_snake_case.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ declare_forge_lint!(
1010
SCREAMING_SNAKE_CASE_CONSTANT,
1111
Severity::Info,
1212
"screaming-snake-case-const",
13-
"constants should use SCREAMING_SNAKE_CASE",
14-
"https://docs.soliditylang.org/en/latest/style-guide.html#constants"
13+
"constants should use SCREAMING_SNAKE_CASE"
1514
);
1615

1716
declare_forge_lint!(

crates/lint/src/sol/macros.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,20 @@
77
/// - `$severity`: The `Severity` of the lint (e.g. `High`, `Med`, `Low`, `Info`, `Gas`).
88
/// - `$str_id`: A unique identifier used to reference a specific lint during configuration.
99
/// - `$desc`: A short description of the lint.
10-
/// - `$help` (optional): Link to additional information about the lint or best practices.
10+
///
11+
/// # Note
12+
/// Each lint must have a `help` section in the foundry book. This help field is auto-generated by
13+
/// the macro. Because of that, to ensure that new lint rules have their corresponding docs in the
14+
/// book, the existence of the lint rule's help section is validated with a unit test.
1115
#[macro_export]
1216
macro_rules! declare_forge_lint {
13-
($id:ident, $severity:expr, $str_id:expr, $desc:expr, $help:expr) => {
17+
($id:ident, $severity:expr, $str_id:expr, $desc:expr) => {
1418
// Declare the static `Lint` metadata
1519
pub static $id: SolLint = SolLint {
1620
id: $str_id,
1721
severity: $severity,
1822
description: $desc,
19-
help: if $help.is_empty() { None } else { Some($help) },
23+
help: concat!("https://book.getfoundry.sh/reference/forge/forge-lint#", $str_id),
2024
};
2125
};
2226

crates/lint/src/sol/med/div_mul.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ declare_forge_lint!(
1010
DIVIDE_BEFORE_MULTIPLY,
1111
Severity::Med,
1212
"divide-before-multiply",
13-
"multiplication should occur before division to avoid loss of precision",
14-
""
13+
"multiplication should occur before division to avoid loss of precision"
1514
);
1615

1716
impl<'ast> EarlyLintPass<'ast> for DivideBeforeMultiply {

crates/lint/src/sol/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ pub enum SolLintError {
159159
pub struct SolLint {
160160
id: &'static str,
161161
description: &'static str,
162-
help: Option<&'static str>,
162+
help: &'static str,
163163
severity: Severity,
164164
}
165165

@@ -173,7 +173,7 @@ impl Lint for SolLint {
173173
fn description(&self) -> &'static str {
174174
self.description
175175
}
176-
fn help(&self) -> Option<&'static str> {
176+
fn help(&self) -> &'static str {
177177
self.help
178178
}
179179
}

crates/lint/testdata/DivideBeforeMultiply.stderr

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,45 @@ warning[divide-before-multiply]: multiplication should occur before division to
44
3 | (1 / 2) * 3;
55
| -----------
66
|
7+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply
78

89
warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision
910
--> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC
1011
|
1112
5 | ((1 / 2) * 3) * 4;
1213
| -----------
1314
|
15+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply
1416

1517
warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision
1618
--> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC
1719
|
1820
6 | ((1 * 2) / 3) * 4;
1921
| -----------------
2022
|
23+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply
2124

2225
warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision
2326
--> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC
2427
|
2528
7 | (1 / 2 / 3) * 4;
2629
| ---------------
2730
|
31+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply
2832

2933
warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision
3034
--> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC
3135
|
3236
8 | (1 / (2 + 3)) * 4;
3337
| -----------------
3438
|
39+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply
3540

3641
warning[divide-before-multiply]: multiplication should occur before division to avoid loss of precision
3742
--> ROOT/testdata/DivideBeforeMultiply.sol:LL:CC
3843
|
3944
15 | 1 / ((2 / 3) * 3);
4045
| -----------
4146
|
47+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#divide-before-multiply
4248

crates/lint/testdata/IncorrectShift.stderr

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,37 @@ warning[incorrect-shift]: the order of args in a shift operation is incorrect
44
21 | result = 2 << stateValue;
55
| ---------------
66
|
7+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift
78

89
warning[incorrect-shift]: the order of args in a shift operation is incorrect
910
--> ROOT/testdata/IncorrectShift.sol:LL:CC
1011
|
1112
22 | result = 8 >> localValue;
1213
| ---------------
1314
|
15+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift
1416

1517
warning[incorrect-shift]: the order of args in a shift operation is incorrect
1618
--> ROOT/testdata/IncorrectShift.sol:LL:CC
1719
|
1820
23 | result = 16 << (stateValue + 1);
1921
| ----------------------
2022
|
23+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift
2124

2225
warning[incorrect-shift]: the order of args in a shift operation is incorrect
2326
--> ROOT/testdata/IncorrectShift.sol:LL:CC
2427
|
2528
24 | result = 32 >> getAmount();
2629
| -----------------
2730
|
31+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift
2832

2933
warning[incorrect-shift]: the order of args in a shift operation is incorrect
3034
--> ROOT/testdata/IncorrectShift.sol:LL:CC
3135
|
3236
25 | ... result = 1 << (localValue > 10 ? localShiftAmount : stateShiftAmount);
3337
| ------------------------------------------------------------
3438
|
39+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#incorrect-shift
3540

crates/lint/testdata/Keccak256.stderr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ note[asm-keccak256]: hash using inline assembly to save gas
44
3 | keccak256(abi.encodePacked(a, b));
55
| ---------
66
|
7+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256
78

89
note[asm-keccak256]: hash using inline assembly to save gas
910
--> ROOT/testdata/Keccak256.sol:LL:CC
1011
|
1112
7 | keccak256(abi.encodePacked(a, b));
1213
| ---------
1314
|
15+
= help: https://book.getfoundry.sh/reference/forge/forge-lint#asm-keccak256
1416

0 commit comments

Comments
 (0)