1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use rustc
::hir
::def
::Def
;
13 use lint
::{LateContext, LintContext, LintArray}
;
14 use lint
::{LintPass, LateLintPass}
;
17 use syntax
::attr
::{self, AttrMetaMethods}
;
18 use syntax
::codemap
::Span
;
20 use rustc
::hir
::{self, PatKind}
;
21 use rustc
::hir
::intravisit
::FnKind
;
24 pub enum MethodLateContext
{
30 pub fn method_context(cx
: &LateContext
, id
: ast
::NodeId
, span
: Span
) -> MethodLateContext
{
31 let def_id
= cx
.tcx
.map
.local_def_id(id
);
32 match cx
.tcx
.impl_or_trait_items
.borrow().get(&def_id
) {
33 None
=> span_bug
!(span
, "missing method descriptor?!"),
34 Some(item
) => match item
.container() {
35 ty
::TraitContainer(..) => MethodLateContext
::TraitDefaultImpl
,
36 ty
::ImplContainer(cid
) => {
37 match cx
.tcx
.impl_trait_ref(cid
) {
38 Some(_
) => MethodLateContext
::TraitImpl
,
39 None
=> MethodLateContext
::PlainImpl
47 pub NON_CAMEL_CASE_TYPES
,
49 "types, variants, traits and type parameters should have camel case names"
52 #[derive(Copy, Clone)]
53 pub struct NonCamelCaseTypes
;
55 impl NonCamelCaseTypes
{
56 fn check_case(&self, cx
: &LateContext
, sort
: &str, name
: ast
::Name
, span
: Span
) {
57 fn is_camel_case(name
: ast
::Name
) -> bool
{
58 let name
= name
.as_str();
62 let name
= name
.trim_matches('_'
);
64 // start with a non-lowercase letter rather than non-uppercase
65 // ones (some scripts don't have a concept of upper/lowercase)
67 !name
.chars().next().unwrap().is_lowercase() &&
71 fn to_camel_case(s
: &str) -> String
{
72 s
.split('_'
).flat_map(|word
| word
.chars().enumerate().map(|(i
, c
)|
74 c
.to_uppercase().collect
::<String
>()
76 c
.to_lowercase().collect()
78 )).collect
::<Vec
<_
>>().concat()
81 let s
= name
.as_str();
83 if !is_camel_case(name
) {
84 let c
= to_camel_case(&s
);
85 let m
= if c
.is_empty() {
86 format
!("{} `{}` should have a camel case name such as `CamelCase`", sort
, s
)
88 format
!("{} `{}` should have a camel case name such as `{}`", sort
, s
, c
)
90 cx
.span_lint(NON_CAMEL_CASE_TYPES
, span
, &m
[..]);
95 impl LintPass
for NonCamelCaseTypes
{
96 fn get_lints(&self) -> LintArray
{
97 lint_array
!(NON_CAMEL_CASE_TYPES
)
101 impl LateLintPass
for NonCamelCaseTypes
{
102 fn check_item(&mut self, cx
: &LateContext
, it
: &hir
::Item
) {
103 let extern_repr_count
= it
.attrs
.iter().filter(|attr
| {
104 attr
::find_repr_attrs(cx
.tcx
.sess
.diagnostic(), attr
).iter()
105 .any(|r
| r
== &attr
::ReprExtern
)
107 let has_extern_repr
= extern_repr_count
> 0;
114 hir
::ItemTy(..) | hir
::ItemStruct(..) => {
115 self.check_case(cx
, "type", it
.name
, it
.span
)
117 hir
::ItemTrait(..) => {
118 self.check_case(cx
, "trait", it
.name
, it
.span
)
120 hir
::ItemEnum(ref enum_definition
, _
) => {
124 self.check_case(cx
, "type", it
.name
, it
.span
);
125 for variant
in &enum_definition
.variants
{
126 self.check_case(cx
, "variant", variant
.node
.name
, variant
.span
);
133 fn check_generics(&mut self, cx
: &LateContext
, it
: &hir
::Generics
) {
134 for gen
in it
.ty_params
.iter() {
135 self.check_case(cx
, "type parameter", gen
.name
, gen
.span
);
143 "variables, methods, functions, lifetime parameters and modules should have snake case names"
146 #[derive(Copy, Clone)]
147 pub struct NonSnakeCase
;
150 fn to_snake_case(mut str: &str) -> String
{
151 let mut words
= vec
![];
152 // Preserve leading underscores
153 str = str.trim_left_matches(|c
: char| {
155 words
.push(String
::new());
161 for s
in str.split('_'
) {
162 let mut last_upper
= false;
163 let mut buf
= String
::new();
167 for ch
in s
.chars() {
168 if !buf
.is_empty() && buf
!= "'"
174 last_upper
= ch
.is_uppercase();
175 buf
.extend(ch
.to_lowercase());
182 fn check_snake_case(&self, cx
: &LateContext
, sort
: &str, name
: &str, span
: Option
<Span
>) {
183 fn is_snake_case(ident
: &str) -> bool
{
184 if ident
.is_empty() {
187 let ident
= ident
.trim_left_matches('
\''
);
188 let ident
= ident
.trim_matches('_'
);
190 let mut allow_underscore
= true;
191 ident
.chars().all(|c
| {
192 allow_underscore
= match c
{
193 '_'
if !allow_underscore
=> return false,
195 // It would be more obvious to use `c.is_lowercase()`,
196 // but some characters do not have a lowercase form
197 c
if !c
.is_uppercase() => true,
204 if !is_snake_case(name
) {
205 let sc
= NonSnakeCase
::to_snake_case(name
);
206 let msg
= if sc
!= name
{
207 format
!("{} `{}` should have a snake case name such as `{}`",
210 format
!("{} `{}` should have a snake case name",
214 Some(span
) => cx
.span_lint(NON_SNAKE_CASE
, span
, &msg
),
215 None
=> cx
.lint(NON_SNAKE_CASE
, &msg
),
221 impl LintPass
for NonSnakeCase
{
222 fn get_lints(&self) -> LintArray
{
223 lint_array
!(NON_SNAKE_CASE
)
227 impl LateLintPass
for NonSnakeCase
{
228 fn check_crate(&mut self, cx
: &LateContext
, cr
: &hir
::Crate
) {
229 let attr_crate_name
= cr
.attrs
.iter().find(|at
| at
.check_name("crate_name"))
230 .and_then(|at
| at
.value_str().map(|s
| (at
, s
)));
231 if let Some(ref name
) = cx
.tcx
.sess
.opts
.crate_name
{
232 self.check_snake_case(cx
, "crate", name
, None
);
233 } else if let Some((attr
, ref name
)) = attr_crate_name
{
234 self.check_snake_case(cx
, "crate", name
, Some(attr
.span
));
238 fn check_fn(&mut self, cx
: &LateContext
,
239 fk
: FnKind
, _
: &hir
::FnDecl
,
240 _
: &hir
::Block
, span
: Span
, id
: ast
::NodeId
) {
242 FnKind
::Method(name
, _
, _
, _
) => match method_context(cx
, id
, span
) {
243 MethodLateContext
::PlainImpl
=> {
244 self.check_snake_case(cx
, "method", &name
.as_str(), Some(span
))
246 MethodLateContext
::TraitDefaultImpl
=> {
247 self.check_snake_case(cx
, "trait method", &name
.as_str(), Some(span
))
251 FnKind
::ItemFn(name
, _
, _
, _
, _
, _
, _
) => {
252 self.check_snake_case(cx
, "function", &name
.as_str(), Some(span
))
254 FnKind
::Closure(_
) => (),
258 fn check_item(&mut self, cx
: &LateContext
, it
: &hir
::Item
) {
259 if let hir
::ItemMod(_
) = it
.node
{
260 self.check_snake_case(cx
, "module", &it
.name
.as_str(), Some(it
.span
));
264 fn check_trait_item(&mut self, cx
: &LateContext
, trait_item
: &hir
::TraitItem
) {
265 if let hir
::MethodTraitItem(_
, None
) = trait_item
.node
{
266 self.check_snake_case(cx
, "trait method", &trait_item
.name
.as_str(),
267 Some(trait_item
.span
));
271 fn check_lifetime_def(&mut self, cx
: &LateContext
, t
: &hir
::LifetimeDef
) {
272 self.check_snake_case(cx
, "lifetime", &t
.lifetime
.name
.as_str(),
273 Some(t
.lifetime
.span
));
276 fn check_pat(&mut self, cx
: &LateContext
, p
: &hir
::Pat
) {
277 if let &PatKind
::Ident(_
, ref path1
, _
) = &p
.node
{
278 let def
= cx
.tcx
.def_map
.borrow().get(&p
.id
).map(|d
| d
.full_def());
279 if let Some(Def
::Local(..)) = def
{
280 self.check_snake_case(cx
, "variable", &path1
.node
.name
.as_str(), Some(p
.span
));
285 fn check_struct_def(&mut self, cx
: &LateContext
, s
: &hir
::VariantData
,
286 _
: ast
::Name
, _
: &hir
::Generics
, _
: ast
::NodeId
) {
287 for sf
in s
.fields() {
288 self.check_snake_case(cx
, "structure field", &sf
.name
.as_str(), Some(sf
.span
));
294 pub NON_UPPER_CASE_GLOBALS
,
296 "static constants should have uppercase identifiers"
299 #[derive(Copy, Clone)]
300 pub struct NonUpperCaseGlobals
;
302 impl NonUpperCaseGlobals
{
303 fn check_upper_case(cx
: &LateContext
, sort
: &str, name
: ast
::Name
, span
: Span
) {
304 let s
= name
.as_str();
306 if s
.chars().any(|c
| c
.is_lowercase()) {
307 let uc
= NonSnakeCase
::to_snake_case(&s
).to_uppercase();
309 cx
.span_lint(NON_UPPER_CASE_GLOBALS
, span
,
310 &format
!("{} `{}` should have an upper case name such as `{}`",
313 cx
.span_lint(NON_UPPER_CASE_GLOBALS
, span
,
314 &format
!("{} `{}` should have an upper case name",
321 impl LintPass
for NonUpperCaseGlobals
{
322 fn get_lints(&self) -> LintArray
{
323 lint_array
!(NON_UPPER_CASE_GLOBALS
)
327 impl LateLintPass
for NonUpperCaseGlobals
{
328 fn check_item(&mut self, cx
: &LateContext
, it
: &hir
::Item
) {
330 // only check static constants
331 hir
::ItemStatic(_
, hir
::MutImmutable
, _
) => {
332 NonUpperCaseGlobals
::check_upper_case(cx
, "static constant", it
.name
, it
.span
);
334 hir
::ItemConst(..) => {
335 NonUpperCaseGlobals
::check_upper_case(cx
, "constant", it
.name
, it
.span
);
341 fn check_trait_item(&mut self, cx
: &LateContext
, ti
: &hir
::TraitItem
) {
343 hir
::ConstTraitItem(..) => {
344 NonUpperCaseGlobals
::check_upper_case(cx
, "associated constant",
351 fn check_impl_item(&mut self, cx
: &LateContext
, ii
: &hir
::ImplItem
) {
353 hir
::ImplItemKind
::Const(..) => {
354 NonUpperCaseGlobals
::check_upper_case(cx
, "associated constant",
361 fn check_pat(&mut self, cx
: &LateContext
, p
: &hir
::Pat
) {
362 // Lint for constants that look like binding identifiers (#7526)
363 match (&p
.node
, cx
.tcx
.def_map
.borrow().get(&p
.id
).map(|d
| d
.full_def())) {
364 (&PatKind
::Ident(_
, ref path1
, _
), Some(Def
::Const(..))) => {
365 NonUpperCaseGlobals
::check_upper_case(cx
, "constant in pattern",
366 path1
.node
.name
, p
.span
);