Skip to content

Commit

Permalink
rustc: Add lint for snake_case functions & methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryman committed May 30, 2014
1 parent 874b56d commit 16f15ce
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 14 deletions.
81 changes: 67 additions & 14 deletions src/librustc/middle/lint.rs
Expand Up @@ -83,6 +83,7 @@ pub enum Lint {
NonCamelCaseTypes,
NonUppercaseStatics,
NonUppercasePatternStatics,
NonSnakeCaseFunctions,
UppercaseVariables,
UnnecessaryParens,
TypeLimits,
Expand Down Expand Up @@ -220,6 +221,13 @@ static lint_table: &'static [(&'static str, LintSpec)] = &[
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,
Expand Down Expand Up @@ -1342,6 +1350,30 @@ fn check_item_non_camel_case_types(cx: &Context, it: &ast::Item) {
}
}

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
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -1889,26 +1932,36 @@ impl<'a> Visitor<()> for Context<'a> {
}

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, ());
})
Expand Down
51 changes: 51 additions & 0 deletions src/test/compile-fail/lint-non-snake-case-functions.rs
@@ -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() { }

0 comments on commit 16f15ce

Please sign in to comment.