Skip to content

feat: Add config to replace specific proc-macros with dummy expanders #11193

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/project_model/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ fn rooted_project_json(data: ProjectJsonData) -> ProjectJson {
}

fn to_crate_graph(project_workspace: ProjectWorkspace) -> CrateGraph {
project_workspace.to_crate_graph(&mut |_| Vec::new(), &mut {
project_workspace.to_crate_graph(&Default::default(), &mut |_, _| Vec::new(), &mut {
let mut counter = 0;
move |_path| {
counter += 1;
Expand Down
23 changes: 16 additions & 7 deletions crates/project_model/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,10 +387,14 @@ impl ProjectWorkspace {

pub fn to_crate_graph(
&self,
load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
dummy_replace: &FxHashMap<Box<str>, Box<[Box<str>]>>,
load_proc_macro: &mut dyn FnMut(&AbsPath, &[Box<str>]) -> Vec<ProcMacro>,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
) -> CrateGraph {
let _p = profile::span("ProjectWorkspace::to_crate_graph");
let load_proc_macro = &mut |crate_name: &_, path: &_| {
load_proc_macro(path, dummy_replace.get(crate_name).map(|it| &**it).unwrap_or_default())
};

let mut crate_graph = match self {
ProjectWorkspace::Json { project, sysroot, rustc_cfg } => project_json_to_crate_graph(
Expand Down Expand Up @@ -432,7 +436,7 @@ impl ProjectWorkspace {

fn project_json_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
project: &ProjectJson,
sysroot: &Option<Sysroot>,
Expand All @@ -452,7 +456,12 @@ fn project_json_to_crate_graph(
})
.map(|(crate_id, krate, file_id)| {
let env = krate.env.clone().into_iter().collect();
let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| load_proc_macro(&it));
let proc_macro = krate.proc_macro_dylib_path.clone().map(|it| {
load_proc_macro(
krate.display_name.as_ref().map(|it| it.canonical_name()).unwrap_or(""),
&it,
)
});

let target_cfgs = match krate.target.as_deref() {
Some(target) => {
Expand Down Expand Up @@ -513,7 +522,7 @@ fn project_json_to_crate_graph(
fn cargo_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
cargo: &CargoWorkspace,
build_scripts: &WorkspaceBuildScripts,
Expand Down Expand Up @@ -571,7 +580,7 @@ fn cargo_to_crate_graph(
&cargo[pkg],
build_scripts.outputs.get(pkg),
cfg_options,
load_proc_macro,
&mut |path| load_proc_macro(&cargo[tgt].name, path),
file_id,
&cargo[tgt].name,
);
Expand Down Expand Up @@ -702,7 +711,7 @@ fn handle_rustc_crates(
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
crate_graph: &mut CrateGraph,
cfg_options: &CfgOptions,
load_proc_macro: &mut dyn FnMut(&AbsPath) -> Vec<ProcMacro>,
load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> Vec<ProcMacro>,
pkg_to_lib_crate: &mut FxHashMap<la_arena::Idx<crate::PackageData>, CrateId>,
public_deps: &SysrootPublicDeps,
cargo: &CargoWorkspace,
Expand Down Expand Up @@ -738,7 +747,7 @@ fn handle_rustc_crates(
&rustc_workspace[pkg],
None,
cfg_options,
load_proc_macro,
&mut |path| load_proc_macro(&rustc_workspace[tgt].name, path),
file_id,
&rustc_workspace[tgt].name,
);
Expand Down
3 changes: 2 additions & 1 deletion crates/rust-analyzer/src/cli/load_cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ pub fn load_workspace(
};

let crate_graph = ws.to_crate_graph(
&mut |path: &AbsPath| load_proc_macro(proc_macro_client.as_ref(), path),
&Default::default(),
&mut |path: &AbsPath, _| load_proc_macro(proc_macro_client.as_ref(), path, &[]),
&mut |path: &AbsPath| {
let contents = loader.load_sync(path);
let path = vfs::VfsPath::from(path.to_path_buf());
Expand Down
18 changes: 17 additions & 1 deletion crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ config_data! {
/// Internal config, path to proc-macro server executable (typically,
/// this is rust-analyzer itself, but we override this in tests).
procMacro_server: Option<PathBuf> = "null",
/// These proc-macros will be ignored when trying to expand them.
///
/// This config takes a map of crate names with the exported proc-macro names to ignore as values.
procMacro_ignored: FxHashMap<Box<str>, Box<[Box<str>]>> = "{}",

/// Command to be executed instead of 'cargo' for runnables.
runnables_overrideCargo: Option<String> = "null",
Expand Down Expand Up @@ -716,6 +720,9 @@ impl Config {
};
Some((path, vec!["proc-macro".into()]))
}
pub fn dummy_replacements(&self) -> &FxHashMap<Box<str>, Box<[Box<str>]>> {
&self.data.procMacro_ignored
}
pub fn expand_proc_attr_macros(&self) -> bool {
self.data.experimental_procAttrMacros
}
Expand Down Expand Up @@ -1163,7 +1170,13 @@ fn get_field<T: DeserializeOwned>(
.find_map(move |field| {
let mut pointer = field.replace('_', "/");
pointer.insert(0, '/');
json.pointer_mut(&pointer).and_then(|it| serde_json::from_value(it.take()).ok())
json.pointer_mut(&pointer).and_then(|it| match serde_json::from_value(it.take()) {
Ok(it) => Some(it),
Err(e) => {
tracing::warn!("Failed to deserialize config field at {}: {:?}", pointer, e);
None
}
})
})
.unwrap_or(default)
}
Expand Down Expand Up @@ -1224,6 +1237,9 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
"items": { "type": "string" },
"uniqueItems": true,
},
"FxHashMap<Box<str>, Box<[Box<str>]>>" => set! {
"type": "object",
},
"FxHashMap<String, SnippetDef>" => set! {
"type": "object",
},
Expand Down
51 changes: 43 additions & 8 deletions crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use ide_db::base_db::{
};
use proc_macro_api::{MacroDylib, ProcMacroServer};
use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
use syntax::SmolStr;
use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind};

use crate::{
Expand Down Expand Up @@ -306,8 +307,9 @@ impl GlobalState {
// Create crate graph from all the workspaces
let crate_graph = {
let proc_macro_client = self.proc_macro_client.as_ref();
let mut load_proc_macro =
move |path: &AbsPath| load_proc_macro(proc_macro_client, path);
let mut load_proc_macro = move |path: &AbsPath, dummy_replace: &_| {
load_proc_macro(proc_macro_client, path, dummy_replace)
};

let vfs = &mut self.vfs.write().0;
let loader = &mut self.loader;
Expand All @@ -328,7 +330,11 @@ impl GlobalState {

let mut crate_graph = CrateGraph::default();
for ws in self.workspaces.iter() {
crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load));
crate_graph.extend(ws.to_crate_graph(
self.config.dummy_replacements(),
&mut load_proc_macro,
&mut load,
));
}
crate_graph
};
Expand Down Expand Up @@ -505,7 +511,13 @@ impl SourceRootConfig {
}
}

pub(crate) fn load_proc_macro(client: Option<&ProcMacroServer>, path: &AbsPath) -> Vec<ProcMacro> {
/// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace`
/// with an identity dummy expander.
pub(crate) fn load_proc_macro(
client: Option<&ProcMacroServer>,
path: &AbsPath,
dummy_replace: &[Box<str>],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if perhaps we should implement this on the higher level and treat such macros as inert attributes?

ah, I see you’ve considered that. I wonder if we can treat this as a custom tool attribute (#[rustfmt and such)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tool attribute would be nice in theory, but since those are unstable and therefor require a feature flag I am doubtful that people would even bother using it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, we presumably have some infrastruture internally to track tool/intert attributes and say things like "inline does nothing". We can use that same infrastructure to implement this feature. Ie, this is about impl detail rather thatn about API.

Basically, make #[tokio::main] behave like #[inline] rather than #[identity].

) -> Vec<ProcMacro> {
let dylib = match MacroDylib::new(path.to_path_buf()) {
Ok(it) => it,
Err(err) => {
Expand All @@ -532,17 +544,25 @@ pub(crate) fn load_proc_macro(client: Option<&ProcMacroServer>, path: &AbsPath)
Vec::new()
}
})
.map(expander_to_proc_macro)
.map(|expander| expander_to_proc_macro(expander, dummy_replace))
.collect();

fn expander_to_proc_macro(expander: proc_macro_api::ProcMacro) -> ProcMacro {
let name = expander.name().into();
fn expander_to_proc_macro(
expander: proc_macro_api::ProcMacro,
dummy_replace: &[Box<str>],
) -> ProcMacro {
let name = SmolStr::from(expander.name());
let kind = match expander.kind() {
proc_macro_api::ProcMacroKind::CustomDerive => ProcMacroKind::CustomDerive,
proc_macro_api::ProcMacroKind::FuncLike => ProcMacroKind::FuncLike,
proc_macro_api::ProcMacroKind::Attr => ProcMacroKind::Attr,
};
let expander = Arc::new(Expander(expander));
let expander: Arc<dyn ProcMacroExpander> =
if dummy_replace.iter().any(|replace| &**replace == name) {
Arc::new(DummyExpander)
} else {
Arc::new(Expander(expander))
};
ProcMacro { name, kind, expander }
}

Expand All @@ -564,6 +584,21 @@ pub(crate) fn load_proc_macro(client: Option<&ProcMacroServer>, path: &AbsPath)
}
}
}

/// Dummy identity expander, used for proc-macros that are deliberately ignored by the user.
#[derive(Debug)]
struct DummyExpander;

impl ProcMacroExpander for DummyExpander {
fn expand(
&self,
subtree: &tt::Subtree,
_: Option<&tt::Subtree>,
_: &Env,
) -> Result<tt::Subtree, ProcMacroExpansionError> {
Ok(subtree.clone())
}
}
}

pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
Expand Down
7 changes: 7 additions & 0 deletions docs/user/generated_config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,13 @@ Enable support for procedural macros, implies `#rust-analyzer.cargo.runBuildScri
Internal config, path to proc-macro server executable (typically,
this is rust-analyzer itself, but we override this in tests).
--
[[rust-analyzer.procMacro.ignored]]rust-analyzer.procMacro.ignored (default: `{}`)::
+
--
These proc-macros will be ignored when trying to expand them.

This config takes a map of crate names with the exported proc-macro names to ignore as values.
--
[[rust-analyzer.runnables.overrideCargo]]rust-analyzer.runnables.overrideCargo (default: `null`)::
+
--
Expand Down
5 changes: 5 additions & 0 deletions editors/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -880,6 +880,11 @@
"string"
]
},
"rust-analyzer.procMacro.ignored": {
"markdownDescription": "These proc-macros will be ignored when trying to expand them.\n\nThis config takes a map of crate names with the exported proc-macro names to ignore as values.",
"default": {},
"type": "object"
},
"rust-analyzer.runnables.overrideCargo": {
"markdownDescription": "Command to be executed instead of 'cargo' for runnables.",
"default": null,
Expand Down