#![feature(rustc_private)]
#![allow(clippy::useless_attribute)]
#![cfg_attr(dylint_lib = "general", allow(crate_wide_allow))]
#![warn(unused_extern_crates)]
#[allow(unused_extern_crates)]
extern crate rustc_driver;
extern crate rustc_data_structures;
extern crate rustc_session;
extern crate rustc_span;
use dylint_internal::{config, env};
use rustc_span::Symbol;
use std::{
any::type_name,
path::{Path, PathBuf},
};
pub use config::{Error as ConfigError, Result as ConfigResult};
pub const DYLINT_VERSION: &str = "0.1.0";
pub use paste;
#[macro_export]
macro_rules! dylint_library {
() => {
#[allow(unused_extern_crates)]
extern crate rustc_driver;
#[doc(hidden)]
#[unsafe(no_mangle)]
pub extern "C" fn dylint_version() -> *mut std::os::raw::c_char {
std::ffi::CString::new($crate::DYLINT_VERSION)
.unwrap()
.into_raw()
}
};
}
#[cfg(not(feature = "constituent"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __maybe_exclude {
($item:item) => {
$item
};
}
#[cfg(feature = "constituent")]
#[doc(hidden)]
#[macro_export]
macro_rules! __maybe_exclude {
($item:item) => {};
}
#[cfg(not(feature = "constituent"))]
#[doc(hidden)]
#[macro_export]
macro_rules! __maybe_mangle {
($item:item) => {
#[unsafe(no_mangle)]
$item
};
}
#[cfg(feature = "constituent")]
#[doc(hidden)]
#[macro_export]
macro_rules! __maybe_mangle {
($item:item) => {
$item
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __declare_and_register_lint {
($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr, $register_pass_method:ident, $pass:expr) => {
$crate::__maybe_exclude! {
$crate::dylint_library!();
}
extern crate rustc_lint;
extern crate rustc_session;
$crate::__maybe_mangle! {
#[allow(clippy::no_mangle_with_rust_abi)]
pub fn register_lints(sess: &rustc_session::Session, lint_store: &mut rustc_lint::LintStore) {
$crate::init_config(sess);
lint_store.register_lints(&[$NAME]);
lint_store.$register_pass_method($pass);
}
}
rustc_session::declare_lint!($(#[$attr])* $vis $NAME, $Level, $desc);
};
}
#[rustversion::before(2022-09-08)]
#[doc(hidden)]
#[macro_export]
macro_rules! __make_late_closure {
($pass:expr) => {
|| Box::new($pass)
};
}
#[rustversion::since(2022-09-08)]
#[doc(hidden)]
#[macro_export]
macro_rules! __make_late_closure {
($pass:expr) => {
|_| Box::new($pass)
};
}
#[macro_export]
macro_rules! impl_pre_expansion_lint {
($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr, $pass:expr) => {
$crate::__declare_and_register_lint!(
$(#[$attr])* $vis $NAME,
$Level,
$desc,
register_pre_expansion_pass,
|| Box::new($pass)
);
$crate::paste::paste! {
rustc_session::impl_lint_pass!([< $NAME:camel >] => [$NAME]);
}
};
}
#[macro_export]
macro_rules! impl_early_lint {
($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr, $pass:expr) => {
$crate::__declare_and_register_lint!(
$(#[$attr])* $vis $NAME,
$Level,
$desc,
register_early_pass,
|| Box::new($pass)
);
$crate::paste::paste! {
rustc_session::impl_lint_pass!([< $NAME:camel >] => [$NAME]);
}
};
}
#[macro_export]
macro_rules! impl_late_lint {
($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr, $pass:expr) => {
$crate::__declare_and_register_lint!(
$(#[$attr])* $vis $NAME,
$Level,
$desc,
register_late_pass,
$crate::__make_late_closure!($pass)
);
$crate::paste::paste! {
rustc_session::impl_lint_pass!([< $NAME:camel >] => [$NAME]);
}
};
}
#[macro_export]
macro_rules! declare_pre_expansion_lint {
($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr) => {
$crate::paste::paste! {
$crate::__declare_and_register_lint!(
$(#[$attr])* $vis $NAME,
$Level,
$desc,
register_pre_expansion_pass,
|| Box::new([< $NAME:camel >])
);
rustc_session::declare_lint_pass!([< $NAME:camel >] => [$NAME]);
}
};
}
#[macro_export]
macro_rules! declare_early_lint {
($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr) => {
$crate::paste::paste! {
$crate::__declare_and_register_lint!(
$(#[$attr])* $vis $NAME,
$Level,
$desc,
register_early_pass,
|| Box::new([< $NAME:camel >])
);
rustc_session::declare_lint_pass!([< $NAME:camel >] => [$NAME]);
}
};
}
#[macro_export]
macro_rules! declare_late_lint {
($(#[$attr:meta])* $vis:vis $NAME:ident, $Level:ident, $desc:expr) => {
$crate::paste::paste! {
$crate::__declare_and_register_lint!(
$(#[$attr])* $vis $NAME,
$Level,
$desc,
register_late_pass,
$crate::__make_late_closure!([< $NAME:camel >])
);
rustc_session::declare_lint_pass!([< $NAME:camel >] => [$NAME]);
}
};
}
pub fn config_or_default<T: Default + serde::de::DeserializeOwned>(name: &str) -> T {
config::<T>(name).map_or_else(
|error| {
panic!(
"Could not parse config as `{}`: {}",
type_name::<T>(),
error
)
},
Option::unwrap_or_default,
)
}
pub fn config<T: serde::de::DeserializeOwned>(name: &str) -> ConfigResult<Option<T>> {
let toml = config_toml(name)?;
toml.map(toml::Value::try_into::<T>)
.transpose()
.map_err(Into::into)
}
pub fn config_toml(name: &str) -> ConfigResult<Option<toml::Value>> {
let Some(config_table) = config::get() else {
return Err(ConfigError::other(
"Config is not initialized; `init_config` should have been called from \
`register_lints`"
.into(),
));
};
Ok(config_table.get(name).cloned())
}
pub fn init_config(sess: &rustc_session::Session) {
try_init_config(sess).unwrap_or_else(|err| {
let msg = format!("could not read configuration file: {err}");
early_error(msg);
});
}
trait EnvDepInfo {
fn env_depinfo(
&self,
) -> &rustc_data_structures::sync::Lock<
rustc_data_structures::fx::FxIndexSet<(Symbol, Option<Symbol>)>,
>;
}
impl EnvDepInfo for rustc_session::Session {
#[rustversion::before(2024-03-05)]
fn env_depinfo(
&self,
) -> &rustc_data_structures::sync::Lock<
rustc_data_structures::fx::FxIndexSet<(Symbol, Option<Symbol>)>,
> {
&self.parse_sess.env_depinfo
}
#[rustversion::all(since(2024-03-05), before(2026-03-18))]
fn env_depinfo(
&self,
) -> &rustc_data_structures::sync::Lock<
rustc_data_structures::fx::FxIndexSet<(Symbol, Option<Symbol>)>,
> {
&self.psess.env_depinfo
}
#[rustversion::since(2026-03-18)]
fn env_depinfo(
&self,
) -> &rustc_data_structures::sync::Lock<
rustc_data_structures::fx::FxIndexSet<(Symbol, Option<Symbol>)>,
> {
&self.env_depinfo
}
}
trait FileDepInfo {
fn file_depinfo(
&self,
) -> &rustc_data_structures::sync::Lock<rustc_data_structures::fx::FxIndexSet<Symbol>>;
}
impl FileDepInfo for rustc_session::Session {
#[rustversion::before(2024-03-05)]
fn file_depinfo(
&self,
) -> &rustc_data_structures::sync::Lock<rustc_data_structures::fx::FxIndexSet<Symbol>> {
&self.parse_sess.file_depinfo
}
#[rustversion::all(since(2024-03-05), before(2026-03-18))]
fn file_depinfo(
&self,
) -> &rustc_data_structures::sync::Lock<rustc_data_structures::fx::FxIndexSet<Symbol>> {
&self.psess.file_depinfo
}
#[rustversion::since(2026-03-18)]
fn file_depinfo(
&self,
) -> &rustc_data_structures::sync::Lock<rustc_data_structures::fx::FxIndexSet<Symbol>> {
&self.file_depinfo
}
}
pub fn try_init_config(sess: &rustc_session::Session) -> ConfigResult<()> {
let result = try_init_config_guarded(sess);
if result.is_ok() && config::get().is_none() {
config::init_from_string("").unwrap();
}
result
}
#[allow(clippy::empty_line_after_outer_attr)]
#[cfg_attr(dylint_lib = "supplementary", allow(commented_out_code))]
fn try_init_config_guarded(sess: &rustc_session::Session) -> ConfigResult<()> {
if config::get().is_some() {
return Ok(());
}
if let Ok(value) = std::env::var(env::DYLINT_TOML) {
config::init_from_string(&value)?;
sess.env_depinfo().lock().insert((
Symbol::intern(env::DYLINT_TOML),
Some(Symbol::intern(&value)),
));
return Ok(());
}
let Some(local_crate_source_file) =
local_crate_source_file(sess).filter(|path| *path != PathBuf::new())
else {
return Ok(());
};
#[rustfmt::skip]
let mut parent = local_crate_source_file
.parent()
.ok_or_else(|| ConfigError::other("Could not get parent directory".into()))?;
if parent.as_os_str().is_empty() {
parent = Path::new(".");
};
let result = cargo_metadata::MetadataCommand::new()
.current_dir(parent)
.no_deps()
.exec();
match result {
Err(cargo_metadata::Error::CargoMetadata { stderr })
if stderr.contains("could not find `Cargo.toml`") => {}
_ => {
let metadata = result?;
let value = config::try_init_with_metadata(&metadata)?;
if let Some(s) = &value {
sess.file_depinfo().lock().insert(Symbol::intern(s));
}
}
}
Ok(())
}
#[rustversion::before(2023-01-19)]
fn local_crate_source_file(sess: &rustc_session::Session) -> Option<PathBuf> {
sess.local_crate_source_file.clone()
}
#[rustversion::all(since(2023-01-19), before(2024-03-29))]
fn local_crate_source_file(sess: &rustc_session::Session) -> Option<PathBuf> {
sess.local_crate_source_file()
}
#[rustversion::since(2024-03-29)]
fn local_crate_source_file(sess: &rustc_session::Session) -> Option<PathBuf> {
use rustc_span::RealFileName;
sess.local_crate_source_file()
.and_then(RealFileName::into_local_path)
}
#[rustversion::before(2023-06-28)]
fn early_error(msg: String) -> ! {
rustc_session::early_error(
rustc_session::config::ErrorOutputType::default(),
Box::leak(msg.into_boxed_str()) as &str,
)
}
#[rustversion::since(2023-06-28)]
extern crate rustc_errors;
#[rustversion::all(since(2023-06-28), before(2023-12-18))]
fn early_error(msg: impl Into<rustc_errors::DiagnosticMessage>) -> ! {
let handler =
rustc_session::EarlyErrorHandler::new(rustc_session::config::ErrorOutputType::default());
handler.early_error(msg)
}
#[rustversion::all(since(2023-12-18), before(2023-12-23))]
fn early_error(msg: impl Into<rustc_errors::DiagnosticMessage>) -> ! {
let handler =
rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
handler.early_error(msg)
}
#[rustversion::all(since(2023-12-23), before(2024-03-05))]
fn early_error(msg: impl Into<rustc_errors::DiagnosticMessage>) -> ! {
let handler =
rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
handler.early_fatal(msg)
}
#[rustversion::since(2024-03-05)]
fn early_error(msg: impl Into<rustc_errors::DiagMessage>) -> ! {
let handler =
rustc_session::EarlyDiagCtxt::new(rustc_session::config::ErrorOutputType::default());
handler.early_fatal(msg)
}