parent
98d0402d87
commit
f991ebaa78
4 changed files with 152 additions and 0 deletions
maud_macros/src
|
@ -3,6 +3,8 @@
|
|||
#![feature(slice_patterns)]
|
||||
#![feature(rustc_private)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate rustc;
|
||||
extern crate rustc_plugin;
|
||||
extern crate syntax;
|
||||
extern crate maud;
|
||||
|
@ -14,6 +16,7 @@ use syntax::ext::base::{DummyResult, ExtCtxt, MacEager, MacResult};
|
|||
use syntax::print::pprust;
|
||||
use syntax::tokenstream::TokenTree;
|
||||
|
||||
mod lints;
|
||||
mod parse;
|
||||
mod render;
|
||||
|
||||
|
@ -41,4 +44,5 @@ fn expand_html_debug<'cx>(cx: &'cx mut ExtCtxt, sp: Span, args: &[TokenTree]) ->
|
|||
pub fn plugin_registrar(reg: &mut Registry) {
|
||||
reg.register_macro("html", expand_html);
|
||||
reg.register_macro("html_debug", expand_html_debug);
|
||||
lints::register_lints(reg);
|
||||
}
|
||||
|
|
42
maud_macros/src/lints/doctype_html.rs
Normal file
42
maud_macros/src/lints/doctype_html.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use rustc::hir::{Expr, ExprCall, ExprLit, ExprPath};
|
||||
use rustc::lint::{LateContext, LateLintPass, LintArray, LintContext, LintPass};
|
||||
use std::ascii::AsciiExt;
|
||||
use super::util::match_def_path;
|
||||
use syntax::ast::LitKind;
|
||||
|
||||
declare_lint! {
|
||||
pub MAUD_DOCTYPE_HTML,
|
||||
Warn,
|
||||
"suggest using the maud::DOCTYPE_HTML constant"
|
||||
}
|
||||
|
||||
pub struct DoctypeHtml;
|
||||
|
||||
impl LintPass for DoctypeHtml {
|
||||
fn get_lints(&self) -> LintArray {
|
||||
lint_array![MAUD_DOCTYPE_HTML]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for DoctypeHtml {
|
||||
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
|
||||
if_let_chain! {[
|
||||
// It's a function call...
|
||||
let ExprCall(ref path_expr, ref args) = expr.node,
|
||||
// ... where the argument is a literal "<!doctype html>"
|
||||
let Some(first_arg) = args.first(),
|
||||
let ExprLit(ref lit) = first_arg.node,
|
||||
let LitKind::Str(s, _) = lit.node,
|
||||
s.as_str().eq_ignore_ascii_case("<!doctype html>"),
|
||||
], {
|
||||
// ... and the callee is `maud::PreEscaped`
|
||||
if let ExprPath(ref qpath) = path_expr.node {
|
||||
let def_id = cx.tcx.tables().qpath_def(qpath, path_expr.id).def_id();
|
||||
if match_def_path(cx, def_id, &["maud", "PreEscaped", "{{constructor}}"]) {
|
||||
cx.struct_span_lint(MAUD_DOCTYPE_HTML, expr.span,
|
||||
"use `maud::DOCTYPE_HTML` instead").emit();
|
||||
}
|
||||
}
|
||||
}}
|
||||
}
|
||||
}
|
14
maud_macros/src/lints/mod.rs
Normal file
14
maud_macros/src/lints/mod.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use rustc_plugin::Registry;
|
||||
|
||||
#[macro_use]
|
||||
mod util;
|
||||
|
||||
pub mod doctype_html;
|
||||
|
||||
pub fn register_lints(reg: &mut Registry) {
|
||||
reg.register_late_lint_pass(Box::new(doctype_html::DoctypeHtml));
|
||||
|
||||
reg.register_lint_group("maud", vec![
|
||||
doctype_html::MAUD_DOCTYPE_HTML,
|
||||
]);
|
||||
}
|
92
maud_macros/src/lints/util.rs
Normal file
92
maud_macros/src/lints/util.rs
Normal file
|
@ -0,0 +1,92 @@
|
|||
//! Miscellaneous utilities for writing lints.
|
||||
//!
|
||||
//! Most of these are adapted from Clippy.
|
||||
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::lint::LateContext;
|
||||
use rustc::ty;
|
||||
use syntax::symbol::{InternedString, Symbol};
|
||||
|
||||
/// Produce a nested chain of if-lets and ifs from the patterns:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// if_let_chain! {[
|
||||
/// let Some(y) = x,
|
||||
/// y.len() == 2,
|
||||
/// let Some(z) = y,
|
||||
/// ], {
|
||||
/// block
|
||||
/// }}
|
||||
/// ```
|
||||
///
|
||||
/// becomes
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// if let Some(y) = x {
|
||||
/// if y.len() == 2 {
|
||||
/// if let Some(z) = y {
|
||||
/// block
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! if_let_chain {
|
||||
([let $pat:pat = $expr:expr, $($tt:tt)+], $block:block) => {
|
||||
if let $pat = $expr {
|
||||
if_let_chain!{ [$($tt)+], $block }
|
||||
}
|
||||
};
|
||||
([let $pat:pat = $expr:expr], $block:block) => {
|
||||
if let $pat = $expr {
|
||||
$block
|
||||
}
|
||||
};
|
||||
([let $pat:pat = $expr:expr,], $block:block) => {
|
||||
if let $pat = $expr {
|
||||
$block
|
||||
}
|
||||
};
|
||||
([$expr:expr, $($tt:tt)+], $block:block) => {
|
||||
if $expr {
|
||||
if_let_chain!{ [$($tt)+], $block }
|
||||
}
|
||||
};
|
||||
([$expr:expr], $block:block) => {
|
||||
if $expr {
|
||||
$block
|
||||
}
|
||||
};
|
||||
([$expr:expr,], $block:block) => {
|
||||
if $expr {
|
||||
$block
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Check if a `DefId`'s path matches the given absolute type path usage.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```rust,ignore
|
||||
/// match_def_path(cx, id, &["core", "option", "Option"])
|
||||
/// ```
|
||||
pub fn match_def_path(cx: &LateContext, def_id: DefId, path: &[&str]) -> bool {
|
||||
struct AbsolutePathBuffer {
|
||||
names: Vec<InternedString>,
|
||||
}
|
||||
|
||||
impl ty::item_path::ItemPathBuffer for AbsolutePathBuffer {
|
||||
fn root_mode(&self) -> &ty::item_path::RootMode {
|
||||
const ABSOLUTE: &'static ty::item_path::RootMode = &ty::item_path::RootMode::Absolute;
|
||||
ABSOLUTE
|
||||
}
|
||||
|
||||
fn push(&mut self, text: &str) {
|
||||
self.names.push(Symbol::intern(text).as_str());
|
||||
}
|
||||
}
|
||||
|
||||
let mut apb = AbsolutePathBuffer { names: vec![] };
|
||||
cx.tcx.push_item_path(&mut apb, def_id);
|
||||
apb.names.len() == path.len() && apb.names.iter().zip(path.iter()).all(|(a, &b)| &**a == b)
|
||||
}
|
Loading…
Add table
Reference in a new issue