diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2acd440122..f46fb8fecc 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,20 +16,6 @@ env: RUSTUP_MAX_RETRIES: 10 jobs: - # rust-audit: - # name: Audit Rust vulnerabilities - # runs-on: ubuntu-latest - # steps: - # - name: Checkout repository - # uses: actions/checkout@v2 - - # - uses: actions-rs/install@v0.1 - # with: - # crate: cargo-audit - # use-tool-cache: true - - # - run: cargo audit - rust: name: Rust runs-on: ${{ matrix.os }} @@ -88,11 +74,14 @@ jobs: if: matrix.os == 'windows-latest' run: Remove-Item ./target/debug/xtask.exe, ./target/debug/deps/xtask.exe - # Weird target to catch non-portable code - rust-power: - name: Rust Power + # Weird targets to catch non-portable code + rust-cross: + name: Rust Cross runs-on: ubuntu-latest + env: + targets: "powerpc-unknown-linux-gnu x86_64-unknown-linux-musl" + steps: - name: Checkout repository uses: actions/checkout@v2 @@ -103,7 +92,9 @@ jobs: toolchain: stable profile: minimal override: true - target: 'powerpc-unknown-linux-gnu' + + - name: Install Rust targets + run: rustup target add ${{ env.targets }} - name: Cache cargo directories uses: actions/cache@v2 @@ -114,7 +105,10 @@ jobs: key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - name: Check - run: cargo check --target=powerpc-unknown-linux-gnu --all-targets + run: | + for target in ${{ env.targets }}; do + cargo check --target=$target --all-targets + done typescript: name: TypeScript diff --git a/Cargo.lock b/Cargo.lock index 451928c27b..2658891625 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -125,9 +125,9 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "chalk-derive" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "654c611946ba2629c5028cb7708687af975faf2c29d731824cb294c873df4697" +checksum = "c1df0dbb57d74b4acd20f20fa66ab2acd09776b79eaeb9d8f947b2f3e01c40bf" dependencies = [ "proc-macro2", "quote", @@ -137,9 +137,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5341fbc654ca886b73b804a36aebf0e621057ccc1a68e9815b5b39b3ac9ae8" +checksum = "44361a25dbdb1dc428f56ad7a3c21ba9ca12f3225c26a47919ff6fcb10a583d4" dependencies = [ "chalk-derive", "lazy_static", @@ -147,9 +147,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4484807b155b5a411e6135d330295f9ba5042e2920b8712c6574ca6ea91e9ee5" +checksum = "dd89556b98de156d5eaf21077d297cd2198628f10f2df140798ea3a5dd84bc86" dependencies = [ "chalk-derive", "chalk-ir", @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.19.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "281f82facd2538997fbe52132b1941ed213d266748215c31d15f62a8664429ad" +checksum = "a886da37a0dc457057d86f78f026f7a09c6d8088aa13f4f4127fdb8dc80119a3" dependencies = [ "chalk-derive", "chalk-ir", @@ -418,9 +418,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f595585f103464d8d2f6e9864682d74c1601fed5e07d62b1c9058dba8246fb" +checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" dependencies = [ "autocfg", ] @@ -465,9 +465,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b88cd59ee5f71fea89a62248fc8f387d44400cefe05ef548466d61ced9029a7" +checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9" dependencies = [ "autocfg", "hashbrown", @@ -852,9 +852,9 @@ dependencies = [ [[package]] name = "perf-event-open-sys" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95db63e37862bc1b842135d2234ef9418f222cc660c6752f45e7cf9ddfb97f96" +checksum = "83e7183862f36d10263d0a1ccaef50fef734ade948bf026afd1bd97355c78273" dependencies = [ "libc", ] @@ -871,9 +871,9 @@ dependencies = [ [[package]] name = "pico-args" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1eee8b1f4966c8343d7ca0f5a8452cd35d5610a2e0efbe2a68cae44bef2046" +checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1" [[package]] name = "plain" @@ -1694,9 +1694,9 @@ dependencies = [ [[package]] name = "tracing-tree" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee7f0f53ed2093971a698db799ef56a2dfd89b32e3aeb5165f0e637a02be04" +checksum = "e1a3dc4774db3a6b2d66a4f8d8de670e874ec3ed55615860c994927419b32c5f" dependencies = [ "ansi_term", "atty", diff --git a/crates/expect/src/lib.rs b/crates/expect/src/lib.rs index 21a458d477..bd83895f78 100644 --- a/crates/expect/src/lib.rs +++ b/crates/expect/src/lib.rs @@ -74,7 +74,7 @@ impl fmt::Display for Position { impl Expect { pub fn assert_eq(&self, actual: &str) { let trimmed = self.trimmed(); - if &trimmed == actual { + if trimmed == actual { return; } Runtime::fail_expect(self, &trimmed, actual); diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 7c38f5ef9d..31e14246de 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -1,4 +1,4 @@ -//! cargo_check provides the functionality needed to run `cargo check` or +//! Flycheck provides the functionality needed to run `cargo check` or //! another compatible command (f.x. clippy) in a background thread and provide //! LSP diagnostics based on the output of the command. @@ -147,6 +147,12 @@ impl FlycheckActor { // avoid busy-waiting. let cargo_handle = self.cargo_handle.take().unwrap(); let res = cargo_handle.join(); + if res.is_err() { + log::error!( + "Flycheck failed to run the following command: {:?}", + self.check_command() + ) + } self.send(Message::Progress(Progress::DidFinish(res))); } Event::CheckEvent(Some(message)) => match message { @@ -253,7 +259,7 @@ impl CargoHandle { return Err(io::Error::new( io::ErrorKind::Other, format!( - "Cargo watcher failed,the command produced no valid metadata (exit code: {:?})", + "Cargo watcher failed, the command produced no valid metadata (exit code: {:?})", exit_status ), )); diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs index 15ec75c956..07c978378a 100644 --- a/crates/ra_assists/src/ast_transform.rs +++ b/crates/ra_assists/src/ast_transform.rs @@ -51,7 +51,7 @@ impl<'a> SubstituteTypeParams<'a> { // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky .skip(1) // The actual list of trait type parameters may be longer than the one - // used in the `impl` block due to trailing default type parametrs. + // used in the `impl` block due to trailing default type parameters. // For that case we extend the `substs` with an empty iterator so we // can still hit those trailing values and check if they actually have // a default type. If they do, go for that type from `hir` to `ast` so diff --git a/crates/ra_assists/src/handlers/add_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index b67438b6ba..ebdf00e676 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs @@ -1,10 +1,10 @@ +use itertools::Itertools; use ra_syntax::{ ast::{self, AstNode}, Direction, SmolStr, SyntaxKind::{IDENT, WHITESPACE}, TextRange, TextSize, }; -use stdx::SepBy; use crate::{ assist_context::{AssistContext, Assists}, @@ -61,9 +61,9 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< .filter(|t| t != trait_token.text()) .collect::>(); let has_more_derives = !new_attr_input.is_empty(); - let new_attr_input = new_attr_input.iter().sep_by(", ").surround_with("(", ")").to_string(); if has_more_derives { + let new_attr_input = format!("({})", new_attr_input.iter().format(", ")); builder.replace(input.syntax().text_range(), new_attr_input); } else { let attr_range = attr.syntax().text_range(); diff --git a/crates/ra_assists/src/handlers/add_turbo_fish.rs b/crates/ra_assists/src/handlers/add_turbo_fish.rs index 0c565e89af..537322a72c 100644 --- a/crates/ra_assists/src/handlers/add_turbo_fish.rs +++ b/crates/ra_assists/src/handlers/add_turbo_fish.rs @@ -41,7 +41,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext) -> Option<( let name_ref = ast::NameRef::cast(ident.parent())?; let def = match classify_name_ref(&ctx.sema, &name_ref)? { NameRefClass::Definition(def) => def, - NameRefClass::FieldShorthand { .. } => return None, + NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None, }; let fun = match def { Definition::ModuleDef(hir::ModuleDef::Function(it)) => it, diff --git a/crates/ra_assists/src/handlers/apply_demorgan.rs b/crates/ra_assists/src/handlers/apply_demorgan.rs index de701f8b83..3ac4aed7d2 100644 --- a/crates/ra_assists/src/handlers/apply_demorgan.rs +++ b/crates/ra_assists/src/handlers/apply_demorgan.rs @@ -4,7 +4,7 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin // Assist: apply_demorgan // -// Apply [De Morgan's law](https://en.wikipedia.org/wiki/De_Morgan%27s_laws). +// Apply https://en.wikipedia.org/wiki/De_Morgan%27s_laws[De Morgan's law]. // This transforms expressions of the form `!l || !r` into `!(l && r)`. // This also works with `&&`. This assist can only be applied with the cursor // on either `||` or `&&`, with both operands being a negation of some kind. diff --git a/crates/ra_assists/src/handlers/fix_visibility.rs b/crates/ra_assists/src/handlers/fix_visibility.rs index 1aefa79cc3..a19dbf33f6 100644 --- a/crates/ra_assists/src/handlers/fix_visibility.rs +++ b/crates/ra_assists/src/handlers/fix_visibility.rs @@ -121,7 +121,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Some(cap) => match current_visibility { Some(current_visibility) => builder.replace_snippet( cap, - dbg!(current_visibility.syntax()).text_range(), + current_visibility.syntax().text_range(), format!("$0{}", missing_visibility), ), None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), diff --git a/crates/ra_assists/src/handlers/generate_impl.rs b/crates/ra_assists/src/handlers/generate_impl.rs index d9b87c9c0d..7162dc1848 100644 --- a/crates/ra_assists/src/handlers/generate_impl.rs +++ b/crates/ra_assists/src/handlers/generate_impl.rs @@ -1,5 +1,6 @@ +use itertools::Itertools; use ra_syntax::ast::{self, AstNode, GenericParamsOwner, NameOwner}; -use stdx::{format_to, SepBy}; +use stdx::format_to; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -50,7 +51,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext) -> Option<() .filter_map(|it| it.name()) .map(|it| it.text().clone()); - let generic_params = lifetime_params.chain(type_params).sep_by(", "); + let generic_params = lifetime_params.chain(type_params).format(", "); format_to!(buf, "<{}>", generic_params) } match ctx.config.snippet_cap { diff --git a/crates/ra_assists/src/handlers/generate_new.rs b/crates/ra_assists/src/handlers/generate_new.rs index b84aa24b6c..32dfed274a 100644 --- a/crates/ra_assists/src/handlers/generate_new.rs +++ b/crates/ra_assists/src/handlers/generate_new.rs @@ -1,9 +1,10 @@ use hir::Adt; +use itertools::Itertools; use ra_syntax::{ ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner}, T, }; -use stdx::{format_to, SepBy}; +use stdx::format_to; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -52,8 +53,8 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()> let params = field_list .fields() .filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax()))) - .sep_by(", "); - let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", "); + .format(", "); + let fields = field_list.fields().filter_map(|f| f.name()).format(", "); format_to!(buf, " {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields); @@ -102,7 +103,7 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String { .map(|it| it.text().clone()); let type_params = type_params.type_params().filter_map(|it| it.name()).map(|it| it.text().clone()); - format_to!(buf, "<{}>", lifetime_params.chain(type_params).sep_by(", ")) + format_to!(buf, "<{}>", lifetime_params.chain(type_params).format(", ")) } format_to!(buf, " {{\n{}\n}}\n", code); diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index 507646cc80..890996a68d 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -66,13 +66,13 @@ pub struct GroupLabel(pub String); #[derive(Debug, Clone)] pub struct Assist { - pub id: AssistId, + id: AssistId, /// Short description of the assist, as shown in the UI. - pub label: String, - pub group: Option, + label: String, + group: Option, /// Target ranges are used to sort assists: the smaller the target range, /// the more specific assist is, and so it should be sorted first. - pub target: TextRange, + target: TextRange, } #[derive(Debug, Clone)] @@ -120,10 +120,25 @@ impl Assist { group: Option, target: TextRange, ) -> Assist { - // FIXME: make fields private, so that this invariant can't be broken assert!(label.starts_with(|c: char| c.is_uppercase())); Assist { id, label, group, target } } + + pub fn id(&self) -> AssistId { + self.id + } + + pub fn label(&self) -> String { + self.label.clone() + } + + pub fn group(&self) -> Option { + self.group.clone() + } + + pub fn target(&self) -> TextRange { + self.target + } } mod handlers { diff --git a/crates/ra_assists/src/tests.rs b/crates/ra_assists/src/tests.rs index 18fcb90498..e738364220 100644 --- a/crates/ra_assists/src/tests.rs +++ b/crates/ra_assists/src/tests.rs @@ -20,7 +20,7 @@ pub(crate) fn check_assist(assist: Handler, ra_fixture_before: &str, ra_fixture_ // FIXME: instead of having a separate function here, maybe use // `extract_ranges` and mark the target as ` ` in the -// fixuture? +// fixture? pub(crate) fn check_assist_target(assist: Handler, ra_fixture: &str, target: &str) { check(assist, ra_fixture, ExpectedResult::Target(target)); } diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 54d5678d14..0de6fdf3fb 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs @@ -257,7 +257,7 @@ pub use prelude::*; .find(|dep| &dep.name.to_string() == std_crate)? .krate; - let mut module = std_crate.root_module(db)?; + let mut module = std_crate.root_module(db); for segment in path { module = module.children(db).find_map(|child| { let name = child.name(db)?; diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 27cdabea03..0007d7fa88 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -4,6 +4,7 @@ use std::{iter, sync::Arc}; use arrayvec::ArrayVec; use either::Either; use hir_def::{ + adt::ReprKind, adt::StructKind, adt::VariantData, builtin_type::BuiltinType, @@ -83,9 +84,9 @@ impl Crate { .collect() } - pub fn root_module(self, db: &dyn HirDatabase) -> Option { + pub fn root_module(self, db: &dyn HirDatabase) -> Module { let module_id = db.crate_def_map(self.id).root; - Some(Module::new(self, module_id)) + Module::new(self, module_id) } pub fn root_file(self, db: &dyn HirDatabase) -> FileId { @@ -431,6 +432,10 @@ impl Struct { Type::from_def(db, self.id.lookup(db.upcast()).container.module(db.upcast()).krate, self.id) } + pub fn repr(self, db: &dyn HirDatabase) -> Option { + db.struct_data(self.id).repr.clone() + } + fn variant_data(self, db: &dyn HirDatabase) -> Arc { db.struct_data(self.id).variant_data.clone() } @@ -1253,6 +1258,19 @@ impl Type { ) } + pub fn is_packed(&self, db: &dyn HirDatabase) -> bool { + let adt_id = match self.ty.value { + Ty::Apply(ApplicationTy { ctor: TypeCtor::Adt(adt_id), .. }) => adt_id, + _ => return false, + }; + + let adt = adt_id.into(); + match adt { + Adt::Struct(s) => matches!(s.repr(db), Some(ReprKind::Packed)), + _ => false, + } + } + pub fn is_raw_ptr(&self) -> bool { matches!(&self.ty.value, Ty::Apply(ApplicationTy { ctor: TypeCtor::RawPtr(..), .. })) } diff --git a/crates/ra_hir/src/diagnostics.rs b/crates/ra_hir/src/diagnostics.rs index 266b513dcf..363164b9b4 100644 --- a/crates/ra_hir/src/diagnostics.rs +++ b/crates/ra_hir/src/diagnostics.rs @@ -1,8 +1,6 @@ //! FIXME: write short doc here pub use hir_def::diagnostics::UnresolvedModule; -pub use hir_expand::diagnostics::{ - AstDiagnostic, Diagnostic, DiagnosticSink, DiagnosticSinkBuilder, -}; +pub use hir_expand::diagnostics::{Diagnostic, DiagnosticSink, DiagnosticSinkBuilder}; pub use hir_ty::diagnostics::{ MismatchedArgCount, MissingFields, MissingMatchArms, MissingOkInTailExpr, NoSuchField, }; diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 31f3241c9e..34b02c5365 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -49,7 +49,7 @@ pub use hir_def::{ docs::Documentation, nameres::ModuleSource, path::{ModPath, Path, PathKind}, - type_ref::Mutability, + type_ref::{Mutability, TypeRef}, }; pub use hir_expand::{ hygiene::Hygiene, name::Name, HirFileId, InFile, MacroCallId, MacroCallLoc, diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 307b336f20..36b688ccb6 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs @@ -8,7 +8,7 @@ use hir_def::{ resolver::{self, HasResolver, Resolver}, AsMacroCall, FunctionId, TraitId, VariantId, }; -use hir_expand::{diagnostics::AstDiagnostic, hygiene::Hygiene, ExpansionInfo}; +use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo}; use hir_ty::associated_type_shorthand_candidates; use itertools::Itertools; use ra_db::{FileId, FileRange}; @@ -24,8 +24,9 @@ use crate::{ diagnostics::Diagnostic, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, resolve_hir_path_qualifier, SourceAnalyzer}, - AssocItem, Callable, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, Module, - ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef, + AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, + Module, ModuleDef, Name, Origin, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, TypeRef, + VariantDef, }; use resolver::TypeNs; @@ -109,13 +110,6 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.parse(file_id) } - pub fn ast(&self, d: &T) -> ::AST { - let file_id = d.source().file_id; - let root = self.db.parse_or_expand(file_id).unwrap(); - self.imp.cache(root, file_id); - d.ast(self.db.upcast()) - } - pub fn expand(&self, macro_call: &ast::MacroCall) -> Option { self.imp.expand(macro_call) } @@ -145,8 +139,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.original_range(node) } - pub fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { - self.imp.diagnostics_range(diagnostics) + pub fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { + self.imp.diagnostics_display_range(diagnostics) } pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator + '_ { @@ -228,6 +222,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_path(path) } + pub fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option { + self.imp.resolve_extern_crate(extern_crate) + } + pub fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option { self.imp.resolve_variant(record_lit).map(VariantDef::from) } @@ -275,6 +273,18 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { pub fn assert_contains_node(&self, node: &SyntaxNode) { self.imp.assert_contains_node(node) } + + pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { + self.imp.is_unsafe_method_call(method_call_expr) + } + + pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { + self.imp.is_unsafe_ref_expr(ref_expr) + } + + pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { + self.imp.is_unsafe_ident_pat(ident_pat) + } } impl<'db> SemanticsImpl<'db> { @@ -372,10 +382,11 @@ impl<'db> SemanticsImpl<'db> { original_range(self.db, node.as_ref()) } - fn diagnostics_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { - let src = diagnostics.source(); + fn diagnostics_display_range(&self, diagnostics: &dyn Diagnostic) -> FileRange { + let src = diagnostics.display_source(); let root = self.db.parse_or_expand(src.file_id).unwrap(); let node = src.value.to_node(&root); + self.cache(root, src.file_id); original_range(self.db, src.with_value(&node)) } @@ -443,6 +454,17 @@ impl<'db> SemanticsImpl<'db> { self.analyze(path.syntax()).resolve_path(self.db, path) } + fn resolve_extern_crate(&self, extern_crate: &ast::ExternCrate) -> Option { + let krate = self.scope(extern_crate.syntax()).krate()?; + krate.dependencies(self.db).into_iter().find_map(|dep| { + if dep.name == extern_crate.name_ref()?.as_name() { + Some(dep.krate) + } else { + None + } + }) + } + fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option { self.analyze(record_lit.syntax()).resolve_variant(self.db, record_lit) } @@ -559,6 +581,90 @@ impl<'db> SemanticsImpl<'db> { }); InFile::new(file_id, node) } + + pub fn is_unsafe_method_call(&self, method_call_expr: ast::MethodCallExpr) -> bool { + method_call_expr + .expr() + .and_then(|expr| { + let field_expr = if let ast::Expr::FieldExpr(field_expr) = expr { + field_expr + } else { + return None; + }; + let ty = self.type_of_expr(&field_expr.expr()?)?; + if !ty.is_packed(self.db) { + return None; + } + + let func = self.resolve_method_call(&method_call_expr).map(Function::from)?; + let is_unsafe = func.has_self_param(self.db) + && matches!(func.params(self.db).first(), Some(TypeRef::Reference(..))); + Some(is_unsafe) + }) + .unwrap_or(false) + } + + pub fn is_unsafe_ref_expr(&self, ref_expr: &ast::RefExpr) -> bool { + ref_expr + .expr() + .and_then(|expr| { + let field_expr = match expr { + ast::Expr::FieldExpr(field_expr) => field_expr, + _ => return None, + }; + let expr = field_expr.expr()?; + self.type_of_expr(&expr) + }) + // Binding a reference to a packed type is possibly unsafe. + .map(|ty| ty.is_packed(self.db)) + .unwrap_or(false) + + // FIXME This needs layout computation to be correct. It will highlight + // more than it should with the current implementation. + } + + pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool { + if !ident_pat.ref_token().is_some() { + return false; + } + + ident_pat + .syntax() + .parent() + .and_then(|parent| { + // `IdentPat` can live under `RecordPat` directly under `RecordPatField` or + // `RecordPatFieldList`. `RecordPatField` also lives under `RecordPatFieldList`, + // so this tries to lookup the `IdentPat` anywhere along that structure to the + // `RecordPat` so we can get the containing type. + let record_pat = ast::RecordPatField::cast(parent.clone()) + .and_then(|record_pat| record_pat.syntax().parent()) + .or_else(|| Some(parent.clone())) + .and_then(|parent| { + ast::RecordPatFieldList::cast(parent)? + .syntax() + .parent() + .and_then(ast::RecordPat::cast) + }); + + // If this doesn't match a `RecordPat`, fallback to a `LetStmt` to see if + // this is initialized from a `FieldExpr`. + if let Some(record_pat) = record_pat { + self.type_of_pat(&ast::Pat::RecordPat(record_pat)) + } else if let Some(let_stmt) = ast::LetStmt::cast(parent) { + let field_expr = match let_stmt.initializer()? { + ast::Expr::FieldExpr(field_expr) => field_expr, + _ => return None, + }; + + self.type_of_expr(&field_expr.expr()?) + } else { + None + } + }) + // Binding a reference to a packed type is possibly unsafe. + .map(|ty| ty.is_packed(self.db)) + .unwrap_or(false) + } } pub trait ToDef: AstNode + Clone { @@ -612,6 +718,10 @@ impl<'a> SemanticsScope<'a> { Some(Module { id: self.resolver.module()? }) } + pub fn krate(&self) -> Option { + Some(Crate { id: self.resolver.krate()? }) + } + /// Note: `FxHashSet` should be treated as an opaque type, passed into `Type // FIXME: rename to visible_traits to not repeat scope? pub fn traits_in_scope(&self) -> FxHashSet { diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs index d0cb62ef01..d3d62debfd 100644 --- a/crates/ra_hir/src/source_analyzer.rs +++ b/crates/ra_hir/src/source_analyzer.rs @@ -265,8 +265,7 @@ impl SourceAnalyzer { } // This must be a normal source file rather than macro file. - let hir_path = - crate::Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; + let hir_path = Path::from_src(path.clone(), &Hygiene::new(db.upcast(), self.file_id))?; // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we // trying to resolve foo::bar. @@ -451,7 +450,7 @@ fn adjust( pub(crate) fn resolve_hir_path( db: &dyn HirDatabase, resolver: &Resolver, - path: &crate::Path, + path: &Path, ) -> Option { let types = resolver.resolve_path_in_type_ns_fully(db.upcast(), path.mod_path()).map(|ty| match ty { @@ -512,7 +511,7 @@ pub(crate) fn resolve_hir_path( pub(crate) fn resolve_hir_path_qualifier( db: &dyn HirDatabase, resolver: &Resolver, - path: &crate::Path, + path: &Path, ) -> Option { let items = resolver .resolve_module_path_in_items(db.upcast(), path.mod_path()) diff --git a/crates/ra_hir_def/src/adt.rs b/crates/ra_hir_def/src/adt.rs index 6cb56a1cd0..35c3a91402 100644 --- a/crates/ra_hir_def/src/adt.rs +++ b/crates/ra_hir_def/src/adt.rs @@ -9,11 +9,12 @@ use hir_expand::{ }; use ra_arena::{map::ArenaMap, Arena}; use ra_syntax::ast::{self, NameOwner, VisibilityOwner}; +use tt::{Delimiter, DelimiterKind, Leaf, Subtree, TokenTree}; use crate::{ body::{CfgExpander, LowerCtx}, db::DefDatabase, - item_tree::{Field, Fields, ItemTree}, + item_tree::{AttrOwner, Field, Fields, ItemTree, ModItem}, src::HasChildSource, src::HasSource, trace::Trace, @@ -29,6 +30,7 @@ use ra_cfg::CfgOptions; pub struct StructData { pub name: Name, pub variant_data: Arc, + pub repr: Option, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -58,26 +60,58 @@ pub struct FieldData { pub visibility: RawVisibility, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ReprKind { + Packed, + Other, +} + +fn repr_from_value(item_tree: &ItemTree, of: AttrOwner) -> Option { + item_tree.attrs(of).by_key("repr").tt_values().find_map(parse_repr_tt) +} + +fn parse_repr_tt(tt: &Subtree) -> Option { + match tt.delimiter { + Some(Delimiter { kind: DelimiterKind::Parenthesis, .. }) => {} + _ => return None, + } + + let mut it = tt.token_trees.iter(); + match it.next()? { + TokenTree::Leaf(Leaf::Ident(ident)) if ident.text == "packed" => Some(ReprKind::Packed), + _ => Some(ReprKind::Other), + } +} + impl StructData { pub(crate) fn struct_data_query(db: &dyn DefDatabase, id: StructId) -> Arc { let loc = id.lookup(db); let item_tree = db.item_tree(loc.id.file_id); + let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into()); let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); let strukt = &item_tree[loc.id.value]; let variant_data = lower_fields(&item_tree, &cfg_options, &strukt.fields); - - Arc::new(StructData { name: strukt.name.clone(), variant_data: Arc::new(variant_data) }) + Arc::new(StructData { + name: strukt.name.clone(), + variant_data: Arc::new(variant_data), + repr, + }) } pub(crate) fn union_data_query(db: &dyn DefDatabase, id: UnionId) -> Arc { let loc = id.lookup(db); let item_tree = db.item_tree(loc.id.file_id); + let repr = repr_from_value(&item_tree, ModItem::from(loc.id.value).into()); let cfg_options = db.crate_graph()[loc.container.module(db).krate].cfg_options.clone(); let union = &item_tree[loc.id.value]; let variant_data = lower_fields(&item_tree, &cfg_options, &union.fields); - Arc::new(StructData { name: union.name.clone(), variant_data: Arc::new(variant_data) }) + Arc::new(StructData { + name: union.name.clone(), + variant_data: Arc::new(variant_data), + repr, + }) } } diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs index 481b13a878..5b7f05bf41 100644 --- a/crates/ra_hir_def/src/diagnostics.rs +++ b/crates/ra_hir_def/src/diagnostics.rs @@ -21,7 +21,7 @@ impl Diagnostic for UnresolvedModule { fn message(&self) -> String { "unresolved module".to_string() } - fn source(&self) -> InFile { + fn display_source(&self) -> InFile { InFile::new(self.file, self.decl.clone().into()) } fn as_any(&self) -> &(dyn Any + Send + 'static) { diff --git a/crates/ra_hir_expand/src/diagnostics.rs b/crates/ra_hir_expand/src/diagnostics.rs index 507132a131..a618934c9a 100644 --- a/crates/ra_hir_expand/src/diagnostics.rs +++ b/crates/ra_hir_expand/src/diagnostics.rs @@ -16,36 +16,21 @@ use std::{any::Any, fmt}; -use ra_syntax::{SyntaxNode, SyntaxNodePtr}; +use ra_syntax::SyntaxNodePtr; -use crate::{db::AstDatabase, InFile}; +use crate::InFile; pub trait Diagnostic: Any + Send + Sync + fmt::Debug + 'static { fn name(&self) -> &'static str; fn message(&self) -> String; - fn source(&self) -> InFile; + /// Used in highlighting and related purposes + fn display_source(&self) -> InFile; fn as_any(&self) -> &(dyn Any + Send + 'static); fn is_experimental(&self) -> bool { false } } -pub trait AstDiagnostic { - type AST; - fn ast(&self, db: &dyn AstDatabase) -> Self::AST; -} - -impl dyn Diagnostic { - pub fn syntax_node(&self, db: &impl AstDatabase) -> SyntaxNode { - let node = db.parse_or_expand(self.source().file_id).unwrap(); - self.source().value.to_node(&node) - } - - pub fn downcast_ref(&self) -> Option<&D> { - self.as_any().downcast_ref() - } -} - pub struct DiagnosticSink<'a> { callbacks: Vec Result<(), ()> + 'a>>, filters: Vec bool + 'a>>, @@ -90,7 +75,7 @@ impl<'a> DiagnosticSinkBuilder<'a> { } pub fn on(mut self, mut cb: F) -> Self { - let cb = move |diag: &dyn Diagnostic| match diag.downcast_ref::() { + let cb = move |diag: &dyn Diagnostic| match diag.as_any().downcast_ref::() { Some(d) => { cb(d); Ok(()) diff --git a/crates/ra_hir_expand/src/hygiene.rs b/crates/ra_hir_expand/src/hygiene.rs index 6b482a60c5..aefe47bd32 100644 --- a/crates/ra_hir_expand/src/hygiene.rs +++ b/crates/ra_hir_expand/src/hygiene.rs @@ -17,7 +17,7 @@ pub struct Hygiene { // This is what `$crate` expands to def_crate: Option, - // Indiciate this is a local inner macro + // Indicate this is a local inner macro local_inner: bool, } diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 2e8d636917..8bb735fc62 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -44,7 +44,8 @@ mod test_db; /// containing the call plus the offset of the macro call in the file. Note that /// this is a recursive definition! However, the size_of of `HirFileId` is /// finite (because everything bottoms out at the real `FileId`) and small -/// (`MacroCallId` uses the location interner). +/// (`MacroCallId` uses the location interning. You can check details here: +/// https://en.wikipedia.org/wiki/String_interning). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct HirFileId(HirFileIdRepr); diff --git a/crates/ra_hir_ty/Cargo.toml b/crates/ra_hir_ty/Cargo.toml index 623ce261a0..83397d5793 100644 --- a/crates/ra_hir_ty/Cargo.toml +++ b/crates/ra_hir_ty/Cargo.toml @@ -28,9 +28,9 @@ test_utils = { path = "../test_utils" } scoped-tls = "1" -chalk-solve = { version = "0.19.0" } -chalk-ir = { version = "0.19.0" } -chalk-recursive = { version = "0.19.0" } +chalk-solve = { version = "0.21.0" } +chalk-ir = { version = "0.21.0" } +chalk-recursive = { version = "0.21.0" } [dev-dependencies] expect = { path = "../expect" } diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs index 56acd3bbf4..45e31033ea 100644 --- a/crates/ra_hir_ty/src/diagnostics.rs +++ b/crates/ra_hir_ty/src/diagnostics.rs @@ -6,10 +6,10 @@ mod unsafe_check; use std::any::Any; use hir_def::DefWithBodyId; -use hir_expand::diagnostics::{AstDiagnostic, Diagnostic, DiagnosticSink}; -use hir_expand::{db::AstDatabase, name::Name, HirFileId, InFile}; +use hir_expand::diagnostics::{Diagnostic, DiagnosticSink}; +use hir_expand::{name::Name, HirFileId, InFile}; use ra_prof::profile; -use ra_syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; +use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; use stdx::format_to; use crate::db::HirDatabase; @@ -41,7 +41,7 @@ impl Diagnostic for NoSuchField { "no such field".to_string() } - fn source(&self) -> InFile { + fn display_source(&self) -> InFile { InFile::new(self.file, self.field.clone().into()) } @@ -50,20 +50,11 @@ impl Diagnostic for NoSuchField { } } -impl AstDiagnostic for NoSuchField { - type AST = ast::RecordExprField; - - fn ast(&self, db: &dyn AstDatabase) -> Self::AST { - let root = db.parse_or_expand(self.source().file_id).unwrap(); - let node = self.source().value.to_node(&root); - ast::RecordExprField::cast(node).unwrap() - } -} - #[derive(Debug)] pub struct MissingFields { pub file: HirFileId, - pub field_list: AstPtr, + pub field_list_parent: AstPtr, + pub field_list_parent_path: Option>, pub missed_fields: Vec, } @@ -78,28 +69,28 @@ impl Diagnostic for MissingFields { } buf } - fn source(&self) -> InFile { - InFile { file_id: self.file, value: self.field_list.clone().into() } + + fn display_source(&self) -> InFile { + InFile { + file_id: self.file, + value: self + .field_list_parent_path + .clone() + .map(SyntaxNodePtr::from) + .unwrap_or_else(|| self.field_list_parent.clone().into()), + } } + fn as_any(&self) -> &(dyn Any + Send + 'static) { self } } -impl AstDiagnostic for MissingFields { - type AST = ast::RecordExprFieldList; - - fn ast(&self, db: &dyn AstDatabase) -> Self::AST { - let root = db.parse_or_expand(self.source().file_id).unwrap(); - let node = self.source().value.to_node(&root); - ast::RecordExprFieldList::cast(node).unwrap() - } -} - #[derive(Debug)] pub struct MissingPatFields { pub file: HirFileId, - pub field_list: AstPtr, + pub field_list_parent: AstPtr, + pub field_list_parent_path: Option>, pub missed_fields: Vec, } @@ -114,8 +105,15 @@ impl Diagnostic for MissingPatFields { } buf } - fn source(&self) -> InFile { - InFile { file_id: self.file, value: self.field_list.clone().into() } + fn display_source(&self) -> InFile { + InFile { + file_id: self.file, + value: self + .field_list_parent_path + .clone() + .map(SyntaxNodePtr::from) + .unwrap_or_else(|| self.field_list_parent.clone().into()), + } } fn as_any(&self) -> &(dyn Any + Send + 'static) { self @@ -136,7 +134,7 @@ impl Diagnostic for MissingMatchArms { fn message(&self) -> String { String::from("Missing match arm") } - fn source(&self) -> InFile { + fn display_source(&self) -> InFile { InFile { file_id: self.file, value: self.match_expr.clone().into() } } fn as_any(&self) -> &(dyn Any + Send + 'static) { @@ -157,7 +155,7 @@ impl Diagnostic for MissingOkInTailExpr { fn message(&self) -> String { "wrap return expression in Ok".to_string() } - fn source(&self) -> InFile { + fn display_source(&self) -> InFile { InFile { file_id: self.file, value: self.expr.clone().into() } } fn as_any(&self) -> &(dyn Any + Send + 'static) { @@ -165,16 +163,6 @@ impl Diagnostic for MissingOkInTailExpr { } } -impl AstDiagnostic for MissingOkInTailExpr { - type AST = ast::Expr; - - fn ast(&self, db: &dyn AstDatabase) -> Self::AST { - let root = db.parse_or_expand(self.file).unwrap(); - let node = self.source().value.to_node(&root); - ast::Expr::cast(node).unwrap() - } -} - #[derive(Debug)] pub struct BreakOutsideOfLoop { pub file: HirFileId, @@ -188,7 +176,7 @@ impl Diagnostic for BreakOutsideOfLoop { fn message(&self) -> String { "break outside of loop".to_string() } - fn source(&self) -> InFile { + fn display_source(&self) -> InFile { InFile { file_id: self.file, value: self.expr.clone().into() } } fn as_any(&self) -> &(dyn Any + Send + 'static) { @@ -196,16 +184,6 @@ impl Diagnostic for BreakOutsideOfLoop { } } -impl AstDiagnostic for BreakOutsideOfLoop { - type AST = ast::Expr; - - fn ast(&self, db: &dyn AstDatabase) -> Self::AST { - let root = db.parse_or_expand(self.file).unwrap(); - let node = self.source().value.to_node(&root); - ast::Expr::cast(node).unwrap() - } -} - #[derive(Debug)] pub struct MissingUnsafe { pub file: HirFileId, @@ -219,7 +197,7 @@ impl Diagnostic for MissingUnsafe { fn message(&self) -> String { format!("This operation is unsafe and requires an unsafe function or block") } - fn source(&self) -> InFile { + fn display_source(&self) -> InFile { InFile { file_id: self.file, value: self.expr.clone().into() } } fn as_any(&self) -> &(dyn Any + Send + 'static) { @@ -227,16 +205,6 @@ impl Diagnostic for MissingUnsafe { } } -impl AstDiagnostic for MissingUnsafe { - type AST = ast::Expr; - - fn ast(&self, db: &dyn AstDatabase) -> Self::AST { - let root = db.parse_or_expand(self.source().file_id).unwrap(); - let node = self.source().value.to_node(&root); - ast::Expr::cast(node).unwrap() - } -} - #[derive(Debug)] pub struct MismatchedArgCount { pub file: HirFileId, @@ -253,7 +221,7 @@ impl Diagnostic for MismatchedArgCount { let s = if self.expected == 1 { "" } else { "s" }; format!("Expected {} argument{}, found {}", self.expected, s, self.found) } - fn source(&self) -> InFile { + fn display_source(&self) -> InFile { InFile { file_id: self.file, value: self.call_expr.clone().into() } } fn as_any(&self) -> &(dyn Any + Send + 'static) { @@ -264,19 +232,13 @@ impl Diagnostic for MismatchedArgCount { } } -impl AstDiagnostic for MismatchedArgCount { - type AST = ast::CallExpr; - fn ast(&self, db: &dyn AstDatabase) -> Self::AST { - let root = db.parse_or_expand(self.source().file_id).unwrap(); - let node = self.source().value.to_node(&root); - ast::CallExpr::cast(node).unwrap() - } -} - #[cfg(test)] mod tests { use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId}; - use hir_expand::diagnostics::{Diagnostic, DiagnosticSinkBuilder}; + use hir_expand::{ + db::AstDatabase, + diagnostics::{Diagnostic, DiagnosticSinkBuilder}, + }; use ra_db::{fixture::WithFixture, FileId, SourceDatabase, SourceDatabaseExt}; use ra_syntax::{TextRange, TextSize}; use rustc_hash::FxHashMap; @@ -321,9 +283,11 @@ mod tests { let mut actual: FxHashMap> = FxHashMap::default(); db.diagnostics(|d| { - // FXIME: macros... - let file_id = d.source().file_id.original_file(&db); - let range = d.syntax_node(&db).text_range(); + let src = d.display_source(); + let root = db.parse_or_expand(src.file_id).unwrap(); + // FIXME: macros... + let file_id = src.file_id.original_file(&db); + let range = src.value.to_node(&root).text_range(); let message = d.message().to_owned(); actual.entry(file_id).or_default().push((range, message)); }); @@ -351,8 +315,8 @@ struct S { foo: i32, bar: () } impl S { fn new() -> S { S { - //^... Missing structure fields: - //| - bar + //^ Missing structure fields: + //| - bar foo: 92, baz: 62, //^^^^^^^ no such field @@ -473,8 +437,8 @@ impl Foo { struct S { foo: i32, bar: () } fn baz(s: S) { let S { foo: _ } = s; - //^^^^^^^^^^ Missing structure fields: - // | - bar + //^ Missing structure fields: + //| - bar } "#, ); diff --git a/crates/ra_hir_ty/src/diagnostics/expr.rs b/crates/ra_hir_ty/src/diagnostics/expr.rs index 95bbf2d955..51adcecafa 100644 --- a/crates/ra_hir_ty/src/diagnostics/expr.rs +++ b/crates/ra_hir_ty/src/diagnostics/expr.rs @@ -100,8 +100,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { if let Ok(source_ptr) = source_map.expr_syntax(id) { let root = source_ptr.file_syntax(db.upcast()); - if let ast::Expr::RecordExpr(record_lit) = &source_ptr.value.to_node(&root) { - if let Some(field_list) = record_lit.record_expr_field_list() { + if let ast::Expr::RecordExpr(record_expr) = &source_ptr.value.to_node(&root) { + if let Some(_) = record_expr.record_expr_field_list() { let variant_data = variant_data(db.upcast(), variant_def); let missed_fields = missed_fields .into_iter() @@ -109,7 +109,8 @@ impl<'a, 'b> ExprValidator<'a, 'b> { .collect(); self.sink.push(MissingFields { file: source_ptr.file_id, - field_list: AstPtr::new(&field_list), + field_list_parent: AstPtr::new(&record_expr), + field_list_parent_path: record_expr.path().map(|path| AstPtr::new(&path)), missed_fields, }) } @@ -131,7 +132,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> { if let Some(expr) = source_ptr.value.as_ref().left() { let root = source_ptr.file_syntax(db.upcast()); if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { - if let Some(field_list) = record_pat.record_pat_field_list() { + if let Some(_) = record_pat.record_pat_field_list() { let variant_data = variant_data(db.upcast(), variant_def); let missed_fields = missed_fields .into_iter() @@ -139,7 +140,10 @@ impl<'a, 'b> ExprValidator<'a, 'b> { .collect(); self.sink.push(MissingPatFields { file: source_ptr.file_id, - field_list: AstPtr::new(&field_list), + field_list_parent: AstPtr::new(&record_pat), + field_list_parent_path: record_pat + .path() + .map(|path| AstPtr::new(&path)), missed_fields, }) } diff --git a/crates/ra_hir_ty/src/diagnostics/match_check.rs b/crates/ra_hir_ty/src/diagnostics/match_check.rs index 507edcb7de..deca244dbb 100644 --- a/crates/ra_hir_ty/src/diagnostics/match_check.rs +++ b/crates/ra_hir_ty/src/diagnostics/match_check.rs @@ -1161,15 +1161,15 @@ fn main() { //^ Missing match arm match a { Either::A { } => (), - //^^^ Missing structure fields: - // | - foo + //^^^^^^^^^ Missing structure fields: + // | - foo Either::B => (), } match a { //^ Missing match arm Either::A { } => (), - } //^^^ Missing structure fields: - // | - foo + } //^^^^^^^^^ Missing structure fields: + // | - foo match a { Either::A { foo: true } => (), diff --git a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs index 5cc76bdce4..61ffbf5d15 100644 --- a/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs +++ b/crates/ra_hir_ty/src/diagnostics/unsafe_check.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use hir_def::{ body::Body, expr::{Expr, ExprId, UnaryOp}, + resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, }; use hir_expand::diagnostics::DiagnosticSink; @@ -70,7 +71,7 @@ pub fn unsafe_expressions( ) -> Vec { let mut unsafe_exprs = vec![]; let body = db.body(def); - walk_unsafe(&mut unsafe_exprs, db, infer, &body, body.body_expr, false); + walk_unsafe(&mut unsafe_exprs, db, infer, def, &body, body.body_expr, false); unsafe_exprs } @@ -79,6 +80,7 @@ fn walk_unsafe( unsafe_exprs: &mut Vec, db: &dyn HirDatabase, infer: &InferenceResult, + def: DefWithBodyId, body: &Body, current: ExprId, inside_unsafe_block: bool, @@ -97,6 +99,15 @@ fn walk_unsafe( } } } + Expr::Path(path) => { + let resolver = resolver_for_expr(db.upcast(), def, current); + let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path.mod_path()); + if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id))) = value_or_partial { + if db.static_data(id).mutable { + unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block }); + } + } + } Expr::MethodCall { .. } => { if infer .method_resolution(current) @@ -112,13 +123,13 @@ fn walk_unsafe( } } Expr::Unsafe { body: child } => { - return walk_unsafe(unsafe_exprs, db, infer, body, *child, true); + return walk_unsafe(unsafe_exprs, db, infer, def, body, *child, true); } _ => {} } expr.walk_child_exprs(|child| { - walk_unsafe(unsafe_exprs, db, infer, body, child, inside_unsafe_block); + walk_unsafe(unsafe_exprs, db, infer, def, body, child, inside_unsafe_block); }); } @@ -167,6 +178,27 @@ fn main() { HasUnsafe.unsafe_fn(); } } +"#, + ); + } + + #[test] + fn missing_unsafe_diagnostic_with_static_mut() { + check_diagnostics( + r#" +struct Ty { + a: u8, +} + +static mut static_mut: Ty = Ty { a: 0 }; + +fn main() { + let x = static_mut.a; + //^^^^^^^^^^ This operation is unsafe and requires an unsafe function or block + unsafe { + let x = static_mut.a; + } +} "#, ); } diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs index 28f32a0a4d..3d12039a6d 100644 --- a/crates/ra_hir_ty/src/infer.rs +++ b/crates/ra_hir_ty/src/infer.rs @@ -440,6 +440,12 @@ impl<'a> InferenceContext<'a> { let ty = self.insert_type_vars(ty.subst(&substs)); forbid_unresolved_segments((ty, Some(strukt.into())), unresolved) } + TypeNs::AdtId(AdtId::UnionId(u)) => { + let substs = Ty::substs_from_path(&ctx, path, u.into(), true); + let ty = self.db.ty(u.into()); + let ty = self.insert_type_vars(ty.subst(&substs)); + forbid_unresolved_segments((ty, Some(u.into())), unresolved) + } TypeNs::EnumVariantId(var) => { let substs = Ty::substs_from_path(&ctx, path, var.into(), true); let ty = self.db.ty(var.parent.into()); @@ -490,10 +496,7 @@ impl<'a> InferenceContext<'a> { // FIXME potentially resolve assoc type (Ty::Unknown, None) } - TypeNs::AdtId(AdtId::EnumId(_)) - | TypeNs::AdtId(AdtId::UnionId(_)) - | TypeNs::BuiltinType(_) - | TypeNs::TraitId(_) => { + TypeNs::AdtId(AdtId::EnumId(_)) | TypeNs::BuiltinType(_) | TypeNs::TraitId(_) => { // FIXME diagnostic (Ty::Unknown, None) } diff --git a/crates/ra_hir_ty/src/lower.rs b/crates/ra_hir_ty/src/lower.rs index 1eacc6f95e..7638f167b5 100644 --- a/crates/ra_hir_ty/src/lower.rs +++ b/crates/ra_hir_ty/src/lower.rs @@ -518,6 +518,7 @@ impl Ty { let (segment, generic_def) = match resolved { ValueTyDefId::FunctionId(it) => (last, Some(it.into())), ValueTyDefId::StructId(it) => (last, Some(it.into())), + ValueTyDefId::UnionId(it) => (last, Some(it.into())), ValueTyDefId::ConstId(it) => (last, Some(it.into())), ValueTyDefId::StaticId(_) => (last, None), ValueTyDefId::EnumVariantId(var) => { @@ -1148,11 +1149,12 @@ impl_from!(BuiltinType, AdtId(StructId, EnumId, UnionId), TypeAliasId for TyDefI pub enum ValueTyDefId { FunctionId(FunctionId), StructId(StructId), + UnionId(UnionId), EnumVariantId(EnumVariantId), ConstId(ConstId), StaticId(StaticId), } -impl_from!(FunctionId, StructId, EnumVariantId, ConstId, StaticId for ValueTyDefId); +impl_from!(FunctionId, StructId, UnionId, EnumVariantId, ConstId, StaticId for ValueTyDefId); /// Build the declared type of an item. This depends on the namespace; e.g. for /// `struct Foo(usize)`, we have two types: The type of the struct itself, and @@ -1179,6 +1181,7 @@ pub(crate) fn value_ty_query(db: &dyn HirDatabase, def: ValueTyDefId) -> Binders match def { ValueTyDefId::FunctionId(it) => type_for_fn(db, it), ValueTyDefId::StructId(it) => type_for_struct_constructor(db, it), + ValueTyDefId::UnionId(it) => type_for_adt(db, it.into()), ValueTyDefId::EnumVariantId(it) => type_for_enum_variant_constructor(db, it), ValueTyDefId::ConstId(it) => type_for_const(db, it), ValueTyDefId::StaticId(it) => type_for_static(db, it), diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs index 3fd7d5cd4f..5a7cf9455b 100644 --- a/crates/ra_hir_ty/src/tests/simple.rs +++ b/crates/ra_hir_ty/src/tests/simple.rs @@ -334,16 +334,44 @@ fn infer_union() { bar: f32, } + fn test() { + let u = MyUnion { foo: 0 }; + unsafe { baz(u); } + let u = MyUnion { bar: 0.0 }; + unsafe { baz(u); } + } + unsafe fn baz(u: MyUnion) { let inner = u.foo; + let inner = u.bar; } "#, expect![[r#" - 61..62 'u': MyUnion - 73..99 '{ ...foo; }': () - 83..88 'inner': u32 - 91..92 'u': MyUnion - 91..96 'u.foo': u32 + 57..172 '{ ...); } }': () + 67..68 'u': MyUnion + 71..89 'MyUnio...o: 0 }': MyUnion + 86..87 '0': u32 + 95..113 'unsafe...(u); }': () + 102..113 '{ baz(u); }': () + 104..107 'baz': fn baz(MyUnion) + 104..110 'baz(u)': () + 108..109 'u': MyUnion + 122..123 'u': MyUnion + 126..146 'MyUnio... 0.0 }': MyUnion + 141..144 '0.0': f32 + 152..170 'unsafe...(u); }': () + 159..170 '{ baz(u); }': () + 161..164 'baz': fn baz(MyUnion) + 161..167 'baz(u)': () + 165..166 'u': MyUnion + 188..189 'u': MyUnion + 200..249 '{ ...bar; }': () + 210..215 'inner': u32 + 218..219 'u': MyUnion + 218..223 'u.foo': u32 + 233..238 'inner': f32 + 241..242 'u': MyUnion + 241..246 'u.bar': f32 "#]], ); } diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs index 28d8f78768..4368e4eec8 100644 --- a/crates/ra_ide/src/completion/complete_snippet.rs +++ b/crates/ra_ide/src/completion/complete_snippet.rs @@ -36,7 +36,7 @@ pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte snippet( ctx, cap, - "Test module", + "tmod (Test module)", "\ #[cfg(test)] mod tests { @@ -54,7 +54,7 @@ mod tests { snippet( ctx, cap, - "Test function", + "tfn (Test function)", "\ #[test] fn ${1:feature}() { @@ -106,10 +106,10 @@ mod tests { } "#, expect![[r#" - sn Test function - sn Test module sn macro_rules sn pub(crate) + sn tfn (Test function) + sn tmod (Test module) "#]], ) } diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 6b03b30bb5..4aa761148d 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -27,7 +27,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) scope: SemanticsScope<'a>, pub(super) db: &'a RootDatabase, pub(super) config: &'a CompletionConfig, - pub(super) offset: TextSize, + pub(super) position: FilePosition, /// The token before the cursor, in the original file. pub(super) original_token: SyntaxToken, /// The token before the cursor, in the macro-expanded file. @@ -117,7 +117,7 @@ impl<'a> CompletionContext<'a> { config, original_token, token, - offset: position.offset, + position, krate, expected_type: None, name_ref_syntax: None, @@ -209,7 +209,7 @@ impl<'a> CompletionContext<'a> { mark::hit!(completes_if_prefix_is_keyword); self.original_token.text_range() } else { - TextRange::empty(self.offset) + TextRange::empty(self.position.offset) } } @@ -379,8 +379,8 @@ impl<'a> CompletionContext<'a> { self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); self.has_type_args = segment.generic_arg_list().is_some(); - #[allow(deprecated)] - if let Some(path) = hir::Path::from_ast(path.clone()) { + let hygiene = hir::Hygiene::new(self.db, self.position.file_id.into()); + if let Some(path) = hir::Path::from_src(path.clone(), &hygiene) { if let Some(path_prefix) = path.qualifier() { self.path_prefix = Some(path_prefix); return; diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs index 9a94ff4767..59f1b14246 100644 --- a/crates/ra_ide/src/completion/presentation.rs +++ b/crates/ra_ide/src/completion/presentation.rs @@ -2,8 +2,8 @@ //! It also handles scoring (sorting) completions. use hir::{Docs, HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type}; +use itertools::Itertools; use ra_syntax::ast::NameOwner; -use stdx::SepBy; use test_utils::mark; use crate::{ @@ -289,16 +289,16 @@ impl Completions { .map(|field| (field.name(ctx.db), field.signature_ty(ctx.db))); let variant_kind = variant.kind(ctx.db); let detail = match variant_kind { - StructKind::Tuple | StructKind::Unit => detail_types - .map(|(_, t)| t.display(ctx.db).to_string()) - .sep_by(", ") - .surround_with("(", ")") - .to_string(), - StructKind::Record => detail_types - .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string())) - .sep_by(", ") - .surround_with("{ ", " }") - .to_string(), + StructKind::Tuple | StructKind::Unit => format!( + "({})", + detail_types.map(|(_, t)| t.display(ctx.db).to_string()).format(", ") + ), + StructKind::Record => format!( + "{{ {} }}", + detail_types + .map(|(n, t)| format!("{}: {}", n, t.display(ctx.db).to_string())) + .format(", ") + ), }; let mut res = CompletionItem::new( CompletionKind::Reference, @@ -412,11 +412,10 @@ impl Builder { self = self.trigger_call_info(); let snippet = match (ctx.config.add_call_argument_snippets, params) { (true, Params::Named(params)) => { - let function_params_snippet = params - .iter() - .enumerate() - .map(|(index, param_name)| format!("${{{}:{}}}", index + 1, param_name)) - .sep_by(", "); + let function_params_snippet = + params.iter().enumerate().format_with(", ", |(index, param_name), f| { + f(&format_args!("${{{}:{}}}", index + 1, param_name)) + }); format!("{}({})$0", name, function_params_snippet) } _ => { diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs index 5ce900bf4d..79dbb0865c 100644 --- a/crates/ra_ide/src/diagnostics.rs +++ b/crates/ra_ide/src/diagnostics.rs @@ -6,22 +6,21 @@ use std::cell::RefCell; -use hir::{ - diagnostics::{AstDiagnostic, Diagnostic as _, DiagnosticSinkBuilder}, - HasSource, HirDisplay, Semantics, VariantDef, -}; +use hir::{diagnostics::DiagnosticSinkBuilder, Semantics}; use itertools::Itertools; use ra_db::SourceDatabase; use ra_ide_db::RootDatabase; use ra_prof::profile; use ra_syntax::{ - algo, - ast::{self, edit::IndentLevel, make, AstNode}, + ast::{self, AstNode}, SyntaxNode, TextRange, T, }; use ra_text_edit::{TextEdit, TextEditBuilder}; -use crate::{AnalysisConfig, Diagnostic, FileId, FileSystemEdit, Fix, SourceFileEdit}; +use crate::{AnalysisConfig, Diagnostic, FileId, Fix, SourceFileEdit}; + +mod diagnostics_with_fix; +use diagnostics_with_fix::DiagnosticWithFix; #[derive(Debug, Copy, Clone)] pub enum Severity { @@ -56,77 +55,16 @@ pub(crate) fn diagnostics( let res = RefCell::new(res); let mut sink_builder = DiagnosticSinkBuilder::new() .on::(|d| { - let original_file = d.source().file_id.original_file(db); - let fix = Fix::new( - "Create module", - FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() } - .into(), - ); - res.borrow_mut().push(Diagnostic { - name: Some(d.name().into()), - range: sema.diagnostics_range(d).range, - message: d.message(), - severity: Severity::Error, - fix: Some(fix), - }) + res.borrow_mut().push(diagnostic_with_fix(d, &sema)); }) .on::(|d| { - // Note that although we could add a diagnostics to - // fill the missing tuple field, e.g : - // `struct A(usize);` - // `let a = A { 0: () }` - // but it is uncommon usage and it should not be encouraged. - let fix = if d.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { - None - } else { - let mut field_list = d.ast(db); - for f in d.missed_fields.iter() { - let field = make::record_expr_field( - make::name_ref(&f.to_string()), - Some(make::expr_unit()), - ); - field_list = field_list.append_field(&field); - } - - let edit = { - let mut builder = TextEditBuilder::default(); - algo::diff(&d.ast(db).syntax(), &field_list.syntax()) - .into_text_edit(&mut builder); - builder.finish() - }; - Some(Fix::new("Fill struct fields", SourceFileEdit { file_id, edit }.into())) - }; - - res.borrow_mut().push(Diagnostic { - name: Some(d.name().into()), - range: sema.diagnostics_range(d).range, - message: d.message(), - severity: Severity::Error, - fix, - }) + res.borrow_mut().push(diagnostic_with_fix(d, &sema)); }) .on::(|d| { - let node = d.ast(db); - let replacement = format!("Ok({})", node.syntax()); - let edit = TextEdit::replace(node.syntax().text_range(), replacement); - let source_change = SourceFileEdit { file_id, edit }.into(); - let fix = Fix::new("Wrap with ok", source_change); - res.borrow_mut().push(Diagnostic { - name: Some(d.name().into()), - range: sema.diagnostics_range(d).range, - message: d.message(), - severity: Severity::Error, - fix: Some(fix), - }) + res.borrow_mut().push(diagnostic_with_fix(d, &sema)); }) .on::(|d| { - res.borrow_mut().push(Diagnostic { - name: Some(d.name().into()), - range: sema.diagnostics_range(d).range, - message: d.message(), - severity: Severity::Error, - fix: missing_struct_field_fix(&sema, file_id, d), - }) + res.borrow_mut().push(diagnostic_with_fix(d, &sema)); }) // Only collect experimental diagnostics when they're enabled. .filter(|diag| !diag.is_experimental() || enable_experimental); @@ -144,7 +82,7 @@ pub(crate) fn diagnostics( res.borrow_mut().push(Diagnostic { name: Some(d.name().into()), message: d.message(), - range: sema.diagnostics_range(d).range, + range: sema.diagnostics_display_range(d).range, severity: Severity::Error, fix: None, }) @@ -157,77 +95,13 @@ pub(crate) fn diagnostics( res.into_inner() } -fn missing_struct_field_fix( - sema: &Semantics, - usage_file_id: FileId, - d: &hir::diagnostics::NoSuchField, -) -> Option { - let record_expr = sema.ast(d); - - let record_lit = ast::RecordExpr::cast(record_expr.syntax().parent()?.parent()?)?; - let def_id = sema.resolve_variant(record_lit)?; - let module; - let def_file_id; - let record_fields = match VariantDef::from(def_id) { - VariantDef::Struct(s) => { - module = s.module(sema.db); - let source = s.source(sema.db); - def_file_id = source.file_id; - let fields = source.value.field_list()?; - record_field_list(fields)? - } - VariantDef::Union(u) => { - module = u.module(sema.db); - let source = u.source(sema.db); - def_file_id = source.file_id; - source.value.record_field_list()? - } - VariantDef::EnumVariant(e) => { - module = e.module(sema.db); - let source = e.source(sema.db); - def_file_id = source.file_id; - let fields = source.value.field_list()?; - record_field_list(fields)? - } - }; - let def_file_id = def_file_id.original_file(sema.db); - - let new_field_type = sema.type_of_expr(&record_expr.expr()?)?; - if new_field_type.is_unknown() { - return None; - } - let new_field = make::record_field( - record_expr.field_name()?, - make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), - ); - - let last_field = record_fields.fields().last()?; - let last_field_syntax = last_field.syntax(); - let indent = IndentLevel::from_node(last_field_syntax); - - let mut new_field = new_field.to_string(); - if usage_file_id != def_file_id { - new_field = format!("pub(crate) {}", new_field); - } - new_field = format!("\n{}{}", indent, new_field); - - let needs_comma = !last_field_syntax.to_string().ends_with(','); - if needs_comma { - new_field = format!(",{}", new_field); - } - - let source_change = SourceFileEdit { - file_id: def_file_id, - edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), - }; - let fix = Fix::new("Create field", source_change.into()); - return Some(fix); - - fn record_field_list(field_def_list: ast::FieldList) -> Option { - match field_def_list { - ast::FieldList::RecordFieldList(it) => Some(it), - ast::FieldList::TupleFieldList(_) => None, - } +fn diagnostic_with_fix(d: &D, sema: &Semantics) -> Diagnostic { + Diagnostic { + name: Some(d.name().into()), + range: sema.diagnostics_display_range(d).range, + message: d.message(), + severity: Severity::Error, + fix: d.fix(&sema), } } @@ -238,25 +112,26 @@ fn check_unnecessary_braces_in_use_statement( ) -> Option<()> { let use_tree_list = ast::UseTreeList::cast(node.clone())?; if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() { - let range = use_tree_list.syntax().text_range(); + let use_range = use_tree_list.syntax().text_range(); let edit = text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(&single_use_tree) .unwrap_or_else(|| { let to_replace = single_use_tree.syntax().text().to_string(); let mut edit_builder = TextEditBuilder::default(); - edit_builder.delete(range); - edit_builder.insert(range.start(), to_replace); + edit_builder.delete(use_range); + edit_builder.insert(use_range.start(), to_replace); edit_builder.finish() }); acc.push(Diagnostic { name: None, - range, + range: use_range, message: "Unnecessary braces in use statement".to_string(), severity: Severity::WeakWarning, fix: Some(Fix::new( "Remove unnecessary braces", SourceFileEdit { file_id, edit }.into(), + use_range, )), }); } @@ -271,8 +146,7 @@ fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement( if single_use_tree.path()?.segment()?.syntax().first_child_or_token()?.kind() == T![self] { let start = use_tree_list_node.prev_sibling_or_token()?.text_range().start(); let end = use_tree_list_node.text_range().end(); - let range = TextRange::new(start, end); - return Some(TextEdit::delete(range)); + return Some(TextEdit::delete(TextRange::new(start, end))); } None } @@ -295,14 +169,16 @@ fn check_struct_shorthand_initialization( edit_builder.insert(record_field.syntax().text_range().start(), field_name); let edit = edit_builder.finish(); + let field_range = record_field.syntax().text_range(); acc.push(Diagnostic { name: None, - range: record_field.syntax().text_range(), + range: field_range, message: "Shorthand struct initialization".to_string(), severity: Severity::WeakWarning, fix: Some(Fix::new( "Use struct shorthand initialization", SourceFileEdit { file_id, edit }.into(), + field_range, )), }); } @@ -326,7 +202,7 @@ mod tests { /// Takes a multi-file input fixture with annotated cursor positions, /// and checks that: /// * a diagnostic is produced - /// * this diagnostic touches the input cursor position + /// * this diagnostic fix trigger range touches the input cursor position /// * that the contents of the file containing the cursor match `after` after the diagnostic fix is applied fn check_fix(ra_fixture_before: &str, ra_fixture_after: &str) { let after = trim_indent(ra_fixture_after); @@ -344,10 +220,10 @@ mod tests { assert_eq_text!(&after, &actual); assert!( - diagnostic.range.start() <= file_position.offset - && diagnostic.range.end() >= file_position.offset, - "diagnostic range {:?} does not touch cursor position {:?}", - diagnostic.range, + fix.fix_trigger_range.start() <= file_position.offset + && fix.fix_trigger_range.end() >= file_position.offset, + "diagnostic fix range {:?} does not touch cursor position {:?}", + fix.fix_trigger_range, file_position.offset ); } @@ -712,6 +588,7 @@ fn test_fn() { ], is_snippet: false, }, + fix_trigger_range: 0..8, }, ), }, diff --git a/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs new file mode 100644 index 0000000000..f7c73773f3 --- /dev/null +++ b/crates/ra_ide/src/diagnostics/diagnostics_with_fix.rs @@ -0,0 +1,171 @@ +//! Provides a way to attach fixes to the diagnostics. +//! The same module also has all curret custom fixes for the diagnostics implemented. +use crate::Fix; +use ast::{edit::IndentLevel, make}; +use hir::{ + db::AstDatabase, + diagnostics::{Diagnostic, MissingFields, MissingOkInTailExpr, NoSuchField, UnresolvedModule}, + HasSource, HirDisplay, Semantics, VariantDef, +}; +use ra_db::FileId; +use ra_ide_db::{ + source_change::{FileSystemEdit, SourceFileEdit}, + RootDatabase, +}; +use ra_syntax::{algo, ast, AstNode}; +use ra_text_edit::{TextEdit, TextEditBuilder}; + +/// A [Diagnostic] that potentially has a fix available. +/// +/// [Diagnostic]: hir::diagnostics::Diagnostic +pub trait DiagnosticWithFix: Diagnostic { + fn fix(&self, sema: &Semantics) -> Option; +} + +impl DiagnosticWithFix for UnresolvedModule { + fn fix(&self, sema: &Semantics) -> Option { + let root = sema.db.parse_or_expand(self.file)?; + let unresolved_module = self.decl.to_node(&root); + Some(Fix::new( + "Create module", + FileSystemEdit::CreateFile { + anchor: self.file.original_file(sema.db), + dst: self.candidate.clone(), + } + .into(), + unresolved_module.syntax().text_range(), + )) + } +} + +impl DiagnosticWithFix for NoSuchField { + fn fix(&self, sema: &Semantics) -> Option { + let root = sema.db.parse_or_expand(self.file)?; + missing_record_expr_field_fix( + &sema, + self.file.original_file(sema.db), + &self.field.to_node(&root), + ) + } +} + +impl DiagnosticWithFix for MissingFields { + fn fix(&self, sema: &Semantics) -> Option { + // Note that although we could add a diagnostics to + // fill the missing tuple field, e.g : + // `struct A(usize);` + // `let a = A { 0: () }` + // but it is uncommon usage and it should not be encouraged. + if self.missed_fields.iter().any(|it| it.as_tuple_index().is_some()) { + return None; + } + + let root = sema.db.parse_or_expand(self.file)?; + let old_field_list = self.field_list_parent.to_node(&root).record_expr_field_list()?; + let mut new_field_list = old_field_list.clone(); + for f in self.missed_fields.iter() { + let field = + make::record_expr_field(make::name_ref(&f.to_string()), Some(make::expr_unit())); + new_field_list = new_field_list.append_field(&field); + } + + let edit = { + let mut builder = TextEditBuilder::default(); + algo::diff(&old_field_list.syntax(), &new_field_list.syntax()) + .into_text_edit(&mut builder); + builder.finish() + }; + Some(Fix::new( + "Fill struct fields", + SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(), + sema.original_range(&old_field_list.syntax()).range, + )) + } +} + +impl DiagnosticWithFix for MissingOkInTailExpr { + fn fix(&self, sema: &Semantics) -> Option { + let root = sema.db.parse_or_expand(self.file)?; + let tail_expr = self.expr.to_node(&root); + let tail_expr_range = tail_expr.syntax().text_range(); + let edit = TextEdit::replace(tail_expr_range, format!("Ok({})", tail_expr.syntax())); + let source_change = + SourceFileEdit { file_id: self.file.original_file(sema.db), edit }.into(); + Some(Fix::new("Wrap with ok", source_change, tail_expr_range)) + } +} + +fn missing_record_expr_field_fix( + sema: &Semantics, + usage_file_id: FileId, + record_expr_field: &ast::RecordExprField, +) -> Option { + let record_lit = ast::RecordExpr::cast(record_expr_field.syntax().parent()?.parent()?)?; + let def_id = sema.resolve_variant(record_lit)?; + let module; + let def_file_id; + let record_fields = match VariantDef::from(def_id) { + VariantDef::Struct(s) => { + module = s.module(sema.db); + let source = s.source(sema.db); + def_file_id = source.file_id; + let fields = source.value.field_list()?; + record_field_list(fields)? + } + VariantDef::Union(u) => { + module = u.module(sema.db); + let source = u.source(sema.db); + def_file_id = source.file_id; + source.value.record_field_list()? + } + VariantDef::EnumVariant(e) => { + module = e.module(sema.db); + let source = e.source(sema.db); + def_file_id = source.file_id; + let fields = source.value.field_list()?; + record_field_list(fields)? + } + }; + let def_file_id = def_file_id.original_file(sema.db); + + let new_field_type = sema.type_of_expr(&record_expr_field.expr()?)?; + if new_field_type.is_unknown() { + return None; + } + let new_field = make::record_field( + record_expr_field.field_name()?, + make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), + ); + + let last_field = record_fields.fields().last()?; + let last_field_syntax = last_field.syntax(); + let indent = IndentLevel::from_node(last_field_syntax); + + let mut new_field = new_field.to_string(); + if usage_file_id != def_file_id { + new_field = format!("pub(crate) {}", new_field); + } + new_field = format!("\n{}{}", indent, new_field); + + let needs_comma = !last_field_syntax.to_string().ends_with(','); + if needs_comma { + new_field = format!(",{}", new_field); + } + + let source_change = SourceFileEdit { + file_id: def_file_id, + edit: TextEdit::insert(last_field_syntax.text_range().end(), new_field), + }; + return Some(Fix::new( + "Create field", + source_change.into(), + record_expr_field.syntax().text_range(), + )); + + fn record_field_list(field_def_list: ast::FieldList) -> Option { + match field_def_list { + ast::FieldList::RecordFieldList(it) => Some(it), + ast::FieldList::TupleFieldList(_) => None, + } + } +} diff --git a/crates/ra_ide/src/display/short_label.rs b/crates/ra_ide/src/display/short_label.rs index 0fdf8e9a58..010c34705c 100644 --- a/crates/ra_ide/src/display/short_label.rs +++ b/crates/ra_ide/src/display/short_label.rs @@ -47,6 +47,12 @@ impl ShortLabel for ast::Module { } } +impl ShortLabel for ast::SourceFile { + fn short_label(&self) -> Option { + None + } +} + impl ShortLabel for ast::TypeAlias { fn short_label(&self) -> Option { short_label_from_node(self, "type ") @@ -55,7 +61,11 @@ impl ShortLabel for ast::TypeAlias { impl ShortLabel for ast::Const { fn short_label(&self) -> Option { - short_label_from_ty(self, self.ty(), "const ") + let mut new_buf = short_label_from_ty(self, self.ty(), "const ")?; + if let Some(expr) = self.body() { + format_to!(new_buf, " = {}", expr.syntax()); + } + Some(new_buf) } } diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 4e3f428fae..45389fd23f 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -1,6 +1,6 @@ use hir::Semantics; use ra_ide_db::{ - defs::{classify_name, classify_name_ref, NameClass}, + defs::{classify_name, classify_name_ref}, symbol_index, RootDatabase, }; use ra_syntax::{ @@ -40,10 +40,7 @@ pub(crate) fn goto_definition( reference_definition(&sema, &name_ref).to_vec() }, ast::Name(name) => { - let def = match classify_name(&sema, &name)? { - NameClass::Definition(def) | NameClass::ConstReference(def) => def, - NameClass::FieldShorthand { local: _, field } => field, - }; + let def = classify_name(&sema, &name)?.definition(sema.db); let nav = def.try_to_nav(sema.db)?; vec![nav] }, @@ -86,8 +83,7 @@ pub(crate) fn reference_definition( ) -> ReferenceResult { let name_kind = classify_name_ref(sema, name_ref); if let Some(def) = name_kind { - let def = def.definition(); - + let def = def.definition(sema.db); return match def.try_to_nav(sema.db) { Some(nav) => ReferenceResult::Exact(nav), None => ReferenceResult::Approximate(Vec::new()), @@ -133,6 +129,32 @@ mod tests { assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); } + #[test] + fn goto_def_for_extern_crate() { + check( + r#" + //- /main.rs + extern crate std<|>; + //- /std/lib.rs + // empty + //^ file + "#, + ) + } + + #[test] + fn goto_def_for_renamed_extern_crate() { + check( + r#" + //- /main.rs + extern crate std as abc<|>; + //- /std/lib.rs + // empty + //^ file + "#, + ) + } + #[test] fn goto_def_in_items() { check( diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index aa48cb412f..f66f62bfb5 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs @@ -85,8 +85,8 @@ pub(crate) fn hover(db: &RootDatabase, position: FilePosition) -> Option classify_name_ref(&sema, &name_ref).map(|d| d.definition()), - ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition()), + ast::NameRef(name_ref) => classify_name_ref(&sema, &name_ref).map(|d| d.definition(sema.db)), + ast::Name(name) => classify_name(&sema, &name).map(|d| d.definition(sema.db)), _ => None, } }; @@ -304,7 +304,10 @@ fn hover_for_definition(db: &RootDatabase, def: Definition) -> Option { let docs = Documentation::from_ast(&it).map(Into::into); hover_markup(docs, it.short_label(), mod_path) } - _ => None, + ModuleSource::SourceFile(it) => { + let docs = Documentation::from_ast(&it).map(Into::into); + hover_markup(docs, it.short_label(), mod_path) + } }, ModuleDef::Function(it) => from_def_source(db, it, mod_path), ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path), @@ -508,6 +511,37 @@ fn main() { } ); } + #[test] + fn hover_shows_fn_doc() { + check( + r#" +/// # Example +/// ``` +/// # use std::path::Path; +/// # +/// foo(Path::new("hello, world!")) +/// ``` +pub fn foo<|>(_: &Path) {} + +fn main() { } +"#, + expect![[r#" + *foo* + ```rust + pub fn foo(_: &Path) + ``` + ___ + + # Example + ``` + # use std::path::Path; + # + foo(Path::new("hello, world!")) + ``` + "#]], + ); + } + #[test] fn hover_shows_struct_field_info() { // Hovering over the field when instantiating @@ -556,16 +590,16 @@ fn main() { #[test] fn hover_const_static() { check( - r#"const foo<|>: u32 = 0;"#, + r#"const foo<|>: u32 = 123;"#, expect![[r#" *foo* ```rust - const foo: u32 + const foo: u32 = 123 ``` "#]], ); check( - r#"static foo<|>: u32 = 0;"#, + r#"static foo<|>: u32 = 456;"#, expect![[r#" *foo* ```rust @@ -800,7 +834,7 @@ fn main() { expect![[r#" *C* ```rust - const C: u32 + const C: u32 = 1 ``` "#]], ) @@ -1106,6 +1140,46 @@ fn bar() { fo<|>o(); } ); } + #[test] + fn test_hover_extern_crate() { + check( + r#" +//- /main.rs +extern crate st<|>d; +//- /std/lib.rs +//! Standard library for this test +//! +//! Printed? +//! abc123 + "#, + expect![[r#" + *std* + Standard library for this test + + Printed? + abc123 + "#]], + ); + check( + r#" +//- /main.rs +extern crate std as ab<|>c; +//- /std/lib.rs +//! Standard library for this test +//! +//! Printed? +//! abc123 + "#, + expect![[r#" + *abc* + Standard library for this test + + Printed? + abc123 + "#]], + ); + } + #[test] fn test_hover_mod_with_same_name_as_function() { check( diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 3822b9409d..2cbd7e4f08 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs @@ -119,13 +119,19 @@ pub struct Diagnostic { pub struct Fix { pub label: String, pub source_change: SourceChange, + /// Allows to trigger the fix only when the caret is in the range given + pub fix_trigger_range: TextRange, } impl Fix { - pub fn new(label: impl Into, source_change: SourceChange) -> Self { + pub fn new( + label: impl Into, + source_change: SourceChange, + fix_trigger_range: TextRange, + ) -> Self { let label = label.into(); assert!(label.starts_with(char::is_uppercase) && !label.ends_with('.')); - Self { label, source_change } + Self { label, source_change, fix_trigger_range } } } diff --git a/crates/ra_ide/src/references.rs b/crates/ra_ide/src/references.rs index cf456630a5..453985de3e 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs @@ -130,13 +130,13 @@ fn find_name( opt_name: Option, ) -> Option> { if let Some(name) = opt_name { - let def = classify_name(sema, &name)?.definition(); + let def = classify_name(sema, &name)?.definition(sema.db); let range = name.syntax().text_range(); return Some(RangeInfo::new(range, def)); } let name_ref = sema.find_node_at_offset_with_descend::(&syntax, position.offset)?; - let def = classify_name_ref(sema, &name_ref)?.definition(); + let def = classify_name_ref(sema, &name_ref)?.definition(sema.db); let range = name_ref.syntax().text_range(); Some(RangeInfo::new(range, def)) } diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs index 4348b43beb..8be862fd6e 100644 --- a/crates/ra_ide/src/ssr.rs +++ b/crates/ra_ide/src/ssr.rs @@ -21,8 +21,8 @@ use ra_ssr::{MatchFinder, SsrError, SsrRule}; // replacement occurs. For example if our replacement template is `foo::Bar` and we match some // code in the `foo` module, we'll insert just `Bar`. // -// Method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will match -// `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`. +// Inherent method calls should generally be written in UFCS form. e.g. `foo::Bar::baz($s, $a)` will +// match `$s.baz($a)`, provided the method call `baz` resolves to the method `foo::Bar::baz`. // // The scope of the search / replace will be restricted to the current selection if any, otherwise // it will apply to the whole workspace. diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index a32ae0165e..c10e15db8b 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs @@ -4,7 +4,7 @@ mod injection; #[cfg(test)] mod tests; -use hir::{Name, Semantics}; +use hir::{Name, Semantics, VariantDef}; use ra_ide_db::{ defs::{classify_name, classify_name_ref, Definition, NameClass, NameRefClass}, RootDatabase, @@ -455,6 +455,18 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option { Some(TextRange::new(range_start, range_end)) } +fn is_possibly_unsafe(name_ref: &ast::NameRef) -> bool { + name_ref + .syntax() + .parent() + .and_then(|parent| { + ast::FieldExpr::cast(parent.clone()) + .map(|_| true) + .or_else(|| ast::RecordPatField::cast(parent).map(|_| true)) + }) + .unwrap_or(false) +} + fn highlight_element( sema: &Semantics, bindings_shadow_count: &mut FxHashMap, @@ -483,11 +495,21 @@ fn highlight_element( }; match name_kind { + Some(NameClass::ExternCrate(_)) => HighlightTag::Module.into(), Some(NameClass::Definition(def)) => { - highlight_name(db, def) | HighlightModifier::Definition + highlight_name(sema, db, def, None, false) | HighlightModifier::Definition + } + Some(NameClass::ConstReference(def)) => highlight_name(sema, db, def, None, false), + Some(NameClass::FieldShorthand { field, .. }) => { + let mut h = HighlightTag::Field.into(); + if let Definition::Field(field) = field { + if let VariantDef::Union(_) = field.parent_def(db) { + h |= HighlightModifier::Unsafe; + } + } + + h } - Some(NameClass::ConstReference(def)) => highlight_name(db, def), - Some(NameClass::FieldShorthand { .. }) => HighlightTag::Field.into(), None => highlight_name_by_syntax(name) | HighlightModifier::Definition, } } @@ -498,8 +520,10 @@ fn highlight_element( } NAME_REF => { let name_ref = element.into_node().and_then(ast::NameRef::cast).unwrap(); + let possibly_unsafe = is_possibly_unsafe(&name_ref); match classify_name_ref(sema, &name_ref) { Some(name_kind) => match name_kind { + NameRefClass::ExternCrate(_) => HighlightTag::Module.into(), NameRefClass::Definition(def) => { if let Definition::Local(local) = &def { if let Some(name) = local.name(db) { @@ -508,11 +532,13 @@ fn highlight_element( binding_hash = Some(calc_binding_hash(&name, *shadow_count)) } }; - highlight_name(db, def) + highlight_name(sema, db, def, Some(name_ref), possibly_unsafe) } NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), }, - None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref), + None if syntactic_name_ref_highlighting => { + highlight_name_ref_by_syntax(name_ref, sema) + } None => HighlightTag::UnresolvedReference.into(), } } @@ -540,9 +566,20 @@ fn highlight_element( } } p if p.is_punct() => match p { - T![::] | T![->] | T![=>] | T![&] | T![..] | T![=] | T![@] => { - HighlightTag::Operator.into() + T![&] => { + let h = HighlightTag::Operator.into(); + let is_unsafe = element + .parent() + .and_then(ast::RefExpr::cast) + .map(|ref_expr| sema.is_unsafe_ref_expr(&ref_expr)) + .unwrap_or(false); + if is_unsafe { + h | HighlightModifier::Unsafe + } else { + h + } } + T![::] | T![->] | T![=>] | T![..] | T![=] | T![@] => HighlightTag::Operator.into(), T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => { HighlightTag::Macro.into() } @@ -623,6 +660,18 @@ fn highlight_element( HighlightTag::SelfKeyword.into() } } + T![ref] => element + .parent() + .and_then(ast::IdentPat::cast) + .and_then(|ident_pat| { + if sema.is_unsafe_ident_pat(&ident_pat) { + Some(HighlightModifier::Unsafe) + } else { + None + } + }) + .map(|modifier| h | modifier) + .unwrap_or(h), _ => h, } } @@ -652,16 +701,40 @@ fn is_child_of_impl(element: &SyntaxElement) -> bool { } } -fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { +fn highlight_name( + sema: &Semantics, + db: &RootDatabase, + def: Definition, + name_ref: Option, + possibly_unsafe: bool, +) -> Highlight { match def { Definition::Macro(_) => HighlightTag::Macro, - Definition::Field(_) => HighlightTag::Field, + Definition::Field(field) => { + let mut h = HighlightTag::Field.into(); + if possibly_unsafe { + if let VariantDef::Union(_) = field.parent_def(db) { + h |= HighlightModifier::Unsafe; + } + } + + return h; + } Definition::ModuleDef(def) => match def { hir::ModuleDef::Module(_) => HighlightTag::Module, hir::ModuleDef::Function(func) => { let mut h = HighlightTag::Function.into(); if func.is_unsafe(db) { h |= HighlightModifier::Unsafe; + } else { + let is_unsafe = name_ref + .and_then(|name_ref| name_ref.syntax().parent()) + .and_then(ast::MethodCallExpr::cast) + .map(|method_call_expr| sema.is_unsafe_method_call(method_call_expr)) + .unwrap_or(false); + if is_unsafe { + h |= HighlightModifier::Unsafe; + } } return h; } @@ -677,6 +750,7 @@ fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { let mut h = Highlight::new(HighlightTag::Static); if s.is_mut(db) { h |= HighlightModifier::Mutable; + h |= HighlightModifier::Unsafe; } return h; } @@ -724,7 +798,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { tag.into() } -fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight { +fn highlight_name_ref_by_syntax(name: ast::NameRef, sema: &Semantics) -> Highlight { let default = HighlightTag::UnresolvedReference; let parent = match name.syntax().parent() { @@ -732,9 +806,36 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight { _ => return default.into(), }; - let tag = match parent.kind() { - METHOD_CALL_EXPR => HighlightTag::Function, - FIELD_EXPR => HighlightTag::Field, + match parent.kind() { + METHOD_CALL_EXPR => { + let mut h = Highlight::new(HighlightTag::Function); + let is_unsafe = ast::MethodCallExpr::cast(parent) + .map(|method_call_expr| sema.is_unsafe_method_call(method_call_expr)) + .unwrap_or(false); + if is_unsafe { + h |= HighlightModifier::Unsafe; + } + + h + } + FIELD_EXPR => { + let h = HighlightTag::Field; + let is_union = ast::FieldExpr::cast(parent) + .and_then(|field_expr| { + let field = sema.resolve_field(&field_expr)?; + Some(if let VariantDef::Union(_) = field.parent_def(sema.db) { + true + } else { + false + }) + }) + .unwrap_or(false); + if is_union { + h | HighlightModifier::Unsafe + } else { + h.into() + } + } PATH_SEGMENT => { let path = match parent.parent().and_then(ast::Path::cast) { Some(it) => it, @@ -758,18 +859,15 @@ fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight { }; match parent.kind() { - CALL_EXPR => HighlightTag::Function, - _ => { - if name.text().chars().next().unwrap_or_default().is_uppercase() { - HighlightTag::Struct - } else { - HighlightTag::Constant - } + CALL_EXPR => HighlightTag::Function.into(), + _ => if name.text().chars().next().unwrap_or_default().is_uppercase() { + HighlightTag::Struct.into() + } else { + HighlightTag::Constant } + .into(), } } - _ => default, - }; - - tag.into() + _ => default.into(), + } } diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs index 8665b480fd..6046643ef1 100644 --- a/crates/ra_ide/src/syntax_highlighting/injection.rs +++ b/crates/ra_ide/src/syntax_highlighting/injection.rs @@ -4,8 +4,8 @@ use std::{collections::BTreeMap, convert::TryFrom}; use ast::{HasQuotes, HasStringValue}; use hir::Semantics; +use itertools::Itertools; use ra_syntax::{ast, AstToken, SyntaxNode, SyntaxToken, TextRange, TextSize}; -use stdx::SepBy; use crate::{ call_info::ActiveParameter, Analysis, Highlight, HighlightModifier, HighlightTag, @@ -129,8 +129,7 @@ pub(super) fn extract_doc_comments( line[pos..].to_owned() }) - .sep_by("\n") - .to_string(); + .join("\n"); if doctest.is_empty() { return None; diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 2deee404cc..a8087635a8 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs @@ -275,19 +275,64 @@ fn test_unsafe_highlighting() { r#" unsafe fn unsafe_fn() {} +union Union { + a: u32, + b: f32, +} + struct HasUnsafeFn; impl HasUnsafeFn { unsafe fn unsafe_method(&self) {} } +struct TypeForStaticMut { + a: u8 +} + +static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 }; + +#[repr(packed)] +struct Packed { + a: u16, +} + +trait DoTheAutoref { + fn calls_autoref(&self); +} + +impl DoTheAutoref for u16 { + fn calls_autoref(&self) {} +} + fn main() { - let x = &5 as *const usize; + let x = &5 as *const _ as *const usize; + let u = Union { b: 0 }; unsafe { + // unsafe fn and method calls unsafe_fn(); + let b = u.b; + match u { + Union { b: 0 } => (), + Union { a } => (), + } HasUnsafeFn.unsafe_method(); - let y = *(x); - let z = -x; + + // unsafe deref + let y = *x; + + // unsafe access to a static mut + let a = global_mut.a; + + // unsafe ref of packed fields + let packed = Packed { a: 0 }; + let a = &packed.a; + let ref a = packed.a; + let Packed { ref a } = packed; + let Packed { a: ref _a } = packed; + + // unsafe auto ref of packed field + packed.a.calls_autoref(); } } "# @@ -373,6 +418,23 @@ macro_rules! noop { ); } +#[test] +fn test_extern_crate() { + check_highlighting( + r#" + //- /main.rs + extern crate std; + extern crate alloc as abc; + //- /std/lib.rs + pub struct S; + //- /alloc/lib.rs + pub struct A + "#, + expect_file!["crates/ra_ide/test_data/highlight_extern_crate.html"], + false, + ); +} + /// Highlights the code given by the `ra_fixture` argument, renders the /// result as HTML, and compares it with the HTML file given as `snapshot`. /// Note that the `snapshot` file is overwritten by the rendered HTML. diff --git a/crates/ra_ide/test_data/highlight_extern_crate.html b/crates/ra_ide/test_data/highlight_extern_crate.html new file mode 100644 index 0000000000..800d894c76 --- /dev/null +++ b/crates/ra_ide/test_data/highlight_extern_crate.html @@ -0,0 +1,40 @@ + + +
extern crate std;
+extern crate alloc as abc;
+
\ No newline at end of file diff --git a/crates/ra_ide/test_data/highlight_unsafe.html b/crates/ra_ide/test_data/highlight_unsafe.html index b81b6f1c3b..552fea6689 100644 --- a/crates/ra_ide/test_data/highlight_unsafe.html +++ b/crates/ra_ide/test_data/highlight_unsafe.html @@ -37,18 +37,63 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
unsafe fn unsafe_fn() {}
 
