Skip to content

Commit 9b8c42c

Browse files
authored
Test that all interned symbols are referenced in Clippy sources (#14842)
This test checks that all symbols defined in Clippy's `sym.rs` file are used in Clippy. Otherwise, it will fail with a list of symbols which are unused. This test only runs when the `internal` feature is set, to allow developers to temporarily define new, yet unused symbols without failing the tests. Also, it will not run as part of the compiler test suite, as an extra symbol should never be seen as a fatal problem in the compiler repo. changelog: none
2 parents 6fa2866 + d82e51b commit 9b8c42c

File tree

2 files changed

+81
-1
lines changed

2 files changed

+81
-1
lines changed

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: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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::ffi::OsStr;
10+
use std::fs;
11+
12+
use regex::Regex;
13+
use walkdir::{DirEntry, WalkDir};
14+
15+
const SYM_FILE: &str = "clippy_utils/src/sym.rs";
16+
17+
type Result<T, E = AnyError> = std::result::Result<T, E>;
18+
type AnyError = Box<dyn std::error::Error>;
19+
20+
#[test]
21+
#[allow(clippy::case_sensitive_file_extension_comparisons)]
22+
fn all_symbols_are_used() -> Result<()> {
23+
if option_env!("RUSTC_TEST_SUITE").is_some() {
24+
return Ok(());
25+
}
26+
27+
// Load all symbols defined in `SYM_FILE`.
28+
let content = fs::read_to_string(SYM_FILE)?;
29+
let content = content
30+
.split_once("generate! {")
31+
.ok_or("cannot find symbols start")?
32+
.1
33+
.split_once("\n}\n")
34+
.ok_or("cannot find symbols end")?
35+
.0;
36+
let mut interned: HashSet<String> = Regex::new(r"(?m)^ (\w+)")
37+
.unwrap()
38+
.captures_iter(content)
39+
.map(|m| m[1].to_owned())
40+
.collect();
41+
42+
// Remove symbols used as `sym::*`.
43+
let used_re = Regex::new(r"\bsym::(\w+)\b").unwrap();
44+
let rs_ext = OsStr::new("rs");
45+
for dir in ["clippy_lints", "clippy_lints_internal", "clippy_utils", "src"] {
46+
for file in WalkDir::new(dir)
47+
.into_iter()
48+
.flatten()
49+
.map(DirEntry::into_path)
50+
.filter(|p| p.extension() == Some(rs_ext))
51+
{
52+
for cap in used_re.captures_iter(&fs::read_to_string(file)?) {
53+
interned.remove(&cap[1]);
54+
}
55+
}
56+
}
57+
58+
// Remove symbols used as part of paths.
59+
let paths_re = Regex::new(r"path!\(([\w:]+)\)").unwrap();
60+
for path in [
61+
"clippy_utils/src/paths.rs",
62+
"clippy_lints_internal/src/internal_paths.rs",
63+
] {
64+
for cap in paths_re.captures_iter(&fs::read_to_string(path)?) {
65+
for sym in cap[1].split("::") {
66+
interned.remove(sym);
67+
}
68+
}
69+
}
70+
71+
let mut extra = interned.iter().collect::<Vec<_>>();
72+
if !extra.is_empty() {
73+
extra.sort_unstable();
74+
eprintln!("Unused symbols defined in {SYM_FILE}:");
75+
for sym in extra {
76+
eprintln!(" - {sym}");
77+
}
78+
Err(format!("extra symbols found — remove them {SYM_FILE}"))?;
79+
}
80+
Ok(())
81+
}

0 commit comments

Comments
 (0)