]>
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 { | |
31 | let def_id = cx.tcx.map.local_def_id(id); | |
476ff2be | 32 | match cx.tcx.associated_items.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 SL |
90 | }; |
91 | cx.span_lint(NON_CAMEL_CASE_TYPES, span, &m[..]); | |
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 SL |
119 | hir::ItemTy(..) | |
120 | hir::ItemStruct(..) | | |
121 | hir::ItemUnion(..) => self.check_case(cx, "type", it.name, it.span), | |
122 | hir::ItemTrait(..) => self.check_case(cx, "trait", it.name, it.span), | |
b039eaaf SL |
123 | hir::ItemEnum(ref enum_definition, _) => { |
124 | if has_extern_repr { | |
125 | return; | |
126 | } | |
127 | self.check_case(cx, "type", it.name, it.span); | |
128 | for variant in &enum_definition.variants { | |
129 | self.check_case(cx, "variant", variant.node.name, variant.span); | |
130 | } | |
131 | } | |
c30ab7b3 | 132 | _ => (), |
b039eaaf SL |
133 | } |
134 | } | |
135 | ||
136 | fn check_generics(&mut self, cx: &LateContext, it: &hir::Generics) { | |
137 | for gen in it.ty_params.iter() { | |
138 | self.check_case(cx, "type parameter", gen.name, gen.span); | |
139 | } | |
140 | } | |
141 | } | |
142 | ||
143 | declare_lint! { | |
144 | pub NON_SNAKE_CASE, | |
145 | Warn, | |
92a42be0 | 146 | "variables, methods, functions, lifetime parameters and modules should have snake case names" |
b039eaaf SL |
147 | } |
148 | ||
149 | #[derive(Copy, Clone)] | |
150 | pub struct NonSnakeCase; | |
151 | ||
152 | impl NonSnakeCase { | |
153 | fn to_snake_case(mut str: &str) -> String { | |
154 | let mut words = vec![]; | |
155 | // Preserve leading underscores | |
156 | str = str.trim_left_matches(|c: char| { | |
157 | if c == '_' { | |
158 | words.push(String::new()); | |
159 | true | |
160 | } else { | |
161 | false | |
162 | } | |
163 | }); | |
164 | for s in str.split('_') { | |
165 | let mut last_upper = false; | |
166 | let mut buf = String::new(); | |
167 | if s.is_empty() { | |
168 | continue; | |
169 | } | |
170 | for ch in s.chars() { | |
c30ab7b3 | 171 | if !buf.is_empty() && buf != "'" && ch.is_uppercase() && !last_upper { |
b039eaaf SL |
172 | words.push(buf); |
173 | buf = String::new(); | |
174 | } | |
175 | last_upper = ch.is_uppercase(); | |
176 | buf.extend(ch.to_lowercase()); | |
177 | } | |
178 | words.push(buf); | |
179 | } | |
180 | words.join("_") | |
181 | } | |
182 | ||
183 | fn check_snake_case(&self, cx: &LateContext, sort: &str, name: &str, span: Option<Span>) { | |
184 | fn is_snake_case(ident: &str) -> bool { | |
185 | if ident.is_empty() { | |
186 | return true; | |
187 | } | |
188 | let ident = ident.trim_left_matches('\''); | |
189 | let ident = ident.trim_matches('_'); | |
190 | ||
191 | let mut allow_underscore = true; | |
192 | ident.chars().all(|c| { | |
193 | allow_underscore = match c { | |
194 | '_' if !allow_underscore => return false, | |
195 | '_' => false, | |
196 | // It would be more obvious to use `c.is_lowercase()`, | |
197 | // but some characters do not have a lowercase form | |
198 | c if !c.is_uppercase() => true, | |
199 | _ => return false, | |
200 | }; | |
201 | true | |
202 | }) | |
203 | } | |
204 | ||
205 | if !is_snake_case(name) { | |
206 | let sc = NonSnakeCase::to_snake_case(name); | |
207 | let msg = if sc != name { | |
208 | format!("{} `{}` should have a snake case name such as `{}`", | |
c30ab7b3 SL |
209 | sort, |
210 | name, | |
211 | sc) | |
b039eaaf | 212 | } else { |
c30ab7b3 | 213 | format!("{} `{}` should have a snake case name", sort, name) |
b039eaaf SL |
214 | }; |
215 | match span { | |
216 | Some(span) => cx.span_lint(NON_SNAKE_CASE, span, &msg), | |
217 | None => cx.lint(NON_SNAKE_CASE, &msg), | |
218 | } | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
223 | impl LintPass for NonSnakeCase { | |
224 | fn get_lints(&self) -> LintArray { | |
225 | lint_array!(NON_SNAKE_CASE) | |
226 | } | |
227 | } | |
228 | ||
476ff2be | 229 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonSnakeCase { |
b039eaaf | 230 | fn check_crate(&mut self, cx: &LateContext, cr: &hir::Crate) { |
c30ab7b3 SL |
231 | let attr_crate_name = cr.attrs |
232 | .iter() | |
233 | .find(|at| at.check_name("crate_name")) | |
234 | .and_then(|at| at.value_str().map(|s| (at, s))); | |
b039eaaf SL |
235 | if let Some(ref name) = cx.tcx.sess.opts.crate_name { |
236 | self.check_snake_case(cx, "crate", name, None); | |
476ff2be SL |
237 | } else if let Some((attr, name)) = attr_crate_name { |
238 | self.check_snake_case(cx, "crate", &name.as_str(), Some(attr.span)); | |
b039eaaf SL |
239 | } |
240 | } | |
241 | ||
c30ab7b3 SL |
242 | fn check_fn(&mut self, |
243 | cx: &LateContext, | |
244 | fk: FnKind, | |
245 | _: &hir::FnDecl, | |
476ff2be | 246 | _: &hir::Expr, |
c30ab7b3 SL |
247 | span: Span, |
248 | id: ast::NodeId) { | |
b039eaaf | 249 | match fk { |
c30ab7b3 SL |
250 | FnKind::Method(name, ..) => { |
251 | match method_context(cx, id, span) { | |
252 | MethodLateContext::PlainImpl => { | |
253 | self.check_snake_case(cx, "method", &name.as_str(), Some(span)) | |
254 | } | |
255 | MethodLateContext::TraitDefaultImpl => { | |
256 | self.check_snake_case(cx, "trait method", &name.as_str(), Some(span)) | |
257 | } | |
258 | _ => (), | |
259 | } | |
260 | } | |
9e0c209e | 261 | FnKind::ItemFn(name, ..) => { |
b039eaaf | 262 | self.check_snake_case(cx, "function", &name.as_str(), Some(span)) |
c30ab7b3 | 263 | } |
54a0048b | 264 | FnKind::Closure(_) => (), |
b039eaaf SL |
265 | } |
266 | } | |
267 | ||
268 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { | |
269 | if let hir::ItemMod(_) = it.node { | |
270 | self.check_snake_case(cx, "module", &it.name.as_str(), Some(it.span)); | |
271 | } | |
272 | } | |
273 | ||
274 | fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) { | |
275 | if let hir::MethodTraitItem(_, None) = trait_item.node { | |
c30ab7b3 SL |
276 | self.check_snake_case(cx, |
277 | "trait method", | |
278 | &trait_item.name.as_str(), | |
b039eaaf SL |
279 | Some(trait_item.span)); |
280 | } | |
281 | } | |
282 | ||
283 | fn check_lifetime_def(&mut self, cx: &LateContext, t: &hir::LifetimeDef) { | |
c30ab7b3 SL |
284 | self.check_snake_case(cx, |
285 | "lifetime", | |
286 | &t.lifetime.name.as_str(), | |
b039eaaf SL |
287 | Some(t.lifetime.span)); |
288 | } | |
289 | ||
290 | fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) { | |
476ff2be SL |
291 | // Exclude parameter names from foreign functions |
292 | let parent_node = cx.tcx.map.get_parent_node(p.id); | |
293 | if let hir::map::NodeForeignItem(item) = cx.tcx.map.get(parent_node) { | |
294 | if let hir::ForeignItemFn(..) = item.node { | |
295 | return; | |
b039eaaf SL |
296 | } |
297 | } | |
476ff2be SL |
298 | |
299 | if let &PatKind::Binding(_, _, ref path1, _) = &p.node { | |
300 | self.check_snake_case(cx, "variable", &path1.node.as_str(), Some(p.span)); | |
301 | } | |
b039eaaf SL |
302 | } |
303 | ||
c30ab7b3 SL |
304 | fn check_struct_def(&mut self, |
305 | cx: &LateContext, | |
306 | s: &hir::VariantData, | |
307 | _: ast::Name, | |
308 | _: &hir::Generics, | |
309 | _: ast::NodeId) { | |
b039eaaf | 310 | for sf in s.fields() { |
54a0048b | 311 | self.check_snake_case(cx, "structure field", &sf.name.as_str(), Some(sf.span)); |
b039eaaf SL |
312 | } |
313 | } | |
314 | } | |
315 | ||
316 | declare_lint! { | |
317 | pub NON_UPPER_CASE_GLOBALS, | |
318 | Warn, | |
319 | "static constants should have uppercase identifiers" | |
320 | } | |
321 | ||
322 | #[derive(Copy, Clone)] | |
323 | pub struct NonUpperCaseGlobals; | |
324 | ||
325 | impl NonUpperCaseGlobals { | |
326 | fn check_upper_case(cx: &LateContext, sort: &str, name: ast::Name, span: Span) { | |
476ff2be SL |
327 | if name.as_str().chars().any(|c| c.is_lowercase()) { |
328 | let uc = NonSnakeCase::to_snake_case(&name.as_str()).to_uppercase(); | |
329 | if name != &*uc { | |
c30ab7b3 SL |
330 | cx.span_lint(NON_UPPER_CASE_GLOBALS, |
331 | span, | |
332 | &format!("{} `{}` should have an upper case name such as `{}`", | |
333 | sort, | |
476ff2be | 334 | name, |
c30ab7b3 | 335 | uc)); |
b039eaaf | 336 | } else { |
c30ab7b3 SL |
337 | cx.span_lint(NON_UPPER_CASE_GLOBALS, |
338 | span, | |
476ff2be | 339 | &format!("{} `{}` should have an upper case name", sort, name)); |
b039eaaf SL |
340 | } |
341 | } | |
342 | } | |
343 | } | |
344 | ||
345 | impl LintPass for NonUpperCaseGlobals { | |
346 | fn get_lints(&self) -> LintArray { | |
347 | lint_array!(NON_UPPER_CASE_GLOBALS) | |
348 | } | |
349 | } | |
350 | ||
476ff2be | 351 | impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NonUpperCaseGlobals { |
b039eaaf SL |
352 | fn check_item(&mut self, cx: &LateContext, it: &hir::Item) { |
353 | match it.node { | |
c30ab7b3 SL |
354 | hir::ItemStatic(..) => { |
355 | NonUpperCaseGlobals::check_upper_case(cx, "static variable", it.name, it.span); | |
b039eaaf SL |
356 | } |
357 | hir::ItemConst(..) => { | |
358 | NonUpperCaseGlobals::check_upper_case(cx, "constant", it.name, it.span); | |
359 | } | |
360 | _ => {} | |
361 | } | |
362 | } | |
363 | ||
364 | fn check_trait_item(&mut self, cx: &LateContext, ti: &hir::TraitItem) { | |
365 | match ti.node { | |
366 | hir::ConstTraitItem(..) => { | |
c30ab7b3 | 367 | NonUpperCaseGlobals::check_upper_case(cx, "associated constant", ti.name, ti.span); |
b039eaaf SL |
368 | } |
369 | _ => {} | |
370 | } | |
371 | } | |
372 | ||
373 | fn check_impl_item(&mut self, cx: &LateContext, ii: &hir::ImplItem) { | |
374 | match ii.node { | |
92a42be0 | 375 | hir::ImplItemKind::Const(..) => { |
c30ab7b3 | 376 | NonUpperCaseGlobals::check_upper_case(cx, "associated constant", ii.name, ii.span); |
b039eaaf SL |
377 | } |
378 | _ => {} | |
379 | } | |
380 | } | |
381 | ||
382 | fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) { | |
383 | // Lint for constants that look like binding identifiers (#7526) | |
476ff2be | 384 | if let PatKind::Path(hir::QPath::Resolved(None, ref path)) = p.node { |
3157f602 | 385 | if !path.global && path.segments.len() == 1 && path.segments[0].parameters.is_empty() { |
476ff2be | 386 | if let Def::Const(..) = path.def { |
c30ab7b3 SL |
387 | NonUpperCaseGlobals::check_upper_case(cx, |
388 | "constant in pattern", | |
389 | path.segments[0].name, | |
390 | path.span); | |
3157f602 | 391 | } |
b039eaaf | 392 | } |
b039eaaf SL |
393 | } |
394 | } | |
395 | } |