| // Copyright 2015 The Rust Project Developers. See the COPYRIGHT |
| // file at the top-level directory of this distribution and at |
| // http://rust-lang.org/COPYRIGHT. |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| use rustc::hir::def::Def; |
| use rustc::ty; |
| use lint::{LateContext, LintContext, LintArray}; |
| use lint::{LintPass, LateLintPass}; |
| |
| use syntax::ast; |
| use syntax::attr::{self, AttrMetaMethods}; |
| use syntax_pos::Span; |
| |
| use rustc::hir::{self, PatKind}; |
| use rustc::hir::intravisit::FnKind; |
| |
| #[derive(PartialEq)] |
| pub enum MethodLateContext { |
| TraitDefaultImpl, |
| TraitImpl, |
| PlainImpl |
| } |
| |
| pub fn method_context(cx: &LateContext, id: ast::NodeId, span: Span) -> MethodLateContext { |
| let def_id = cx.tcx.map.local_def_id(id); |
| match cx.tcx.impl_or_trait_items.borrow().get(&def_id) { |
| None => span_bug!(span, "missing method descriptor?!"), |
| Some(item) => match item.container() { |
| ty::TraitContainer(..) => MethodLateContext::TraitDefaultImpl, |
| ty::ImplContainer(cid) => { |
| match cx.tcx.impl_trait_ref(cid) { |
| Some(_) => MethodLateContext::TraitImpl, |
| None => MethodLateContext::PlainImpl |
| } |
| } |
| } |
| } |
| } |
| |
| declare_lint! { |
| pub NON_CAMEL_CASE_TYPES, |
| Warn, |
| "types, variants, traits and type parameters should have camel case names" |
| } |
| |
| #[derive(Copy, Clone)] |
| pub struct NonCamelCaseTypes; |
| |
| impl NonCamelCaseTypes { |
| fn check_case(&self, cx: &LateContext, sort: &str, name: ast::Name, span: Span) { |
| fn is_camel_case(name: ast::Name) -> bool { |
| let name = name.as_str(); |
| if name.is_empty() { |
| return true; |
| } |
| let name = name.trim_matches('_'); |
| |
| // start with a non-lowercase letter rather than non-uppercase |
| // ones (some scripts don't have a concept of upper/lowercase) |
| !name.is_empty() && |
| !name.chars().next().unwrap().is_lowercase() && |
| !name.contains('_') |
| } |
| |
| fn to_camel_case(s: &str) -> String { |
| s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)| |
| if i == 0 { |
| c.to_uppercase().collect::<String>() |
| } else { |
| c.to_lowercase().collect() |
| } |
| )).collect::<Vec<_>>().concat() |
| } |
| |
| let s = name.as_str(); |
| |
| if !is_camel_case(name) { |
| let c = to_camel_case(&s); |
| let m = if c.is_empty() { |
| format!("{} `{}` should have a camel case name such as `CamelCase`", sort, s) |
| } else { |
| format!("{} `{}` should have a camel case name such as `{}`", sort, s, c) |
| }; |
| cx.span_lint(NON_CAMEL_CASE_TYPES, span, &m[..]); |
| } |
| } |
| } |
| |
| impl LintPass for NonCamelCaseTypes { |
| fn get_lints(&self) -> LintArray { |
| lint_array!(NON_CAMEL_CASE_TYPES) |
| } |
| } |
| |
| impl LateLintPass for NonCamelCaseTypes { |
| fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { |
| let extern_repr_count = it.attrs.iter().filter(|attr| { |
| attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter() |
| .any(|r| r == &attr::ReprExtern) |
| }).count(); |
| let has_extern_repr = extern_repr_count > 0; |
| |
| if has_extern_repr { |
| return; |
| } |
| |
| match it.node { |
| hir::ItemTy(..) | hir::ItemStruct(..) => { |
| self.check_case(cx, "type", it.name, it.span) |
| } |
| hir::ItemTrait(..) => { |
| self.check_case(cx, "trait", it.name, it.span) |
| } |
| hir::ItemEnum(ref enum_definition, _) => { |
| if has_extern_repr { |
| return; |
| } |
| self.check_case(cx, "type", it.name, it.span); |
| for variant in &enum_definition.variants { |
| self.check_case(cx, "variant", variant.node.name, variant.span); |
| } |
| } |
| _ => () |
| } |
| } |
| |
| fn check_generics(&mut self, cx: &LateContext, it: &hir::Generics) { |
| for gen in it.ty_params.iter() { |
| self.check_case(cx, "type parameter", gen.name, gen.span); |
| } |
| } |
| } |
| |
| declare_lint! { |
| pub NON_SNAKE_CASE, |
| Warn, |
| "variables, methods, functions, lifetime parameters and modules should have snake case names" |
| } |
| |
| #[derive(Copy, Clone)] |
| pub struct NonSnakeCase; |
| |
| impl NonSnakeCase { |
| fn to_snake_case(mut str: &str) -> String { |
| let mut words = vec![]; |
| // Preserve leading underscores |
| str = str.trim_left_matches(|c: char| { |
| if c == '_' { |
| words.push(String::new()); |
| true |
| } else { |
| false |
| } |
| }); |
| for s in str.split('_') { |
| let mut last_upper = false; |
| let mut buf = String::new(); |
| if s.is_empty() { |
| continue; |
| } |
| for ch in s.chars() { |
| if !buf.is_empty() && buf != "'" |
| && ch.is_uppercase() |
| && !last_upper { |
| words.push(buf); |
| buf = String::new(); |
| } |
| last_upper = ch.is_uppercase(); |
| buf.extend(ch.to_lowercase()); |
| } |
| words.push(buf); |
| } |
| words.join("_") |
| } |
| |
| fn check_snake_case(&self, cx: &LateContext, sort: &str, name: &str, span: Option<Span>) { |
| fn is_snake_case(ident: &str) -> bool { |
| if ident.is_empty() { |
| return true; |
| } |
| let ident = ident.trim_left_matches('\''); |
| let ident = ident.trim_matches('_'); |
| |
| let mut allow_underscore = true; |
| ident.chars().all(|c| { |
| allow_underscore = match c { |
| '_' if !allow_underscore => return false, |
| '_' => false, |
| // It would be more obvious to use `c.is_lowercase()`, |
| // but some characters do not have a lowercase form |
| c if !c.is_uppercase() => true, |
| _ => return false, |
| }; |
| true |
| }) |
| } |
| |
| if !is_snake_case(name) { |
| let sc = NonSnakeCase::to_snake_case(name); |
| let msg = if sc != name { |
| format!("{} `{}` should have a snake case name such as `{}`", |
| sort, name, sc) |
| } else { |
| format!("{} `{}` should have a snake case name", |
| sort, name) |
| }; |
| match span { |
| Some(span) => cx.span_lint(NON_SNAKE_CASE, span, &msg), |
| None => cx.lint(NON_SNAKE_CASE, &msg), |
| } |
| } |
| } |
| } |
| |
| impl LintPass for NonSnakeCase { |
| fn get_lints(&self) -> LintArray { |
| lint_array!(NON_SNAKE_CASE) |
| } |
| } |
| |
| impl LateLintPass for NonSnakeCase { |
| fn check_crate(&mut self, cx: &LateContext, cr: &hir::Crate) { |
| let attr_crate_name = cr.attrs.iter().find(|at| at.check_name("crate_name")) |
| .and_then(|at| at.value_str().map(|s| (at, s))); |
| if let Some(ref name) = cx.tcx.sess.opts.crate_name { |
| self.check_snake_case(cx, "crate", name, None); |
| } else if let Some((attr, ref name)) = attr_crate_name { |
| self.check_snake_case(cx, "crate", name, Some(attr.span)); |
| } |
| } |
| |
| fn check_fn(&mut self, cx: &LateContext, |
| fk: FnKind, _: &hir::FnDecl, |
| _: &hir::Block, span: Span, id: ast::NodeId) { |
| match fk { |
| FnKind::Method(name, _, _, _) => match method_context(cx, id, span) { |
| MethodLateContext::PlainImpl => { |
| self.check_snake_case(cx, "method", &name.as_str(), Some(span)) |
| }, |
| MethodLateContext::TraitDefaultImpl => { |
| self.check_snake_case(cx, "trait method", &name.as_str(), Some(span)) |
| }, |
| _ => (), |
| }, |
| FnKind::ItemFn(name, _, _, _, _, _, _) => { |
| self.check_snake_case(cx, "function", &name.as_str(), Some(span)) |
| }, |
| FnKind::Closure(_) => (), |
| } |
| } |
| |
| fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { |
| if let hir::ItemMod(_) = it.node { |
| self.check_snake_case(cx, "module", &it.name.as_str(), Some(it.span)); |
| } |
| } |
| |
| fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) { |
| if let hir::MethodTraitItem(_, None) = trait_item.node { |
| self.check_snake_case(cx, "trait method", &trait_item.name.as_str(), |
| Some(trait_item.span)); |
| } |
| } |
| |
| fn check_lifetime_def(&mut self, cx: &LateContext, t: &hir::LifetimeDef) { |
| self.check_snake_case(cx, "lifetime", &t.lifetime.name.as_str(), |
| Some(t.lifetime.span)); |
| } |
| |
| fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) { |
| if let &PatKind::Binding(_, ref path1, _) = &p.node { |
| // Exclude parameter names from foreign functions (they have no `Def`) |
| if cx.tcx.expect_def_or_none(p.id).is_some() { |
| self.check_snake_case(cx, "variable", &path1.node.as_str(), Some(p.span)); |
| } |
| } |
| } |
| |
| fn check_struct_def(&mut self, cx: &LateContext, s: &hir::VariantData, |
| _: ast::Name, _: &hir::Generics, _: ast::NodeId) { |
| for sf in s.fields() { |
| self.check_snake_case(cx, "structure field", &sf.name.as_str(), Some(sf.span)); |
| } |
| } |
| } |
| |
| declare_lint! { |
| pub NON_UPPER_CASE_GLOBALS, |
| Warn, |
| "static constants should have uppercase identifiers" |
| } |
| |
| #[derive(Copy, Clone)] |
| pub struct NonUpperCaseGlobals; |
| |
| impl NonUpperCaseGlobals { |
| fn check_upper_case(cx: &LateContext, sort: &str, name: ast::Name, span: Span) { |
| let s = name.as_str(); |
| |
| if s.chars().any(|c| c.is_lowercase()) { |
| let uc = NonSnakeCase::to_snake_case(&s).to_uppercase(); |
| if uc != &s[..] { |
| cx.span_lint(NON_UPPER_CASE_GLOBALS, span, |
| &format!("{} `{}` should have an upper case name such as `{}`", |
| sort, s, uc)); |
| } else { |
| cx.span_lint(NON_UPPER_CASE_GLOBALS, span, |
| &format!("{} `{}` should have an upper case name", |
| sort, s)); |
| } |
| } |
| } |
| } |
| |
| impl LintPass for NonUpperCaseGlobals { |
| fn get_lints(&self) -> LintArray { |
| lint_array!(NON_UPPER_CASE_GLOBALS) |
| } |
| } |
| |
| impl LateLintPass for NonUpperCaseGlobals { |
| fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { |
| match it.node { |
| // only check static constants |
| hir::ItemStatic(_, hir::MutImmutable, _) => { |
| NonUpperCaseGlobals::check_upper_case(cx, "static constant", it.name, it.span); |
| } |
| hir::ItemConst(..) => { |
| NonUpperCaseGlobals::check_upper_case(cx, "constant", it.name, it.span); |
| } |
| _ => {} |
| } |
| } |
| |
| fn check_trait_item(&mut self, cx: &LateContext, ti: &hir::TraitItem) { |
| match ti.node { |
| hir::ConstTraitItem(..) => { |
| NonUpperCaseGlobals::check_upper_case(cx, "associated constant", |
| ti.name, ti.span); |
| } |
| _ => {} |
| } |
| } |
| |
| fn check_impl_item(&mut self, cx: &LateContext, ii: &hir::ImplItem) { |
| match ii.node { |
| hir::ImplItemKind::Const(..) => { |
| NonUpperCaseGlobals::check_upper_case(cx, "associated constant", |
| ii.name, ii.span); |
| } |
| _ => {} |
| } |
| } |
| |
| fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) { |
| // Lint for constants that look like binding identifiers (#7526) |
| if let PatKind::Path(None, ref path) = p.node { |
| if !path.global && path.segments.len() == 1 && path.segments[0].parameters.is_empty() { |
| if let Def::Const(..) = cx.tcx.expect_def(p.id) { |
| NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern", |
| path.segments[0].name, path.span); |
| } |
| } |
| } |
| } |
| } |