1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
#![allow(clippy::type_complexity)]
use rustc_driver::Callbacks;
use rustc_errors::FatalError;
use rustc_interface::interface::Config;
use rustc_lint::LintStore;
use rustc_session::config::ErrorOutputType;
use rustc_session::EarlyDiagCtxt;
use rustc_span::Symbol;
use std::sync::Arc;
struct Lints {
callback: Arc<Box<dyn Fn(&mut LintStore) + Send + Sync + 'static>>,
/// If one of these files is modified, the linter needs to be re-run.
tracked_files: Arc<Vec<String>>,
}
impl Callbacks for Lints {
fn config(&mut self, config: &mut Config) {
// Should always be `None` but just in case...
let previous = config.register_lints.take();
let tracked_files = Arc::clone(&self.tracked_files);
config.psess_created = Some(Box::new(move |parse_sess| {
// In here, we insert the files that, if modified, will tell cargo that the command
// needs to be re-run.
if tracked_files.is_empty() {
return;
}
let file_depinfo = parse_sess.file_depinfo.get_mut();
for tracked_file in tracked_files.iter() {
file_depinfo.insert(Symbol::intern(tracked_file));
}
}));
let callback = Arc::clone(&self.callback);
config.register_lints = Some(Box::new(move |sess, lint_store| {
if let Some(previous) = &previous {
(previous)(sess, lint_store);
}
(*callback)(lint_store);
}));
}
}
/// If you want to create a linter, this the function you want to use.
///
/// * `args` is what is provided to the compiler.
/// * `tracked_files` is the files which will trigger a re-compilation if they are modified.
/// * `callback` is called when everything is setup. This is where you will register your lints
/// before they are run by rustc directly. It provides a mutable reference to the [`LintStore`]
/// type.
///
/// Take a look at the `examples/lint.rs` file if you want an example on how to create lints.
///
/// **VERY IMPORTANT TO NOTE**: if you want to run this code on a crate with dependencies, you'll
/// need to pass the according options so that `rustc` knows where to look for them. otherwise it
/// will simply fail to compile and the `callback` won't be called. A good example of the list
/// of the expected arguments can be seen when you run `cargo build -v`.
///
/// Take a look at [`cargo_integration`](crate::cargo_integration) and at
/// [rustc-tools-example](https://github.com/GuillaumeGomez/rustc-tools-example) to see how to
/// write a cargo integration.
///
/// [`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
pub fn with_lints<F: Fn(&mut LintStore) + Send + Sync + 'static>(
args: &[String],
tracked_files: Vec<String>,
callback: F,
) -> Result<(), FatalError> {
with_lints_and_error_output(args, tracked_files, ErrorOutputType::default(), callback)
}
/// If you want to create a linter, this the function you want to use.
///
/// * `args` is what is provided to the compiler.
/// * `tracked_files` is the files which will trigger a re-compilation if they are modified.
/// * `error_output` is the equivalent of `--output-format`, it is quite useful if you want
/// to run `rustfix` on the warnings/errors generated by your linter.
/// * `callback` is called when everything is setup. This is where you will register your lints
/// before they are run by rustc directly. It provides a mutable reference to the [`LintStore`]
/// type.
///
/// Take a look at the `examples/lint.rs` file if you want an example on how to create lints.
///
/// **VERY IMPORTANT TO NOTE**: if you want to run this code on a crate with dependencies, you'll
/// need to pass the according options so that `rustc` knows where to look for them. otherwise it
/// will simply fail to compile and the `callback` won't be called. A good example of the list
/// of the expected arguments can be seen when you run `cargo build -v`.
///
/// Take a look at [`cargo_integration`](crate::cargo_integration) and at
/// [rustc-tools-example](https://github.com/GuillaumeGomez/rustc-tools-example) to see how to
/// write a cargo integration.
///
/// [`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
pub fn with_lints_and_error_output<F: Fn(&mut LintStore) + Send + Sync + 'static>(
args: &[String],
tracked_files: Vec<String>,
error_output: ErrorOutputType,
callback: F,
) -> Result<(), FatalError> {
let handler = EarlyDiagCtxt::new(error_output);
rustc_driver::init_rustc_env_logger(&handler);
if rustc_driver::catch_fatal_errors(move || {
rustc_driver::RunCompiler::new(
args,
&mut Lints {
callback: Arc::new(Box::new(callback)),
tracked_files: Arc::new(tracked_files),
},
)
.run()
.map(|_| ())
})
.is_err()
{
Err(FatalError)
} else {
Ok(())
}
}