Lint against PreEscaped("<!DOCTYPE html>")

See 
This commit is contained in:
Chris Wong 2016-12-26 22:04:45 +13:00
parent 98d0402d87
commit f991ebaa78
4 changed files with 152 additions and 0 deletions

View file

@ -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);
}

View 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();
}
}
}}
}
}

View 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,
]);
}

View 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)
}