Skip to content

Commit eb03af0

Browse files
committed
Test that all interned symbols are referenced in Clippy sources
1 parent b87e90b commit eb03af0

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ quote = "1.0.25"
5050
syn = { version = "2.0", features = ["full"] }
5151
futures = "0.3"
5252
parking_lot = "0.12"
53-
tokio = { version = "1", features = ["io-util"] }
53+
tokio = { version = "1", features = ["io-util", "fs", "rt-multi-thread", "macros"] }
5454

5555
[build-dependencies]
5656
rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }

clippy_utils/src/sym.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ generate! {
132132
enum_glob_use,
133133
enumerate,
134134
err,
135-
error,
136135
exp,
137136
expect_err,
138137
expn_data,

tests/symbols-used.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// This test checks that all symbols defined in Clippy's `sym.rs` file
2+
// are used in Clippy. Otherwise, it will fail with a list of symbols
3+
// which are unused.
4+
//
5+
// This test is a no-op if run as part of the compiler test suite
6+
// and will always succeed.
7+
8+
use std::collections::HashSet;
9+
use std::path::Path;
10+
11+
use futures::StreamExt;
12+
use futures::stream::FuturesUnordered;
13+
use regex::Regex;
14+
use walkdir::{DirEntry, WalkDir};
15+
16+
const SYM_FILE: &str = "clippy_utils/src/sym.rs";
17+
18+
type Result<T, E = AnyError> = std::result::Result<T, E>;
19+
type AnyError = Box<dyn std::error::Error>;
20+
21+
async fn load_interned_symbols() -> Result<HashSet<String>> {
22+
let content = tokio::fs::read_to_string(SYM_FILE).await?;
23+
let content = content
24+
.split_once("generate! {")
25+
.ok_or("cannot find symbols start")?
26+
.1
27+
.split_once("\n}\n")
28+
.ok_or("cannot find symbols end")?
29+
.0;
30+
let re = Regex::new(r"(?m)^ (\w+)").unwrap();
31+
Ok(re.captures_iter(content).map(|m| m[1].to_owned()).collect())
32+
}
33+
34+
async fn load_symbols(file: impl AsRef<Path>, re: &Regex) -> Result<Vec<String>> {
35+
let content = tokio::fs::read_to_string(file).await?;
36+
Ok(re.captures_iter(&content).map(|m| m[1].to_owned()).collect())
37+
}
38+
39+
async fn load_paths(file: impl AsRef<Path>, re: &Regex) -> Result<Vec<String>> {
40+
let content = tokio::fs::read_to_string(file).await?;
41+
Ok(re
42+
.captures_iter(&content)
43+
.flat_map(|m| m[1].split("::").map(String::from).collect::<Vec<_>>())
44+
.collect())
45+
}
46+
47+
#[tokio::test]
48+
#[allow(clippy::case_sensitive_file_extension_comparisons)]
49+
async fn all_symbols_are_used() -> Result<()> {
50+
if option_env!("RUSTC_TEST_SUITE").is_some() {
51+
return Ok(());
52+
}
53+
54+
let used_re = &Regex::new(r"\bsym::(\w+)\b").unwrap();
55+
let mut used_symbols_per_file = ["clippy_lints", "clippy_lints_internal", "clippy_utils", "src"]
56+
.iter()
57+
.flat_map(|dir| {
58+
WalkDir::new(dir)
59+
.into_iter()
60+
.filter_entry(|e| e.file_name().to_str().is_some_and(|s| s.ends_with(".rs")) || e.file_type().is_dir())
61+
.flat_map(|e| e.map(DirEntry::into_path))
62+
})
63+
.map(|file| load_symbols(file, used_re))
64+
.collect::<FuturesUnordered<_>>();
65+
let used_symbols = async {
66+
let mut symbols = HashSet::default();
67+
while let Some(syms) = used_symbols_per_file.next().await {
68+
if let Ok(syms) = syms {
69+
symbols.extend(syms);
70+
}
71+
}
72+
symbols
73+
};
74+
75+
let paths_re = Regex::new(r"path!\(([\w:]+)\)").unwrap();
76+
let (interned, mut used, paths, paths_internal) = futures::join!(
77+
load_interned_symbols(),
78+
used_symbols,
79+
load_paths("clippy_utils/src/paths.rs", &paths_re),
80+
load_paths("clippy_lints_internal/src/internal_paths.rs", &paths_re),
81+
);
82+
used.extend(paths?);
83+
used.extend(paths_internal?);
84+
let interned = interned?;
85+
let mut extra = interned.difference(&used).collect::<Vec<_>>();
86+
if !extra.is_empty() {
87+
extra.sort_unstable();
88+
eprintln!("Unused symbols defined in {SYM_FILE}:");
89+
for sym in extra {
90+
eprintln!(" - {sym}");
91+
}
92+
Err(format!("extra symbols found — remove them {SYM_FILE}"))?;
93+
}
94+
Ok(())
95+
}

0 commit comments

Comments
 (0)