Skip to content

Commit 1bd3845

Browse files
author
Paolo Tranquilli
committed
Rust: expose more rust-analyzer config knobs
1 parent f7ca717 commit 1bd3845

File tree

4 files changed

+172
-87
lines changed

4 files changed

+172
-87
lines changed

rust/extractor/macros/src/lib.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,17 @@ pub fn extractor_cli_config(_attr: TokenStream, item: TokenStream) -> TokenStrea
1919
.fields
2020
.iter()
2121
.map(|f| {
22+
let ty_tip = get_type_tip(&f.ty);
2223
if f.ident.as_ref().is_some_and(|i| i != "inputs")
23-
&& get_type_tip(&f.ty).is_some_and(|i| i == "Vec")
24+
&& ty_tip.is_some_and(|i| i == "Vec")
2425
{
2526
quote! {
26-
#[serde(deserialize_with="deserialize_newline_or_comma_separated")]
27+
#[serde(deserialize_with="deserialize::deserialize_newline_or_comma_separated_vec")]
28+
#f
29+
}
30+
} else if ty_tip.is_some_and(|i| i == "FxHashMap" || i == "HashMap") {
31+
quote! {
32+
#[serde(deserialize_with="deserialize::deserialize_newline_or_comma_separated_map")]
2733
#f
2834
}
2935
} else {
@@ -60,7 +66,7 @@ pub fn extractor_cli_config(_attr: TokenStream, item: TokenStream) -> TokenStrea
6066
quote! {
6167
#f
6268
}
63-
} else if type_tip.is_some_and(|i| i == "Vec") {
69+
} else if type_tip.is_some_and(|i| i == "Vec" || i == "FxHashMap" || i == "HashMap") {
6470
quote! {
6571
#[arg(long)]
6672
#id: Option<String>

rust/extractor/src/config.rs

+66-34
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
mod deserialize_vec;
1+
mod deserialize;
22

33
use anyhow::Context;
44
use clap::Parser;
55
use codeql_extractor::trap;
6-
use deserialize_vec::deserialize_newline_or_comma_separated;
76
use figment::{
87
providers::{Env, Format, Serialized, Yaml},
98
value::Value,
@@ -14,7 +13,7 @@ use num_traits::Zero;
1413
use ra_ap_cfg::{CfgAtom, CfgDiff};
1514
use ra_ap_ide_db::FxHashMap;
1615
use ra_ap_intern::Symbol;
17-
use ra_ap_paths::{AbsPath, Utf8PathBuf};
16+
use ra_ap_paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
1817
use ra_ap_project_model::{CargoConfig, CargoFeatures, CfgOverrides, RustLibSource, Sysroot};
1918
use rust_extractor_macros::extractor_cli_config;
2019
use serde::{Deserialize, Serialize};
@@ -51,12 +50,20 @@ pub struct Config {
5150
pub cargo_target: Option<String>,
5251
pub cargo_features: Vec<String>,
5352
pub cargo_cfg_overrides: Vec<String>,
53+
pub cargo_extra_env: FxHashMap<String, String>,
54+
pub cargo_extra_args: Vec<String>,
55+
pub cargo_all_targets: bool,
5456
pub verbose: u8,
5557
pub compression: Compression,
5658
pub inputs: Vec<PathBuf>,
5759
pub qltest: bool,
5860
pub qltest_cargo_check: bool,
5961
pub qltest_dependencies: Vec<String>,
62+
pub sysroot: Option<PathBuf>,
63+
pub sysroot_src: Option<PathBuf>,
64+
pub rustc_src: Option<PathBuf>,
65+
pub build_script_command: Vec<String>,
66+
pub extra_includes: Vec<PathBuf>,
6067
}
6168

6269
impl Config {
@@ -87,41 +94,66 @@ impl Config {
8794
}
8895

8996
pub fn to_cargo_config(&self, dir: &AbsPath) -> CargoConfig {
90-
let sysroot = Sysroot::discover(dir, &FxHashMap::default());
91-
let sysroot_src = sysroot.src_root().map(ToOwned::to_owned);
92-
let sysroot = sysroot
93-
.root()
94-
.map(ToOwned::to_owned)
95-
.map(RustLibSource::Path);
96-
97-
let target_dir = self
98-
.cargo_target_dir
99-
.clone()
100-
.unwrap_or_else(|| self.scratch_dir.join("target"));
101-
let target_dir = Utf8PathBuf::from_path_buf(target_dir).ok();
97+
let path_buf_to_abs_path_buf = |path: &PathBuf| {
98+
let Ok(path) = Utf8PathBuf::from_path_buf(path.clone()) else {
99+
panic!("non utf8 input: {}", path.display())
100+
};
101+
dir.join(path)
102+
};
103+
let sysroot_input = self.sysroot.as_ref().map(path_buf_to_abs_path_buf);
104+
let sysroot_src_input = self.sysroot_src.as_ref().map(path_buf_to_abs_path_buf);
105+
let rustc_src_input = self.rustc_src.as_ref().map(path_buf_to_abs_path_buf);
102106

103-
let features = if self.cargo_features.is_empty() {
104-
Default::default()
105-
} else if self.cargo_features.contains(&"*".to_string()) {
106-
CargoFeatures::All
107-
} else {
108-
CargoFeatures::Selected {
109-
features: self.cargo_features.clone(),
110-
no_default_features: false,
107+
let sysroot = match (sysroot_input, sysroot_src_input) {
108+
(None, None) => Sysroot::discover(dir, &self.cargo_extra_env),
109+
(Some(sysroot), None) => Sysroot::discover_sysroot_src_dir(sysroot),
110+
(None, Some(sysroot_src)) => {
111+
Sysroot::discover_with_src_override(dir, &self.cargo_extra_env, sysroot_src)
111112
}
113+
(Some(sysroot), Some(sysroot_src)) => Sysroot::new(Some(sysroot), Some(sysroot_src)),
112114
};
113-
114-
let target = self.cargo_target.clone();
115-
116-
let cfg_overrides = to_cfg_overrides(&self.cargo_cfg_overrides);
117-
118115
CargoConfig {
119-
sysroot,
120-
sysroot_src,
121-
target_dir,
122-
features,
123-
target,
124-
cfg_overrides,
116+
all_targets: self.cargo_all_targets,
117+
sysroot_src: sysroot.src_root().map(ToOwned::to_owned),
118+
rustc_source: rustc_src_input
119+
.or_else(|| sysroot.discover_rustc_src().map(AbsPathBuf::from))
120+
.map(RustLibSource::Path),
121+
sysroot: sysroot
122+
.root()
123+
.map(ToOwned::to_owned)
124+
.map(RustLibSource::Path),
125+
126+
extra_env: self.cargo_extra_env.clone(),
127+
extra_args: self.cargo_extra_args.clone(),
128+
extra_includes: self
129+
.extra_includes
130+
.iter()
131+
.map(path_buf_to_abs_path_buf)
132+
.collect(),
133+
target_dir: Utf8PathBuf::from_path_buf(
134+
self.cargo_target_dir
135+
.clone()
136+
.unwrap_or_else(|| self.scratch_dir.join("target")),
137+
)
138+
.ok(),
139+
features: if self.cargo_features.is_empty() {
140+
Default::default()
141+
} else if self.cargo_features.contains(&"*".to_string()) {
142+
CargoFeatures::All
143+
} else {
144+
CargoFeatures::Selected {
145+
features: self.cargo_features.clone(),
146+
no_default_features: false,
147+
}
148+
},
149+
target: self.cargo_target.clone(),
150+
cfg_overrides: to_cfg_overrides(&self.cargo_cfg_overrides),
151+
wrap_rustc_in_build_scripts: false,
152+
run_build_script_command: if self.build_script_command.is_empty() {
153+
None
154+
} else {
155+
Some(self.build_script_command.clone())
156+
},
125157
..Default::default()
126158
}
127159
}
+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use serde::de::{Error, Unexpected, Visitor};
2+
use serde::Deserializer;
3+
use std::collections::HashMap;
4+
use std::fmt::Formatter;
5+
use std::hash::BuildHasher;
6+
use std::marker::PhantomData;
7+
8+
// phantom data is required to allow parametrizing on `T` without actual `T` data
9+
struct VectorVisitor<T: From<String>>(PhantomData<T>);
10+
struct MapVisitor<S: BuildHasher + Default>(PhantomData<S>);
11+
12+
impl<'de, T: From<String>> Visitor<'de> for VectorVisitor<T> {
13+
type Value = Vec<T>;
14+
15+
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
16+
formatter.write_str("either a sequence, or a comma or newline separated string")
17+
}
18+
19+
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Vec<T>, E> {
20+
Ok(value
21+
.split(['\n', ','])
22+
.map(|s| T::from(s.to_owned()))
23+
.collect())
24+
}
25+
26+
fn visit_seq<A>(self, mut seq: A) -> Result<Vec<T>, A::Error>
27+
where
28+
A: serde::de::SeqAccess<'de>,
29+
{
30+
let mut ret = Vec::new();
31+
while let Some(el) = seq.next_element::<String>()? {
32+
ret.push(T::from(el));
33+
}
34+
Ok(ret)
35+
}
36+
}
37+
38+
impl<'de, S: BuildHasher + Default> Visitor<'de> for MapVisitor<S> {
39+
type Value = HashMap<String, String, S>;
40+
41+
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
42+
formatter.write_str(
43+
"either a sequence, or a comma or newline separated string of key=value entries",
44+
)
45+
}
46+
47+
fn visit_str<E: serde::de::Error>(self, value: &str) -> Result<Self::Value, E> {
48+
value
49+
.split(['\n', ','])
50+
.map(|s| {
51+
s.split_once('=')
52+
.ok_or_else(|| E::custom(format!("key=value expected, found {s}")))
53+
.map(|(key, value)| (key.to_owned(), value.to_owned()))
54+
})
55+
.collect()
56+
}
57+
58+
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
59+
where
60+
A: serde::de::SeqAccess<'de>,
61+
{
62+
let mut ret = HashMap::with_hasher(Default::default());
63+
while let Some(el) = seq.next_element::<String>()? {
64+
let (key, value) = el
65+
.split_once('=')
66+
.ok_or_else(|| A::Error::invalid_value(Unexpected::Str(&el), &self))?;
67+
ret.insert(key.to_owned(), value.to_owned());
68+
}
69+
Ok(ret)
70+
}
71+
}
72+
73+
/// deserialize into a vector of `T` either of:
74+
/// * a sequence of elements serializable into `String`s, or
75+
/// * a single element serializable into `String`, then split on `,` and `\n`
76+
pub(crate) fn deserialize_newline_or_comma_separated_vec<
77+
'a,
78+
D: Deserializer<'a>,
79+
T: From<String>,
80+
>(
81+
deserializer: D,
82+
) -> Result<Vec<T>, D::Error> {
83+
deserializer.deserialize_seq(VectorVisitor(PhantomData))
84+
}
85+
86+
/// deserialize into a map of `String`s to `String`s either of:
87+
/// * a sequence of elements serializable into `String`s, or
88+
/// * a single element serializable into `String`, then split on `,` and `\n`
89+
pub(crate) fn deserialize_newline_or_comma_separated_map<
90+
'a,
91+
D: Deserializer<'a>,
92+
S: BuildHasher + Default,
93+
>(
94+
deserializer: D,
95+
) -> Result<HashMap<String, String, S>, D::Error> {
96+
deserializer.deserialize_map(MapVisitor(PhantomData))
97+
}

rust/extractor/src/config/deserialize_vec.rs

-50
This file was deleted.

0 commit comments

Comments
 (0)