How to add customized lint pass for rustc?

I recently learned from an old technique article that we can add customized lint pass with compiler plugin. This is quite a convenient way to do some lightweight code check during compilation. However, compiler plugin is already a deprecated feature, which may be removed from compiler in the near future. So I wonder what's the proper way to add customized lint pass now without compiler plugin?

Below are a simple example code I used now, which add a simple lint pass with compiler plugin. (I omit codes that are not important)

//! my-lint-plugin/src/
//! define a new lint plugin

use rustc_lint::{LateContext, LateLintPass};
    "A check for demonstration"
declare_lint_pass!(Pass => [MY_CHECKER]);

impl<'tcx> LateLintPass<'tcx> for Pass {
    fn check_fn(
        &mut self,
        late_ctx: &LateContext<'tcx>,
    ) {
        // check each function

fn __rustc_plugin_registrar(reg: &mut Registry) {
    reg.lint_store.register_late_pass(|_| box Pass);

//! my-lint-plugin-test/src/
//! test the plugin. The plugin will work on this crate.
//! When building this crate, our lint pass will be called.
#![feature(plugin, register_tool)]
#![plugin(my_lint_plugin)] // This feature was deprecated!

fn main() {
    // ......

For now probably the only way is using the external tool which uses rustc as library, as it is done with clippy.

Thanks a lot. I will look at how clippy works.