]> git.lizzy.rs Git - rust.git/commitdiff
rustc: Add lint for snake_case functions & methods.
authorKevin Butler <haqkrs@gmail.com>
Sun, 25 May 2014 22:23:41 +0000 (23:23 +0100)
committerKevin Butler <haqkrs@gmail.com>
Fri, 30 May 2014 16:55:41 +0000 (17:55 +0100)
src/librustc/middle/lint.rs
src/test/compile-fail/lint-non-snake-case-functions.rs [new file with mode: 0644]

index eba0bc03bf77654a3be74faf52205da95dc4bde3..8cceb16b34fe83809931a3dac897166afe9d0b52 100644 (file)
@@ -83,6 +83,7 @@ pub enum Lint {
     NonCamelCaseTypes,
     NonUppercaseStatics,
     NonUppercasePatternStatics,
+    NonSnakeCaseFunctions,
     UppercaseVariables,
     UnnecessaryParens,
     TypeLimits,
@@ -220,6 +221,13 @@ pub enum LintSource {
          default: Warn
      }),
 
+    ("non_snake_case_functions",
+     LintSpec {
+         lint: NonSnakeCaseFunctions,
+         desc: "methods and functions should have snake case names",
+         default: Warn
+     }),
+
     ("uppercase_variables",
      LintSpec {
          lint: UppercaseVariables,
@@ -1342,6 +1350,30 @@ fn check_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
     }
 }
 
+fn check_snake_case(cx: &Context, sort: &str, ident: ast::Ident, span: Span) {
+    fn is_snake_case(ident: ast::Ident) -> bool {
+        let ident = token::get_ident(ident);
+        assert!(!ident.get().is_empty());
+        let ident = ident.get().trim_chars('_');
+
+        let mut allow_underscore = true;
+        ident.chars().all(|c| {
+            allow_underscore = match c {
+                c if c.is_lowercase() || c.is_digit() => true,
+                '_' if allow_underscore => false,
+                _ => return false,
+            };
+            true
+        })
+    }
+
+    if !is_snake_case(ident) {
+        cx.span_lint(NonSnakeCaseFunctions, span,
+                    format!("{} `{}` should have a snake case identifier",
+                            sort, token::get_ident(ident)).as_slice());
+    }
+}
+
 fn check_item_non_uppercase_statics(cx: &Context, it: &ast::Item) {
     match it.node {
         // only check static constants
@@ -1618,7 +1650,27 @@ fn check_missing_doc_item(cx: &Context, it: &ast::Item) {
                             desc);
 }
 
+#[deriving(Eq)]
+enum MethodContext {
+    TraitDefaultImpl,
+    TraitImpl,
+    PlainImpl
+}
+
 fn check_missing_doc_method(cx: &Context, m: &ast::Method) {
+    // If the method is an impl for a trait, don't doc.
+    if method_context(cx, m) == TraitImpl { return; }
+
+    // Otherwise, doc according to privacy. This will also check
+    // doc for default methods defined on traits.
+    check_missing_doc_attrs(cx,
+                            Some(m.id),
+                            m.attrs.as_slice(),
+                            m.span,
+                            "a method");
+}
+
+fn method_context(cx: &Context, m: &ast::Method) -> MethodContext {
     let did = ast::DefId {
         krate: ast::LOCAL_CRATE,
         node: m.id
@@ -1628,25 +1680,16 @@ fn check_missing_doc_method(cx: &Context, m: &ast::Method) {
         None => cx.tcx.sess.span_bug(m.span, "missing method descriptor?!"),
         Some(md) => {
             match md.container {
-                // Always check default methods defined on traits.
-                ty::TraitContainer(..) => {}
-                // For methods defined on impls, it depends on whether
-                // it is an implementation for a trait or is a plain
-                // impl.
+                ty::TraitContainer(..) => TraitDefaultImpl,
                 ty::ImplContainer(cid) => {
                     match ty::impl_trait_ref(cx.tcx, cid) {
-                        Some(..) => return, // impl for trait: don't doc
-                        None => {} // plain impl: doc according to privacy
+                        Some(..) => TraitImpl,
+                        None => PlainImpl
                     }
                 }
             }
         }
     }
-    check_missing_doc_attrs(cx,
-                            Some(m.id),
-                            m.attrs.as_slice(),
-                            m.span,
-                            "a method");
 }
 
 fn check_missing_doc_ty_method(cx: &Context, tm: &ast::TypeMethod) {
@@ -1889,26 +1932,36 @@ fn visit_fn(&mut self, fk: &visit::FnKind, decl: &ast::FnDecl,
         }
 
         match *fk {
-            visit::FkMethod(_, _, m) => {
+            visit::FkMethod(ident, _, m) => {
                 self.with_lint_attrs(m.attrs.as_slice(), |cx| {
                     check_missing_doc_method(cx, m);
                     check_attrs_usage(cx, m.attrs.as_slice());
 
+                    match method_context(cx, m) {
+                        PlainImpl => check_snake_case(cx, "method", ident, span),
+                        TraitDefaultImpl => check_snake_case(cx, "trait method", ident, span),
+                        _ => (),
+                    }
+
                     cx.visit_ids(|v| {
                         v.visit_fn(fk, decl, body, span, id, ());
                     });
                     recurse(cx);
                 })
+            },
+            visit::FkItemFn(ident, _, _, _) => {
+                check_snake_case(self, "function", ident, span);
+                recurse(self);
             }
             _ => recurse(self),
         }
     }
 
-
     fn visit_ty_method(&mut self, t: &ast::TypeMethod, _: ()) {
         self.with_lint_attrs(t.attrs.as_slice(), |cx| {
             check_missing_doc_ty_method(cx, t);
             check_attrs_usage(cx, t.attrs.as_slice());
+            check_snake_case(cx, "trait method", t.ident, t.span);
 
             visit::walk_ty_method(cx, t, ());
         })
diff --git a/src/test/compile-fail/lint-non-snake-case-functions.rs b/src/test/compile-fail/lint-non-snake-case-functions.rs
new file mode 100644 (file)
index 0000000..02ab85a
--- /dev/null
@@ -0,0 +1,51 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(non_snake_case_functions)]
+#![allow(dead_code)]
+
+struct Foo;
+
+impl Foo {
+    fn Foo_Method() {}
+    //~^ ERROR method `Foo_Method` should have a snake case identifier
+
+    // Don't allow two underscores in a row
+    fn foo__method(&self) {}
+    //~^ ERROR method `foo__method` should have a snake case identifier
+
+    pub fn xyZ(&mut self) {}
+    //~^ ERROR method `xyZ` should have a snake case identifier
+}
+
+trait X {
+    fn ABC();
+    //~^ ERROR trait method `ABC` should have a snake case identifier
+
+    fn a_b_C(&self) {}
+    //~^ ERROR trait method `a_b_C` should have a snake case identifier
+
+    fn something__else(&mut self);
+    //~^ ERROR trait method `something__else` should have a snake case identifier
+}
+
+impl X for Foo {
+    // These errors should be caught at the trait definition not the impl
+    fn ABC() {}
+    fn something__else(&mut self) {}
+}
+
+fn Cookie() {}
+//~^ ERROR function `Cookie` should have a snake case identifier
+
+pub fn bi_S_Cuit() {}
+//~^ ERROR function `bi_S_Cuit` should have a snake case identifier
+
+fn main() { }