]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/bad_style.rs
Auto merge of #35856 - phimuemue:master, r=brson
[rust.git] / src / librustc_lint / bad_style.rs
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.
4 //
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.
10
11 use rustc::hir::def::Def;
12 use rustc::ty;
13 use lint::{LateContext, LintContext, LintArray};
14 use lint::{LintPass, LateLintPass};
15
16 use syntax::ast;
17 use syntax::attr;
18 use syntax_pos::Span;
19
20 use rustc::hir::{self, PatKind};
21 use rustc::hir::intravisit::FnKind;
22
23 #[derive(PartialEq)]
24 pub enum MethodLateContext {
25     TraitDefaultImpl,
26     TraitImpl,
27     PlainImpl
28 }
29
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
40                 }
41             }
42         }
43     }
44 }
45
46 declare_lint! {
47     pub NON_CAMEL_CASE_TYPES,
48     Warn,
49     "types, variants, traits and type parameters should have camel case names"
50 }
51
52 #[derive(Copy, Clone)]
53 pub struct NonCamelCaseTypes;
54
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();
59             if name.is_empty() {
60                 return true;
61             }
62             let name = name.trim_matches('_');
63
64             // start with a non-lowercase letter rather than non-uppercase
65             // ones (some scripts don't have a concept of upper/lowercase)
66             !name.is_empty() &&
67                 !name.chars().next().unwrap().is_lowercase() &&
68                 !name.contains('_')
69         }
70
71         fn to_camel_case(s: &str) -> String {
72             s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
73                 if i == 0 {
74                     c.to_uppercase().collect::<String>()
75                 } else {
76                     c.to_lowercase().collect()
77                 }
78             )).collect::<Vec<_>>().concat()
79         }
80
81         let s = name.as_str();
82
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)
87             } else {
88                 format!("{} `{}` should have a camel case name such as `{}`", sort, s, c)
89             };
90             cx.span_lint(NON_CAMEL_CASE_TYPES, span, &m[..]);
91         }
92     }
93 }
94
95 impl LintPass for NonCamelCaseTypes {
96     fn get_lints(&self) -> LintArray {
97         lint_array!(NON_CAMEL_CASE_TYPES)
98     }
99 }
100
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)
106         }).count();
107         let has_extern_repr = extern_repr_count > 0;
108
109         if has_extern_repr {
110             return;
111         }
112
113         match it.node {
114             hir::ItemTy(..) | hir::ItemStruct(..) => {
115                 self.check_case(cx, "type", it.name, it.span)
116             }
117             hir::ItemTrait(..) => {
118                 self.check_case(cx, "trait", it.name, it.span)
119             }
120             hir::ItemEnum(ref enum_definition, _) => {
121                 if has_extern_repr {
122                     return;
123                 }
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);
127                 }
128             }
129             _ => ()
130         }
131     }
132
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);
136         }
137     }
138 }
139
140 declare_lint! {
141     pub NON_SNAKE_CASE,
142     Warn,
143     "variables, methods, functions, lifetime parameters and modules should have snake case names"
144 }
145
146 #[derive(Copy, Clone)]
147 pub struct NonSnakeCase;
148
149 impl 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| {
154             if c == '_' {
155                 words.push(String::new());
156                 true
157             } else {
158                 false
159             }
160         });
161         for s in str.split('_') {
162             let mut last_upper = false;
163             let mut buf = String::new();
164             if s.is_empty() {
165                 continue;
166             }
167             for ch in s.chars() {
168                 if !buf.is_empty() && buf != "'"
169                                    && ch.is_uppercase()
170                                    && !last_upper {
171                     words.push(buf);
172                     buf = String::new();
173                 }
174                 last_upper = ch.is_uppercase();
175                 buf.extend(ch.to_lowercase());
176             }
177             words.push(buf);
178         }
179         words.join("_")
180     }
181
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() {
185                 return true;
186             }
187             let ident = ident.trim_left_matches('\'');
188             let ident = ident.trim_matches('_');
189
190             let mut allow_underscore = true;
191             ident.chars().all(|c| {
192                 allow_underscore = match c {
193                     '_' if !allow_underscore => return false,
194                     '_' => 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,
198                     _ => return false,
199                 };
200                 true
201             })
202         }
203
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 `{}`",
208                         sort, name, sc)
209             } else {
210                 format!("{} `{}` should have a snake case name",
211                         sort, name)
212             };
213             match span {
214                 Some(span) => cx.span_lint(NON_SNAKE_CASE, span, &msg),
215                 None => cx.lint(NON_SNAKE_CASE, &msg),
216             }
217         }
218     }
219 }
220
221 impl LintPass for NonSnakeCase {
222     fn get_lints(&self) -> LintArray {
223         lint_array!(NON_SNAKE_CASE)
224     }
225 }
226
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));
235         }
236     }
237
238     fn check_fn(&mut self, cx: &LateContext,
239                 fk: FnKind, _: &hir::FnDecl,
240                 _: &hir::Block, span: Span, id: ast::NodeId) {
241         match fk {
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))
245                 },
246                 MethodLateContext::TraitDefaultImpl => {
247                     self.check_snake_case(cx, "trait method", &name.as_str(), Some(span))
248                 },
249                 _ => (),
250             },
251             FnKind::ItemFn(name, _, _, _, _, _, _) => {
252                 self.check_snake_case(cx, "function", &name.as_str(), Some(span))
253             },
254             FnKind::Closure(_) => (),
255         }
256     }
257
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));
261         }
262     }
263
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));
268         }
269     }
270
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));
274     }
275
276     fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) {
277         if let &PatKind::Binding(_, ref path1, _) = &p.node {
278             // Exclude parameter names from foreign functions (they have no `Def`)
279             if cx.tcx.expect_def_or_none(p.id).is_some() {
280                 self.check_snake_case(cx, "variable", &path1.node.as_str(), Some(p.span));
281             }
282         }
283     }
284
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));
289         }
290     }
291 }
292
293 declare_lint! {
294     pub NON_UPPER_CASE_GLOBALS,
295     Warn,
296     "static constants should have uppercase identifiers"
297 }
298
299 #[derive(Copy, Clone)]
300 pub struct NonUpperCaseGlobals;
301
302 impl NonUpperCaseGlobals {
303     fn check_upper_case(cx: &LateContext, sort: &str, name: ast::Name, span: Span) {
304         let s = name.as_str();
305
306         if s.chars().any(|c| c.is_lowercase()) {
307             let uc = NonSnakeCase::to_snake_case(&s).to_uppercase();
308             if uc != &s[..] {
309                 cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
310                     &format!("{} `{}` should have an upper case name such as `{}`",
311                              sort, s, uc));
312             } else {
313                 cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
314                     &format!("{} `{}` should have an upper case name",
315                              sort, s));
316             }
317         }
318     }
319 }
320
321 impl LintPass for NonUpperCaseGlobals {
322     fn get_lints(&self) -> LintArray {
323         lint_array!(NON_UPPER_CASE_GLOBALS)
324     }
325 }
326
327 impl LateLintPass for NonUpperCaseGlobals {
328     fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
329         match it.node {
330             // only check static constants
331             hir::ItemStatic(_, hir::MutImmutable, _) => {
332                 NonUpperCaseGlobals::check_upper_case(cx, "static constant", it.name, it.span);
333             }
334             hir::ItemConst(..) => {
335                 NonUpperCaseGlobals::check_upper_case(cx, "constant", it.name, it.span);
336             }
337             _ => {}
338         }
339     }
340
341     fn check_trait_item(&mut self, cx: &LateContext, ti: &hir::TraitItem) {
342         match ti.node {
343             hir::ConstTraitItem(..) => {
344                 NonUpperCaseGlobals::check_upper_case(cx, "associated constant",
345                                                       ti.name, ti.span);
346             }
347             _ => {}
348         }
349     }
350
351     fn check_impl_item(&mut self, cx: &LateContext, ii: &hir::ImplItem) {
352         match ii.node {
353             hir::ImplItemKind::Const(..) => {
354                 NonUpperCaseGlobals::check_upper_case(cx, "associated constant",
355                                                       ii.name, ii.span);
356             }
357             _ => {}
358         }
359     }
360
361     fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) {
362         // Lint for constants that look like binding identifiers (#7526)
363         if let PatKind::Path(None, ref path) = p.node {
364             if !path.global && path.segments.len() == 1 && path.segments[0].parameters.is_empty() {
365                 if let Def::Const(..) = cx.tcx.expect_def(p.id) {
366                     NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
367                                                           path.segments[0].name, path.span);
368                 }
369             }
370         }
371     }
372 }