diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs index 3b169440ad..890cefcafb 100644 --- a/crates/ra_hir_def/src/body.rs +++ b/crates/ra_hir_def/src/body.rs @@ -27,7 +27,7 @@ use crate::{ AsMacroCall, DefWithBodyId, HasModule, Lookup, ModuleId, }; -/// A subser of Exander that only deals with cfg attributes. We only need it to +/// A subset of Exander that only deals with cfg attributes. We only need it to /// avoid cyclic queries in crate def map during enum processing. pub(crate) struct CfgExpander { cfg_options: CfgOptions, diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs index 0caedd8d85..5716038543 100644 --- a/crates/ra_hir_def/src/body/lower.rs +++ b/crates/ra_hir_def/src/body/lower.rs @@ -141,6 +141,10 @@ impl ExprCollector<'_> { fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { let syntax_ptr = AstPtr::new(&expr); + let attrs = self.expander.parse_attrs(&expr); + if !self.expander.is_cfg_enabled(&attrs) { + return self.missing_expr(); + } match expr { ast::Expr::IfExpr(e) => { let then_branch = self.collect_block_opt(e.then_branch()); diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs index b6a96bb5ca..588d812820 100644 --- a/crates/ra_hir_ty/src/tests.rs +++ b/crates/ra_hir_ty/src/tests.rs @@ -390,6 +390,38 @@ fn no_such_field_with_feature_flag_diagnostics_on_struct_lit() { assert_snapshot!(diagnostics, @r###""###); } +#[test] +fn no_such_field_with_feature_flag_diagnostics_on_block_expr() { + let diagnostics = TestDB::with_files( + r#" + //- /lib.rs crate:foo cfg:feature=foo + struct S { + #[cfg(feature = "foo")] + foo: u32, + #[cfg(not(feature = "foo"))] + bar: u32, + } + + impl S { + fn new(bar: u32) -> Self { + #[cfg(feature = "foo")] + { + Self { foo: bar } + } + #[cfg(not(feature = "foo"))] + { + Self { bar } + } + } + } + "#, + ) + .diagnostics() + .0; + + assert_snapshot!(diagnostics, @r###""###); +} + #[test] fn no_such_field_with_feature_flag_diagnostics_on_struct_fields() { let diagnostics = TestDB::with_files( diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 715eddadb6..74a63e32a3 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -69,6 +69,7 @@ pub enum RustfmtConfig { pub struct ClientCapsConfig { pub location_link: bool, pub line_folding_only: bool, + pub hierarchical_symbols: bool, } impl Default for Config { @@ -215,6 +216,11 @@ impl Config { if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { self.client_caps.line_folding_only = value } + if let Some(value) = + caps.document_symbol.as_ref().and_then(|it| it.hierarchical_document_symbol_support) + { + self.client_caps.hierarchical_symbols = value + } self.completion.allow_snippets(false); if let Some(completion) = &caps.completion { if let Some(completion_item) = &completion.completion_item { diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index c2c1a23cdf..e87e8db5d3 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs @@ -16,7 +16,7 @@ use lsp_types::{ Hover, HoverContents, Location, MarkupContent, MarkupKind, Position, PrepareRenameResponse, Range, RenameParams, SemanticTokensParams, SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult, SymbolInformation, TextDocumentIdentifier, - TextEdit, WorkspaceEdit, + TextEdit, Url, WorkspaceEdit, }; use ra_ide::{ Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, @@ -219,6 +219,7 @@ pub fn handle_document_symbol( let _p = profile("handle_document_symbol"); let file_id = params.text_document.try_conv_with(&world)?; let line_index = world.analysis().file_line_index(file_id)?; + let url = file_id.try_conv_with(&world)?; let mut parents: Vec<(DocumentSymbol, Option)> = Vec::new(); @@ -234,10 +235,10 @@ pub fn handle_document_symbol( }; parents.push((doc_symbol, symbol.parent)); } - let mut res = Vec::new(); + let mut document_symbols = Vec::new(); while let Some((node, parent)) = parents.pop() { match parent { - None => res.push(node), + None => document_symbols.push(node), Some(i) => { let children = &mut parents[i].0.children; if children.is_none() { @@ -248,7 +249,35 @@ pub fn handle_document_symbol( } } - Ok(Some(res.into())) + if world.config.client_caps.hierarchical_symbols { + Ok(Some(document_symbols.into())) + } else { + let mut symbol_information = Vec::::new(); + for symbol in document_symbols { + flatten_document_symbol(&symbol, None, &url, &mut symbol_information); + } + + Ok(Some(symbol_information.into())) + } +} + +fn flatten_document_symbol( + symbol: &DocumentSymbol, + container_name: Option, + url: &Url, + res: &mut Vec, +) { + res.push(SymbolInformation { + name: symbol.name.clone(), + kind: symbol.kind, + deprecated: symbol.deprecated, + location: Location::new(url.clone(), symbol.range), + container_name: container_name, + }); + + for child in symbol.children.iter().flatten() { + flatten_document_symbol(child, Some(symbol.name.clone()), url, res); + } } pub fn handle_workspace_symbol( diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 0ad4b63aeb..97e7940915 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -4,7 +4,7 @@ import * as vscode from 'vscode'; import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; -export async function createClient(serverPath: string, cwd: string): Promise { +export function createClient(serverPath: string, cwd: string): lc.LanguageClient { // '.' Is the fallback if no folder is open // TODO?: Workspace folders support Uri's (eg: file://test.txt). // It might be a good idea to test if the uri points to a file. diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index f7ed62d035..41df119910 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -21,7 +21,7 @@ export class Ctx { serverPath: string, cwd: string, ): Promise { - const client = await createClient(serverPath, cwd); + const client = createClient(serverPath, cwd); const res = new Ctx(config, extCtx, client, serverPath); res.pushCleanup(client.start()); await client.onReady();