Skip to content

Commit ed77fcc

Browse files
committed
Split out the bad_style lints into a new module
Part of #22206
1 parent 837840c commit ed77fcc

File tree

3 files changed

+382
-354
lines changed

3 files changed

+382
-354
lines changed

src/librustc_lint/bad_style.rs

+372
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,372 @@
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 middle::def;
12+
use middle::def_id::DefId;
13+
use middle::ty;
14+
use lint::{LateContext, LintContext, LintArray};
15+
use lint::{LintPass, LateLintPass};
16+
17+
use syntax::ast;
18+
use syntax::attr::{self, AttrMetaMethods};
19+
use syntax::codemap::Span;
20+
21+
use rustc_front::hir;
22+
use rustc_front::visit::FnKind;
23+
24+
#[derive(PartialEq)]
25+
pub enum MethodLateContext {
26+
TraitDefaultImpl,
27+
TraitImpl,
28+
PlainImpl
29+
}
30+
31+
pub fn method_context(cx: &LateContext, id: ast::NodeId, span: Span) -> MethodLateContext {
32+
match cx.tcx.impl_or_trait_items.borrow().get(&DefId::local(id)) {
33+
None => cx.sess().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, ident: ast::Ident, span: Span) {
57+
fn is_camel_case(ident: ast::Ident) -> bool {
58+
let ident = ident.name.as_str();
59+
if ident.is_empty() {
60+
return true;
61+
}
62+
let ident = ident.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+
!ident.is_empty() && !ident.char_at(0).is_lowercase() && !ident.contains('_')
67+
}
68+
69+
fn to_camel_case(s: &str) -> String {
70+
s.split('_').flat_map(|word| word.chars().enumerate().map(|(i, c)|
71+
if i == 0 {
72+
c.to_uppercase().collect::<String>()
73+
} else {
74+
c.to_lowercase().collect()
75+
}
76+
)).collect::<Vec<_>>().concat()
77+
}
78+
79+
let s = ident.name.as_str();
80+
81+
if !is_camel_case(ident) {
82+
let c = to_camel_case(&s);
83+
let m = if c.is_empty() {
84+
format!("{} `{}` should have a camel case name such as `CamelCase`", sort, s)
85+
} else {
86+
format!("{} `{}` should have a camel case name such as `{}`", sort, s, c)
87+
};
88+
cx.span_lint(NON_CAMEL_CASE_TYPES, span, &m[..]);
89+
}
90+
}
91+
}
92+
93+
impl LintPass for NonCamelCaseTypes {
94+
fn get_lints(&self) -> LintArray {
95+
lint_array!(NON_CAMEL_CASE_TYPES)
96+
}
97+
}
98+
99+
impl LateLintPass for NonCamelCaseTypes {
100+
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
101+
let extern_repr_count = it.attrs.iter().filter(|attr| {
102+
attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter()
103+
.any(|r| r == &attr::ReprExtern)
104+
}).count();
105+
let has_extern_repr = extern_repr_count > 0;
106+
107+
if has_extern_repr {
108+
return;
109+
}
110+
111+
match it.node {
112+
hir::ItemTy(..) | hir::ItemStruct(..) => {
113+
self.check_case(cx, "type", it.ident, it.span)
114+
}
115+
hir::ItemTrait(..) => {
116+
self.check_case(cx, "trait", it.ident, it.span)
117+
}
118+
hir::ItemEnum(ref enum_definition, _) => {
119+
if has_extern_repr {
120+
return;
121+
}
122+
self.check_case(cx, "type", it.ident, it.span);
123+
for variant in &enum_definition.variants {
124+
self.check_case(cx, "variant", variant.node.name, variant.span);
125+
}
126+
}
127+
_ => ()
128+
}
129+
}
130+
131+
fn check_generics(&mut self, cx: &LateContext, it: &hir::Generics) {
132+
for gen in it.ty_params.iter() {
133+
self.check_case(cx, "type parameter", gen.ident, gen.span);
134+
}
135+
}
136+
}
137+
138+
declare_lint! {
139+
pub NON_SNAKE_CASE,
140+
Warn,
141+
"methods, functions, lifetime parameters and modules should have snake case names"
142+
}
143+
144+
#[derive(Copy, Clone)]
145+
pub struct NonSnakeCase;
146+
147+
impl NonSnakeCase {
148+
fn to_snake_case(mut str: &str) -> String {
149+
let mut words = vec![];
150+
// Preserve leading underscores
151+
str = str.trim_left_matches(|c: char| {
152+
if c == '_' {
153+
words.push(String::new());
154+
true
155+
} else {
156+
false
157+
}
158+
});
159+
for s in str.split('_') {
160+
let mut last_upper = false;
161+
let mut buf = String::new();
162+
if s.is_empty() {
163+
continue;
164+
}
165+
for ch in s.chars() {
166+
if !buf.is_empty() && buf != "'"
167+
&& ch.is_uppercase()
168+
&& !last_upper {
169+
words.push(buf);
170+
buf = String::new();
171+
}
172+
last_upper = ch.is_uppercase();
173+
buf.extend(ch.to_lowercase());
174+
}
175+
words.push(buf);
176+
}
177+
words.join("_")
178+
}
179+
180+
fn check_snake_case(&self, cx: &LateContext, sort: &str, name: &str, span: Option<Span>) {
181+
fn is_snake_case(ident: &str) -> bool {
182+
if ident.is_empty() {
183+
return true;
184+
}
185+
let ident = ident.trim_left_matches('\'');
186+
let ident = ident.trim_matches('_');
187+
188+
let mut allow_underscore = true;
189+
ident.chars().all(|c| {
190+
allow_underscore = match c {
191+
'_' if !allow_underscore => return false,
192+
'_' => false,
193+
// It would be more obvious to use `c.is_lowercase()`,
194+
// but some characters do not have a lowercase form
195+
c if !c.is_uppercase() => true,
196+
_ => return false,
197+
};
198+
true
199+
})
200+
}
201+
202+
if !is_snake_case(name) {
203+
let sc = NonSnakeCase::to_snake_case(name);
204+
let msg = if sc != name {
205+
format!("{} `{}` should have a snake case name such as `{}`",
206+
sort, name, sc)
207+
} else {
208+
format!("{} `{}` should have a snake case name",
209+
sort, name)
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+
225+
impl LateLintPass for NonSnakeCase {
226+
fn check_crate(&mut self, cx: &LateContext, cr: &hir::Crate) {
227+
let attr_crate_name = cr.attrs.iter().find(|at| at.check_name("crate_name"))
228+
.and_then(|at| at.value_str().map(|s| (at, s)));
229+
if let Some(ref name) = cx.tcx.sess.opts.crate_name {
230+
self.check_snake_case(cx, "crate", name, None);
231+
} else if let Some((attr, ref name)) = attr_crate_name {
232+
self.check_snake_case(cx, "crate", name, Some(attr.span));
233+
}
234+
}
235+
236+
fn check_fn(&mut self, cx: &LateContext,
237+
fk: FnKind, _: &hir::FnDecl,
238+
_: &hir::Block, span: Span, id: ast::NodeId) {
239+
match fk {
240+
FnKind::Method(ident, _, _) => match method_context(cx, id, span) {
241+
MethodLateContext::PlainImpl => {
242+
self.check_snake_case(cx, "method", &ident.name.as_str(), Some(span))
243+
},
244+
MethodLateContext::TraitDefaultImpl => {
245+
self.check_snake_case(cx, "trait method", &ident.name.as_str(), Some(span))
246+
},
247+
_ => (),
248+
},
249+
FnKind::ItemFn(ident, _, _, _, _, _) => {
250+
self.check_snake_case(cx, "function", &ident.name.as_str(), Some(span))
251+
},
252+
_ => (),
253+
}
254+
}
255+
256+
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
257+
if let hir::ItemMod(_) = it.node {
258+
self.check_snake_case(cx, "module", &it.ident.name.as_str(), Some(it.span));
259+
}
260+
}
261+
262+
fn check_trait_item(&mut self, cx: &LateContext, trait_item: &hir::TraitItem) {
263+
if let hir::MethodTraitItem(_, None) = trait_item.node {
264+
self.check_snake_case(cx, "trait method", &trait_item.ident.name.as_str(),
265+
Some(trait_item.span));
266+
}
267+
}
268+
269+
fn check_lifetime_def(&mut self, cx: &LateContext, t: &hir::LifetimeDef) {
270+
self.check_snake_case(cx, "lifetime", &t.lifetime.name.as_str(),
271+
Some(t.lifetime.span));
272+
}
273+
274+
fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) {
275+
if let &hir::PatIdent(_, ref path1, _) = &p.node {
276+
let def = cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def());
277+
if let Some(def::DefLocal(_)) = def {
278+
self.check_snake_case(cx, "variable", &path1.node.name.as_str(), Some(p.span));
279+
}
280+
}
281+
}
282+
283+
fn check_struct_def(&mut self, cx: &LateContext, s: &hir::StructDef,
284+
_: ast::Ident, _: &hir::Generics, _: ast::NodeId) {
285+
for sf in &s.fields {
286+
if let hir::StructField_ { kind: hir::NamedField(ident, _), .. } = sf.node {
287+
self.check_snake_case(cx, "structure field", &ident.name.as_str(),
288+
Some(sf.span));
289+
}
290+
}
291+
}
292+
}
293+
294+
declare_lint! {
295+
pub NON_UPPER_CASE_GLOBALS,
296+
Warn,
297+
"static constants should have uppercase identifiers"
298+
}
299+
300+
#[derive(Copy, Clone)]
301+
pub struct NonUpperCaseGlobals;
302+
303+
impl NonUpperCaseGlobals {
304+
fn check_upper_case(cx: &LateContext, sort: &str, ident: ast::Ident, span: Span) {
305+
let s = ident.name.as_str();
306+
307+
if s.chars().any(|c| c.is_lowercase()) {
308+
let uc = NonSnakeCase::to_snake_case(&s).to_uppercase();
309+
if uc != &s[..] {
310+
cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
311+
&format!("{} `{}` should have an upper case name such as `{}`",
312+
sort, s, uc));
313+
} else {
314+
cx.span_lint(NON_UPPER_CASE_GLOBALS, span,
315+
&format!("{} `{}` should have an upper case name",
316+
sort, s));
317+
}
318+
}
319+
}
320+
}
321+
322+
impl LintPass for NonUpperCaseGlobals {
323+
fn get_lints(&self) -> LintArray {
324+
lint_array!(NON_UPPER_CASE_GLOBALS)
325+
}
326+
}
327+
328+
impl LateLintPass for NonUpperCaseGlobals {
329+
fn check_item(&mut self, cx: &LateContext, it: &hir::Item) {
330+
match it.node {
331+
// only check static constants
332+
hir::ItemStatic(_, hir::MutImmutable, _) => {
333+
NonUpperCaseGlobals::check_upper_case(cx, "static constant", it.ident, it.span);
334+
}
335+
hir::ItemConst(..) => {
336+
NonUpperCaseGlobals::check_upper_case(cx, "constant", it.ident, it.span);
337+
}
338+
_ => {}
339+
}
340+
}
341+
342+
fn check_trait_item(&mut self, cx: &LateContext, ti: &hir::TraitItem) {
343+
match ti.node {
344+
hir::ConstTraitItem(..) => {
345+
NonUpperCaseGlobals::check_upper_case(cx, "associated constant",
346+
ti.ident, ti.span);
347+
}
348+
_ => {}
349+
}
350+
}
351+
352+
fn check_impl_item(&mut self, cx: &LateContext, ii: &hir::ImplItem) {
353+
match ii.node {
354+
hir::ConstImplItem(..) => {
355+
NonUpperCaseGlobals::check_upper_case(cx, "associated constant",
356+
ii.ident, ii.span);
357+
}
358+
_ => {}
359+
}
360+
}
361+
362+
fn check_pat(&mut self, cx: &LateContext, p: &hir::Pat) {
363+
// Lint for constants that look like binding identifiers (#7526)
364+
match (&p.node, cx.tcx.def_map.borrow().get(&p.id).map(|d| d.full_def())) {
365+
(&hir::PatIdent(_, ref path1, _), Some(def::DefConst(..))) => {
366+
NonUpperCaseGlobals::check_upper_case(cx, "constant in pattern",
367+
path1.node, p.span);
368+
}
369+
_ => {}
370+
}
371+
}
372+
}

0 commit comments

Comments
 (0)