Skip to content

Commit

Permalink
RustClippyLintBear: Linter support for Rust code
Browse files Browse the repository at this point in the history
Closes coala#50
  • Loading branch information
rubdos committed Jul 20, 2017
1 parent 989b5cd commit 076aecd
Show file tree
Hide file tree
Showing 10 changed files with 157 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ htmlcov
*.egg-info
.coverage.*
src
!tests/rust/test_*/src
site
.zanata-cache
*.exe
Expand Down
77 changes: 77 additions & 0 deletions bears/rust/RustClippyLintBear.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import json
import os

from coalib.bears.GlobalBear import GlobalBear
from coalib.misc.Shell import run_shell_command
from coalib.results.Result import Result
from coalib.results.RESULT_SEVERITY import RESULT_SEVERITY
from dependency_management.requirements.CargoRequirement import (
CargoRequirement)


class RustClippyLintBear(GlobalBear):
LANGUAGES = {'Rust'}
AUTHORS = {'The coala developers'}
AUTHORS_EMAILS = {'[email protected]'}
LICENSE = 'AGPL-3.0'
CAN_DETECT = {
'Formatting',
'Unused Code',
'Syntax',
'Unreachable Code',
'Smell',
'Code Simplification',
}
EXECUTABLE = 'cargo.exe' if os.name == 'nt' else 'cargo'
REQUIREMENTS = {
CargoRequirement('clippy')
}
SEVERITY_MAP = {
'warning': RESULT_SEVERITY.NORMAL,
'error': RESULT_SEVERITY.MAJOR,
}

def run(self):
args = ('clippy', '--quiet', '--color', 'never',
'--', '-Z', 'unstable-options',
'--error-format', 'json',
'--test')
# In the future, we might want to use --manifest-path, which
# should point to the Cargo.toml file, instead of relying on
# the config directory.
# Currently bugged:
# https://github.com/Manishearth/rust-clippy/issues/1611

_, stderr_output = run_shell_command(
(self.EXECUTABLE,) + args,
cwd=self.get_config_dir(),
universal_newlines=True)

# Rust outputs \n's, instead of the system default.
for line in stderr_output.split('\n'):
# cargo still outputs some text, even when in quiet mode,
# when a project does not build. We treat it as
# a major concern.
if line.startswith('error: Could not compile '):
yield Result(
origin=self.__class__.__name__,
message=line,
severity=RESULT_SEVERITY.MAJOR)
continue
if not line:
continue
if line.startswith('To learn more, run the command again'):
continue
yield self.new_result(json.loads(line))

def new_result(self, issue):
span = issue['spans'][0]
return Result.from_values(
origin=self.__class__.__name__,
message=issue['message'],
file=span['file_name'],
line=span['line_start'],
end_line=span['line_end'],
column=span['column_start'],
end_column=span['column_end'],
severity=self.SEVERITY_MAP[issue['level']])
Empty file added bears/rust/__init__.py
Empty file.
2 changes: 2 additions & 0 deletions tests/rust/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
target
Cargo.lock
40 changes: 40 additions & 0 deletions tests/rust/RustClippyLintBearTest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import unittest
import os
from queue import Queue
from shutil import which
from unittest.case import skipIf

from coalib.settings.Section import Section

from bears.rust.RustClippyLintBear import RustClippyLintBear


@skipIf(which('cargo') is None, 'Cargo is not installed')
class RustClippyLintBearTest(unittest.TestCase):

def setUp(self):
self.section = Section('name')
self.queue = Queue()
self.file_dict = {}
self.uut = RustClippyLintBear(self.file_dict, self.section, self.queue)

def change_directory(self, directory_name):
test_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
directory_name))
os.chdir(test_path)

def set_config_dir(self, directory):
# Work around https://github.com/coala/coala/issues/3867
test_path = os.path.abspath(os.path.join(
os.path.dirname(__file__), directory))
self.uut.get_config_dir = lambda *args, **kwargs: test_path

def test_ok_source(self):
self.set_config_dir('test_ok')
results = list(self.uut.run())
self.assertTrue(len(results) == 0)

def test_bad_source(self):
self.set_config_dir('test_bad')
results = list(self.uut.run())
self.assertTrue(len(results) >= 3)
Empty file added tests/rust/__init__.py
Empty file.
5 changes: 5 additions & 0 deletions tests/rust/test_bad/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "coala_test_files"
version = "0.1.0"

[dependencies]
24 changes: 24 additions & 0 deletions tests/rust/test_bad/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// This function should trigger unused code (warning level)
fn testssss() {
// This comparison should trigger a `deny` level lint
let mut x: f64 = 0.0;
if x == std::f64::NAN {
}
x += 1.; // Triggers allow style lint
println!("{}", x);
}


// We test whether bad code is found in (unit) tests
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let x = Some(1u8);
// This match triggers a `warning` level lint
match x {
Some(y) => println!("{:?}", y),
_ => ()
}
}
}
5 changes: 5 additions & 0 deletions tests/rust/test_ok/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[package]
name = "coala_test_files"
version = "0.1.0"

[dependencies]
3 changes: 3 additions & 0 deletions tests/rust/test_ok/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#[test]
fn it_works() {
}

0 comments on commit 076aecd

Please sign in to comment.