Skip to content

Commit 15ad78c

Browse files
bors[bot]matklad
andauthored
Merge #5507
5507: Require quotes around key-value cfg flags in rust-project.json r=matklad a=matklad This matches rustc command-line flags, as well as the build.rs format. bors r+ 🤖 Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2 parents 243b997 + 7c07432 commit 15ad78c

File tree

6 files changed

+107
-71
lines changed

6 files changed

+107
-71
lines changed

crates/ra_project_model/src/cargo_workspace.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ use ra_arena::{Arena, Idx};
1414
use ra_db::Edition;
1515
use rustc_hash::FxHashMap;
1616

17+
use crate::cfg_flag::CfgFlag;
18+
1719
/// `CargoWorkspace` represents the logical structure of, well, a Cargo
1820
/// workspace. It pretty closely mirrors `cargo metadata` output.
1921
///
@@ -78,7 +80,7 @@ pub struct PackageData {
7880
pub dependencies: Vec<PackageDependency>,
7981
pub edition: Edition,
8082
pub features: Vec<String>,
81-
pub cfgs: Vec<String>,
83+
pub cfgs: Vec<CfgFlag>,
8284
pub out_dir: Option<AbsPathBuf>,
8385
pub proc_macro_dylib_path: Option<AbsPathBuf>,
8486
}
@@ -276,7 +278,7 @@ impl CargoWorkspace {
276278
pub struct ExternResources {
277279
out_dirs: FxHashMap<PackageId, AbsPathBuf>,
278280
proc_dylib_paths: FxHashMap<PackageId, AbsPathBuf>,
279-
cfgs: FxHashMap<PackageId, Vec<String>>,
281+
cfgs: FxHashMap<PackageId, Vec<CfgFlag>>,
280282
}
281283

282284
pub fn load_extern_resources(
@@ -303,6 +305,18 @@ pub fn load_extern_resources(
303305
if let Ok(message) = message {
304306
match message {
305307
Message::BuildScriptExecuted(BuildScript { package_id, out_dir, cfgs, .. }) => {
308+
let cfgs = {
309+
let mut acc = Vec::new();
310+
for cfg in cfgs {
311+
match cfg.parse::<CfgFlag>() {
312+
Ok(it) => acc.push(it),
313+
Err(err) => {
314+
anyhow::bail!("invalid cfg from cargo-metadata: {}", err)
315+
}
316+
};
317+
}
318+
acc
319+
};
306320
// cargo_metadata crate returns default (empty) path for
307321
// older cargos, which is not absolute, so work around that.
308322
if out_dir != PathBuf::default() {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//! Parsing of CfgFlags as command line arguments, as in
2+
//!
3+
//! rustc main.rs --cfg foo --cfg 'feature="bar"'
4+
use std::str::FromStr;
5+
6+
use ra_cfg::CfgOptions;
7+
use stdx::split_delim;
8+
9+
#[derive(Clone, Eq, PartialEq, Debug)]
10+
pub enum CfgFlag {
11+
Atom(String),
12+
KeyValue { key: String, value: String },
13+
}
14+
15+
impl FromStr for CfgFlag {
16+
type Err = String;
17+
fn from_str(s: &str) -> Result<Self, Self::Err> {
18+
let res = match split_delim(s, '=') {
19+
Some((key, value)) => {
20+
if !(value.starts_with('"') && value.ends_with('"')) {
21+
return Err(format!("Invalid cfg ({:?}), value should be in quotes", s));
22+
}
23+
let key = key.to_string();
24+
let value = value[1..value.len() - 1].to_string();
25+
CfgFlag::KeyValue { key, value }
26+
}
27+
None => CfgFlag::Atom(s.into()),
28+
};
29+
Ok(res)
30+
}
31+
}
32+
33+
impl<'de> serde::Deserialize<'de> for CfgFlag {
34+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
35+
where
36+
D: serde::Deserializer<'de>,
37+
{
38+
String::deserialize(deserializer)?.parse().map_err(serde::de::Error::custom)
39+
}
40+
}
41+
42+
impl Extend<CfgFlag> for CfgOptions {
43+
fn extend<T: IntoIterator<Item = CfgFlag>>(&mut self, iter: T) {
44+
for cfg_flag in iter {
45+
match cfg_flag {
46+
CfgFlag::Atom(it) => self.insert_atom(it.into()),
47+
CfgFlag::KeyValue { key, value } => self.insert_key_value(key.into(), value.into()),
48+
}
49+
}
50+
}
51+
}

crates/ra_project_model/src/lib.rs

Lines changed: 30 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,28 @@
33
mod cargo_workspace;
44
mod project_json;
55
mod sysroot;
6+
mod cfg_flag;
67

78
use std::{
89
fs::{self, read_dir, ReadDir},
910
io,
10-
process::{Command, Output},
11+
process::Command,
1112
};
1213

1314
use anyhow::{bail, Context, Result};
1415
use paths::{AbsPath, AbsPathBuf};
1516
use ra_cfg::CfgOptions;
1617
use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
1718
use rustc_hash::{FxHashMap, FxHashSet};
18-
use stdx::split_delim;
19+
20+
use crate::cfg_flag::CfgFlag;
1921

2022
pub use crate::{
2123
cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind},
2224
project_json::{ProjectJson, ProjectJsonData},
2325
sysroot::Sysroot,
2426
};
27+
2528
pub use ra_proc_macro::ProcMacroClient;
2629

2730
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -250,7 +253,7 @@ impl ProjectWorkspace {
250253
let mut crate_graph = CrateGraph::default();
251254
match self {
252255
ProjectWorkspace::Json { project } => {
253-
let mut target_cfg_map = FxHashMap::<Option<&str>, CfgOptions>::default();
256+
let mut cfg_cache: FxHashMap<Option<&str>, Vec<CfgFlag>> = FxHashMap::default();
254257
let crates: FxHashMap<_, _> = project
255258
.crates
256259
.iter()
@@ -266,11 +269,12 @@ impl ProjectWorkspace {
266269
.map(|it| proc_macro_client.by_dylib_path(&it));
267270

268271
let target = krate.target.as_deref().or(target);
269-
let target_cfgs = target_cfg_map
270-
.entry(target.clone())
271-
.or_insert_with(|| get_rustc_cfg_options(target.as_deref()));
272-
let mut cfg_options = krate.cfg.clone();
273-
cfg_options.append(target_cfgs);
272+
let target_cfgs = cfg_cache
273+
.entry(target)
274+
.or_insert_with(|| get_rustc_cfg_options(target));
275+
276+
let mut cfg_options = CfgOptions::default();
277+
cfg_options.extend(target_cfgs.iter().chain(krate.cfg.iter()).cloned());
274278

275279
// FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
276280
Some((
@@ -307,7 +311,8 @@ impl ProjectWorkspace {
307311
}
308312
}
309313
ProjectWorkspace::Cargo { cargo, sysroot } => {
310-
let mut cfg_options = get_rustc_cfg_options(target);
314+
let mut cfg_options = CfgOptions::default();
315+
cfg_options.extend(get_rustc_cfg_options(target));
311316

312317
let sysroot_crates: FxHashMap<_, _> = sysroot
313318
.crates()
@@ -354,6 +359,7 @@ impl ProjectWorkspace {
354359

355360
// Add test cfg for non-sysroot crates
356361
cfg_options.insert_atom("test".into());
362+
cfg_options.insert_atom("debug_assertions".into());
357363

358364
// Next, create crates for each package, target pair
359365
for pkg in cargo.packages() {
@@ -367,15 +373,7 @@ impl ProjectWorkspace {
367373
for feature in cargo[pkg].features.iter() {
368374
opts.insert_key_value("feature".into(), feature.into());
369375
}
370-
for cfg in cargo[pkg].cfgs.iter() {
371-
match cfg.find('=') {
372-
Some(split) => opts.insert_key_value(
373-
cfg[..split].into(),
374-
cfg[split + 1..].trim_matches('"').into(),
375-
),
376-
None => opts.insert_atom(cfg.into()),
377-
};
378-
}
376+
opts.extend(cargo[pkg].cfgs.iter().cloned());
379377
opts
380378
};
381379
let mut env = Env::default();
@@ -503,51 +501,35 @@ impl ProjectWorkspace {
503501
}
504502
}
505503

506-
fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
507-
let mut cfg_options = CfgOptions::default();
504+
fn get_rustc_cfg_options(target: Option<&str>) -> Vec<CfgFlag> {
505+
let mut res = Vec::new();
508506

509507
// Some nightly-only cfgs, which are required for stdlib
510-
{
511-
cfg_options.insert_atom("target_thread_local".into());
512-
for &target_has_atomic in ["8", "16", "32", "64", "cas", "ptr"].iter() {
513-
cfg_options.insert_key_value("target_has_atomic".into(), target_has_atomic.into());
514-
cfg_options
515-
.insert_key_value("target_has_atomic_load_store".into(), target_has_atomic.into());
508+
res.push(CfgFlag::Atom("target_thread_local".into()));
509+
for &ty in ["8", "16", "32", "64", "cas", "ptr"].iter() {
510+
for &key in ["target_has_atomic", "target_has_atomic_load_store"].iter() {
511+
res.push(CfgFlag::KeyValue { key: key.to_string(), value: ty.into() });
516512
}
517513
}
518514

519-
let rustc_cfgs = || -> Result<String> {
520-
// `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here.
515+
let rustc_cfgs = {
521516
let mut cmd = Command::new(ra_toolchain::rustc());
522517
cmd.args(&["--print", "cfg", "-O"]);
523518
if let Some(target) = target {
524519
cmd.args(&["--target", target]);
525520
}
526-
let output = output(cmd)?;
527-
Ok(String::from_utf8(output.stdout)?)
528-
}();
521+
utf8_stdout(cmd)
522+
};
529523

530524
match rustc_cfgs {
531-
Ok(rustc_cfgs) => {
532-
for line in rustc_cfgs.lines() {
533-
match split_delim(line, '=') {
534-
None => cfg_options.insert_atom(line.into()),
535-
Some((key, value)) => {
536-
let value = value.trim_matches('"');
537-
cfg_options.insert_key_value(key.into(), value.into());
538-
}
539-
}
540-
}
541-
}
525+
Ok(rustc_cfgs) => res.extend(rustc_cfgs.lines().map(|it| it.parse().unwrap())),
542526
Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
543527
}
544528

545-
cfg_options.insert_atom("debug_assertions".into());
546-
547-
cfg_options
529+
res
548530
}
549531

550-
fn output(mut cmd: Command) -> Result<Output> {
532+
fn utf8_stdout(mut cmd: Command) -> Result<String> {
551533
let output = cmd.output().with_context(|| format!("{:?} failed", cmd))?;
552534
if !output.status.success() {
553535
match String::from_utf8(output.stderr) {
@@ -557,5 +539,6 @@ fn output(mut cmd: Command) -> Result<Output> {
557539
_ => bail!("{:?} failed, {}", cmd, output.status),
558540
}
559541
}
560-
Ok(output)
542+
let stdout = String::from_utf8(output.stdout)?;
543+
Ok(stdout)
561544
}

crates/ra_project_model/src/project_json.rs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
use std::path::PathBuf;
44

55
use paths::{AbsPath, AbsPathBuf};
6-
use ra_cfg::CfgOptions;
76
use ra_db::{CrateId, CrateName, Dependency, Edition};
8-
use rustc_hash::{FxHashMap, FxHashSet};
7+
use rustc_hash::FxHashMap;
98
use serde::{de, Deserialize};
10-
use stdx::split_delim;
9+
10+
use crate::cfg_flag::CfgFlag;
1111

1212
/// Roots and crates that compose this Rust project.
1313
#[derive(Clone, Debug, Eq, PartialEq)]
@@ -22,7 +22,7 @@ pub struct Crate {
2222
pub(crate) root_module: AbsPathBuf,
2323
pub(crate) edition: Edition,
2424
pub(crate) deps: Vec<Dependency>,
25-
pub(crate) cfg: CfgOptions,
25+
pub(crate) cfg: Vec<CfgFlag>,
2626
pub(crate) target: Option<String>,
2727
pub(crate) env: FxHashMap<String, String>,
2828
pub(crate) proc_macro_dylib_path: Option<AbsPathBuf>,
@@ -65,18 +65,7 @@ impl ProjectJson {
6565
name: dep_data.name,
6666
})
6767
.collect::<Vec<_>>(),
68-
cfg: {
69-
let mut cfg = CfgOptions::default();
70-
for entry in &crate_data.cfg {
71-
match split_delim(entry, '=') {
72-
Some((key, value)) => {
73-
cfg.insert_key_value(key.into(), value.into());
74-
}
75-
None => cfg.insert_atom(entry.into()),
76-
}
77-
}
78-
cfg
79-
},
68+
cfg: crate_data.cfg,
8069
target: crate_data.target,
8170
env: crate_data.env,
8271
proc_macro_dylib_path: crate_data
@@ -103,7 +92,7 @@ struct CrateData {
10392
edition: EditionData,
10493
deps: Vec<DepData>,
10594
#[serde(default)]
106-
cfg: FxHashSet<String>,
95+
cfg: Vec<CfgFlag>,
10796
target: Option<String>,
10897
#[serde(default)]
10998
env: FxHashMap<String, String>,

crates/ra_project_model/src/sysroot.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use anyhow::{bail, format_err, Result};
66
use paths::{AbsPath, AbsPathBuf};
77
use ra_arena::{Arena, Idx};
88

9-
use crate::output;
9+
use crate::utf8_stdout;
1010

1111
#[derive(Default, Debug, Clone, Eq, PartialEq)]
1212
pub struct Sysroot {
@@ -92,15 +92,14 @@ fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> {
9292
let current_dir = cargo_toml.parent().unwrap();
9393
let mut rustc = Command::new(ra_toolchain::rustc());
9494
rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
95-
let rustc_output = output(rustc)?;
96-
let stdout = String::from_utf8(rustc_output.stdout)?;
95+
let stdout = utf8_stdout(rustc)?;
9796
let sysroot_path = AbsPath::assert(Path::new(stdout.trim()));
9897
let src_path = sysroot_path.join("lib/rustlib/src/rust/src");
9998

10099
if !src_path.exists() {
101100
let mut rustup = Command::new(ra_toolchain::rustup());
102101
rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
103-
let _output = output(rustup)?;
102+
utf8_stdout(rustup)?;
104103
}
105104
if !src_path.exists() {
106105
bail!(

crates/rust-analyzer/tests/heavy_tests/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ fn test_missing_module_code_action_in_json_project() {
318318
"root_module": path.join("src/lib.rs"),
319319
"deps": [],
320320
"edition": "2015",
321-
"cfg": [ "cfg_atom_1", "feature=cfg_1"],
321+
"cfg": [ "cfg_atom_1", "feature=\"cfg_1\""],
322322
} ]
323323
});
324324

0 commit comments

Comments
 (0)