I'm wanting to write some custom lints and am following the following guides linked below. I got stuck trying to implement my idea. Maybe someone more familiar with clippy/rustc internals might be able to shed some light on the method I need to call to get the type information I need.
I want to create a lint which forbids implementing from and try_from from tuples. Such as
struct Foo;
impl From<(u32,u32)> for Foo {...}
Here's where I'm at:
#![feature(rustc_private)]
#![feature(let_chains)]
extern crate rustc_ast;
extern crate rustc_hir;
extern crate rustc_lint;
extern crate rustc_session;
extern crate rustc_span;
mod disallowed_from_tuple {
use rustc_hir::{self as hir};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_tool_lint! {
pub lint::IMPL_FROM_TUPLE,
Warn,
"Warn if implemeting From or TryFrom from a tuple",
report_in_external_macro: false
}
declare_lint_pass!(ImplFromTuple => [IMPL_FROM_TUPLE]);
impl<'tcx> LateLintPass<'tcx> for ImplFromTuple {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
let tcx = cx.tcx;
if let hir::ItemKind::Impl(impl_) = item.kind
// the trait is a `From` or `TryFrom` implementation
&& let Some(trait_) = &impl_.of_trait
&& let Some(did) = trait_.trait_def_id()
&& (tcx.is_diagnostic_item(sym::From, did) ||
tcx.is_diagnostic_item(sym::TryFrom, did) )
// for From<T>, TryFrom<T> T is a tuple
// stuck here unsure how to see if T is a tuple
&& let generic = tcx.generics_of(did)
// && let x = tcx.impl_subject(did)
{
eprintln!("{generic:?}");
let warning_msg = if tcx.is_diagnostic_item(sym::From, did) {
"implementing From using a tuple"
} else {
"implementing TryFrom using a tuple"
};
cx.span_lint(IMPL_FROM_TUPLE, trait_.path.span, |diag| { diag.primary_message(warning_msg); });
}
}
}
}
use rustc_lint::LintStore;
use crate::disallowed_from_tuple::ImplFromTuple;
fn main() -> Result<(), ()> {
let args: Vec<String> = std::env::args().collect();
if args.is_empty() {
eprintln!("Missing file operand");
return Err(());
}
println!("Running lint example with arguments `{:?}`", args);
rustc_tools::with_lints(&args, vec![], |store: &mut LintStore| {
store.register_late_pass(|_| Box::new(ImplFromTuple));
})
.map_err(|_| ())
}
I've been testing on this foo.rs
pub fn foo() {}
fn bar() {}
pub fn generics<A: Sized>(_: A) {
println!("hello");
}
struct Bar;
impl From<i32> for Bar {
fn from(value: i32) -> Self {
Bar
}
}
impl From<(i32, i32)> for Bar {
fn from(value: (i32,i32)) -> Self {
Bar
}
}
fn main() {
foo();
bar();
generics(0i8);
}
playground is not runnable because it requires some rust components which aren't included by default
[package]
name = "custom-lints"
version = "0.1.0"
edition = "2021"
rustc-workspace-hack = "1.0.0"
[dependencies]
rustc-tools = "0.80"
I'm using the rust docs from the second link
https://blog.guillaume-gomez.fr/articles/2024-01-18+Writing+your+own+Rust+linter
https://guillaumegomez.github.io/rustc-tools/compiler/0.80/doc/rustc_middle/ty/context/struct.TyCtxt.html
Any help is appreciated.