Skip to content

Commit

Permalink
Merge pull request #12 from hobofan/feat/check_use_std
Browse files Browse the repository at this point in the history
feat(check): add simple check for `use std::` statements
  • Loading branch information
hobofan authored Jan 17, 2019
2 parents c44e237 + 111bdb1 commit c09cf98
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 13 deletions.
50 changes: 43 additions & 7 deletions src/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ use ext::*;

#[derive(Debug, PartialEq, Eq)]
pub enum CrateSupport {
AlwaysNoStd,
OnlyWithoutFeature(String),
/// proc macros are not actually linked, so they don't hinder no_std support
ProcMacro,
NotDetected,
SourceOffenses(Vec<SourceOffense>),
NoOffenseDetected,
}

#[derive(Debug)]
Expand Down Expand Up @@ -84,6 +84,15 @@ impl ConditionalAttribute {
}
}

#[derive(Debug, PartialEq, Eq)]
pub enum SourceOffense {
/// Source code is missing a `#![no_std]` attribute.
/// Only valid for entry point file (main.rs / lib.rs).
MissingNoStdAttribute,
/// Source code contains an explicit `use std::` statement.
UseStdStatement,
}

pub fn get_crate_support_from_source(src_path: &PathBuf) -> CrateSupport {
let mut file = File::open(&src_path).expect("Unable to open file");

Expand All @@ -105,13 +114,40 @@ pub fn get_crate_support_from_source(src_path: &PathBuf) -> CrateSupport {
}
}

let mut offenses = vec![];

let use_statements: Vec<_> = syntax.items.iter().filter_map(|item| match item {
syn::Item::Use(item) => Some(item),
_ => None,
}).collect();

let mut has_use_std = false;
let std_ident: syn::Ident = syn::parse_quote!(std);
for use_statement in &use_statements {
match use_statement.tree {
syn::UseTree::Path(ref first_path) => {
let first_ident = &first_path.ident;
if first_ident == &std_ident {
has_use_std = true;
}
},
_ => unimplemented!(),
}
}
if has_use_std {
offenses.push(SourceOffense::UseStdStatement);
}

let always_no_std: syn::Attribute = syn::parse_quote!(#![no_std]);
let contains_always_no_std = syntax.attrs.contains(&always_no_std);
if contains_always_no_std {
return CrateSupport::AlwaysNoStd;
if !contains_always_no_std {
offenses.push(SourceOffense::MissingNoStdAttribute);
}

CrateSupport::NotDetected
match offenses.is_empty() {
true => CrateSupport::NoOffenseDetected,
false => CrateSupport::SourceOffenses(offenses),
}
}

pub struct CheckResult {
Expand All @@ -123,10 +159,10 @@ pub struct CheckResult {
impl CheckResult {
pub fn no_std_itself(&self) -> bool {
match self.support {
CrateSupport::AlwaysNoStd => true,
CrateSupport::ProcMacro => true,
CrateSupport::OnlyWithoutFeature(ref feature) => !self.is_feature_active(feature),
CrateSupport::NotDetected => false,
CrateSupport::NoOffenseDetected => true,
CrateSupport::SourceOffenses(_) => false,
}
}

Expand Down
21 changes: 15 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ fn check_and_print_package(
let _resolved_dependency_features =
package.all_dependency_features(&metadata_full, &active_features);

let mut support = CrateSupport::NotDetected;
let mut support = CrateSupport::NoOffenseDetected;
if package.is_proc_macro() {
support = CrateSupport::ProcMacro;
}
if support == CrateSupport::NotDetected {
if support == CrateSupport::NoOffenseDetected {
match is_main_pkg {
false => {
let srcs: Vec<_> = package
Expand All @@ -62,7 +62,7 @@ fn check_and_print_package(
.into_iter()
.map(|src_path| get_crate_support_from_source(&src_path))
.next()
.unwrap_or(CrateSupport::NotDetected);
.unwrap_or(CrateSupport::NoOffenseDetected);
}
true => {
let srcs: Vec<_> = package
Expand All @@ -75,7 +75,7 @@ fn check_and_print_package(
.into_iter()
.map(|src_path| get_crate_support_from_source(&src_path))
.next()
.unwrap_or(CrateSupport::NotDetected);
.unwrap_or(CrateSupport::NoOffenseDetected);
}
}
}
Expand Down Expand Up @@ -106,8 +106,17 @@ fn check_and_print_package(
let feat = check.find_active_feature_by_name(&feature).unwrap();
feat.print(&metadata, 2);
}
if check.support == CrateSupport::NotDetected {
println!(" - Did not find a #![no_std] attribute or a simple conditional attribute like #[cfg_attr(not(feature = \"std\"), no_std)] in the crate source. Crate most likely doesn't support no_std without changes.");
if let CrateSupport::SourceOffenses(ref offenses) = check.support {
for offense in offenses {
match offense {
SourceOffense::MissingNoStdAttribute => {
println!(" - Did not find a #![no_std] attribute or a simple conditional attribute like #[cfg_attr(not(feature = \"std\"), no_std)] in the crate source. Crate most likely doesn't support no_std without changes.");
},
SourceOffense::UseStdStatement => {
println!(" - Source code contains an explicit `use std::` statement.");
}
}
}
}

package_did_fail
Expand Down
1 change: 1 addition & 0 deletions tests/detect_explicit_use_std/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target
4 changes: 4 additions & 0 deletions tests/detect_explicit_use_std/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions tests/detect_explicit_use_std/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "detect_explicit_use_std"
version = "0.1.0"
authors = ["Maximilian Goisser <goisser94@gmail.com>"]
edition = "2018"

[dependencies]
7 changes: 7 additions & 0 deletions tests/detect_explicit_use_std/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#![no_std]

use std::ops::Add;

fn main() {
println!("Hello, world!");
}
30 changes: 30 additions & 0 deletions tests/detect_explict_use_std.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
extern crate assert_cmd;

use assert_cmd::prelude::*;
use std::process::Command;

#[test]
fn it_fails_with_exit_code_1() {
Command::main_binary()
.unwrap()
.arg("check")
.current_dir("./tests/detect_explicit_use_std")
.assert()
.code(1);
}

#[test]
fn it_prints_cause() {
let output = Command::main_binary()
.unwrap()
.arg("check")
.current_dir("./tests/detect_explicit_use_std")
.output()
.unwrap()
.stdout;
let output = String::from_utf8(output).unwrap();

let expected_cause =
"Source code contains an explicit `use std::` statement";
assert!(output.contains(expected_cause));
}

0 comments on commit c09cf98

Please sign in to comment.