Skip to content

Commit 531a51c

Browse files
bors[bot]xFrednet
andauthored
Merge #203
203: Rustc: Create `Adapter` early to register lints with rustc r=NathanFrasier a=xFrednet Registering the lints early, makes rustc track the lint level correctly (Assuming that marker is registered as a tool) Not much more to say, I wrote this thing last night and I was super tired at the end. But it works, that's the important part ^^ --- Closes: #200 Co-authored-by: xFrednet <[email protected]>
2 parents 97ba19a + 461c5b2 commit 531a51c

File tree

10 files changed

+189
-49
lines changed

10 files changed

+189
-49
lines changed

marker_adapter/src/lib.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
pub mod context;
88
mod loader;
99

10-
use std::ops::ControlFlow;
10+
use std::{cell::RefCell, ops::ControlFlow};
1111

1212
use loader::LintCrateRegistry;
1313
use marker_api::{
@@ -22,34 +22,48 @@ use marker_api::{
2222
};
2323
use marker_utils::visitor::{self, Visitor};
2424

25-
/// This struct is the interface used by lint drivers to pass transformed objects to
26-
/// external lint passes.
25+
/// This struct is the interface used by lint drivers to load lint crates, pass
26+
/// `marker_api` objects to external lint passes and all other magic you can think of.
2727
pub struct Adapter {
28+
/// [`LintPass`] functions are called with a mutable `self` parameter as the
29+
/// first argument. This `RefCell` acts as a wrapper to hide the internal
30+
/// mutability from drivers.
31+
///
32+
/// The effects of the mutability should never reach the driver anyways and
33+
/// this just makes it way easier to handle the adapter in drivers.
34+
inner: RefCell<AdapterInner>,
35+
}
36+
37+
struct AdapterInner {
2838
external_lint_crates: LintCrateRegistry,
2939
}
3040

3141
impl Adapter {
3242
#[must_use]
3343
pub fn new_from_env() -> Self {
3444
let external_lint_crates = LintCrateRegistry::new_from_env();
35-
Self { external_lint_crates }
45+
Self {
46+
inner: RefCell::new(AdapterInner { external_lint_crates }),
47+
}
3648
}
3749

3850
#[must_use]
39-
pub fn registered_lints(&self) -> Vec<LintPassInfo> {
40-
self.external_lint_crates.collect_lint_pass_info()
51+
pub fn lint_pass_infos(&self) -> Vec<LintPassInfo> {
52+
self.inner.borrow().external_lint_crates.collect_lint_pass_info()
4153
}
4254

43-
pub fn process_krate<'ast>(&mut self, cx: &'ast AstContext<'ast>, krate: &Crate<'ast>) {
44-
self.external_lint_crates.set_ast_context(cx);
55+
pub fn process_krate<'ast>(&self, cx: &'ast AstContext<'ast>, krate: &Crate<'ast>) {
56+
let inner = &mut *self.inner.borrow_mut();
57+
58+
inner.external_lint_crates.set_ast_context(cx);
4559

4660
for item in krate.items() {
47-
visitor::traverse_item::<()>(cx, self, *item);
61+
visitor::traverse_item::<()>(cx, inner, *item);
4862
}
4963
}
5064
}
5165

52-
impl Visitor<()> for Adapter {
66+
impl Visitor<()> for AdapterInner {
5367
fn visit_item<'ast>(&mut self, cx: &'ast AstContext<'ast>, item: ItemKind<'ast>) -> ControlFlow<()> {
5468
self.external_lint_crates.check_item(cx, item);
5569
ControlFlow::Continue(())

marker_api/src/interface.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,3 +169,10 @@ impl LintPassInfoBuilder {
169169
pub struct LintPassInfo {
170170
lints: FfiSlice<'static, &'static Lint>,
171171
}
172+
173+
#[cfg(feature = "driver-api")]
174+
impl LintPassInfo {
175+
pub fn lints(&self) -> &[&'static Lint] {
176+
self.lints.get()
177+
}
178+
}

marker_lints/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ marker_api::declare_lint!(
1818
);
1919

2020
#[derive(Debug, Default)]
21-
struct MarkerLintPass;
21+
struct MarkerLintsLintPass;
2222

23-
marker_api::export_lint_pass!(MarkerLintPass);
23+
marker_api::export_lint_pass!(MarkerLintsLintPass);
2424

25-
impl LintPass for MarkerLintPass {
25+
impl LintPass for MarkerLintsLintPass {
2626
fn info(&self) -> LintPassInfo {
2727
LintPassInfoBuilder::new(Box::new([DIAG_MSG_UPPERCASE_START])).build()
2828
}

marker_rustc_driver/src/conversion/rustc.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,27 @@ use rustc_hash::FxHashMap;
88

99
use crate::context::storage::Storage;
1010

11+
thread_local! {
12+
/// This maps marker lints to lint instances used by rustc.
13+
///
14+
/// Rustc requires lints to be registered, before the lint pass is run. This
15+
/// is a problem for this conversion setup, as the used `*Converter` structs
16+
/// require the `'ast` lifetime. Storing this specific map outside the struct
17+
/// and providing a static conversion method, is simply a hack to allow the
18+
/// early conversion of lints, so they can be registered.
19+
///
20+
/// If we run into more problems like this, we might have to rethink the structure
21+
/// again... let's just hope this doesn't happen!
22+
static LINTS_MAP: RefCell<FxHashMap<&'static Lint, &'static rustc_lint::Lint>> = RefCell::default();
23+
}
24+
1125
pub struct RustcConverter<'ast, 'tcx> {
1226
rustc_cx: rustc_middle::ty::TyCtxt<'tcx>,
1327
storage: &'ast Storage<'ast>,
14-
lints: RefCell<FxHashMap<&'static Lint, &'static rustc_lint::Lint>>,
1528
}
1629

1730
impl<'ast, 'tcx> RustcConverter<'ast, 'tcx> {
1831
pub fn new(rustc_cx: rustc_middle::ty::TyCtxt<'tcx>, storage: &'ast Storage<'ast>) -> Self {
19-
Self {
20-
rustc_cx,
21-
storage,
22-
lints: RefCell::default(),
23-
}
32+
Self { rustc_cx, storage }
2433
}
2534
}

marker_rustc_driver/src/conversion/rustc/common.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,16 @@ impl<'ast, 'tcx> RustcConverter<'ast, 'tcx> {
164164

165165
#[must_use]
166166
pub fn to_lint_level(&self, api_level: Level) -> rustc_lint::Level {
167+
Self::static_to_lint_level(api_level)
168+
}
169+
170+
/// This not being a method taking `&self` is a small hack, to allow the
171+
/// creation of `&'static Lint` instances before the start of the `'ast`
172+
/// lifetime, required by the [`RustcConverter`].
173+
///
174+
/// When possible, please use [`RustcConverter::to_lint_level`] instead.
175+
#[must_use]
176+
pub fn static_to_lint_level(api_level: Level) -> rustc_lint::Level {
167177
match api_level {
168178
Level::Allow => rustc_lint::Level::Allow,
169179
Level::Warn => rustc_lint::Level::Warn,
@@ -173,6 +183,7 @@ impl<'ast, 'tcx> RustcConverter<'ast, 'tcx> {
173183
}
174184
}
175185

186+
#[must_use]
176187
pub(crate) fn to_applicability(&self, app: Applicability) -> rustc_errors::Applicability {
177188
match app {
178189
Applicability::MachineApplicable => rustc_errors::Applicability::MachineApplicable,
@@ -183,6 +194,7 @@ impl<'ast, 'tcx> RustcConverter<'ast, 'tcx> {
183194
}
184195
}
185196

197+
#[must_use]
186198
pub fn to_span(&self, api_span: &Span<'ast>) -> rustc_span::Span {
187199
let (_, src_info) = self
188200
.storage

marker_rustc_driver/src/conversion/rustc/unstable.rs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,42 @@ use marker_api::lint::{Lint, MacroReport};
33
use super::RustcConverter;
44

55
impl<'ast, 'tcx> RustcConverter<'ast, 'tcx> {
6+
#[must_use]
67
pub fn to_lint(&self, api_lint: &'static Lint) -> &'static rustc_lint::Lint {
7-
self.lints.borrow_mut().entry(api_lint).or_insert_with(|| {
8-
// Not extracted to an extra function, as it's very specific
9-
let report_in_external_macro = match api_lint.report_in_macro {
10-
MacroReport::No => false,
11-
MacroReport::All => true,
12-
_ => unreachable!(),
13-
};
8+
Self::static_to_lint(api_lint)
9+
}
10+
11+
/// This not being a method taking `&self` is a small hack, to allow the
12+
/// creation of `&'static Lint` instances before the start of the `'ast`
13+
/// lifetime, required by the [`RustcConverter`].
14+
///
15+
/// When possible, please use [`RustcConverter::to_lint`] instead.
16+
#[must_use]
17+
pub fn static_to_lint(api_lint: &'static Lint) -> &'static rustc_lint::Lint {
18+
super::LINTS_MAP.with(|lints| {
19+
// This extra value, with the explicit lifetime is needed to make rustc
20+
// see that it actually has the `'static` lifetime
21+
let lint: &'static rustc_lint::Lint = lints.borrow_mut().entry(api_lint).or_insert_with(move || {
22+
// Not extracted to an extra function, as it's very specific
23+
let report_in_external_macro = match api_lint.report_in_macro {
24+
MacroReport::No => false,
25+
MacroReport::All => true,
26+
_ => unreachable!(),
27+
};
1428

15-
Box::leak(Box::new(rustc_lint::Lint {
16-
name: api_lint.name,
17-
default_level: self.to_lint_level(api_lint.default_level),
18-
desc: api_lint.explanation,
19-
edition_lint_opts: None,
20-
report_in_external_macro,
21-
future_incompatible: None,
22-
is_plugin: true,
23-
feature_gate: None,
24-
crate_level_only: false,
25-
}))
29+
Box::leak(Box::new(rustc_lint::Lint {
30+
name: api_lint.name,
31+
default_level: Self::static_to_lint_level(api_lint.default_level),
32+
desc: api_lint.explanation,
33+
edition_lint_opts: None,
34+
report_in_external_macro,
35+
future_incompatible: None,
36+
is_plugin: true,
37+
feature_gate: None,
38+
crate_level_only: false,
39+
}))
40+
});
41+
lint
2642
})
2743
}
2844
}

marker_rustc_driver/src/lint_pass.rs

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,65 @@
1+
use std::cell::LazyCell;
2+
13
use marker_adapter::Adapter;
4+
use marker_api::lint::Lint;
25

36
use crate::context::{storage::Storage, RustcContext};
47

5-
pub struct MarkerLintPass;
8+
thread_local! {
9+
/// The [`Adapter`] loads the lint crates and is the general interface used
10+
/// by drivers to communicate with lint crates.
11+
///
12+
/// The lint crates have to be loaded before the instantiation of [`RustcLintPass`]
13+
/// to allow this driver to register the lints before the lint pass starts.
14+
/// (See [`super::MarkerCallback::config`]). Storing the `Adapter` in a `thread_local`
15+
/// cell is the easiest solution I could come up with. It should be fine performance
16+
/// wise.
17+
///
18+
/// Storing the [`Adapter`] in a `thread_local` is safe, since rustc is currently
19+
/// only single threaded. This cell will therefore only be constructed once, and
20+
/// this driver will always use the same adapter.
21+
static ADAPTER: LazyCell<Adapter> = LazyCell::new(|| {
22+
Adapter::new_from_env()
23+
});
24+
}
25+
26+
pub struct RustcLintPass;
27+
28+
impl RustcLintPass {
29+
pub fn marker_lints() -> Vec<&'static Lint> {
30+
ADAPTER.with(|adapter| {
31+
adapter
32+
.lint_pass_infos()
33+
.iter()
34+
.flat_map(marker_api::LintPassInfo::lints)
35+
.copied()
36+
.collect()
37+
})
38+
}
39+
}
640

7-
rustc_lint_defs::impl_lint_pass!(MarkerLintPass => []);
41+
rustc_lint_defs::impl_lint_pass!(RustcLintPass => []);
842

9-
impl<'tcx> rustc_lint::LateLintPass<'tcx> for MarkerLintPass {
43+
impl<'tcx> rustc_lint::LateLintPass<'tcx> for RustcLintPass {
1044
fn check_crate(&mut self, rustc_cx: &rustc_lint::LateContext<'tcx>) {
11-
process_crate(rustc_cx);
45+
ADAPTER.with(|adapter| {
46+
process_crate(rustc_cx, adapter);
47+
});
1248
}
1349
}
1450

15-
fn process_crate(rustc_cx: &rustc_lint::LateContext<'_>) {
51+
fn process_crate(rustc_cx: &rustc_lint::LateContext<'_>, adapter: &Adapter) {
1652
let storage = Storage::default();
17-
process_crate_lifetime(rustc_cx, &storage);
53+
process_crate_lifetime(rustc_cx, &storage, adapter);
1854
}
1955

2056
/// This function marks the start of the `'ast` lifetime. The lifetime is defined
2157
/// by the [`Storage`] object.
22-
fn process_crate_lifetime<'ast, 'tcx: 'ast>(rustc_cx: &rustc_lint::LateContext<'tcx>, storage: &'ast Storage<'ast>) {
58+
fn process_crate_lifetime<'ast, 'tcx: 'ast>(
59+
rustc_cx: &rustc_lint::LateContext<'tcx>,
60+
storage: &'ast Storage<'ast>,
61+
adapter: &Adapter,
62+
) {
2363
let driver_cx = RustcContext::new(rustc_cx.tcx, rustc_cx.lint_store, storage);
2464

2565
// To support debug printing of AST nodes, as these might sometimes require the
@@ -31,6 +71,5 @@ fn process_crate_lifetime<'ast, 'tcx: 'ast>(rustc_cx: &rustc_lint::LateContext<'
3171
.marker_converter
3272
.to_crate(rustc_hir::def_id::LOCAL_CRATE, driver_cx.rustc_cx.hir().root_module());
3373

34-
let mut adapter = Adapter::new_from_env();
3574
adapter.process_krate(driver_cx.ast_cx(), krate);
3675
}

marker_rustc_driver/src/main.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![feature(let_chains)]
44
#![feature(lint_reasons)]
55
#![feature(iter_collect_into)]
6+
#![feature(lazy_cell)]
67
#![feature(non_exhaustive_omitted_patterns_lint)]
78
#![warn(rustc::internal)]
89
#![warn(clippy::pedantic)]
@@ -40,6 +41,8 @@ use std::process::{exit, Command};
4041
use rustc_session::config::ErrorOutputType;
4142
use rustc_session::EarlyErrorHandler;
4243

44+
use crate::conversion::rustc::RustcConverter;
45+
4346
const RUSTC_TOOLCHAIN_VERSION: &str = "nightly-2023-07-13";
4447

4548
struct DefaultCallbacks;
@@ -49,13 +52,21 @@ struct MarkerCallback;
4952

5053
impl rustc_driver::Callbacks for MarkerCallback {
5154
fn config(&mut self, config: &mut rustc_interface::Config) {
52-
// Clippy explicitly calls any previous functions. This will not be
53-
// done here to keep it simple and to ensure that only known code is
54-
// executed.
55+
// Clippy explicitly calls any previous `register_lints` functions. This
56+
// will not be done here to keep it simple and to ensure that only known
57+
// code is executed.
5558
assert!(config.register_lints.is_none());
5659

5760
config.register_lints = Some(Box::new(|_sess, lint_store| {
58-
lint_store.register_late_pass(|_| Box::new(lint_pass::MarkerLintPass));
61+
// Register lints from lint crates. This is required to have rustc track
62+
// the lint level correctly.
63+
let lints: Vec<_> = lint_pass::RustcLintPass::marker_lints()
64+
.into_iter()
65+
.map(RustcConverter::static_to_lint)
66+
.collect();
67+
lint_store.register_lints(&lints);
68+
69+
lint_store.register_late_pass(|_| Box::new(lint_pass::RustcLintPass));
5970
}));
6071
}
6172
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![feature(register_tool)]
2+
#![register_tool(marker)]
3+
4+
const FIND_ME_DEFAULT: i32 = 0;
5+
6+
#[allow(marker::item_with_test_name)]
7+
const FIND_ME_ALLOW: i32 = 0;
8+
9+
#[deny(marker::item_with_test_name)]
10+
const FIND_ME_DENY: i32 = 0;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
warning: found a `const` item with a test name
2+
--> $DIR/lint_level_attributes.rs:4:1
3+
|
4+
4 | const FIND_ME_DEFAULT: i32 = 0;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `#[warn(marker::item_with_test_name)]` on by default
8+
9+
error: found a `const` item with a test name
10+
--> $DIR/lint_level_attributes.rs:10:1
11+
|
12+
10 | const FIND_ME_DENY: i32 = 0;
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
14+
|
15+
note: the lint level is defined here
16+
--> $DIR/lint_level_attributes.rs:9:8
17+
|
18+
9 | #[deny(marker::item_with_test_name)]
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
21+
error: aborting due to previous error; 1 warning emitted
22+

0 commit comments

Comments
 (0)