-
Notifications
You must be signed in to change notification settings - Fork 144
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Lint against
PreEscaped("<!DOCTYPE html>")
See #66
- Loading branch information
1 parent
7925fba
commit 0a6255c
Showing
4 changed files
with
152 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} | ||
}} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
]); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |