Merge pull request #4548 from bnjjj/fix_4464

add support of feature flag for runnables
This commit is contained in:
Aleksey Kladov 2020-05-24 15:34:35 +02:00 committed by GitHub
commit 130318b823
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 234 additions and 19 deletions

View file

@ -40,6 +40,7 @@ ra_project_model = { path = "../ra_project_model" }
ra_syntax = { path = "../ra_syntax" }
ra_text_edit = { path = "../ra_text_edit" }
ra_vfs = "0.6.0"
ra_cfg = { path = "../ra_cfg"}
# This should only be used in CLI
ra_db = { path = "../ra_db" }
@ -55,6 +56,8 @@ winapi = "0.3.8"
tempfile = "3.1.0"
insta = "0.16.0"
test_utils = { path = "../test_utils" }
mbe = { path = "../ra_mbe", package = "ra_mbe" }
tt = { path = "../ra_tt", package = "ra_tt" }
[features]
jemalloc = [ "ra_prof/jemalloc" ]

View file

@ -4,6 +4,7 @@ use ra_ide::{FileId, RunnableKind, TestId};
use ra_project_model::{self, ProjectWorkspace, TargetKind};
use crate::{world::WorldSnapshot, Result};
use ra_syntax::SmolStr;
/// Abstract representation of Cargo target.
///
@ -20,6 +21,7 @@ impl CargoTargetSpec {
pub(crate) fn runnable_args(
spec: Option<CargoTargetSpec>,
kind: &RunnableKind,
features_needed: &Vec<SmolStr>,
) -> Result<(Vec<String>, Vec<String>)> {
let mut args = Vec::new();
let mut extra_args = Vec::new();
@ -73,6 +75,12 @@ impl CargoTargetSpec {
}
}
}
features_needed.iter().for_each(|feature| {
args.push("--features".to_string());
args.push(feature.to_string());
});
Ok((args, extra_args))
}

View file

@ -17,13 +17,14 @@ use lsp_types::{
SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult,
SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, Url, WorkspaceEdit,
};
use ra_cfg::CfgExpr;
use ra_ide::{
FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, SearchScope,
TextEdit,
};
use ra_prof::profile;
use ra_project_model::TargetKind;
use ra_syntax::{AstNode, SyntaxKind, TextRange, TextSize};
use ra_syntax::{AstNode, SmolStr, SyntaxKind, TextRange, TextSize};
use rustc_hash::FxHashMap;
use serde::{Deserialize, Serialize};
use serde_json::to_value;
@ -978,7 +979,12 @@ fn to_lsp_runnable(
) -> Result<lsp_ext::Runnable> {
let spec = CargoTargetSpec::for_file(world, file_id)?;
let target = spec.as_ref().map(|s| s.target.clone());
let (args, extra_args) = CargoTargetSpec::runnable_args(spec, &runnable.kind)?;
let mut features_needed = vec![];
for cfg_expr in &runnable.cfg_exprs {
collect_minimal_features_needed(cfg_expr, &mut features_needed);
}
let (args, extra_args) =
CargoTargetSpec::runnable_args(spec, &runnable.kind, &features_needed)?;
let line_index = world.analysis().file_line_index(file_id)?;
let label = match &runnable.kind {
RunnableKind::Test { test_id, .. } => format!("test {}", test_id),
@ -1004,6 +1010,26 @@ fn to_lsp_runnable(
})
}
/// Fill minimal features needed
fn collect_minimal_features_needed(cfg_expr: &CfgExpr, features: &mut Vec<SmolStr>) {
match cfg_expr {
CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.clone()),
CfgExpr::All(preds) => {
preds.iter().for_each(|cfg| collect_minimal_features_needed(cfg, features));
}
CfgExpr::Any(preds) => {
for cfg in preds {
let len_features = features.len();
collect_minimal_features_needed(cfg, features);
if len_features != features.len() {
break;
}
}
}
_ => {}
}
}
pub fn handle_inlay_hints(
world: WorldSnapshot,
params: InlayHintsParams,
@ -1140,3 +1166,54 @@ pub fn handle_semantic_tokens_range(
let semantic_tokens = to_proto::semantic_tokens(&text, &line_index, highlights);
Ok(Some(semantic_tokens.into()))
}
#[cfg(test)]
mod tests {
use super::*;
use mbe::{ast_to_token_tree, TokenMap};
use ra_cfg::parse_cfg;
use ra_syntax::{
ast::{self, AstNode},
SmolStr,
};
fn get_token_tree_generated(input: &str) -> (tt::Subtree, TokenMap) {
let source_file = ast::SourceFile::parse(input).ok().unwrap();
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
ast_to_token_tree(&tt).unwrap()
}
#[test]
fn test_cfg_expr_minimal_features_needed() {
let (subtree, _) = get_token_tree_generated(r#"#![cfg(feature = "baz")]"#);
let cfg_expr = parse_cfg(&subtree);
let mut min_features = vec![];
collect_minimal_features_needed(&cfg_expr, &mut min_features);
assert_eq!(min_features, vec![SmolStr::new("baz")]);
let (subtree, _) =
get_token_tree_generated(r#"#![cfg(all(feature = "baz", feature = "foo"))]"#);
let cfg_expr = parse_cfg(&subtree);
let mut min_features = vec![];
collect_minimal_features_needed(&cfg_expr, &mut min_features);
assert_eq!(min_features, vec![SmolStr::new("baz"), SmolStr::new("foo")]);
let (subtree, _) =
get_token_tree_generated(r#"#![cfg(any(feature = "baz", feature = "foo", unix))]"#);
let cfg_expr = parse_cfg(&subtree);
let mut min_features = vec![];
collect_minimal_features_needed(&cfg_expr, &mut min_features);
assert_eq!(min_features, vec![SmolStr::new("baz")]);
let (subtree, _) = get_token_tree_generated(r#"#![cfg(foo)]"#);
let cfg_expr = parse_cfg(&subtree);
let mut min_features = vec![];
collect_minimal_features_needed(&cfg_expr, &mut min_features);
assert!(min_features.is_empty());
}
}