+union Union {
+    a: u32,
+    b: f32,
+}
+
 struct HasUnsafeFn;
 
 impl HasUnsafeFn {
     unsafe fn unsafe_method(&self) {}
 }
 
+struct TypeForStaticMut {
+    a: u8
+}
+
+static mut global_mut: TypeForStaticMut = TypeForStaticMut { a: 0 };
+
+#[repr(packed)]
+struct Packed {
+    a: u16,
+}
+
+trait DoTheAutoref {
+    fn calls_autoref(&self);
+}
+
+impl DoTheAutoref for u16 {
+    fn calls_autoref(&self) {}
+}
+
 fn main() {
-    let x = &5 as *const usize;
+    let x = &5 as *const _ as *const usize;
+    let u = Union { b: 0 };
     unsafe {
+        // unsafe fn and method calls
         unsafe_fn();
+        let b = u.b;
+        match u {
+            Union { b: 0 } => (),
+            Union { a } => (),
+        }
         HasUnsafeFn.unsafe_method();
-        let y = *(x);
-        let z = -x;
+
+        // unsafe deref
+        let y = *x;
+
+        // unsafe access to a static mut
+        let a = global_mut.a;
+
+        // unsafe ref of packed fields
+        let packed = Packed { a: 0 };
+        let a = &packed.a;
+        let ref a = packed.a;
+        let Packed { ref a } = packed;
+        let Packed { a: ref _a } = packed;
+
+        // unsafe auto ref of packed field
+        packed.a.calls_autoref();
     }
 }
