Line data Source code
1 : //! This crate provides macros for creating [Dylint] libraries, and utilities for creating
2 : //! configurable libraries.
3 : //!
4 : //! **Contents**
5 : //!
6 : //! - [`dylint_library!`]
7 : //! - [`declare_late_lint!`, `declare_early_lint!`, `declare_pre_expansion_lint!`]
8 : //! - [`impl_late_lint!`, `impl_early_lint!`, `impl_pre_expansion_lint!`]
9 : //! - [`constituent` feature]
10 : //! - [Configurable libraries]
11 : //!
12 : //! # `dylint_library!`
13 : //!
14 : //! The `dylint_library!` macro expands to the following:
15 : //!
16 : //! ```rust,ignore
17 : //! #[allow(unused_extern_crates)]
18 : //! extern crate rustc_driver;
19 : //!
20 : //! #[no_mangle]
21 : //! pub extern "C" fn dylint_version() -> *mut std::os::raw::c_char {
22 : //! std::ffi::CString::new($crate::DYLINT_VERSION)
23 : //! .unwrap()
24 : //! .into_raw()
25 : //! }
26 : //! ```
27 : //!
28 : //! If your library uses the `dylint_library!` macro and the [`dylint-link`] tool, then all you
29 : //! should have to do is implement the [`register_lints`] function. See the [examples] in this
30 : //! repository.
31 : //!
32 : //! # `declare_late_lint!`, etc.
33 : //!
34 : //! If your library contains just one lint, using `declare_late_lint!`, etc. can make your code more
35 : //! concise. Each of these macros requires the same arguments as [`declare_lint!`], and wraps the
36 : //! following:
37 : //!
38 : //! - a call to `dylint_library!`
39 : //! - an implementation of the `register_lints` function
40 : //! - a call to `declare_lint!`
41 : //! - a call to [`declare_lint_pass!`]
42 : //!
43 : //! For example, `declare_late_lint!(vis NAME, Level, "description")` expands to the following:
44 : //!
45 : //! ```rust,ignore
46 : //! dylint_linting::dylint_library!();
47 : //!
48 : //! extern crate rustc_lint;
49 : //! extern crate rustc_session;
50 : //!
51 : //! #[no_mangle]
52 : //! pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) {
53 : //! dylint_linting::init_config(sess);
54 : //! lint_store.register_lints(&[NAME]);
55 : //! lint_store.register_late_pass(|_| Box::new(Name));
56 : //! }
57 : //!
58 : //! rustc_session::declare_lint!(vis NAME, Level, "description");
59 : //!
60 : //! rustc_session::declare_lint_pass!(Name => [NAME]);
61 : //! ```
62 : //!
63 : //! `declare_early_lint!` and `declare_pre_expansion_lint!` are defined similarly.
64 : //!
65 : //! # `impl_late_lint!`, etc.
66 : //!
67 : //! `impl_late_lint!`, etc. are like `declare_late_lint!`, etc. except:
68 : //!
69 : //! - each calls [`impl_lint_pass!`] instead of `declare_lint_pass!`;
70 : //! - each requires an additional argument to specify the value of the lint's [`LintPass`]
71 : //! structure.
72 : //!
73 : //! That is, `impl_late_lint!`'s additional argument is what goes here:
74 : //!
75 : //! ```rust,ignore
76 : //! lint_store.register_late_pass(|_| Box::new(...));
77 : //! ^^^
78 : //! ```
79 : //!
80 : //! # `constituent` feature
81 : //!
82 : //! Enabling the package-level `constituent` feature changes the way the above macros work.
83 : //! Specifically, it causes them to _exclude_:
84 : //!
85 : //! - the call to `dylint_library!`
86 : //! - the use of `#[no_mangle]` just prior to the declaration of `register_lints`
87 : //!
88 : //! Such changes facilitate inclusion of a lint declared with one of the above macros into a larger
89 : //! library. That is:
90 : //!
91 : //! - With the feature turned off, the lint can be built as a library by itself.
92 : //! - With the feature turned on, the lint can be built as part of a larger library, alongside other
93 : //! lints.
94 : //!
95 : //! The [general-purpose] and [supplementary] lints in this repository employ this technique.
96 : //! That is, each general-purpose lint can be built as a library by itself, or as part of the
97 : //! [`general` library]. An analogous statement applies to the supplementary lints and the
98 : //! [`supplementary` library]. The `constituent` feature is the underlying mechanism that makes this
99 : //! work.
100 : //!
101 : //! # Configurable libraries
102 : //!
103 : //! Libraries can be configured by including a `dylint.toml` file in the target workspace's root
104 : //! directory. This crate provides the following functions for reading and parsing `dylint.toml`
105 : //! files:
106 : //!
107 : //! - [`config_or_default`]
108 : //! - [`config`]
109 : //! - [`config_toml`]
110 : //! - [`init_config`]
111 : //! - [`try_init_config`]
112 : //!
113 : //! A configurable library containing just one lint will typically have a `lib.rs` file of the
114 : //! following form:
115 : //!
116 : //! ```rust,ignore
117 : //! dylint_linting::impl_late_lint! {
118 : //! ...,
119 : //! LintName::new()
120 : //! }
121 : //!
122 : //! // Lint configuration
123 : //! #[derive(Default, serde::Deserialize)]
124 : //! struct Config {
125 : //! boolean: bool,
126 : //! strings: Vec<String>,
127 : //! }
128 : //!
129 : //! // Keep a copy of the configuration in the `LintPass` structure.
130 : //! struct LintName {
131 : //! config: Config,
132 : //! }
133 : //!
134 : //! // Read the configuration from the `dylint.toml` file, or use the default configuration if
135 : //! // none is present.
136 : //! impl LintName {
137 : //! pub fn new() -> Self {
138 : //! Self {
139 : //! config: dylint_linting::config_or_default(env!("CARGO_PKG_NAME")),
140 : //! }
141 : //! }
142 : //! }
143 : //! ```
144 : //!
145 : //! For a concrete example of a `lib.rs` file with this form, see the
146 : //! [`non_local_effect_before_error_return`] library in this repository.
147 : //!
148 : //! A library containing more than one lint must implement the `register_lints` function without
149 : //! relying on the above macros. If the library is configurable, then its `register_lints` function
150 : //! should include a call to `dylint_linting::init_config`, as in the following example:
151 : //!
152 : //! ```rust,ignore
153 : //! #[no_mangle]
154 : //! pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) {
155 : //! // `init_config` or `try_init_config` must be called before `config_or_default`, `config`,
156 : //! // or `config_toml` is called.
157 : //! dylint_linting::init_config(sess);
158 : //!
159 : //! lint_store.register_lints(&[FIRST_LINT_NAME, SECOND_LINT_NAME]);
160 : //!
161 : //! lint_store.register_late_pass(|_| Box::new(LintPassName::new()));
162 : //! }
163 : //! ```
164 : //!
165 : //! Additional documentation on `config_or_default`, etc. can be found on [docs.rs].
166 : //!
167 : //! [Configurable libraries]: #configurable-libraries
168 : //! [Dylint]: https://github.com/trailofbits/dylint/tree/master
169 : //! [`LintPass`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LintPass.html
170 : //! [`config_or_default`]: https://docs.rs/dylint_linting/latest/dylint_linting/fn.config_or_default.html
171 : //! [`config_toml`]: https://docs.rs/dylint_linting/latest/dylint_linting/fn.config_toml.html
172 : //! [`config`]: https://docs.rs/dylint_linting/latest/dylint_linting/fn.config.html
173 : //! [`constituent` feature]: #constituent-feature
174 : //! [`declare_late_lint!`, `declare_early_lint!`, `declare_pre_expansion_lint!`]: #declare_late_lint-etc
175 : //! [`declare_lint!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/macro.declare_lint.html
176 : //! [`declare_lint_pass!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/macro.declare_lint_pass.html
177 : //! [`dylint-link`]: https://github.com/trailofbits/dylint/tree/master/dylint-link
178 : //! [`dylint_library!`]: #dylint_library
179 : //! [`general` library]: https://github.com/trailofbits/dylint/tree/master/examples/general/src/lib.rs
180 : //! [`impl_late_lint!`, `impl_early_lint!`, `impl_pre_expansion_lint!`]: #impl_late_lint-etc
181 : //! [`impl_lint_pass!`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_session/macro.impl_lint_pass.html
182 : //! [`init_config`]: https://docs.rs/dylint_linting/latest/dylint_linting/fn.init_config.html
183 : //! [`non_local_effect_before_error_return`]: https://github.com/trailofbits/dylint/tree/master/examples/general/non_local_effect_before_error_return/src/lib.rs
184 : //! [`register_lints`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_interface/interface/struct.Config.html#structfield.register_lints
185 : //! [`supplementary` library]: https://github.com/trailofbits/dylint/tree/master/examples/supplementary/src/lib.rs
186 : //! [`try_init_config`]: https://docs.rs/dylint_linting/latest/dylint_linting/fn.try_init_config.html
187 : //! [docs.rs documentation]: https://docs.rs/dylint_linting/latest/dylint_linting/
188 : //! [docs.rs]: https://docs.rs/dylint_linting/latest/dylint_linting/
189 : //! [examples]: https://github.com/trailofbits/dylint/tree/master/examples
190 : //! [general-purpose]: https://github.com/trailofbits/dylint/tree/master/examples/general
191 : //! [supplementary]: https://github.com/trailofbits/dylint/tree/master/examples/supplementary
192 :
193 : #![feature(rustc_private)]
194 : #![warn(unused_extern_crates)]
195 :
196 : #[allow(unused_extern_crates)]
197 : extern crate rustc_driver;
198 :
199 : extern crate rustc_session;
200 : extern crate rustc_span;
201 :
202 : use dylint_internal::{config, env};
203 : use rustc_span::Symbol;
204 : use std::{
205 : any::type_name,
206 : path::{Path, PathBuf},
207 : };
208 :
209 : pub use config::{Error as ConfigError, Result as ConfigResult};
210 :
211 : pub const DYLINT_VERSION: &str = "0.1.0";
212 :
213 : pub use paste;
214 :
215 : // smoelius: Including `extern crate rustc_driver` causes the library to link against
216 : // `librustc_driver.so`, which dylint-driver also links against. So, essentially, the library uses
217 : // dylint-driver's copy of the Rust compiler crates.
218 : #[macro_export]
219 : macro_rules! dylint_library {
220 : () => {
221 : #[allow(unused_extern_crates)]
222 : extern crate rustc_driver;
223 :
224 : #[doc(hidden)]
225 : #[no_mangle]
226 : pub extern "C" fn dylint_version() -> *mut std::os::raw::c_char {
227 : std::ffi::CString::new($crate::DYLINT_VERSION)
228 : .unwrap()
229 : .into_raw()
230 : }
231 : };
232 : }
233 :
234 : #[cfg(not(feature = "constituent"))]
235 : #[doc(hidden)]
236 : #[macro_export]
237 : macro_rules! __maybe_exclude {
238 : ($item:item) => {
239 : $item
240 : };
241 : }
242 :
243 : #[cfg(feature = "constituent")]
244 : #[doc(hidden)]
245 : #[macro_export]
246 : macro_rules! __maybe_exclude {
247 : ($item:item) => {};
248 : }
249 :
250 : #[cfg(not(feature = "constituent"))]
251 : #[doc(hidden)]
252 : #[macro_export]
253 : macro_rules! __maybe_mangle {
254 : ($item:item) => {
255 : #[no_mangle]
256 : $item
257 : };
258 : }
259 :
260 : #[cfg(feature = "constituent")]
261 : #[doc(hidden)]
262 : #[macro_export]
263 : macro_rules! __maybe_mangle {
264 : ($item:item) => {
265 : $item
266 : };
267 : }
268 :
269 : #[doc(hidden)]
270 : #[macro_export]
271 : macro_rules! __declare_and_register_lint {
272 : ($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr, $register_pass_method:ident, $pass:expr) => {
273 : $crate::__maybe_exclude! {
274 : $crate::dylint_library!();
275 : }
276 :
277 : extern crate rustc_lint;
278 : extern crate rustc_session;
279 :
280 : $crate::__maybe_mangle! {
281 : #[allow(clippy::no_mangle_with_rust_abi)]
282 : pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) {
283 : $crate::init_config(sess);
284 : lint_store.register_lints(&[$NAME]);
285 : lint_store.$register_pass_method($pass);
286 : }
287 : }
288 :
289 : rustc_session::declare_lint!($(#[$attr])* $vis $NAME, $Level, $desc);
290 : };
291 : }
292 :
293 : #[rustversion::before(2022-09-08)]
294 : #[doc(hidden)]
295 : #[macro_export]
296 : macro_rules! __make_late_closure {
297 : ($pass:expr) => {
298 : || Box::new($pass)
299 : };
300 : }
301 :
302 : // smoelius: Relevant PR and merge commit:
303 : // - https://github.com/rust-lang/rust/pull/101501
304 : // - https://github.com/rust-lang/rust/commit/87788097b776f8e3662f76627944230684b671bd
305 : #[rustversion::since(2022-09-08)]
306 : #[doc(hidden)]
307 : #[macro_export]
308 : macro_rules! __make_late_closure {
309 : ($pass:expr) => {
310 : |_| Box::new($pass)
311 : };
312 : }
313 :
314 : #[macro_export]
315 : macro_rules! impl_pre_expansion_lint {
316 : ($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr, $pass:expr) => {
317 : $crate::__declare_and_register_lint!(
318 : $(#[$attr])* $vis $NAME,
319 : $Level,
320 : $desc,
321 : register_pre_expansion_pass,
322 : || Box::new($pass)
323 : );
324 : $crate::paste::paste! {
325 : rustc_session::impl_lint_pass!([< $NAME:camel >] => [$NAME]);
326 : }
327 : };
328 : }
329 :
330 : #[macro_export]
331 : macro_rules! impl_early_lint {
332 : ($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr, $pass:expr) => {
333 : $crate::__declare_and_register_lint!(
334 : $(#[$attr])* $vis $NAME,
335 : $Level,
336 : $desc,
337 : register_early_pass,
338 : || Box::new($pass)
339 : );
340 : $crate::paste::paste! {
341 : rustc_session::impl_lint_pass!([< $NAME:camel >] => [$NAME]);
342 : }
343 : };
344 : }
345 :
346 : #[macro_export]
347 : macro_rules! impl_late_lint {
348 : ($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr, $pass:expr) => {
349 : $crate::__declare_and_register_lint!(
350 : $(#[$attr])* $vis $NAME,
351 : $Level,
352 : $desc,
353 : register_late_pass,
354 : $crate::__make_late_closure!($pass)
355 : );
356 : $crate::paste::paste! {
357 : rustc_session::impl_lint_pass!([< $NAME:camel >] => [$NAME]);
358 : }
359 : };
360 : }
361 :
362 : #[macro_export]
363 : macro_rules! declare_pre_expansion_lint {
364 : ($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr) => {
365 : $crate::paste::paste! {
366 : $crate::__declare_and_register_lint!(
367 : $(#[$attr])* $vis $NAME,
368 : $Level,
369 : $desc,
370 : register_pre_expansion_pass,
371 : || Box::new([< $NAME:camel >])
372 : );
373 : rustc_session::declare_lint_pass!([< $NAME:camel >] => [$NAME]);
374 : }
375 : };
376 : }
377 :
378 : #[macro_export]
379 : macro_rules! declare_early_lint {
380 : ($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr) => {
381 : $crate::paste::paste! {
382 : $crate::__declare_and_register_lint!(
383 : $(#[$attr])* $vis $NAME,
384 : $Level,
385 : $desc,
386 : register_early_pass,
387 : || Box::new([< $NAME:camel >])
388 : );
389 : rustc_session::declare_lint_pass!([< $NAME:camel >] => [$NAME]);
390 : }
391 : };
392 : }
393 :
394 : #[macro_export]
395 : macro_rules! declare_late_lint {
396 : ($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr) => {
397 : $crate::paste::paste! {
398 : $crate::__declare_and_register_lint!(
399 : $(#[$attr])* $vis $NAME,
400 : $Level,
401 : $desc,
402 : register_late_pass,
403 : $crate::__make_late_closure!([< $NAME:camel >])
404 : );
405 : rustc_session::declare_lint_pass!([< $NAME:camel >] => [$NAME]);
406 : }
407 : };
408 : }
409 :
410 : /// Reads and deserializes an entry from the workspace's `dylint.toml` file, and returns the default
411 : /// value if the entry is not present.
412 : ///
413 : /// - If the target workspace's `dylint.toml` file contains key `name` and its value can be
414 : /// deserializes as `T`, `config_or_default` returns the deserialized value.
415 : /// - If the target workspace's `dylint.toml` file does not exist or does not contain key `name`,
416 : /// `config_or_default` returns `T::default()`.
417 : /// - If an error occurs (e.g., the value cannot be deserialized as `T`), `config_or_default`
418 : /// panics.
419 : ///
420 : /// Note: `init_config` or `try_init_config` must be called before `config_or_default` is called.
421 : /// However, the `register_lints` function generated by `impl_late_lint`, etc. includes a call to
422 : /// `init_config`.
423 0 : pub fn config_or_default<T: Default + serde::de::DeserializeOwned>(name: &str) -> T {
424 0 : config::<T>(name).map_or_else(
425 0 : |error| {
426 0 : panic!(
427 0 : "Could not parse config as `{}`: {}",
428 0 : type_name::<T>(),
429 0 : error
430 0 : )
431 0 : },
432 0 : Option::unwrap_or_default,
433 0 : )
434 0 : }
435 :
436 : /// Reads and deserializes an entry from the workspace's `dylint.toml` file.
437 : ///
438 : /// Returns:
439 : /// - `Ok(Some(...))` if the target workspace's `dylint.toml` file contains key `name` and its value
440 : /// can be deserialized as `T`
441 : /// - `Ok(None)` if the target workspace's `dylint.toml` file does not exist or does not contain key
442 : /// `name`
443 : /// - `Err(...)` if an error occurs (e.g., the value cannot be deserialized as `T`)
444 : ///
445 : /// Note: `init_config` or `try_init_config` must be called before `config` is called. However, the
446 : /// `register_lints` function generated by `impl_late_lint`, etc. includes a call to `init_config`.
447 0 : pub fn config<T: serde::de::DeserializeOwned>(name: &str) -> ConfigResult<Option<T>> {
448 0 : let toml = config_toml(name)?;
449 0 : toml.map(toml::Value::try_into::<T>)
450 0 : .transpose()
451 0 : .map_err(Into::into)
452 0 : }
453 :
454 : /// Reads an entry from the workspace's `dylint.toml` file as a raw `toml::Value`.
455 : ///
456 : /// Returns:
457 : /// - `Ok(Some(...))` if the target workspace's `dylint.toml` file contains key `name`
458 : /// - `Ok(None)` if the target workspace's `dylint.toml` file does not exist or does not contain key
459 : /// `name`
460 : /// - `Err(...)` if an error occurs (e.g., `init_config` was not called)
461 : ///
462 : /// Note: `init_config` or `try_init_config` must be called before `config_toml` is called. However,
463 : /// the `register_lints` function generated by `impl_late_lint`, etc. includes a call to
464 : /// `init_config`.
465 0 : pub fn config_toml(name: &str) -> ConfigResult<Option<toml::Value>> {
466 0 : let Some(config_table) = config::get() else {
467 0 : return Err(ConfigError::other(
468 0 : "Config is not initialized; `init_config` should have been called from \
469 0 : `register_lints`"
470 0 : .into(),
471 0 : ));
472 : };
473 0 : Ok(config_table.get(name).cloned())
474 0 : }
475 :
476 : /// A wrapper around `try_init_config`. Calls `rustc_session::early_error` if `try_init_config`
477 : /// returns an error.
478 : ///
479 : /// Note: `init_config` or `try_init_config` must be called before `config_or_default`, `config`, or
480 : /// `config_toml` is called. However, the `register_lints` function generated by `impl_late_lint`,
481 : /// etc. includes a call to `init_config`.
482 0 : pub fn init_config(sess: &rustc_session::Session) {
483 0 : try_init_config(sess).unwrap_or_else(|err| {
484 0 : let msg = format!("could not read configuration file: {err}");
485 0 : early_error(msg);
486 0 : });
487 0 : }
488 :
489 : trait ParseSess {
490 : fn parse_sess(&self) -> &rustc_session::parse::ParseSess;
491 : }
492 :
493 : impl ParseSess for rustc_session::Session {
494 : #[rustversion::before(2024-03-05)]
495 : fn parse_sess(&self) -> &rustc_session::parse::ParseSess {
496 : &self.parse_sess
497 : }
498 :
499 : #[rustversion::since(2024-03-05)]
500 0 : fn parse_sess(&self) -> &rustc_session::parse::ParseSess {
501 0 : &self.psess
502 0 : }
503 : }
504 :
505 : /// Reads the target workspace's `dylint.toml` file and parses it as a `toml::value::Table`.
506 : ///
507 : /// Note: `init_config` or `try_init_config` must be called before `config_or_default`, `config`, or
508 : /// `config_toml` is called. However, the `register_lints` function generated by `impl_late_lint`,
509 : /// etc. includes a call to `init_config`.
510 0 : pub fn try_init_config(sess: &rustc_session::Session) -> ConfigResult<()> {
511 0 : let result = try_init_config_guarded(sess);
512 0 :
513 0 : // smoelius: If we're returning `Ok(())`, ensure that `config::get()` will later return
514 0 : // `Some(..)`.
515 0 : if result.is_ok() && config::get().is_none() {
516 0 : config::init_from_string("").unwrap();
517 0 : }
518 :
519 0 : result
520 0 : }
521 :
522 : #[allow(clippy::empty_line_after_outer_attr)]
523 : #[cfg_attr(dylint_lib = "supplementary", allow(commented_code))]
524 0 : fn try_init_config_guarded(sess: &rustc_session::Session) -> ConfigResult<()> {
525 0 : if config::get().is_some() {
526 0 : return Ok(());
527 0 : }
528 :
529 0 : if let Ok(value) = std::env::var(env::DYLINT_TOML) {
530 0 : config::init_from_string(&value)?;
531 0 : sess.parse_sess().env_depinfo.lock().insert((
532 0 : Symbol::intern(env::DYLINT_TOML),
533 0 : Some(Symbol::intern(&value)),
534 0 : ));
535 0 : return Ok(());
536 0 : }
537 :
538 0 : let Some(local_crate_source_file) =
539 0 : local_crate_source_file(sess).filter(|path| *path != PathBuf::new())
540 : else {
541 0 : return Ok(());
542 : };
543 :
544 : #[rustfmt::skip]
545 : // smoelius: Canonicalizing `local_crate_source_file` causes errors like the following on
546 : // Windows:
547 : //
548 : // error: could not read configuration file: cargo metadata error: `cargo metadata` exited with an error: error: failed to load manifest for dependency `await_holding_span_guard`
549 : //
550 : // Caused by:
551 : // failed to parse manifest at `D:\a\dylint\dylint\examples\general\await_holding_span_guard\Cargo.toml`
552 : //
553 : // Caused by:
554 : // error inheriting `clippy_utils` from workspace root manifest's `workspace.dependencies.clippy_utils`
555 : //
556 : // Caused by:
557 : // `workspace.dependencies` was not defined
558 : //
559 : // The issue is that `canonicalize` prepends `\\?\` to the path, and such "verbatim" paths
560 : // cause problems for Cargo. See the following GitHub issue for more information:
561 : // https://github.com/rust-lang/cargo/issues/9770#issuecomment-993069234
562 : //
563 : // For reasons that I don't understand, fixing this problem in Cargo would be difficult.
564 :
565 : /* let local_crate_source_file = local_crate_source_file.canonicalize().map_err(|error| {
566 : ConfigErrorInner::Io(
567 : format!("Could not canonicalize {local_crate_source_file:?}"),
568 : error,
569 : )
570 : })?; */
571 :
572 0 : let mut parent = local_crate_source_file
573 0 : .parent()
574 0 : .ok_or_else(|| ConfigError::other("Could not get parent directory".into()))?;
575 :
576 : // smoelius: https://users.rust-lang.org/t/pathbuf-equivalent-to-string-is-empty/24823
577 0 : if parent.as_os_str().is_empty() {
578 0 : parent = Path::new(".");
579 0 : };
580 :
581 0 : let result = cargo_metadata::MetadataCommand::new()
582 0 : .current_dir(parent)
583 0 : .no_deps()
584 0 : .exec();
585 :
586 0 : match result {
587 0 : Err(cargo_metadata::Error::CargoMetadata { stderr })
588 0 : if stderr.contains("could not find `Cargo.toml`") => {}
589 : _ => {
590 0 : let metadata = result?;
591 :
592 0 : let value = config::try_init_with_metadata(&metadata)?;
593 :
594 0 : if let Some(s) = &value {
595 0 : sess.parse_sess()
596 0 : .file_depinfo
597 0 : .lock()
598 0 : .insert(Symbol::intern(s));
599 0 : }
600 : }
601 : }
602 :
603 0 : Ok(())
604 0 : }
605 :
606 : #[rustversion::before(2023-01-19)]
607 : fn local_crate_source_file(sess: &rustc_session::Session) -> Option<PathBuf> {
608 : sess.local_crate_source_file.clone()
609 : }
610 :
611 : // smoelius: Relevant PR and merge commit:
612 : // - https://github.com/rust-lang/rust/pull/106810
613 : // - https://github.com/rust-lang/rust/commit/65d2f2a5f9c323c88d1068e8e90d0b47a20d491c
614 : #[rustversion::all(since(2023-01-19), before(2024-03-29))]
615 : fn local_crate_source_file(sess: &rustc_session::Session) -> Option<PathBuf> {
616 : sess.local_crate_source_file()
617 : }
618 :
619 : // smoelius: Relevant PR and merge commit:
620 : // - https://github.com/rust-lang/rust/pull/122450
621 : // - https://github.com/rust-lang/rust/commit/685927aae69657b46323cffbeb0062835bd7fa2b
622 : #[rustversion::since(2024-03-29)]
623 0 : fn local_crate_source_file(sess: &rustc_session::Session) -> Option<PathBuf> {
624 : use rustc_span::RealFileName;
625 0 : sess.local_crate_source_file()
626 0 : .and_then(RealFileName::into_local_path)
627 0 : }
628 :
629 : #[rustversion::before(2023-06-28)]
630 : fn early_error(msg: String) -> ! {
631 : rustc_session::early_error(
632 : rustc_session::config::ErrorOutputType::default(),
633 : Box::leak(msg.into_boxed_str()) as &str,
634 : )
635 : }
636 :
637 : #[rustversion::since(2023-06-28)]
638 : extern crate rustc_errors;
639 :
640 : #[rustversion::all(since(2023-06-28), before(2023-12-18))]
641 : fn early_error(msg: impl Into<rustc_errors::DiagnosticMessage>) -> ! {
642 : let handler =
643 : rustc_session::EarlyErrorHandler::new(rustc_session::config::ErrorOutputType::default());
644 : handler.early_error(msg)
645 : }
646 :
647 : #[rustversion::all(since(2023-12-18), before(2023-12-23))]
648 : fn early_error(msg: impl Into<rustc_errors::DiagnosticMessage>) -> ! {
649 : let handler =
650 : rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
651 : handler.early_error(msg)
652 : }
653 :
654 : #[rustversion::all(since(2023-12-23), before(2024-03-05))]
655 : fn early_error(msg: impl Into<rustc_errors::DiagnosticMessage>) -> ! {
656 : let handler =
657 : rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
658 : handler.early_fatal(msg)
659 : }
660 :
661 : #[rustversion::since(2024-03-05)]
662 0 : fn early_error(msg: impl Into<rustc_errors::DiagMessage>) -> ! {
663 0 : let handler =
664 0 : rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
665 0 : handler.early_fatal(msg)
666 : }
|