1 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}
;
3 use rustc_attr
as attr
;
4 use rustc_errors
::Applicability
;
6 use rustc_hir
::def
::{DefKind, Res}
;
7 use rustc_hir
::intravisit
::FnKind
;
8 use rustc_hir
::{GenericParamKind, PatKind}
;
10 use rustc_span
::symbol
::sym
;
11 use rustc_span
::{symbol::Ident, BytePos, Span}
;
12 use rustc_target
::spec
::abi
::Abi
;
15 pub enum MethodLateContext
{
21 pub fn method_context(cx
: &LateContext
<'_
>, id
: hir
::HirId
) -> MethodLateContext
{
22 let def_id
= cx
.tcx
.hir().local_def_id(id
);
23 let item
= cx
.tcx
.associated_item(def_id
);
24 match item
.container
{
25 ty
::TraitContainer(..) => MethodLateContext
::TraitAutoImpl
,
26 ty
::ImplContainer(cid
) => match cx
.tcx
.impl_trait_ref(cid
) {
27 Some(_
) => MethodLateContext
::TraitImpl
,
28 None
=> MethodLateContext
::PlainImpl
,
34 pub NON_CAMEL_CASE_TYPES
,
36 "types, variants, traits and type parameters should have camel case names"
39 declare_lint_pass
!(NonCamelCaseTypes
=> [NON_CAMEL_CASE_TYPES
]);
41 fn char_has_case(c
: char) -> bool
{
42 c
.is_lowercase() || c
.is_uppercase()
45 fn is_camel_case(name
: &str) -> bool
{
46 let name
= name
.trim_matches('_'
);
51 // start with a non-lowercase letter rather than non-uppercase
52 // ones (some scripts don't have a concept of upper/lowercase)
53 !name
.chars().next().unwrap().is_lowercase()
54 && !name
.contains("__")
55 && !name
.chars().collect
::<Vec
<_
>>().windows(2).any(|pair
| {
56 // contains a capitalisable character followed by, or preceded by, an underscore
57 char_has_case(pair
[0]) && pair
[1] == '_'
|| char_has_case(pair
[1]) && pair
[0] == '_'
61 fn to_camel_case(s
: &str) -> String
{
64 .filter(|component
| !component
.is_empty())
66 let mut camel_cased_component
= String
::new();
68 let mut new_word
= true;
69 let mut prev_is_lower_case
= true;
71 for c
in component
.chars() {
72 // Preserve the case if an uppercase letter follows a lowercase letter, so that
73 // `camelCase` is converted to `CamelCase`.
74 if prev_is_lower_case
&& c
.is_uppercase() {
79 camel_cased_component
.push_str(&c
.to_uppercase().to_string());
81 camel_cased_component
.push_str(&c
.to_lowercase().to_string());
84 prev_is_lower_case
= c
.is_lowercase();
90 .fold((String
::new(), None
), |(acc
, prev
): (String
, Option
<String
>), next
| {
91 // separate two components with an underscore if their boundary cannot
92 // be distinguished using a uppercase/lowercase case distinction
93 let join
= if let Some(prev
) = prev
{
94 let l
= prev
.chars().last().unwrap();
95 let f
= next
.chars().next().unwrap();
96 !char_has_case(l
) && !char_has_case(f
)
100 (acc
+ if join { "_" }
else { "" }
+ &next
, Some(next
))
105 impl NonCamelCaseTypes
{
106 fn check_case(&self, cx
: &EarlyContext
<'_
>, sort
: &str, ident
: &Ident
) {
107 let name
= &ident
.name
.as_str();
109 if !is_camel_case(name
) {
110 cx
.struct_span_lint(NON_CAMEL_CASE_TYPES
, ident
.span
, |lint
| {
111 let msg
= format
!("{} `{}` should have an upper camel case name", sort
, name
);
115 "convert the identifier to upper camel case",
117 Applicability
::MaybeIncorrect
,
125 impl EarlyLintPass
for NonCamelCaseTypes
{
126 fn check_item(&mut self, cx
: &EarlyContext
<'_
>, it
: &ast
::Item
) {
130 .any(|attr
| attr
::find_repr_attrs(&cx
.sess
, attr
).contains(&attr
::ReprC
));
137 ast
::ItemKind
::TyAlias(..)
138 | ast
::ItemKind
::Enum(..)
139 | ast
::ItemKind
::Struct(..)
140 | ast
::ItemKind
::Union(..) => self.check_case(cx
, "type", &it
.ident
),
141 ast
::ItemKind
::Trait(..) => self.check_case(cx
, "trait", &it
.ident
),
146 fn check_trait_item(&mut self, cx
: &EarlyContext
<'_
>, it
: &ast
::AssocItem
) {
147 if let ast
::AssocItemKind
::TyAlias(..) = it
.kind
{
148 self.check_case(cx
, "associated type", &it
.ident
);
152 fn check_variant(&mut self, cx
: &EarlyContext
<'_
>, v
: &ast
::Variant
) {
153 self.check_case(cx
, "variant", &v
.ident
);
156 fn check_generic_param(&mut self, cx
: &EarlyContext
<'_
>, param
: &ast
::GenericParam
) {
157 if let ast
::GenericParamKind
::Type { .. }
= param
.kind
{
158 self.check_case(cx
, "type parameter", ¶m
.ident
);
166 "variables, methods, functions, lifetime parameters and modules should have snake case names"
169 declare_lint_pass
!(NonSnakeCase
=> [NON_SNAKE_CASE
]);
172 fn to_snake_case(mut str: &str) -> String
{
173 let mut words
= vec
![];
174 // Preserve leading underscores
175 str = str.trim_start_matches(|c
: char| {
177 words
.push(String
::new());
183 for s
in str.split('_'
) {
184 let mut last_upper
= false;
185 let mut buf
= String
::new();
189 for ch
in s
.chars() {
190 if !buf
.is_empty() && buf
!= "'" && ch
.is_uppercase() && !last_upper
{
194 last_upper
= ch
.is_uppercase();
195 buf
.extend(ch
.to_lowercase());
202 /// Checks if a given identifier is snake case, and reports a diagnostic if not.
203 fn check_snake_case(&self, cx
: &LateContext
<'_
>, sort
: &str, ident
: &Ident
) {
204 fn is_snake_case(ident
: &str) -> bool
{
205 if ident
.is_empty() {
208 let ident
= ident
.trim_start_matches('
\''
);
209 let ident
= ident
.trim_matches('_'
);
211 let mut allow_underscore
= true;
212 ident
.chars().all(|c
| {
213 allow_underscore
= match c
{
214 '_'
if !allow_underscore
=> return false,
216 // It would be more obvious to use `c.is_lowercase()`,
217 // but some characters do not have a lowercase form
218 c
if !c
.is_uppercase() => true,
225 let name
= &ident
.name
.as_str();
227 if !is_snake_case(name
) {
228 cx
.struct_span_lint(NON_SNAKE_CASE
, ident
.span
, |lint
| {
229 let sc
= NonSnakeCase
::to_snake_case(name
);
230 let msg
= format
!("{} `{}` should have a snake case name", sort
, name
);
231 let mut err
= lint
.build(&msg
);
232 // We have a valid span in almost all cases, but we don't have one when linting a crate
233 // name provided via the command line.
234 if !ident
.span
.is_dummy() {
237 "convert the identifier to snake case",
239 Applicability
::MaybeIncorrect
,
242 err
.help(&format
!("convert the identifier to snake case: `{}`", sc
));
251 impl<'tcx
> LateLintPass
<'tcx
> for NonSnakeCase
{
254 cx
: &LateContext
<'_
>,
255 _
: &'tcx hir
::Mod
<'tcx
>,
259 if id
!= hir
::CRATE_HIR_ID
{
263 let crate_ident
= if let Some(name
) = &cx
.tcx
.sess
.opts
.crate_name
{
264 Some(Ident
::from_str(name
))
267 .find_by_name(&cx
.tcx
.hir().attrs(hir
::CRATE_HIR_ID
), sym
::crate_name
)
268 .and_then(|attr
| attr
.meta())
270 meta
.name_value_literal().and_then(|lit
| {
271 if let ast
::LitKind
::Str(name
, ..) = lit
.kind
{
272 // Discard the double quotes surrounding the literal.
276 .span_to_snippet(lit
.span
)
278 .and_then(|snippet
| {
279 let left
= snippet
.find('
"')?;
281 snippet.rfind('"'
).map(|pos
| snippet
.len() - pos
)?
;
285 .with_lo(lit
.span
.lo() + BytePos(left
as u32 + 1))
286 .with_hi(lit
.span
.hi() - BytePos(right
as u32)),
289 .unwrap_or_else(|| lit
.span
);
291 Some(Ident
::new(name
, sp
))
299 if let Some(ident
) = &crate_ident
{
300 self.check_snake_case(cx
, "crate", ident
);
304 fn check_generic_param(&mut self, cx
: &LateContext
<'_
>, param
: &hir
::GenericParam
<'_
>) {
305 if let GenericParamKind
::Lifetime { .. }
= param
.kind
{
306 self.check_snake_case(cx
, "lifetime", ¶m
.name
.ident());
312 cx
: &LateContext
<'_
>,
320 FnKind
::Method(ident
, ..) => match method_context(cx
, id
) {
321 MethodLateContext
::PlainImpl
=> {
322 self.check_snake_case(cx
, "method", ident
);
324 MethodLateContext
::TraitAutoImpl
=> {
325 self.check_snake_case(cx
, "trait method", ident
);
329 FnKind
::ItemFn(ident
, _
, header
, _
, attrs
) => {
330 // Skip foreign-ABI #[no_mangle] functions (Issue #31924)
331 if header
.abi
!= Abi
::Rust
&& cx
.sess().contains_name(attrs
, sym
::no_mangle
) {
334 self.check_snake_case(cx
, "function", ident
);
336 FnKind
::Closure(_
) => (),
340 fn check_item(&mut self, cx
: &LateContext
<'_
>, it
: &hir
::Item
<'_
>) {
341 if let hir
::ItemKind
::Mod(_
) = it
.kind
{
342 self.check_snake_case(cx
, "module", &it
.ident
);
346 fn check_trait_item(&mut self, cx
: &LateContext
<'_
>, item
: &hir
::TraitItem
<'_
>) {
347 if let hir
::TraitItemKind
::Fn(_
, hir
::TraitFn
::Required(pnames
)) = item
.kind
{
348 self.check_snake_case(cx
, "trait method", &item
.ident
);
349 for param_name
in pnames
{
350 self.check_snake_case(cx
, "variable", param_name
);
355 fn check_pat(&mut self, cx
: &LateContext
<'_
>, p
: &hir
::Pat
<'_
>) {
356 if let &PatKind
::Binding(_
, hid
, ident
, _
) = &p
.kind
{
357 if let hir
::Node
::Pat(parent_pat
) = cx
.tcx
.hir().get(cx
.tcx
.hir().get_parent_node(hid
))
359 if let PatKind
::Struct(_
, field_pats
, _
) = &parent_pat
.kind
{
360 for field
in field_pats
.iter() {
361 if field
.ident
!= ident
{
362 // Only check if a new name has been introduced, to avoid warning
363 // on both the struct definition and this pattern.
364 self.check_snake_case(cx
, "variable", &ident
);
370 self.check_snake_case(cx
, "variable", &ident
);
374 fn check_struct_def(&mut self, cx
: &LateContext
<'_
>, s
: &hir
::VariantData
<'_
>) {
375 for sf
in s
.fields() {
376 self.check_snake_case(cx
, "structure field", &sf
.ident
);
382 pub NON_UPPER_CASE_GLOBALS
,
384 "static constants should have uppercase identifiers"
387 declare_lint_pass
!(NonUpperCaseGlobals
=> [NON_UPPER_CASE_GLOBALS
]);
389 impl NonUpperCaseGlobals
{
390 fn check_upper_case(cx
: &LateContext
<'_
>, sort
: &str, ident
: &Ident
) {
391 let name
= &ident
.name
.as_str();
392 if name
.chars().any(|c
| c
.is_lowercase()) {
393 cx
.struct_span_lint(NON_UPPER_CASE_GLOBALS
, ident
.span
, |lint
| {
394 let uc
= NonSnakeCase
::to_snake_case(&name
).to_uppercase();
395 lint
.build(&format
!("{} `{}` should have an upper case name", sort
, name
))
398 "convert the identifier to upper case",
400 Applicability
::MaybeIncorrect
,
408 impl<'tcx
> LateLintPass
<'tcx
> for NonUpperCaseGlobals
{
409 fn check_item(&mut self, cx
: &LateContext
<'_
>, it
: &hir
::Item
<'_
>) {
411 hir
::ItemKind
::Static(..) if !cx
.sess().contains_name(&it
.attrs
, sym
::no_mangle
) => {
412 NonUpperCaseGlobals
::check_upper_case(cx
, "static variable", &it
.ident
);
414 hir
::ItemKind
::Const(..) => {
415 NonUpperCaseGlobals
::check_upper_case(cx
, "constant", &it
.ident
);
421 fn check_trait_item(&mut self, cx
: &LateContext
<'_
>, ti
: &hir
::TraitItem
<'_
>) {
422 if let hir
::TraitItemKind
::Const(..) = ti
.kind
{
423 NonUpperCaseGlobals
::check_upper_case(cx
, "associated constant", &ti
.ident
);
427 fn check_impl_item(&mut self, cx
: &LateContext
<'_
>, ii
: &hir
::ImplItem
<'_
>) {
428 if let hir
::ImplItemKind
::Const(..) = ii
.kind
{
429 NonUpperCaseGlobals
::check_upper_case(cx
, "associated constant", &ii
.ident
);
433 fn check_pat(&mut self, cx
: &LateContext
<'_
>, p
: &hir
::Pat
<'_
>) {
434 // Lint for constants that look like binding identifiers (#7526)
435 if let PatKind
::Path(hir
::QPath
::Resolved(None
, ref path
)) = p
.kind
{
436 if let Res
::Def(DefKind
::Const
, _
) = path
.res
{
437 if path
.segments
.len() == 1 {
438 NonUpperCaseGlobals
::check_upper_case(
440 "constant in pattern",
441 &path
.segments
[0].ident
,
448 fn check_generic_param(&mut self, cx
: &LateContext
<'_
>, param
: &hir
::GenericParam
<'_
>) {
449 if let GenericParamKind
::Const { .. }
= param
.kind
{
450 NonUpperCaseGlobals
::check_upper_case(cx
, "const parameter", ¶m
.name
.ident());