Basic framework for structured logging

This commit is contained in:
Manish Goregaokar 2015-07-26 20:23:11 +05:30
parent ac698826d7
commit 0e8e8cfc9b
16 changed files with 73 additions and 46 deletions

View File

@ -20,3 +20,7 @@ compiletest_rs = "*"
regex = "*"
regex_macros = "*"
lazy_static = "*"
[features]
structured_logging = []

View File

@ -7,6 +7,7 @@ use syntax::ast_util::{is_comparison_binop, binop_to_string};
use syntax::ptr::P;
use syntax::codemap::Span;
use std::f64::consts as f64;
use utils::span_lint;
declare_lint! {
pub APPROX_CONSTANT,
@ -51,7 +52,7 @@ fn check_known_consts(cx: &Context, span: Span, str: &str, module: &str) {
if let Ok(value) = str.parse::<f64>() {
for &(constant, name) in KNOWN_CONSTS {
if within_epsilon(constant, value) {
cx.span_lint(APPROX_CONSTANT, span, &format!(
span_lint(cx, APPROX_CONSTANT, span, &format!(
"Approximate value of {}::{} found, consider using it directly.", module, &name));
}
}

View File

@ -6,7 +6,7 @@ use syntax::ast::*;
use syntax::ptr::P;
use syntax::codemap::{Span, ExpnInfo};
use syntax::parse::token::InternedString;
use utils::{in_macro, match_path};
use utils::{in_macro, match_path, span_lint};
declare_lint! { pub INLINE_ALWAYS, Warn,
"#[inline(always)] is usually a bad idea."}
@ -100,7 +100,7 @@ fn check_attrs(cx: &Context, info: Option<&ExpnInfo>, ident: &Ident,
if values.len() != 1 || inline != &"inline" { continue; }
if let MetaWord(ref always) = values[0].node {
if always != &"always" { continue; }
cx.span_lint(INLINE_ALWAYS, attr.span, &format!(
span_lint(cx, INLINE_ALWAYS, attr.span, &format!(
"You have declared #[inline(always)] on {}. This \
is usually a bad idea. Are you sure?",
ident.as_str()));

View File

@ -6,6 +6,7 @@ use syntax::ast::*;
use syntax::ast_util::{is_comparison_binop, binop_to_string};
use syntax::ptr::P;
use syntax::codemap::Span;
use utils::span_lint;
declare_lint! {
pub BAD_BIT_MASK,
@ -95,18 +96,18 @@ fn check_bit_mask(cx: &Context, bit_op: BinOp_, cmp_op: BinOp_,
BiEq | BiNe => match bit_op {
BiBitAnd => if mask_value & cmp_value != mask_value {
if cmp_value != 0 {
cx.span_lint(BAD_BIT_MASK, *span, &format!(
span_lint(cx, BAD_BIT_MASK, *span, &format!(
"incompatible bit mask: _ & {} can never be equal to {}",
mask_value, cmp_value));
}
} else {
if mask_value == 0 {
cx.span_lint(BAD_BIT_MASK, *span,
span_lint(cx, BAD_BIT_MASK, *span,
&format!("&-masking with zero"));
}
},
BiBitOr => if mask_value | cmp_value != cmp_value {
cx.span_lint(BAD_BIT_MASK, *span, &format!(
span_lint(cx, BAD_BIT_MASK, *span, &format!(
"incompatible bit mask: _ | {} can never be equal to {}",
mask_value, cmp_value));
},
@ -114,22 +115,22 @@ fn check_bit_mask(cx: &Context, bit_op: BinOp_, cmp_op: BinOp_,
},
BiLt | BiGe => match bit_op {
BiBitAnd => if mask_value < cmp_value {
cx.span_lint(BAD_BIT_MASK, *span, &format!(
span_lint(cx, BAD_BIT_MASK, *span, &format!(
"incompatible bit mask: _ & {} will always be lower than {}",
mask_value, cmp_value));
} else {
if mask_value == 0 {
cx.span_lint(BAD_BIT_MASK, *span,
span_lint(cx, BAD_BIT_MASK, *span,
&format!("&-masking with zero"));
}
},
BiBitOr => if mask_value >= cmp_value {
cx.span_lint(BAD_BIT_MASK, *span, &format!(
span_lint(cx, BAD_BIT_MASK, *span, &format!(
"incompatible bit mask: _ | {} will never be lower than {}",
mask_value, cmp_value));
} else {
if mask_value < cmp_value {
cx.span_lint(INEFFECTIVE_BIT_MASK, *span, &format!(
span_lint(cx, INEFFECTIVE_BIT_MASK, *span, &format!(
"ineffective bit mask: x | {} compared to {} is the same as x compared directly",
mask_value, cmp_value));
}
@ -138,22 +139,22 @@ fn check_bit_mask(cx: &Context, bit_op: BinOp_, cmp_op: BinOp_,
},
BiLe | BiGt => match bit_op {
BiBitAnd => if mask_value <= cmp_value {
cx.span_lint(BAD_BIT_MASK, *span, &format!(
span_lint(cx, BAD_BIT_MASK, *span, &format!(
"incompatible bit mask: _ & {} will never be higher than {}",
mask_value, cmp_value));
} else {
if mask_value == 0 {
cx.span_lint(BAD_BIT_MASK, *span,
span_lint(cx, BAD_BIT_MASK, *span,
&format!("&-masking with zero"));
}
},
BiBitOr => if mask_value > cmp_value {
cx.span_lint(BAD_BIT_MASK, *span, &format!(
span_lint(cx, BAD_BIT_MASK, *span, &format!(
"incompatible bit mask: _ | {} will always be higher than {}",
mask_value, cmp_value));
} else {
if mask_value < cmp_value {
cx.span_lint(INEFFECTIVE_BIT_MASK, *span, &format!(
span_lint(cx, INEFFECTIVE_BIT_MASK, *span, &format!(
"ineffective bit mask: x | {} compared to {} is the same as x compared directly",
mask_value, cmp_value));
}

View File

@ -19,7 +19,7 @@ use syntax::ast::*;
use syntax::ptr::P;
use syntax::codemap::{Span, Spanned, ExpnInfo};
use syntax::print::pprust::expr_to_string;
use utils::in_macro;
use utils::{in_macro, span_lint};
declare_lint! {
pub COLLAPSIBLE_IF,
@ -47,7 +47,7 @@ fn check_expr_expd(cx: &Context, e: &Expr, info: Option<&ExpnInfo>) {
if let ExprIf(ref check, ref then, None) = e.node {
if let Some(&Expr{ node: ExprIf(ref check_inner, _, None), ..}) =
single_stmt_of_block(then) {
cx.span_lint(COLLAPSIBLE_IF, e.span, &format!(
span_lint(cx, COLLAPSIBLE_IF, e.span, &format!(
"This if statement can be collapsed. Try: if {} && {}\n{:?}",
check_to_string(check), check_to_string(check_inner), e));
}

View File

@ -3,6 +3,7 @@ use syntax::ast::*;
use syntax::ast_util as ast_util;
use syntax::ptr::P;
use syntax::codemap as code;
use utils::span_lint;
declare_lint! {
pub EQ_OP,
@ -21,7 +22,7 @@ impl LintPass for EqOp {
fn check_expr(&mut self, cx: &Context, e: &Expr) {
if let ExprBinary(ref op, ref left, ref right) = e.node {
if is_cmp_or_bit(op) && is_exp_equal(left, right) {
cx.span_lint(EQ_OP, e.span, &format!(
span_lint(cx, EQ_OP, e.span, &format!(
"equal expressions as operands to {}",
ast_util::binop_to_string(op.node)));
}

View File

@ -3,6 +3,8 @@ use rustc::lint::{Context, LintPass, LintArray, Lint, Level};
use syntax::codemap::{Span, Spanned};
use syntax::print::pprust::expr_to_string;
use utils::span_lint;
#[allow(missing_copy_implementations)]
pub struct EtaPass;
@ -48,7 +50,7 @@ impl LintPass for EtaPass {
return
}
}
cx.span_lint(REDUNDANT_CLOSURE, expr.span,
span_lint(cx, REDUNDANT_CLOSURE, expr.span,
&format!("Redundant closure found, consider using `{}` in its place",
expr_to_string(caller))[..])
}

View File

@ -7,7 +7,7 @@ use syntax::ast_util::{is_comparison_binop, binop_to_string};
use syntax::ptr::P;
use syntax::codemap::Span;
use utils::snippet;
use utils::{span_lint, snippet};
declare_lint! { pub IDENTITY_OP, Warn,
"Warn on identity operations, e.g. '_ + 0'"}
@ -48,7 +48,7 @@ impl LintPass for IdentityOp {
fn check(cx: &Context, e: &Expr, m: i8, span: Span, arg: Span) {
if have_lit(cx, e, m) {
cx.span_lint(IDENTITY_OP, span, &format!(
span_lint(cx, IDENTITY_OP, span, &format!(
"The operation is ineffective. Consider reducing it to '{}'",
snippet(cx, arg, "..")));
}

View File

@ -10,6 +10,7 @@ use rustc::middle::def::{DefTy, DefStruct, DefTrait};
use syntax::codemap::{Span, Spanned};
use syntax::ast::*;
use misc::walk_ty;
use utils::span_lint;
declare_lint!(pub LEN_ZERO, Warn,
"Warn when .is_empty() could be used instead of checking .len()");
@ -54,10 +55,10 @@ fn check_trait_items(cx: &Context, item: &Item, trait_items: &[P<TraitItem>]) {
}
if !trait_items.iter().any(|i| is_named_self(i, "is_empty")) {
//cx.span_lint(LEN_WITHOUT_IS_EMPTY, item.span, &format!("trait {}", item.ident.as_str()));
//span_lint(cx, LEN_WITHOUT_IS_EMPTY, item.span, &format!("trait {}", item.ident.as_str()));
for i in trait_items {
if is_named_self(i, "len") {
cx.span_lint(LEN_WITHOUT_IS_EMPTY, i.span,
span_lint(cx, LEN_WITHOUT_IS_EMPTY, i.span,
&format!("Trait '{}' has a '.len(_: &Self)' method, but no \
'.is_empty(_: &Self)' method. Consider adding one.",
item.ident.as_str()));
@ -76,7 +77,7 @@ fn check_impl_items(cx: &Context, item: &Item, impl_items: &[P<ImplItem>]) {
for i in impl_items {
if is_named_self(i, "len") {
let s = i.span;
cx.span_lint(LEN_WITHOUT_IS_EMPTY,
span_lint(cx, LEN_WITHOUT_IS_EMPTY,
Span{ lo: s.lo, hi: s.lo, expn_id: s.expn_id },
&format!("Item '{}' has a '.len(_: &Self)' method, but no \
'.is_empty(_: &Self)' method. Consider adding one.",
@ -107,7 +108,7 @@ fn check_len_zero(cx: &Context, span: Span, method: &SpannedIdent,
if let &Spanned{node: LitInt(0, _), ..} = lit {
if method.node.as_str() == "len" && args.len() == 1 &&
has_is_empty(cx, &*args[0]) {
cx.span_lint(LEN_ZERO, span, &format!(
span_lint(cx, LEN_ZERO, span, &format!(
"Consider replacing the len comparison with '{}_.is_empty()'",
empty))
}

View File

@ -8,7 +8,7 @@ use rustc::middle::ty;
use syntax::codemap::{Span, Spanned};
use types::span_note_and_lint;
use utils::{match_path, snippet};
use utils::{match_path, snippet, span_lint};
pub fn walk_ty<'t>(ty: ty::Ty<'t>) -> ty::Ty<'t> {
match ty.sty {
@ -72,7 +72,7 @@ impl LintPass for StrToStringPass {
ast::ExprMethodCall(ref method, _, ref args)
if method.node.as_str() == "to_string"
&& is_str(cx, &*args[0]) => {
cx.span_lint(STR_TO_STRING, expr.span, "str.to_owned() is faster");
span_lint(cx, STR_TO_STRING, expr.span, "str.to_owned() is faster");
},
_ => ()
}
@ -100,7 +100,7 @@ impl LintPass for TopLevelRefPass {
fn check_fn(&mut self, cx: &Context, _: FnKind, decl: &FnDecl, _: &Block, _: Span, _: NodeId) {
for ref arg in decl.inputs.iter() {
if let PatIdent(BindByRef(_), _, _) = arg.pat.node {
cx.span_lint(
span_lint(cx,
TOPLEVEL_REF_ARG,
arg.pat.span,
"`ref` directly on a function argument is ignored. Have you considered using a reference type instead?"
@ -136,7 +136,7 @@ impl LintPass for CmpNan {
fn check_nan(cx: &Context, path: &Path, span: Span) {
path.segments.last().map(|seg| if seg.identifier.as_str() == "NAN" {
cx.span_lint(CMP_NAN, span, "Doomed comparison with NAN, use std::{f32,f64}::is_nan instead");
span_lint(cx, CMP_NAN, span, "Doomed comparison with NAN, use std::{f32,f64}::is_nan instead");
});
}
@ -155,7 +155,7 @@ impl LintPass for FloatCmp {
if let ExprBinary(ref cmp, ref left, ref right) = expr.node {
let op = cmp.node;
if (op == BiEq || op == BiNe) && (is_float(cx, left) || is_float(cx, right)) {
cx.span_lint(FLOAT_CMP, expr.span, &format!(
span_lint(cx, FLOAT_CMP, expr.span, &format!(
"{}-Comparison of f32 or f64 detected. You may want to change this to 'abs({} - {}) < epsilon' for some suitable value of epsilon",
binop_to_string(op), snippet(cx, left.span, ".."),
snippet(cx, right.span, "..")));
@ -186,7 +186,7 @@ impl LintPass for Precedence {
fn check_expr(&mut self, cx: &Context, expr: &Expr) {
if let ExprBinary(Spanned { node: op, ..}, ref left, ref right) = expr.node {
if is_bit_op(op) && (is_arith_expr(left) || is_arith_expr(right)) {
cx.span_lint(PRECEDENCE, expr.span,
span_lint(cx, PRECEDENCE, expr.span,
"Operator precedence can trip the unwary. Consider adding parenthesis to the subexpression.");
}
}
@ -241,7 +241,7 @@ fn check_to_owned(cx: &Context, expr: &Expr, other_span: Span) {
let name = ident.as_str();
if name == "to_string" ||
name == "to_owned" && is_str_arg(cx, args) {
cx.span_lint(CMP_OWNED, expr.span, &format!(
span_lint(cx, CMP_OWNED, expr.span, &format!(
"this creates an owned instance just for comparison. \
Consider using {}.as_slice() to compare without allocation",
snippet(cx, other_span, "..")))
@ -251,7 +251,7 @@ fn check_to_owned(cx: &Context, expr: &Expr, other_span: Span) {
if let &ExprPath(None, ref path) = &path.node {
if match_path(path, &["String", "from_str"]) ||
match_path(path, &["String", "from"]) {
cx.span_lint(CMP_OWNED, expr.span, &format!(
span_lint(cx, CMP_OWNED, expr.span, &format!(
"this creates an owned instance just for comparison. \
Consider using {}.as_slice() to compare without allocation",
snippet(cx, other_span, "..")))

View File

@ -3,7 +3,7 @@ use syntax::ast::*;
use rustc::lint::{Context, LintPass, LintArray, Lint};
use rustc::middle::ty::{TypeVariants, TypeAndMut, TyRef};
use syntax::codemap::{BytePos, ExpnInfo, Span};
use utils::in_macro;
use utils::{in_macro, span_lint};
declare_lint!(pub MUT_MUT, Warn,
"Warn on usage of double-mut refs, e.g. '&mut &mut ...'");
@ -22,7 +22,7 @@ impl LintPass for MutMut {
}
fn check_ty(&mut self, cx: &Context, ty: &Ty) {
unwrap_mut(ty).and_then(unwrap_mut).map_or((), |_| cx.span_lint(MUT_MUT,
unwrap_mut(ty).and_then(unwrap_mut).map_or((), |_| span_lint(cx, MUT_MUT,
ty.span, "Generally you want to avoid &mut &mut _ if possible."))
}
}
@ -39,12 +39,12 @@ fn check_expr_expd(cx: &Context, expr: &Expr, info: Option<&ExpnInfo>) {
unwrap_addr(expr).map_or((), |e| {
unwrap_addr(e).map(|_| {
cx.span_lint(MUT_MUT, expr.span,
span_lint(cx, MUT_MUT, expr.span,
"Generally you want to avoid &mut &mut _ if possible.")
}).unwrap_or_else(|| {
if let TyRef(_, TypeAndMut{ty: _, mutbl: MutMutable}) =
cx.tcx.expr_ty(e).sty {
cx.span_lint(MUT_MUT, expr.span,
span_lint(cx, MUT_MUT, expr.span,
"This expression mutably borrows a mutable reference. \
Consider reborrowing")
}

View File

@ -10,7 +10,7 @@ use syntax::ast::*;
use syntax::ast_util::{is_comparison_binop, binop_to_string};
use syntax::ptr::P;
use syntax::codemap::Span;
use utils::de_p;
use utils::{de_p, span_lint};
declare_lint! {
pub NEEDLESS_BOOL,
@ -30,16 +30,16 @@ impl LintPass for NeedlessBool {
if let ExprIf(_, ref then_block, Option::Some(ref else_expr)) = e.node {
match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
(Option::Some(true), Option::Some(true)) => {
cx.span_lint(NEEDLESS_BOOL, e.span,
span_lint(cx, NEEDLESS_BOOL, e.span,
"your if-then-else expression will always return true"); },
(Option::Some(true), Option::Some(false)) => {
cx.span_lint(NEEDLESS_BOOL, e.span,
span_lint(cx, NEEDLESS_BOOL, e.span,
"you can reduce your if-statement to its predicate"); },
(Option::Some(false), Option::Some(true)) => {
cx.span_lint(NEEDLESS_BOOL, e.span,
span_lint(cx, NEEDLESS_BOOL, e.span,
"you can reduce your if-statement to '!' + your predicate"); },
(Option::Some(false), Option::Some(false)) => {
cx.span_lint(NEEDLESS_BOOL, e.span,
span_lint(cx, NEEDLESS_BOOL, e.span,
"your if-then-else expression will always return false"); },
_ => ()
}

View File

@ -11,6 +11,7 @@ use syntax::ast_util::{is_comparison_binop, binop_to_string};
use syntax::ptr::P;
use syntax::codemap::Span;
use types::match_ty_unwrap;
use utils::span_lint;
declare_lint! {
pub PTR_ARG,
@ -58,10 +59,10 @@ fn check_fn(cx: &Context, decl: &FnDecl) {
fn check_ptr_subtype(cx: &Context, span: Span, ty: &Ty) {
match_ty_unwrap(ty, &["Vec"]).map_or_else(|| match_ty_unwrap(ty,
&["String"]).map_or((), |_| {
cx.span_lint(PTR_ARG, span,
span_lint(cx, PTR_ARG, span,
"Writing '&String' instead of '&str' involves a new Object \
where a slices will do. Consider changing the type to &str")
}), |_| cx.span_lint(PTR_ARG, span, "Writing '&Vec<_>' instead of \
}), |_| span_lint(cx, PTR_ARG, span, "Writing '&Vec<_>' instead of \
'&[_]' involves one more reference and cannot be used with \
non-vec-based slices. Consider changing the type to &[...]")
)

View File

@ -6,6 +6,8 @@ use syntax::ast::*;
use rustc::lint::{Context, LintPass, LintArray, Lint, Level};
use syntax::codemap::Span;
use utils::span_lint;
/// Handles all the linting of funky types
#[allow(missing_copy_implementations)]
pub struct TypePass;
@ -40,7 +42,7 @@ pub fn match_ty_unwrap<'a>(ty: &'a Ty, segments: &[&str]) -> Option<&'a [P<Ty>]>
/// Lets me span a note only if the lint is shown
pub fn span_note_and_lint(cx: &Context, lint: &'static Lint, span: Span, msg: &str, note: &str) {
cx.span_lint(lint, span, msg);
span_lint(cx, lint, span, msg);
if cx.current_level(lint) != Level::Allow {
cx.sess().span_note(span, note);
}

View File

@ -1,6 +1,7 @@
use rustc::lint::*;
use syntax::ast::*;
use syntax::codemap::{BytePos, Span};
use utils::span_lint;
declare_lint!{ pub ZERO_WIDTH_SPACE, Deny, "Zero-width space is confusing" }
@ -36,7 +37,7 @@ fn check_str(cx: &Context, string: &str, span: Span) {
fn lint_zero_width(cx: &Context, span: Span, start: Option<usize>) {
start.map(|index| {
cx.span_lint(ZERO_WIDTH_SPACE, Span {
span_lint(cx, ZERO_WIDTH_SPACE, Span {
lo: span.lo + BytePos(index as u32),
hi: span.lo + BytePos(index as u32),
expn_id: span.expn_id,

View File

@ -1,4 +1,4 @@
use rustc::lint::Context;
use rustc::lint::{Context, Lint};
use syntax::ast::{DefId, Name, Path};
use syntax::codemap::{ExpnInfo, Span};
use syntax::ptr::P;
@ -52,3 +52,16 @@ pub fn snippet<'a>(cx: &Context, span: Span, default: &'a str) -> Cow<'a, str> {
/// dereference a P<T> and return a ref on the result
pub fn de_p<T>(p: &P<T>) -> &T { &*p }
#[cfg(not(feature="structured_logging"))]
pub fn span_lint(cx: &Context, lint: &'static Lint, sp: Span, msg: &str) {
cx.span_lint(lint, sp, msg);
}
#[cfg(feature="structured_logging")]
pub fn span_lint(cx: &Context, lint: &'static Lint, sp: Span, msg: &str) {
// lint.name / lint.desc is can give details of the lint
// cx.sess().codemap() has all these nice functions for line/column/snippet details
// http://doc.rust-lang.org/syntax/codemap/struct.CodeMap.html#method.span_to_string
cx.span_lint(lint, sp, msg);
}