diff --git a/rust/codeql-extractor.yml b/rust/codeql-extractor.yml index ce876e8d3fff..c7785e5f8c72 100644 --- a/rust/codeql-extractor.yml +++ b/rust/codeql-extractor.yml @@ -45,9 +45,10 @@ options: cargo_features: title: Cargo features to turn on description: > - Comma-separated list of features to turn on. If any value is `*` all features - are turned on. By default only default cargo features are enabled. Can be - repeated. + Comma-separated list of features to turn on. By default all features are enabled. + If any features are specified, then only those features are enabled. The `default` + feature must be explicitly specified if only default features are desired. + Can be repeated. type: array cargo_cfg_overrides: title: Cargo cfg overrides diff --git a/rust/extractor/src/config.rs b/rust/extractor/src/config.rs index 8124f825da3c..a2a74420b5d8 100644 --- a/rust/extractor/src/config.rs +++ b/rust/extractor/src/config.rs @@ -129,6 +129,23 @@ impl Config { } } + fn cargo_features(&self) -> CargoFeatures { + // '*' is to be considered deprecated but still kept in for backward compatibility + if self.cargo_features.is_empty() || self.cargo_features.iter().any(|f| f == "*") { + CargoFeatures::All + } else { + CargoFeatures::Selected { + features: self + .cargo_features + .iter() + .filter(|f| *f != "default") + .cloned() + .collect(), + no_default_features: !self.cargo_features.iter().any(|f| f == "default"), + } + } + } + pub fn to_cargo_config(&self, dir: &AbsPath) -> (CargoConfig, LoadCargoConfig) { let sysroot = self.sysroot(dir); ( @@ -159,16 +176,7 @@ impl Config { .unwrap_or_else(|| self.scratch_dir.join("target")), ) .ok(), - features: if self.cargo_features.is_empty() { - Default::default() - } else if self.cargo_features.contains(&"*".to_string()) { - CargoFeatures::All - } else { - CargoFeatures::Selected { - features: self.cargo_features.clone(), - no_default_features: false, - } - }, + features: self.cargo_features(), target: self.cargo_target.clone(), cfg_overrides: to_cfg_overrides(&self.cargo_cfg_overrides), wrap_rustc_in_build_scripts: false, diff --git a/rust/ql/integration-tests/options/features/functions.expected b/rust/ql/integration-tests/options/features/functions.none.expected similarity index 100% rename from rust/ql/integration-tests/options/features/functions.expected rename to rust/ql/integration-tests/options/features/functions.none.expected diff --git a/rust/ql/integration-tests/options/features/test_features_option.py b/rust/ql/integration-tests/options/features/test_features_option.py index 75c5058be078..bb1b57c8de53 100644 --- a/rust/ql/integration-tests/options/features/test_features_option.py +++ b/rust/ql/integration-tests/options/features/test_features_option.py @@ -1,5 +1,6 @@ import pytest +@pytest.mark.ql_test(expected=".all.expected") def test_default(codeql, rust): codeql.database.create() @@ -8,10 +9,33 @@ def test_default(codeql, rust): pytest.param(p, marks=pytest.mark.ql_test(expected=f".{e}.expected")) for p, e in ( + ("default", "none"), ("foo", "foo"), ("bar", "bar"), ("*", "all"), - ("foo,bar", "all")) + ("foo,bar", "all"), + ("default,foo", "foo"), + ("default,bar", "bar"), + ) ]) def test_features(codeql, rust, features): codeql.database.create(extractor_option=f"cargo_features={features}") + +@pytest.mark.parametrize("features", + [ + pytest.param(p, + marks=pytest.mark.ql_test(expected=f".{e}.expected")) + for p, e in ( + ("default", "foo"), + ("foo", "foo"), + ("bar", "bar"), + ("*", "all"), + ("foo,bar", "all"), + ("default,foo", "foo"), + ("default,bar", "all"), + ) + ]) +def test_features_with_default(codeql, rust, features): + with open("Cargo.toml", "a") as f: + print('default = ["foo"]', file=f) + codeql.database.create(extractor_option=f"cargo_features={features}")