]>
Commit | Line | Data |
---|---|---|
b039eaaf SL |
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 | ||
54a0048b SL |
11 | use rustc::hir::def::Def; |
12 | use rustc::ty; | |
b039eaaf SL |
13 | use lint::{LateContext, LintContext, LintArray}; |
14 | use lint::{LintPass, LateLintPass}; | |
15 | ||
16 | use syntax::ast; | |
9e0c209e | 17 | use syntax::attr; |
3157f602 | 18 | use syntax_pos::Span; |
b039eaaf | 19 | |
54a0048b SL |
20 | use rustc::hir::{self, PatKind}; |
21 | use rustc::hir::intravisit::FnKind; | |
b039eaaf SL |
22 | |
23 | #[derive(PartialEq)] | |
24 | pub enum MethodLateContext { | |
25 | TraitDefaultImpl, | |
26 | TraitImpl, | |
c30ab7b3 | 27 | PlainImpl, |
b039eaaf SL |
28 | } |
29 | ||
30 | pub fn method_context(cx: &LateContext, id: ast::NodeId, span: Span) -> MethodLateContext { | |
32a655c1 | 31 | let def_id = cx.tcx.hir.local_def_id(id); |
8bb4bdeb | 32 | match cx.tcx.maps.associated_item.borrow().get(&def_id) { |
54a0048b | 33 | None => span_bug!(span, "missing method descriptor?!"), |
c30ab7b3 | 34 | Some(item) => { |
476ff2be | 35 | match item.container { |
c30ab7b3 SL |
36 | ty::TraitContainer(..) => MethodLateContext::TraitDefaultImpl, |
37 | ty::ImplContainer(cid) => { | |
38 | match cx.tcx.impl_trait_ref(cid) { | |
39 | Some(_) => MethodLateContext::TraitImpl, | |
40 | None => MethodLateContext::PlainImpl, | |
41 | } | |
b039eaaf SL |
42 | } |
43 | } | |
44 | } | |
45 | } | |
46 | } | |
47 | ||
48 | declare_lint! { | |
49 | pub NON_CAMEL_CASE_TYPES, | |
50 | Warn, | |
51 | "types, variants, traits and type parameters should have camel case names" | |
52 | } | |
53 | ||
54 | #[derive(Copy, Clone)] | |
55 | pub struct NonCamelCaseTypes; | |
56 | ||
57 | impl NonCamelCaseTypes { | |
58 | fn check_case(&self, cx: &LateContext, sort: &str, name: ast::Name, span: Span) { | |
59 | fn is_camel_case(name: ast::Name) -> bool { | |
60 | let name = name.as_str(); | |
61 | if name.is_empty() { | |
62 | return true; | |
63 | } | |
64 | let name = name.trim_matches('_'); | |
65 | ||
66 | // start with a non-lowercase letter rather than non-uppercase | |
67 | // ones (some scripts don't have a concept of upper/lowercase) | |
c30ab7b3 | 68 | !name.is_empty() && !name.chars().next().unwrap().is_lowercase() && !name.contains('_') |
b039eaaf SL |
69 | } |
70 | ||
71 | fn to_camel_case(s: &str) -> String { | |
c30ab7b3 SL |
72 | s.split('_') |
73 | .flat_map(|word| { | |
74 | word.chars().enumerate().map(|(i, c)| if i == 0 { | |
75 | c.to_uppercase().collect::<String>() | |
76 | } else { | |
77 | c.to_lowercase().collect() | |
78 | }) | |
79 | }) | |
80 | .collect::<Vec<_>>() | |
81 | .concat() | |
b039eaaf SL |
82 | } |
83 | ||
b039eaaf | 84 | if !is_camel_case(name) { |
476ff2be | 85 | let c = to_camel_case(&name.as_str()); |
b039eaaf | 86 | let m = if c.is_empty() { |
476ff2be | 87 | format!("{} `{}` should have a camel case name such as `CamelCase`", sort, name) |
b039eaaf | 88 | } else { |
476ff2be | 89 | format!("{} `{}` should have a camel case name such as `{}`", sort, name, c) |
b039eaaf | 90 | }; |
cc61c64b | 91 | cx.span_lint(NON_CAMEL_CASE_TYPES, span, &m); |
b039eaaf SL |
92 | } |
93 | } | |
94 | } | |
95 | ||
96 | impl LintPass for NonCamelCaseTypes { | |
97 | fn get_lints(&self) -> LintArray { | |
98 | lint_array!(NON_CAMEL_CASE_TYPES) | |
99 | } | |
100 | } | |
101 | ||
476ff2be | 102 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonCamelCaseTypes { |
b039eaaf | 103 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { |
c30ab7b3 SL |
104 | let extern_repr_count = it.attrs |
105 | .iter() | |
106 | .filter(|attr| { | |
107 | attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr) | |
108 | .iter() | |
109 | .any(|r| r == &attr::ReprExtern) | |
110 | }) | |
111 | .count(); | |
b039eaaf SL |
112 | let has_extern_repr = extern_repr_count > 0; |
113 | ||
114 | if has_extern_repr { | |
115 | return; | |
116 | } | |
117 | ||
118 | match it.node { | |
c30ab7b3 | 119 | hir::ItemTy(..) | |
8bb4bdeb | 120 | hir::ItemEnum(..) | |
c30ab7b3 SL |
121 | hir::ItemStruct(..) | |
122 | hir::ItemUnion(..) => self.check_case(cx, "type", it.name, it.span), | |
123 | hir::ItemTrait(..) => self.check_case(cx, "trait", it.name, it.span), | |
c30ab7b3 | 124 | _ => (), |
b039eaaf SL |
125 | } |
126 | } | |
127 | ||
8bb4bdeb XL |
128 | fn check_variant(&mut self, cx: &LateContext, v: &hir::Variant, _: &hir::Generics) { |
129 | self.check_case(cx, "variant", v.node.name, v.span); | |
130 | } | |
131 | ||
b039eaaf SL |
132 | fn check_generics(&mut self, cx: &LateContext, it: &hir::Generics) { |
133 | for gen in it.ty_params.iter() { | |
134 | self.check_case(cx, "type parameter", gen.name, gen.span); | |
135 | } | |
136 | } | |
137 | } | |
138 | ||
139 | declare_lint! { | |
140 | pub NON_SNAKE_CASE, | |
141 | Warn, | |
92a42be0 | 142 | "variables, methods, functions, lifetime parameters and modules should have snake case names" |
b039eaaf SL |
143 | } |
144 | ||
145 | #[derive(Copy, Clone)] | |
146 | pub struct NonSnakeCase; | |
147 | ||
148 | impl NonSnakeCase { | |
149 | fn to_snake_case(mut str: &str) -> String { | |
150 | let mut words = vec![]; | |
151 | // Preserve leading underscores | |
152 | str = str.trim_left_matches(|c: char| { | |
153 | if c == '_' { | |
154 | words.push(String::new()); | |
155 | true | |
156 | } else { | |
157 | false | |
158 | } | |
159 | }); | |
160 | for s in str.split('_') { | |
161 | let mut last_upper = false; | |
162 | let mut buf = String::new(); | |
163 | if s.is_empty() { | |
164 | continue; | |
165 | } | |
166 | for ch in s.chars() { | |
c30ab7b3 | 167 | if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper { |
b039eaaf SL |
168 | words.push(buf); |
169 | buf = String::new(); | |
170 | } | |
171 | last_upper = ch.is_uppercase(); | |
172 | buf.extend(ch.to_lowercase()); | |
173 | } | |
174 | words.push(buf); | |
175 | } | |
176 | words.join("_") | |
177 | } | |
178 | ||
179 | fn check_snake_case(&self, cx: &LateContext, sort: &str, name: &str, span: Option<Span>) { | |
180 | fn is_snake_case(ident: &str) -> bool { | |
181 | if ident.is_empty() { | |
182 | return true; | |
183 | } | |
184 | let ident = ident.trim_left_matches('\''); | |
185 | let ident = ident.trim_matches('_'); | |
186 | ||
187 | let mut allow_underscore = true; | |
188 | ident.chars().all(|c| { | |
189 | allow_underscore = match c { | |
190 | '_' if !allow_underscore => return false, | |
191 | '_' => false, | |
192 | // It would be more obvious to use `c.is_lowercase()`, | |
193 | // but some characters do not have a lowercase form | |
194 | c if !c.is_uppercase() => true, | |
195 | _ => return false, | |
196 | }; | |
197 | true | |
198 | }) | |
199 | } | |
200 | ||
201 | if !is_snake_case(name) { | |
202 | let sc = NonSnakeCase::to_snake_case(name); | |
203 | let msg = if sc != name { | |
204 | format!("{} `{}` should have a snake case name such as `{}`", | |
c30ab7b3 SL |
205 | sort, |
206 | name, | |
207 | sc) | |
b039eaaf | 208 | } else { |
c30ab7b3 | 209 | format!("{} `{}` should have a snake case name", sort, name) |
b039eaaf SL |
210 | }; |
211 | match span { | |
212 | Some(span) => cx.span_lint(NON_SNAKE_CASE, span, &msg), | |
213 | None => cx.lint(NON_SNAKE_CASE, &msg), | |
214 | } | |
215 | } | |
216 | } | |
217 | } | |
218 | ||
219 | impl LintPass for NonSnakeCase { | |
220 | fn get_lints(&self) -> LintArray { | |
221 | lint_array!(NON_SNAKE_CASE) | |
222 | } | |
223 | } | |
224 | ||
476ff2be | 225 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSnakeCase { |
b039eaaf | 226 | fn check_crate(&mut self, cx: &LateContext, cr: &hir::Crate) { |
c30ab7b3 SL |
227 | let attr_crate_name = cr.attrs |
228 | .iter() | |
229 | .find(|at| at.check_name("crate_name")) | |
230 | .and_then(|at| at.value_str().map(|s| (at, s))); | |
b039eaaf SL |
231 | if let Some(ref name) = cx.tcx.sess.opts.crate_name { |
232 | self.check_snake_case(cx, "crate", name, None); | |
476ff2be SL |
233 | } else if let Some((attr, name)) = attr_crate_name { |
234 | self.check_snake_case(cx, "crate", &name.as_str(), Some(attr.span)); | |
b039eaaf SL |
235 | } |
236 | } | |
237 | ||
c30ab7b3 SL |
238 | fn check_fn(&mut self, |
239 | cx: &LateContext, | |
240 | fk: FnKind, | |
241 | _: &hir::FnDecl, | |
32a655c1 | 242 | _: &hir::Body, |
c30ab7b3 SL |
243 | span: Span, |
244 | id: ast::NodeId) { | |
b039eaaf | 245 | match fk { |
c30ab7b3 SL |
246 | FnKind::Method(name, ..) => { |
247 | match method_context(cx, id, span) { | |
248 | MethodLateContext::PlainImpl => { | |
249 | self.check_snake_case(cx, "method", &name.as_str(), Some(span)) | |
250 | } | |
251 | MethodLateContext::TraitDefaultImpl => { | |
252 | self.check_snake_case(cx, "trait method", &name.as_str(), Some(span)) | |
253 | } | |
254 | _ => (), | |
255 | } | |
256 | } | |
9e0c209e | 257 | FnKind::ItemFn(name, ..) => { |
b039eaaf | 258 | self.check_snake_case(cx, "function", &name.as_str(), Some(span)) |
c30ab7b3 | 259 | } |
54a0048b | 260 | FnKind::Closure(_) => (), |
b039eaaf SL |
261 | } |
262 | } | |
263 | ||
264 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { | |
265 | if let hir::ItemMod(_) = it.node { | |
266 | self.check_snake_case(cx, "module", &it.name.as_str(), Some(it.span)); | |
267 | } | |
268 | } | |
269 | ||
32a655c1 SL |
270 | fn check_trait_item(&mut self, cx: &LateContext, item: &hir::TraitItem) { |
271 | if let hir::TraitItemKind::Method(_, hir::TraitMethod::Required(ref names)) = item.node { | |
c30ab7b3 SL |
272 | self.check_snake_case(cx, |
273 | "trait method", | |
32a655c1 SL |
274 | &item.name.as_str(), |
275 | Some(item.span)); | |
276 | for name in names { | |
277 | self.check_snake_case(cx, "variable", &name.node.as_str(), Some(name.span)); | |
278 | } | |
b039eaaf SL |
279 | } |
280 | } | |
281 | ||
282 | fn check_lifetime_def(&mut self, cx: &LateContext, t: &hir::LifetimeDef) { | |
c30ab7b3 SL |
283 | self.check_snake_case(cx, |
284 | "lifetime", | |
285 | &t.lifetime.name.as_str(), | |
b039eaaf SL |
286 | Some(t.lifetime.span)); |
287 | } | |
288 | ||
289 | fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) { | |
476ff2be SL |
290 | if let &PatKind::Binding(_, _, ref path1, _) = &p.node { |
291 | self.check_snake_case(cx, "variable", &path1.node.as_str(), Some(p.span)); | |
292 | } | |
b039eaaf SL |
293 | } |
294 | ||
c30ab7b3 SL |
295 | fn check_struct_def(&mut self, |
296 | cx: &LateContext, | |
297 | s: &hir::VariantData, | |
298 | _: ast::Name, | |
299 | _: &hir::Generics, | |
300 | _: ast::NodeId) { | |
b039eaaf | 301 | for sf in s.fields() { |
54a0048b | 302 | self.check_snake_case(cx, "structure field", &sf.name.as_str(), Some(sf.span)); |
b039eaaf SL |
303 | } |
304 | } | |
305 | } | |
306 | ||
307 | declare_lint! { | |
308 | pub NON_UPPER_CASE_GLOBALS, | |
309 | Warn, | |
310 | "static constants should have uppercase identifiers" | |
311 | } | |
312 | ||
313 | #[derive(Copy, Clone)] | |
314 | pub struct NonUpperCaseGlobals; | |
315 | ||
316 | impl NonUpperCaseGlobals { | |
317 | fn check_upper_case(cx: &LateContext, sort: &str, name: ast::Name, span: Span) { | |
476ff2be SL |
318 | if name.as_str().chars().any(|c| c.is_lowercase()) { |
319 | let uc = NonSnakeCase::to_snake_case(&name.as_str()).to_uppercase(); | |
320 | if name != &*uc { | |
c30ab7b3 SL |
321 | cx.span_lint(NON_UPPER_CASE_GLOBALS, |
322 | span, | |
323 | &format!("{} `{}` should have an upper case name such as `{}`", | |
324 | sort, | |
476ff2be | 325 | name, |
c30ab7b3 | 326 | uc)); |
b039eaaf | 327 | } else { |
c30ab7b3 SL |
328 | cx.span_lint(NON_UPPER_CASE_GLOBALS, |
329 | span, | |
476ff2be | 330 | &format!("{} `{}` should have an upper case name", sort, name)); |
b039eaaf SL |
331 | } |
332 | } | |
333 | } | |
334 | } | |
335 | ||
336 | impl LintPass for NonUpperCaseGlobals { | |
337 | fn get_lints(&self) -> LintArray { | |
338 | lint_array!(NON_UPPER_CASE_GLOBALS) | |
339 | } | |
340 | } | |
341 | ||
476ff2be | 342 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonUpperCaseGlobals { |
b039eaaf SL |
343 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { |
344 | match it.node { | |
c30ab7b3 SL |
345 | hir::ItemStatic(..) => { |
346 | NonUpperCaseGlobals::check_upper_case(cx, "static variable", it.name, it.span); | |
b039eaaf SL |
347 | } |
348 | hir::ItemConst(..) => { | |
349 | NonUpperCaseGlobals::check_upper_case(cx, "constant", it.name, it.span); | |
350 | } | |
351 | _ => {} | |
352 | } | |
353 | } | |
354 | ||
355 | fn check_trait_item(&mut self, cx: &LateContext, ti: &hir::TraitItem) { | |
356 | match ti.node { | |
32a655c1 | 357 | hir::TraitItemKind::Const(..) => { |
c30ab7b3 | 358 | NonUpperCaseGlobals::check_upper_case(cx, "associated constant", ti.name, ti.span); |
b039eaaf SL |
359 | } |
360 | _ => {} | |
361 | } | |
362 | } | |
363 | ||
364 | fn check_impl_item(&mut self, cx: &LateContext, ii: &hir::ImplItem) { | |
365 | match ii.node { | |
92a42be0 | 366 | hir::ImplItemKind::Const(..) => { |
c30ab7b3 | 367 | NonUpperCaseGlobals::check_upper_case(cx, "associated constant", ii.name, ii.span); |
b039eaaf SL |
368 | } |
369 | _ => {} | |
370 | } | |
371 | } | |
372 | ||
373 | fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) { | |
374 | // Lint for constants that look like binding identifiers (#7526) | |
476ff2be | 375 | if let PatKind::Path(hir::QPath::Resolved(None, ref path)) = p.node { |
32a655c1 SL |
376 | if let Def::Const(..) = path.def { |
377 | if path.segments.len() == 1 { | |
c30ab7b3 SL |
378 | NonUpperCaseGlobals::check_upper_case(cx, |
379 | "constant in pattern", | |
380 | path.segments[0].name, | |
381 | path.span); | |
3157f602 | 382 | } |
b039eaaf | 383 | } |
b039eaaf SL |
384 | } |
385 | } | |
386 | } |