1 use rustc
::hir
::{self, GenericParamKind, PatKind}
;
2 use rustc
::hir
::def
::{Res, DefKind}
;
3 use rustc
::hir
::intravisit
::FnKind
;
6 use rustc_target
::spec
::abi
::Abi
;
7 use lint
::{EarlyContext, LateContext, LintContext, LintArray}
;
8 use lint
::{EarlyLintPass, LintPass, LateLintPass}
;
11 use syntax
::errors
::Applicability
;
12 use syntax
::symbol
::sym
;
13 use syntax_pos
::{BytePos, symbol::Ident, Span}
;
16 pub enum MethodLateContext
{
22 pub fn method_context(cx
: &LateContext
<'_
, '_
>, id
: hir
::HirId
) -> MethodLateContext
{
23 let def_id
= cx
.tcx
.hir().local_def_id(id
);
24 let item
= cx
.tcx
.associated_item(def_id
);
25 match item
.container
{
26 ty
::TraitContainer(..) => MethodLateContext
::TraitAutoImpl
,
27 ty
::ImplContainer(cid
) => {
28 match cx
.tcx
.impl_trait_ref(cid
) {
29 Some(_
) => MethodLateContext
::TraitImpl
,
30 None
=> MethodLateContext
::PlainImpl
,
37 pub NON_CAMEL_CASE_TYPES
,
39 "types, variants, traits and type parameters should have camel case names"
42 declare_lint_pass
!(NonCamelCaseTypes
=> [NON_CAMEL_CASE_TYPES
]);
44 fn char_has_case(c
: char) -> bool
{
45 c
.is_lowercase() || c
.is_uppercase()
48 fn is_camel_case(name
: &str) -> bool
{
49 let name
= name
.trim_matches('_'
);
54 // start with a non-lowercase letter rather than non-uppercase
55 // ones (some scripts don't have a concept of upper/lowercase)
56 !name
.chars().next().unwrap().is_lowercase()
57 && !name
.contains("__")
58 && !name
.chars().collect
::<Vec
<_
>>().windows(2).any(|pair
| {
59 // contains a capitalisable character followed by, or preceded by, an underscore
60 char_has_case(pair
[0]) && pair
[1] == '_'
|| char_has_case(pair
[1]) && pair
[0] == '_'
64 fn to_camel_case(s
: &str) -> String
{
67 .filter(|component
| !component
.is_empty())
69 let mut camel_cased_component
= String
::new();
71 let mut new_word
= true;
72 let mut prev_is_lower_case
= true;
74 for c
in component
.chars() {
75 // Preserve the case if an uppercase letter follows a lowercase letter, so that
76 // `camelCase` is converted to `CamelCase`.
77 if prev_is_lower_case
&& c
.is_uppercase() {
82 camel_cased_component
.push_str(&c
.to_uppercase().to_string());
84 camel_cased_component
.push_str(&c
.to_lowercase().to_string());
87 prev_is_lower_case
= c
.is_lowercase();
94 (String
::new(), None
),
95 |(acc
, prev
): (String
, Option
<String
>), next
| {
96 // separate two components with an underscore if their boundary cannot
97 // be distinguished using a uppercase/lowercase case distinction
98 let join
= if let Some(prev
) = prev
{
99 let l
= prev
.chars().last().unwrap();
100 let f
= next
.chars().next().unwrap();
101 !char_has_case(l
) && !char_has_case(f
)
105 (acc
+ if join { "_" }
else { "" }
+ &next
, Some(next
))
111 impl NonCamelCaseTypes
{
112 fn check_case(&self, cx
: &EarlyContext
<'_
>, sort
: &str, ident
: &Ident
) {
113 let name
= &ident
.name
.as_str();
115 if !is_camel_case(name
) {
116 let msg
= format
!("{} `{}` should have an upper camel case name", sort
, name
);
117 cx
.struct_span_lint(NON_CAMEL_CASE_TYPES
, ident
.span
, &msg
)
120 "convert the identifier to upper camel case",
122 Applicability
::MaybeIncorrect
,
129 impl EarlyLintPass
for NonCamelCaseTypes
{
130 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, it
: &ast
::Item
) {
131 let has_repr_c
= it
.attrs
133 .any(|attr
| attr
::find_repr_attrs(&cx
.sess
.parse_sess
, attr
).contains(&attr
::ReprC
));
140 ast
::ItemKind
::TyAlias(..) |
141 ast
::ItemKind
::Enum(..) |
142 ast
::ItemKind
::Struct(..) |
143 ast
::ItemKind
::Union(..) => self.check_case(cx
, "type", &it
.ident
),
144 ast
::ItemKind
::Trait(..) => self.check_case(cx
, "trait", &it
.ident
),
149 fn check_variant(&mut self, cx
: &EarlyContext
<'_
>, v
: &ast
::Variant
, _
: &ast
::Generics
) {
150 self.check_case(cx
, "variant", &v
.node
.ident
);
153 fn check_generic_param(&mut self, cx
: &EarlyContext
<'_
>, param
: &ast
::GenericParam
) {
154 if let ast
::GenericParamKind
::Type { .. }
= param
.kind
{
155 self.check_case(cx
, "type parameter", ¶m
.ident
);
163 "variables, methods, functions, lifetime parameters and modules should have snake case names"
166 declare_lint_pass
!(NonSnakeCase
=> [NON_SNAKE_CASE
]);
169 fn to_snake_case(mut str: &str) -> String
{
170 let mut words
= vec
![];
171 // Preserve leading underscores
172 str = str.trim_start_matches(|c
: char| {
174 words
.push(String
::new());
180 for s
in str.split('_'
) {
181 let mut last_upper
= false;
182 let mut buf
= String
::new();
186 for ch
in s
.chars() {
187 if !buf
.is_empty() && buf
!= "'" && ch
.is_uppercase() && !last_upper
{
191 last_upper
= ch
.is_uppercase();
192 buf
.extend(ch
.to_lowercase());
199 /// Checks if a given identifier is snake case, and reports a diagnostic if not.
200 fn check_snake_case(&self, cx
: &LateContext
<'_
, '_
>, sort
: &str, ident
: &Ident
) {
201 fn is_snake_case(ident
: &str) -> bool
{
202 if ident
.is_empty() {
205 let ident
= ident
.trim_start_matches('
\''
);
206 let ident
= ident
.trim_matches('_'
);
208 let mut allow_underscore
= true;
209 ident
.chars().all(|c
| {
210 allow_underscore
= match c
{
211 '_'
if !allow_underscore
=> return false,
213 // It would be more obvious to use `c.is_lowercase()`,
214 // but some characters do not have a lowercase form
215 c
if !c
.is_uppercase() => true,
222 let name
= &ident
.name
.as_str();
224 if !is_snake_case(name
) {
225 let sc
= NonSnakeCase
::to_snake_case(name
);
227 let msg
= format
!("{} `{}` should have a snake case name", sort
, name
);
228 let mut err
= cx
.struct_span_lint(NON_SNAKE_CASE
, ident
.span
, &msg
);
230 // We have a valid span in almost all cases, but we don't have one when linting a crate
231 // name provided via the command line.
232 if !ident
.span
.is_dummy() {
235 "convert the identifier to snake case",
237 Applicability
::MaybeIncorrect
,
240 err
.help(&format
!("convert the identifier to snake case: `{}`", sc
));
248 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for NonSnakeCase
{
249 fn check_mod(&mut self, cx
: &LateContext
<'_
, '_
>, _
: &'tcx hir
::Mod
, _
: Span
, id
: hir
::HirId
) {
250 if id
!= hir
::CRATE_HIR_ID
{
254 let crate_ident
= if let Some(name
) = &cx
.tcx
.sess
.opts
.crate_name
{
255 Some(Ident
::from_str(name
))
257 attr
::find_by_name(&cx
.tcx
.hir().attrs(hir
::CRATE_HIR_ID
), sym
::crate_name
)
258 .and_then(|attr
| attr
.meta())
260 meta
.name_value_literal().and_then(|lit
| {
261 if let ast
::LitKind
::Str(name
, ..) = lit
.node
{
262 // Discard the double quotes surrounding the literal.
263 let sp
= cx
.sess().source_map().span_to_snippet(lit
.span
)
265 .and_then(|snippet
| {
266 let left
= snippet
.find('
"')?;
267 let right = snippet.rfind('"'
).map(|pos
| snippet
.len() - pos
)?
;
271 .with_lo(lit
.span
.lo() + BytePos(left
as u32 + 1))
272 .with_hi(lit
.span
.hi() - BytePos(right
as u32)),
275 .unwrap_or_else(|| lit
.span
);
277 Some(Ident
::new(name
, sp
))
285 if let Some(ident
) = &crate_ident
{
286 self.check_snake_case(cx
, "crate", ident
);
290 fn check_generic_param(&mut self, cx
: &LateContext
<'_
, '_
>, param
: &hir
::GenericParam
) {
291 if let GenericParamKind
::Lifetime { .. }
= param
.kind
{
292 self.check_snake_case(cx
, "lifetime", ¶m
.name
.ident());
298 cx
: &LateContext
<'_
, '_
>,
306 FnKind
::Method(ident
, ..) => {
307 match method_context(cx
, id
) {
308 MethodLateContext
::PlainImpl
=> {
309 self.check_snake_case(cx
, "method", ident
);
311 MethodLateContext
::TraitAutoImpl
=> {
312 self.check_snake_case(cx
, "trait method", ident
);
317 FnKind
::ItemFn(ident
, _
, header
, _
, attrs
) => {
318 // Skip foreign-ABI #[no_mangle] functions (Issue #31924)
319 if header
.abi
!= Abi
::Rust
&& attr
::contains_name(attrs
, sym
::no_mangle
) {
322 self.check_snake_case(cx
, "function", ident
);
324 FnKind
::Closure(_
) => (),
328 fn check_item(&mut self, cx
: &LateContext
<'_
, '_
>, it
: &hir
::Item
) {
329 if let hir
::ItemKind
::Mod(_
) = it
.node
{
330 self.check_snake_case(cx
, "module", &it
.ident
);
334 fn check_trait_item(&mut self, cx
: &LateContext
<'_
, '_
>, item
: &hir
::TraitItem
) {
335 if let hir
::TraitItemKind
::Method(_
, hir
::TraitMethod
::Required(pnames
)) = &item
.node
{
336 self.check_snake_case(cx
, "trait method", &item
.ident
);
337 for param_name
in pnames
{
338 self.check_snake_case(cx
, "variable", param_name
);
343 fn check_pat(&mut self, cx
: &LateContext
<'_
, '_
>, p
: &hir
::Pat
) {
344 if let &PatKind
::Binding(_
, _
, ident
, _
) = &p
.node
{
345 self.check_snake_case(cx
, "variable", &ident
);
351 cx
: &LateContext
<'_
, '_
>,
352 s
: &hir
::VariantData
,
357 for sf
in s
.fields() {
358 self.check_snake_case(cx
, "structure field", &sf
.ident
);
364 pub NON_UPPER_CASE_GLOBALS
,
366 "static constants should have uppercase identifiers"
369 declare_lint_pass
!(NonUpperCaseGlobals
=> [NON_UPPER_CASE_GLOBALS
]);
371 impl NonUpperCaseGlobals
{
372 fn check_upper_case(cx
: &LateContext
<'_
, '_
>, sort
: &str, ident
: &Ident
) {
373 let name
= &ident
.name
.as_str();
375 if name
.chars().any(|c
| c
.is_lowercase()) {
376 let uc
= NonSnakeCase
::to_snake_case(&name
).to_uppercase();
378 let msg
= format
!("{} `{}` should have an upper case name", sort
, name
);
379 cx
.struct_span_lint(NON_UPPER_CASE_GLOBALS
, ident
.span
, &msg
)
382 "convert the identifier to upper case",
384 Applicability
::MaybeIncorrect
,
391 impl<'a
, 'tcx
> LateLintPass
<'a
, 'tcx
> for NonUpperCaseGlobals
{
392 fn check_item(&mut self, cx
: &LateContext
<'_
, '_
>, it
: &hir
::Item
) {
394 hir
::ItemKind
::Static(..) if !attr
::contains_name(&it
.attrs
, sym
::no_mangle
) => {
395 NonUpperCaseGlobals
::check_upper_case(cx
, "static variable", &it
.ident
);
397 hir
::ItemKind
::Const(..) => {
398 NonUpperCaseGlobals
::check_upper_case(cx
, "constant", &it
.ident
);
404 fn check_trait_item(&mut self, cx
: &LateContext
<'_
, '_
>, ti
: &hir
::TraitItem
) {
405 if let hir
::TraitItemKind
::Const(..) = ti
.node
{
406 NonUpperCaseGlobals
::check_upper_case(cx
, "associated constant", &ti
.ident
);
410 fn check_impl_item(&mut self, cx
: &LateContext
<'_
, '_
>, ii
: &hir
::ImplItem
) {
411 if let hir
::ImplItemKind
::Const(..) = ii
.node
{
412 NonUpperCaseGlobals
::check_upper_case(cx
, "associated constant", &ii
.ident
);
416 fn check_pat(&mut self, cx
: &LateContext
<'_
, '_
>, p
: &hir
::Pat
) {
417 // Lint for constants that look like binding identifiers (#7526)
418 if let PatKind
::Path(hir
::QPath
::Resolved(None
, ref path
)) = p
.node
{
419 if let Res
::Def(DefKind
::Const
, _
) = path
.res
{
420 if path
.segments
.len() == 1 {
421 NonUpperCaseGlobals
::check_upper_case(
423 "constant in pattern",
424 &path
.segments
[0].ident
431 fn check_generic_param(&mut self, cx
: &LateContext
<'_
, '_
>, param
: &hir
::GenericParam
) {
432 if let GenericParamKind
::Const { .. }
= param
.kind
{
433 NonUpperCaseGlobals
::check_upper_case(