\ No newline at end of file diff --git a/crates/ra_ide/test_data/highlighting.html b/crates/ra_ide/test_data/highlighting.html index 23c25ad8ce..8e0160eee5 100644 --- a/crates/ra_ide/test_data/highlighting.html +++ b/crates/ra_ide/test_data/highlighting.html @@ -64,7 +64,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } } -static mut STATIC_MUT: i32 = 0; +static mut STATIC_MUT: i32 = 0; fn foo<'a, T>() -> T { foo::<'a, i32>() @@ -97,7 +97,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } unsafe { vec.set_len(0); - STATIC_MUT = 1; + STATIC_MUT = 1; } for e in vec { diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index b51000b03f..9bb95277d0 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs @@ -6,8 +6,8 @@ // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). use hir::{ - Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution, - Semantics, TypeParam, Visibility, + db::HirDatabase, Crate, Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, + Name, PathResolution, Semantics, TypeParam, Visibility, }; use ra_prof::profile; use ra_syntax::{ @@ -80,6 +80,7 @@ impl Definition { #[derive(Debug)] pub enum NameClass { + ExternCrate(Crate), Definition(Definition), /// `None` in `if let None = Some(82) {}` ConstReference(Definition), @@ -90,16 +91,18 @@ pub enum NameClass { } impl NameClass { - pub fn into_definition(self) -> Option { - match self { - NameClass::Definition(it) => Some(it), - NameClass::ConstReference(_) => None, - NameClass::FieldShorthand { local, field: _ } => Some(Definition::Local(local)), - } + pub fn into_definition(self, db: &dyn HirDatabase) -> Option { + Some(match self { + NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), + NameClass::Definition(it) => it, + NameClass::ConstReference(_) => return None, + NameClass::FieldShorthand { local, field: _ } => Definition::Local(local), + }) } - pub fn definition(self) -> Definition { + pub fn definition(self, db: &dyn HirDatabase) -> Definition { match self { + NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), NameClass::Definition(it) | NameClass::ConstReference(it) => it, NameClass::FieldShorthand { local: _, field } => field, } @@ -120,32 +123,37 @@ pub fn classify_name(sema: &Semantics, name: &ast::Name) -> Option match_ast! { match parent { ast::Rename(it) => { - let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?; - let path = use_tree.path()?; - let path_segment = path.segment()?; - let name_ref_class = path_segment - .name_ref() - // The rename might be from a `self` token, so fallback to the name higher - // in the use tree. - .or_else(||{ - if path_segment.self_token().is_none() { - return None; - } + if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) { + let path = use_tree.path()?; + let path_segment = path.segment()?; + let name_ref_class = path_segment + .name_ref() + // The rename might be from a `self` token, so fallback to the name higher + // in the use tree. + .or_else(||{ + if path_segment.self_token().is_none() { + return None; + } - let use_tree = use_tree - .syntax() - .parent() - .as_ref() - // Skip over UseTreeList - .and_then(SyntaxNode::parent) - .and_then(ast::UseTree::cast)?; - let path = use_tree.path()?; - let path_segment = path.segment()?; - path_segment.name_ref() - }) - .and_then(|name_ref| classify_name_ref(sema, &name_ref))?; + let use_tree = use_tree + .syntax() + .parent() + .as_ref() + // Skip over UseTreeList + .and_then(SyntaxNode::parent) + .and_then(ast::UseTree::cast)?; + let path = use_tree.path()?; + let path_segment = path.segment()?; + path_segment.name_ref() + }) + .and_then(|name_ref| classify_name_ref(sema, &name_ref))?; - Some(NameClass::Definition(name_ref_class.definition())) + Some(NameClass::Definition(name_ref_class.definition(sema.db))) + } else { + let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?; + let resolved = sema.resolve_extern_crate(&extern_crate)?; + Some(NameClass::ExternCrate(resolved)) + } }, ast::IdentPat(it) => { let local = sema.to_def(&it)?; @@ -220,13 +228,15 @@ pub fn classify_name(sema: &Semantics, name: &ast::Name) -> Option #[derive(Debug)] pub enum NameRefClass { + ExternCrate(Crate), Definition(Definition), FieldShorthand { local: Local, field: Definition }, } impl NameRefClass { - pub fn definition(self) -> Definition { + pub fn definition(self, db: &dyn HirDatabase) -> Definition { match self { + NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()), NameRefClass::Definition(def) => def, NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local), } @@ -307,9 +317,15 @@ pub fn classify_name_ref( } } - let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; - let resolved = sema.resolve_path(&path)?; - Some(NameRefClass::Definition(resolved.into())) + if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { + if let Some(resolved) = sema.resolve_path(&path) { + return Some(NameRefClass::Definition(resolved.into())); + } + } + + let extern_crate = ast::ExternCrate::cast(parent)?; + let resolved = sema.resolve_extern_crate(&extern_crate)?; + Some(NameRefClass::ExternCrate(resolved)) } impl From for Definition { diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs index 1fba71ff85..9e040973b3 100644 --- a/crates/ra_ide_db/src/imports_locator.rs +++ b/crates/ra_ide_db/src/imports_locator.rs @@ -61,5 +61,5 @@ fn get_name_definition<'a>( candidate_node }; let name = ast::Name::cast(candidate_name_node)?; - classify_name(sema, &name)?.into_definition() + classify_name(sema, &name)?.into_definition(sema.db) } diff --git a/crates/ra_mbe/src/mbe_expander/matcher.rs b/crates/ra_mbe/src/mbe_expander/matcher.rs index f9e515b811..933a3a3b5e 100644 --- a/crates/ra_mbe/src/mbe_expander/matcher.rs +++ b/crates/ra_mbe/src/mbe_expander/matcher.rs @@ -276,7 +276,7 @@ impl<'a> TtIter<'a> { Ok(tt::Subtree { delimiter: None, token_trees: vec![ - tt::Leaf::Punct(punct.clone()).into(), + tt::Leaf::Punct(*punct).into(), tt::Leaf::Ident(ident.clone()).into(), ], } diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index c2e1d701e2..88468bc971 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs @@ -110,7 +110,7 @@ pub(crate) mod fragments { } pub(crate) fn item(p: &mut Parser) { - items::item_or_macro(p, true, items::ItemFlavor::Mod) + items::item_or_macro(p, true) } pub(crate) fn macro_items(p: &mut Parser) { diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs index e1c25a838f..3291e3f146 100644 --- a/crates/ra_parser/src/grammar/expressions.rs +++ b/crates/ra_parser/src/grammar/expressions.rs @@ -73,7 +73,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) { // test block_items // fn a() { fn b() {} } - let m = match items::maybe_item(p, m, items::ItemFlavor::Mod) { + let m = match items::maybe_item(p, m) { Ok(()) => return, Err(m) => m, }; @@ -509,7 +509,6 @@ fn method_call_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { // x.1i32; // x.0x01; // } -#[allow(clippy::if_same_then_else)] fn field_expr(p: &mut Parser, lhs: CompletedMarker) -> CompletedMarker { assert!(p.at(T![.])); let m = lhs.precede(p); diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index cca524ceaa..d091b0fbb2 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -22,24 +22,19 @@ use super::*; pub(super) fn mod_contents(p: &mut Parser, stop_on_r_curly: bool) { attributes::inner_attributes(p); while !(stop_on_r_curly && p.at(T!['}']) || p.at(EOF)) { - item_or_macro(p, stop_on_r_curly, ItemFlavor::Mod) + item_or_macro(p, stop_on_r_curly) } } -pub(super) enum ItemFlavor { - Mod, - Trait, -} - pub(super) const ITEM_RECOVERY_SET: TokenSet = token_set![ FN_KW, STRUCT_KW, ENUM_KW, IMPL_KW, TRAIT_KW, CONST_KW, STATIC_KW, LET_KW, MOD_KW, PUB_KW, CRATE_KW, USE_KW, MACRO_KW ]; -pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemFlavor) { +pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool) { let m = p.start(); attributes::outer_attributes(p); - let m = match maybe_item(p, m, flavor) { + let m = match maybe_item(p, m) { Ok(()) => { if p.at(T![;]) { p.err_and_bump( @@ -76,7 +71,7 @@ pub(super) fn item_or_macro(p: &mut Parser, stop_on_r_curly: bool, flavor: ItemF } } -pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Result<(), Marker> { +pub(super) fn maybe_item(p: &mut Parser, m: Marker) -> Result<(), Marker> { // test_err pub_expr // fn foo() { pub 92; } let has_visibility = opt_visibility(p); @@ -114,38 +109,31 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul has_mods = true; } - if p.at(IDENT) - && p.at_contextual_kw("default") - && (match p.nth(1) { - T![impl] => true, + // test default_item + // default impl T for Foo {} + if p.at(IDENT) && p.at_contextual_kw("default") { + match p.nth(1) { + T![fn] | T![type] | T![const] | T![impl] => { + p.bump_remap(T![default]); + has_mods = true; + } T![unsafe] => { - // test default_unsafe_impl - // default unsafe impl Foo {} - - // test default_unsafe_fn - // impl T for Foo { + // test default_unsafe_item + // default unsafe impl T for Foo { // default unsafe fn foo() {} // } - if p.nth(2) == T![impl] || p.nth(2) == T![fn] { + if matches!(p.nth(2), T![impl] | T![fn]) { p.bump_remap(T![default]); p.bump(T![unsafe]); has_mods = true; } - false } - T![fn] | T![type] | T![const] => { - if let ItemFlavor::Mod = flavor { - true - } else { - false - } - } - _ => false, - }) - { - p.bump_remap(T![default]); - has_mods = true; + _ => (), + } } + + // test existential_type + // existential type Foo: Fn() -> usize; if p.at(IDENT) && p.at_contextual_kw("existential") && p.nth(1) == T![type] { p.bump_remap(T![existential]); has_mods = true; @@ -153,79 +141,31 @@ pub(super) fn maybe_item(p: &mut Parser, m: Marker, flavor: ItemFlavor) -> Resul // items match p.current() { - // test async_fn - // async fn foo() {} - - // test extern_fn - // extern fn foo() {} - - // test const_fn - // const fn foo() {} - - // test const_unsafe_fn - // const unsafe fn foo() {} - - // test unsafe_extern_fn - // unsafe extern "C" fn foo() {} - - // test unsafe_fn - // unsafe fn foo() {} - - // test combined_fns - // async unsafe fn foo() {} - // const unsafe fn bar() {} - - // test_err wrong_order_fns - // unsafe async fn foo() {} - // unsafe const fn bar() {} + // test fn + // fn foo() {} T![fn] => { fn_def(p); m.complete(p, FN); } - // test unsafe_trait - // unsafe trait T {} - - // test auto_trait - // auto trait T {} - - // test unsafe_auto_trait - // unsafe auto trait T {} + // test trait + // trait T {} T![trait] => { traits::trait_def(p); m.complete(p, TRAIT); } - // test unsafe_impl - // unsafe impl Foo {} - - // test default_impl - // default impl Foo {} - - // test_err default_fn_type - // trait T { - // default type T = Bar; - // default fn foo() {} - // } - - // test default_fn_type - // impl T for Foo { - // default type T = Bar; - // default fn foo() {} - // } T![const] => { consts::const_def(p, m); } - // test unsafe_default_impl - // unsafe default impl Foo {} + // test impl + // impl T for S {} T![impl] => { traits::impl_def(p); m.complete(p, IMPL); } - // test existential_type - // existential type Foo: Fn() -> usize; T![type] => { type_def(p, m); } diff --git a/crates/ra_parser/src/grammar/items/traits.rs b/crates/ra_parser/src/grammar/items/traits.rs index ef9c8ff5b0..751ce65f2d 100644 --- a/crates/ra_parser/src/grammar/items/traits.rs +++ b/crates/ra_parser/src/grammar/items/traits.rs @@ -47,7 +47,7 @@ pub(crate) fn trait_item_list(p: &mut Parser) { error_block(p, "expected an item"); continue; } - item_or_macro(p, true, ItemFlavor::Trait); + item_or_macro(p, true); } p.expect(T!['}']); m.complete(p, ASSOC_ITEM_LIST); @@ -104,7 +104,7 @@ pub(crate) fn impl_item_list(p: &mut Parser) { error_block(p, "expected an item"); continue; } - item_or_macro(p, true, ItemFlavor::Mod); + item_or_macro(p, true); } p.expect(T!['}']); m.complete(p, ASSOC_ITEM_LIST); diff --git a/crates/ra_parser/src/parser.rs b/crates/ra_parser/src/parser.rs index d797f2cc96..d2487acc3b 100644 --- a/crates/ra_parser/src/parser.rs +++ b/crates/ra_parser/src/parser.rs @@ -269,8 +269,8 @@ impl Marker { pub(crate) fn complete(mut self, p: &mut Parser, kind: SyntaxKind) -> CompletedMarker { self.bomb.defuse(); let idx = self.pos as usize; - match p.events[idx] { - Event::Start { kind: ref mut slot, .. } => { + match &mut p.events[idx] { + Event::Start { kind: slot, .. } => { *slot = kind; } _ => unreachable!(), @@ -320,8 +320,8 @@ impl CompletedMarker { pub(crate) fn precede(self, p: &mut Parser) -> Marker { let new_pos = p.start(); let idx = self.start_pos as usize; - match p.events[idx] { - Event::Start { ref mut forward_parent, .. } => { + match &mut p.events[idx] { + Event::Start { forward_parent, .. } => { *forward_parent = Some(new_pos.pos - self.start_pos); } _ => unreachable!(), @@ -333,12 +333,12 @@ impl CompletedMarker { pub(crate) fn undo_completion(self, p: &mut Parser) -> Marker { let start_idx = self.start_pos as usize; let finish_idx = self.finish_pos as usize; - match p.events[start_idx] { - Event::Start { ref mut kind, forward_parent: None } => *kind = TOMBSTONE, + match &mut p.events[start_idx] { + Event::Start { kind, forward_parent: None } => *kind = TOMBSTONE, _ => unreachable!(), } - match p.events[finish_idx] { - ref mut slot @ Event::Finish => *slot = Event::tombstone(), + match &mut p.events[finish_idx] { + slot @ Event::Finish => *slot = Event::tombstone(), _ => unreachable!(), } Marker::new(self.start_pos) diff --git a/crates/ra_proc_macro/src/process.rs b/crates/ra_proc_macro/src/process.rs index 5bcdacb487..37dd3f4965 100644 --- a/crates/ra_proc_macro/src/process.rs +++ b/crates/ra_proc_macro/src/process.rs @@ -90,7 +90,7 @@ impl ProcMacroProcessSrv { } Some(it) => it, }; - sender.send(Task { req: req.into(), result_tx }).unwrap(); + sender.send(Task { req, result_tx }).unwrap(); let res = result_rx .recv() .map_err(|_| ra_tt::ExpansionError::Unknown("Proc macro thread is closed.".into()))?; diff --git a/crates/ra_prof/src/memory_usage.rs b/crates/ra_prof/src/memory_usage.rs index c2ecbd33cf..83390212ae 100644 --- a/crates/ra_prof/src/memory_usage.rs +++ b/crates/ra_prof/src/memory_usage.rs @@ -24,7 +24,7 @@ impl std::ops::Sub for MemoryUsage { impl MemoryUsage { pub fn current() -> MemoryUsage { cfg_if! { - if #[cfg(target_os = "linux")] { + if #[cfg(all(target_os = "linux", target_env = "gnu"))] { // Note: This is incredibly slow. let alloc = unsafe { libc::mallinfo() }.uordblks as isize; MemoryUsage { allocated: Bytes(alloc) } diff --git a/crates/ra_ssr/src/resolving.rs b/crates/ra_ssr/src/resolving.rs index df60048eb2..d53bd46c77 100644 --- a/crates/ra_ssr/src/resolving.rs +++ b/crates/ra_ssr/src/resolving.rs @@ -5,7 +5,7 @@ use crate::{parsing, SsrError}; use parsing::Placeholder; use ra_db::FilePosition; use ra_syntax::{ast, SmolStr, SyntaxKind, SyntaxNode, SyntaxToken}; -use rustc_hash::{FxHashMap, FxHashSet}; +use rustc_hash::FxHashMap; use test_utils::mark; pub(crate) struct ResolutionScope<'db> { @@ -124,8 +124,10 @@ impl Resolver<'_, '_> { .resolution_scope .resolve_path(&path) .ok_or_else(|| error!("Failed to resolve path `{}`", node.text()))?; - resolved_paths.insert(node, ResolvedPath { resolution, depth }); - return Ok(()); + if self.ok_to_use_path_resolution(&resolution) { + resolved_paths.insert(node, ResolvedPath { resolution, depth }); + return Ok(()); + } } } for node in node.children() { @@ -149,6 +151,27 @@ impl Resolver<'_, '_> { } false } + + fn ok_to_use_path_resolution(&self, resolution: &hir::PathResolution) -> bool { + match resolution { + hir::PathResolution::AssocItem(hir::AssocItem::Function(function)) => { + if function.has_self_param(self.resolution_scope.scope.db) { + // If we don't use this path resolution, then we won't be able to match method + // calls. e.g. `Foo::bar($s)` should match `x.bar()`. + true + } else { + mark::hit!(replace_associated_trait_default_function_call); + false + } + } + hir::PathResolution::AssocItem(_) => { + // Not a function. Could be a constant or an associated type. + mark::hit!(replace_associated_trait_constant); + false + } + _ => true, + } + } } impl<'db> ResolutionScope<'db> { @@ -195,7 +218,7 @@ impl<'db> ResolutionScope<'db> { adt.ty(self.scope.db).iterate_path_candidates( self.scope.db, self.scope.module()?.krate(), - &FxHashSet::default(), + &self.scope.traits_in_scope(), Some(hir_path.segments().last()?.name), |_ty, assoc_item| Some(hir::PathResolution::AssocItem(assoc_item)), ) diff --git a/crates/ra_ssr/src/tests.rs b/crates/ra_ssr/src/tests.rs index d483640df1..7d4d470c0e 100644 --- a/crates/ra_ssr/src/tests.rs +++ b/crates/ra_ssr/src/tests.rs @@ -549,6 +549,70 @@ fn replace_associated_function_call() { ); } +#[test] +fn replace_associated_trait_default_function_call() { + mark::check!(replace_associated_trait_default_function_call); + assert_ssr_transform( + "Bar2::foo() ==>> Bar2::foo2()", + r#" + trait Foo { fn foo() {} } + pub struct Bar {} + impl Foo for Bar {} + pub struct Bar2 {} + impl Foo for Bar2 {} + impl Bar2 { fn foo2() {} } + fn main() { + Bar::foo(); + Bar2::foo(); + } + "#, + expect![[r#" + trait Foo { fn foo() {} } + pub struct Bar {} + impl Foo for Bar {} + pub struct Bar2 {} + impl Foo for Bar2 {} + impl Bar2 { fn foo2() {} } + fn main() { + Bar::foo(); + Bar2::foo2(); + } + "#]], + ); +} + +#[test] +fn replace_associated_trait_constant() { + mark::check!(replace_associated_trait_constant); + assert_ssr_transform( + "Bar2::VALUE ==>> Bar2::VALUE_2222", + r#" + trait Foo { const VALUE: i32; const VALUE_2222: i32; } + pub struct Bar {} + impl Foo for Bar { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; } + pub struct Bar2 {} + impl Foo for Bar2 { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; } + impl Bar2 { fn foo2() {} } + fn main() { + Bar::VALUE; + Bar2::VALUE; + } + "#, + expect![[r#" + trait Foo { const VALUE: i32; const VALUE_2222: i32; } + pub struct Bar {} + impl Foo for Bar { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; } + pub struct Bar2 {} + impl Foo for Bar2 { const VALUE: i32 = 1; const VALUE_2222: i32 = 2; } + impl Bar2 { fn foo2() {} } + fn main() { + Bar::VALUE; + Bar2::VALUE_2222; + } + "#]], + ); +} + #[test] fn replace_path_in_different_contexts() { // Note the <|> inside module a::b which marks the point where the rule is interpreted. We diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index 3a56b1674c..0bdc22d953 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs @@ -1,7 +1,7 @@ //! Various traits that are implemented by ast nodes. //! //! The implementations are usually trivial, and live in generated.rs -use stdx::SepBy; +use itertools::Itertools; use crate::{ ast::{self, support, AstChildren, AstNode, AstToken}, @@ -119,8 +119,7 @@ impl CommentIter { // of a line in markdown. line[pos..end].to_owned() }) - .sep_by("\n") - .to_string(); + .join("\n"); if has_comments { Some(docs) diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rast b/crates/ra_syntax/test_data/parser/err/0043_default_const.rast deleted file mode 100644 index 51ad2a846f..0000000000 --- a/crates/ra_syntax/test_data/parser/err/0043_default_const.rast +++ /dev/null @@ -1,40 +0,0 @@ -SOURCE_FILE@0..39 - TRAIT@0..38 - TRAIT_KW@0..5 "trait" - WHITESPACE@5..6 " " - NAME@6..7 - IDENT@6..7 "T" - WHITESPACE@7..8 " " - ASSOC_ITEM_LIST@8..38 - L_CURLY@8..9 "{" - WHITESPACE@9..12 "\n " - MACRO_CALL@12..19 - PATH@12..19 - PATH_SEGMENT@12..19 - NAME_REF@12..19 - IDENT@12..19 "default" - WHITESPACE@19..20 " " - CONST@20..36 - CONST_KW@20..25 "const" - WHITESPACE@25..26 " " - NAME@26..27 - IDENT@26..27 "f" - COLON@27..28 ":" - WHITESPACE@28..29 " " - PATH_TYPE@29..31 - PATH@29..31 - PATH_SEGMENT@29..31 - NAME_REF@29..31 - IDENT@29..31 "u8" - WHITESPACE@31..32 " " - EQ@32..33 "=" - WHITESPACE@33..34 " " - LITERAL@34..35 - INT_NUMBER@34..35 "0" - SEMICOLON@35..36 ";" - WHITESPACE@36..37 "\n" - R_CURLY@37..38 "}" - WHITESPACE@38..39 "\n" -error 19..19: expected BANG -error 19..19: expected `{`, `[`, `(` -error 19..19: expected SEMICOLON diff --git a/crates/ra_syntax/test_data/parser/err/0043_default_const.rs b/crates/ra_syntax/test_data/parser/err/0043_default_const.rs deleted file mode 100644 index 80f15474a5..0000000000 --- a/crates/ra_syntax/test_data/parser/err/0043_default_const.rs +++ /dev/null @@ -1,3 +0,0 @@ -trait T { - default const f: u8 = 0; -} diff --git a/crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rast b/crates/ra_syntax/test_data/parser/err/0043_weird_blocks.rast similarity index 100% rename from crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rast rename to crates/ra_syntax/test_data/parser/err/0043_weird_blocks.rast diff --git a/crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rs b/crates/ra_syntax/test_data/parser/err/0043_weird_blocks.rs similarity index 100% rename from crates/ra_syntax/test_data/parser/err/0163_weird_blocks.rs rename to crates/ra_syntax/test_data/parser/err/0043_weird_blocks.rs diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast b/crates/ra_syntax/test_data/parser/err/0045_item_modifiers.rast similarity index 100% rename from crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rast rename to crates/ra_syntax/test_data/parser/err/0045_item_modifiers.rast diff --git a/crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rs b/crates/ra_syntax/test_data/parser/err/0045_item_modifiers.rs similarity index 100% rename from crates/ra_syntax/test_data/parser/inline/err/0010_wrong_order_fns.rs rename to crates/ra_syntax/test_data/parser/err/0045_item_modifiers.rs diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rast b/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rast deleted file mode 100644 index acd72094b9..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rast +++ /dev/null @@ -1,58 +0,0 @@ -SOURCE_FILE@0..62 - TRAIT@0..61 - TRAIT_KW@0..5 "trait" - WHITESPACE@5..6 " " - NAME@6..7 - IDENT@6..7 "T" - WHITESPACE@7..8 " " - ASSOC_ITEM_LIST@8..61 - L_CURLY@8..9 "{" - WHITESPACE@9..14 "\n " - MACRO_CALL@14..21 - PATH@14..21 - PATH_SEGMENT@14..21 - NAME_REF@14..21 - IDENT@14..21 "default" - WHITESPACE@21..22 " " - TYPE_ALIAS@22..35 - TYPE_KW@22..26 "type" - WHITESPACE@26..27 " " - NAME@27..28 - IDENT@27..28 "T" - WHITESPACE@28..29 " " - EQ@29..30 "=" - WHITESPACE@30..31 " " - PATH_TYPE@31..34 - PATH@31..34 - PATH_SEGMENT@31..34 - NAME_REF@31..34 - IDENT@31..34 "Bar" - SEMICOLON@34..35 ";" - WHITESPACE@35..40 "\n " - MACRO_CALL@40..47 - PATH@40..47 - PATH_SEGMENT@40..47 - NAME_REF@40..47 - IDENT@40..47 "default" - WHITESPACE@47..48 " " - FN@48..59 - FN_KW@48..50 "fn" - WHITESPACE@50..51 " " - NAME@51..54 - IDENT@51..54 "foo" - PARAM_LIST@54..56 - L_PAREN@54..55 "(" - R_PAREN@55..56 ")" - WHITESPACE@56..57 " " - BLOCK_EXPR@57..59 - L_CURLY@57..58 "{" - R_CURLY@58..59 "}" - WHITESPACE@59..60 "\n" - R_CURLY@60..61 "}" - WHITESPACE@61..62 "\n" -error 21..21: expected BANG -error 21..21: expected `{`, `[`, `(` -error 21..21: expected SEMICOLON -error 47..47: expected BANG -error 47..47: expected `{`, `[`, `(` -error 47..47: expected SEMICOLON diff --git a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rs b/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rs deleted file mode 100644 index 15ba8f4a85..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/err/0014_default_fn_type.rs +++ /dev/null @@ -1,4 +0,0 @@ -trait T { - default type T = Bar; - default fn foo() {} -} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rast b/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rast deleted file mode 100644 index 625ab4c2d9..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rast +++ /dev/null @@ -1,13 +0,0 @@ -SOURCE_FILE@0..18 - TRAIT@0..17 - UNSAFE_KW@0..6 "unsafe" - WHITESPACE@6..7 " " - TRAIT_KW@7..12 "trait" - WHITESPACE@12..13 " " - NAME@13..14 - IDENT@13..14 "T" - WHITESPACE@14..15 " " - ASSOC_ITEM_LIST@15..17 - L_CURLY@15..16 "{" - R_CURLY@16..17 "}" - WHITESPACE@17..18 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rs b/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rs deleted file mode 100644 index 04e021550d..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0016_unsafe_trait.rs +++ /dev/null @@ -1 +0,0 @@ -unsafe trait T {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rast deleted file mode 100644 index 293b1d64c7..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rast +++ /dev/null @@ -1,21 +0,0 @@ -SOURCE_FILE@0..30 - FN@0..29 - UNSAFE_KW@0..6 "unsafe" - WHITESPACE@6..7 " " - ABI@7..17 - EXTERN_KW@7..13 "extern" - WHITESPACE@13..14 " " - STRING@14..17 "\"C\"" - WHITESPACE@17..18 " " - FN_KW@18..20 "fn" - WHITESPACE@20..21 " " - NAME@21..24 - IDENT@21..24 "foo" - PARAM_LIST@24..26 - L_PAREN@24..25 "(" - R_PAREN@25..26 ")" - WHITESPACE@26..27 " " - BLOCK_EXPR@27..29 - L_CURLY@27..28 "{" - R_CURLY@28..29 "}" - WHITESPACE@29..30 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rs deleted file mode 100644 index 1295c2cd22..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0036_unsafe_extern_fn.rs +++ /dev/null @@ -1 +0,0 @@ -unsafe extern "C" fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rast deleted file mode 100644 index d6dfa83b70..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rast +++ /dev/null @@ -1,18 +0,0 @@ -SOURCE_FILE@0..27 - IMPL@0..26 - UNSAFE_KW@0..6 "unsafe" - WHITESPACE@6..7 " " - DEFAULT_KW@7..14 "default" - WHITESPACE@14..15 " " - IMPL_KW@15..19 "impl" - WHITESPACE@19..20 " " - PATH_TYPE@20..23 - PATH@20..23 - PATH_SEGMENT@20..23 - NAME_REF@20..23 - IDENT@20..23 "Foo" - WHITESPACE@23..24 " " - ASSOC_ITEM_LIST@24..26 - L_CURLY@24..25 "{" - R_CURLY@25..26 "}" - WHITESPACE@26..27 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rs deleted file mode 100644 index 9cd6c57bd8..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0047_unsafe_default_impl.rs +++ /dev/null @@ -1 +0,0 @@ -unsafe default impl Foo {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rast deleted file mode 100644 index 97548a5eeb..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rast +++ /dev/null @@ -1,16 +0,0 @@ -SOURCE_FILE@0..18 - FN@0..17 - CONST_KW@0..5 "const" - WHITESPACE@5..6 " " - FN_KW@6..8 "fn" - WHITESPACE@8..9 " " - NAME@9..12 - IDENT@9..12 "foo" - PARAM_LIST@12..14 - L_PAREN@12..13 "(" - R_PAREN@13..14 ")" - WHITESPACE@14..15 " " - BLOCK_EXPR@15..17 - L_CURLY@15..16 "{" - R_CURLY@16..17 "}" - WHITESPACE@17..18 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rs deleted file mode 100644 index 8c84d9cd7c..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0057_const_fn.rs +++ /dev/null @@ -1 +0,0 @@ -const fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rast deleted file mode 100644 index 43c09affed..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rast +++ /dev/null @@ -1,16 +0,0 @@ -SOURCE_FILE@0..19 - IMPL@0..18 - UNSAFE_KW@0..6 "unsafe" - WHITESPACE@6..7 " " - IMPL_KW@7..11 "impl" - WHITESPACE@11..12 " " - PATH_TYPE@12..15 - PATH@12..15 - PATH_SEGMENT@12..15 - NAME_REF@12..15 - IDENT@12..15 "Foo" - WHITESPACE@15..16 " " - ASSOC_ITEM_LIST@16..18 - L_CURLY@16..17 "{" - R_CURLY@17..18 "}" - WHITESPACE@18..19 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rs deleted file mode 100644 index 41055f41d9..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0087_unsafe_impl.rs +++ /dev/null @@ -1 +0,0 @@ -unsafe impl Foo {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rast deleted file mode 100644 index 405b6a259f..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rast +++ /dev/null @@ -1,17 +0,0 @@ -SOURCE_FILE@0..19 - FN@0..18 - ABI@0..6 - EXTERN_KW@0..6 "extern" - WHITESPACE@6..7 " " - FN_KW@7..9 "fn" - WHITESPACE@9..10 " " - NAME@10..13 - IDENT@10..13 "foo" - PARAM_LIST@13..15 - L_PAREN@13..14 "(" - R_PAREN@14..15 ")" - WHITESPACE@15..16 " " - BLOCK_EXPR@16..18 - L_CURLY@16..17 "{" - R_CURLY@17..18 "}" - WHITESPACE@18..19 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rs deleted file mode 100644 index 394a049f0f..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0089_extern_fn.rs +++ /dev/null @@ -1 +0,0 @@ -extern fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rast b/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rast deleted file mode 100644 index 0cac9ac431..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rast +++ /dev/null @@ -1,13 +0,0 @@ -SOURCE_FILE@0..16 - TRAIT@0..15 - AUTO_KW@0..4 "auto" - WHITESPACE@4..5 " " - TRAIT_KW@5..10 "trait" - WHITESPACE@10..11 " " - NAME@11..12 - IDENT@11..12 "T" - WHITESPACE@12..13 " " - ASSOC_ITEM_LIST@13..15 - L_CURLY@13..14 "{" - R_CURLY@14..15 "}" - WHITESPACE@15..16 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rs b/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rs deleted file mode 100644 index 72adf60351..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0091_auto_trait.rs +++ /dev/null @@ -1 +0,0 @@ -auto trait T {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rast b/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rast deleted file mode 100644 index 0ef11c6825..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rast +++ /dev/null @@ -1,15 +0,0 @@ -SOURCE_FILE@0..23 - TRAIT@0..22 - UNSAFE_KW@0..6 "unsafe" - WHITESPACE@6..7 " " - AUTO_KW@7..11 "auto" - WHITESPACE@11..12 " " - TRAIT_KW@12..17 "trait" - WHITESPACE@17..18 " " - NAME@18..19 - IDENT@18..19 "T" - WHITESPACE@19..20 " " - ASSOC_ITEM_LIST@20..22 - L_CURLY@20..21 "{" - R_CURLY@21..22 "}" - WHITESPACE@22..23 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rs b/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rs deleted file mode 100644 index 03d29f3241..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0094_unsafe_auto_trait.rs +++ /dev/null @@ -1 +0,0 @@ -unsafe auto trait T {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rast deleted file mode 100644 index 0a1b21d6e6..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rast +++ /dev/null @@ -1,16 +0,0 @@ -SOURCE_FILE@0..20 - IMPL@0..19 - DEFAULT_KW@0..7 "default" - WHITESPACE@7..8 " " - IMPL_KW@8..12 "impl" - WHITESPACE@12..13 " " - PATH_TYPE@13..16 - PATH@13..16 - PATH_SEGMENT@13..16 - NAME_REF@13..16 - IDENT@13..16 "Foo" - WHITESPACE@16..17 " " - ASSOC_ITEM_LIST@17..19 - L_CURLY@17..18 "{" - R_CURLY@18..19 "}" - WHITESPACE@19..20 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rs deleted file mode 100644 index ef6aa84a29..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0097_default_impl.rs +++ /dev/null @@ -1 +0,0 @@ -default impl Foo {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rast deleted file mode 100644 index 32a77ba490..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rast +++ /dev/null @@ -1,18 +0,0 @@ -SOURCE_FILE@0..25 - FN@0..24 - CONST_KW@0..5 "const" - WHITESPACE@5..6 " " - UNSAFE_KW@6..12 "unsafe" - WHITESPACE@12..13 " " - FN_KW@13..15 "fn" - WHITESPACE@15..16 " " - NAME@16..19 - IDENT@16..19 "foo" - PARAM_LIST@19..21 - L_PAREN@19..20 "(" - R_PAREN@20..21 ")" - WHITESPACE@21..22 " " - BLOCK_EXPR@22..24 - L_CURLY@22..23 "{" - R_CURLY@23..24 "}" - WHITESPACE@24..25 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rs deleted file mode 100644 index 31a1e435f5..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0098_const_unsafe_fn.rs +++ /dev/null @@ -1 +0,0 @@ -const unsafe fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rast deleted file mode 100644 index 73c94e5d43..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rast +++ /dev/null @@ -1,16 +0,0 @@ -SOURCE_FILE@0..19 - FN@0..18 - UNSAFE_KW@0..6 "unsafe" - WHITESPACE@6..7 " " - FN_KW@7..9 "fn" - WHITESPACE@9..10 " " - NAME@10..13 - IDENT@10..13 "foo" - PARAM_LIST@13..15 - L_PAREN@13..14 "(" - R_PAREN@14..15 ")" - WHITESPACE@15..16 " " - BLOCK_EXPR@16..18 - L_CURLY@16..17 "{" - R_CURLY@17..18 "}" - WHITESPACE@18..19 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rs deleted file mode 100644 index 33cfc4cd7a..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0101_unsafe_fn.rs +++ /dev/null @@ -1 +0,0 @@ -unsafe fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rast deleted file mode 100644 index a7df188bd6..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rast +++ /dev/null @@ -1,16 +0,0 @@ -SOURCE_FILE@0..18 - FN@0..17 - ASYNC_KW@0..5 "async" - WHITESPACE@5..6 " " - FN_KW@6..8 "fn" - WHITESPACE@8..9 " " - NAME@9..12 - IDENT@9..12 "foo" - PARAM_LIST@12..14 - L_PAREN@12..13 "(" - R_PAREN@13..14 ")" - WHITESPACE@14..15 " " - BLOCK_EXPR@15..17 - L_CURLY@15..16 "{" - R_CURLY@16..17 "}" - WHITESPACE@17..18 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rs deleted file mode 100644 index f4adcb62b3..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0124_async_fn.rs +++ /dev/null @@ -1 +0,0 @@ -async fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rast b/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rast deleted file mode 100644 index 98a20f36d6..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rast +++ /dev/null @@ -1,35 +0,0 @@ -SOURCE_FILE@0..50 - FN@0..24 - ASYNC_KW@0..5 "async" - WHITESPACE@5..6 " " - UNSAFE_KW@6..12 "unsafe" - WHITESPACE@12..13 " " - FN_KW@13..15 "fn" - WHITESPACE@15..16 " " - NAME@16..19 - IDENT@16..19 "foo" - PARAM_LIST@19..21 - L_PAREN@19..20 "(" - R_PAREN@20..21 ")" - WHITESPACE@21..22 " " - BLOCK_EXPR@22..24 - L_CURLY@22..23 "{" - R_CURLY@23..24 "}" - WHITESPACE@24..25 "\n" - FN@25..49 - CONST_KW@25..30 "const" - WHITESPACE@30..31 " " - UNSAFE_KW@31..37 "unsafe" - WHITESPACE@37..38 " " - FN_KW@38..40 "fn" - WHITESPACE@40..41 " " - NAME@41..44 - IDENT@41..44 "bar" - PARAM_LIST@44..46 - L_PAREN@44..45 "(" - R_PAREN@45..46 ")" - WHITESPACE@46..47 " " - BLOCK_EXPR@47..49 - L_CURLY@47..48 "{" - R_CURLY@48..49 "}" - WHITESPACE@49..50 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rs b/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rs deleted file mode 100644 index 1262871453..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0128_combined_fns.rs +++ /dev/null @@ -1,2 +0,0 @@ -async unsafe fn foo() {} -const unsafe fn bar() {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rast b/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rast deleted file mode 100644 index b8d26a53a5..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rast +++ /dev/null @@ -1,55 +0,0 @@ -SOURCE_FILE@0..69 - IMPL@0..68 - IMPL_KW@0..4 "impl" - WHITESPACE@4..5 " " - PATH_TYPE@5..6 - PATH@5..6 - PATH_SEGMENT@5..6 - NAME_REF@5..6 - IDENT@5..6 "T" - WHITESPACE@6..7 " " - FOR_KW@7..10 "for" - WHITESPACE@10..11 " " - PATH_TYPE@11..14 - PATH@11..14 - PATH_SEGMENT@11..14 - NAME_REF@11..14 - IDENT@11..14 "Foo" - WHITESPACE@14..15 " " - ASSOC_ITEM_LIST@15..68 - L_CURLY@15..16 "{" - WHITESPACE@16..21 "\n " - TYPE_ALIAS@21..42 - DEFAULT_KW@21..28 "default" - WHITESPACE@28..29 " " - TYPE_KW@29..33 "type" - WHITESPACE@33..34 " " - NAME@34..35 - IDENT@34..35 "T" - WHITESPACE@35..36 " " - EQ@36..37 "=" - WHITESPACE@37..38 " " - PATH_TYPE@38..41 - PATH@38..41 - PATH_SEGMENT@38..41 - NAME_REF@38..41 - IDENT@38..41 "Bar" - SEMICOLON@41..42 ";" - WHITESPACE@42..47 "\n " - FN@47..66 - DEFAULT_KW@47..54 "default" - WHITESPACE@54..55 " " - FN_KW@55..57 "fn" - WHITESPACE@57..58 " " - NAME@58..61 - IDENT@58..61 "foo" - PARAM_LIST@61..63 - L_PAREN@61..62 "(" - R_PAREN@62..63 ")" - WHITESPACE@63..64 " " - BLOCK_EXPR@64..66 - L_CURLY@64..65 "{" - R_CURLY@65..66 "}" - WHITESPACE@66..67 "\n" - R_CURLY@67..68 "}" - WHITESPACE@68..69 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rs b/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rs deleted file mode 100644 index 8f5d611139..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0132_default_fn_type.rs +++ /dev/null @@ -1,4 +0,0 @@ -impl T for Foo { - default type T = Bar; - default fn foo() {} -} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rast new file mode 100644 index 0000000000..23c4269b30 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rast @@ -0,0 +1,14 @@ +SOURCE_FILE@0..12 + FN@0..11 + FN_KW@0..2 "fn" + WHITESPACE@2..3 " " + NAME@3..6 + IDENT@3..6 "foo" + PARAM_LIST@6..8 + L_PAREN@6..7 "(" + R_PAREN@7..8 ")" + WHITESPACE@8..9 " " + BLOCK_EXPR@9..11 + L_CURLY@9..10 "{" + R_CURLY@10..11 "}" + WHITESPACE@11..12 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rs new file mode 100644 index 0000000000..8f3b7ef112 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0151_fn.rs @@ -0,0 +1 @@ +fn foo() {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rast new file mode 100644 index 0000000000..7968cf9ffa --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rast @@ -0,0 +1,22 @@ +SOURCE_FILE@0..16 + IMPL@0..15 + IMPL_KW@0..4 "impl" + WHITESPACE@4..5 " " + PATH_TYPE@5..6 + PATH@5..6 + PATH_SEGMENT@5..6 + NAME_REF@5..6 + IDENT@5..6 "T" + WHITESPACE@6..7 " " + FOR_KW@7..10 "for" + WHITESPACE@10..11 " " + PATH_TYPE@11..12 + PATH@11..12 + PATH_SEGMENT@11..12 + NAME_REF@11..12 + IDENT@11..12 "S" + WHITESPACE@12..13 " " + ASSOC_ITEM_LIST@13..15 + L_CURLY@13..14 "{" + R_CURLY@14..15 "}" + WHITESPACE@15..16 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rs new file mode 100644 index 0000000000..a1a550d8a6 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0152_impl.rs @@ -0,0 +1 @@ +impl T for S {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rast b/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rast new file mode 100644 index 0000000000..9881e5048c --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rast @@ -0,0 +1,11 @@ +SOURCE_FILE@0..11 + TRAIT@0..10 + TRAIT_KW@0..5 "trait" + WHITESPACE@5..6 " " + NAME@6..7 + IDENT@6..7 "T" + WHITESPACE@7..8 " " + ASSOC_ITEM_LIST@8..10 + L_CURLY@8..9 "{" + R_CURLY@9..10 "}" + WHITESPACE@10..11 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rs b/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rs new file mode 100644 index 0000000000..8d183dbb5d --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0153_trait.rs @@ -0,0 +1 @@ +trait T {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast deleted file mode 100644 index 1269621dc2..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rast +++ /dev/null @@ -1,40 +0,0 @@ -SOURCE_FILE@0..50 - IMPL@0..49 - IMPL_KW@0..4 "impl" - WHITESPACE@4..5 " " - PATH_TYPE@5..6 - PATH@5..6 - PATH_SEGMENT@5..6 - NAME_REF@5..6 - IDENT@5..6 "T" - WHITESPACE@6..7 " " - FOR_KW@7..10 "for" - WHITESPACE@10..11 " " - PATH_TYPE@11..14 - PATH@11..14 - PATH_SEGMENT@11..14 - NAME_REF@11..14 - IDENT@11..14 "Foo" - WHITESPACE@14..15 " " - ASSOC_ITEM_LIST@15..49 - L_CURLY@15..16 "{" - WHITESPACE@16..21 "\n " - FN@21..47 - DEFAULT_KW@21..28 "default" - WHITESPACE@28..29 " " - UNSAFE_KW@29..35 "unsafe" - WHITESPACE@35..36 " " - FN_KW@36..38 "fn" - WHITESPACE@38..39 " " - NAME@39..42 - IDENT@39..42 "foo" - PARAM_LIST@42..44 - L_PAREN@42..43 "(" - R_PAREN@43..44 ")" - WHITESPACE@44..45 " " - BLOCK_EXPR@45..47 - L_CURLY@45..46 "{" - R_CURLY@46..47 "}" - WHITESPACE@47..48 "\n" - R_CURLY@48..49 "}" - WHITESPACE@49..50 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast deleted file mode 100644 index 6bfe925af2..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rast +++ /dev/null @@ -1,18 +0,0 @@ -SOURCE_FILE@0..27 - IMPL@0..26 - DEFAULT_KW@0..7 "default" - WHITESPACE@7..8 " " - UNSAFE_KW@8..14 "unsafe" - WHITESPACE@14..15 " " - IMPL_KW@15..19 "impl" - WHITESPACE@19..20 " " - PATH_TYPE@20..23 - PATH@20..23 - PATH_SEGMENT@20..23 - NAME_REF@20..23 - IDENT@20..23 "Foo" - WHITESPACE@23..24 " " - ASSOC_ITEM_LIST@24..26 - L_CURLY@24..25 "{" - R_CURLY@25..26 "}" - WHITESPACE@26..27 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs deleted file mode 100644 index ba0998ff4d..0000000000 --- a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_impl.rs +++ /dev/null @@ -1 +0,0 @@ -default unsafe impl Foo {} diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rast b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rast new file mode 100644 index 0000000000..f2e2014605 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rast @@ -0,0 +1,44 @@ +SOURCE_FILE@0..65 + IMPL@0..64 + DEFAULT_KW@0..7 "default" + WHITESPACE@7..8 " " + UNSAFE_KW@8..14 "unsafe" + WHITESPACE@14..15 " " + IMPL_KW@15..19 "impl" + WHITESPACE@19..20 " " + PATH_TYPE@20..21 + PATH@20..21 + PATH_SEGMENT@20..21 + NAME_REF@20..21 + IDENT@20..21 "T" + WHITESPACE@21..22 " " + FOR_KW@22..25 "for" + WHITESPACE@25..26 " " + PATH_TYPE@26..29 + PATH@26..29 + PATH_SEGMENT@26..29 + NAME_REF@26..29 + IDENT@26..29 "Foo" + WHITESPACE@29..30 " " + ASSOC_ITEM_LIST@30..64 + L_CURLY@30..31 "{" + WHITESPACE@31..36 "\n " + FN@36..62 + DEFAULT_KW@36..43 "default" + WHITESPACE@43..44 " " + UNSAFE_KW@44..50 "unsafe" + WHITESPACE@50..51 " " + FN_KW@51..53 "fn" + WHITESPACE@53..54 " " + NAME@54..57 + IDENT@54..57 "foo" + PARAM_LIST@57..59 + L_PAREN@57..58 "(" + R_PAREN@58..59 ")" + WHITESPACE@59..60 " " + BLOCK_EXPR@60..62 + L_CURLY@60..61 "{" + R_CURLY@61..62 "}" + WHITESPACE@62..63 "\n" + R_CURLY@63..64 "}" + WHITESPACE@64..65 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rs similarity index 50% rename from crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs rename to crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rs index 12926cd8a1..96340f84ab 100644 --- a/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_fn.rs +++ b/crates/ra_syntax/test_data/parser/inline/ok/0163_default_unsafe_item.rs @@ -1,3 +1,3 @@ -impl T for Foo { +default unsafe impl T for Foo { default unsafe fn foo() {} } diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rast b/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rast new file mode 100644 index 0000000000..9282772f34 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rast @@ -0,0 +1,24 @@ +SOURCE_FILE@0..26 + IMPL@0..25 + DEFAULT_KW@0..7 "default" + WHITESPACE@7..8 " " + IMPL_KW@8..12 "impl" + WHITESPACE@12..13 " " + PATH_TYPE@13..14 + PATH@13..14 + PATH_SEGMENT@13..14 + NAME_REF@13..14 + IDENT@13..14 "T" + WHITESPACE@14..15 " " + FOR_KW@15..18 "for" + WHITESPACE@18..19 " " + PATH_TYPE@19..22 + PATH@19..22 + PATH_SEGMENT@19..22 + NAME_REF@19..22 + IDENT@19..22 "Foo" + WHITESPACE@22..23 " " + ASSOC_ITEM_LIST@23..25 + L_CURLY@23..24 "{" + R_CURLY@24..25 "}" + WHITESPACE@25..26 "\n" diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rs b/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rs new file mode 100644 index 0000000000..a6836cbd57 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/inline/ok/0164_default_item.rs @@ -0,0 +1 @@ +default impl T for Foo {} diff --git a/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rast b/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rast deleted file mode 100644 index 5524efaafe..0000000000 --- a/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rast +++ /dev/null @@ -1,56 +0,0 @@ -SOURCE_FILE@0..71 - FN@0..19 - ABI@0..6 - EXTERN_KW@0..6 "extern" - WHITESPACE@6..7 " " - FN_KW@7..9 "fn" - WHITESPACE@9..10 " " - NAME@10..13 - IDENT@10..13 "foo" - PARAM_LIST@13..15 - L_PAREN@13..14 "(" - R_PAREN@14..15 ")" - WHITESPACE@15..16 " " - BLOCK_EXPR@16..19 - L_CURLY@16..17 "{" - WHITESPACE@17..18 "\n" - R_CURLY@18..19 "}" - WHITESPACE@19..21 "\n\n" - FN@21..44 - ABI@21..31 - EXTERN_KW@21..27 "extern" - WHITESPACE@27..28 " " - STRING@28..31 "\"C\"" - WHITESPACE@31..32 " " - FN_KW@32..34 "fn" - WHITESPACE@34..35 " " - NAME@35..38 - IDENT@35..38 "bar" - PARAM_LIST@38..40 - L_PAREN@38..39 "(" - R_PAREN@39..40 ")" - WHITESPACE@40..41 " " - BLOCK_EXPR@41..44 - L_CURLY@41..42 "{" - WHITESPACE@42..43 "\n" - R_CURLY@43..44 "}" - WHITESPACE@44..46 "\n\n" - FN@46..70 - ABI@46..57 - EXTERN_KW@46..52 "extern" - WHITESPACE@52..53 " " - RAW_STRING@53..57 "r\"D\"" - WHITESPACE@57..58 " " - FN_KW@58..60 "fn" - WHITESPACE@60..61 " " - NAME@61..64 - IDENT@61..64 "baz" - PARAM_LIST@64..66 - L_PAREN@64..65 "(" - R_PAREN@65..66 ")" - WHITESPACE@66..67 " " - BLOCK_EXPR@67..70 - L_CURLY@67..68 "{" - WHITESPACE@68..69 "\n" - R_CURLY@69..70 "}" - WHITESPACE@70..71 "\n" diff --git a/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rs b/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rs deleted file mode 100644 index e929eef741..0000000000 --- a/crates/ra_syntax/test_data/parser/ok/0021_extern_fn.rs +++ /dev/null @@ -1,8 +0,0 @@ -extern fn foo() { -} - -extern "C" fn bar() { -} - -extern r"D" fn baz() { -} diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast deleted file mode 100644 index 6246a31a66..0000000000 --- a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rast +++ /dev/null @@ -1,44 +0,0 @@ -SOURCE_FILE@0..46 - IMPL@0..45 - IMPL_KW@0..4 "impl" - WHITESPACE@4..5 " " - PATH_TYPE@5..6 - PATH@5..6 - PATH_SEGMENT@5..6 - NAME_REF@5..6 - IDENT@5..6 "T" - WHITESPACE@6..7 " " - FOR_KW@7..10 "for" - WHITESPACE@10..11 " " - PATH_TYPE@11..14 - PATH@11..14 - PATH_SEGMENT@11..14 - NAME_REF@11..14 - IDENT@11..14 "Foo" - WHITESPACE@14..15 " " - ASSOC_ITEM_LIST@15..45 - L_CURLY@15..16 "{" - WHITESPACE@16..19 "\n " - CONST@19..43 - DEFAULT_KW@19..26 "default" - WHITESPACE@26..27 " " - CONST_KW@27..32 "const" - WHITESPACE@32..33 " " - NAME@33..34 - IDENT@33..34 "f" - COLON@34..35 ":" - WHITESPACE@35..36 " " - PATH_TYPE@36..38 - PATH@36..38 - PATH_SEGMENT@36..38 - NAME_REF@36..38 - IDENT@36..38 "u8" - WHITESPACE@38..39 " " - EQ@39..40 "=" - WHITESPACE@40..41 " " - LITERAL@41..42 - INT_NUMBER@41..42 "0" - SEMICOLON@42..43 ";" - WHITESPACE@43..44 "\n" - R_CURLY@44..45 "}" - WHITESPACE@45..46 "\n" diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs deleted file mode 100644 index dfb3b92dca..0000000000 --- a/crates/ra_syntax/test_data/parser/ok/0066_default_const.rs +++ /dev/null @@ -1,3 +0,0 @@ -impl T for Foo { - default const f: u8 = 0; -} diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rast b/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rast new file mode 100644 index 0000000000..e9b57ec3b3 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rast @@ -0,0 +1,218 @@ +SOURCE_FILE@0..294 + TRAIT@0..113 + TRAIT_KW@0..5 "trait" + WHITESPACE@5..6 " " + NAME@6..7 + IDENT@6..7 "T" + WHITESPACE@7..8 " " + ASSOC_ITEM_LIST@8..113 + L_CURLY@8..9 "{" + WHITESPACE@9..12 "\n " + TYPE_ALIAS@12..33 + DEFAULT_KW@12..19 "default" + WHITESPACE@19..20 " " + TYPE_KW@20..24 "type" + WHITESPACE@24..25 " " + NAME@25..26 + IDENT@25..26 "T" + WHITESPACE@26..27 " " + EQ@27..28 "=" + WHITESPACE@28..29 " " + PATH_TYPE@29..32 + PATH@29..32 + PATH_SEGMENT@29..32 + NAME_REF@29..32 + IDENT@29..32 "Bar" + SEMICOLON@32..33 ";" + WHITESPACE@33..36 "\n " + CONST@36..60 + DEFAULT_KW@36..43 "default" + WHITESPACE@43..44 " " + CONST_KW@44..49 "const" + WHITESPACE@49..50 " " + NAME@50..51 + IDENT@50..51 "f" + COLON@51..52 ":" + WHITESPACE@52..53 " " + PATH_TYPE@53..55 + PATH@53..55 + PATH_SEGMENT@53..55 + NAME_REF@53..55 + IDENT@53..55 "u8" + WHITESPACE@55..56 " " + EQ@56..57 "=" + WHITESPACE@57..58 " " + LITERAL@58..59 + INT_NUMBER@58..59 "0" + SEMICOLON@59..60 ";" + WHITESPACE@60..63 "\n " + FN@63..82 + DEFAULT_KW@63..70 "default" + WHITESPACE@70..71 " " + FN_KW@71..73 "fn" + WHITESPACE@73..74 " " + NAME@74..77 + IDENT@74..77 "foo" + PARAM_LIST@77..79 + L_PAREN@77..78 "(" + R_PAREN@78..79 ")" + WHITESPACE@79..80 " " + BLOCK_EXPR@80..82 + L_CURLY@80..81 "{" + R_CURLY@81..82 "}" + WHITESPACE@82..85 "\n " + FN@85..111 + DEFAULT_KW@85..92 "default" + WHITESPACE@92..93 " " + UNSAFE_KW@93..99 "unsafe" + WHITESPACE@99..100 " " + FN_KW@100..102 "fn" + WHITESPACE@102..103 " " + NAME@103..106 + IDENT@103..106 "bar" + PARAM_LIST@106..108 + L_PAREN@106..107 "(" + R_PAREN@107..108 ")" + WHITESPACE@108..109 " " + BLOCK_EXPR@109..111 + L_CURLY@109..110 "{" + R_CURLY@110..111 "}" + WHITESPACE@111..112 "\n" + R_CURLY@112..113 "}" + WHITESPACE@113..115 "\n\n" + IMPL@115..235 + IMPL_KW@115..119 "impl" + WHITESPACE@119..120 " " + PATH_TYPE@120..121 + PATH@120..121 + PATH_SEGMENT@120..121 + NAME_REF@120..121 + IDENT@120..121 "T" + WHITESPACE@121..122 " " + FOR_KW@122..125 "for" + WHITESPACE@125..126 " " + PATH_TYPE@126..129 + PATH@126..129 + PATH_SEGMENT@126..129 + NAME_REF@126..129 + IDENT@126..129 "Foo" + WHITESPACE@129..130 " " + ASSOC_ITEM_LIST@130..235 + L_CURLY@130..131 "{" + WHITESPACE@131..134 "\n " + TYPE_ALIAS@134..155 + DEFAULT_KW@134..141 "default" + WHITESPACE@141..142 " " + TYPE_KW@142..146 "type" + WHITESPACE@146..147 " " + NAME@147..148 + IDENT@147..148 "T" + WHITESPACE@148..149 " " + EQ@149..150 "=" + WHITESPACE@150..151 " " + PATH_TYPE@151..154 + PATH@151..154 + PATH_SEGMENT@151..154 + NAME_REF@151..154 + IDENT@151..154 "Bar" + SEMICOLON@154..155 ";" + WHITESPACE@155..158 "\n " + CONST@158..182 + DEFAULT_KW@158..165 "default" + WHITESPACE@165..166 " " + CONST_KW@166..171 "const" + WHITESPACE@171..172 " " + NAME@172..173 + IDENT@172..173 "f" + COLON@173..174 ":" + WHITESPACE@174..175 " " + PATH_TYPE@175..177 + PATH@175..177 + PATH_SEGMENT@175..177 + NAME_REF@175..177 + IDENT@175..177 "u8" + WHITESPACE@177..178 " " + EQ@178..179 "=" + WHITESPACE@179..180 " " + LITERAL@180..181 + INT_NUMBER@180..181 "0" + SEMICOLON@181..182 ";" + WHITESPACE@182..185 "\n " + FN@185..204 + DEFAULT_KW@185..192 "default" + WHITESPACE@192..193 " " + FN_KW@193..195 "fn" + WHITESPACE@195..196 " " + NAME@196..199 + IDENT@196..199 "foo" + PARAM_LIST@199..201 + L_PAREN@199..200 "(" + R_PAREN@200..201 ")" + WHITESPACE@201..202 " " + BLOCK_EXPR@202..204 + L_CURLY@202..203 "{" + R_CURLY@203..204 "}" + WHITESPACE@204..207 "\n " + FN@207..233 + DEFAULT_KW@207..214 "default" + WHITESPACE@214..215 " " + UNSAFE_KW@215..221 "unsafe" + WHITESPACE@221..222 " " + FN_KW@222..224 "fn" + WHITESPACE@224..225 " " + NAME@225..228 + IDENT@225..228 "bar" + PARAM_LIST@228..230 + L_PAREN@228..229 "(" + R_PAREN@229..230 ")" + WHITESPACE@230..231 " " + BLOCK_EXPR@231..233 + L_CURLY@231..232 "{" + R_CURLY@232..233 "}" + WHITESPACE@233..234 "\n" + R_CURLY@234..235 "}" + WHITESPACE@235..237 "\n\n" + IMPL@237..261 + DEFAULT_KW@237..244 "default" + WHITESPACE@244..245 " " + IMPL_KW@245..249 "impl" + WHITESPACE@249..250 " " + PATH_TYPE@250..251 + PATH@250..251 + PATH_SEGMENT@250..251 + NAME_REF@250..251 + IDENT@250..251 "T" + WHITESPACE@251..252 " " + FOR_KW@252..255 "for" + WHITESPACE@255..256 " " + TUPLE_TYPE@256..258 + L_PAREN@256..257 "(" + R_PAREN@257..258 ")" + WHITESPACE@258..259 " " + ASSOC_ITEM_LIST@259..261 + L_CURLY@259..260 "{" + R_CURLY@260..261 "}" + WHITESPACE@261..262 "\n" + IMPL@262..293 + DEFAULT_KW@262..269 "default" + WHITESPACE@269..270 " " + UNSAFE_KW@270..276 "unsafe" + WHITESPACE@276..277 " " + IMPL_KW@277..281 "impl" + WHITESPACE@281..282 " " + PATH_TYPE@282..283 + PATH@282..283 + PATH_SEGMENT@282..283 + NAME_REF@282..283 + IDENT@282..283 "T" + WHITESPACE@283..284 " " + FOR_KW@284..287 "for" + WHITESPACE@287..288 " " + TUPLE_TYPE@288..290 + L_PAREN@288..289 "(" + R_PAREN@289..290 ")" + WHITESPACE@290..291 " " + ASSOC_ITEM_LIST@291..293 + L_CURLY@291..292 "{" + R_CURLY@292..293 "}" + WHITESPACE@293..294 "\n" diff --git a/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rs b/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rs new file mode 100644 index 0000000000..e443e3495e --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0066_default_modifier.rs @@ -0,0 +1,16 @@ +trait T { + default type T = Bar; + default const f: u8 = 0; + default fn foo() {} + default unsafe fn bar() {} +} + +impl T for Foo { + default type T = Bar; + default const f: u8 = 0; + default fn foo() {} + default unsafe fn bar() {} +} + +default impl T for () {} +default unsafe impl T for () {} diff --git a/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rast b/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rast new file mode 100644 index 0000000000..50a6d8ee9a --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rast @@ -0,0 +1,218 @@ +SOURCE_FILE@0..304 + FN@0..17 + ASYNC_KW@0..5 "async" + WHITESPACE@5..6 " " + FN_KW@6..8 "fn" + WHITESPACE@8..9 " " + NAME@9..12 + IDENT@9..12 "foo" + PARAM_LIST@12..14 + L_PAREN@12..13 "(" + R_PAREN@13..14 ")" + WHITESPACE@14..15 " " + BLOCK_EXPR@15..17 + L_CURLY@15..16 "{" + R_CURLY@16..17 "}" + WHITESPACE@17..18 "\n" + FN@18..36 + ABI@18..24 + EXTERN_KW@18..24 "extern" + WHITESPACE@24..25 " " + FN_KW@25..27 "fn" + WHITESPACE@27..28 " " + NAME@28..31 + IDENT@28..31 "foo" + PARAM_LIST@31..33 + L_PAREN@31..32 "(" + R_PAREN@32..33 ")" + WHITESPACE@33..34 " " + BLOCK_EXPR@34..36 + L_CURLY@34..35 "{" + R_CURLY@35..36 "}" + WHITESPACE@36..37 "\n" + FN@37..54 + CONST_KW@37..42 "const" + WHITESPACE@42..43 " " + FN_KW@43..45 "fn" + WHITESPACE@45..46 " " + NAME@46..49 + IDENT@46..49 "foo" + PARAM_LIST@49..51 + L_PAREN@49..50 "(" + R_PAREN@50..51 ")" + WHITESPACE@51..52 " " + BLOCK_EXPR@52..54 + L_CURLY@52..53 "{" + R_CURLY@53..54 "}" + WHITESPACE@54..55 "\n" + FN@55..79 + CONST_KW@55..60 "const" + WHITESPACE@60..61 " " + UNSAFE_KW@61..67 "unsafe" + WHITESPACE@67..68 " " + FN_KW@68..70 "fn" + WHITESPACE@70..71 " " + NAME@71..74 + IDENT@71..74 "foo" + PARAM_LIST@74..76 + L_PAREN@74..75 "(" + R_PAREN@75..76 ")" + WHITESPACE@76..77 " " + BLOCK_EXPR@77..79 + L_CURLY@77..78 "{" + R_CURLY@78..79 "}" + WHITESPACE@79..80 "\n" + FN@80..109 + UNSAFE_KW@80..86 "unsafe" + WHITESPACE@86..87 " " + ABI@87..97 + EXTERN_KW@87..93 "extern" + WHITESPACE@93..94 " " + STRING@94..97 "\"C\"" + WHITESPACE@97..98 " " + FN_KW@98..100 "fn" + WHITESPACE@100..101 " " + NAME@101..104 + IDENT@101..104 "foo" + PARAM_LIST@104..106 + L_PAREN@104..105 "(" + R_PAREN@105..106 ")" + WHITESPACE@106..107 " " + BLOCK_EXPR@107..109 + L_CURLY@107..108 "{" + R_CURLY@108..109 "}" + WHITESPACE@109..110 "\n" + FN@110..128 + UNSAFE_KW@110..116 "unsafe" + WHITESPACE@116..117 " " + FN_KW@117..119 "fn" + WHITESPACE@119..120 " " + NAME@120..123 + IDENT@120..123 "foo" + PARAM_LIST@123..125 + L_PAREN@123..124 "(" + R_PAREN@124..125 ")" + WHITESPACE@125..126 " " + BLOCK_EXPR@126..128 + L_CURLY@126..127 "{" + R_CURLY@127..128 "}" + WHITESPACE@128..129 "\n" + FN@129..153 + ASYNC_KW@129..134 "async" + WHITESPACE@134..135 " " + UNSAFE_KW@135..141 "unsafe" + WHITESPACE@141..142 " " + FN_KW@142..144 "fn" + WHITESPACE@144..145 " " + NAME@145..148 + IDENT@145..148 "foo" + PARAM_LIST@148..150 + L_PAREN@148..149 "(" + R_PAREN@149..150 ")" + WHITESPACE@150..151 " " + BLOCK_EXPR@151..153 + L_CURLY@151..152 "{" + R_CURLY@152..153 "}" + WHITESPACE@153..154 "\n" + FN@154..178 + CONST_KW@154..159 "const" + WHITESPACE@159..160 " " + UNSAFE_KW@160..166 "unsafe" + WHITESPACE@166..167 " " + FN_KW@167..169 "fn" + WHITESPACE@169..170 " " + NAME@170..173 + IDENT@170..173 "bar" + PARAM_LIST@173..175 + L_PAREN@173..174 "(" + R_PAREN@174..175 ")" + WHITESPACE@175..176 " " + BLOCK_EXPR@176..178 + L_CURLY@176..177 "{" + R_CURLY@177..178 "}" + WHITESPACE@178..180 "\n\n" + TRAIT@180..197 + UNSAFE_KW@180..186 "unsafe" + WHITESPACE@186..187 " " + TRAIT_KW@187..192 "trait" + WHITESPACE@192..193 " " + NAME@193..194 + IDENT@193..194 "T" + WHITESPACE@194..195 " " + ASSOC_ITEM_LIST@195..197 + L_CURLY@195..196 "{" + R_CURLY@196..197 "}" + WHITESPACE@197..198 "\n" + TRAIT@198..213 + AUTO_KW@198..202 "auto" + WHITESPACE@202..203 " " + TRAIT_KW@203..208 "trait" + WHITESPACE@208..209 " " + NAME@209..210 + IDENT@209..210 "T" + WHITESPACE@210..211 " " + ASSOC_ITEM_LIST@211..213 + L_CURLY@211..212 "{" + R_CURLY@212..213 "}" + WHITESPACE@213..214 "\n" + TRAIT@214..236 + UNSAFE_KW@214..220 "unsafe" + WHITESPACE@220..221 " " + AUTO_KW@221..225 "auto" + WHITESPACE@225..226 " " + TRAIT_KW@226..231 "trait" + WHITESPACE@231..232 " " + NAME@232..233 + IDENT@232..233 "T" + WHITESPACE@233..234 " " + ASSOC_ITEM_LIST@234..236 + L_CURLY@234..235 "{" + R_CURLY@235..236 "}" + WHITESPACE@236..238 "\n\n" + IMPL@238..256 + UNSAFE_KW@238..244 "unsafe" + WHITESPACE@244..245 " " + IMPL_KW@245..249 "impl" + WHITESPACE@249..250 " " + PATH_TYPE@250..253 + PATH@250..253 + PATH_SEGMENT@250..253 + NAME_REF@250..253 + IDENT@250..253 "Foo" + WHITESPACE@253..254 " " + ASSOC_ITEM_LIST@254..256 + L_CURLY@254..255 "{" + R_CURLY@255..256 "}" + WHITESPACE@256..257 "\n" + IMPL@257..276 + DEFAULT_KW@257..264 "default" + WHITESPACE@264..265 " " + IMPL_KW@265..269 "impl" + WHITESPACE@269..270 " " + PATH_TYPE@270..273 + PATH@270..273 + PATH_SEGMENT@270..273 + NAME_REF@270..273 + IDENT@270..273 "Foo" + WHITESPACE@273..274 " " + ASSOC_ITEM_LIST@274..276 + L_CURLY@274..275 "{" + R_CURLY@275..276 "}" + WHITESPACE@276..277 "\n" + IMPL@277..303 + UNSAFE_KW@277..283 "unsafe" + WHITESPACE@283..284 " " + DEFAULT_KW@284..291 "default" + WHITESPACE@291..292 " " + IMPL_KW@292..296 "impl" + WHITESPACE@296..297 " " + PATH_TYPE@297..300 + PATH@297..300 + PATH_SEGMENT@297..300 + NAME_REF@297..300 + IDENT@297..300 "Foo" + WHITESPACE@300..301 " " + ASSOC_ITEM_LIST@301..303 + L_CURLY@301..302 "{" + R_CURLY@302..303 "}" + WHITESPACE@303..304 "\n" diff --git a/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rs b/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rs new file mode 100644 index 0000000000..8d697c04b9 --- /dev/null +++ b/crates/ra_syntax/test_data/parser/ok/0068_item_modifiers.rs @@ -0,0 +1,16 @@ +async fn foo() {} +extern fn foo() {} +const fn foo() {} +const unsafe fn foo() {} +unsafe extern "C" fn foo() {} +unsafe fn foo() {} +async unsafe fn foo() {} +const unsafe fn bar() {} + +unsafe trait T {} +auto trait T {} +unsafe auto trait T {} + +unsafe impl Foo {} +default impl Foo {} +unsafe default impl Foo {} diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs index 25554f583e..d68791cf1f 100644 --- a/crates/ra_text_edit/src/lib.rs +++ b/crates/ra_text_edit/src/lib.rs @@ -76,10 +76,6 @@ impl TextEdit { self.indels.iter() } - pub fn into_iter(self) -> vec::IntoIter { - self.indels.into_iter() - } - pub fn apply(&self, text: &mut String) { match self.len() { 0 => return, @@ -141,6 +137,15 @@ impl TextEdit { } } +impl IntoIterator for TextEdit { + type Item = Indel; + type IntoIter = vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.indels.into_iter() + } +} + impl TextEditBuilder { pub fn replace(&mut self, range: TextRange, replace_with: String) { self.indels.push(Indel::replace(range, replace_with)) diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs index 8faf1cc679..20c3f5eabf 100644 --- a/crates/ra_tt/src/lib.rs +++ b/crates/ra_tt/src/lib.rs @@ -107,7 +107,7 @@ fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usi for (idx, child) in subtree.token_trees.iter().enumerate() { print_debug_token(f, child, level + 1)?; if idx != subtree.token_trees.len() - 1 { - writeln!(f, "")?; + writeln!(f)?; } } } diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 721d41a58f..0d386841e5 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -72,7 +72,7 @@ impl AnalysisStatsCmd { shuffle(&mut rng, &mut krates); } for krate in krates { - let module = krate.root_module(db).expect("crate without root module"); + let module = krate.root_module(db); let file_id = module.definition_source(db).file_id; let file_id = file_id.original_file(db); let source_root = db.file_source_root(file_id); diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 4ac8c8772e..f17fc5dfe9 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -28,7 +28,7 @@ pub fn diagnostics( let mut work = Vec::new(); let krates = Crate::all(db); for krate in krates { - let module = krate.root_module(db).expect("crate without root module"); + let module = krate.root_module(db); let file_id = module.definition_source(db).file_id; let file_id = file_id.original_file(db); let source_root = db.file_source_root(file_id); diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 46cb7ebe2e..f9f045f131 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -73,6 +73,7 @@ pub(crate) struct GlobalState { pub(crate) mem_docs: FxHashMap, pub(crate) semantic_tokens_cache: Arc>>, pub(crate) vfs: Arc)>>, + pub(crate) shutdown_requested: bool, pub(crate) status: Status, pub(crate) source_root_config: SourceRootConfig, pub(crate) proc_macro_client: ProcMacroClient, @@ -124,6 +125,7 @@ impl GlobalState { mem_docs: FxHashMap::default(), semantic_tokens_cache: Arc::new(Default::default()), vfs: Arc::new(RwLock::new((vfs::Vfs::default(), FxHashMap::default()))), + shutdown_requested: false, status: Status::default(), source_root_config: SourceRootConfig::default(), proc_macro_client: ProcMacroClient::dummy(), diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 067259e246..785dd2a267 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -773,12 +773,11 @@ fn handle_fixes( let diagnostics = snap.analysis.diagnostics(file_id, snap.config.experimental_diagnostics)?; - let fixes_from_diagnostics = diagnostics + for fix in diagnostics .into_iter() - .filter_map(|d| Some((d.range, d.fix?))) - .filter(|(diag_range, _fix)| diag_range.intersect(range).is_some()) - .map(|(_range, fix)| fix); - for fix in fixes_from_diagnostics { + .filter_map(|d| d.fix) + .filter(|fix| fix.fix_trigger_range.intersect(range).is_some()) + { let title = fix.label; let edit = to_proto::snippet_workspace_edit(&snap, fix.source_change)?; let action = lsp_ext::CodeAction { @@ -864,7 +863,7 @@ pub(crate) fn handle_resolve_code_action( let (id_string, index) = split_once(¶ms.id, ':').unwrap(); let index = index.parse::().unwrap(); let assist = &assists[index]; - assert!(assist.assist.id.0 == id_string); + assert!(assist.assist.id().0 == id_string); Ok(to_proto::resolved_code_action(&snap, assist.clone())?.edit) } @@ -892,7 +891,7 @@ pub(crate) fn handle_code_lens( } let action = runnable.action(); - let range = to_proto::range(&line_index, runnable.nav.focus_or_full_range()); + let range = to_proto::range(&line_index, runnable.nav.full_range); let r = to_proto::runnable(&snap, file_id, runnable)?; if snap.config.lens.run { let lens = CodeLens { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ceddb2b056..e6cf46df23 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -47,7 +47,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> { SetThreadPriority(thread, thread_priority_above_normal); } - GlobalState::new(connection.sender.clone(), config).run(connection.receiver) + GlobalState::new(connection.sender, config).run(connection.receiver) } enum Event { @@ -337,11 +337,34 @@ impl GlobalState { fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { self.register_request(&req, request_received); + if self.shutdown_requested { + self.respond(Response::new_err( + req.id, + lsp_server::ErrorCode::InvalidRequest as i32, + "Shutdown already requested.".to_owned(), + )); + + return Ok(()); + } + + if self.status == Status::Loading && req.method != "shutdown" { + self.respond(lsp_server::Response::new_err( + req.id, + // FIXME: i32 should impl From (from() guarantees lossless conversion) + lsp_server::ErrorCode::ContentModified as i32, + "Rust Analyzer is still loading...".to_owned(), + )); + return Ok(()); + } + RequestDispatcher { req: Some(req), global_state: self } .on_sync::(|s, ()| Ok(s.fetch_workspaces()))? .on_sync::(|s, p| handlers::handle_join_lines(s.snapshot(), p))? .on_sync::(|s, p| handlers::handle_on_enter(s.snapshot(), p))? - .on_sync::(|_, ()| Ok(()))? + .on_sync::(|s, ()| { + s.shutdown_requested = true; + Ok(()) + })? .on_sync::(|s, p| { handlers::handle_selection_range(s.snapshot(), p) })? diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 5eba1f1555..62fda8a1f2 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -704,10 +704,10 @@ pub(crate) fn unresolved_code_action( index: usize, ) -> Result { let res = lsp_ext::CodeAction { - title: assist.label, - id: Some(format!("{}:{}", assist.id.0.to_owned(), index.to_string())), - group: assist.group.filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), - kind: Some(code_action_kind(assist.id.1)), + title: assist.label(), + id: Some(format!("{}:{}", assist.id().0.to_owned(), index.to_string())), + group: assist.group().filter(|_| snap.config.client_caps.code_action_group).map(|gr| gr.0), + kind: Some(code_action_kind(assist.id().1)), edit: None, is_preferred: None, }; @@ -755,7 +755,8 @@ pub(crate) fn runnable( } pub(crate) fn markup_content(markup: Markup) -> lsp_types::MarkupContent { - lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value: markup.into() } + let value = crate::markdown::format_docs(markup.as_str()); + lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value } } #[cfg(test)] diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs index b65875c96e..3c5027fe57 100644 --- a/crates/stdx/src/lib.rs +++ b/crates/stdx/src/lib.rs @@ -1,5 +1,5 @@ //! Missing batteries for standard libraries. -use std::{cell::Cell, fmt, time::Instant}; +use std::time::Instant; mod macros; @@ -8,69 +8,6 @@ pub fn is_ci() -> bool { option_env!("CI").is_some() } -pub trait SepBy: Sized { - /// Returns an `impl fmt::Display`, which joins elements via a separator. - fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self>; -} - -impl SepBy for I -where - I: Iterator, - I::Item: fmt::Display, -{ - fn sep_by<'a>(self, sep: &'a str) -> SepByBuilder<'a, Self> { - SepByBuilder::new(sep, self) - } -} - -pub struct SepByBuilder<'a, I> { - sep: &'a str, - prefix: &'a str, - suffix: &'a str, - iter: Cell>, -} - -impl<'a, I> SepByBuilder<'a, I> { - fn new(sep: &'a str, iter: I) -> SepByBuilder<'a, I> { - SepByBuilder { sep, prefix: "", suffix: "", iter: Cell::new(Some(iter)) } - } - - pub fn prefix(mut self, prefix: &'a str) -> Self { - self.prefix = prefix; - self - } - - pub fn suffix(mut self, suffix: &'a str) -> Self { - self.suffix = suffix; - self - } - - /// Set both suffix and prefix. - pub fn surround_with(self, prefix: &'a str, suffix: &'a str) -> Self { - self.prefix(prefix).suffix(suffix) - } -} - -impl fmt::Display for SepByBuilder<'_, I> -where - I: Iterator, - I::Item: fmt::Display, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(self.prefix)?; - let mut first = true; - for item in self.iter.take().unwrap() { - if !first { - f.write_str(self.sep)?; - } - first = false; - fmt::Display::fmt(&item, f)?; - } - f.write_str(self.suffix)?; - Ok(()) - } -} - #[must_use] pub fn timeit(label: &'static str) -> impl Drop { struct Guard { diff --git a/docs/dev/README.md b/docs/dev/README.md index 67813a9c07..51cf716b3d 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -256,9 +256,9 @@ Release steps: * checkout the `release` branch * reset it to `upstream/nightly` * push it to `upstream`. This triggers GitHub Actions which: - ** runs `cargo xtask dist` to package binaries and VS Code extension - ** makes a GitHub release - ** pushes VS Code extension to the marketplace + * runs `cargo xtask dist` to package binaries and VS Code extension + * makes a GitHub release + * pushes VS Code extension to the marketplace * create new changelog in `rust-analyzer.github.io` * create `rust-analyzer.github.io/git.log` file with the log of merge commits since last release 2. While the release is in progress, fill-in the changelog using `git.log` diff --git a/docs/dev/syntax.md b/docs/dev/syntax.md index d4bc4b07c4..f1bcdc4aff 100644 --- a/docs/dev/syntax.md +++ b/docs/dev/syntax.md @@ -74,7 +74,7 @@ Points of note: * The original text can be recovered by concatenating the texts of all tokens in order. * Accessing a child of particular type (for example, parameter list of a function) generally involves linerary traversing the children, looking for a specific `kind`. * Modifying the tree is roughly `O(depth)`. - We don't make special efforts to guarantree that the depth is not liner, but, in practice, syntax trees are branchy and shallow. + We don't make special efforts to guarantee that the depth is not linear, but, in practice, syntax trees are branchy and shallow. * If mandatory (grammar wise) node is missing from the input, it's just missing from the tree. * If an extra erroneous input is present, it is wrapped into a node with `ERROR` kind, and treated just like any other node. * Parser errors are not a part of syntax tree. diff --git a/editors/code/package.json b/editors/code/package.json index 86584c071c..d186d1474e 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -607,7 +607,7 @@ "items": { "type": "string" }, - "description": "List of warnings warnings that should be displayed with hint severity.\nThe warnings will be indicated by faded text or three dots in code and will not show up in the problems panel.", + "description": "List of warnings that should be displayed with hint severity.\nThe warnings will be indicated by faded text or three dots in code and will not show up in the problems panel.", "default": [] }, "rust-analyzer.analysis.disabledDiagnostics": { diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 18948cb3c4..f5db55b8cc 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -4,7 +4,7 @@ import * as ra from '../src/lsp_ext'; import * as Is from 'vscode-languageclient/lib/utils/is'; import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; -import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; +import { SemanticTokensFeature } from 'vscode-languageclient/lib/semanticTokens.proposed'; import { assert } from './util'; function renderCommand(cmd: ra.CommandLink) { @@ -44,12 +44,6 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient diagnosticCollectionName: "rustc", traceOutputChannel, middleware: { - // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576 - async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken, next: DocumentSemanticsTokensSignature) { - const res = await next(document, token); - if (res === undefined) throw new Error('busy'); - return res; - }, async provideHover(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken, _next: lc.ProvideHoverSignature) { return client.sendRequest(lc.HoverRequest.type, client.code2ProtocolConverter.asTextDocumentPositionParams(document, position), token).then( (result) => { @@ -135,7 +129,7 @@ export function createClient(serverPath: string, cwd: string): lc.LanguageClient ); } - } as any + } }; const client = new lc.LanguageClient( diff --git a/editors/code/src/util.ts b/editors/code/src/util.ts index 970fedb378..49d2d1c6fb 100644 --- a/editors/code/src/util.ts +++ b/editors/code/src/util.ts @@ -64,7 +64,8 @@ export async function sendRequestWithRetry( param: TParam, token?: vscode.CancellationToken, ): Promise { - for (const delay of [2, 4, 6, 8, 10, null]) { + // The sequence is `10 * (2 ** (2 * n))` where n is 1, 2, 3... + for (const delay of [40, 160, 640, 2560, 10240, null]) { try { return await (token ? client.sendRequest(reqType, param, token) @@ -84,8 +85,7 @@ export async function sendRequestWithRetry( log.warn("LSP request failed", { method: reqType.method, param, error }); throw error; } - - await sleep(10 * (1 << delay)); + await sleep(delay); } } throw 'unreachable'; diff --git a/xtask/tests/tidy.rs b/xtask/tests/tidy.rs index d65a2acbca..4ff72865e8 100644 --- a/xtask/tests/tidy.rs +++ b/xtask/tests/tidy.rs @@ -44,11 +44,26 @@ fn rust_files_are_tidy() { let text = fs2::read_to_string(&path).unwrap(); check_todo(&path, &text); check_trailing_ws(&path, &text); + deny_clippy(&path, &text); tidy_docs.visit(&path, &text); } tidy_docs.finish(); } +fn deny_clippy(path: &PathBuf, text: &String) { + if text.contains("[\u{61}llow(clippy") { + panic!( + "\n\nallowing lints is forbidden: {}. +rust-analyzer intentionally doesn't check clippy on CI. +You can allow lint globally via `xtask clippy`. +See https://github.com/rust-lang/rust-clippy/issues/5537 for discussion. + +", + path.display() + ) + } +} + #[test] fn check_licenses() { let expected = "