Skip to content

Commit

Permalink
Format inner attributes (#4207)
Browse files Browse the repository at this point in the history
  • Loading branch information
topecongiro authored May 26, 2020
2 parents 3eb864e + 6b1063d commit 2b8d4fd
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 131 deletions.
25 changes: 6 additions & 19 deletions rustfmt-core/rustfmt-lib/src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ pub(crate) use syntux::session::ParseSess;

use self::newline_style::apply_newline_style;
use crate::config::{Config, FileName};
use crate::formatting::modules::Module;
use crate::formatting::{
comment::{CharClasses, FullCodeCharKind},
report::NonFormattedRange,
Expand Down Expand Up @@ -127,14 +128,12 @@ fn format_project(
should_emit_verbose(input_is_stdin, operation_setting.verbosity, || {
println!("Formatting {}", path)
});
let is_root = path == main_file;
format_file(
&parse_session,
config,
&krate,
path,
&module,
is_root,
&format_report,
original_snippet.clone(),
);
Expand All @@ -157,29 +156,17 @@ fn format_file(
config: &Config,
krate: &ast::Crate,
path: FileName,
module: &ast::Mod,
is_root: bool,
module: &Module<'_>,
report: &FormatReport,
original_snippet: Option<String>,
) {
let snippet_provider = parse_session.snippet_provider(module.inner);
let snippet_provider = parse_session.snippet_provider(module.as_ref().inner);
let mut visitor =
FmtVisitor::from_parse_sess(&parse_session, config, &snippet_provider, report.clone());
visitor.skip_context.update_with_attrs(&krate.attrs);

// Format inner attributes if available.
if !krate.attrs.is_empty() && is_root {
visitor.skip_empty_lines(snippet_provider.end_pos());
if visitor.visit_attrs(&krate.attrs, ast::AttrStyle::Inner) {
visitor.push_rewrite(module.inner, None);
} else {
visitor.format_separate_mod(module, snippet_provider.end_pos());
}
} else {
visitor.last_pos = snippet_provider.start_pos();
visitor.skip_empty_lines(snippet_provider.end_pos());
visitor.format_separate_mod(module, snippet_provider.end_pos());
};
visitor.last_pos = snippet_provider.start_pos();
visitor.skip_empty_lines(snippet_provider.end_pos());
visitor.format_separate_mod(module, snippet_provider.end_pos());

debug_assert_eq!(
visitor.line_number,
Expand Down
107 changes: 79 additions & 28 deletions rustfmt-core/rustfmt-lib/src/formatting/modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::collections::BTreeMap;
use std::path::{Path, PathBuf};

use rustc_ast::ast;
use rustc_ast::attr::HasAttrs;
use rustc_ast::visit::Visitor;
use rustc_span::symbol::{self, sym, Symbol};
use thiserror::Error;
Expand All @@ -18,12 +19,48 @@ use crate::formatting::{

mod visitor;

type FileModMap<'ast> = BTreeMap<FileName, Cow<'ast, ast::Mod>>;
type FileModMap<'ast> = BTreeMap<FileName, Module<'ast>>;

lazy_static! {
static ref CFG_IF: Symbol = Symbol::intern("cfg_if");
}

/// Represents module with its inner attributes.
#[derive(Debug, Clone)]
pub(crate) struct Module<'a> {
ast_mod: Cow<'a, ast::Mod>,
inner_attr: Vec<ast::Attribute>,
}

impl<'a> Module<'a> {
pub(crate) fn new(ast_mod: Cow<'a, ast::Mod>, attrs: &[ast::Attribute]) -> Self {
let inner_attr = attrs
.iter()
.filter(|attr| attr.style == ast::AttrStyle::Inner)
.cloned()
.collect();
Module {
ast_mod,
inner_attr,
}
}
}

impl<'a> HasAttrs for Module<'a> {
fn attrs(&self) -> &[ast::Attribute] {
&self.inner_attr
}
fn visit_attrs(&mut self, f: impl FnOnce(&mut Vec<ast::Attribute>)) {
f(&mut self.inner_attr)
}
}

impl<'a> AsRef<ast::Mod> for Module<'a> {
fn as_ref(&self) -> &ast::Mod {
&self.ast_mod
}
}

/// Maps each module to the corresponding file.
pub(crate) struct ModResolver<'ast, 'sess> {
parse_sess: &'sess ParseSess,
Expand Down Expand Up @@ -53,9 +90,9 @@ pub(crate) enum ModuleResolutionErrorKind {
#[derive(Clone)]
enum SubModKind<'a, 'ast> {
/// `mod foo;`
External(PathBuf, DirectoryOwnership, Cow<'ast, ast::Mod>),
External(PathBuf, DirectoryOwnership, Module<'ast>),
/// `mod foo;` with multiple sources.
MultiExternal(Vec<(PathBuf, DirectoryOwnership, Cow<'ast, ast::Mod>)>),
MultiExternal(Vec<(PathBuf, DirectoryOwnership, Module<'ast>)>),
/// `mod foo {}`
Internal(&'a ast::Item),
}
Expand Down Expand Up @@ -94,8 +131,10 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
self.visit_mod_from_ast(&krate.module)?;
}

self.file_map
.insert(root_filename, Cow::Borrowed(&krate.module));
self.file_map.insert(
root_filename,
Module::new(Cow::Borrowed(&krate.module), &krate.attrs),
);
Ok(self.file_map)
}

Expand All @@ -105,7 +144,10 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
visitor.visit_item(&item);
for module_item in visitor.mods() {
if let ast::ItemKind::Mod(ref sub_mod) = module_item.item.kind {
self.visit_sub_mod(&module_item.item, Cow::Owned(sub_mod.clone()))?;
self.visit_sub_mod(
&module_item.item,
Module::new(Cow::Owned(sub_mod.clone()), &module_item.item.attrs),
)?;
}
}
Ok(())
Expand All @@ -120,7 +162,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
}

if let ast::ItemKind::Mod(ref sub_mod) = item.kind {
self.visit_sub_mod(&item, Cow::Owned(sub_mod.clone()))?;
self.visit_sub_mod(&item, Module::new(Cow::Owned(sub_mod.clone()), &item.attrs))?;
}
}
Ok(())
Expand All @@ -134,7 +176,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
}

if let ast::ItemKind::Mod(ref sub_mod) = item.kind {
self.visit_sub_mod(item, Cow::Borrowed(sub_mod))?;
self.visit_sub_mod(item, Module::new(Cow::Borrowed(sub_mod), &item.attrs))?;
}
}
Ok(())
Expand All @@ -143,12 +185,12 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
fn visit_sub_mod(
&mut self,
item: &'c ast::Item,
sub_mod: Cow<'ast, ast::Mod>,
sub_mod: Module<'ast>,
) -> Result<(), ModuleResolutionError> {
let old_directory = self.directory.clone();
let sub_mod_kind = self.peek_sub_mod(item, &sub_mod)?;
if let Some(sub_mod_kind) = sub_mod_kind {
self.insert_sub_mod(sub_mod_kind.clone(), sub_mod.clone())?;
self.insert_sub_mod(sub_mod_kind.clone())?;
self.visit_sub_mod_inner(sub_mod, sub_mod_kind)?;
}
self.directory = old_directory;
Expand All @@ -159,7 +201,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
fn peek_sub_mod(
&self,
item: &'c ast::Item,
sub_mod: &Cow<'ast, ast::Mod>,
sub_mod: &Module<'ast>,
) -> Result<Option<SubModKind<'c, 'ast>>, ModuleResolutionError> {
if contains_skip(&item.attrs) {
return Ok(None);
Expand All @@ -178,7 +220,6 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
fn insert_sub_mod(
&mut self,
sub_mod_kind: SubModKind<'c, 'ast>,
_sub_mod: Cow<'ast, ast::Mod>,
) -> Result<(), ModuleResolutionError> {
match sub_mod_kind {
SubModKind::External(mod_path, _, sub_mod) => {
Expand All @@ -200,7 +241,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {

fn visit_sub_mod_inner(
&mut self,
sub_mod: Cow<'ast, ast::Mod>,
sub_mod: Module<'ast>,
sub_mod_kind: SubModKind<'c, 'ast>,
) -> Result<(), ModuleResolutionError> {
match sub_mod_kind {
Expand Down Expand Up @@ -230,13 +271,13 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {

fn visit_sub_mod_after_directory_update(
&mut self,
sub_mod: Cow<'ast, ast::Mod>,
sub_mod: Module<'ast>,
directory: Option<Directory>,
) -> Result<(), ModuleResolutionError> {
if let Some(directory) = directory {
self.directory = directory;
}
match sub_mod {
match sub_mod.ast_mod {
Cow::Borrowed(sub_mod) => self.visit_mod_from_ast(sub_mod),
Cow::Owned(sub_mod) => self.visit_mod_outside_ast(sub_mod),
}
Expand All @@ -247,7 +288,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
&self,
mod_name: symbol::Ident,
attrs: &[ast::Attribute],
sub_mod: &Cow<'ast, ast::Mod>,
sub_mod: &Module<'ast>,
) -> Result<Option<SubModKind<'c, 'ast>>, ModuleResolutionError> {
let relative = match self.directory.ownership {
DirectoryOwnership::Owned { relative } => relative,
Expand All @@ -257,11 +298,12 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
if self.parse_sess.is_file_parsed(&path) {
return Ok(None);
}
return match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.inner) {
return match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.ast_mod.inner)
{
Ok(m) => Ok(Some(SubModKind::External(
path,
DirectoryOwnership::Owned { relative: None },
Cow::Owned(m),
Module::new(Cow::Owned(m.0), &m.1),
))),
Err(ParserError::ParseError) => Err(ModuleResolutionError {
module: mod_name.to_string(),
Expand Down Expand Up @@ -299,12 +341,18 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
return Ok(Some(SubModKind::MultiExternal(mods_outside_ast)));
}
}
match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.inner) {
Ok(m) if outside_mods_empty => {
Ok(Some(SubModKind::External(path, ownership, Cow::Owned(m))))
}
match Parser::parse_file_as_module(self.parse_sess, &path, sub_mod.ast_mod.inner) {
Ok(m) if outside_mods_empty => Ok(Some(SubModKind::External(
path,
ownership,
Module::new(Cow::Owned(m.0), &m.1),
))),
Ok(m) => {
mods_outside_ast.push((path.clone(), ownership, Cow::Owned(m)));
mods_outside_ast.push((
path.clone(),
ownership,
Module::new(Cow::Owned(m.0), &m.1),
));
if should_insert {
mods_outside_ast.push((path, ownership, sub_mod.clone()));
}
Expand Down Expand Up @@ -366,8 +414,8 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
fn find_mods_outside_of_ast(
&self,
attrs: &[ast::Attribute],
sub_mod: &Cow<'ast, ast::Mod>,
) -> Vec<(PathBuf, DirectoryOwnership, Cow<'ast, ast::Mod>)> {
sub_mod: &Module<'ast>,
) -> Vec<(PathBuf, DirectoryOwnership, Module<'ast>)> {
// Filter nested path, like `#[cfg_attr(feature = "foo", path = "bar.rs")]`.
let mut path_visitor = visitor::PathVisitor::default();
for attr in attrs.iter() {
Expand All @@ -394,16 +442,19 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
continue;
}

let m = match Parser::parse_file_as_module(self.parse_sess, &actual_path, sub_mod.inner)
{
let m = match Parser::parse_file_as_module(
self.parse_sess,
&actual_path,
sub_mod.ast_mod.inner,
) {
Ok(m) => m,
Err(..) => continue,
};

result.push((
actual_path,
DirectoryOwnership::Owned { relative: None },
Cow::Owned(m),
Module::new(Cow::Owned(m.0), &m.1),
))
}
result
Expand Down
Loading

0 comments on commit 2b8d4fd

Please sign in to comment.