Skip to content

Commit

Permalink
feat(help): add suggestions for replacing use std:: with `use core:…
Browse files Browse the repository at this point in the history
…:` where possible

REFERENCE #10
  • Loading branch information
hobofan committed Jan 17, 2019
1 parent 3e82ace commit 5c2e11c
Show file tree
Hide file tree
Showing 7 changed files with 258 additions and 146 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ syn = { version = "=0.15.18", default-features = false, features = ["full", "ext
quote = { version = "0.6.10", default-features = false }
clap = "2.32.0"
proc-macro2 = { version = "0.4.24", default-features = false }
glob = "0.2.11"

[dev-dependencies]
assert_cmd = "0.10.2"
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ However it is currently a very cumbersome process to find out if and why (not) a

```bash
cargo install cargo-nono
# For warnings with more informative messages install like this
RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install cargo-nono
```

## Demo
Expand All @@ -35,7 +37,6 @@ The `cargo nono check` subcommand also understands the `--no-default-features` a
### Planned features

- Warn of `[build-dependencies]` features bleeding over: [cargo#5730](https://github.com/rust-lang/cargo/issues/5730)
- Check for `use std::` statements

## License

Expand Down
150 changes: 5 additions & 145 deletions src/check.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
use proc_macro2;
use proc_macro2::TokenTree;
use quote::quote;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use syn::spanned::Spanned;

#[cfg(feature = "proc_macro_spans")]
use std::io::BufRead;

use check_source::*;
use ext::*;

#[derive(Debug, PartialEq, Eq)]
Expand All @@ -22,13 +15,13 @@ pub enum CrateSupport {
}

#[derive(Debug)]
struct ConditionalAttribute {
pub struct ConditionalAttribute {
condition: proc_macro2::TokenStream,
attribute: syn::Ident,
pub attribute: syn::Ident,
}

impl ConditionalAttribute {
fn from_attribute(attr: &syn::Attribute) -> Option<Self> {
pub fn from_attribute(attr: &syn::Attribute) -> Option<Self> {
let cfg_attr_path: syn::Path = syn::parse_quote!(cfg_attr);
if attr.path == cfg_attr_path {
if let Some(ref first_group_ts) = attr.clone().tts.into_iter().next() {
Expand Down Expand Up @@ -56,7 +49,7 @@ impl ConditionalAttribute {
return None;
}

fn required_feature(&self) -> Option<proc_macro2::Literal> {
pub fn required_feature(&self) -> Option<proc_macro2::Literal> {
let not_ident: syn::Ident = syn::parse_quote!(not);
let feature_ident: syn::Ident = syn::parse_quote!(feature);
let equal_punct: proc_macro2::Punct = syn::parse_quote!(=);
Expand Down Expand Up @@ -89,139 +82,6 @@ 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(UseStdStmt),
}

#[derive(Debug)]
pub struct UseStdStmt {
src_path: PathBuf,
span: proc_macro2::Span,
}

impl PartialEq for UseStdStmt {
fn eq(&self, other: &UseStdStmt) -> bool {
self.src_path == other.src_path
}
}
impl Eq for UseStdStmt {}

#[cfg(feature = "proc_macro_spans")]
impl fmt::Display for UseStdStmt {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let file = File::open(&self.src_path).unwrap();
let file = std::io::BufReader::new(file);
let line = file
.lines()
.skip(self.span.start().line - 1)
.next()
.unwrap()
.unwrap();

writeln!(
f,
" --> {src}:{line}:{column}",
src = self
.src_path
.strip_prefix(std::env::current_dir().unwrap())
.unwrap()
.display(),
line = self.span.start().line,
column = self.span.start().column
)?;
writeln!(f, " |")?;

writeln!(
f,
"{line_num:<4}|{line}",
line_num = self.span.start().line,
line = line
)?;
writeln!(f, " |")
}
}

#[cfg(not(feature = "proc_macro_spans"))]
impl fmt::Display for UseStdStmt {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(
f,
" --> {src}",
src = self
.src_path
.strip_prefix(std::env::current_dir().unwrap())
.unwrap()
.display(),
)
}
}

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

let mut src = String::new();
file.read_to_string(&mut src).expect("Unable to read file");

let syntax = syn::parse_file(&src).expect("Unable to parse file");

for attr in &syntax.attrs {
if let Some(conditional_attr) = ConditionalAttribute::from_attribute(&attr) {
let no_std_ident: syn::Ident = syn::parse_quote!(no_std);
if conditional_attr.attribute == no_std_ident {
if let Some(required_feature) = conditional_attr.required_feature() {
let mut feature_name = required_feature.to_string();
feature_name = feature_name[1..feature_name.len() - 1].to_owned();
return CrateSupport::OnlyWithoutFeature(feature_name);
}
}
}
}

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 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 {
let stmt = UseStdStmt {
src_path: src_path.clone(),
span: use_statement.tree.span(),
};
offenses.push(SourceOffense::UseStdStatement(stmt));
}
}
_ => unimplemented!(),
}
}

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 {
offenses.push(SourceOffense::MissingNoStdAttribute);
}

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

pub struct CheckResult {
pub package_name: String,
pub support: CrateSupport,
Expand Down
Loading

0 comments on commit 5c2e11c

Please sign in to comment.