Skip to content

Commit 50abdc6

Browse files
augustin-vsmoelius
authored andcommitted
feat(examples)#1225: non existent path in comment
1 parent 5cb7877 commit 50abdc6

File tree

5 files changed

+206
-0
lines changed

5 files changed

+206
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[package]
2+
name = "nonexistent_path_in_comment"
3+
version = "0.1.0"
4+
authors = ["Augustin Villetard"]
5+
description = "Lint for nonexistent paths in comments"
6+
edition = "2021"
7+
publish = false
8+
9+
[lib]
10+
crate-type = ["cdylib"]
11+
12+
[dependencies]
13+
clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "19e305bb57a7595f2a8d81f521c0dd8bf854e739" }
14+
dylint_linting = "3.5.1"
15+
regex = { workspace = true }
16+
once_cell = { workspace = true }
17+
18+
[dev-dependencies]
19+
dylint_testing = "3.5.1"
20+
21+
[package.metadata.rust-analyzer]
22+
rustc_private = true
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Nonexistent Path in Comment Lint
2+
3+
### What it does
4+
5+
This lint checks comments in the code for file path references and verifies that the referenced files actually do exist. It processes both line comments (using `//`) and block comments (`/* ... */`) by using regex to extract potential file paths. When the lint finds a file path that does not exist on the filesystem it emits a warning.
6+
7+
### Why is this bad?
8+
9+
References to nonexistent files in comments can be misleading:
10+
- They clutter the code with outdated or inaccurate references.
11+
- They may cause confusion among developers who are trying to trace implementation details or documentation.
12+
13+
### Known problems
14+
- Can only check for absolute path or path relative to commented file.
15+
- [This example from the issue](https://github.com/trailofbits/dylint/issues/1225#issue-2315607396) would get flaged even if it did exist, as this lint doesn't check for project root.
16+
17+
### Example
18+
19+
The lint issues a warning when a comment refers to a file that does not exist:
20+
21+
```
22+
// See ../nonexistent/path/file.rs for further details
23+
fn main() {}
24+
```
25+
26+
Use this approach instead, referring to a file that exists:
27+
28+
```
29+
// See ../existing/file.rs for actual implementation details
30+
fn main() {}
31+
```
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#![feature(rustc_private)]
2+
#![feature(let_chains)]
3+
#![warn(unused_extern_crates)]
4+
5+
extern crate rustc_span;
6+
7+
use clippy_utils::diagnostics::span_lint_and_help;
8+
use once_cell::sync::Lazy;
9+
use regex::Regex;
10+
use rustc_lint::{LateContext, LateLintPass};
11+
use rustc_span::{Span, SyntaxContext};
12+
use std::path::Path;
13+
14+
dylint_linting::declare_late_lint! {
15+
/// ### What it does
16+
/// Checks for file paths in comments that don't exist in the filesystem.
17+
///
18+
/// ### Why is this bad?
19+
/// References to nonexistent files in comments can be misleading and may indicate outdated
20+
/// documentation or typos.
21+
///
22+
/// ### Example
23+
/// ```
24+
/// // See ../nonexistent/path/file.rs for implementation details
25+
/// fn main() {}
26+
/// ```
27+
/// Use instead:
28+
/// ```
29+
/// // See ../actual/path/file.rs for implementation details
30+
/// fn main() {}
31+
/// ```
32+
pub NONEXISTENT_PATH_IN_COMMENT,
33+
Warn,
34+
"reports file paths in comments that do not exist"
35+
}
36+
37+
static LINE_COMMENT: Lazy<Regex> = Lazy::new(|| Regex::new("(^|[^/])(//([^/].*))").unwrap());
38+
static BLOCK_COMMENT: Lazy<Regex> = Lazy::new(|| Regex::new(r"/\*(([^*]|\*[^/])*)\*/").unwrap());
39+
static PATH_REGEX: Lazy<Regex> =
40+
Lazy::new(|| Regex::new(r"((?:\.\./|/|[\w/-]+/)+[\w-]+(?:\.[\w-]+)+)").unwrap());
41+
42+
impl<'tcx> LateLintPass<'tcx> for NonexistentPathInComment {
43+
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
44+
let source_map = cx.tcx.sess.source_map();
45+
46+
for file in source_map.files().iter() {
47+
if let Ok(content) = file.src.as_ref().ok_or("err") {
48+
let file_start = file.start_pos;
49+
50+
for cap in LINE_COMMENT.captures_iter(content) {
51+
if let Some(comment_text) = cap.get(3) {
52+
check_comment(
53+
cx,
54+
Span::new(
55+
file_start + rustc_span::BytePos(comment_text.start() as u32),
56+
file_start + rustc_span::BytePos(comment_text.end() as u32),
57+
SyntaxContext::root(),
58+
None,
59+
),
60+
comment_text.as_str(),
61+
&file.name,
62+
);
63+
}
64+
}
65+
66+
for cap in BLOCK_COMMENT.captures_iter(content) {
67+
if let Some(comment_text) = cap.get(1) {
68+
check_comment(
69+
cx,
70+
Span::new(
71+
file_start + rustc_span::BytePos(comment_text.start() as u32),
72+
file_start + rustc_span::BytePos(comment_text.end() as u32),
73+
SyntaxContext::root(),
74+
None,
75+
),
76+
comment_text.as_str(),
77+
&file.name,
78+
);
79+
}
80+
}
81+
}
82+
}
83+
}
84+
}
85+
86+
fn check_comment(
87+
cx: &LateContext<'_>,
88+
span: Span,
89+
comment_text: &str,
90+
filename: &rustc_span::FileName,
91+
) {
92+
let base_dir = match filename {
93+
rustc_span::FileName::Real(real_filename) => Path::new(real_filename.local_path().unwrap())
94+
.parent()
95+
.unwrap()
96+
.to_path_buf(),
97+
_ => return,
98+
};
99+
100+
for captures in PATH_REGEX.captures_iter(comment_text) {
101+
let path_str = &captures[1];
102+
let full_path = base_dir.join(path_str);
103+
104+
if !full_path.exists() {
105+
span_lint_and_help(
106+
cx,
107+
NONEXISTENT_PATH_IN_COMMENT,
108+
span,
109+
format!("referenced path does not exist: {}", path_str),
110+
None,
111+
"verify the path is correct or remove the reference",
112+
);
113+
}
114+
}
115+
}
116+
117+
#[test]
118+
fn ui() {
119+
dylint_testing::ui_test(env!("CARGO_PKG_NAME"), "ui");
120+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// This path does not exist
2+
// See ../nonexistent/path/file.rs
3+
4+
// This path should exist
5+
// See ../src/lib.rs
6+
7+
/* This is a block comment with a nonexistent path
8+
See ../another/nonexistent/path/file.go
9+
*/
10+
11+
fn main() {}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
warning: referenced path does not exist: ../nonexistent/path/file.rs
2+
--> $DIR/main.rs:2:3
3+
|
4+
LL | // See ../nonexistent/path/file.rs
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= help: verify the path is correct or remove the reference
8+
= note: `#[warn(nonexistent_path_in_comment)]` on by default
9+
10+
warning: referenced path does not exist: ../another/nonexistent/path/file.go
11+
--> $DIR/main.rs:7:3
12+
|
13+
LL | /* This is a block comment with a nonexistent path
14+
| ___^
15+
LL | | See ../another/nonexistent/path/file.go
16+
LL | | */
17+
| |_^
18+
|
19+
= help: verify the path is correct or remove the reference
20+
21+
warning: 2 warnings emitted
22+

0 commit comments

Comments
 (0)