From ceb9a8bdd79e4c2081d88d40d3a8a2b6f080268f Mon Sep 17 00:00:00 2001 From: llogiq Date: Sun, 7 Feb 2016 22:50:54 +0100 Subject: [PATCH] regex macro lint --- src/regex.rs | 56 +++++++++++++++++++++++++++++++++++-- tests/compile-fail/regex.rs | 11 ++++++-- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/regex.rs b/src/regex.rs index cf19c764361..e7abc9be876 100644 --- a/src/regex.rs +++ b/src/regex.rs @@ -1,14 +1,16 @@ use regex_syntax; use std::error::Error; use syntax::ast::Lit_::LitStr; +use syntax::ast::NodeId; use syntax::codemap::{Span, BytePos}; use syntax::parse::token::InternedString; use rustc_front::hir::*; +use rustc_front::intravisit::{Visitor, walk_block, FnKind}; use rustc::middle::const_eval::{eval_const_expr_partial, ConstVal}; use rustc::middle::const_eval::EvalHint::ExprTypeChecked; use rustc::lint::*; -use utils::{match_path, REGEX_NEW_PATH, span_lint, span_help_and_lint}; +use utils::{is_expn_of, match_path, REGEX_NEW_PATH, span_lint, span_help_and_lint}; /// **What it does:** This lint checks `Regex::new(_)` invocations for correct regex syntax. /// @@ -37,16 +39,41 @@ declare_lint! { "finds trivial regular expressions in `Regex::new(_)` invocations" } +/// **What it does:** This lint checks for usage of `regex!(_)` which as of now is usually slower than `Regex::new(_)` unless called in a loop (which is a bad idea anyway). +/// +/// **Why is this bad?** Performance, at least for now. The macro version is likely to catch up long-term, but for now the dynamic version is faster. +/// +/// **Known problems:** None +/// +/// **Example:** `regex!("foo|bar")` +declare_lint! { + pub REGEX_MACRO, + Allow, + "finds use of `regex!(_)`, suggests `Regex::new(_)` instead" +} + #[derive(Copy,Clone)] pub struct RegexPass; impl LintPass for RegexPass { fn get_lints(&self) -> LintArray { - lint_array!(INVALID_REGEX, TRIVIAL_REGEX) + lint_array!(INVALID_REGEX, REGEX_MACRO, TRIVIAL_REGEX) } } impl LateLintPass for RegexPass { + fn check_fn(&mut self, + cx: &LateContext, + _: FnKind, + _: &FnDecl, + block: &Block, + _: Span, + _: NodeId) { + let mut visitor = RegexVisitor { cx: cx, last: BytePos(0) }; + visitor.visit_block(block); + } + + fn check_expr(&mut self, cx: &LateContext, expr: &Expr) { if_let_chain!{[ let ExprCall(ref fun, ref args) = expr.node, @@ -139,3 +166,28 @@ fn is_trivial_regex(s: ®ex_syntax::Expr) -> Option<&'static str> { _ => None, } } + +struct RegexVisitor<'v, 't: 'v> { + cx: &'v LateContext<'v, 't>, + last: BytePos +} + +impl<'v, 't: 'v> Visitor<'v> for RegexVisitor<'v, 't> { + fn visit_block(&mut self, block: &'v Block) { + if let Some(ref expr) = block.expr { + if let Some(span) = is_expn_of(self.cx, expr.span, "regex") { + if span.lo == BytePos(0) || span.lo == self.last { + return; + } + span_lint(self.cx, + REGEX_MACRO, + span, + &format!("regex!(_): {:?}, {:?}", self.last, span.lo)); + //"`regex!(_)` found. Use `Regex::new(_)`, which is faster for now."); + self.last = span.lo; + return; + } + } + walk_block(self, block); + } +} diff --git a/tests/compile-fail/regex.rs b/tests/compile-fail/regex.rs index 2e8228a823d..df52cc3dff0 100644 --- a/tests/compile-fail/regex.rs +++ b/tests/compile-fail/regex.rs @@ -1,8 +1,8 @@ #![feature(plugin)] -#![plugin(clippy)] +#![plugin(clippy, regex_macros)] #![allow(unused)] -#![deny(invalid_regex, trivial_regex)] +#![deny(invalid_regex, trivial_regex, regex_macro)] extern crate regex; @@ -70,7 +70,14 @@ fn trivial_regex() { let non_trivial_ends_with = Regex::new("foo|bar"); } +fn regex_macro() { + let some_regex = regex!("for real!"); //~ERROR `regex!(_)` + let other_regex = regex!("[a-z]_[A-Z]"); //~ERROR `regex!(_)` +} + + fn main() { + regex_macro(); syntax_error(); trivial_regex(); }