diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index e8cd3a500084a..d332908954b8f 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -2494,8 +2494,7 @@ impl UnsafeCell { #[rustc_diagnostic_item = "unsafe_cell_raw_get"] pub const fn raw_get(this: *const Self) -> *mut T { // We can just cast the pointer from `UnsafeCell` to `T` because of - // #[repr(transparent)]. This exploits std's special status, there is - // no guarantee for user code that this will work in future versions of the compiler! + // #[repr(transparent)]. this as *const T as *mut T } diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 5d5eae5b26a19..613397e6903c1 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -1911,6 +1911,7 @@ pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { use crate::fs::{OpenOptions, Permissions}; let mut options = OpenOptions::new(); + options.read(true); // ESP-IDF and Horizon do not support O_NOFOLLOW, so we skip setting it. // Their filesystems do not have symbolic links, so no special handling is required. @@ -1920,7 +1921,7 @@ pub fn set_perm_nofollow(p: &CStr, perm: FilePermissions) -> io::Result<()> { use crate::os::unix::fs::OpenOptionsExt; #[cfg(target_os = "wasi")] use crate::os::wasi::fs::OpenOptionsExt; - options.read(true).custom_flags(libc::O_NOFOLLOW); + options.custom_flags(libc::O_NOFOLLOW); } // SAFETY: Since this function is called with `with_native_path` diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index f1864c736ed03..91a6ecf4d6bb9 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -2231,30 +2231,37 @@ impl Step for FileCheck { }; // There is a LLVM config set, take filecheck from it - // Note: because `download-ci-llvm` currently overrides `llvm-config`, when the LLVM is - // downloaded, we go through this branch. Ideally, this should be changed so that - // `download-ci-llvm` doesn't override the config. - if let Some(s) = target_config.and_then(|c| c.llvm_config.as_ref()) { - let llvm_bindir = command(s).arg("--bindir").run_capture_stdout(builder).stdout(); - let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", self.target)); - let filecheck = if filecheck.exists() { - filecheck - } else { - // On Fedora the system LLVM installs FileCheck in the - // llvm subdirectory of the libdir. - let llvm_libdir = command(s).arg("--libdir").run_capture_stdout(builder).stdout(); - let lib_filecheck = - Path::new(llvm_libdir.trim()).join("llvm").join(exe("FileCheck", self.target)); - if lib_filecheck.exists() { - lib_filecheck - } else { - // Return the most normal file name, even though - // it doesn't exist, so that any error message - // refers to that. + if let Some(llvm_config) = target_config.and_then(|c| c.llvm_config.as_ref()) { + // We can only execute llvm-config if we're on the same host target + return if builder.is_host_target(self.target) { + let llvm_bindir = + command(llvm_config).arg("--bindir").run_capture_stdout(builder).stdout(); + let filecheck = Path::new(llvm_bindir.trim()).join(exe("FileCheck", self.target)); + + if filecheck.exists() { filecheck + } else { + // On Fedora the system LLVM installs FileCheck in the + // llvm subdirectory of the libdir. + let llvm_libdir = + command(llvm_config).arg("--libdir").run_capture_stdout(builder).stdout(); + let lib_filecheck = Path::new(llvm_libdir.trim()) + .join("llvm") + .join(exe("FileCheck", self.target)); + if lib_filecheck.exists() { + lib_filecheck + } else { + // Return the most normal file name, even though + // it doesn't exist, so that any error message + // refers to that. + filecheck + } } + } else { + // In other cases, just guess that Filecheck is available in the same directory + // as the llvm-config + llvm_config.parent().unwrap().join(exe("FileCheck", self.target)) }; - return filecheck; } // Here we take the filecheck from LLVM directly let llvm_output = builder.ensure(Llvm { target: self.target }); diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index f87e0780ce49c..a513c45bce9b1 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -1008,11 +1008,6 @@ impl Config { target.llvm_has_rust_patches = Some(patches); } if let Some(ref s) = target_llvm_filecheck { - if target_llvm_config.is_none() { - panic!( - "You must also configure `llvm-config` when setting `llvm-filecheck` for target {triple}", - ); - } target.llvm_filecheck = Some(src.join(s)); } target.llvm_libunwind = target_llvm_libunwind.as_ref().map(|v| { diff --git a/src/librustdoc/passes/lint/bare_urls.rs b/src/librustdoc/passes/lint/bare_urls.rs index 0928980e390a8..287a1f50b5aa1 100644 --- a/src/librustdoc/passes/lint/bare_urls.rs +++ b/src/librustdoc/passes/lint/bare_urls.rs @@ -2,13 +2,14 @@ //! Suggests wrapping the link with angle brackets: `Go to .` to linkify it. use core::ops::Range; -use std::mem; use std::sync::LazyLock; use regex::Regex; use rustc_errors::{Applicability, DiagDecorator}; use rustc_hir::HirId; -use rustc_resolve::rustdoc::pulldown_cmark::{Event, Parser, Tag}; +use rustc_resolve::rustdoc::pulldown_cmark::{ + DefaultBrokenLinkCallback, Event, Tag, TextMergeWithOffset, +}; use rustc_resolve::rustdoc::source_span_for_markdown_range; use tracing::trace; @@ -55,21 +56,20 @@ pub(super) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: & ); }; - let mut p = Parser::new_ext(dox, main_body_opts()).into_offset_iter(); + // pulldown-cmark can split a URL into multiple `Text` events while processing + // characters such as `_` according to CommonMark's emphasis rules. + // `TextMergeWithOffset` merges these events so we can check the complete URL. + let mut p = TextMergeWithOffset::::new_ext(dox, main_body_opts()); while let Some((event, range)) = p.next() { match event { Event::Text(s) => find_raw_urls(cx, dox, &s, range, &report_diag), // We don't want to check the text inside code blocks or links. Event::Start(tag @ (Tag::CodeBlock(_) | Tag::Link { .. })) => { + let end = tag.to_end(); for (event, _) in p.by_ref() { - match event { - Event::End(end) - if mem::discriminant(&end) == mem::discriminant(&tag.to_end()) => - { - break; - } - _ => {} + if matches!(event, Event::End(tag) if tag == end) { + break; } } } @@ -83,7 +83,12 @@ static URL_REGEX: LazyLock = LazyLock::new(|| { r"https?://", // url scheme r"([-a-zA-Z0-9@:%._\+~#=]{2,256}\.)+", // one or more subdomains r"[a-zA-Z]{2,63}", // root domain - r"\b([-a-zA-Z0-9@:%_\+.~#?&/=]*)", // optional query or url fragments + // Match URL characters and balanced parenthesized segments, without + // consuming a trailing `)` that belongs to the surrounding prose. + r"\b(?:", + r"[-a-zA-Z0-9@:%_\+.~#?&/=]", + r"|\([-a-zA-Z0-9@:%_\+.~#?&/=]*\)", + r")*", )) .expect("failed to build regex") }); diff --git a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml index abb9b521f1a50..0f510ecf4723d 100644 --- a/src/tools/rust-analyzer/.github/workflows/autopublish.yaml +++ b/src/tools/rust-analyzer/.github/workflows/autopublish.yaml @@ -28,7 +28,7 @@ jobs: run: rustup update --no-self-update stable - name: Install cargo-workspaces - run: cargo install cargo-workspaces --version "0.3.6" + run: cargo install --locked cargo-workspaces --version "0.3.6" - name: Publish Crates env: diff --git a/src/tools/rust-analyzer/.github/workflows/ci.yaml b/src/tools/rust-analyzer/.github/workflows/ci.yaml index 7ea734e7ef1c0..d7b51a171d4be 100644 --- a/src/tools/rust-analyzer/.github/workflows/ci.yaml +++ b/src/tools/rust-analyzer/.github/workflows/ci.yaml @@ -54,7 +54,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha }} - name: Install rustup-toolchain-install-master - run: cargo install rustup-toolchain-install-master@1.11.0 + run: cargo install --locked rustup-toolchain-install-master@1.11.0 # Install a pinned rustc commit to avoid surprises - name: Install Rust toolchain @@ -317,7 +317,7 @@ jobs: timeout-minutes: 10 env: FORCE_COLOR: 1 - TYPOS_VERSION: v1.38.1 + TYPOS_VERSION: v1.50.0 steps: - name: download typos run: curl -LsSf https://github.com/crate-ci/typos/releases/download/$TYPOS_VERSION/typos-$TYPOS_VERSION-x86_64-unknown-linux-musl.tar.gz | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin diff --git a/src/tools/rust-analyzer/.github/workflows/fuzz.yml b/src/tools/rust-analyzer/.github/workflows/fuzz.yml index af0e03598ecf2..1c3ad1cd22211 100644 --- a/src/tools/rust-analyzer/.github/workflows/fuzz.yml +++ b/src/tools/rust-analyzer/.github/workflows/fuzz.yml @@ -38,6 +38,6 @@ jobs: - name: Build fuzzers run: | - cargo install cargo-fuzz + cargo install --locked cargo-fuzz cd crates/syntax cargo +nightly fuzz build diff --git a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml index 762b7bda871c0..ff04f1b830666 100644 --- a/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml +++ b/src/tools/rust-analyzer/.github/workflows/publish-libs.yaml @@ -22,7 +22,7 @@ jobs: run: rustup update --no-self-update stable - name: Install cargo-workspaces - run: cargo install cargo-workspaces --version "0.3.6" + run: cargo install --locked cargo-workspaces --version "0.3.6" - name: Publish Crates env: diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index 873daa3bf3b83..97789f0abc669 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -40,5 +40,7 @@ inh = "inh" anc = "anc" datas = "datas" impl_froms = "impl_froms" +implicits = "implicits" selfs = "selfs" taits = "taits" +verifys = "verifys" diff --git a/src/tools/rust-analyzer/crates/base-db/Cargo.toml b/src/tools/rust-analyzer/crates/base-db/Cargo.toml index 7425856b30e6a..9bf41b3aa7160 100644 --- a/src/tools/rust-analyzer/crates/base-db/Cargo.toml +++ b/src/tools/rust-analyzer/crates/base-db/Cargo.toml @@ -2,7 +2,7 @@ name = "base-db" version = "0.0.0" repository.workspace = true -description = "Basic database traits for rust-analyzer. The concrete DB is defined by `ide` (aka `ra_ap_ide`)." +description = "Basic database trait and infra for rust-analyzer's crate and source root inputs. The concrete DB is defined by `ide` (aka `ra_ap_ide`)." authors.workspace = true edition.workspace = true diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 1e32ee69804c7..230b7cbed680f 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -257,9 +257,10 @@ impl fmt::Display for LangCrateOrigin { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct CrateDisplayName { - // The name we use to display various paths (with `_`). + /// The name we use to display various paths (with `_`). crate_name: CrateName, - // The name as specified in Cargo.toml (with `-`). + /// The name as self-declared by the crate. For example, the name declared in the manifest of + /// the crate. This may contain dashes `-`. canonical_name: Symbol, } @@ -936,8 +937,8 @@ impl<'a> IntoIterator for &'a Env { /// /// ## dev-dependencies /// -/// Note that it's actually legal for a cargo package (i.e. a thing -/// with a Cargo.toml) to depend on itself in dev-dependencies. This +/// Note that it's actually legal for a Cargo package (i.e. a thing +/// with a `Cargo.toml`) to depend on itself in dev-dependencies. This /// can enable additional features, and is typically used when a /// project wants features to be enabled in tests. Dev-dependencies /// are not propagated, so they aren't visible to package that depend diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 4346a2a8c8294..0da1faba676c0 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -1,5 +1,6 @@ -//! base_db defines basic database traits. The concrete DB is defined by ide. -// FIXME: Rename this crate, base db is non descriptive +//! This crate defines the basic database trait for interacting with source code using [`salsa`]. +//! +//! The concrete implementation DB is defined by ide. #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs index f91140ee8f934..ac8ca70ccc4b9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs/docs.rs @@ -22,8 +22,8 @@ use hir_expand::{ }; use span::AstIdMap; use syntax::{ - AstNode, AstToken, SyntaxNode, - ast::{self, AttrDocCommentIter, IsString}, + AstNode, SyntaxNode, + ast::{self, IsString}, }; use thin_vec::ThinVec; use tt::{TextRange, TextSize}; @@ -213,15 +213,10 @@ impl Docs { )); } - fn extend_with_doc_comment(&mut self, comment: ast::Comment, indent: &mut Indent) { - let Some((doc, offset)) = comment.doc_comment() else { return }; - let offset = comment.syntax().text_range().start() + offset; - self.extend_with_doc_str( - doc, - offset, - DocCommentKind::Sugared(comment.kind().shape), - indent, - ); + fn extend_with_doc_comment(&mut self, comment: ast::DocComment, indent: &mut Indent) { + let doc = comment.text(); + let offset = comment.syntax().text_range().start() + ast::DocComment::PREFIX_LEN; + self.extend_with_doc_str(doc, offset, DocCommentKind::Sugared(comment.shape()), indent); } fn extend_with_doc_attr(&mut self, value: ast::String, indent: &mut Indent) { @@ -662,13 +657,13 @@ fn extend_with_attrs<'a, 'db>( let mut expander = None; expand_cfg_attr_with_doc_comments::<_, Infallible>( - AttrDocCommentIter::from_syntax_node(node).filter(|attr| match attr { - Either::Left(attr) => attr.kind().is_inner() == expect_inner_attrs, - Either::Right(comment) => comment - .kind() - .doc - .is_some_and(|kind| (kind == ast::CommentPlacement::Inner) == expect_inner_attrs), - }), + node.children() + .filter_map(ast::AnyAttr::cast) + .filter(|attr| attr.kind().is_inner() == expect_inner_attrs) + .map(|attr| match attr { + ast::AnyAttr::Attr(it) => Either::Left(it), + ast::AnyAttr::DocComment(it) => Either::Right(it), + }), || *cfg_options.get_or_insert_with(get_cfg_options), |attr| { match attr { @@ -795,7 +790,7 @@ pub(crate) fn extract_docs<'a, 'db>( mod tests { use expect_test::expect; use hir_expand::InFile; - use syntax::{AstToken, ast}; + use syntax::{AstNode, ast}; use test_fixture::WithFixture; use thin_vec::ThinVec; use tt::{TextRange, TextSize}; @@ -1016,8 +1011,8 @@ mod tests { let comment = syntax::SourceFile::parse(source, span::Edition::CURRENT) .syntax_node() .descendants_with_tokens() - .filter_map(|it| it.into_token()) - .find_map(ast::Comment::cast) + .filter_map(|it| it.into_node()) + .find_map(ast::DocComment::cast) .expect("no comment in the fixture"); let mut docs = Docs { docs: String::new(), diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index e952390268753..3a63ca80ffc68 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -125,7 +125,7 @@ struct ExpressionOnlyStore { /// A map from an variable usages to their hygiene ID. /// - /// Expressions (and destructuing patterns) that can be recorded here are single segment path, although not all single segments path refer + /// Expressions (and destructuring patterns) that can be recorded here are single segment path, although not all single segments path refer /// to variables and have hygiene (some refer to items, we don't know at this stage). ident_hygiene: FxHashMap, @@ -321,6 +321,15 @@ struct FormatTemplate { implicit_capture_to_source: FxHashMap>, } +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub enum MissingBodyItemKind { + AssocConst, + AssocType, + Const, + Static, + TypeAlias, +} + #[derive(Debug, Eq, PartialEq)] pub enum ExpressionStoreDiagnostics { InactiveCode { node: InFile, cfg: CfgExpr, opts: CfgOptions }, @@ -330,6 +339,7 @@ pub enum ExpressionStoreDiagnostics { UndeclaredLabel { node: InFile>, name: Name }, PatternArgInExternFn { node: InFile> }, FruInDestructuringAssignment { node: InFile> }, + MissingBody { node: InFile, kind: MissingBodyItemKind }, } impl ExpressionStoreBuilder { @@ -689,8 +699,7 @@ impl ExpressionStore { visitor.on_pat(*pat); visitor.on_expr(*expr); } - Expr::Block { statements, tail, id: _, label: _ } - | Expr::Unsafe { statements, tail, id: _ } => { + Expr::Block { statements, tail, id: _, label: _, unsafe_: _ } => { for stmt in statements { match stmt { Statement::Let { initializer, else_branch, pat, type_ref } => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs index 74c86ca4e89d4..cd04e2c335737 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs @@ -5,7 +5,7 @@ use std::ops; use base_db::SourceDatabase; use hir_expand::{InFile, Lookup}; use span::Edition; -use syntax::ast; +use syntax::{SyntaxNodePtr, ast}; use triomphe::Arc; use crate::{ @@ -96,7 +96,7 @@ impl Body { let mut is_async_fn = false; let mut is_gen_fn = false; - let InFile { file_id, value: body } = { + let (InFile { file_id, value: body }, syntax_node) = { match def { DefWithBodyId::FunctionId(f) => { let f = f.lookup(db); @@ -104,28 +104,32 @@ impl Body { params = src.value.param_list(); is_async_fn = src.value.async_token().is_some(); is_gen_fn = src.value.gen_token().is_some(); - src.map(|it| it.body().map(ast::Expr::from)) + let syntax_node = SyntaxNodePtr::new(src.syntax().value); + (src.map(|it| it.body().map(ast::Expr::from)), syntax_node) } DefWithBodyId::ConstId(c) => { let c = c.lookup(db); let src = c.source(db); - src.map(|it| it.body()) + let syntax_node = SyntaxNodePtr::new(src.syntax().value); + (src.map(|it| it.body()), syntax_node) } DefWithBodyId::StaticId(s) => { let s = s.lookup(db); let src = s.source(db); - src.map(|it| it.body()) + let syntax_node = SyntaxNodePtr::new(src.syntax().value); + (src.map(|it| it.body()), syntax_node) } DefWithBodyId::VariantId(v) => { let s = v.lookup(db); let src = s.source(db); - src.map(|it| it.const_arg()?.expr()) + let syntax_node = SyntaxNodePtr::new(src.syntax().value); + (src.map(|it| it.const_arg()?.expr()), syntax_node) } } }; let module = def.module(db); let (body, source_map) = - lower_body(db, def, file_id, module, params, body, is_async_fn, is_gen_fn); + lower_body(db, def, syntax_node, file_id, module, params, body, is_async_fn, is_gen_fn); (Arc::new(body), source_map) } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs index 08cecb52570eb..bb60766197b37 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs @@ -35,14 +35,14 @@ use thin_vec::ThinVec; use tt::TextRange; use crate::{ - AdtId, BlockId, BlockIdLt, ConstId, DefWithBodyId, FunctionId, GenericDefId, ImplId, + AdtId, BlockId, BlockIdLt, ConstId, DefWithBodyId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LoweringMode, MacroId, ModuleDefId, ModuleId, TraitId, TypeAliasId, UnresolvedMacro, attrs::AttrFlags, expr_store::{ Body, BodySourceMap, ExprPtr, ExprRoot, ExpressionStore, ExpressionStoreBuilder, ExpressionStoreDiagnostics, ExpressionStoreSourceMap, HygieneId, LabelPtr, LifetimePtr, - PatPtr, StoreVisitor, TypePtr, + MissingBodyItemKind, PatPtr, StoreVisitor, TypePtr, body::Param, expander::Expander, lower::generics::ImplTraitLowerFn, @@ -52,7 +52,7 @@ use crate::{ Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, CoroutineKind, CoroutineSource, Expr, ExprId, Item, Label, LabelId, Literal, LoopSource, MatchArm, Movability, OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread, - Statement, generics::GenericParams, + Statement, Unsafe, generics::GenericParams, }, item_scope::BuiltinShadowMode, lang_item::{LangItemTarget, LangItems}, @@ -70,6 +70,7 @@ pub use self::path::hir_segment_to_ast_segment; pub(super) fn lower_body( db: &dyn SourceDatabase, owner: DefWithBodyId, + syntax_node: SyntaxNodePtr, current_file_id: HirFileId, module: ModuleId, parameters: Option, @@ -133,7 +134,7 @@ pub(super) fn lower_body( BodySourceMap { self_param: source_map_self_param, store: source_map }, ); } - + validate_required_body(db, owner, current_file_id, syntax_node, body.as_ref(), &mut collector); collector.with_expr_root(|collector| { if let DefWithBodyId::FunctionId(func) = owner && let Some(param_list) = parameters @@ -206,6 +207,40 @@ pub(super) fn lower_body( ) } +fn validate_required_body( + db: &(dyn SourceDatabase + 'static), + owner: DefWithBodyId, + current_file_id: HirFileId, + syntax_node: SyntaxNodePtr, + body: Option<&ast::Expr>, + collector: &mut ExprCollector<'_>, +) { + if body.is_some() { + return; + } + let diagnostic_kind = match owner { + // FIXME: add diagnostic for missing body + // rustc says: if body.is_none() && !is_intrinsic && !self.is_sdylib_interface + DefWithBodyId::FunctionId(_function_id) => None, + DefWithBodyId::StaticId(id) => match id.loc(db).container { + ItemContainerId::ModuleId(_) => Some(MissingBodyItemKind::Static), + ItemContainerId::ExternBlockId(_) + | ItemContainerId::ImplId(_) + | ItemContainerId::TraitId(_) => None, + }, + DefWithBodyId::ConstId(id) => match id.loc(db).container { + ItemContainerId::ModuleId(_) => Some(MissingBodyItemKind::Const), + ItemContainerId::ImplId(_) => Some(MissingBodyItemKind::AssocConst), + ItemContainerId::ExternBlockId(_) | ItemContainerId::TraitId(_) => None, + }, + DefWithBodyId::VariantId(_) => None, + }; + if let Some(kind) = diagnostic_kind { + let node = InFile::new(current_file_id, syntax_node); + collector.store.diagnostics.push(ExpressionStoreDiagnostics::MissingBody { node, kind }); + } +} + pub(crate) fn lower_type_ref( db: &dyn SourceDatabase, module: ModuleId, @@ -290,12 +325,13 @@ pub(crate) fn lower_trait( pub(crate) fn lower_type_alias( db: &dyn SourceDatabase, - module: ModuleId, + container: ItemContainerId, alias: InFile, type_alias_id: TypeAliasId, ) -> (ExpressionStore, ExpressionStoreSourceMap, GenericParams, Box<[TypeBound]>, Option) { - let mut expr_collector = ExprCollector::new(db, module, alias.file_id, LoweringMode::Analysis); + let mut expr_collector = + ExprCollector::new(db, container.module(db), alias.file_id, LoweringMode::Analysis); let bounds = alias .value .type_bound_list() @@ -319,6 +355,21 @@ pub(crate) fn lower_type_alias( .value .ty() .map(|ty| expr_collector.lower_type_ref(ty, &mut ExprCollector::impl_trait_allocator)); + if alias.value.ty().is_none() { + let diagnostic_kind = match container { + ItemContainerId::ModuleId(_) => Some(MissingBodyItemKind::TypeAlias), + ItemContainerId::ImplId(_) => Some(MissingBodyItemKind::AssocType), + ItemContainerId::ExternBlockId(_) => None, + ItemContainerId::TraitId(_) => None, + }; + if let Some(kind) = diagnostic_kind { + let node = InFile::new(alias.file_id, SyntaxNodePtr::new(alias.value.syntax())); + expr_collector + .store + .diagnostics + .push(ExpressionStoreDiagnostics::MissingBody { node, kind }); + } + }; let (store, source_map) = expr_collector.store.finish(); (store, source_map, params, bounds, type_ref) } @@ -1188,7 +1239,13 @@ impl<'db> ExprCollector<'db> { statements: Box<[Statement]>, tail: Option, ) -> Expr { - let block = self.alloc_expr_desugared(Expr::Block { label: None, id, statements, tail }); + let block = self.alloc_expr_desugared(Expr::Block { + label: None, + id, + statements, + tail, + unsafe_: Unsafe::No, + }); Expr::Closure { args: Box::default(), arg_types: Box::default(), @@ -1390,10 +1447,12 @@ impl<'db> ExprCollector<'db> { self.desugar_try_block(e, result_type) } Some(ast::BlockModifier::Unsafe(_)) => { - self.collect_block_(e, |_, id, statements, tail| Expr::Unsafe { + self.collect_block_(e, |_, id, statements, tail| Expr::Block { id, statements, tail, + label: None, + unsafe_: Unsafe::Yes, }) } Some(ast::BlockModifier::Label(label)) => { @@ -1405,6 +1464,7 @@ impl<'db> ExprCollector<'db> { statements, tail, label: Some(label_id), + unsafe_: Unsafe::No, }) }) } @@ -2247,7 +2307,7 @@ impl<'db> ExprCollector<'db> { let mut btail = None; let block = this.collect_block_(e, |_, id, statements, tail| { btail = tail; - Expr::Block { id, statements, tail, label: Some(label) } + Expr::Block { id, statements, tail, label: Some(label), unsafe_: Unsafe::No } }); (btail, block) }); @@ -2296,6 +2356,7 @@ impl<'db> ExprCollector<'db> { }]), tail: Some(tail_expr), label: None, + unsafe_: Unsafe::No, }, ptr, ) @@ -2315,8 +2376,11 @@ impl<'db> ExprCollector<'db> { /// } /// } /// ``` - /// FIXME: Rustc wraps the condition in a construct equivalent to `{ let _t = ; _t }` - /// to preserve drop semantics. We should probably do the same in future. + /// FIXME: Rustc wraps the condition in [`DropTemps`] -- a construct equivalent to + /// `{ let _t = ; _t }` -- to preserve drop semantics. + /// We should probably do the same in future. + /// + /// [`DropTemps`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.ExprKind.html#variant.DropTemps fn collect_while_loop(&mut self, syntax_ptr: AstPtr, e: ast::WhileExpr) -> ExprId { let label = e.label().map(|label| { (self.hygiene_id_for(label.syntax().text_range()), self.collect_label(label)) @@ -2429,6 +2493,7 @@ impl<'db> ExprCollector<'db> { statements: Box::default(), tail: Some(loop_inner), label: None, + unsafe_: Unsafe::No, }, syntax_ptr, ); @@ -2727,6 +2792,7 @@ impl<'db> ExprCollector<'db> { statements, tail, label: None, + unsafe_: Unsafe::No, }) } @@ -3398,6 +3464,9 @@ impl<'db> ExprCollector<'db> { } // endregion: labels + /// While `expr` is a macro call, repeatedly expand it. If the end result is a string literal, + /// return that, along with a boolean for whether it was a direct string literal, i.e. no macro + /// calls were involved. In all other cases, return `None`. fn expand_macros_to_string(&mut self, expr: ast::Expr) -> Option<(ast::String, bool)> { let m = match expr { ast::Expr::MacroExpr(m) => m, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs index ad548c6758c16..d6f0aa60978af 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs @@ -12,7 +12,7 @@ use syntax::{ use crate::{ expr_store::{HygieneId, lower::ExprCollector, path::Path}, hir::{ - Array, BindingAnnotation, Expr, ExprId, Literal, Pat, Statement, + Array, BindingAnnotation, Expr, ExprId, Literal, Pat, Statement, Unsafe, format_args::{ self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatSign, FormatTrait, @@ -187,6 +187,7 @@ impl<'db> ExprCollector<'db> { .collect(), tail: Some(from_str), label: None, + unsafe_: Unsafe::No, }, syntax_ptr, ) @@ -378,7 +379,13 @@ impl<'db> ExprCollector<'db> { self.alloc_expr_desugared(Expr::Call { callee: new, args: Box::new([template, args]) }) }; let call = self.alloc_expr( - Expr::Unsafe { id: None, statements: Box::new([]), tail: Some(call) }, + Expr::Block { + id: None, + statements: Box::new([]), + tail: Some(call), + label: None, + unsafe_: Unsafe::Yes, + }, syntax_ptr, ); @@ -402,6 +409,7 @@ impl<'db> ExprCollector<'db> { statements: statements.into_boxed_slice(), tail: Some(call), label: None, + unsafe_: Unsafe::No, }, syntax_ptr, ) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs index 558693ee8adad..7d7b948d34149 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs @@ -19,7 +19,7 @@ use crate::{ expr_store::path::{GenericArg, GenericArgs}, hir::{ Array, BindingAnnotation, CaptureBy, ClosureKind, CoroutineKind, Literal, Movability, - RecordSpread, Statement, + RecordSpread, Statement, Unsafe, generics::{GenericParams, WherePredicate}, }, lang_item::LangItemTarget, @@ -843,14 +843,11 @@ impl Printer<'_> { w!(self, "]"); } Expr::Literal(lit) => self.print_literal(lit), - Expr::Block { id: _, statements, tail, label } => { + Expr::Block { id: _, statements, tail, label, unsafe_ } => { let label = label.map(|lbl| { format!("{}: ", self.store[lbl].name.display(self.db, self.edition)) }); - self.print_block(label.as_deref(), statements, tail); - } - Expr::Unsafe { id: _, statements, tail } => { - self.print_block(Some("unsafe "), statements, tail); + self.print_block(label.as_deref(), *unsafe_, statements, tail); } Expr::Const(id) => { w!(self, "const {{ /* {id:?} */ }}"); @@ -870,6 +867,7 @@ impl Printer<'_> { fn print_block( &mut self, label: Option<&str>, + unsafe_: Unsafe, statements: &[Statement], tail: &Option>, ) { @@ -877,6 +875,9 @@ impl Printer<'_> { if let Some(lbl) = label { w!(self, "{}", lbl); } + if unsafe_ == Unsafe::Yes { + w!(self, "unsafe "); + } w!(self, "{{"); if !statements.is_empty() || tail.is_some() { self.indented(|p| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs index f0a4b5ab6128c..24baa0f8c8d89 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs @@ -335,12 +335,9 @@ impl StoreVisitor for ExprScopeVisitor<'_> { fn on_expr(&mut self, expr: ExprId) { self.scopes.set_scope(expr, self.scope); match &self.store[expr] { - Expr::Block { statements, tail, id, label } => { + Expr::Block { statements, tail, id, label, unsafe_: _ } => { self.visit_block(expr, *id, statements, *tail, *label); } - Expr::Unsafe { id, statements, tail } => { - self.visit_block(expr, *id, statements, *tail, None); - } Expr::Loop { body, label, source: _ } => { let scope = self.scopes.new_labeled_scope(self.scope, *label); self.with_scope(scope, |this| this.on_expr(*body)); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 0ac315ea50fc6..5785a546513c9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -10,7 +10,9 @@ //! names refer to. //! 4. Desugared. There's no `if let`. //! -//! See also a neighboring `body` module. +//! See also a neighboring [`body`] module. +//! +//! [`body`]: crate::expr_store::body pub mod format_args; pub mod generics; @@ -269,6 +271,12 @@ pub enum RecordSpread { Expr(ExprId), } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Unsafe { + Yes, + No, +} + #[derive(Debug, Clone, Eq, PartialEq)] pub enum Expr { /// This is produced if the syntax tree does not have a required expression piece. @@ -288,14 +296,9 @@ pub enum Expr { statements: Box<[Statement]>, tail: Option, label: Option, + unsafe_: Unsafe, }, Const(ExprId), - // FIXME: Fold this into Block with an unsafe flag? - Unsafe { - id: Option, - statements: Box<[Statement]>, - tail: Option, - }, Loop { body: ExprId, label: Option, @@ -404,7 +407,6 @@ impl Expr { Expr::Array(_) | Expr::InlineAsm(_) | Expr::Block { .. } - | Expr::Unsafe { .. } | Expr::Const(_) | Expr::If { .. } | Expr::Literal(_) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs index b46d258686add..c61cffd56467e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs @@ -834,7 +834,7 @@ impl TypeAliasSignature { let source = loc.source(db); let name = as_name_opt(source.value.name()); let (store, source_map, generic_params, bounds, ty) = - lower_type_alias(db, loc.container.module(db), source, id); + lower_type_alias(db, loc.container, source, id); ( Arc::new(TypeAliasSignature { store, generic_params, flags, bounds, name, ty }), @@ -849,14 +849,6 @@ pub struct FunctionBody { pub parameters: Box<[PatId]>, } -#[derive(Debug, PartialEq, Eq)] -pub struct SimpleBody { - pub store: ExpressionStore, -} -pub type StaticBody = SimpleBody; -pub type ConstBody = SimpleBody; -pub type EnumVariantBody = SimpleBody; - #[derive(Debug, PartialEq, Eq)] pub struct VariantFieldsBody { pub store: ExpressionStore, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs index f5e581e8cf472..185298d5f937f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/attrs.rs @@ -114,7 +114,7 @@ pub fn expand_cfg_attr_with_doc_comments<'a, DocComment, BreakValue>( mut callback: impl FnMut(Either<(ast::Meta, ast::Attr), DocComment>) -> ControlFlow, ) -> Option { let mut stack = SmallVec::<[_; 1]>::new(); - loop { + 'process_attrs: loop { let (mut meta, top_attr) = if let Some(it) = stack.pop() { it } else { @@ -134,7 +134,7 @@ pub fn expand_cfg_attr_with_doc_comments<'a, DocComment, BreakValue>( }; while let ast::Meta::UnsafeMeta(unsafe_meta) = &meta { - let Some(inner) = unsafe_meta.meta() else { continue }; + let Some(inner) = unsafe_meta.meta() else { continue 'process_attrs }; meta = inner; } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 4cc194452fbcd..e329e36dde8cf 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -316,6 +316,7 @@ fn convert_path( let name = match segment.kind()? { ast::PathSegmentKind::Name(name) => name.as_name(), ast::PathSegmentKind::SelfKw => continue, + ast::PathSegmentKind::SelfTypeKw => Name::new_symbol_root(sym::Self_), _ => return None, }; mod_path.segments.push(name); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index f42a428bbdd21..76c33d3cae7b2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -25,7 +25,7 @@ use triomphe::Arc; use crate::{ FieldType, GenericDefaultsRef, GenericPredicates, ImplTraitId, InferBodyId, TyDefId, - TyLoweringResult, ValueTyDefId, + TyLoweringDiagnostic, TyLoweringResult, ValueTyDefId, consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, @@ -506,42 +506,53 @@ impl HasResolver for AnonConstId<'_> { } } +pub fn signature_anon_consts_and_diagnostics<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, +) -> ArrayVec<(&'db [AnonConstId<'db>], &'db [TyLoweringDiagnostic]), 5> { + let mut result = ArrayVec::new(); + + // Queries common to all generic defs: + push(&mut result, db.generic_defaults_with_diagnostics(def)); + push(&mut result, GenericPredicates::query_with_diagnostics(db, def)); + push(&mut result, db.const_param_types_with_diagnostics(def)); + + match def { + GenericDefId::ImplId(id) => { + push(&mut result, db.impl_self_ty_with_diagnostics(id)); + if let Some(trait_ref) = db.impl_trait_with_diagnostics(id) { + push(&mut result, trait_ref); + } + } + GenericDefId::TypeAliasId(id) => { + push(&mut result, db.type_for_type_alias_with_diagnostics(id)); + push(&mut result, db.type_alias_bounds_with_diagnostics(id)); + } + GenericDefId::FunctionId(id) => push(&mut result, db.fn_sig_for_fn_with_diagnostics(id)), + GenericDefId::ConstId(def) => push(&mut result, db.type_for_const_with_diagnostics(def)), + GenericDefId::StaticId(def) => push(&mut result, db.type_for_static_with_diagnostics(def)), + GenericDefId::TraitId(_) | GenericDefId::AdtId(_) => {} + } + + return result; + + fn push<'db, T>( + result: &mut ArrayVec<(&'db [AnonConstId<'db>], &'db [TyLoweringDiagnostic]), 5>, + item: &'db TyLoweringResult<'db, T>, + ) { + result.push((item.defined_anon_consts(), item.diagnostics())); + } +} + impl<'db> AnonConstId<'db> { pub fn all_from_signature( db: &'db dyn HirDatabase, def: GenericDefId, - ) -> ArrayVec<&'db [Self], 5> { - let mut result = ArrayVec::new(); - - // Queries common to all generic defs: - result.push(db.generic_defaults_with_diagnostics(def).defined_anon_consts()); - result.push(GenericPredicates::query_with_diagnostics(db, def).defined_anon_consts()); - result.push(db.const_param_types_with_diagnostics(def).defined_anon_consts()); - - match def { - GenericDefId::ImplId(id) => { - result.push(db.impl_self_ty_with_diagnostics(id).defined_anon_consts()); - if let Some(trait_ref) = db.impl_trait_with_diagnostics(id) { - result.push(trait_ref.defined_anon_consts()); - } - } - GenericDefId::TypeAliasId(id) => { - result.push(db.type_for_type_alias_with_diagnostics(id).defined_anon_consts()); - result.push(db.type_alias_bounds_with_diagnostics(id).defined_anon_consts()); - } - GenericDefId::FunctionId(id) => { - result.push(db.fn_sig_for_fn_with_diagnostics(id).defined_anon_consts()) - } - GenericDefId::ConstId(def) => { - result.push(db.type_for_const_with_diagnostics(def).defined_anon_consts()) - } - GenericDefId::StaticId(def) => { - result.push(db.type_for_static_with_diagnostics(def).defined_anon_consts()) - } - GenericDefId::TraitId(_) | GenericDefId::AdtId(_) => {} - } - - result + ) -> impl Iterator> { + signature_anon_consts_and_diagnostics(db, def) + .into_iter() + .flat_map(|(anon_consts, _)| anon_consts) + .copied() } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs index 0947f456adeae..94b1ec331f87d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs @@ -151,8 +151,8 @@ impl<'db> ExprValidator<'db> { Expr::If { .. } => { self.check_for_unnecessary_else(id, expr); } - Expr::Block { .. } | Expr::Unsafe { .. } => { - self.validate_block(expr); + Expr::Block { statements, .. } => { + self.validate_block(statements); } _ => {} } @@ -314,13 +314,10 @@ impl<'db> ExprValidator<'db> { } } - fn validate_block(&mut self, expr: &Expr) { - let (Expr::Block { statements, .. } | Expr::Unsafe { statements, .. }) = expr else { - return; - }; + fn validate_block(&mut self, statements: &[Statement]) { let pattern_arena = Arena::new(); let cx = MatchCheckCtx::new(self.owner.module(self.db()), &self.infcx, self.env); - for stmt in &**statements { + for stmt in statements { match *stmt { Statement::Expr { expr: stmt_expr, has_semi: true } if self.validate_lints => { let mut diags = Vec::new(); @@ -417,9 +414,7 @@ impl<'db> ExprValidator<'db> { // `expr`; branching containers (`if`/`match`) recurse on each arm. loop { match &self.body[expr] { - Expr::Block { tail: Some(tail), .. } - | Expr::Unsafe { tail: Some(tail), .. } - | Expr::Const(tail) => expr = *tail, + Expr::Block { tail: Some(tail), .. } | Expr::Const(tail) => expr = *tail, Expr::If { then_branch, else_branch, .. } => { self.check_unused_must_use(*then_branch, acc); if let Some(else_branch) = else_branch { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 58598980707b3..ca843c8690f28 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -8,7 +8,10 @@ use hir_def::{ AdtId, CallableDefId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, FunctionId, GenericDefId, VariantId, expr_store::{Body, ExpressionStore, path::Path}, - hir::{AsmOperand, Expr, ExprId, ExprOrPatId, InlineAsmKind, Pat, PatId, Statement, UnaryOp}, + hir::{ + AsmOperand, Expr, ExprId, ExprOrPatId, InlineAsmKind, Pat, PatId, Statement, UnaryOp, + Unsafe, + }, resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs}, signatures::{FunctionSignature, StaticFlags, StaticSignature}, type_ref::Rawness, @@ -391,7 +394,7 @@ impl<'db> UnsafeVisitor<'db> { self.on_unsafe_op(current.into(), UnsafetyReason::UnionField); } } - Expr::Unsafe { statements, .. } => { + Expr::Block { unsafe_: Unsafe::Yes, statements, .. } => { self.with_inside_unsafe_block(InsideUnsafeBlock::Yes, |this| { this.walk_pats_top( statements.iter().filter_map(|statement| match statement { @@ -404,7 +407,7 @@ impl<'db> UnsafeVisitor<'db> { }); return; } - Expr::Block { statements, .. } => { + Expr::Block { unsafe_: Unsafe::No, statements, .. } => { self.walk_pats_top( statements.iter().filter_map(|statement| match statement { &Statement::Let { pat, .. } => Some(pat), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index dfa088830543f..a87a55b3c8980 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -520,6 +520,11 @@ impl DisplayTarget { let edition = krate.data(db).edition; Self { krate, edition } } + + pub fn from_crate_and_edition(db: &dyn HirDatabase, krate: Crate, edition: Edition) -> Self { + let _ = db; + Self { krate, edition } + } } #[derive(Clone, Copy)] @@ -1232,41 +1237,41 @@ fn render_variant_after_name<'db>( memory_map: &MemoryMap<'db>, ) -> Result { let param_env = ParamEnvAndCrate { param_env, krate: f.krate() }; + let render_field = |f: &mut HirFormatter<'_, 'db>, id: LocalFieldId| { + let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); + let ty = field_types[id].ty().instantiate(f.interner, args).skip_norm_wip(); + let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else { + return f.write_str(""); + }; + let size = layout.size.bytes_usize(); + render_const_scalar(f, &b[offset..offset + size], memory_map, ty) + }; match data.shape { - FieldsShape::Record | FieldsShape::Tuple => { - let render_field = |f: &mut HirFormatter<'_, 'db>, id: LocalFieldId| { - let offset = layout.fields.offset(u32::from(id.into_raw()) as usize).bytes_usize(); - let ty = field_types[id].ty().instantiate(f.interner, args).skip_norm_wip(); - let Ok(layout) = f.db.layout_of_ty(ty.store(), param_env.store()) else { - return f.write_str(""); - }; - let size = layout.size.bytes_usize(); - render_const_scalar(f, &b[offset..offset + size], memory_map, ty) - }; + FieldsShape::Record => { let mut it = data.fields().iter(); - if matches!(data.shape, FieldsShape::Record) { - write!(f, " {{")?; - if let Some((id, data)) = it.next() { - write!(f, " {}: ", data.name.display(f.db, f.edition()))?; - render_field(f, id)?; - } - for (id, data) in it { - write!(f, ", {}: ", data.name.display(f.db, f.edition()))?; - render_field(f, id)?; - } - write!(f, " }}")?; - } else { - let mut it = it.map(|it| it.0); - write!(f, "(")?; - if let Some(id) = it.next() { - render_field(f, id)?; - } - for id in it { - write!(f, ", ")?; - render_field(f, id)?; - } - write!(f, ")")?; + write!(f, " {{")?; + if let Some((id, data)) = it.next() { + write!(f, " {}: ", data.name.display(f.db, f.edition()))?; + render_field(f, id)?; + } + for (id, data) in it { + write!(f, ", {}: ", data.name.display(f.db, f.edition()))?; + render_field(f, id)?; + } + write!(f, " }}")?; + Ok(()) + } + FieldsShape::Tuple => { + let mut it = data.fields().iter().map(|it| it.0); + write!(f, "(")?; + if let Some(id) = it.next() { + render_field(f, id)?; + } + for id in it { + write!(f, ", ")?; + render_field(f, id)?; } + write!(f, ")")?; Ok(()) } FieldsShape::Unit => Ok(()), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 3fbb02aee94bd..0ab147096a749 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -42,16 +42,16 @@ use std::{ use base_db::{Crate, FxIndexMap}; use either::Either; use hir_def::{ - AdtId, AssocItemId, AttrDefId, ConstId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, - FunctionId, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, StaticId, TraitId, - TupleFieldId, TupleId, VariantId, + AdtId, AssocItemId, AttrDefId, DefWithBodyId, ExpressionStoreOwnerId, FieldId, FunctionId, + GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, TraitId, TupleFieldId, TupleId, + VariantId, attrs::AttrFlags, expr_store::{Body, ExpressionStore, HygieneId, body::Param, path::Path}, hir::{BindingId, ExprId, ExprOrPatId, ExprOrPatIdPacked, LabelId, PatId, UnaryOp}, lang_item::LangItems, layout::Integer, resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs}, - signatures::{ConstSignature, EnumSignature, FunctionSignature, StaticSignature}, + signatures::EnumSignature, type_ref::{LifetimeRefId, TypeRefId}, unstable_features::UnstableFeatures, }; @@ -74,7 +74,7 @@ use thin_vec::ThinVec; use crate::{ ImplTraitId, IncorrectGenericsLenKind, InferBodyId, PathLoweringDiagnostic, Span, - TargetFeatures, + TargetFeatures, ValueTyDefId, closure_analysis::PlaceBase, consteval::{create_anon_const, path_to_const}, db::{AnonConstId, GeneralConstId, HirDatabase, InternedOpaqueTyId}, @@ -95,8 +95,8 @@ use crate::{ unify::resolve_completely::WriteBackCtxt, }, lower::{ - ImplTraitIdx, ImplTraitLoweringMode, LifetimeElisionKind, LifetimeLoweringMode, - LoweringMode, diagnostics::TyLoweringDiagnostic, + ImplTraitIdx, LifetimeElisionKind, LifetimeLoweringMode, LoweringMode, + diagnostics::TyLoweringDiagnostic, }, method_resolution::CandidateId, next_solver::{ @@ -152,8 +152,8 @@ pub fn infer_query_with_inspect<'db>( DefWithBodyId::FunctionId(f) => { ctx.collect_fn(f, body.self_param.map(|param| param.formal), &body.params) } - DefWithBodyId::ConstId(c) => ctx.collect_const(c, ConstSignature::of(db, c)), - DefWithBodyId::StaticId(s) => ctx.collect_static(s, StaticSignature::of(db, s)), + DefWithBodyId::ConstId(c) => ctx.collect_const_or_static(c.into()), + DefWithBodyId::StaticId(s) => ctx.collect_const_or_static(s.into()), DefWithBodyId::VariantId(v) => { ctx.return_ty = match EnumSignature::variant_body_type(db, v.lookup(db).parent) { hir_def::layout::IntegerType::Pointer(signed) => match signed { @@ -286,14 +286,6 @@ pub enum ByRef { #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct BindingMode(pub ByRef, pub Mutability); -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum InferenceTyDiagnosticSource { - /// Diagnostics that come from types in the body. - Body, - /// Diagnostics that come from types in fn parameters/return type, or static & const types. - Signature, -} - #[derive(Debug, PartialEq, Eq, Clone, TypeVisitable, TypeFoldable)] pub enum InferenceDiagnostic { NoSuchField { @@ -475,8 +467,6 @@ pub enum InferenceDiagnostic { cast_ty: StoredTy, }, TyDiagnostic { - #[type_visitable(ignore)] - source: InferenceTyDiagnosticSource, #[type_visitable(ignore)] diag: TyLoweringDiagnostic, }, @@ -1530,6 +1520,7 @@ impl<'db> InferenceContext<'db> { ); } self.defined_anon_consts.borrow_mut().extend(other.defined_anon_consts.iter().copied()); + self.diagnostics.extend(&other.diagnostics); fn merge_hash_set(dest: &mut FxHashSet, source: &FxHashSet) { dest.extend(source.iter().cloned()); @@ -1764,28 +1755,9 @@ impl<'db> InferenceContext<'db> { result } - fn collect_const(&mut self, id: ConstId, data: &'db ConstSignature) { - let return_ty = self.make_ty( - data.type_ref, - &data.store, - InferenceTyDiagnosticSource::Signature, - ExpressionStoreOwnerId::Signature(id.into()), - LifetimeElisionKind::for_const(self.interner(), id.loc(self.db).container), - ); - - self.return_ty = return_ty; - } - - fn collect_static(&mut self, id: StaticId, data: &'db StaticSignature) { - let return_ty = self.make_ty( - data.type_ref, - &data.store, - InferenceTyDiagnosticSource::Signature, - ExpressionStoreOwnerId::Signature(id.into()), - LifetimeElisionKind::Elided(self.types.regions.statik), - ); - - self.return_ty = return_ty; + fn collect_const_or_static(&mut self, id: ValueTyDefId) { + let return_ty = self.db.value_ty(id).unwrap().instantiate_identity().skip_norm_wip(); + self.return_ty = self.process_remote_user_written_ty(return_ty); } fn collect_fn( @@ -1794,63 +1766,40 @@ impl<'db> InferenceContext<'db> { self_param: Option, params: &[Param], ) { - let data = FunctionSignature::of(self.db, func); - let mut param_tys = self.with_ty_lowering( - &data.store, - InferenceTyDiagnosticSource::Signature, - ExpressionStoreOwnerId::Signature(func.into()), - LifetimeElisionKind::for_fn_params(data), - |ctx| data.params.iter().map(|&type_ref| ctx.lower_ty(type_ref)).collect::>(), + let sig = self.interner().liberate_late_bound_regions( + func.into(), + self.db.callable_item_signature(func.into()).instantiate_identity().skip_norm_wip(), ); - // Check if function contains a va_list, if it does then we append it to the parameter types - // that are collected from the function data - if data.is_varargs() { - let va_list_ty = match self.resolve_va_list() { - Some(va_list) => Ty::new_adt( - self.interner(), - va_list, - GenericArgs::for_item_with_defaults( - self.interner(), - va_list.into(), - |_, id, _| self.table.var_for_def(id, Span::Dummy), - ), - ), - None => self.err_ty(), - }; + // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` + // (as it's created inside the body itself, not passed in from outside). + let maybe_va_list = + if sig.fn_sig_kind.c_variadic() { self.resolve_va_list() } else { None }; + let maybe_va_list = maybe_va_list.map(|va_list| { + let region = self.table.next_region_var( + params.last().expect("variadic function must have parameters").user_written.into(), + ); + Ty::new_adt(self.interner(), va_list, GenericArgs::new_from_slice(&[region.into()])) + }); + + let mut param_tys = sig.inputs().iter().copied(); - param_tys.push(va_list_ty); - } - let mut param_tys = param_tys.into_iter(); if let Some(self_param) = self_param && let Some(ty) = param_tys.next() { - let ty = self.process_user_written_ty(ty); + let ty = self.process_remote_user_written_ty(ty); self.write_binding_ty(self_param, ty); } + + let mut param_tys = param_tys.chain(maybe_va_list); for pat in params { let ty = param_tys.next().unwrap_or_else(|| self.table.next_ty_var(Span::Dummy)); - let ty = self.process_user_written_ty(ty); + let ty = self.process_remote_user_written_ty(ty); self.infer_top_pat(pat.formal, ty, PatOrigin::Param); } - self.return_ty = match data.ret_type { - Some(return_ty) => { - let return_ty = self.with_ty_lowering( - &data.store, - InferenceTyDiagnosticSource::Signature, - ExpressionStoreOwnerId::Signature(func.into()), - LifetimeElisionKind::for_fn_ret(self.interner()), - |ctx| { - ctx.impl_trait_mode(ImplTraitLoweringMode::Opaque); - ctx.lower_ty(return_ty) - }, - ); - self.process_user_written_ty(return_ty) - } - None => self.types.types.unit, - }; + self.return_ty = self.process_remote_user_written_ty(sig.output()); self.return_coercion = Some(CoerceMany::new(self.return_ty)); } @@ -1983,76 +1932,33 @@ impl<'db> InferenceContext<'db> { self.deferred_call_resolutions.remove(&closure_def_id).unwrap_or_default() } - fn with_ty_lowering( - &mut self, - store: &'db ExpressionStore, - types_source: InferenceTyDiagnosticSource, - store_owner: ExpressionStoreOwnerId, - lifetime_elision: LifetimeElisionKind<'db>, - f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> R, - ) -> R { - let infer_vars = match types_source { - InferenceTyDiagnosticSource::Body => Some(&mut InferenceTyLoweringVarsCtx { - table: &mut self.table, - type_of_type_placeholder: &mut self.result.type_of_type_placeholder, - } as _), - InferenceTyDiagnosticSource::Signature => None, + fn with_ty_lowering(&mut self, f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> R) -> R { + let mut infer_vars = InferenceTyLoweringVarsCtx { + table: &mut self.table, + type_of_type_placeholder: &mut self.result.type_of_type_placeholder, }; let mut ctx = TyLoweringContext::new( self.db, &self.resolver, - store, + self.store, &self.diagnostics, - types_source, - store_owner, + self.store_owner, self.generic_def, &self.generics, - lifetime_elision, + LifetimeElisionKind::Infer, self.allow_using_generic_params, - infer_vars, + &mut infer_vars, &self.defined_anon_consts, LifetimeLoweringMode::LateParam, ); f(&mut ctx) } - fn with_body_ty_lowering( - &mut self, - f: impl FnOnce(&mut TyLoweringContext<'db, '_>) -> R, - ) -> R { - self.with_ty_lowering( - self.store, - InferenceTyDiagnosticSource::Body, - self.store_owner, - LifetimeElisionKind::Infer, - f, - ) - } - - fn make_ty( - &mut self, - type_ref: TypeRefId, - store: &'db ExpressionStore, - type_source: InferenceTyDiagnosticSource, - store_owner: ExpressionStoreOwnerId, - lifetime_elision: LifetimeElisionKind<'db>, - ) -> Ty<'db> { - let ty = self.with_ty_lowering(store, type_source, store_owner, lifetime_elision, |ctx| { - ctx.lower_ty(type_ref) - }); + pub(crate) fn make_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { + let ty = self.with_ty_lowering(|ctx| ctx.lower_ty(type_ref)); self.process_user_written_ty(ty) } - pub(crate) fn make_body_ty(&mut self, type_ref: TypeRefId) -> Ty<'db> { - self.make_ty( - type_ref, - self.store, - InferenceTyDiagnosticSource::Body, - self.store_owner, - LifetimeElisionKind::Infer, - ) - } - fn generics(&self) -> &Generics<'db> { self.generics.get_or_init(|| crate::generics::generics(self.db, self.generic_def)) } @@ -2063,7 +1969,7 @@ impl<'db> InferenceContext<'db> { }) } - pub(crate) fn create_body_anon_const( + pub(crate) fn create_anon_const( &mut self, expr: ExprId, expected_ty: Ty<'db>, @@ -2096,7 +2002,7 @@ impl<'db> InferenceContext<'db> { konst.unwrap_or_else(|_| self.table.next_const_var(Span::Dummy)) } - pub(crate) fn make_path_as_body_const(&mut self, path: &Path) -> Const<'db> { + pub(crate) fn make_path_as_const(&mut self, path: &Path) -> Const<'db> { let forbid_params_after = if self.allow_using_generic_params { None } else { Some(0) }; // FIXME: Report errors. path_to_const(self.db, &self.resolver, &|| self.generics(), forbid_params_after, path) @@ -2107,14 +2013,8 @@ impl<'db> InferenceContext<'db> { self.types.types.error } - pub(crate) fn make_body_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Region<'db> { - let lt = self.with_ty_lowering( - self.store, - InferenceTyDiagnosticSource::Body, - self.store_owner, - LifetimeElisionKind::Infer, - |ctx| ctx.lower_lifetime(lifetime_ref), - ); + pub(crate) fn make_lifetime(&mut self, lifetime_ref: LifetimeRefId) -> Region<'db> { + let lt = self.with_ty_lowering(|ctx| ctx.lower_lifetime(lifetime_ref)); self.insert_type_vars(lt) } @@ -2343,13 +2243,12 @@ impl<'db> InferenceContext<'db> { &self.resolver, self.store, &self.diagnostics, - InferenceTyDiagnosticSource::Body, self.store_owner, self.generic_def, &self.generics, LifetimeElisionKind::Infer, self.allow_using_generic_params, - Some(&mut vars_ctx), + &mut vars_ctx, &self.defined_anon_consts, LifetimeLoweringMode::LateParam, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 9af182fc49288..4ce7e3a72b956 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -967,7 +967,7 @@ impl<'db> InferenceContext<'db> { let interner = self.interner(); let supplied_return = match decl_output { - Some(output) => self.make_body_ty(output), + Some(output) => self.make_ty(output), None => match closure_kind { // In the case of the async block that we create for a function body, // we expect the return type of the block to match that of the enclosing @@ -1005,7 +1005,7 @@ impl<'db> InferenceContext<'db> { }; // First, convert the types that the user supplied (if any). let supplied_arguments = decl_inputs.iter().map(|&input| match input { - Some(input) => self.make_body_ty(input), + Some(input) => self.make_ty(input), None => self.table.next_ty_var(closure_expr.into()), }); @@ -1134,11 +1134,11 @@ impl<'db> InferenceContext<'db> { let err_ty = Ty::new_error(interner, ErrorGuaranteed); if let Some(output) = decl_output { - self.make_body_ty(output); + self.make_ty(output); } let supplied_arguments = decl_inputs.iter().map(|&input| match input { Some(input) => { - self.make_body_ty(input); + self.make_ty(input); err_ty } None => err_ty, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs index 78940783bd9bf..da9c5ab10fc46 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis/expr_use_visitor.rs @@ -631,8 +631,7 @@ impl<'a, 'db, D: Delegate<'db>> ExprUseVisitor<'a, 'db, D> { self.consume_expr(rhs)?; } - Expr::Block { ref statements, tail, .. } - | Expr::Unsafe { ref statements, tail, .. } => { + Expr::Block { ref statements, tail, .. } => { for stmt in statements { self.walk_stmt(stmt)?; } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs index 69753687afddd..918965c4f5be3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/diagnostics.rs @@ -19,7 +19,7 @@ use thin_vec::ThinVec; use crate::lower::LifetimeLoweringMode; use crate::{ - InferenceDiagnostic, InferenceTyDiagnosticSource, Span, TyLoweringDiagnostic, + InferenceDiagnostic, Span, TyLoweringDiagnostic, db::{AnonConstId, HirDatabase}, generics::Generics, infer::unify::InferenceTable, @@ -42,14 +42,14 @@ impl Diagnostics { self.0.borrow_mut().push(diagnostic); } - fn push_ty_diagnostics( - &self, - source: InferenceTyDiagnosticSource, - diagnostics: ThinVec, - ) { - self.0.borrow_mut().extend( - diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { source, diag }), - ); + pub(super) fn extend(&self, diagnostic: &[InferenceDiagnostic]) { + self.0.borrow_mut().extend(diagnostic.iter().cloned()); + } + + fn push_ty_diagnostics(&self, diagnostics: ThinVec) { + self.0 + .borrow_mut() + .extend(diagnostics.into_iter().map(|diag| InferenceDiagnostic::TyDiagnostic { diag })); } pub(super) fn finish(self) -> ThinVec { @@ -92,7 +92,6 @@ impl<'db> TyLoweringInferVarsCtx<'db> for InferenceTyLoweringVarsCtx<'_, 'db> { pub(super) struct InferenceTyLoweringContext<'db, 'a> { ctx: TyLoweringContext<'db, 'a>, diagnostics: &'a Diagnostics, - source: InferenceTyDiagnosticSource, defined_anon_consts: &'a RefCell>>, } @@ -103,13 +102,12 @@ impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> { resolver: &'a Resolver<'db>, store: &'db ExpressionStore, diagnostics: &'a Diagnostics, - source: InferenceTyDiagnosticSource, def: ExpressionStoreOwnerId, generic_def: GenericDefId, generics: &'a OnceCell>, lifetime_elision: LifetimeElisionKind<'db>, allow_using_generic_params: bool, - infer_vars: Option<&'a mut dyn TyLoweringInferVarsCtx<'db>>, + infer_vars: &'a mut InferenceTyLoweringVarsCtx<'a, 'db>, defined_anon_consts: &'a RefCell>>, lifetime_lowering_mode: LifetimeLoweringMode, ) -> Self { @@ -123,11 +121,11 @@ impl<'db, 'a> InferenceTyLoweringContext<'db, 'a> { lifetime_elision, lifetime_lowering_mode, ) - .with_infer_vars_behavior(infer_vars); + .with_infer_vars_behavior(Some(infer_vars)); if !allow_using_generic_params { ctx.forbid_params_after(0, ForbidParamsAfterReason::AnonConst); } - Self { ctx, diagnostics, source, defined_anon_consts } + Self { ctx, diagnostics, defined_anon_consts } } #[inline] @@ -187,8 +185,7 @@ impl DerefMut for InferenceTyLoweringContext<'_, '_> { impl Drop for InferenceTyLoweringContext<'_, '_> { #[inline] fn drop(&mut self) { - self.diagnostics - .push_ty_diagnostics(self.source, std::mem::take(&mut self.ctx.diagnostics)); + self.diagnostics.push_ty_diagnostics(std::mem::take(&mut self.ctx.diagnostics)); self.defined_anon_consts.borrow_mut().extend(self.ctx.defined_anon_consts.iter().copied()); } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index fc53d64a2f984..af301731c7156 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -265,7 +265,6 @@ impl<'db> InferenceContext<'db> { | Expr::Assignment { .. } | Expr::Yield { .. } | Expr::Cast { .. } - | Expr::Unsafe { .. } | Expr::Await { .. } | Expr::Ref { .. } | Expr::RecordLit { .. } @@ -391,12 +390,9 @@ impl<'db> InferenceContext<'db> { ); self.types.types.bool } - Expr::Block { statements, tail, label, id: _ } => { + Expr::Block { statements, tail, label, id: _, unsafe_: _ } => { self.infer_block(tgt_expr, statements, *tail, *label, expected) } - Expr::Unsafe { id: _, statements, tail } => { - self.infer_block(tgt_expr, statements, *tail, None, expected) - } Expr::Const(id) => { self.with_breakable_ctx(BreakableKind::Border, None, None, |this| { this.infer_expr(*id, expected, ExprIsRead::Yes) @@ -617,7 +613,7 @@ impl<'db> InferenceContext<'db> { Expr::Field { expr, name } => self.infer_field_access(tgt_expr, *expr, name, expected), Expr::Await { expr } => self.infer_await_expr(tgt_expr, *expr), Expr::Cast { expr, type_ref } => { - let cast_ty = self.make_body_ty(*type_ref); + let cast_ty = self.make_ty(*type_ref); let expr_ty = self.infer_expr(*expr, &Expectation::Castable(cast_ty), ExprIsRead::Yes); self.deferred_cast_checks.push(CastCheck::new(tgt_expr, *expr, expr_ty, cast_ty)); @@ -1314,7 +1310,7 @@ impl<'db> InferenceContext<'db> { expr: ExprId, ) -> Ty<'db> { let interner = self.interner(); - let count_ct = self.create_body_anon_const(count, self.types.types.usize, true); + let count_ct = self.create_anon_const(count, self.types.types.usize, true); let count = self.table.try_structurally_resolve_const(count.into(), count_ct); let uty = match expected { @@ -1480,7 +1476,7 @@ impl<'db> InferenceContext<'db> { Statement::Let { pat, type_ref, initializer, else_branch } => { let decl_ty = type_ref .as_ref() - .map(|&tr| this.make_body_ty(tr)) + .map(|&tr| this.make_ty(tr)) .unwrap_or_else(|| this.table.next_ty_var((*pat).into())); this.infer_let( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 9a6414ee2ba11..09b0d3c03d5b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -86,8 +86,7 @@ impl<'db> InferenceContext<'db> { self.infer_mut_expr(*id, Mutability::Not); } Expr::Let { pat, expr } => self.infer_mut_expr(*expr, self.pat_bound_mutability(*pat)), - Expr::Block { id: _, statements, tail, label: _ } - | Expr::Unsafe { id: _, statements, tail } => { + Expr::Block { id: _, statements, tail, label: _, unsafe_: _ } => { for st in statements.iter() { match st { Statement::Let { pat, type_ref: _, initializer, else_branch } => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 47a905fc142cb..ec0788b43b573 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -24,7 +24,7 @@ use crate::{ }, }; -use super::{InferenceContext, InferenceTyDiagnosticSource}; +use super::InferenceContext; impl<'db> InferenceContext<'db> { pub(super) fn infer_path( @@ -122,7 +122,7 @@ impl<'db> InferenceContext<'db> { // to the type alias and they may have different generics. self.types.empty.generic_args } else { - self.with_body_ty_lowering(|ctx| { + self.with_ty_lowering(|ctx| { let mut path_ctx = ctx.at_path(path, id); let last_segment = path.segments().len().checked_sub(1); if let Some(last_segment) = last_segment { @@ -159,13 +159,12 @@ impl<'db> InferenceContext<'db> { &self.resolver, self.store, &self.diagnostics, - InferenceTyDiagnosticSource::Body, self.store_owner, self.generic_def, &self.generics, LifetimeElisionKind::Infer, self.allow_using_generic_params, - Some(&mut vars_ctx), + &mut vars_ctx, &self.defined_anon_consts, LifetimeLoweringMode::LateParam, ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 21e68a6312d39..540d3db74d707 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -106,9 +106,8 @@ use crate::{ pub use autoderef::autoderef; pub use infer::{ Adjust, Adjustment, AutoBorrow, BindingMode, ByRef, ExplicitDropMethodUseKind, - InferenceDiagnostic, InferenceResult, InferenceTyDiagnosticSource, OverloadedDeref, - PointerCast, ReturnKind, cast::CastError, could_coerce, could_unify, could_unify_deeply, - infer_query_with_inspect, + InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast, ReturnKind, + cast::CastError, could_coerce, could_unify, could_unify_deeply, infer_query_with_inspect, }; pub use lower::{ FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, LifetimeElisionKind, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 7733b49d32f78..b86585231cca1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -315,11 +315,6 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { Self { impl_trait_mode: ImplTraitLoweringState::new(impl_trait_mode), ..self } } - pub(crate) fn impl_trait_mode(&mut self, impl_trait_mode: ImplTraitLoweringMode) -> &mut Self { - self.impl_trait_mode = ImplTraitLoweringState::new(impl_trait_mode); - self - } - pub(crate) fn forbid_params_after(&mut self, index: u32, reason: ForbidParamsAfterReason) { self.forbid_params_after = Some(index); self.forbid_params_after_reason = reason; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs index 6d948464b1c03..5d10a0b21e46b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/confirm.rs @@ -395,16 +395,16 @@ impl<'a, 'db> ConfirmContext<'a, 'db> { ( GenericParamDataRef::LifetimeParamData(_), HirGenericArg::Lifetime(lifetime), - ) => self.ctx.make_body_lifetime(*lifetime).into(), + ) => self.ctx.make_lifetime(*lifetime).into(), (GenericParamDataRef::TypeParamData(_), HirGenericArg::Type(type_ref)) => { - self.ctx.make_body_ty(*type_ref).into() + self.ctx.make_ty(*type_ref).into() } (GenericParamDataRef::ConstParamData(_), HirGenericArg::Const(konst)) => { let GenericParamId::ConstParamId(const_id) = param_id else { unreachable!("non-const param ID for const param"); }; let const_ty = self.ctx.db.const_param_ty(const_id); - self.ctx.create_body_anon_const(konst.expr, const_ty, false).into() + self.ctx.create_anon_const(konst.expr, const_ty, false).into() } _ => unreachable!("unmatching param kinds were passed to `provided_kind()`"), } @@ -417,7 +417,7 @@ impl<'a, 'db> ConfirmContext<'a, 'db> { arg: TypeLikeConst<'_>, ) -> Const<'db> { match arg { - TypeLikeConst::Path(path) => self.ctx.make_path_as_body_const(path), + TypeLikeConst::Path(path) => self.ctx.make_path_as_const(path), TypeLikeConst::Infer => self.ctx.table.next_const_var(Span::Dummy), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index fc71e716d90f6..433fc274ea955 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -92,7 +92,7 @@ pub enum OperandKind { /// /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there /// is no such requirement. - Copy(Place), + Copy(StoredPlace), /// Creates a value by performing loading the place, just like the `Copy` operand. /// @@ -101,7 +101,7 @@ pub enum OperandKind { /// place without first re-initializing it. /// /// [UCG#188]: https://github.com/rust-lang/unsafe-code-guidelines/issues/188 - Move(Place), + Move(StoredPlace), /// Constants are already semantically values, and remain unchanged. Constant { konst: StoredConst, @@ -267,56 +267,54 @@ impl<'db> std::ops::Deref for Projection<'db> { } impl StoredProjection { - // FIXME: rename to as_slice - pub fn lookup(&self) -> &[PlaceElem] { + pub fn as_slice(&self) -> &[PlaceElem] { self.as_ref().as_slice() } pub fn is_empty(&self) -> bool { - self.lookup().is_empty() + self.as_slice().is_empty() } } -// FIXME: would be nicer to rename PlaceRef -> Place, Place -> StoredPlace, but I didn't want to blow up the diff #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub struct PlaceRef<'db> { +pub struct Place<'db> { pub local: LocalId, pub projection: Projection<'db>, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Place { +pub struct StoredPlace { pub local: LocalId, pub projection: StoredProjection, } -impl Place { - pub fn as_ref<'db>(&self) -> PlaceRef<'db> { - PlaceRef { local: self.local, projection: self.projection.as_ref() } +impl StoredPlace { + pub fn as_ref<'db>(&self) -> Place<'db> { + Place { local: self.local, projection: self.projection.as_ref() } } } -impl<'db> PlaceRef<'db> { - fn is_parent(&self, child: PlaceRef<'db>) -> bool { +impl<'db> Place<'db> { + fn is_parent(&self, child: Place<'db>) -> bool { self.local == child.local && child.projection.as_slice().starts_with(self.projection.as_slice()) } /// The place itself is not included - fn iterate_over_parents<'a>(&'a self) -> impl Iterator> + 'a { + fn iterate_over_parents<'a>(&'a self) -> impl Iterator> + 'a { let projection = self.projection.as_slice(); - (0..projection.len()).map(move |x| PlaceRef { + (0..projection.len()).map(move |x| Place { local: self.local, projection: Projection::new_from_slice(&projection[0..x]), }) } - fn project(&self, projection: PlaceElem) -> PlaceRef<'db> { - PlaceRef { local: self.local, projection: self.projection.project(projection) } + fn project(&self, projection: PlaceElem) -> Place<'db> { + Place { local: self.local, projection: self.projection.project(projection) } } - pub fn store(&self) -> Place { - Place { local: self.local, projection: self.projection.store() } + pub fn store(&self) -> StoredPlace { + StoredPlace { local: self.local, projection: self.projection.store() } } pub fn ty( &self, @@ -332,10 +330,10 @@ impl<'db> PlaceRef<'db> { } } -impl<'db> From for PlaceRef<'db> { +impl<'db> From for Place<'db> { fn from(local: LocalId) -> Self { let empty: &[PlaceElem] = &[]; - PlaceRef { local, projection: Projection::new_from_slice(empty) } + Place { local, projection: Projection::new_from_slice(empty) } } } @@ -494,7 +492,7 @@ pub enum TerminatorKind { /// > The drop glue is executed if, among all statements executed within this `Body`, an assignment to /// > the place or one of its "parents" occurred more recently than a move out of it. This does not /// > consider indirect assignments. - Drop { place: Place, target: BasicBlockId, unwind: Option }, + Drop { place: StoredPlace, target: BasicBlockId, unwind: Option }, /// Drops the place and assigns a new value to it. /// @@ -527,7 +525,7 @@ pub enum TerminatorKind { /// /// Disallowed after drop elaboration. DropAndReplace { - place: Place, + place: StoredPlace, value: Operand, target: BasicBlockId, unwind: Option, @@ -552,7 +550,7 @@ pub enum TerminatorKind { /// reused across function calls without duplicating the contents. args: Box<[Operand]>, /// Where the returned value will be written - destination: Place, + destination: StoredPlace, /// Where to go after this call returns. If none, the call necessarily diverges. target: Option, /// Cleanups to be done if the call unwinds. @@ -597,7 +595,7 @@ pub enum TerminatorKind { /// Where to resume to. resume: BasicBlockId, /// The place to store the resume argument in. - resume_arg: Place, + resume_arg: StoredPlace, /// Cleanup to be done if the coroutine is dropped at this suspend point. drop: Option, }, @@ -889,7 +887,7 @@ pub enum Rvalue { /// exactly what the behavior of this operation should be. /// /// `Shallow` borrows are disallowed after drop lowering. - Ref(BorrowKind, Place), + Ref(BorrowKind, StoredPlace), /// Creates a pointer/reference to the given thread local. /// @@ -920,7 +918,7 @@ pub enum Rvalue { /// If the type of the place is an array, this is the array length. For slices (`[T]`, not /// `&[T]`) this accesses the place's metadata to determine the length. This rvalue is /// ill-formed for places of other types. - Len(Place), + Len(StoredPlace), /// Performs essentially all of the casts that can be performed via `as`. /// @@ -981,7 +979,7 @@ pub enum Rvalue { /// variant index; use `discriminant_for_variant` to convert. /// /// [#91095]: https://github.com/rust-lang/rust/issues/91095 - Discriminant(Place), + Discriminant(StoredPlace), /// Creates an aggregate value, like a tuple or struct. /// @@ -1001,18 +999,18 @@ pub enum Rvalue { /// read never happened and just projects further. This allows simplifying various MIR /// optimizations and codegen backends that previously had to handle deref operations anywhere /// in a place. - CopyForDeref(Place), + CopyForDeref(StoredPlace), } #[derive(Debug, PartialEq, Eq, Clone)] pub enum StatementKind { - Assign(Place, Rvalue), - FakeRead(Place), + Assign(StoredPlace, Rvalue), + FakeRead(StoredPlace), //SetDiscriminant { // place: Box, // variant_index: VariantIdx, //}, - Deinit(Place), + Deinit(StoredPlace), StorageLive(LocalId), StorageDead(LocalId), //Retag(RetagKind, Box), @@ -1073,8 +1071,8 @@ impl MirBody<'_> { self.binding_locals.iter().map(|(it, y)| (*y, it)).collect() } - fn walk_places(&mut self, mut f: impl FnMut(&mut Place)) { - fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut Place)) { + fn walk_places(&mut self, mut f: impl FnMut(&mut StoredPlace)) { + fn for_operand(op: &mut Operand, f: &mut impl FnMut(&mut StoredPlace)) { match &mut op.kind { OperandKind::Copy(p) | OperandKind::Move(p) => { f(p); @@ -1195,13 +1193,13 @@ impl From<&ExprId> for MirSpan { } } -impl<'tcx> PlaceRef<'tcx> { +impl<'tcx> Place<'tcx> { /// If this place represents a local variable like `_X` with no /// projections, return `Some(_X)`. #[inline] pub fn as_local(&self) -> Option { match *self { - PlaceRef { local, projection } if projection.as_slice().is_empty() => Some(local), + Place { local, projection } if projection.as_slice().is_empty() => Some(local), _ => None, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index e513f13ed85a5..d6db51dec52b9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -57,8 +57,8 @@ use crate::{ use super::{ AggregateKind, BasicBlockId, BinOp, CastKind, LocalId, MirBody, MirLowerError, MirSpan, - Operand, OperandKind, Place, PlaceElem, PlaceRef, PlaceTy, ProjectionElem, Rvalue, - StatementKind, TerminatorKind, UnOp, return_slot, + Operand, OperandKind, Place, PlaceElem, PlaceTy, ProjectionElem, Rvalue, StatementKind, + StoredPlace, TerminatorKind, UnOp, return_slot, }; mod shim; @@ -563,11 +563,11 @@ type Result<'db, T> = std::result::Result>; #[derive(Debug, Default)] struct DropFlags<'db> { - need_drop: FxHashSet>, + need_drop: FxHashSet>, } impl<'db> DropFlags<'db> { - fn add_place(&mut self, p: PlaceRef<'db>) { + fn add_place(&mut self, p: Place<'db>) { if p.iterate_over_parents().any(|it| self.need_drop.contains(&it)) { return; } @@ -575,7 +575,7 @@ impl<'db> DropFlags<'db> { self.need_drop.insert(p); } - fn remove_place(&mut self, p: PlaceRef<'db>) -> bool { + fn remove_place(&mut self, p: Place<'db>) -> bool { // FIXME: replace parents with parts if let Some(parent) = p.iterate_over_parents().find(|it| self.need_drop.contains(it)) { self.need_drop.remove(&parent); @@ -713,11 +713,11 @@ impl<'a, 'db> Evaluator<'a, 'db> { self.infcx.interner.lang_items() } - fn place_addr(&self, p: &Place, locals: &Locals<'a, 'db>) -> Result<'db, Address> { + fn place_addr(&self, p: &StoredPlace, locals: &Locals<'a, 'db>) -> Result<'db, Address> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.0) } - fn place_interval(&self, p: &Place, locals: &Locals<'a, 'db>) -> Result<'db, Interval> { + fn place_interval(&self, p: &StoredPlace, locals: &Locals<'a, 'db>) -> Result<'db, Interval> { let place_addr_and_ty = self.place_addr_and_ty_and_metadata(p, locals)?; Ok(Interval { addr: place_addr_and_ty.0, @@ -786,13 +786,13 @@ impl<'a, 'db> Evaluator<'a, 'db> { fn place_addr_and_ty_and_metadata<'b>( &'b self, - p: &Place, + p: &StoredPlace, locals: &'b Locals<'a, 'db>, ) -> Result<'db, (Address, Ty<'db>, Option)> { let mut addr = locals.ptr[p.local].addr; let mut ty = PlaceTy::from_ty(locals.body.locals[p.local].ty.as_ref()); let mut metadata: Option = None; // locals are always sized - for proj in p.projection.lookup() { + for proj in p.projection.as_slice() { let prev_ty = ty; ty = self.projected_ty(ty, *proj); match proj { @@ -814,8 +814,7 @@ impl<'a, 'db> Evaluator<'a, 'db> { self.read_memory(locals.ptr[*op].addr, self.ptr_size())? ); metadata = None; // Result of index is always sized - let ty_size = - self.size_of_sized(ty.ty, locals, "array inner type should be sized")?; + let ty_size = self.size_of_sized(ty.ty, locals, "array inner type")?; addr = addr.offset(ty_size * offset); } &ProjectionElem::ConstantIndex { from_end, offset } => { @@ -838,8 +837,7 @@ impl<'a, 'db> Evaluator<'a, 'db> { offset as usize }; metadata = None; // Result of index is always sized - let ty_size = - self.size_of_sized(ty.ty, locals, "array inner type should be sized")?; + let ty_size = self.size_of_sized(ty.ty, locals, "array inner type")?; addr = addr.offset(ty_size * offset); } &ProjectionElem::Subslice { from, to } => { @@ -856,8 +854,7 @@ impl<'a, 'db> Evaluator<'a, 'db> { } None => None, }; - let ty_size = - self.size_of_sized(inner_ty, locals, "array inner type should be sized")?; + let ty_size = self.size_of_sized(inner_ty, locals, "array inner type")?; addr = addr.offset(ty_size * (from as usize)); } ProjectionElem::Field(f) => { @@ -908,7 +905,11 @@ impl<'a, 'db> Evaluator<'a, 'db> { self.layout(Ty::new_adt(self.interner(), adt, subst)) } - fn place_ty<'b>(&'b self, p: &Place, locals: &'b Locals<'a, 'db>) -> Result<'db, Ty<'db>> { + fn place_ty<'b>( + &'b self, + p: &StoredPlace, + locals: &'b Locals<'a, 'db>, + ) -> Result<'db, Ty<'db>> { Ok(self.place_addr_and_ty_and_metadata(p, locals)?.1) } @@ -2176,7 +2177,7 @@ impl<'a, 'db> Evaluator<'a, 'db> { Ok(Interval::new(addr, size)) } - fn eval_place(&mut self, p: &Place, locals: &Locals<'a, 'db>) -> Result<'db, Interval> { + fn eval_place(&mut self, p: &StoredPlace, locals: &Locals<'a, 'db>) -> Result<'db, Interval> { let addr = self.place_addr(p, locals)?; Ok(Interval::new( addr, @@ -3081,7 +3082,7 @@ impl<'a, 'db> Evaluator<'a, 'db> { fn drop_place( &mut self, - place: &Place, + place: &StoredPlace, locals: &mut Locals<'a, 'db>, span: MirSpan, ) -> Result<'db, ()> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index ab0c25201a925..da631c0d595ea 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -46,8 +46,8 @@ use crate::{ mir::{ AggregateKind, Arena, BasicBlock, BasicBlockId, BinOp, BorrowKind, CastKind, Expr, FieldIndex, GenericArgs, Idx, InferenceResult, Local, LocalId, MemoryMap, MirBody, MirSpan, - Mutability, Operand, Place, PlaceElem, PointerCast, Projection, ProjectionElem, Rvalue, - Statement, StatementKind, SwitchTargets, Terminator, TerminatorKind, Ty, UnOp, VariantId, + Mutability, Operand, PlaceElem, PointerCast, Projection, ProjectionElem, Rvalue, Statement, + StatementKind, StoredPlace, SwitchTargets, Terminator, TerminatorKind, Ty, UnOp, VariantId, return_slot, }, next_solver::{ @@ -57,7 +57,7 @@ use crate::{ }, }; -use super::{OperandKind, PlaceRef}; +use super::{OperandKind, Place}; mod as_place; mod pattern_matching; @@ -69,7 +69,7 @@ struct LoopBlocks { begin: BasicBlockId, /// `None` for loops that are not terminating end: Option, - place: Place, + place: StoredPlace, drop_scope_index: usize, } @@ -85,7 +85,7 @@ struct MirLowerCtx<'a, 'db> { store_owner: ExpressionStoreOwnerId, current_loop_blocks: Option, labeled_loop_blocks: FxHashMap, - discr_temp: Option, + discr_temp: Option, db: &'db dyn HirDatabase, store: &'a ExpressionStore, infer: &'a InferenceResult<'db>, @@ -123,7 +123,7 @@ pub enum MirLowerError<'db> { LangItemNotFound, MutatingRvalue, UnresolvedLabel, - UnresolvedUpvar(Place), + UnresolvedUpvar(StoredPlace), InaccessibleLocal, // monomorphization errors: @@ -379,7 +379,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_with_adjust( &mut self, expr_id: ExprId, - place: PlaceRef<'db>, + place: Place<'db>, current: BasicBlockId, adjustments: &[Adjustment], ) -> Result<'db, Option> { @@ -439,7 +439,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_with_borrow_adjust( &mut self, expr_id: ExprId, - place: PlaceRef<'db>, + place: Place<'db>, current: BasicBlockId, rest: &[Adjustment], m: Mutability, @@ -457,7 +457,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place( &mut self, expr_id: ExprId, - place: PlaceRef<'db>, + place: Place<'db>, prev_block: BasicBlockId, ) -> Result<'db, Option> { if let Some(adjustments) = self.infer.expr_adjustments.get(&expr_id) { @@ -469,7 +469,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_expr_to_place_without_adjust( &mut self, expr_id: ExprId, - place: PlaceRef<'db>, + place: Place<'db>, mut current: BasicBlockId, ) -> Result<'db, Option> { match &self.store[expr_id] { @@ -649,10 +649,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { } Ok(self.merge_blocks(Some(then_target), else_target, expr_id.into())) } - Expr::Unsafe { id: _, statements, tail } => { - self.lower_block_to_place(statements, current, *tail, place, expr_id.into()) - } - Expr::Block { id: _, statements, tail, label } => { + Expr::Block { id: _, statements, tail, label, unsafe_: _ } => { if let Some(label) = label { self.lower_loop(current, place, Some(*label), expr_id.into(), |this, begin| { if let Some(current) = this.lower_block_to_place( @@ -1199,7 +1196,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { else { not_supported!("non-local capture"); }; - Ok(Place { + Ok(StoredPlace { local: this.binding_local(local)?, projection: Projection::new_from_iter(convert_closure_capture_projections( self.db, place, @@ -1328,7 +1325,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn push_field_projection( &mut self, - place: &mut PlaceRef<'db>, + place: &mut Place<'db>, expr_id: ExprId, ) -> Result<'db, ()> { if let Expr::Field { expr, name } = &self.store[expr_id] { @@ -1461,7 +1458,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, const_id: GeneralConstId<'db>, prev_block: BasicBlockId, - place: PlaceRef<'db>, + place: Place<'db>, subst: GenericArgs<'db>, span: MirSpan, ) -> Result<'db, ()> { @@ -1494,7 +1491,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn write_bytes_to_place( &mut self, prev_block: BasicBlockId, - place: PlaceRef<'db>, + place: Place<'db>, cv: Box<[u8]>, ty: Ty<'db>, span: MirSpan, @@ -1507,7 +1504,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, variant_id: EnumVariantId, prev_block: BasicBlockId, - place: PlaceRef<'db>, + place: Place<'db>, ty: Ty<'db>, fields: Box<[Operand]>, span: MirSpan, @@ -1529,7 +1526,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: impl Iterator, - place: PlaceRef<'db>, + place: Place<'db>, mut current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1554,7 +1551,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { &mut self, func: Operand, args: Box<[Operand]>, - place: PlaceRef<'db>, + place: Place<'db>, current: BasicBlockId, is_uninhabited: bool, span: MirSpan, @@ -1605,27 +1602,27 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.result.basic_blocks[block].statements.push(statement); } - fn push_fake_read(&mut self, block: BasicBlockId, p: PlaceRef<'db>, span: MirSpan) { + fn push_fake_read(&mut self, block: BasicBlockId, p: Place<'db>, span: MirSpan) { self.push_statement(block, StatementKind::FakeRead(p.store()).with_span(span)); } fn push_assignment( &mut self, block: BasicBlockId, - place: PlaceRef<'db>, + place: Place<'db>, rvalue: Rvalue, span: MirSpan, ) { self.push_statement(block, StatementKind::Assign(place.store(), rvalue).with_span(span)); } - fn discr_temp_place(&mut self, current: BasicBlockId) -> PlaceRef<'db> { + fn discr_temp_place(&mut self, current: BasicBlockId) -> Place<'db> { match &self.discr_temp { Some(it) => it.as_ref(), None => { // FIXME: rustc's ty is dependent on the adt type, maybe we need to do that as well let discr_ty = Ty::new_int(self.interner(), rustc_type_ir::IntTy::I128); - let tmp: PlaceRef<'_> = self + let tmp: Place<'_> = self .temp(discr_ty, current, MirSpan::Unknown) .expect("discr_ty is never unsized") .into(); @@ -1638,7 +1635,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { fn lower_loop( &mut self, prev_block: BasicBlockId, - place: PlaceRef<'db>, + place: Place<'db>, label: Option, span: MirSpan, f: impl FnOnce(&mut MirLowerCtx<'_, 'db>, BasicBlockId) -> Result<'db, ()>, @@ -1749,7 +1746,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { statements: &[hir_def::hir::Statement], mut current: BasicBlockId, tail: Option, - place: PlaceRef<'db>, + place: Place<'db>, span: MirSpan, ) -> Result<'db, Option>> { let scope = self.push_drop_scope(); @@ -2002,7 +1999,7 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> { self.set_terminator( prev, TerminatorKind::Drop { - place: PlaceRef::from(l).store(), + place: Place::from(l).store(), target: *current, unwind: None, }, @@ -2119,12 +2116,14 @@ pub fn mir_body_for_closure_query<'db>( projections.push(ProjectionElem::Deref); } projections.push(ProjectionElem::Field(FieldIndex(capture_idx as u32))); - let capture_param_place = Place { + let capture_param_place = StoredPlace { local: closure_local, projection: Projection::new_from_slice(&projections).store(), }; - let capture_local_place = - Place { local: capture_local, projection: Projection::new_from_slice(&[]).store() }; + let capture_local_place = StoredPlace { + local: capture_local, + projection: Projection::new_from_slice(&[]).store(), + }; let capture_local_rvalue = Rvalue::Use(Operand { kind: OperandKind::Move(capture_param_place), span: None }); ctx.push_assignment( @@ -2152,7 +2151,7 @@ pub fn mir_body_for_closure_query<'db>( } let mut err = None; ctx.result.walk_places(|mir_place| { - let mir_projections = mir_place.projection.lookup(); + let mir_projections = mir_place.projection.as_slice(); if let Some(hir_places) = upvar_map.get(&mir_place.local) { let projections = hir_places.iter().find_map(|hir_place| { let iter = mir_projections diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs index 71014eb661742..beb1d317d1101 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs @@ -14,7 +14,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, expr_id: ExprId, prev_block: BasicBlockId, - ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId)>> { let ty = self.expr_ty_without_adjust(expr_id); let place = self.temp(ty, prev_block, expr_id.into())?.into(); let Some(current) = self.lower_expr_to_place_without_adjust(expr_id, place, prev_block)? @@ -29,7 +29,7 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, prev_block: BasicBlockId, adjustments: &[Adjustment], - ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId)>> { let ty = adjustments .last() .map(|it| it.target.as_ref()) @@ -49,7 +49,7 @@ impl<'db> MirLowerCtx<'_, 'db> { expr_id: ExprId, upgrade_rvalue: bool, adjustments: &[Adjustment], - ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -107,7 +107,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId)>> { match self.infer.expr_adjustments.get(&expr_id) { Some(a) => self.lower_expr_as_place_with_adjust(current, expr_id, upgrade_rvalue, a), None => self.lower_expr_as_place_without_adjust(current, expr_id, upgrade_rvalue), @@ -119,7 +119,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, expr_id: ExprId, upgrade_rvalue: bool, - ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId)>> { let try_rvalue = |this: &mut MirLowerCtx<'_, 'db>| { if !upgrade_rvalue { return Err(MirLowerError::MutatingRvalue); @@ -262,13 +262,13 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_overloaded_index( &mut self, current: BasicBlockId, - place: PlaceRef<'db>, + place: Place<'db>, base_ty: Ty<'db>, result_ty: Ty<'db>, index_operand: Operand, span: MirSpan, index_fn: (FunctionId, GenericArgs<'db>), - ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId)>> { let mutability = match base_ty.as_reference() { Some((_, _, mutability)) => mutability, None => Mutability::Not, @@ -302,12 +302,12 @@ impl<'db> MirLowerCtx<'_, 'db> { fn lower_overloaded_deref( &mut self, current: BasicBlockId, - place: PlaceRef<'db>, + place: Place<'db>, source_ty: Ty<'db>, target_ty: Ty<'db>, span: MirSpan, mutability: bool, - ) -> Result<'db, Option<(PlaceRef<'db>, BasicBlockId)>> { + ) -> Result<'db, Option<(Place<'db>, BasicBlockId)>> { let lang_items = self.lang_items(); let (mutability, deref_fn, borrow_kind) = if !mutability { (Mutability::Not, lang_items.Deref_deref, BorrowKind::Shared) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs index 44f410408a40a..682ae827db67d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs @@ -9,7 +9,7 @@ use rustc_type_ir::inherent::{IntoKind, Ty as _}; use crate::{ BindingMode, ByRef, mir::{ - FieldIndex, LocalId, MutBorrowKind, Operand, OperandKind, PlaceRef, Projection, + FieldIndex, LocalId, MutBorrowKind, Operand, OperandKind, Place, Projection, lower::{ BasicBlockId, BinOp, BindingId, BorrowKind, Expr, Idx, MemoryMap, MirLowerCtx, MirLowerError, MirSpan, Pat, PatId, PlaceElem, ProjectionElem, ResolveValueResult, @@ -67,7 +67,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, current: BasicBlockId, current_else: Option, - cond_place: PlaceRef<'db>, + cond_place: Place<'db>, pattern: PatId, ) -> Result<'db, (BasicBlockId, Option)> { let (current, current_else) = self.pattern_match_inner( @@ -90,7 +90,7 @@ impl<'db> MirLowerCtx<'_, 'db> { pub(super) fn pattern_match_assignment( &mut self, current: BasicBlockId, - value: PlaceRef<'db>, + value: Place<'db>, pattern: PatId, ) -> Result<'db, BasicBlockId> { let (current, _) = @@ -118,7 +118,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, mut current: BasicBlockId, mut current_else: Option, - mut cond_place: PlaceRef<'db>, + mut cond_place: Place<'db>, pattern: PatId, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { @@ -527,7 +527,7 @@ impl<'db> MirLowerCtx<'_, 'db> { &mut self, id: BindingId, mode: BindingMode, - cond_place: PlaceRef<'db>, + cond_place: Place<'db>, span: MirSpan, current: BasicBlockId, current_else: Option, @@ -543,7 +543,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current: BasicBlockId, target_place: LocalId, mode: BindingMode, - cond_place: PlaceRef<'db>, + cond_place: Place<'db>, span: MirSpan, ) { self.push_assignment( @@ -570,7 +570,7 @@ impl<'db> MirLowerCtx<'_, 'db> { current_else: Option, current: BasicBlockId, c: Operand, - cond_place: PlaceRef<'db>, + cond_place: Place<'db>, pattern: Idx, ) -> Result<'db, (BasicBlockId, Option)> { let then_target = self.new_basic_block(); @@ -600,7 +600,7 @@ impl<'db> MirLowerCtx<'_, 'db> { fn pattern_matching_variant( &mut self, - cond_place: PlaceRef<'db>, + cond_place: Place<'db>, variant: VariantId, mut current: BasicBlockId, span: MirSpan, @@ -671,7 +671,7 @@ impl<'db> MirLowerCtx<'_, 'db> { v: VariantId, current: BasicBlockId, current_else: Option, - cond_place: &PlaceRef<'db>, + cond_place: &Place<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { let downcast_place = if matches!(v, VariantId::EnumVariantId(_)) { @@ -718,7 +718,7 @@ impl<'db> MirLowerCtx<'_, 'db> { mut current: BasicBlockId, mut current_else: Option, args: impl Iterator, - cond_place: &PlaceRef<'db>, + cond_place: &Place<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { for (proj, arg) in args { @@ -736,7 +736,7 @@ impl<'db> MirLowerCtx<'_, 'db> { args: &[PatId], ellipsis: Option, fields: impl DoubleEndedIterator + Clone, - cond_place: &PlaceRef<'db>, + cond_place: &Place<'db>, mode: MatchingMode, ) -> Result<'db, (BasicBlockId, Option)> { let (al, ar) = args.split_at(ellipsis.map_or(args.len(), |it| it as usize)); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs index 4a51b5113a436..3498d88b4f756 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs @@ -25,7 +25,7 @@ use crate::{ use super::{ AggregateKind, BasicBlockId, BorrowKind, LocalId, MirBody, MutBorrowKind, Operand, OperandKind, - Place, Rvalue, UnOp, + Rvalue, StoredPlace, UnOp, }; macro_rules! w { @@ -319,7 +319,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { } } - fn place(&mut self, p: &Place) { + fn place(&mut self, p: &StoredPlace) { fn f<'db>(this: &mut MirPrettyCtx<'_, 'db>, local: LocalId, projections: &[PlaceElem]) { let Some((last, head)) = projections.split_last() else { // no projection @@ -398,7 +398,7 @@ impl<'a, 'db> MirPrettyCtx<'a, 'db> { } } } - f(self, p.local, p.projection.lookup()); + f(self, p.local, p.projection.as_slice()); } fn operand(&mut self, r: &Operand) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs index c19a1f27af96e..2e433e8be8909 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs @@ -527,7 +527,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String { if store.expr_roots().next().is_none() { continue; } - for &anon_const in AnonConstId::all_from_signature(&db, def).into_iter().flatten() { + for anon_const in AnonConstId::all_from_signature(&db, def) { let infer = InferenceResult::of(&db, anon_const); infer_def(infer, store, source_map, None, krate); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs index efbb49b0eeb81..db21c4cf949aa 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/display_source_code.rs @@ -85,7 +85,7 @@ fn render_dyn_for_ty() { trait Foo<'a> {} fn foo(foo: &dyn for<'a> Foo<'a>) {} - // ^^^ &(dyn Foo<'_> + 'static) + // ^^^ &(dyn Foo<'?0.0> + 'static) "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index ff0e075ff6d69..7934ffec28745 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -3039,6 +3039,7 @@ fn array_repeat_closure() { r#" fn f() {[_; || ()]} // ^^^^^^^^^^ expected (), got [{unknown}; _] + // ^^^^^ expected usize, got impl Fn() "#, ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 97921e8ab92d5..0a6250c450f8b 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -4395,7 +4395,7 @@ fn hrtb_fn_ptr() { fn foo<'b>(f: for <'a> fn(&'a u32, &'b u32)) {} "#, expect![[r#" - 12..13 'f': fn(&'_ u32, &'_ u32) + 12..13 'f': fn(&'?0.0 u32, &'_ u32) 46..48 '{}': () "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index fad944589dab6..2c107da42ee9a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -5372,7 +5372,7 @@ impl<'a, 'b> Trait<'a, 'b> for Foo {} fn run_dyn<'b>(val: &dyn for<'a> Trait<'a, 'b>) {} "#, expect![[r#" - 91..94 'val': &'? (dyn Trait<'_, '_> + 'static) + 91..94 'val': &'? (dyn Trait<'?0.0, '_> + 'static) 124..126 '{}': () "#]], ); diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index 9a2f1fd11d6a4..5c3b628f386d5 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -3,38 +3,66 @@ //! //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. +use std::mem::discriminant; + use cfg::{CfgExpr, CfgOptions}; use either::Either; use hir_def::{ - DefWithBodyId, GenericParamId, HasModule, SyntheticSyntax, + AdtId, AssocItemId, DefWithBodyId, EnumId, EnumVariantId, GenericDefId, GenericParamId, ImplId, + Lookup, MacroId, ModuleDefId, ModuleId, SyntheticSyntax, TraitId, + attrs::AttrFlags, expr_store::{ - ExprOrPatPtr, ExpressionStoreSourceMap, hir_assoc_type_binding_to_ast, - hir_generic_arg_to_ast, hir_segment_to_ast_segment, + Body, ExprOrPatPtr, ExpressionStore, ExpressionStoreDiagnostics, ExpressionStoreSourceMap, + hir_assoc_type_binding_to_ast, hir_generic_arg_to_ast, hir_segment_to_ast_segment, }, hir::{ExprId, ExprOrPatId, PatId}, + nameres::{ + DefMap, + assoc::{ImplItems, TraitItems}, + diagnostics::{DefDiagnosticKind, DefDiagnostics}, + }, + signatures::{ + ConstSignature, FunctionSignature, ImplFlags, ImplSignature, TraitFlags, TraitSignature, + TypeAliasSignature, + }, type_ref::TypeRefId, + unstable_features::UnstableFeatures, +}; +use hir_expand::{ + HirFileId, InFile, MacroCallId, MacroCallKind, MacroKind, RenderedExpandError, ValueResult, + mod_path::ModPath, name::Name, }; -use hir_expand::{HirFileId, InFile, mod_path::ModPath, name::Name}; use hir_ty::{ - CastError, ExplicitDropMethodUseKind, InferenceDiagnostic, InferenceTyDiagnosticSource, - PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, - db::HirDatabase, + CastError, ExplicitDropMethodUseKind, InferBodyId, InferenceDiagnostic, InferenceResult, + ParamEnvAndCrate, PathGenericsSource, PathLoweringDiagnostic, TyLoweringDiagnostic, + check_orphan_rules, + db::{AnonConstId, HirDatabase, signature_anon_consts_and_diagnostics}, diagnostics::{BodyValidationDiagnostic, UnsafetyReason}, display::{DisplayTarget, HirDisplay}, - next_solver::{DbInterner, EarlyBinder}, + method_resolution::TraitImpls, + next_solver::{ + DbInterner, EarlyBinder, TyKind, TypingMode, + infer::{DbInternerInferExt, InferCtxt}, + }, solver_errors::SolverDiagnosticKind, + traits::{is_inherent_impl_coherent, structurally_normalize_ty}, }; +use rustc_type_ir::inherent::IntoKind as _; +use span::Edition; use stdx::{impl_from, never}; use syntax::{ AstNode, AstPtr, SyntaxError, SyntaxNodePtr, TextRange, - ast::{self, HasGenericArgs}, + ast::{self, HasGenericArgs, HasName}, match_ast, }; use triomphe::Arc; -use crate::{AssocItem, Field, Function, GenericDef, Trait, Type, TypeOwnerId, Variant}; +use crate::{ + AnyFunctionId, AssocItem, Field, Function, GenericDef, Trait, Type, TypeOwnerId, Variant, + struct_tail_raw, +}; -pub use hir_def::VariantId; +pub use hir_def::{VariantId, expr_store::MissingBodyItemKind}; pub use hir_ty::{ GenericArgsProhibitedReason, IncorrectGenericsLenKind, ReturnKind, diagnostics::{CaseType, IncorrectCase}, @@ -113,6 +141,7 @@ diagnostics![AnyDiagnostic<'db> -> ExpectedFunction<'db>, ExplicitDropMethodUse, FruInDestructuringAssignment, + MissingBody, FunctionalRecordUpdateOnNonStruct, GenericDefaultRefersToSelf, InactiveCode, @@ -373,6 +402,12 @@ pub struct FruInDestructuringAssignment { pub node: InFile>, } +#[derive(Debug)] +pub struct MissingBody { + pub node: InFile, + pub kind: MissingBodyItemKind, +} + #[derive(Debug)] pub struct FunctionalRecordUpdateOnNonStruct { pub base_expr: InFile, @@ -681,8 +716,701 @@ pub struct ReturnOutsideFunction { pub kind: ReturnKind, } +pub(crate) struct DiagnosticsCollector<'a, 'db> { + db: &'db dyn HirDatabase, + krate: base_db::Crate, + edition: Edition, + style_lints: bool, + acc: &'a mut Vec>, +} + +fn precise_macro_call_location( + ast: &MacroCallKind, + db: &dyn HirDatabase, + krate: base_db::Crate, +) -> InFile { + // FIXME: maybe we actually want slightly different ranges for the different macro diagnostics + // - e.g. the full attribute for macro errors, but only the name for name resolution + match ast { + MacroCallKind::FnLike { ast_id, .. } => { + let node = ast_id.to_node(db); + let range = node + .path() + .and_then(|it| it.segment()) + .and_then(|it| it.name_ref()) + .map(|it| it.syntax().text_range()); + let range = range.unwrap_or_else(|| node.syntax().text_range()); + ast_id.with_value(range) + } + MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { + let range = derive_attr_index.find_derive_range(db, krate, *ast_id, *derive_index); + ast_id.with_value(range) + } + MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { + let attr_range = + attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range(); + ast_id.with_value(attr_range) + } + } +} + +impl<'a, 'db> DiagnosticsCollector<'a, 'db> { + pub(crate) fn collect( + db: &'db dyn HirDatabase, + module: ModuleId, + acc: &'a mut Vec>, + style_lints: bool, + ) { + let krate = module.krate(db); + DiagnosticsCollector { db, krate, edition: krate.data(db).edition, style_lints, acc } + .collect_module(module); + } + + fn emit_def_diagnostic(&mut self, diag: &DefDiagnosticKind) { + match diag { + DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => { + let decl = declaration.to_ptr(self.db); + self.acc.push( + UnresolvedModule { + decl: InFile::new(declaration.file_id, decl), + candidates: candidates.clone(), + } + .into(), + ) + } + DefDiagnosticKind::UnresolvedExternCrate { ast } => { + let item = ast.to_ptr(self.db); + self.acc + .push(UnresolvedExternCrate { decl: InFile::new(ast.file_id, item) }.into()); + } + + DefDiagnosticKind::MacroError { ast, path, err } => { + let item = ast.to_ptr(self.db); + let RenderedExpandError { message, error, kind } = err.render_to_string(self.db); + self.acc.push( + MacroError { + range: InFile::new(ast.file_id, item.text_range()), + message: format!("{}: {message}", path.display(self.db, self.edition)), + error, + kind, + } + .into(), + ) + } + DefDiagnosticKind::UnresolvedImport { id, index } => { + let file_id = id.file_id; + + let use_tree = hir_def::src::use_tree_to_ast(self.db, *id, *index); + self.acc.push( + UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) }.into(), + ); + } + + DefDiagnosticKind::UnconfiguredCode { ast_id, cfg, opts } => { + let ast_id_map = ast_id.file_id.ast_id_map(self.db); + let ptr = ast_id_map.get_erased(ast_id.value); + self.acc.push( + InactiveCode { + node: InFile::new(ast_id.file_id, ptr), + cfg: cfg.clone(), + opts: opts.clone(), + } + .into(), + ); + } + DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { + let location = precise_macro_call_location(ast, self.db, self.krate); + self.acc.push( + UnresolvedMacroCall { + range: location, + path: path.clone(), + is_bang: matches!(ast, MacroCallKind::FnLike { .. }), + } + .into(), + ); + } + DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { + let node = ast.to_node(self.db); + // Must have a name, otherwise we wouldn't emit it. + let name = node.name().expect("unimplemented builtin macro with no name"); + self.acc.push( + UnimplementedBuiltinMacro { + node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&name))), + } + .into(), + ); + } + DefDiagnosticKind::InvalidDeriveTarget { ast, id } => { + let (_, attr) = id.find_attr_range(self.db, self.krate, *ast); + let derive = attr + .path() + .map(|path| path.syntax().text_range()) + .unwrap_or_else(|| attr.syntax().text_range()); + self.acc.push(InvalidDeriveTarget { range: ast.with_value(derive) }.into()); + } + DefDiagnosticKind::MalformedDerive { ast, id } => { + let derive = id.find_attr_range(self.db, self.krate, *ast).1.syntax().text_range(); + self.acc.push(MalformedDerive { range: ast.with_value(derive) }.into()); + } + DefDiagnosticKind::MacroDefError { ast, message } => { + let node = ast.to_node(self.db); + self.acc.push( + MacroDefError { + node: InFile::new(ast.file_id, AstPtr::new(&node)), + name: node.name().map(|it| it.syntax().text_range()), + message: message.clone(), + } + .into(), + ); + } + } + } + + fn emit_def_diagnostics(&mut self, diagnostics: &DefDiagnostics) { + diagnostics.iter().for_each(|diag| self.emit_def_diagnostic(&diag.kind)); + } + + fn emit_case_diagnostics(&mut self, def: ModuleDefId) { + self.acc + .extend(hir_ty::diagnostics::incorrect_case(self.db, def).into_iter().map(Into::into)); + } + + fn collect_macro_call(&mut self, macro_call_id: MacroCallId) { + let Some(e) = macro_call_id.parse_macro_expansion_error(self.db) else { + return; + }; + let ValueResult { value: parse_errors, err } = e; + if let Some(err) = err { + let loc = macro_call_id.loc(self.db); + let file_id = loc.kind.file_id(); + let mut range = precise_macro_call_location(&loc.kind, self.db, loc.krate); + let RenderedExpandError { message, error, kind } = err.render_to_string(self.db); + if Some(err.span().anchor.file_id) + == file_id.file_id().map(|it| it.span_file_id(self.db)) + { + range.value = err.span().range + + file_id + .ast_id_map(self.db) + .get_erased(err.span().anchor.ast_id) + .text_range() + .start(); + } + self.acc.push(MacroError { range, message, error, kind }.into()); + } + + if !parse_errors.is_empty() { + let loc = macro_call_id.loc(self.db); + let range = precise_macro_call_location(&loc.kind, self.db, loc.krate); + self.acc.push(MacroExpansionParseError { range, errors: parse_errors.clone() }.into()) + } + } + + fn collect_assoc_items(&mut self, defs: &[(Name, AssocItemId)], def_map: &DefMap) { + for &(_, def) in defs { + self.collect_module_def(def.into(), def_map); + } + } + + fn collect_trait(&mut self, def: TraitId, def_map: &DefMap) { + let (signature, source_map) = TraitSignature::with_source_map(self.db, def); + let items = TraitItems::query_with_diagnostics(self.db, def); + + self.collect_generic_def(&signature.store, source_map, def.into()); + self.emit_def_diagnostics(&items.1); + items.0.macro_calls().for_each(|(_, call)| self.collect_macro_call(call)); + self.collect_assoc_items(&items.0.items, def_map); + } + + fn collect_impl( + &mut self, + def: ImplId, + infcx: &InferCtxt<'db>, + def_map: &'db DefMap, + impl_assoc_items_scratch: &mut Vec<(Name, AssocItemId)>, + ) { + let (impl_signature, source_map) = ImplSignature::with_source_map(self.db, def); + let impl_items = ImplItems::of(self.db, def); + + self.collect_generic_def(&impl_signature.store, source_map, def.into()); + self.emit_def_diagnostics(&impl_items.1); + impl_items.0.macro_calls().for_each(|(_, call)| self.collect_macro_call(call)); + self.collect_assoc_items(&impl_items.0.items, def_map); + + let loc = def.lookup(self.db); + + let file_id = loc.id.file_id; + if file_id.macro_file().is_some_and(|it| it.kind(self.db) == MacroKind::DeriveBuiltIn) { + // these expansion come from us, diagnosing them is a waste of resources + // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow + return; + } + + let ast_id_map = file_id.ast_id_map(self.db); + + let trait_impl = impl_signature.target_trait.is_some(); + if !trait_impl && !is_inherent_impl_coherent(self.db, def_map, def) { + self.acc.push(IncoherentImpl { impl_: ast_id_map.get(loc.id.value), file_id }.into()) + } + + if trait_impl && !check_orphan_rules(self.db, def) { + self.acc.push(TraitImplOrphan { impl_: ast_id_map.get(loc.id.value), file_id }.into()) + } + + let trait_ = trait_impl + .then(|| self.db.impl_trait(def)) + .flatten() + .map(|trait_ref| trait_ref.instantiate_identity().skip_norm_wip().def_id.0); + let mut trait_is_unsafe = trait_.is_some_and(|trait_| { + TraitSignature::of(self.db, trait_).flags.contains(TraitFlags::UNSAFE) + }); + let impl_is_negative = impl_signature.is_negative(); + let impl_is_unsafe = impl_signature.flags.contains(ImplFlags::UNSAFE); + + let trait_is_unresolved = trait_.is_none() && trait_impl; + if trait_is_unresolved { + // Ignore trait safety errors when the trait is unresolved, as otherwise we'll treat it as safe, + // which may not be correct. + trait_is_unsafe = impl_is_unsafe; + } + + let drop_maybe_dangle = (|| { + let trait_ = trait_?; + let drop_trait = infcx.interner.lang_items().Drop?; + if drop_trait != trait_ { + return None; + } + let parent = def.into(); + let (lifetimes_attrs, type_and_consts_attrs) = + AttrFlags::query_generic_params(self.db, parent); + let res = lifetimes_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE)) + || type_and_consts_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE)); + Some(res) + })() + .unwrap_or(false); + + match (impl_is_unsafe, trait_is_unsafe, impl_is_negative, drop_maybe_dangle) { + // unsafe negative impl + (true, _, true, _) | + // unsafe impl for safe trait + (true, false, _, false) => self.acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(loc.id.value), file_id, should_be_safe: true }.into()), + // safe impl for unsafe trait + (false, true, false, _) | + // safe impl of dangling drop + (false, false, _, true) => self.acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(loc.id.value), file_id, should_be_safe: false }.into()), + _ => (), + }; + + // Negative impls can't have items, don't emit missing items diagnostic for them + if let (false, Some(trait_)) = (impl_is_negative, trait_) { + let trait_items = &trait_.trait_items(self.db).items; + let required_items = trait_items.iter().filter(|&(_, assoc)| match *assoc { + AssocItemId::FunctionId(it) => !FunctionSignature::of(self.db, it).has_body(), + AssocItemId::ConstId(id) => !ConstSignature::of(self.db, id).has_body(), + AssocItemId::TypeAliasId(it) => TypeAliasSignature::of(self.db, it).ty.is_none(), + }); + impl_assoc_items_scratch.extend(impl_items.0.items.iter().cloned()); + + let redundant = impl_assoc_items_scratch + .iter() + .filter(|(name, id)| { + !trait_items.iter().any(|(impl_name, impl_item)| { + discriminant(impl_item) == discriminant(id) && impl_name == name + }) + }) + .map(|(name, item)| (name.clone(), AssocItem::from(*item))); + for (name, assoc_item) in redundant { + self.acc.push( + TraitImplRedundantAssocItems { + trait_: trait_.into(), + file_id, + impl_: ast_id_map.get(loc.id.value), + assoc_item: (name, assoc_item), + } + .into(), + ) + } + + let mut missing: Vec<_> = required_items + .filter(|(name, id)| { + !impl_assoc_items_scratch.iter().any(|(impl_name, impl_item)| { + discriminant(impl_item) == discriminant(id) && impl_name == name + }) + }) + .map(|(name, item)| (name.clone(), AssocItem::from(*item))) + .collect(); + + if !missing.is_empty() { + let env = ParamEnvAndCrate { + param_env: self.db.trait_environment(def.into()), + krate: self.krate, + }; + let self_ty = self.db.impl_self_ty(def).instantiate_identity().skip_norm_wip(); + let self_ty = structurally_normalize_ty(infcx, self_ty, env.param_env); + let tail_ty = struct_tail_raw(self.db, infcx.interner, self_ty, |ty| { + structurally_normalize_ty(infcx, ty, env.param_env) + }); + let self_ty_is_guaranteed_unsized = + matches!(tail_ty.kind(), TyKind::Dynamic(..) | TyKind::Slice(..) | TyKind::Str); + if self_ty_is_guaranteed_unsized { + missing.retain(|(_, assoc_item)| { + let assoc_item = match *assoc_item { + AssocItem::Function(it) => match it.id { + AnyFunctionId::FunctionId(id) => id.into(), + AnyFunctionId::BuiltinDeriveImplMethod { .. } => { + never!("should not have an `AnyFunctionId::BuiltinDeriveImplMethod` here"); + return false; + }, + }, + AssocItem::Const(it) => it.id.into(), + AssocItem::TypeAlias(it) => it.id.into(), + }; + !hir_ty::dyn_compatibility::generics_require_sized_self(self.db, assoc_item) + }); + } + } + + // HACK: When specialization is enabled in the current crate, and there exists + // *any* blanket impl that provides a default implementation for the missing item, + // suppress the missing associated item diagnostic. + // This can lead to false negatives when the impl in question does not actually + // specialize that blanket impl, but determining the exact specialization + // relationship here would be significantly more expensive. + if !missing.is_empty() { + let features = UnstableFeatures::query(self.db, self.krate); + if features.specialization || features.min_specialization { + missing.retain(|(assoc_name, assoc_item)| { + let AssocItem::Function(_) = assoc_item else { + return true; + }; + + for &impl_ in + TraitImpls::for_crate(self.db, self.krate).blanket_impls(trait_) + { + if impl_ == def { + continue; + } + + for (name, item) in &impl_.impl_items(self.db).items { + let AssocItemId::FunctionId(fn_) = item else { + continue; + }; + if name != assoc_name { + continue; + } + + if FunctionSignature::of(self.db, *fn_).is_default() { + return false; + } + } + } + + true + }); + } + } + + if !missing.is_empty() { + self.acc.push( + TraitImplMissingAssocItems { + impl_: ast_id_map.get(loc.id.value), + file_id, + missing, + } + .into(), + ) + } + impl_assoc_items_scratch.clear(); + } + } + + fn collect_module(&mut self, def: ModuleId) { + let _p = tracing::info_span!("diagnostics", name = ?def.name(self.db)).entered(); + + let def_map = def.def_map(self.db); + let scope = &def_map[def].scope; + + for diag in def_map.diagnostics() { + if diag.in_module != def { + // FIXME: This is accidentally quadratic. + continue; + } + self.emit_def_diagnostic(&diag.kind); + } + + if !def.is_block_module(self.db) { + // These are reported by the body of block modules + scope.all_macro_calls().for_each(|call| self.collect_macro_call(call)); + } + + scope + .declarations() + .chain(scope.unnamed_consts().map(ModuleDefId::ConstId)) + .for_each(|def| self.collect_module_def(def, def_map)); + + scope.legacy_macros().flat_map(|(_, it)| it).for_each(|&def| { + self.emit_case_diagnostics(def.into()); + self.collect_macro_def(def); + }); + + let interner = DbInterner::new_with(self.db, self.krate); + let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); + let mut impl_assoc_items_scratch = Vec::new(); + scope.impls().for_each(|def| { + impl_assoc_items_scratch.clear(); + self.collect_impl(def, &infcx, def_map, &mut impl_assoc_items_scratch) + }); + } + + fn collect_macro_def(&mut self, def: MacroId) { + let id = def.definition(self.db); + if let hir_expand::MacroDefKind::Declarative(ast, _) = id.kind + && let expander = ast.decl_macro_expander(self.db, id.krate) + && let Some(e) = expander.mac.err() + { + self.emit_def_diagnostic(&DefDiagnosticKind::MacroDefError { + ast, + message: e.to_string(), + }); + } + } + + fn collect_anon_const(&mut self, source_map: &ExpressionStoreSourceMap, def: AnonConstId<'db>) { + self.emit_inference_errors(def.into(), source_map, def.into()); + } + + fn collect_enum(&mut self, def: EnumId) { + self.collect_only_generic_def(def); + + let variants = def.enum_variants_with_diagnostics(self.db); + variants.0.variants.values().for_each(|&(def, _)| self.collect_enum_variant(def)); + + let file = def.lookup(self.db).id.file_id; + let ast_id_map = file.ast_id_map(self.db); + for diag in &variants.1 { + self.acc.push( + InactiveCode { + node: InFile::new(file, ast_id_map.get(diag.ast_id).syntax_node_ptr()), + cfg: diag.cfg.clone(), + opts: diag.opts.clone(), + } + .into(), + ); + } + } + + fn collect_enum_variant(&mut self, def: EnumVariantId) { + self.collect_def_with_body( + def.into(), + TypeOwnerId::GenericDefId(def.loc(self.db).parent.into()), + ); + self.collect_variant(def.into()); + } + + fn collect_anon_consts_and_ty_diagnostics( + &mut self, + source_map: &ExpressionStoreSourceMap, + anon_consts: &[AnonConstId<'db>], + diagnostics: &[TyLoweringDiagnostic], + ) { + anon_consts.iter().for_each(|&anon_const| self.collect_anon_const(source_map, anon_const)); + + diagnostics + .iter() + .filter_map(|diag| AnyDiagnostic::ty_diagnostic(diag, source_map, self.db)) + .for_each(|diag| self.acc.push(diag)); + } + + fn collect_generic_def( + &mut self, + store: &ExpressionStore, + source_map: &ExpressionStoreSourceMap, + def: GenericDefId, + ) { + self.collect_expr_store(store, source_map); + for (anon_consts, diagnostics) in signature_anon_consts_and_diagnostics(self.db, def) { + self.collect_anon_consts_and_ty_diagnostics(source_map, anon_consts, diagnostics); + } + } + + fn collect_def_with_body(&mut self, def: DefWithBodyId, type_owner: TypeOwnerId<'db>) { + let (body, source_map) = Body::with_source_map(self.db, def); + + self.collect_expr_store(body, source_map); + self.emit_inference_errors(def.into(), source_map, type_owner); + + // FIXME: Missing unsafe and body validation should be defined for any `InferBodyId`. + let missing_unsafe = hir_ty::diagnostics::missing_unsafe(self.db, def); + for (node, reason) in missing_unsafe.unsafe_exprs { + match source_map.expr_or_pat_syntax(node) { + Ok(node) => self.acc.push( + MissingUnsafe { + node, + lint: if missing_unsafe.fn_is_unsafe { + UnsafeLint::UnsafeOpInUnsafeFn + } else { + UnsafeLint::HardError + }, + reason, + } + .into(), + ), + Err(SyntheticSyntax) => { + // FIXME: Here and elsewhere in this file, the `expr` was + // desugared, report or assert that this doesn't happen. + } + } + } + for node in missing_unsafe.deprecated_safe_calls { + match source_map.expr_syntax(node) { + Ok(node) => self.acc.push( + MissingUnsafe { + node, + lint: UnsafeLint::DeprecatedSafe2024, + reason: UnsafetyReason::UnsafeFnCall, + } + .into(), + ), + Err(SyntheticSyntax) => never!("synthetic DeprecatedSafe2024"), + } + } + + for diagnostic in BodyValidationDiagnostic::collect(self.db, def, self.style_lints) { + self.acc + .extend(AnyDiagnostic::body_validation_diagnostic(self.db, diagnostic, source_map)); + } + } + + fn emit_inference_errors( + &mut self, + def: InferBodyId<'db>, + source_map: &ExpressionStoreSourceMap, + type_owner: TypeOwnerId<'db>, + ) { + let infer = InferenceResult::of(self.db, def); + + self.acc.extend(infer.diagnostics().iter().filter_map(|diag| { + AnyDiagnostic::inference_diagnostic( + self.db, + self.krate, + self.edition, + diag, + source_map, + type_owner, + ) + })); + } + + fn collect_variant(&mut self, def: VariantId) { + let (fields, source_map) = def.fields_with_source_map(self.db); + self.collect_expr_store(&fields.store, source_map); + + let lowering = self.db.field_types_with_diagnostics(def); + self.collect_anon_consts_and_ty_diagnostics( + source_map, + lowering.defined_anon_consts(), + lowering.diagnostics(), + ); + } + + fn collect_generic_def_with_body( + &mut self, + def: impl Into + Into + Copy, + ) { + let generic_def: GenericDefId = def.into(); + let (signature_store, signature_source_map) = + ExpressionStore::with_source_map(self.db, generic_def.into()); + self.collect_generic_def(signature_store, signature_source_map, generic_def); + + let def_with_body: DefWithBodyId = def.into(); + self.collect_def_with_body(def_with_body, generic_def.into()); + } + + fn collect_generic_variant(&mut self, def: impl Into + Into + Copy) { + let generic_def: GenericDefId = def.into(); + let (store, source_map) = ExpressionStore::with_source_map(self.db, generic_def.into()); + self.collect_generic_def(store, source_map, generic_def); + self.collect_variant(def.into()); + } + + fn collect_only_generic_def(&mut self, def: impl Into) { + let generic_def: GenericDefId = def.into(); + let (store, source_map) = ExpressionStore::with_source_map(self.db, generic_def.into()); + self.collect_generic_def(store, source_map, generic_def); + } + + fn collect_module_def(&mut self, def: ModuleDefId, def_map: &DefMap) { + self.emit_case_diagnostics(def); + + match def { + ModuleDefId::ModuleId(def) => { + // Only add diagnostics from inline modules + if def_map[def].origin.is_inline() { + self.collect_module(def); + } + } + ModuleDefId::TraitId(def) => self.collect_trait(def, def_map), + ModuleDefId::MacroId(def) => self.collect_macro_def(def), + ModuleDefId::FunctionId(def) => self.collect_generic_def_with_body(def), + ModuleDefId::ConstId(def) => self.collect_generic_def_with_body(def), + ModuleDefId::StaticId(def) => self.collect_generic_def_with_body(def), + ModuleDefId::EnumVariantId(def) => self.collect_enum_variant(def), + ModuleDefId::AdtId(AdtId::StructId(def)) => self.collect_generic_variant(def), + ModuleDefId::AdtId(AdtId::UnionId(def)) => self.collect_generic_variant(def), + ModuleDefId::AdtId(AdtId::EnumId(def)) => self.collect_enum(def), + ModuleDefId::TypeAliasId(def) => self.collect_only_generic_def(def), + ModuleDefId::BuiltinType(_) => {} + } + } + + fn collect_expr_store( + &mut self, + store: &ExpressionStore, + source_map: &ExpressionStoreSourceMap, + ) { + for (_, def_map) in store.blocks(self.db) { + self.collect_module(def_map.root_module_id()); + } + + for diag in source_map.diagnostics() { + self.acc.push(match diag { + ExpressionStoreDiagnostics::InactiveCode { node, cfg, opts } => { + InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into() + } + ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => { + UnresolvedMacroCall { + range: node.map(|ptr| ptr.text_range()), + path: path.clone(), + is_bang: true, + } + .into() + } + ExpressionStoreDiagnostics::AwaitOutsideOfAsync { node, location } => { + AwaitOutsideOfAsync { node: *node, location: location.clone() }.into() + } + ExpressionStoreDiagnostics::UnreachableLabel { node, name } => { + UnreachableLabel { node: *node, name: name.clone() }.into() + } + ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => { + UndeclaredLabel { node: *node, name: name.clone() }.into() + } + ExpressionStoreDiagnostics::PatternArgInExternFn { node } => { + PatternArgInExternFn { node: *node }.into() + } + ExpressionStoreDiagnostics::FruInDestructuringAssignment { node } => { + FruInDestructuringAssignment { node: *node }.into() + } + ExpressionStoreDiagnostics::MissingBody { node, kind } => { + MissingBody { node: *node, kind: *kind }.into() + } + }); + } + + source_map.macro_calls().for_each(|(_ast_id, call_id)| self.collect_macro_call(call_id)); + } +} + impl<'db> AnyDiagnostic<'db> { - pub(crate) fn body_validation_diagnostic( + fn body_validation_diagnostic( db: &'db dyn HirDatabase, diagnostic: BodyValidationDiagnostic<'db>, source_map: &hir_def::expr_store::BodySourceMap, @@ -814,12 +1542,12 @@ impl<'db> AnyDiagnostic<'db> { None } - pub(crate) fn inference_diagnostic( + fn inference_diagnostic( db: &'db dyn HirDatabase, - def: DefWithBodyId, + krate: base_db::Crate, + edition: Edition, d: &'db InferenceDiagnostic, - source_map: &hir_def::expr_store::BodySourceMap, - sig_map: &hir_def::expr_store::ExpressionStoreSourceMap, + source_map: &ExpressionStoreSourceMap, type_owner: TypeOwnerId<'db>, ) -> Option> { let expr_syntax = |expr| Self::expr_syntax(expr, source_map); @@ -1009,11 +1737,7 @@ impl<'db> AnyDiagnostic<'db> { let expr = expr_syntax(*expr)?; CannotIndexInto { expr, found: new_ty(found.as_ref()) }.into() } - InferenceDiagnostic::TyDiagnostic { source, diag } => { - let source_map = match source { - InferenceTyDiagnosticSource::Body => source_map, - InferenceTyDiagnosticSource::Signature => sig_map, - }; + InferenceDiagnostic::TyDiagnostic { diag } => { Self::ty_diagnostic(diag, source_map, db)? } InferenceDiagnostic::PathDiagnostic { node, diag } => { @@ -1085,7 +1809,9 @@ impl<'db> AnyDiagnostic<'db> { rustc_type_ir::GenericArgKind::Type(ty) => Either::Left(new_ty(ty)), // FIXME: Printing the const to string is definitely not the correct thing to do here. rustc_type_ir::GenericArgKind::Const(konst) => Either::Right( - konst.display(db, DisplayTarget::from_crate(db, def.krate(db))).to_string(), + konst + .display(db, DisplayTarget::from_crate_and_edition(db, krate, edition)) + .to_string(), ), rustc_type_ir::GenericArgKind::Lifetime(_) => { unreachable!("we currently don't emit TypeMustBeKnown for lifetimes") @@ -1365,7 +2091,7 @@ impl<'db> AnyDiagnostic<'db> { }) } - pub(crate) fn ty_diagnostic( + fn ty_diagnostic( diag: &TyLoweringDiagnostic, source_map: &ExpressionStoreSourceMap, db: &'db dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 9238cdcb3ef85..34b9ede4985a6 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -41,7 +41,6 @@ pub use hir_def::ModuleId; use std::{ borrow::Borrow, fmt, iter, - mem::discriminant, ops::{ControlFlow, Not}, }; @@ -52,11 +51,11 @@ use hir_def::{ AdtId, AssocItemId, AssocItemLoc, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExpressionStoreOwnerId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, - Lookup, MacroExpander, MacroId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId, - TypeOrConstParamId, TypeParamId, UnionId, + Lookup, MacroExpander, MacroId, StaticId, StructId, TupleId, TypeAliasId, TypeOrConstParamId, + TypeParamId, UnionId, attrs::AttrFlags, builtin_derive::BuiltinDeriveImplMethod, - expr_store::{ExpressionStore, ExpressionStoreDiagnostics, ExpressionStoreSourceMap}, + expr_store::ExpressionStore, hir::{ BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat, generics::{GenericParams, LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, @@ -64,10 +63,6 @@ use hir_def::{ item_tree::ImportAlias, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, - nameres::{ - assoc::TraitItems, - diagnostics::{DefDiagnostic, DefDiagnosticKind}, - }, per_ns::PerNs, resolver::{HasResolver, Resolver}, signatures::{ @@ -79,19 +74,15 @@ use hir_def::{ unstable_features::UnstableFeatures, visibility::visibility_from_ast, }; -use hir_expand::{ - AstId, MacroCallKind, RenderedExpandError, ValueResult, builtin::BuiltinDeriveExpander, - proc_macro::ProcMacroKind, -}; +use hir_expand::{builtin::BuiltinDeriveExpander, proc_macro::ProcMacroKind}; use hir_ty::{ - GenericPredicates, InferBodyId, InferenceResult, ParamEnvAndCrate, TyDefId, - TyLoweringDiagnostic, ValueTyDefId, all_super_traits, autoderef, check_orphan_rules, + GenericPredicates, InferBodyId, InferenceResult, ParamEnvAndCrate, TyDefId, ValueTyDefId, + all_super_traits, autoderef, check_orphan_rules, consteval::try_const_usize, db::{ AnonConstId, InternedClosure, InternedClosureId, InternedCoroutineClosureId, InternedCoroutineId, }, - diagnostics::BodyValidationDiagnostic, direct_super_traits, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, method_resolution::{self, InherentImpls, MethodResolutionContext}, @@ -101,7 +92,7 @@ use hir_ty::{ GenericArg, GenericArgs, ParamEnv, PolyFnSig, Region, SolverDefId, Ty, TyKind, TypingMode, infer::{DbInternerInferExt, InferCtxt}, }, - traits::{self, is_inherent_impl_coherent, structurally_normalize_ty}, + traits::{self, structurally_normalize_ty}, }; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; @@ -466,50 +457,6 @@ impl ModuleDef { Some(name) } - pub fn diagnostics<'db>( - self, - db: &'db dyn HirDatabase, - style_lints: bool, - ) -> Vec> { - let id = match self { - ModuleDef::Adt(it) => match it { - Adt::Struct(it) => it.id.into(), - Adt::Enum(it) => it.id.into(), - Adt::Union(it) => it.id.into(), - }, - ModuleDef::Trait(it) => it.id.into(), - ModuleDef::Function(it) => match it.id { - AnyFunctionId::FunctionId(it) => it.into(), - AnyFunctionId::BuiltinDeriveImplMethod { .. } => return Vec::new(), - }, - ModuleDef::TypeAlias(it) => it.id.into(), - ModuleDef::Module(it) => it.id.into(), - ModuleDef::Const(it) => it.id.into(), - ModuleDef::Static(it) => it.id.into(), - ModuleDef::EnumVariant(it) => it.id.into(), - ModuleDef::BuiltinType(_) | ModuleDef::Macro(_) => return Vec::new(), - }; - - let mut acc = Vec::new(); - - match self.as_def_with_body() { - Some(def) => { - def.diagnostics(db, &mut acc, style_lints); - } - None => { - for diag in hir_ty::diagnostics::incorrect_case(db, id) { - acc.push(diag.into()) - } - } - } - - if let Some(def) = self.as_self_generic_def() { - def.diagnostics(db, &mut acc); - } - - acc - } - pub fn as_def_with_body(self) -> Option { match self { ModuleDef::Function(it) => Some(it.into()), @@ -749,341 +696,7 @@ impl Module { acc: &mut Vec>, style_lints: bool, ) { - let _p = tracing::info_span!("diagnostics", name = ?self.name(db)).entered(); - let edition = self.id.krate(db).data(db).edition; - let def_map = self.id.def_map(db); - for diag in def_map.diagnostics() { - if diag.in_module != self.id { - // FIXME: This is accidentally quadratic. - continue; - } - emit_def_diagnostic(db, acc, diag, edition, def_map.krate()); - } - - if !self.id.is_block_module(db) { - // These are reported by the body of block modules - let scope = &def_map[self.id].scope; - scope.all_macro_calls().for_each(|it| macro_call_diagnostics(db, it, acc)); - } - - for def in self.declarations(db) { - match def { - ModuleDef::Module(m) => { - // Only add diagnostics from inline modules - if def_map[m.id].origin.is_inline() { - m.diagnostics(db, acc, style_lints) - } - acc.extend(def.diagnostics(db, style_lints)) - } - ModuleDef::Trait(t) => { - let krate = t.krate(db); - for diag in TraitItems::query_with_diagnostics(db, t.id).1.iter() { - emit_def_diagnostic(db, acc, diag, edition, krate.id); - } - - for item in t.items(db) { - item.diagnostics(db, acc, style_lints); - } - - t.all_macro_calls(db) - .iter() - .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc)); - - acc.extend(def.diagnostics(db, style_lints)) - } - ModuleDef::Adt(adt) => { - match adt { - Adt::Struct(s) => { - let source_map = &StructSignature::with_source_map(db, s.id).1; - expr_store_diagnostics(db, acc, source_map); - let source_map = &s.id.fields_with_source_map(db).1; - expr_store_diagnostics(db, acc, source_map); - push_ty_diagnostics( - db, - acc, - db.field_types_with_diagnostics(s.id.into()).diagnostics(), - source_map, - ); - } - Adt::Union(u) => { - let source_map = &UnionSignature::with_source_map(db, u.id).1; - expr_store_diagnostics(db, acc, source_map); - let source_map = &u.id.fields_with_source_map(db).1; - expr_store_diagnostics(db, acc, source_map); - push_ty_diagnostics( - db, - acc, - db.field_types_with_diagnostics(u.id.into()).diagnostics(), - source_map, - ); - } - Adt::Enum(e) => { - let source_map = &EnumSignature::with_source_map(db, e.id).1; - expr_store_diagnostics(db, acc, source_map); - let (variants, diagnostics) = e.id.enum_variants_with_diagnostics(db); - let file = e.id.lookup(db).id.file_id; - let ast_id_map = file.ast_id_map(db); - for diag in diagnostics { - acc.push( - InactiveCode { - node: InFile::new( - file, - ast_id_map.get(diag.ast_id).syntax_node_ptr(), - ), - cfg: diag.cfg.clone(), - opts: diag.opts.clone(), - } - .into(), - ); - } - for &(v, _) in variants.variants.values() { - let source_map = &v.fields_with_source_map(db).1; - push_ty_diagnostics( - db, - acc, - db.field_types_with_diagnostics(v.into()).diagnostics(), - source_map, - ); - expr_store_diagnostics(db, acc, source_map); - } - } - } - acc.extend(def.diagnostics(db, style_lints)) - } - ModuleDef::Macro(m) => emit_macro_def_diagnostics(db, acc, m), - ModuleDef::TypeAlias(type_alias) => { - let source_map = &TypeAliasSignature::with_source_map(db, type_alias.id).1; - expr_store_diagnostics(db, acc, source_map); - push_ty_diagnostics( - db, - acc, - db.type_for_type_alias_with_diagnostics(type_alias.id).diagnostics(), - source_map, - ); - acc.extend(def.diagnostics(db, style_lints)); - } - _ => acc.extend(def.diagnostics(db, style_lints)), - } - } - self.legacy_macros(db).into_iter().for_each(|m| emit_macro_def_diagnostics(db, acc, m)); - - let interner = DbInterner::new_with(db, self.id.krate(db)); - let infcx = interner.infer_ctxt().build(TypingMode::non_body_analysis()); - - let mut impl_assoc_items_scratch = vec![]; - for impl_def in self.impl_defs(db) { - GenericDef::Impl(impl_def).diagnostics(db, acc); - - let AnyImplId::ImplId(impl_id) = impl_def.id else { - continue; - }; - let loc = impl_id.lookup(db); - let (impl_signature, source_map) = ImplSignature::with_source_map(db, impl_id); - expr_store_diagnostics(db, acc, source_map); - - let file_id = loc.id.file_id; - if file_id.macro_file().is_some_and(|it| it.kind(db) == MacroKind::DeriveBuiltIn) { - // these expansion come from us, diagnosing them is a waste of resources - // FIXME: Once we diagnose the inputs to builtin derives, we should at least extract those diagnostics somehow - continue; - } - impl_def - .all_macro_calls(db) - .iter() - .for_each(|&(_ast, call_id)| macro_call_diagnostics(db, call_id, acc)); - - let ast_id_map = file_id.ast_id_map(db); - - for diag in impl_id.impl_items_with_diagnostics(db).1.iter() { - emit_def_diagnostic(db, acc, diag, edition, loc.container.krate(db)); - } - - let trait_impl = impl_signature.target_trait.is_some(); - if !trait_impl && !is_inherent_impl_coherent(db, def_map, impl_id) { - acc.push(IncoherentImpl { impl_: ast_id_map.get(loc.id.value), file_id }.into()) - } - - if trait_impl && !impl_def.check_orphan_rules(db) { - acc.push(TraitImplOrphan { impl_: ast_id_map.get(loc.id.value), file_id }.into()) - } - - let trait_ = trait_impl.then(|| impl_def.trait_(db)).flatten(); - let mut trait_is_unsafe = trait_.is_some_and(|t| t.is_unsafe(db)); - let impl_is_negative = impl_def.is_negative(db); - let impl_is_unsafe = impl_def.is_unsafe(db); - - let trait_is_unresolved = trait_.is_none() && trait_impl; - if trait_is_unresolved { - // Ignore trait safety errors when the trait is unresolved, as otherwise we'll treat it as safe, - // which may not be correct. - trait_is_unsafe = impl_is_unsafe; - } - - let drop_maybe_dangle = (|| { - let trait_ = trait_?; - let drop_trait = interner.lang_items().Drop?; - if drop_trait != trait_.into() { - return None; - } - let parent = impl_id.into(); - let (lifetimes_attrs, type_and_consts_attrs) = - AttrFlags::query_generic_params(db, parent); - let res = lifetimes_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE)) - || type_and_consts_attrs.values().any(|it| it.contains(AttrFlags::MAY_DANGLE)); - Some(res) - })() - .unwrap_or(false); - - match (impl_is_unsafe, trait_is_unsafe, impl_is_negative, drop_maybe_dangle) { - // unsafe negative impl - (true, _, true, _) | - // unsafe impl for safe trait - (true, false, _, false) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(loc.id.value), file_id, should_be_safe: true }.into()), - // safe impl for unsafe trait - (false, true, false, _) | - // safe impl of dangling drop - (false, false, _, true) => acc.push(TraitImplIncorrectSafety { impl_: ast_id_map.get(loc.id.value), file_id, should_be_safe: false }.into()), - _ => (), - }; - - // Negative impls can't have items, don't emit missing items diagnostic for them - if let (false, Some(trait_)) = (impl_is_negative, trait_) { - let items = &trait_.id.trait_items(db).items; - let required_items = items.iter().filter(|&(_, assoc)| match *assoc { - AssocItemId::FunctionId(it) => !FunctionSignature::of(db, it).has_body(), - AssocItemId::ConstId(id) => !ConstSignature::of(db, id).has_body(), - AssocItemId::TypeAliasId(it) => TypeAliasSignature::of(db, it).ty.is_none(), - }); - impl_assoc_items_scratch.extend(impl_id.impl_items(db).items.iter().cloned()); - - let redundant = impl_assoc_items_scratch - .iter() - .filter(|(name, id)| { - !items.iter().any(|(impl_name, impl_item)| { - discriminant(impl_item) == discriminant(id) && impl_name == name - }) - }) - .map(|(name, item)| (name.clone(), AssocItem::from(*item))); - for (name, assoc_item) in redundant { - acc.push( - TraitImplRedundantAssocItems { - trait_, - file_id, - impl_: ast_id_map.get(loc.id.value), - assoc_item: (name, assoc_item), - } - .into(), - ) - } - - let mut missing: Vec<_> = required_items - .filter(|(name, id)| { - !impl_assoc_items_scratch.iter().any(|(impl_name, impl_item)| { - discriminant(impl_item) == discriminant(id) && impl_name == name - }) - }) - .map(|(name, item)| (name.clone(), AssocItem::from(*item))) - .collect(); - - if !missing.is_empty() { - let env = ParamEnvAndCrate { - param_env: db.trait_environment(GenericDefId::from(impl_id)), - krate: self.id.krate(db), - }; - let self_ty = db.impl_self_ty(impl_id).instantiate_identity().skip_norm_wip(); - let self_ty = structurally_normalize_ty(&infcx, self_ty, env.param_env); - let tail_ty = struct_tail_raw(db, interner, self_ty, |ty| { - structurally_normalize_ty(&infcx, ty, env.param_env) - }); - let self_ty_is_guaranteed_unsized = matches!( - tail_ty.kind(), - TyKind::Dynamic(..) | TyKind::Slice(..) | TyKind::Str - ); - if self_ty_is_guaranteed_unsized { - missing.retain(|(_, assoc_item)| { - let assoc_item = match *assoc_item { - AssocItem::Function(it) => match it.id { - AnyFunctionId::FunctionId(id) => id.into(), - AnyFunctionId::BuiltinDeriveImplMethod { .. } => { - never!("should not have an `AnyFunctionId::BuiltinDeriveImplMethod` here"); - return false; - }, - }, - AssocItem::Const(it) => it.id.into(), - AssocItem::TypeAlias(it) => it.id.into(), - }; - !hir_ty::dyn_compatibility::generics_require_sized_self(db, assoc_item) - }); - } - } - - // HACK: When specialization is enabled in the current crate, and there exists - // *any* blanket impl that provides a default implementation for the missing item, - // suppress the missing associated item diagnostic. - // This can lead to false negatives when the impl in question does not actually - // specialize that blanket impl, but determining the exact specialization - // relationship here would be significantly more expensive. - if !missing.is_empty() { - let krate = self.krate(db).id; - let features = UnstableFeatures::query(db, krate); - if features.specialization || features.min_specialization { - missing.retain(|(assoc_name, assoc_item)| { - let AssocItem::Function(_) = assoc_item else { - return true; - }; - - for &impl_ in TraitImpls::for_crate(db, krate).blanket_impls(trait_.id) - { - if impl_ == impl_id { - continue; - } - - for (name, item) in &impl_.impl_items(db).items { - let AssocItemId::FunctionId(fn_) = item else { - continue; - }; - if name != assoc_name { - continue; - } - - if FunctionSignature::of(db, *fn_).is_default() { - return false; - } - } - } - - true - }); - } - } - - if !missing.is_empty() { - acc.push( - TraitImplMissingAssocItems { - impl_: ast_id_map.get(loc.id.value), - file_id, - missing, - } - .into(), - ) - } - impl_assoc_items_scratch.clear(); - } - - push_ty_diagnostics( - db, - acc, - db.impl_self_ty_with_diagnostics(impl_id).diagnostics(), - source_map, - ); - if let Some(it) = db.impl_trait_with_diagnostics(impl_id) { - push_ty_diagnostics(db, acc, it.diagnostics(), source_map); - } - - for &(_, item) in impl_id.impl_items(db).items.iter() { - AssocItem::from(item).diagnostics(db, acc, style_lints); - } - } + crate::diagnostics::DiagnosticsCollector::collect(db, self.id, acc, style_lints); } pub fn declarations(self, db: &dyn HirDatabase) -> Vec { @@ -1157,201 +770,6 @@ impl Module { } } -fn macro_call_diagnostics<'db>( - db: &'db dyn HirDatabase, - macro_call_id: MacroCallId, - acc: &mut Vec>, -) { - let Some(e) = macro_call_id.parse_macro_expansion_error(db) else { - return; - }; - let ValueResult { value: parse_errors, err } = e; - if let Some(err) = err { - let loc = macro_call_id.loc(db); - let file_id = loc.kind.file_id(); - let mut range = precise_macro_call_location(&loc.kind, db, loc.krate); - let RenderedExpandError { message, error, kind } = err.render_to_string(db); - if Some(err.span().anchor.file_id) == file_id.file_id().map(|it| it.span_file_id(db)) { - range.value = err.span().range - + file_id.ast_id_map(db).get_erased(err.span().anchor.ast_id).text_range().start(); - } - acc.push(MacroError { range, message, error, kind }.into()); - } - - if !parse_errors.is_empty() { - let loc = macro_call_id.loc(db); - let range = precise_macro_call_location(&loc.kind, db, loc.krate); - acc.push(MacroExpansionParseError { range, errors: parse_errors.clone() }.into()) - } -} - -fn emit_macro_def_diagnostics<'db>( - db: &'db dyn HirDatabase, - acc: &mut Vec>, - m: Macro, -) { - let id = m.id.definition(db); - let krate = id.krate; - if let hir_expand::MacroDefKind::Declarative(ast, _) = id.kind - && let expander = ast.decl_macro_expander(db, krate) - && let Some(e) = expander.mac.err() - { - let edition = krate.data(db).edition; - emit_def_diagnostic_( - db, - acc, - &DefDiagnosticKind::MacroDefError { ast, message: e.to_string() }, - edition, - krate, - ); - } -} - -fn emit_def_diagnostic<'db>( - db: &'db dyn HirDatabase, - acc: &mut Vec>, - diag: &DefDiagnostic, - edition: Edition, - krate: base_db::Crate, -) { - emit_def_diagnostic_(db, acc, &diag.kind, edition, krate) -} - -fn emit_def_diagnostic_<'db>( - db: &'db dyn HirDatabase, - acc: &mut Vec>, - diag: &DefDiagnosticKind, - edition: Edition, - krate: base_db::Crate, -) { - match diag { - DefDiagnosticKind::UnresolvedModule { ast: declaration, candidates } => { - let decl = declaration.to_ptr(db); - acc.push( - UnresolvedModule { - decl: InFile::new(declaration.file_id, decl), - candidates: candidates.clone(), - } - .into(), - ) - } - DefDiagnosticKind::UnresolvedExternCrate { ast } => { - let item = ast.to_ptr(db); - acc.push(UnresolvedExternCrate { decl: InFile::new(ast.file_id, item) }.into()); - } - - DefDiagnosticKind::MacroError { ast, path, err } => { - let item = ast.to_ptr(db); - let RenderedExpandError { message, error, kind } = err.render_to_string(db); - acc.push( - MacroError { - range: InFile::new(ast.file_id, item.text_range()), - message: format!("{}: {message}", path.display(db, edition)), - error, - kind, - } - .into(), - ) - } - DefDiagnosticKind::UnresolvedImport { id, index } => { - let file_id = id.file_id; - - let use_tree = hir_def::src::use_tree_to_ast(db, *id, *index); - acc.push( - UnresolvedImport { decl: InFile::new(file_id, AstPtr::new(&use_tree)) }.into(), - ); - } - - DefDiagnosticKind::UnconfiguredCode { ast_id, cfg, opts } => { - let ast_id_map = ast_id.file_id.ast_id_map(db); - let ptr = ast_id_map.get_erased(ast_id.value); - acc.push( - InactiveCode { - node: InFile::new(ast_id.file_id, ptr), - cfg: cfg.clone(), - opts: opts.clone(), - } - .into(), - ); - } - DefDiagnosticKind::UnresolvedMacroCall { ast, path } => { - let location = precise_macro_call_location(ast, db, krate); - acc.push( - UnresolvedMacroCall { - range: location, - path: path.clone(), - is_bang: matches!(ast, MacroCallKind::FnLike { .. }), - } - .into(), - ); - } - DefDiagnosticKind::UnimplementedBuiltinMacro { ast } => { - let node = ast.to_node(db); - // Must have a name, otherwise we wouldn't emit it. - let name = node.name().expect("unimplemented builtin macro with no name"); - acc.push( - UnimplementedBuiltinMacro { - node: ast.with_value(SyntaxNodePtr::from(AstPtr::new(&name))), - } - .into(), - ); - } - DefDiagnosticKind::InvalidDeriveTarget { ast, id } => { - let (_, attr) = id.find_attr_range(db, krate, *ast); - let derive = attr - .path() - .map(|path| path.syntax().text_range()) - .unwrap_or_else(|| attr.syntax().text_range()); - acc.push(InvalidDeriveTarget { range: ast.with_value(derive) }.into()); - } - DefDiagnosticKind::MalformedDerive { ast, id } => { - let derive = id.find_attr_range(db, krate, *ast).1.syntax().text_range(); - acc.push(MalformedDerive { range: ast.with_value(derive) }.into()); - } - DefDiagnosticKind::MacroDefError { ast, message } => { - let node = ast.to_node(db); - acc.push( - MacroDefError { - node: InFile::new(ast.file_id, AstPtr::new(&node)), - name: node.name().map(|it| it.syntax().text_range()), - message: message.clone(), - } - .into(), - ); - } - } -} - -fn precise_macro_call_location( - ast: &MacroCallKind, - db: &dyn HirDatabase, - krate: base_db::Crate, -) -> InFile { - // FIXME: maybe we actually want slightly different ranges for the different macro diagnostics - // - e.g. the full attribute for macro errors, but only the name for name resolution - match ast { - MacroCallKind::FnLike { ast_id, .. } => { - let node = ast_id.to_node(db); - let range = node - .path() - .and_then(|it| it.segment()) - .and_then(|it| it.name_ref()) - .map(|it| it.syntax().text_range()); - let range = range.unwrap_or_else(|| node.syntax().text_range()); - ast_id.with_value(range) - } - MacroCallKind::Derive { ast_id, derive_attr_index, derive_index, .. } => { - let range = derive_attr_index.find_derive_range(db, krate, *ast_id, *derive_index); - ast_id.with_value(range) - } - MacroCallKind::Attr { ast_id, censored_attr_ids: attr_ids, .. } => { - let attr_range = - attr_ids.invoc_attr().find_attr_range(db, krate, *ast_id).1.syntax().text_range(); - ast_id.with_value(attr_range) - } - } -} - impl HasVisibility for Module { fn visibility(&self, db: &dyn HirDatabase) -> Visibility { let def_map = self.id.def_map(db); @@ -2020,93 +1438,6 @@ impl DefWithBody { } } - pub fn diagnostics<'db>( - self, - db: &'db dyn HirDatabase, - acc: &mut Vec>, - style_lints: bool, - ) { - let Ok(id) = self.try_into() else { - return; - }; - - let (body, source_map) = Body::with_source_map(db, id); - let sig_source_map = match self { - DefWithBody::Function(id) => match id.id { - AnyFunctionId::FunctionId(id) => &FunctionSignature::with_source_map(db, id).1, - AnyFunctionId::BuiltinDeriveImplMethod { .. } => return, - }, - DefWithBody::Static(id) => &StaticSignature::with_source_map(db, id.into()).1, - DefWithBody::Const(id) => &ConstSignature::with_source_map(db, id.into()).1, - DefWithBody::EnumVariant(variant) => { - let enum_id = variant.parent_enum(db).id; - &EnumSignature::with_source_map(db, enum_id).1 - } - }; - - for (_, def_map) in body.blocks(db) { - Module { id: def_map.root_module_id() }.diagnostics(db, acc, style_lints); - } - - expr_store_diagnostics(db, acc, source_map); - - let infer = InferenceResult::of(db, id); - let type_owner = id.generic_def(db).into(); - for d in infer.diagnostics() { - acc.extend(AnyDiagnostic::inference_diagnostic( - db, - id, - d, - source_map, - sig_source_map, - type_owner, - )); - } - - let missing_unsafe = hir_ty::diagnostics::missing_unsafe(db, id); - for (node, reason) in missing_unsafe.unsafe_exprs { - match source_map.expr_or_pat_syntax(node) { - Ok(node) => acc.push( - MissingUnsafe { - node, - lint: if missing_unsafe.fn_is_unsafe { - UnsafeLint::UnsafeOpInUnsafeFn - } else { - UnsafeLint::HardError - }, - reason, - } - .into(), - ), - Err(SyntheticSyntax) => { - // FIXME: Here and elsewhere in this file, the `expr` was - // desugared, report or assert that this doesn't happen. - } - } - } - for node in missing_unsafe.deprecated_safe_calls { - match source_map.expr_syntax(node) { - Ok(node) => acc.push( - MissingUnsafe { - node, - lint: UnsafeLint::DeprecatedSafe2024, - reason: UnsafetyReason::UnsafeFnCall, - } - .into(), - ), - Err(SyntheticSyntax) => never!("synthetic DeprecatedSafe2024"), - } - } - - for diagnostic in BodyValidationDiagnostic::collect(db, id, style_lints) { - acc.extend(AnyDiagnostic::body_validation_diagnostic(db, diagnostic, source_map)); - } - - for diag in hir_ty::diagnostics::incorrect_case(db, id.into()) { - acc.push(diag.into()) - } - } - /// Returns an iterator over the inferred types of all expressions in this body. pub fn expression_types<'db>( self, @@ -2141,45 +1472,6 @@ impl DefWithBody { } } -fn expr_store_diagnostics<'db>( - db: &'db dyn HirDatabase, - acc: &mut Vec>, - source_map: &ExpressionStoreSourceMap, -) { - for diag in source_map.diagnostics() { - acc.push(match diag { - ExpressionStoreDiagnostics::InactiveCode { node, cfg, opts } => { - InactiveCode { node: *node, cfg: cfg.clone(), opts: opts.clone() }.into() - } - ExpressionStoreDiagnostics::UnresolvedMacroCall { node, path } => UnresolvedMacroCall { - range: node.map(|ptr| ptr.text_range()), - path: path.clone(), - is_bang: true, - } - .into(), - ExpressionStoreDiagnostics::AwaitOutsideOfAsync { node, location } => { - AwaitOutsideOfAsync { node: *node, location: location.clone() }.into() - } - ExpressionStoreDiagnostics::UnreachableLabel { node, name } => { - UnreachableLabel { node: *node, name: name.clone() }.into() - } - ExpressionStoreDiagnostics::UndeclaredLabel { node, name } => { - UndeclaredLabel { node: *node, name: name.clone() }.into() - } - ExpressionStoreDiagnostics::PatternArgInExternFn { node } => { - PatternArgInExternFn { node: *node }.into() - } - ExpressionStoreDiagnostics::FruInDestructuringAssignment { node } => { - FruInDestructuringAssignment { node: *node }.into() - } - }); - } - - source_map - .macro_calls() - .for_each(|(_ast_id, call_id)| macro_call_diagnostics(db, call_id, acc)); -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] enum AnyFunctionId { FunctionId(FunctionId), @@ -3004,10 +2296,6 @@ impl Trait { violations.is_empty().not().then_some(violations) } - fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId, MacroCallId)]> { - self.id.trait_items(db).macro_calls.to_vec().into_boxed_slice() - } - /// `#[rust_analyzer::completions(...)]` mode. pub fn complete(self, db: &dyn HirDatabase) -> Complete { Complete::extract(true, self.attrs(db).attrs) @@ -3691,36 +2979,6 @@ impl AssocItem { _ => None, } } - - pub fn diagnostics<'db>( - self, - db: &'db dyn HirDatabase, - acc: &mut Vec>, - style_lints: bool, - ) { - match self { - AssocItem::Function(func) => { - GenericDef::Function(func).diagnostics(db, acc); - DefWithBody::from(func).diagnostics(db, acc, style_lints); - } - AssocItem::Const(const_) => { - GenericDef::Const(const_).diagnostics(db, acc); - DefWithBody::from(const_).diagnostics(db, acc, style_lints); - } - AssocItem::TypeAlias(type_alias) => { - GenericDef::TypeAlias(type_alias).diagnostics(db, acc); - push_ty_diagnostics( - db, - acc, - db.type_for_type_alias_with_diagnostics(type_alias.id).diagnostics(), - &TypeAliasSignature::with_source_map(db, type_alias.id).1, - ); - for diag in hir_ty::diagnostics::incorrect_case(db, type_alias.id.into()) { - acc.push(diag.into()); - } - } - } - } } impl HasVisibility for AssocItem { @@ -3846,48 +3104,6 @@ impl GenericDef { }) } - pub fn diagnostics<'db>(self, db: &'db dyn HirDatabase, acc: &mut Vec>) { - let Some(def) = self.id() else { return }; - - let generics = GenericParams::of(db, def); - - if generics.is_empty() && generics.has_no_predicates() { - return; - } - - let source_map = match def { - GenericDefId::AdtId(AdtId::EnumId(it)) => &EnumSignature::with_source_map(db, it).1, - GenericDefId::AdtId(AdtId::StructId(it)) => &StructSignature::with_source_map(db, it).1, - GenericDefId::AdtId(AdtId::UnionId(it)) => &UnionSignature::with_source_map(db, it).1, - GenericDefId::ConstId(_) => return, - GenericDefId::FunctionId(it) => &FunctionSignature::with_source_map(db, it).1, - GenericDefId::ImplId(it) => &ImplSignature::with_source_map(db, it).1, - GenericDefId::StaticId(_) => return, - GenericDefId::TraitId(it) => &TraitSignature::with_source_map(db, it).1, - GenericDefId::TypeAliasId(it) => &TypeAliasSignature::with_source_map(db, it).1, - }; - - expr_store_diagnostics(db, acc, source_map); - push_ty_diagnostics( - db, - acc, - db.generic_defaults_with_diagnostics(def).diagnostics(), - source_map, - ); - push_ty_diagnostics( - db, - acc, - GenericPredicates::query_with_diagnostics(db, def).diagnostics(), - source_map, - ); - push_ty_diagnostics( - db, - acc, - db.const_param_types_with_diagnostics(def).diagnostics(), - source_map, - ); - } - /// Returns a string describing the kind of this type. #[inline] pub fn description(self) -> &'static str { @@ -4745,13 +3961,6 @@ impl Impl { AnyImplId::BuiltinDeriveImplId(_) => true, } } - - fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId, MacroCallId)]> { - match self.id { - AnyImplId::ImplId(id) => id.impl_items(db).macro_calls.to_vec().into_boxed_slice(), - AnyImplId::BuiltinDeriveImplId(_) => Box::default(), - } - } } #[derive(Clone, PartialEq, Eq, Debug, Hash)] @@ -7248,19 +6457,6 @@ pub enum DocLinkDef { SelfType(Trait), } -fn push_ty_diagnostics<'db>( - db: &'db dyn HirDatabase, - acc: &mut Vec>, - diagnostics: &[TyLoweringDiagnostic], - source_map: &ExpressionStoreSourceMap, -) { - acc.extend( - diagnostics - .iter() - .filter_map(|diagnostic| AnyDiagnostic::ty_diagnostic(diagnostic, source_map, db)), - ); -} - pub trait MethodCandidateCallback { fn on_inherent_method(&mut self, f: Function) -> ControlFlow<()>; @@ -7409,7 +6605,7 @@ impl MacroCallIdExt for span::MacroCallId { } // Like https://github.com/rust-lang/rust/blob/7c3c88f42ad444f4688b865591d84660be4ece2f/compiler/rustc_middle/src/ty/util.rs#L254-L310 -pub fn struct_tail_raw<'db>( +fn struct_tail_raw<'db>( db: &'db dyn HirDatabase, interner: DbInterner<'db>, mut ty: Ty<'db>, diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index f298e25489a59..49055172a1552 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -17,7 +17,7 @@ use hir_def::{ StructId, TraitId, VariantId, attrs::parse_extra_crate_attrs, expr_store::{Body, ExprOrPatSource, ExpressionStore, HygieneId, path::Path}, - hir::{BindingId, Expr, ExprId, ExprOrPatId}, + hir::{BindingId, Expr, ExprId, ExprOrPatId, Unsafe}, nameres::{ModuleOrigin, crate_def_map}, resolver::{self, HasResolver, Resolver, TypeNs, ValueNs}, type_ref::Mutability, @@ -2187,13 +2187,9 @@ impl<'db> SemanticsImpl<'db> { def: DefWithoutBodyWithAnonConsts, ) -> &'a ExprToAnonConst<'db> { cache.entry(def).or_insert_with(|| match def { - Either::Left(def) => { - let all_anon_consts = - AnonConstId::all_from_signature(self.db, def).into_iter().flatten().copied(); - all_anon_consts - .map(|anon_const| (anon_const.loc(self.db).expr, anon_const)) - .collect() - } + Either::Left(def) => AnonConstId::all_from_signature(self.db, def) + .map(|anon_const| (anon_const.loc(self.db).expr, anon_const)) + .collect(), Either::Right(def) => { let all_anon_consts = self.db.field_types_with_diagnostics(def).defined_anon_consts().iter().copied(); @@ -2427,7 +2423,7 @@ impl<'db> SemanticsImpl<'db> { if let Some(parent) = ast::Expr::cast(parent.clone()) && let Some(ExprOrPatId::ExprId(expr_id)) = source_map.node_expr(InFile { file_id, value: &parent }) - && let Expr::Unsafe { .. } = body[expr_id] + && let Expr::Block { unsafe_: Unsafe::Yes, .. } = body[expr_id] { break true; } diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index 0a5d95d6136bf..18985c825c385 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -175,6 +175,7 @@ impl<'db> LookupTable<'db> { /// transitive. For example `Vec` and `FxHashSet` both unify with `Iterator`, /// but they clearly do not unify themselves. fn insert(&mut self, ty: Type<'db>, exprs: impl Iterator>) { + let ty = ty.instantiate_with_errors(); match self.data.get_mut(&ty) { Some(it) => { it.extend_with_threshold(self.many_threshold, exprs); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 8b129acad3319..9f9bb1d131548 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -2117,7 +2117,7 @@ macro_rules! define_method { } trait AnotherTrait { define_method!(); } impl AnotherTrait for () { - $0fn method(&mut self,params: ::Output) { + $0fn method(&mut self, params: ::Output) { todo!() } } @@ -2154,7 +2154,7 @@ macro_rules! define_method { } trait AnotherTrait { define_method!(T); } impl AnotherTrait for () { - $0fn method(&mut self,params: ::Output) { + $0fn method(&mut self, params: ::Output) { todo!() } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs index d950b6df214a3..ace5fc3d757b5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_block.rs @@ -1,7 +1,7 @@ use itertools::Itertools; use syntax::{ - AstToken, Direction, SyntaxElement, TextRange, - ast::{self, Comment, CommentKind, CommentShape, Whitespace, edit::IndentLevel}, + AstToken, SyntaxToken, TextRange, + ast::{self, CommentKind, CommentShape, Whitespace, edit::IndentLevel}, }; use crate::{AssistContext, AssistId, Assists}; @@ -22,19 +22,19 @@ use crate::{AssistContext, AssistId, Assists}; // */ // ``` pub(crate) fn convert_comment_block(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { - let comment = ctx.find_token_at_offset::()?; + let comment = ctx.find_token_at_offset::()?; // Only allow comments which are alone on their line if let Some(prev) = comment.syntax().prev_token() { Whitespace::cast(prev).filter(|w| w.text().contains('\n'))?; } - match comment.kind().shape { + match comment.shape() { ast::CommentShape::Block => block_to_line(acc, comment), ast::CommentShape::Line => line_to_block(acc, comment), } } -fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> { +fn block_to_line(acc: &mut Assists, comment: ast::AnyComment) -> Option<()> { let target = comment.syntax().text_range(); acc.add( @@ -45,9 +45,7 @@ fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> { let indentation = IndentLevel::from_token(comment.syntax()); let line_prefix = CommentKind { shape: CommentShape::Line, ..comment.kind() }.prefix(); - let text = comment.text(); - let text = &text[comment.prefix().len()..(text.len() - "*/".len())].trim(); - + let text = comment.text().trim(); let lines = text.lines().peekable(); let indent_spaces = indentation.to_string(); @@ -69,7 +67,7 @@ fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> { ) } -fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> { +fn line_to_block(acc: &mut Assists, comment: ast::AnyComment) -> Option<()> { // Find all the comments we'll be collapsing into a block let comments = relevant_line_comments(&comment); @@ -109,37 +107,26 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> { /// The line -> block assist can be invoked from anywhere within a sequence of line comments. /// relevant_line_comments crawls backwards and forwards finding the complete sequence of comments that will /// be joined. -pub(crate) fn relevant_line_comments(comment: &ast::Comment) -> Vec { - // The prefix identifies the kind of comment we're dealing with - let prefix = comment.prefix(); - let same_prefix = |c: &ast::Comment| c.prefix() == prefix; +pub(crate) fn relevant_line_comments(comment: &ast::AnyComment) -> Vec { + let expected_kind = comment.kind(); + let same_kind = |c: &ast::AnyComment| c.kind() == expected_kind; // These tokens are allowed to exist between comments - let skippable = |not: &SyntaxElement| { - not.clone() - .into_token() - .and_then(Whitespace::cast) - .map(|w| !w.spans_multiple_lines()) - .unwrap_or(false) + let skippable = |not: &SyntaxToken| { + Whitespace::cast(not.clone()).map(|w| !w.spans_multiple_lines()).unwrap_or(false) }; // Find all preceding comments (in reverse order) that have the same prefix - let prev_comments = comment - .syntax() - .siblings_with_tokens(Direction::Prev) + let prev_comments = std::iter::successors(Some(comment.syntax().clone()), |it| it.prev_token()) .filter(|s| !skippable(s)) - .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix)) - .take_while(|opt_com| opt_com.is_some()) - .flatten() + .map_while(ast::AnyComment::cast) + .take_while(same_kind) .skip(1); // skip the first element so we don't duplicate it in next_comments - let next_comments = comment - .syntax() - .siblings_with_tokens(Direction::Next) + let next_comments = std::iter::successors(Some(comment.syntax().clone()), |it| it.next_token()) .filter(|s| !skippable(s)) - .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix)) - .take_while(|opt_com| opt_com.is_some()) - .flatten(); + .map_while(ast::AnyComment::cast) + .take_while(same_kind); let mut comments: Vec<_> = prev_comments.collect(); comments.reverse(); @@ -161,7 +148,7 @@ pub(crate) fn relevant_line_comments(comment: &ast::Comment) -> Vec { // */ // // But since such comments aren't idiomatic we're okay with this. -pub(crate) fn line_comment_text(indentation: IndentLevel, comm: ast::Comment) -> String { +pub(crate) fn line_comment_text(indentation: IndentLevel, comm: ast::AnyComment) -> String { let text = comm.text(); let contents_without_prefix = text.strip_prefix(comm.prefix()).unwrap_or(text); let contents = contents_without_prefix.strip_prefix(' ').unwrap_or(contents_without_prefix); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs index 11a3c64188d49..ae2b98423d819 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_comment_from_or_to_doc.rs @@ -1,10 +1,12 @@ use itertools::Itertools; use syntax::{ - AstToken, Direction, SyntaxElement, TextRange, - ast::{self, Comment, CommentPlacement, Whitespace, edit::IndentLevel}, + AstToken, TextRange, + ast::{self, AttrKind, Whitespace, edit::IndentLevel}, }; -use crate::{AssistContext, AssistId, Assists}; +use crate::{ + AssistContext, AssistId, Assists, handlers::convert_comment_block::relevant_line_comments, +}; // Assist: comment_to_doc // @@ -23,7 +25,7 @@ pub(crate) fn convert_comment_from_or_to_doc( acc: &mut Assists, ctx: &AssistContext<'_, '_>, ) -> Option<()> { - let comment = ctx.find_token_at_offset::()?; + let comment = ctx.find_token_at_offset::()?; match comment.kind().doc { Some(_) => doc_to_comment(acc, comment), @@ -31,7 +33,7 @@ pub(crate) fn convert_comment_from_or_to_doc( } } -fn doc_to_comment(acc: &mut Assists, comment: ast::Comment) -> Option<()> { +fn doc_to_comment(acc: &mut Assists, comment: ast::AnyComment) -> Option<()> { let target = if comment.kind().shape.is_line() { line_comments_text_range(&comment)? } else { @@ -52,7 +54,7 @@ fn doc_to_comment(acc: &mut Assists, comment: ast::Comment) -> Option<()> { let prefix = format!("{indentation}//"); relevant_line_comments(&comment) .iter() - .map(|comment| comment.text()) + .map(|comment| comment.text_with_markers()) .flat_map(|text| text.lines()) .map(|line| line.replacen(line_start, &prefix, 1)) .join("\n") @@ -60,7 +62,7 @@ fn doc_to_comment(acc: &mut Assists, comment: ast::Comment) -> Option<()> { ast::CommentShape::Block => { let block_start = comment.prefix(); comment - .text() + .text_with_markers() .lines() .enumerate() .map(|(idx, line)| { @@ -78,7 +80,7 @@ fn doc_to_comment(acc: &mut Assists, comment: ast::Comment) -> Option<()> { ) } -fn comment_to_doc(acc: &mut Assists, comment: ast::Comment, style: CommentPlacement) -> Option<()> { +fn comment_to_doc(acc: &mut Assists, comment: ast::AnyComment, style: AttrKind) -> Option<()> { let target = if comment.kind().shape.is_line() { line_comments_text_range(&comment)? } else { @@ -96,23 +98,23 @@ fn comment_to_doc(acc: &mut Assists, comment: ast::Comment, style: CommentPlacem ast::CommentShape::Line => { let indentation = IndentLevel::from_token(comment.syntax()); let line_start = match style { - CommentPlacement::Inner => format!("{indentation}//!"), - CommentPlacement::Outer => format!("{indentation}///"), + AttrKind::Inner => format!("{indentation}//!"), + AttrKind::Outer => format!("{indentation}///"), }; relevant_line_comments(&comment) .iter() - .map(|comment| comment.text()) + .map(|comment| comment.text_with_markers()) .flat_map(|text| text.lines()) .map(|line| line.replacen("//", &line_start, 1)) .join("\n") } ast::CommentShape::Block => { let block_start = match style { - CommentPlacement::Inner => "/*!", - CommentPlacement::Outer => "/**", + AttrKind::Inner => "/*!", + AttrKind::Outer => "/**", }; comment - .text() + .text_with_markers() .lines() .enumerate() .map(|(idx, line)| { @@ -176,7 +178,7 @@ fn comment_to_doc(acc: &mut Assists, comment: ast::Comment, style: CommentPlacem /// // Modules only normally get inner documentation when they are defined as a separate file. /// } /// ``` -fn can_be_doc_comment(comment: &ast::Comment) -> Option { +fn can_be_doc_comment(comment: &ast::AnyComment) -> Option { use syntax::SyntaxKind::*; // if the comment is not on its own line, then we do not propose anything. @@ -186,7 +188,7 @@ fn can_be_doc_comment(comment: &ast::Comment) -> Option { Whitespace::cast(prev).filter(|w| w.text().contains('\n'))?; } // There is no previous token, this is the start of the file. - None => return Some(CommentPlacement::Inner), + None => return Some(AttrKind::Inner), } // check if comment is followed by: `struct`, `trait`, `mod`, `fn`, `type`, `extern crate`, @@ -194,51 +196,10 @@ fn can_be_doc_comment(comment: &ast::Comment) -> Option { let parent = comment.syntax().parent(); let par_kind = parent.as_ref().map(|parent| parent.kind()); matches!(par_kind, Some(STRUCT | TRAIT | MODULE | FN | TYPE_ALIAS | EXTERN_CRATE | USE | CONST)) - .then_some(CommentPlacement::Outer) -} - -/// The line -> block assist can be invoked from anywhere within a sequence of line comments. -/// relevant_line_comments crawls backwards and forwards finding the complete sequence of comments that will -/// be joined. -pub(crate) fn relevant_line_comments(comment: &ast::Comment) -> Vec { - // The prefix identifies the kind of comment we're dealing with - let prefix = comment.prefix(); - let same_prefix = |c: &ast::Comment| c.prefix() == prefix; - - // These tokens are allowed to exist between comments - let skippable = |not: &SyntaxElement| { - not.clone() - .into_token() - .and_then(Whitespace::cast) - .map(|w| !w.spans_multiple_lines()) - .unwrap_or(false) - }; - - // Find all preceding comments (in reverse order) that have the same prefix - let prev_comments = comment - .syntax() - .siblings_with_tokens(Direction::Prev) - .filter(|s| !skippable(s)) - .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix)) - .take_while(|opt_com| opt_com.is_some()) - .flatten() - .skip(1); // skip the first element so we don't duplicate it in next_comments - - let next_comments = comment - .syntax() - .siblings_with_tokens(Direction::Next) - .filter(|s| !skippable(s)) - .map(|not| not.into_token().and_then(Comment::cast).filter(same_prefix)) - .take_while(|opt_com| opt_com.is_some()) - .flatten(); - - let mut comments: Vec<_> = prev_comments.collect(); - comments.reverse(); - comments.extend(next_comments); - comments + .then_some(AttrKind::Outer) } -fn line_comments_text_range(comment: &ast::Comment) -> Option { +fn line_comments_text_range(comment: &ast::AnyComment) -> Option { let comments = relevant_line_comments(comment); let first = comments.first()?; let indentation = IndentLevel::from_token(first.syntax()); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs index e6784a0c3b397..b0657ef434095 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/desugar_doc_comment.rs @@ -1,8 +1,8 @@ use either::Either; use itertools::Itertools; use syntax::{ - AstToken, TextRange, - ast::{self, CommentPlacement, Whitespace, edit::IndentLevel}, + AstNode, AstToken, TextRange, + ast::{self, AttrKind, Whitespace, edit::IndentLevel}, }; use crate::{ @@ -25,22 +25,22 @@ use crate::{ // comment"] // ``` pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { - let comment = ctx.find_token_at_offset::()?; + let comment = ctx.find_node_at_offset::()?; // Only allow doc comments - let placement = comment.kind().doc?; + let placement = comment.kind(); // Only allow comments which are alone on their line - if let Some(prev) = comment.syntax().prev_token() { + if let Some(prev) = comment.syntax().first_token().and_then(|it| it.prev_token()) { Whitespace::cast(prev).filter(|w| w.text().contains('\n'))?; } - let indentation = IndentLevel::from_token(comment.syntax()).to_string(); + let indentation = IndentLevel::from_node(comment.syntax()).to_string(); - let (target, comments) = match comment.kind().shape { + let (target, comments) = match comment.shape() { ast::CommentShape::Block => (comment.syntax().text_range(), Either::Left(comment)), ast::CommentShape::Line => { // Find all the comments we'll be desugaring - let comments = relevant_line_comments(&comment); + let comments = relevant_line_comments(&comment.token()); // Establish the target of our edit based on the comments we found ( @@ -59,26 +59,22 @@ pub(crate) fn desugar_doc_comment(acc: &mut Assists, ctx: &AssistContext<'_, '_> target, |edit| { let text = match comments { - Either::Left(comment) => { - let text = comment.text(); - text[comment.prefix().len()..(text.len() - "*/".len())] - .trim() - .lines() - .map(|l| l.strip_prefix(&indentation).unwrap_or(l)) - .join("\n") - } - Either::Right(comments) => comments - .into_iter() - .map(|cm| line_comment_text(IndentLevel(0), cm)) - .collect::>() + Either::Left(comment) => comment + .text() + .trim() + .lines() + .map(|l| l.strip_prefix(&indentation).unwrap_or(l)) .join("\n"), + Either::Right(comments) => { + comments.into_iter().map(|cm| line_comment_text(IndentLevel(0), cm)).join("\n") + } }; let hashes = "#".repeat(required_hashes(&text)); let prefix = match placement { - CommentPlacement::Inner => "#!", - CommentPlacement::Outer => "#", + AttrKind::Inner => "#!", + AttrKind::Outer => "#", }; let output = format!(r#"{prefix}[doc = r{hashes}"{text}"{hashes}]"#); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 46333ed726388..038d100e621ec 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -76,7 +76,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - return None; } - if node.kind() == COMMENT { + if ast::AnyComment::can_cast(node.kind()) { cov_mark::hit!(extract_function_in_comment_is_not_applicable); return None; } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs index 60a1c7ab44eb0..9ff07d8dd6d2c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_module.rs @@ -469,7 +469,10 @@ impl Module { syntax.children_with_tokens().find(|nt| { !matches!( nt.kind(), - SyntaxKind::COMMENT | SyntaxKind::ATTR | SyntaxKind::WHITESPACE + SyntaxKind::COMMENT + | SyntaxKind::DOC_COMMENT + | SyntaxKind::ATTR + | SyntaxKind::WHITESPACE ) }) }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index c1ac4f1724893..8d0b9da112c39 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -365,7 +365,7 @@ fn collect_variant_comments( for child in node.children_with_tokens() { match child.kind() { - COMMENT => { + COMMENT | DOC_COMMENT => { after_comment = true; to_insert.push(child.clone()); to_delete.push(child); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index e514e8be4233a..e2ff8fa2fc6d5 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -89,11 +89,15 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) - } } else { match ctx.covering_element() { - NodeOrToken::Node(it) => it, + NodeOrToken::Node(it) if it.kind() == SyntaxKind::DOC_COMMENT => { + cov_mark::hit!(extract_var_in_comment_is_not_applicable); + return None; + } NodeOrToken::Token(it) if it.kind() == SyntaxKind::COMMENT => { cov_mark::hit!(extract_var_in_comment_is_not_applicable); return None; } + NodeOrToken::Node(it) => it, NodeOrToken::Token(it) => it.parent()?, } }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index d0f5c7c5003d4..54784fa9903c6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -91,6 +91,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_, ' it.kind(), syntax::SyntaxKind::WHITESPACE | syntax::SyntaxKind::COMMENT + | syntax::SyntaxKind::DOC_COMMENT | syntax::SyntaxKind::ATTR ) }) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs index ba6bb5c70bcb1..b24ba4b9abcfd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_derive.rs @@ -1,5 +1,5 @@ use syntax::{ - SyntaxKind::{ATTR, COMMENT, WHITESPACE}, + SyntaxKind::{ATTR, COMMENT, DOC_COMMENT, WHITESPACE}, T, ast::{self, AstNode, HasAttrs, edit::IndentLevel}, syntax_editor::{Element, Position}, @@ -55,7 +55,7 @@ pub(crate) fn generate_derive(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> let after_attrs_and_comments = nominal .syntax() .children_with_tokens() - .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) + .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | DOC_COMMENT | ATTR)) .map_or(Position::first_child_of(nominal.syntax()), Position::before); editor.insert_all( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs index 89adda93866f0..72971e55222cc 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_documentation_template.rs @@ -3,9 +3,9 @@ use ide_db::assists::AssistId; use itertools::Itertools; use stdx::{format_to, to_lower_snake_case}; use syntax::{ - AstNode, AstToken, Edition, + AstNode, Edition, algo::skip_whitespace_token, - ast::{self, HasDocComments, HasGenericArgs, HasName, edit::IndentLevel}, + ast::{self, HasAttrs, HasGenericArgs, HasName, edit::IndentLevel}, match_ast, }; @@ -96,11 +96,13 @@ pub(crate) fn generate_documentation_template( // pub fn add(a: i32, b: i32) -> i32 { a + b } // ``` pub(crate) fn generate_doc_example(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> { - let tok: ast::Comment = ctx.find_token_at_offset()?; - let node = tok.syntax().parent()?; - let last_doc_token = - ast::AnyHasDocComments::cast(node.clone())?.doc_comments().last()?.syntax().clone(); - let next_token = skip_whitespace_token(last_doc_token.next_token()?, syntax::Direction::Next)?; + let doc_at_cursor: ast::DocComment = ctx.find_node_at_offset()?; + let node = doc_at_cursor.syntax().parent()?; + let last_doc_comment = ast::AnyHasAttrs::cast(node.clone())?.doc_comments().last()?; + let next_token = skip_whitespace_token( + last_doc_comment.syntax().last_token()?.next_token()?, + syntax::Direction::Next, + )?; let example = match_ast! { match node { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 354447cf3356e..79254044a9608 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_trait_from_impl.rs @@ -1,9 +1,9 @@ use crate::assist_context::{AssistContext, Assists}; use ide_db::{assists::AssistId, defs::Definition, search::SearchScope}; use syntax::{ - AstNode, AstToken, SyntaxKind, T, + AstNode, SyntaxKind, T, ast::{ - self, HasDocComments, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, + self, HasAttrs, HasGenericParams, HasName, HasVisibility, edit::AstNodeEdit, syntax_factory::SyntaxFactory, }, syntax_editor::{Position, SyntaxEditor}, @@ -226,7 +226,7 @@ fn remove_items_visibility(editor: &SyntaxEditor, item: &ast::AssocItem) { fn remove_doc_comments(editor: &SyntaxEditor, item: &ast::AssocItem) { for doc in item.doc_comments() { - if let Some(next) = doc.syntax().next_token() + if let Some(next) = doc.syntax().last_token().and_then(|it| it.next_token()) && next.kind() == SyntaxKind::WHITESPACE { editor.delete(next); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index 0f12ec79ee6e8..e5e735faf6f93 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -57,12 +57,7 @@ pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option TextSize { node.children_with_tokens() - .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | ATTR)) + .find(|it| !matches!(it.kind(), WHITESPACE | COMMENT | DOC_COMMENT | ATTR)) .map(|it| it.text_range().start()) .unwrap_or_else(|| node.text_range().start()) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs index 1e5240218571a..e779cf8e9dd62 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/format_string.rs @@ -37,19 +37,24 @@ pub(crate) fn format_string( }); ctx.scope.process_all_names(&mut |name, scope| { if let ScopeDef::ModuleDef(module_def) = scope { + let mut const_value = None; let symbol_kind = match module_def { - ModuleDef::Const(..) => SymbolKind::Const, + ModuleDef::Const(c) => { + const_value = Some(c); + SymbolKind::Const + } ModuleDef::Static(..) => SymbolKind::Static, _ => return, }; - CompletionItem::new( + let mut builder = CompletionItem::new( CompletionItemKind::SymbolKind(symbol_kind), source_range, name.display_no_db(ctx.edition).to_smolstr(), ctx.edition, - ) - .add_to(acc, ctx.db); + ); + builder.const_value(const_value, ctx.db, ctx.display_target); + builder.add_to(acc, ctx.db); } }); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index ee6788b16e45c..bf834e8b76b7e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -112,7 +112,10 @@ fn complete_trait_impl_name( .find(|child| { !matches!( child.kind(), - SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR + SyntaxKind::COMMENT + | SyntaxKind::DOC_COMMENT + | SyntaxKind::WHITESPACE + | SyntaxKind::ATTR ) }) .unwrap_or_else(|| SyntaxElement::Node(real_file_item.clone())); @@ -1365,7 +1368,7 @@ noop! { struct Test; impl Foo for Test { - fn foo(&mut self,bar: i64,baz: &mut u32) -> Result<(),u32> { + fn foo(&mut self, bar: i64, baz: &mut u32) -> Result<(), u32> { $0 } } @@ -1440,7 +1443,7 @@ macro_rules! define_method { } trait AnotherTrait { define_method!(); } impl AnotherTrait for () { - fn method(&mut self,params: ::Output) { + fn method(&mut self, params: ::Output) { $0 } } @@ -1478,7 +1481,7 @@ macro_rules! define_method { } trait AnotherTrait { define_method!(T); } impl AnotherTrait for () { - fn method(&mut self,params: ::Output) { + fn method(&mut self, params: ::Output) { $0 } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 675ffac040293..89796901f4b16 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -2,7 +2,8 @@ use std::{fmt, mem}; -use hir::Mutability; +use hir::db::HirDatabase; +use hir::{DisplayTarget, Mutability}; use ide_db::text_edit::TextEdit; use ide_db::{ RootDatabase, SnippetCap, SymbolKind, documentation::Documentation, @@ -12,7 +13,7 @@ use itertools::Itertools; use macros::UpmapFromRaFixture; use smallvec::SmallVec; use stdx::{format_to, impl_from, never}; -use syntax::{Edition, SmolStr, TextRange, TextSize, format_smolstr}; +use syntax::{Edition, SmolStr, TextRange, TextSize, ToSmolStr, format_smolstr}; use crate::{ context::{CompletionContext, PathCompletionCtx}, @@ -497,6 +498,7 @@ impl CompletionItem { imports_to_add: Default::default(), doc_aliases: vec![], adds_text: None, + const_value: None, edition, } } @@ -534,6 +536,7 @@ pub(crate) struct Builder { trait_name: Option, doc_aliases: Vec, adds_text: Option, + const_value: Option, label: SmolStr, insert_text: Option, is_snippet: bool, @@ -603,6 +606,9 @@ impl Builder { lookup = format_smolstr!("{lookup}{lookup_doc_aliases}"); } } + if let Some(const_value) = self.const_value { + to_detail_left(format_args!(" = {}", const_value.trim())); + } if let Some(adds_text) = self.adds_text { to_detail_left(format_args!("(adds {})", adds_text.trim())); } @@ -681,6 +687,21 @@ impl Builder { self.adds_text = Some(adds_text); self } + pub(crate) fn const_value( + &mut self, + const_value: Option, + db: &dyn HirDatabase, + display_target: DisplayTarget, + ) -> &mut Builder { + if let Some(const_value) = const_value { + if let Ok(evaluated_value) = const_value.eval(db) { + self.const_value = Some(evaluated_value.render(db, display_target).to_smolstr()); + } else if let Some(written_value) = const_value.value(db) { + self.const_value = Some(written_value.to_smolstr()); + } + } + self + } pub(crate) fn insert_text(&mut self, insert_text: impl Into) -> &mut Builder { self.insert_text = Some(insert_text.into()); self diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index de0a9a9174314..f5c54832a12ce 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -546,6 +546,9 @@ fn render_resolution_simple_<'db>( local_name.as_str().to_smolstr(), ctx.completion.edition, ); + if let ScopeDef::ModuleDef(ModuleDef::Const(konst)) = resolution { + item.const_value(Some(konst), db, ctx.completion.display_target); + } item.set_relevance(ctx.completion_relevance()) .set_documentation(scope_def_docs(db, resolution)) .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); @@ -1941,7 +1944,9 @@ fn main() { A$0 } [ CompletionItem { label: "A", - detail_left: None, + detail_left: Some( + " = 0", + ), detail_right: Some( "i32", ), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index c14fc1704c5ba..4922c72468ae1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -26,6 +26,7 @@ fn render(ctx: RenderContext<'_, '_>, const_: hir::Const) -> Option, const_: hir::Const) -> Option i32 "#]], ); @@ -1435,7 +1435,7 @@ fn function() { } "#, expect![[r#" - ct FooConst (use module::FooConst) + ct FooConst = () (use module::FooConst) st FooStruct (use module::FooStruct) "#]], ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs index bb79af7e98df1..86fcea5983b55 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item.rs @@ -355,7 +355,7 @@ fn bar() { } "#, expect![[r#" - ct CONST Unit + ct CONST = Unit Unit en Enum Enum fn bar() fn() fn foo() fn() @@ -422,7 +422,7 @@ macro_rules! foo { foo!(f$0); "#, expect![[r#" - ct BAR u8 + ct BAR = f u8 fn foo() fn() -> u8 ma foo!(…) macro_rules! foo bt u32 u32 @@ -443,6 +443,42 @@ foo!(f$0); ); } +#[test] +fn const_eval_label_details() { + check( + r#" +pub const MAX: u32 = !0; +pub const MIN: u32 = 0; +pub const MNOEVAL: u32 = unknown(); + +fn main() { + let x = M$0 +} + "#, + expect![[r#" + ct MAX = 4294967295 u32 + ct MIN = 0 u32 + ct MNOEVAL = unknown() u32 + fn main() fn() + bt u32 u32 + kw const + kw crate:: + kw false + kw for + kw if + kw if let + kw loop + kw match + kw return + kw self:: + kw true + kw unsafe + kw while + kw while let + "#]], + ); +} + #[test] fn completes_variant_through_hidden_enum_alias() { check( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs index 0d85f2e9ad627..d1e78bb069688 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs @@ -115,7 +115,7 @@ fn foo() { } "#, expect![[r#" - ct CONST + ct CONST = Unit en Enum ma makro!(…) macro_rules! makro md module @@ -342,10 +342,10 @@ fn func() { } "#, expect![[r#" - ct ASSOC_CONST const ASSOC_CONST: () - bn RecordV {…} RecordV { field$1 }$0 - bn TupleV(…) TupleV($1)$0 - bn UnitV UnitV$0 + ct ASSOC_CONST = () const ASSOC_CONST: () + bn RecordV {…} RecordV { field$1 }$0 + bn TupleV(…) TupleV($1)$0 + bn UnitV UnitV$0 "#]], ); } @@ -360,7 +360,7 @@ fn func() { } "#, expect![[r#" - ct CONST + ct CONST = Unit en Enum ma makro!(…) macro_rules! makro md module @@ -662,7 +662,7 @@ fn f(t: Ty) { } "#, expect![[r#" - ct ABC const ABC: Self + ct ABC = Ty(0) const ABC: Self "#]], ); @@ -683,8 +683,8 @@ fn f(e: MyEnum) { } "#, expect![[r#" - ct A pub const A: i32 - ct B pub const B: i32 + ct A = 123 pub const A: i32 + ct B = 456 pub const B: i32 "#]], ); @@ -708,8 +708,8 @@ fn f(u: U) { } "#, expect![[r#" - ct C pub const C: i32 - ct D pub const D: i32 + ct C = 123 pub const C: i32 + ct D = 456 pub const D: i32 "#]], ); @@ -729,7 +729,7 @@ fn f(v: u32) { } "#, expect![[r#" - ct MIN pub const MIN: Self + ct MIN = 0 pub const MIN: Self "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 6c2502a4eb670..51ece1b63cc9e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -206,9 +206,9 @@ impl S { fn foo() { let _ = lib::S::$0 } "#, expect![[r#" - ct PUBLIC_CONST pub const PUBLIC_CONST: u32 - fn public_method() fn() - ta PublicType pub type PublicType = u32 + ct PUBLIC_CONST = 1 pub const PUBLIC_CONST: u32 + fn public_method() fn() + ta PublicType pub type PublicType = u32 "#]], ); } @@ -337,14 +337,14 @@ impl Sub for Wrap { } "#, expect![[r#" - ct C2 (as Sub) const C2: () - ct CONST (as Super) const CONST: u8 - fn func() (as Super) fn() - fn subfunc() (as Sub) fn() - me method(…) (as Super) fn(&self) - me submethod(…) (as Sub) fn(&self) - ta SubTy (as Sub) type SubTy - ta Ty (as Super) type Ty + ct C2 = () (as Sub) const C2: () + ct CONST = 0 (as Super) const CONST: u8 + fn func() (as Super) fn() + fn subfunc() (as Sub) fn() + me method(…) (as Super) fn(&self) + me submethod(…) (as Sub) fn(&self) + ta SubTy (as Sub) type SubTy + ta Ty (as Super) type Ty "#]], ); } @@ -427,9 +427,9 @@ mod p { } "#, expect![[r#" - ct RIGHT_CONST u32 - fn right_fn() fn() - st RightType WrongType + ct RIGHT_CONST = 1 u32 + fn right_fn() fn() + st RightType WrongType "#]], ); @@ -823,8 +823,8 @@ impl u8 { } "#, expect![[r#" - ct MAX pub const MAX: Self - me func(…) fn(self) + ct MAX = 255 pub const MAX: Self + me func(…) fn(self) "#]], ); } @@ -1054,7 +1054,7 @@ fn main() { } "#, expect![[r#" - ct by_macro (as MyTrait) pub const by_macro: u8 + ct by_macro = 1 (as MyTrait) pub const by_macro: u8 "#]], ) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs index 419b15ed868b3..8fa2d361829de 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs @@ -1077,7 +1077,7 @@ trait MyTrait { fn f(t: impl MyTrait = ()>>() {} "#, expect![[r#" - ct CONST Unit - ct X usize + ct CONST = Unit Unit + ct X = 0 usize ma makro!(…) macro_rules! makro kw crate:: kw dyn @@ -1316,8 +1316,8 @@ fn completes_const_and_type_generics_separately() { } "#, expect![[r#" - ct CONST Unit - ct X usize + ct CONST = Unit Unit + ct X = 0 usize ma makro!(…) macro_rules! makro kw crate:: kw dyn @@ -1339,8 +1339,8 @@ fn completes_const_and_type_generics_separately() { } "#, expect![[r#" - ct CONST Unit - ct X usize + ct CONST = Unit Unit + ct X = 0 usize ma makro!(…) macro_rules! makro kw crate:: kw dyn @@ -1361,8 +1361,8 @@ fn completes_const_and_type_generics_separately() { } "#, expect![[r#" - ct CONST Unit - ct X usize + ct CONST = Unit Unit + ct X = 0 usize ma makro!(…) macro_rules! makro kw crate:: kw dyn @@ -1381,8 +1381,8 @@ fn completes_const_and_type_generics_separately() { impl Foo<(), $0> for () {} "#, expect![[r#" - ct CONST Unit - ct X usize + ct CONST = Unit Unit + ct X = 0 usize ma makro!(…) macro_rules! makro kw crate:: kw dyn @@ -1403,8 +1403,8 @@ fn completes_const_and_type_generics_separately() { fn foo>() {} "#, expect![[r#" - ct CONST Unit - ct X usize + ct CONST = Unit Unit + ct X = 0 usize ma makro!(…) macro_rules! makro kw crate:: kw dyn @@ -1422,7 +1422,7 @@ struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); fn foo<'a>() { S::; } "#, expect![[r#" - ct CONST Unit + ct CONST = Unit Unit ma makro!(…) macro_rules! makro kw crate:: kw dyn @@ -1439,7 +1439,7 @@ struct S<'a, 'b, const C: usize, T>(core::marker::PhantomData<&'a &'b T>); fn foo<'a>() { S::<'static, 'static, F$0, _>; } "#, expect![[r#" - ct CONST Unit + ct CONST = Unit Unit ma makro!(…) macro_rules! makro kw crate:: kw dyn diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs index 593b1edde5cc5..98e44b3bb3a46 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs @@ -252,7 +252,7 @@ mod a { } "#, expect![[r#" - ct A usize + ct A = 0 usize md b kw super:: "#]], diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs index 0235389763080..bf7aa771c2afa 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/insert_use.rs @@ -573,5 +573,5 @@ fn insert_use_with_editor_( } fn is_inner_attribute(node: SyntaxNode) -> bool { - ast::Attr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) + ast::AnyAttr::cast(node).map(|attr| attr.kind()) == Some(ast::AttrKind::Inner) } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs index b02bccf5a533c..5ab30fffe76da 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/explicit_drop_method_use.rs @@ -127,9 +127,7 @@ fn fix_path( #[cfg(test)] mod tests { - use crate::tests::{ - check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled, - }; + use crate::tests::{check_diagnostics, check_fix}; #[test] fn method_call_diagnostic() { @@ -289,7 +287,7 @@ fn main(mut a: A) { #[test] fn path_diagnostic() { - check_diagnostics_with_disabled( + check_diagnostics( r#" //- minicore: drop struct A; @@ -301,10 +299,6 @@ fn main(mut a: A) { d(&mut a); } "#, - // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. - // Arguably a bug in r-a (rustc doesn't emit a warning in this case) - // FIXME: remove this once r-a no longer warns - &["unused_variables"], ); } @@ -312,7 +306,7 @@ fn main(mut a: A) { // NOTE: Here, the fix is not completely correct, as it doesn't replace `d(&mut a)` with `d(a)`. // Oh well, rustc doesn't either fn path_fix() { - check_fix_with_disabled( + check_fix( r#" //- minicore: drop struct A; @@ -332,10 +326,6 @@ fn main(mut a: A) { d(&mut a); } "#, - // Because of the error, the code isn't analyzed further (?), and so `d` is warned on as unused. - // Arguably a bug in r-a (rustc doesn't emit a warning in this case) - // FIXME: remove this once r-a no longer warns - &["unused_variables"], ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs index f8d3d80d62f6b..270b27a8e37cc 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/fru_in_destructuring_assignment.rs @@ -18,11 +18,11 @@ pub(crate) fn fru_in_destructuring_assignment( #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_diagnostics_with_disabled}; + use crate::tests::check_diagnostics; #[test] fn spread_variable() { - check_diagnostics_with_disabled( + check_diagnostics( r#" struct Foo { bar: u32, baz: u32 } fn test(f: Foo, g: Foo, mut bar: u32, mut baz: u32) { @@ -34,8 +34,6 @@ fn test(f: Foo, g: Foo, mut bar: u32, mut baz: u32) { // ^ error: functional record updates are not allowed in destructuring assignments } "#, - // We don't end up using neither `bar` nor `baz` - &["unused_variables"], ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 888f55cea777e..a72da8e7722a5 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -61,7 +61,7 @@ fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::IncorrectCase) -> Option u8 { "#, ); - check_fix( + check_fix_with_disabled( r#" fn some_fn() { let whatAWeird_Formatting$0 = 10; @@ -121,6 +121,7 @@ fn some_fn() { another_func(what_aweird_formatting); } "#, + &["E0425"], ); check_fix( @@ -617,7 +618,7 @@ trait BAD_TRAIT { cov_mark::check!(trait_impl_assoc_const_incorrect_case_ignored); cov_mark::check!(trait_impl_assoc_type_incorrect_case_ignored); cov_mark::check_count!(trait_impl_assoc_func_name_incorrect_case_ignored, 2); - check_diagnostics_with_disabled( + check_diagnostics( r#" trait BAD_TRAIT { // ^^^^^^^^^ 💡 warn: Trait `BAD_TRAIT` should have UpperCamelCase name, e.g. `BadTrait` @@ -643,7 +644,6 @@ impl BAD_TRAIT for () { fn BadFunction() {} } "#, - &["unused_variables"], ); } @@ -854,8 +854,6 @@ static FOO: () = { } #[test] - // FIXME - #[should_panic] fn enum_variant_body_inner_item() { check_diagnostics( r#" @@ -1006,7 +1004,6 @@ fn func() { fn override_lint_level() { check_diagnostics( r#" -#![allow(unused_variables)] #[warn(nonstandard_style)] fn foo() { let BAR: i32; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs index 87647a84cc7ce..c9dd53dfe176c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -465,7 +465,7 @@ fn foo() { //^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const T` } "#, - &["E0308", "unused_variables"], + &["E0308"], ); } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_body.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_body.rs new file mode 100644 index 0000000000000..7905336272643 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_body.rs @@ -0,0 +1,72 @@ +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +// Diagnostic: missing-body +// +// This diagnostic is triggered when a body is missing. +pub(crate) fn missing_body(ctx: &DiagnosticsContext<'_, '_>, d: &hir::MissingBody) -> Diagnostic { + let message = match d.kind { + hir::MissingBodyItemKind::AssocConst => "associated constant in `impl` without body", + hir::MissingBodyItemKind::AssocType => "associated type in `impl` without body", + hir::MissingBodyItemKind::Const => "free constant item without body", + hir::MissingBodyItemKind::Static => "free static item without body", + hir::MissingBodyItemKind::TypeAlias => "free type alias without body", + }; + Diagnostic::new_with_syntax_node_ptr(ctx, DiagnosticCode::SyntaxError, message, d.node).stable() +} + +#[cfg(test)] +mod tests { + use crate::tests::check_diagnostics; + + #[test] + fn associated_const() { + check_diagnostics( + r#" +trait Foo { const BAR: u32; } +impl Foo for () { const BAR: u32; } + //^^^^^^^^^^^^^^^ error: associated constant in `impl` without body + "#, + ); + } + + #[test] + fn associated_type_impl() { + check_diagnostics( + r#" +trait Foo { type Bar; } +impl Foo for () { type Bar; } + //^^^^^^^^^ error: associated type in `impl` without body + "#, + ); + } + + #[test] + fn free_const() { + check_diagnostics( + r#" + const FOO: u32; +//^^^^^^^^^^^^^^^ error: free constant item without body + "#, + ); + } + + #[test] + fn free_static() { + check_diagnostics( + r#" + static FOO: u32; +//^^^^^^^^^^^^^^^^ error: free static item without body + "#, + ); + } + + #[test] + fn type_alias_module() { + check_diagnostics( + r#" + type Foo; +//^^^^^^^^^ error: free type alias without body + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index 4b55995a058df..abdee7da3fb6c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -425,7 +425,6 @@ fn main() { check_diagnostics( r#" //- minicore: index, slice -#![allow(unused_variables)] fn main() { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 9be7ef6fe7181..8f36f1e583f31 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -105,4 +105,16 @@ fn foo() { "#, ); } + + #[test] + fn tool_attribute_with_keyword_segment() { + check_diagnostics( + r#" +#[diagnostic::Self(message = "foo")] +trait Foo {} +#[clippy::Self(message = "foo")] +trait Bar {} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 5d816a8d41c3c..4e941f8da0709 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -60,6 +60,7 @@ mod handlers { pub(crate) mod method_call_illegal_sized_bound; pub(crate) mod mismatched_arg_count; pub(crate) mod mismatched_array_pat_len; + pub(crate) mod missing_body; pub(crate) mod missing_fields; pub(crate) mod missing_lifetime; pub(crate) mod missing_match_arms; @@ -545,6 +546,7 @@ pub fn semantic_diagnostics( } AnyDiagnostic::UnimplementedTrait(d) => handlers::unimplemented_trait::unimplemented_trait(&ctx, &d), AnyDiagnostic::FruInDestructuringAssignment(d) => handlers::fru_in_destructuring_assignment::fru_in_destructuring_assignment(&ctx, &d), + AnyDiagnostic::MissingBody(d) => handlers::missing_body::missing_body(&ctx, &d), AnyDiagnostic::ExplicitDropMethodUse(d) => handlers::explicit_drop_method_use::explicit_drop_method_use(&ctx, &d), AnyDiagnostic::YieldOutsideCoroutine(d) => handlers::yield_outside_coroutine::yield_outside_coroutine(&ctx, &d), AnyDiagnostic::ReturnOutsideFunction(d) => handlers::return_outside_function::return_outside_function(&ctx, &d), diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs index 83b8c3dc81ea6..c3089262ecc1d 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/from_comment.rs @@ -22,7 +22,7 @@ pub fn ssr_from_comment( let file = file_id.parse(db); file.tree().syntax().token_at_offset(frange.range.start()).find_map(ast::Comment::cast) }?; - let comment_text_without_prefix = comment.text().strip_prefix(comment.prefix()).unwrap(); + let comment_text_without_prefix = comment.text_without_markers(); let ssr_rule = comment_text_without_prefix.parse().ok()?; let lookup_context = FilePosition { file_id: frange.file_id, offset: frange.range.start() }; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 2f29fc31f8a84..c152d7e9cc964 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -277,19 +277,28 @@ pub(crate) struct DocCommentToken { } pub(crate) fn token_as_doc_comment(doc_token: &SyntaxToken) -> Option { - (match_ast! { - match doc_token { - ast::Comment(comment) => TextSize::try_from(comment.prefix().len()).ok(), - ast::String(string) => { - doc_token.parent_ancestors().find_map(ast::Attr::cast).filter(|attr| attr.simple_name().as_deref() == Some("doc"))?; - if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()) == Some("include_str")).is_some() { - return None; - } - string.open_quote_text_range().map(|it| it.len()) - }, - _ => None, + let prefix_len = if matches!(doc_token.kind(), INNER_DOC_COMMENT | OUTER_DOC_COMMENT) { + ast::DocComment::PREFIX_LEN + } else { + let string = ast::String::cast(doc_token.clone())?; + doc_token + .parent_ancestors() + .find_map(ast::Attr::cast) + .filter(|attr| attr.simple_name().as_deref() == Some("doc"))?; + if doc_token + .parent_ancestors() + .find_map(ast::MacroCall::cast) + .filter(|mac| { + mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()) + == Some("include_str") + }) + .is_some() + { + return None; } - }).map(|prefix_len| DocCommentToken { prefix_len, doc_token: doc_token.clone() }) + string.open_quote_text_range()?.len() + }; + Some(DocCommentToken { prefix_len, doc_token: doc_token.clone() }) } impl DocCommentToken { @@ -308,8 +317,8 @@ impl DocCommentToken { sema.descend_into_macros(doc_token).into_iter().find_map(|t| { let (node, descended_prefix_len, is_inner) = match_ast!{ match t { - ast::Comment(comment) => { - (t.parent()?, TextSize::try_from(comment.prefix().len()).ok()?, comment.is_inner()) + ast::AnyComment(comment) => { + (t.parent()?.parent()?, TextSize::try_from(comment.prefix().len()).ok()?, comment.is_inner()) }, ast::String(string) => { let attr = t.parent_ancestors().find_map(ast::Attr::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs index 720528d0b52f2..f5755da13bdbe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links/tests.rs @@ -449,7 +449,7 @@ fn doc_links_items_simple() { check_doc_links( r#" //- /main.rs crate:main deps:krate -/// [`krate`] +//! [`krate`] //! [`Trait`] //! [`function`] //! [`CONST`] diff --git a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs index 2926384c40786..079f8426cf5af 100644 --- a/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs +++ b/src/tools/rust-analyzer/crates/ide/src/extend_selection.rs @@ -35,7 +35,8 @@ fn try_extend_selection( ) -> Option { let range = frange.range; - let string_kinds = [COMMENT, STRING, BYTE_STRING, C_STRING]; + let string_kinds = + [COMMENT, INNER_DOC_COMMENT, OUTER_DOC_COMMENT, STRING, BYTE_STRING, C_STRING]; let list_kinds = [ RECORD_PAT_FIELD_LIST, MATCH_ARM_LIST, @@ -81,7 +82,7 @@ fn try_extend_selection( if token.text_range() != range { return Some(token.text_range()); } - if let Some(comment) = ast::Comment::cast(token.clone()) + if let Some(comment) = ast::AnyComment::cast(token.clone()) && let Some(range) = extend_comments(comment) { return Some(range); @@ -292,7 +293,7 @@ fn extend_list_item(node: &SyntaxNode) -> Option { None } -fn extend_comments(comment: ast::Comment) -> Option { +fn extend_comments(comment: ast::AnyComment) -> Option { let prev = adj_comments(&comment, Direction::Prev); let next = adj_comments(&comment, Direction::Next); if prev != next { @@ -302,14 +303,14 @@ fn extend_comments(comment: ast::Comment) -> Option { } } -fn adj_comments(comment: &ast::Comment, dir: Direction) -> ast::Comment { +fn adj_comments(comment: &ast::AnyComment, dir: Direction) -> ast::AnyComment { let mut res = comment.clone(); for element in comment.syntax().siblings_with_tokens(dir) { let token = match element.as_token() { None => break, Some(token) => token, }; - if let Some(c) = ast::Comment::cast(token.clone()) { + if let Some(c) = ast::AnyComment::cast(token.clone()) { res = c } else if token.kind() != WHITESPACE || token.text().contains("\n\n") { break; diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs index ae006d152a386..4cac61d9f6cef 100644 --- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs +++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs @@ -10,8 +10,8 @@ use syntax::{ use std::hash::Hash; -const REGION_START: &str = "// region:"; -const REGION_END: &str = "// endregion"; +const REGION_START: &str = "region:"; +const REGION_END: &str = "endregion"; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum FoldKind { @@ -109,7 +109,7 @@ pub(crate) fn folding_ranges(file: &SourceFile, add_collapsed_text: bool) -> Vec match element { NodeOrToken::Token(token) => { // Fold groups of comments - if let Some(comment) = ast::Comment::cast(token) { + if let Some(comment) = ast::AnyComment::cast(token) { if visited_comments.contains(&comment) { continue; } @@ -200,7 +200,7 @@ fn fold_kind( } match element.kind() { - COMMENT => Some(FoldKind::Comment), + COMMENT | INNER_DOC_COMMENT | OUTER_DOC_COMMENT => Some(FoldKind::Comment), ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList), ARRAY_EXPR => Some(FoldKind::Array), RET_TYPE => Some(FoldKind::ReturnType), @@ -391,8 +391,8 @@ fn eq_visibility(vis0: Option, vis1: Option) - } fn contiguous_range_for_comment( - first: ast::Comment, - visited: &mut FxHashSet, + first: ast::AnyComment, + visited: &mut FxHashSet, ) -> Option { visited.insert(first.clone()); @@ -403,33 +403,29 @@ fn contiguous_range_for_comment( } let mut last = first.clone(); - for element in first.syntax().siblings_with_tokens(Direction::Next) { - match element { - NodeOrToken::Token(token) => { - if let Some(ws) = ast::Whitespace::cast(token.clone()) - && !ws.spans_multiple_lines() - { - // Ignore whitespace without blank lines - continue; - } - if let Some(c) = ast::Comment::cast(token) - && c.kind() == group_kind - { - let text = c.text().trim_start(); - // regions are not real comments - if !(text.starts_with(REGION_START) || text.starts_with(REGION_END)) { - visited.insert(c.clone()); - last = c; - continue; - } - } - // The comment group ends because either: - // * An element of a different kind was reached - // * A comment of a different flavor was reached - break; + let next_comments = std::iter::successors(Some(first.syntax().clone()), |it| it.next_token()); + for token in next_comments { + if let Some(ws) = ast::Whitespace::cast(token.clone()) + && !ws.spans_multiple_lines() + { + // Ignore whitespace without blank lines + continue; + } + if let Some(c) = ast::AnyComment::cast(token) + && c.kind() == group_kind + { + let text = c.text().trim_start(); + // regions are not real comments + if !(text.starts_with(REGION_START) || text.starts_with(REGION_END)) { + visited.insert(c.clone()); + last = c; + continue; } - NodeOrToken::Node(_) => break, - }; + } + // The comment group ends because either: + // * An element of a different kind was reached + // * A comment of a different flavor was reached + break; } if first != last { diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 6947f00e21870..033de7dcc20b4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -71,7 +71,9 @@ pub(crate) fn goto_definition( | T![super] | T![crate] | T![Self] - | COMMENT => 4, + | COMMENT + | INNER_DOC_COMMENT + | OUTER_DOC_COMMENT => 4, // index and prefix ops T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, kind if kind.is_keyword(edition) => 2, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 3d73b5b0f24d4..92473de4e634f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -182,8 +182,12 @@ fn hover_offset( _ => 1, })?; - if let Some(doc_comment) = token_as_doc_comment(&original_token) { + if ast::Comment::can_cast(original_token.kind()) { cov_mark::hit!(no_highlight_on_comment_hover); + return None; + } + + if let Some(doc_comment) = token_as_doc_comment(&original_token) { return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| { let res = hover_for_definition( sema, diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index f70783fd3c0c4..1897168821863 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -842,6 +842,29 @@ pub(super) fn literal( } .original; + let parse_float = |text: &str| { + if ty.as_builtin().map(|it| it.is_f16()).unwrap_or(false) { + match text.parse::() { + Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), + Err(e) => Err(e.0.to_owned()), + } + } else if ty.as_builtin().map(|it| it.is_f32()).unwrap_or(false) { + match text.parse::() { + Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), + Err(e) => Err(e.to_string()), + } + } else if ty.as_builtin().map(|it| it.is_f128()).unwrap_or(false) { + match text.parse::() { + Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), + Err(e) => Err(e.0.to_owned()), + } + } else { + match text.parse::() { + Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), + Err(e) => Err(e.to_string()), + } + } + }; let value = match_ast! { match token { ast::String(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string), @@ -849,29 +872,9 @@ pub(super) fn literal( ast::CString(string) => string.value().as_ref().map_err(|e| format!("{e:?}")).map(|it| std::str::from_utf8(it).map_or_else(|e| format!("{e:?}"), ToOwned::to_owned)), ast::Char(char) => char .value().as_ref().map_err(|e| format!("{e:?}")).map(ToString::to_string), ast::Byte(byte) => byte .value().as_ref().map_err(|e| format!("{e:?}")).map(|it| format!("0x{it:X}")), - ast::FloatNumber(num) => { - let text = num.value_string(); - if ty.as_builtin().map(|it| it.is_f16()).unwrap_or(false) { - match text.parse::() { - Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), - Err(e) => Err(e.0.to_owned()), - } - } else if ty.as_builtin().map(|it| it.is_f32()).unwrap_or(false) { - match text.parse::() { - Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), - Err(e) => Err(e.to_string()), - } - } else if ty.as_builtin().map(|it| it.is_f128()).unwrap_or(false) { - match text.parse::() { - Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), - Err(e) => Err(e.0.to_owned()), - } - } else { - match text.parse::() { - Ok(num) => Ok(format!("{num} (bits: 0x{:X})", num.to_bits())), - Err(e) => Err(e.to_string()), - } - } + ast::FloatNumber(num) => parse_float(&num.value_string()), + ast::IntNumber(num) if matches!(num.suffix(), Some("f16" | "f32" | "f64" | "f128")) => { + parse_float(&num.value_string()) }, ast::IntNumber(num) => match num.value() { Ok(num) => Ok(format!("{num} (0x{num:X}|0b{num:b})")), diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 66ccd1924639e..c8aa8a2221640 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -9139,6 +9139,22 @@ fn main() { ); check( r#" +fn main() { + $01f64; +} +"#, + expect![[r#" + *1f64* + ```rust + f64 + ``` + --- + + value of literal: ` 1 (bits: 0x3FF0000000000000) ` + "#]], + ); + check( + r#" fn main() { $00.1ea123; } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 4b06f83971b25..e008a7b940662 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -34,7 +34,9 @@ pub(super) fn hints( .filter_map(NodeOrToken::into_token) .filter(|t| match t.kind() { SyntaxKind::WHITESPACE if !t.text().contains('\n') => false, - SyntaxKind::COMMENT => false, + SyntaxKind::COMMENT | SyntaxKind::OUTER_DOC_COMMENT | SyntaxKind::INNER_DOC_COMMENT => { + false + } _ => true, }); diff --git a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs index a946559c35455..4c648d3cb9a0f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/join_lines.rs +++ b/src/tools/rust-analyzer/crates/ide/src/join_lines.rs @@ -186,9 +186,10 @@ fn remove_newline( } } + // We can't use `prev` and `next`, since `DOC_COMMENT` has only one token, so `token` has no siblings. if let (Some(_), Some(next)) = ( - prev.as_token().cloned().and_then(ast::Comment::cast), - next.as_token().cloned().and_then(ast::Comment::cast), + token.prev_token().and_then(ast::AnyComment::cast), + token.next_token().and_then(ast::AnyComment::cast), ) { // Removes: newline (incl. surrounding whitespace), start of the next comment edit.delete(TextRange::new( @@ -674,14 +675,14 @@ fn foo() { fn test_join_lines_doc_comments() { check_join_lines( r" +/// Hello$0 +/// world! fn foo() { - /// Hello$0 - /// world! } ", r" +/// Hello$0 world! fn foo() { - /// Hello$0 world! } ", ); diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index c92f2bbace84a..dd1c7379e7906 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -154,7 +154,9 @@ pub(crate) fn moniker( | T![super] | T![crate] | T![Self] - | COMMENT => 2, + | COMMENT + | INNER_DOC_COMMENT + | OUTER_DOC_COMMENT => 2, kind if kind.is_trivia() => 0, _ => 1, })?; diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs index 9e8d772cb3d12..7a01eb9054bfa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs +++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs @@ -12,7 +12,7 @@ use ide_db::{ famous_defs::FamousDefs, ra_fixture::RaFixtureConfig, }; -use syntax::{AstNode, AstToken, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, TextRange, ast}; +use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, TextRange}; use crate::navigation_target::UpmappingResult; use crate::{ @@ -349,15 +349,20 @@ fn definition_range_excluding_trivia( } fn is_leading_trivia_excluding_docs(token: &SyntaxToken) -> bool { - match token.kind() { - SyntaxKind::WHITESPACE => true, - SyntaxKind::COMMENT => ast::Comment::cast(token.clone()).is_none_or(|it| !it.is_outer()), - _ => false, - } + matches!( + token.kind(), + SyntaxKind::WHITESPACE | SyntaxKind::COMMENT | SyntaxKind::INNER_DOC_COMMENT + ) } fn is_trailing_trivia(token: &SyntaxToken) -> bool { - matches!(token.kind(), SyntaxKind::WHITESPACE | SyntaxKind::COMMENT) + matches!( + token.kind(), + SyntaxKind::WHITESPACE + | SyntaxKind::COMMENT + | SyntaxKind::INNER_DOC_COMMENT + | SyntaxKind::OUTER_DOC_COMMENT + ) } #[cfg(test)] diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index 9fd3f005ec70a..c29f180101632 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -534,7 +534,7 @@ fn descend_token( sema: &Semantics<'_, RootDatabase>, token: InRealFile, ) -> InFile> { - if token.value.kind() == COMMENT { + if ast::AnyComment::can_cast(token.value.kind()) { return token.map(NodeOrToken::Token).into(); } let ranker = Ranker::from_token(&token.value); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 92daacd6d314f..07b191231807c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -11,7 +11,7 @@ use ide_db::{ }; use span::Edition; use syntax::{ - AstNode, AstPtr, AstToken, NodeOrToken, + AstNode, AstPtr, NodeOrToken, SyntaxKind::{self, *}, SyntaxNode, SyntaxNodePtr, SyntaxToken, T, ast, match_ast, }; @@ -28,15 +28,9 @@ pub(super) fn token( is_unsafe_node: &impl Fn(AstPtr>) -> bool, in_tt: bool, ) -> Option { - if let Some(comment) = ast::Comment::cast(token.clone()) { - let h = HlTag::Comment; - return Some(match comment.kind().doc { - Some(_) => h | HlMod::Documentation, - None => h.into(), - }); - } - let h = match token.kind() { + COMMENT => HlTag::Comment.into(), + INNER_DOC_COMMENT | OUTER_DOC_COMMENT => HlTag::Comment | HlMod::Documentation, STRING | BYTE_STRING | C_STRING => HlTag::StringLiteral.into(), INT_NUMBER | FLOAT_NUMBER => HlTag::NumericLiteral.into(), BYTE => HlTag::ByteLiteral.into(), diff --git a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs index 4e3c491418754..c4f53337b3910 100644 --- a/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs +++ b/src/tools/rust-analyzer/crates/ide/src/typing/on_enter.rs @@ -55,7 +55,7 @@ pub(crate) fn on_enter(db: &RootDatabase, position: FilePosition) -> Option Option Option { @@ -159,7 +159,7 @@ fn brace_contents_on_same_line(l_curly: &SyntaxToken) -> Option<(SyntaxToken, St } } -fn followed_by_comment(comment: &ast::Comment) -> bool { +fn followed_by_comment(comment: &ast::AnyComment) -> bool { let ws = match comment.syntax().next_token().and_then(ast::Whitespace::cast) { Some(it) => it, None => return false, @@ -167,7 +167,7 @@ fn followed_by_comment(comment: &ast::Comment) -> bool { if ws.spans_multiple_lines() { return false; } - ws.syntax().next_token().and_then(ast::Comment::cast).is_some() + ws.syntax().next_token().and_then(ast::AnyComment::cast).is_some() } fn node_indent(file: &SourceFile, token: &SyntaxToken) -> Option { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs index 2eeaa25257dba..acce7afb54e9b 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs @@ -1,23 +1,29 @@ use super::*; -pub(super) const ATTRIBUTE_FIRST: TokenSet = TokenSet::new(&[T![#]]); +pub(super) const OUTER_ATTR_FIRST: TokenSet = TokenSet::new(&[T![#], OUTER_DOC_COMMENT]); pub(super) fn inner_attrs(p: &mut Parser<'_>) { - while p.at(T![#]) && p.nth(1) == T![!] { + while p.at(INNER_DOC_COMMENT) || (p.at(T![#]) && p.nth_at(1, T![!])) { attr(p, true); } } pub(super) fn outer_attrs(p: &mut Parser<'_>) { - while p.at(T![#]) { + while p.at_ts(OUTER_ATTR_FIRST) { attr(p, false); } } fn attr(p: &mut Parser<'_>, inner: bool) { - assert!(p.at(T![#])); + if (inner && p.at(INNER_DOC_COMMENT)) || (!inner && p.at(OUTER_DOC_COMMENT)) { + let m = p.start(); + p.bump_any(); + m.complete(p, DOC_COMMENT); + return; + } let attr = p.start(); + p.bump(T![#]); if inner { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs index d419817e5cd70..661ab0562e70e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/generic_params.rs @@ -1,4 +1,4 @@ -use crate::grammar::attributes::ATTRIBUTE_FIRST; +use crate::grammar::attributes::OUTER_ATTR_FIRST; use super::*; @@ -22,7 +22,7 @@ pub(super) fn generic_param_list(p: &mut Parser<'_>) { T![>], T![,], || "expected generic parameter".into(), - GENERIC_PARAM_FIRST.union(ATTRIBUTE_FIRST), + GENERIC_PARAM_FIRST.union(OUTER_ATTR_FIRST), |p| { // test generic_param_attribute // fn foo<#[lt_attr] 'a, #[t_attr] T>() {} diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs index 33e19f5725b36..ec4e595398788 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items/adt.rs @@ -1,4 +1,4 @@ -use crate::grammar::attributes::ATTRIBUTE_FIRST; +use crate::grammar::attributes::OUTER_ATTR_FIRST; use super::*; @@ -180,7 +180,7 @@ pub(crate) fn record_field_list(p: &mut Parser<'_>) { } const TUPLE_FIELD_FIRST: TokenSet = - types::TYPE_FIRST.union(ATTRIBUTE_FIRST).union(VISIBILITY_FIRST); + types::TYPE_FIRST.union(OUTER_ATTR_FIRST).union(VISIBILITY_FIRST); // test_err tuple_field_list_recovery // struct S(struct S; diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs index 51ffcd070694c..4c7b057f03129 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/params.rs @@ -1,4 +1,4 @@ -use crate::grammar::attributes::ATTRIBUTE_FIRST; +use crate::grammar::attributes::OUTER_ATTR_FIRST; use super::*; @@ -64,7 +64,7 @@ fn list_(p: &mut Parser<'_>, flavor: Flavor) { } }; - if !p.at_ts(PARAM_FIRST.union(ATTRIBUTE_FIRST)) { + if !p.at_ts(PARAM_FIRST.union(OUTER_ATTR_FIRST)) { p.error("expected value parameter"); m.abandon(p); if p.eat(T![,]) { @@ -74,7 +74,7 @@ fn list_(p: &mut Parser<'_>, flavor: Flavor) { } param(p, m, flavor); if !p.eat(T![,]) { - if p.at_ts(PARAM_FIRST.union(ATTRIBUTE_FIRST)) { + if p.at_ts(PARAM_FIRST.union(OUTER_ATTR_FIRST)) { p.error("expected `,`"); } else { break; @@ -165,7 +165,23 @@ fn variadic_param(p: &mut Parser<'_>) -> bool { // fn e(mut self) {} // } fn opt_self_param(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { - if p.at(T![self]) || p.at(T![mut]) && p.nth(1) == T![self] { + let is_isolated_self = |p: &mut Parser<'_>, n| { + // test non_isolated_self + // fn f(self::S: S) {} + // fn g(&self::S: &S) {} + // fn h(&mut self::S: &mut S) {} + + // test_err non_isolated_self_err + // fn f(mut self::S: S) {} + // fn g(&'l self::S: &S) {} + // fn h(&'l mut self::S: &mut S) {} + p.nth_at(n, T![self]) && !p.nth_at(n + 1, T![::]) + }; + let mut self_pos = 0; + if p.at(T![mut]) { + self_pos += 1; + } + if is_isolated_self(p, self_pos) { p.eat(T![mut]); self_as_name(p); // test arb_self_types @@ -177,17 +193,20 @@ fn opt_self_param(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { types::ascription(p); } } else { - let la1 = p.nth(1); - let la2 = p.nth(2); - let la3 = p.nth(3); - if !matches!( - (p.current(), la1, la2, la3), - (T![&], T![self], _, _) - | (T![&], T![mut] | LIFETIME_IDENT, T![self], _) - | (T![&], LIFETIME_IDENT, T![mut], T![self]) - ) { + if !p.at(T![&]) { return Err(m); } + let mut self_pos = 1; + if p.nth_at(self_pos, LIFETIME_IDENT) { + self_pos += 1; + } + if p.nth_at(self_pos, T![mut]) { + self_pos += 1; + } + if !is_isolated_self(p, self_pos) { + return Err(m); + } + p.bump(T![&]); if p.at(LIFETIME_IDENT) { lifetime(p); diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index d7eec6cde8c01..ec994b731bf95 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -195,6 +195,14 @@ impl<'a> Converter<'a> { } } + fn comment_kind(doc_style: Option) -> SyntaxKind { + match doc_style { + Some(rustc_lexer::DocStyle::Outer) => OUTER_DOC_COMMENT, + Some(rustc_lexer::DocStyle::Inner) => INNER_DOC_COMMENT, + None => COMMENT, + } + } + fn extend_token(&mut self, kind: &rustc_lexer::TokenKind, mut token_text: &str) { // A note on an intended tradeoff: // We drop some useful information here (see patterns with double dots `..`) @@ -204,14 +212,14 @@ impl<'a> Converter<'a> { let syntax_kind = { match kind { - rustc_lexer::TokenKind::LineComment { doc_style: _ } => COMMENT, - rustc_lexer::TokenKind::BlockComment { doc_style: _, terminated } => { + rustc_lexer::TokenKind::LineComment { doc_style } => Self::comment_kind(*doc_style), + rustc_lexer::TokenKind::BlockComment { doc_style, terminated } => { if !terminated { errors.push( "Missing trailing `*/` symbols to terminate the block comment".into(), ); } - COMMENT + Self::comment_kind(*doc_style) } rustc_lexer::TokenKind::Frontmatter { diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 5604da5026e90..81a3b423e4436 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -162,8 +162,10 @@ pub enum SyntaxKind { ERROR, FRONTMATTER, IDENT, + INNER_DOC_COMMENT, LIFETIME_IDENT, NEWLINE, + OUTER_DOC_COMMENT, SHEBANG, WHITESPACE, ABI, @@ -204,6 +206,7 @@ pub enum SyntaxKind { CONST_PARAM, CONTINUE_EXPR, DEREF_PAT, + DOC_COMMENT, DYN_TRAIT_TYPE, ENUM, EXPR_STMT, @@ -391,6 +394,7 @@ impl SyntaxKind { | CONST_PARAM | CONTINUE_EXPR | DEREF_PAT + | DOC_COMMENT | DYN_TRAIT_TYPE | ENUM | EXPR_STMT @@ -526,8 +530,10 @@ impl SyntaxKind { | ERROR | FRONTMATTER | IDENT + | INNER_DOC_COMMENT | LIFETIME_IDENT | NEWLINE + | OUTER_DOC_COMMENT | SHEBANG | WHITESPACE => panic!("no text for these `SyntaxKind`s"), DOLLAR => "$", @@ -1226,6 +1232,8 @@ macro_rules ! T_ { [string] => { $ crate :: SyntaxKind :: STRING }; [shebang] => { $ crate :: SyntaxKind :: SHEBANG }; [frontmatter] => { $ crate :: SyntaxKind :: FRONTMATTER }; + [inner_doc_comment] => { $ crate :: SyntaxKind :: INNER_DOC_COMMENT }; + [outer_doc_comment] => { $ crate :: SyntaxKind :: OUTER_DOC_COMMENT }; } impl ::core::marker::Copy for SyntaxKind {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 520e736e302bb..9e3de7517f227 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -492,6 +492,10 @@ mod ok { run_and_expect_no_errors("test_data/parser/inline/ok/nocontentexpr_after_item.rs"); } #[test] + fn non_isolated_self() { + run_and_expect_no_errors("test_data/parser/inline/ok/non_isolated_self.rs"); + } + #[test] fn not_null_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/not_null_pat.rs"); } #[test] fn offset_of_parens() { @@ -928,6 +932,10 @@ mod err { run_and_expect_errors("test_data/parser/inline/err/missing_static_type.rs"); } #[test] + fn non_isolated_self_err() { + run_and_expect_errors("test_data/parser/inline/err/non_isolated_self_err.rs"); + } + #[test] fn path_item_without_excl() { run_and_expect_errors("test_data/parser/inline/err/path_item_without_excl.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_nested_block_comment_partially.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_nested_block_comment_partially.rast index e9b74ee7f8276..54341df49c869 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_nested_block_comment_partially.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/err/unclosed_nested_block_comment_partially.rast @@ -1 +1 @@ -COMMENT "/** /*! /* comment */ */\n" error: Missing trailing `*/` symbols to terminate the block comment +OUTER_DOC_COMMENT "/** /*! /* comment */ */\n" error: Missing trailing `*/` symbols to terminate the block comment diff --git a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/single_line_comments.rast b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/single_line_comments.rast index c4e531b449f7b..17ef356156f7e 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/single_line_comments.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/lexer/ok/single_line_comments.rast @@ -1,21 +1,21 @@ SHEBANG "#!/usr/bin/env bash\n" COMMENT "// hello" WHITESPACE "\n" -COMMENT "//! World" +INNER_DOC_COMMENT "//! World" WHITESPACE "\n" -COMMENT "//!! Inner line doc" +INNER_DOC_COMMENT "//!! Inner line doc" WHITESPACE "\n" -COMMENT "/// Outer line doc" +OUTER_DOC_COMMENT "/// Outer line doc" WHITESPACE "\n" COMMENT "//// Just a comment" WHITESPACE "\n\n" COMMENT "//" WHITESPACE "\n" -COMMENT "//!" +INNER_DOC_COMMENT "//!" WHITESPACE "\n" -COMMENT "//!!" +INNER_DOC_COMMENT "//!!" WHITESPACE "\n" -COMMENT "///" +OUTER_DOC_COMMENT "///" WHITESPACE "\n" COMMENT "////" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/non_isolated_self_err.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/non_isolated_self_err.rast new file mode 100644 index 0000000000000..e75e641fedcc0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/non_isolated_self_err.rast @@ -0,0 +1,131 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + PARAM + IDENT_PAT + MUT_KW "mut" + WHITESPACE " " + ERROR + SELF_KW "self" + COLON ":" + ERROR + COLON ":" + PARAM + IDENT_PAT + NAME + IDENT "S" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "g" + PARAM_LIST + L_PAREN "(" + PARAM + REF_PAT + AMP "&" + ERROR + LIFETIME_IDENT "'l" + WHITESPACE " " + PARAM + PATH_PAT + PATH + PATH + PATH_SEGMENT + NAME_REF + SELF_KW "self" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON ":" + WHITESPACE " " + REF_TYPE + AMP "&" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "h" + PARAM_LIST + L_PAREN "(" + PARAM + REF_PAT + AMP "&" + ERROR + LIFETIME_IDENT "'l" + WHITESPACE " " + PARAM + IDENT_PAT + MUT_KW "mut" + WHITESPACE " " + ERROR + SELF_KW "self" + COLON ":" + ERROR + COLON ":" + PARAM + IDENT_PAT + NAME + IDENT "S" + COLON ":" + WHITESPACE " " + REF_TYPE + AMP "&" + MUT_KW "mut" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" +error 9: expected a name +error 14: expected type +error 15: expected `,` +error 30: expected pattern +error 32: missing type for function parameter +error 32: expected `,` +error 55: expected pattern +error 57: missing type for function parameter +error 57: expected `,` +error 62: expected a name +error 67: expected type +error 68: expected `,` diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/non_isolated_self_err.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/non_isolated_self_err.rs new file mode 100644 index 0000000000000..d36479c660bdd --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/err/non_isolated_self_err.rs @@ -0,0 +1,3 @@ +fn f(mut self::S: S) {} +fn g(&'l self::S: &S) {} +fn h(&'l mut self::S: &mut S) {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/non_isolated_self.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/non_isolated_self.rast new file mode 100644 index 0000000000000..fd4735452ba90 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/non_isolated_self.rast @@ -0,0 +1,109 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "f" + PARAM_LIST + L_PAREN "(" + PARAM + PATH_PAT + PATH + PATH + PATH_SEGMENT + NAME_REF + SELF_KW "self" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON ":" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "g" + PARAM_LIST + L_PAREN "(" + PARAM + REF_PAT + AMP "&" + PATH_PAT + PATH + PATH + PATH_SEGMENT + NAME_REF + SELF_KW "self" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON ":" + WHITESPACE " " + REF_TYPE + AMP "&" + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "h" + PARAM_LIST + L_PAREN "(" + PARAM + REF_PAT + AMP "&" + MUT_KW "mut" + WHITESPACE " " + PATH_PAT + PATH + PATH + PATH_SEGMENT + NAME_REF + SELF_KW "self" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON ":" + WHITESPACE " " + REF_TYPE + AMP "&" + MUT_KW "mut" + WHITESPACE " " + PATH_TYPE + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/non_isolated_self.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/non_isolated_self.rs new file mode 100644 index 0000000000000..29ee4ce4c2f6a --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/non_isolated_self.rs @@ -0,0 +1,3 @@ +fn f(self::S: S) {} +fn g(&self::S: &S) {} +fn h(&mut self::S: &mut S) {} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0035_weird_exprs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0035_weird_exprs.rast index 15ce6c70bea72..b96711c613ca2 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0035_weird_exprs.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0035_weird_exprs.rast @@ -1,11 +1,15 @@ SOURCE_FILE - COMMENT "//! Adapted from a `rustc` test, which can be found at " + DOC_COMMENT + INNER_DOC_COMMENT "//! Adapted from a `rustc` test, which can be found at " WHITESPACE "\n" - COMMENT "//! https://github.com/rust-lang/rust/blob/6d34ec18c7d7e574553f6347ecf08e1e1c45c13d/src/test/run-pass/weird-exprs.rs." + DOC_COMMENT + INNER_DOC_COMMENT "//! https://github.com/rust-lang/rust/blob/6d34ec18c7d7e574553f6347ecf08e1e1c45c13d/src/test/run-pass/weird-exprs.rs." WHITESPACE "\n" - COMMENT "//! " + DOC_COMMENT + INNER_DOC_COMMENT "//! " WHITESPACE "\n" - COMMENT "//! Reported to rust-analyzer in https://github.com/rust-lang/rust-analyzer/issues/290" + DOC_COMMENT + INNER_DOC_COMMENT "//! Reported to rust-analyzer in https://github.com/rust-lang/rust-analyzer/issues/290" WHITESPACE "\n\n" ATTR POUND "#" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0037_mod.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0037_mod.rast index b4a3fc6292e96..59c33eba25c10 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0037_mod.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0037_mod.rast @@ -1,7 +1,8 @@ SOURCE_FILE COMMENT "// https://github.com/rust-lang/rust-analyzer/issues/357" WHITESPACE "\n\n" - COMMENT "//! docs" + DOC_COMMENT + INNER_DOC_COMMENT "//! docs" WHITESPACE "\n" MODULE COMMENT "// non-docs" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast index f26bb85df2927..2dc22d12370ee 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0045_block_attrs.rast @@ -27,7 +27,8 @@ SOURCE_FILE R_PAREN ")" R_BRACK "]" WHITESPACE "\n " - COMMENT "//! As are ModuleDoc style comments" + DOC_COMMENT + INNER_DOC_COMMENT "//! As are ModuleDoc style comments" WHITESPACE "\n " EXPR_STMT BLOCK_EXPR @@ -64,7 +65,8 @@ SOURCE_FILE R_PAREN ")" R_BRACK "]" WHITESPACE "\n " - COMMENT "//! As are ModuleDoc style comments" + DOC_COMMENT + INNER_DOC_COMMENT "//! As are ModuleDoc style comments" WHITESPACE "\n " R_CURLY "}" SEMICOLON ";" @@ -88,7 +90,8 @@ SOURCE_FILE R_PAREN ")" R_BRACK "]" WHITESPACE "\n " - COMMENT "//! As are ModuleDoc style comments" + DOC_COMMENT + INNER_DOC_COMMENT "//! As are ModuleDoc style comments" WHITESPACE "\n " R_CURLY "}" WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast index 3d33eb4ff73ce..8338f1416c044 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0046_extern_inner_attributes.rast @@ -8,7 +8,8 @@ SOURCE_FILE EXTERN_ITEM_LIST L_CURLY "{" WHITESPACE "\n " - COMMENT "//! This is a doc comment" + DOC_COMMENT + INNER_DOC_COMMENT "//! This is a doc comment" WHITESPACE "\n " ATTR POUND "#" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast index c300b7af50589..c9eeda6582afc 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0053_outer_attribute_on_macro_rules.rast @@ -1,6 +1,7 @@ SOURCE_FILE MACRO_RULES - COMMENT "/// Some docs" + DOC_COMMENT + OUTER_DOC_COMMENT "/// Some docs" WHITESPACE "\n" ATTR POUND "#" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0065_comment_newline.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0065_comment_newline.rast index 3ffcb48f5e424..e95ed7d38c90a 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0065_comment_newline.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/ok/0065_comment_newline.rast @@ -1,6 +1,7 @@ SOURCE_FILE FN - COMMENT "/// Example" + DOC_COMMENT + OUTER_DOC_COMMENT "/// Example" WHITESPACE "\n\n" FN_KW "fn" WHITESPACE " " diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs index 8da0a574e7645..40cf975e4522b 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs @@ -211,7 +211,10 @@ impl WorkspaceBuildScripts { let proc_macro_dylibs: Vec<(String, AbsPathBuf)> = std::fs::read_dir(target_libdir)? .filter_map(|entry| { let dir_entry = entry.ok()?; - if dir_entry.file_type().ok()?.is_file() { + // Use `fs::metadata` rather than `DirEntry::file_type` so that symlinks + // are followed; sysroots assembled out of symlinks (e.g. by nix) link + // the proc-macro dylibs into the target libdir. + if std::fs::metadata(dir_entry.path()).ok()?.is_file() { let path = dir_entry.path(); let extension = path.extension()?; if extension == std::env::consts::DLL_EXTENSION { diff --git a/src/tools/rust-analyzer/crates/stdx/src/tempfile.rs b/src/tools/rust-analyzer/crates/stdx/src/tempfile.rs index fe9ae83ef539e..8907f4ca18b80 100644 --- a/src/tools/rust-analyzer/crates/stdx/src/tempfile.rs +++ b/src/tools/rust-analyzer/crates/stdx/src/tempfile.rs @@ -91,7 +91,7 @@ mod general_imp { INTERNAL_COUNTER.fetch_add(1, Ordering::AcqRel), )); let mut open_options = OpenOptions::new(); - open_options.create_new(true); + open_options.write(true).create_new(true); match create(open_options, &path) { Err(e) if e.kind() == ErrorKind::AlreadyExists => {} Err(e) => { @@ -187,3 +187,19 @@ mod imp { Ok(NamedTempFile { _file: Some(file), path, delete_on_drop: true }) } } + +#[cfg(test)] +mod tests { + use std::fs; + + use super::*; + + #[test] + fn named_temp_file_new_creates_file() { + let file = NamedTempFile::new("test-").unwrap(); + assert!(file.path().exists()); + let path = file.path().as_os_str().to_owned(); + drop(file); + assert!(!fs::exists(path).unwrap()); + } +} diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs index 181f9a14e764d..3e6e5f804e263 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs @@ -12,10 +12,10 @@ use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, Span, SpanAnchor, SpanMap, SyntaxContext}; use stdx::{format_to, never}; use syntax::{ - AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, + Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind::{self, *}, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, T, TextRange, TextSize, WalkEvent, - ast::{self, make::tokens::doc_comment}, + ast::make::tokens::doc_comment, format_smolstr, }; use tt::{Punct, buffer::Cursor, token_to_literal}; @@ -250,9 +250,9 @@ where Some(leaf) => leaf.clone(), None => match token.kind(conv) { // Desugar doc comments into doc attributes - COMMENT => { + kind @ (INNER_DOC_COMMENT | OUTER_DOC_COMMENT) => { let span = conv.span_for(abs_range); - conv.convert_doc_comment(&token, span, &mut builder); + conv.convert_doc_comment(&token, kind == INNER_DOC_COMMENT, span, &mut builder); continue; } kind if kind.is_punct() && kind != UNDERSCORE => { @@ -419,13 +419,11 @@ pub fn desugar_doc_comment_text(text: &str, mode: DocCommentDesugarMode) -> (Sym fn convert_doc_comment( token: &syntax::SyntaxToken, + is_inner: bool, span: Span, mode: DocCommentDesugarMode, builder: &mut tt::TopSubtreeBuilder, ) { - let Some(comment) = ast::Comment::cast(token.clone()) else { return }; - let Some(doc) = comment.kind().doc else { return }; - let mk_ident = |s: &str| { tt::Leaf::from(tt::Ident { sym: Symbol::intern(s), span, is_raw: tt::IdentIsRaw::No }) }; @@ -433,14 +431,11 @@ fn convert_doc_comment( let mk_punct = |c: char| tt::Leaf::from(tt::Punct { char: c, spacing: tt::Spacing::Alone, span }); - let mk_doc_literal = |comment: &ast::Comment| { - let prefix_len = comment.prefix().len(); - let mut text = &comment.text()[prefix_len..]; + let mk_doc_literal = |token: &SyntaxToken| { + let text = token.text(); + let from_end = if text.starts_with("/*") && text.ends_with("*/") { 2 } else { 0 }; + let text = &text[3..text.len() - from_end]; - // Remove ending "*/" - if comment.kind().shape == ast::CommentShape::Block { - text = &text[0..text.len() - 2]; - } let (text, kind) = desugar_doc_comment_text(text, mode); let lit = tt::Literal { text_and_suffix: text, span, kind, suffix_len: 0 }; @@ -448,11 +443,11 @@ fn convert_doc_comment( }; // Make `doc="\" Comments\"" - let meta_tkns = [mk_ident("doc"), mk_punct('='), mk_doc_literal(&comment)]; + let meta_tkns = [mk_ident("doc"), mk_punct('='), mk_doc_literal(token)]; // Make `#![]` builder.push(mk_punct('#')); - if let ast::CommentPlacement::Inner = doc { + if is_inner { builder.push(mk_punct('!')); } builder.open(tt::DelimiterKind::Bracket, span); @@ -494,6 +489,7 @@ trait TokenConverter: Sized { fn convert_doc_comment( &self, token: &Self::Token, + is_inner: bool, span: Span, builder: &mut tt::TopSubtreeBuilder, ); @@ -538,9 +534,15 @@ impl SrcToken> for usize { impl TokenConverter for RawConverter<'_> { type Token = usize; - fn convert_doc_comment(&self, &token: &usize, span: Span, builder: &mut tt::TopSubtreeBuilder) { + fn convert_doc_comment( + &self, + &token: &usize, + is_inner: bool, + span: Span, + builder: &mut tt::TopSubtreeBuilder, + ) { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span, self.mode, builder); + convert_doc_comment(&doc_comment(text), is_inner, span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -574,9 +576,15 @@ impl TokenConverter for RawConverter<'_> { impl TokenConverter for StaticRawConverter<'_> { type Token = usize; - fn convert_doc_comment(&self, &token: &usize, span: Span, builder: &mut tt::TopSubtreeBuilder) { + fn convert_doc_comment( + &self, + &token: &usize, + is_inner: bool, + span: Span, + builder: &mut tt::TopSubtreeBuilder, + ) { let text = self.lexed.text(token); - convert_doc_comment(&doc_comment(text), span, self.mode, builder); + convert_doc_comment(&doc_comment(text), is_inner, span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { @@ -752,10 +760,11 @@ where fn convert_doc_comment( &self, token: &Self::Token, + is_inner: bool, span: Span, builder: &mut tt::TopSubtreeBuilder, ) { - convert_doc_comment(token.token(), span, self.mode, builder); + convert_doc_comment(token.token(), is_inner, span, self.mode, builder); } fn bump(&mut self) -> Option<(Self::Token, TextRange)> { diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index 001c920c9b824..c5e4aca95ee1f 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -147,6 +147,14 @@ pub fn prettify_macro_expansion( T![!] if is_last(|it| it == MACRO_RULES_KW, false) && is_next(is_text, false) => { mods.push(do_ws(after, tok)); } + T![,] if tok.parent().is_some_and(|it| it.kind() != MATCH_ARM) => { + if is_next( + |it| !matches!(it, R_BRACK | R_PAREN | R_CURLY | T![,] | WHITESPACE), + false, + ) { + mods.push(do_ws(after, tok)); + } + } _ => (), } @@ -229,8 +237,8 @@ mod tests { macro_rules! foo { () => { $crate::foo::bar!(); - (1..2,1..=2); - (a==b,a!=b,a<=b,a>=b,x+=2,x<<=2); + (1..2, 1..=2); + (a==b, a!=b, a<=b, a>=b, x+=2, x<<=2); }; } "#]], @@ -280,11 +288,11 @@ mod tests { let mut y = 3; let ref mut z@0..5 = 4; let ref mut t@0..=5 = 4; - let (x,ref y) = (5,6); + let (x, ref y) = (5, 6); let (Foo { - x,y - },Bar(z,t)); - let (&mut x,(y|y)); + x, y + }, Bar(z, t)); + let (&mut x, (y|y)); match (){} }; "#]], @@ -324,6 +332,9 @@ mod tests { fn foo() {} struct Foo {} struct Foo; + struct Bar { + x: i32, + } enum Foo {} impl Foo {} const _: () = {}; @@ -341,6 +352,9 @@ mod tests { struct Foo {} struct Foo; + struct Bar { + x: i32, + } enum Foo {} impl Foo {} const _: () = {}; @@ -352,7 +366,7 @@ mod tests { type X = 2; use a; use b::{ - c,d + c, d }; macro_rules! foo { () => {}; @@ -373,6 +387,7 @@ mod tests { let _ = async move {}; let _ = x.await; let _ = (1..2, 1..=2); + let _ = (3,); 'lab: for _ in 0..5 { loop { } break 'lab expr; @@ -394,7 +409,8 @@ mod tests { let _ = async move||{}; let _ = async move {}; let _ = x.await; - let _ = (1..2,1..=2); + let _ = (1..2, 1..=2); + let _ = (3,); 'lab: for _ in 0..5 { loop {} break 'lab expr; diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 7a24b32c87cfe..91387041a1116 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -87,15 +87,15 @@ GenericParam = | TypeParam TypeParam = - Attr* Name (':' TypeBoundList?)? + AnyAttr* Name (':' TypeBoundList?)? ('=' default_type:Type)? ConstParam = - Attr* 'const' Name ':' Type + AnyAttr* 'const' Name ':' Type ('=' default_val:ConstArg)? LifetimeParam = - Attr* Lifetime (':' TypeBoundList?)? + AnyAttr* Lifetime (':' TypeBoundList?)? WhereClause = 'where' predicates:(WherePred (',' WherePred)* ','?) @@ -109,7 +109,7 @@ WherePred = //*************************// MacroCall = - Attr* Path '!' TokenTree ';'? + AnyAttr* Path '!' TokenTree ';'? TokenTree = '(' ')' @@ -123,9 +123,15 @@ MacroStmts = statements:Stmt* Expr? +AnyAttr = + Attr | DocComment + Attr = '#' '!'? '[' Meta ']' +DocComment = + '#inner_doc_comment' | '#outer_doc_comment' + CfgAttrMeta = 'cfg_attr' '(' CfgPredicate ',' (Meta (',' Meta)* ','?) ')' @@ -169,7 +175,7 @@ TokenTreeMeta = SourceFile = '#shebang'? '#frontmatter'? - Attr* + AnyAttr* Item* Item = @@ -192,32 +198,32 @@ Item = | AsmExpr MacroRules = - Attr* Visibility? + AnyAttr* Visibility? 'macro_rules' '!' Name TokenTree MacroDef = - Attr* Visibility? + AnyAttr* Visibility? 'macro' Name args:TokenTree? body:TokenTree Module = - Attr* Visibility? + AnyAttr* Visibility? 'mod' Name (ItemList | ';') ItemList = - '{' Attr* Item* '}' + '{' AnyAttr* Item* '}' ExternCrate = - Attr* Visibility? + AnyAttr* Visibility? 'extern' 'crate' NameRef Rename? ';' Rename = 'as' (Name | '_') Use = - Attr* Visibility? + AnyAttr* Visibility? 'use' UseTree ';' UseTree = @@ -228,7 +234,7 @@ UseTreeList = '{' (UseTree (',' UseTree)* ','?)? '}' Fn = - Attr* Visibility? + AnyAttr* Visibility? 'default'? 'const'? 'async'? 'gen'? 'unsafe'? 'safe'? Abi? 'fn' Name GenericParamList? ParamList RetType? WhereClause? (body:BlockExpr | ';') @@ -244,13 +250,13 @@ ParamList = | '|' (Param (',' Param)* ','?)? '|' SelfParam = - Attr* ( + AnyAttr* ( ('&' Lifetime?)? 'mut'? Name | 'mut'? Name ':' Type ) Param = - Attr* ( + AnyAttr* ( Pat (':' Type)? | Type | '...' @@ -260,13 +266,13 @@ RetType = '->' Type TypeAlias = - Attr* Visibility? + AnyAttr* Visibility? 'default'? 'type' Name GenericParamList? (':' TypeBoundList?)? WhereClause? ('=' Type)? ';' Struct = - Attr* Visibility? + AnyAttr* Visibility? 'struct' Name GenericParamList? ( WhereClause? (RecordFieldList | ';') | TupleFieldList WhereClause? ';' @@ -276,7 +282,7 @@ RecordFieldList = '{' fields:(RecordField (',' RecordField)* ','?)? '}' RecordField = - Attr* Visibility? 'unsafe'? + AnyAttr* Visibility? 'unsafe'? MutRestriction? Name ':' Type ('=' default_val:ConstArg)? @@ -284,7 +290,7 @@ TupleFieldList = '(' fields:(TupleField (',' TupleField)* ','?)? ')' TupleField = - Attr* Visibility? + AnyAttr* Visibility? MutRestriction? Type @@ -296,7 +302,7 @@ MutRestriction = 'mut' VisibilityInner Enum = - Attr* Visibility? + AnyAttr* Visibility? 'enum' Name GenericParamList? WhereClause? VariantList @@ -304,11 +310,11 @@ VariantList = '{' (Variant (',' Variant)* ','?)? '}' Variant = - Attr* Visibility? + AnyAttr* Visibility? (Name | '_') FieldList? ('=' ConstArg)? Union = - Attr* Visibility? + AnyAttr* Visibility? 'union' Name GenericParamList? WhereClause? RecordFieldList @@ -326,7 +332,7 @@ VariantDef = | Variant Const = - Attr* Visibility? + AnyAttr* Visibility? 'default'? 'type'? 'const' (Name | '_') GenericParamList? ':' Type @@ -334,13 +340,13 @@ Const = WhereClause? ';' Static = - Attr* Visibility? + AnyAttr* Visibility? 'unsafe'? 'safe'? 'static' 'mut'? Name ':' Type ('=' body:Expr)? ';' Trait = - Attr* Visibility? + AnyAttr* Visibility? 'unsafe'? 'auto'? ImplRestriction? 'trait' Name GenericParamList? @@ -351,7 +357,7 @@ ImplRestriction = 'impl' VisibilityInner AssocItemList = - '{' Attr* AssocItem* '}' + '{' AnyAttr* AssocItem* '}' AssocItem = Const @@ -360,16 +366,16 @@ AssocItem = | TypeAlias Impl = - Attr* Visibility? + AnyAttr* Visibility? 'default'? 'unsafe'? 'impl' GenericParamList? ('const'? '!'? trait:Type 'for')? self_ty:Type WhereClause? AssocItemList ExternBlock = - Attr* 'unsafe'? Abi ExternItemList + AnyAttr* 'unsafe'? Abi ExternItemList ExternItemList = - '{' Attr* ExternItem* '}' + '{' AnyAttr* ExternItem* '}' ExternItem = Fn @@ -394,7 +400,7 @@ Stmt = | LetStmt LetStmt = - Attr* 'super'? 'let' Pat (':' Type)? + AnyAttr* 'super'? 'let' Pat (':' Type)? '=' initializer:Expr LetElse? ';' @@ -450,13 +456,13 @@ IncludeBytesExpr = 'builtin' '#' 'include_bytes' OffsetOfExpr = - Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' + AnyAttr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' // asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")" // global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" // format_string := STRING_LITERAL / RAW_STRING_LITERAL AsmExpr = - Attr* 'builtin' '#' ( 'asm' | 'global_asm' | 'naked_asm' ) + AnyAttr* 'builtin' '#' ( 'asm' | 'global_asm' | 'naked_asm' ) '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')' // operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_" @@ -468,21 +474,21 @@ AsmRegSpec = '@string' | NameRef // reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr AsmRegOperand = AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr // clobber_abi := "clobber_abi(" *("," ) [","] ")" -AsmClobberAbi = Attr* 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')' +AsmClobberAbi = AnyAttr* 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')' // option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw" AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind' // options := "options(" option *("," option) [","] ")" -AsmOptions = Attr* 'options' '(' (AsmOption (',' AsmOption)*) ','? ')' +AsmOptions = AnyAttr* 'options' '(' (AsmOption (',' AsmOption)*) ','? ')' AsmLabel = 'label' BlockExpr AsmSym = 'sym' Path AsmConst = 'const' Expr // operand := reg_operand / clobber_abi / options AsmOperand = AsmRegOperand | AsmLabel | AsmSym | AsmConst -AsmOperandNamed = Attr* (Name '=')? AsmOperand +AsmOperandNamed = AnyAttr* (Name '=')? AsmOperand AsmPiece = AsmOperandNamed | AsmClobberAbi | AsmOptions FormatArgsExpr = - Attr* 'builtin' '#' 'format_args' '(' + AnyAttr* 'builtin' '#' 'format_args' '(' template:Expr (',' args:(FormatArgsArg (',' FormatArgsArg)* ','?)? )? ')' @@ -494,7 +500,7 @@ MacroExpr = MacroCall Literal = - Attr* value:( + AnyAttr* value:( '@int_number' | '@float_number' | '@string' | '@byte_string' @@ -504,32 +510,32 @@ Literal = ) PathExpr = - Attr* Path + AnyAttr* Path StmtList = '{' - Attr* + AnyAttr* statements:Stmt* tail_expr:Expr? '}' RefExpr = - Attr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr + AnyAttr* '&' (('raw' 'const'?)| ('raw'? 'mut') ) Expr TryExpr = - Attr* Expr '?' + AnyAttr* Expr '?' TryBlockModifier = 'try' ('bikeshed' Type)? BlockExpr = - Attr* Label? (TryBlockModifier | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList + AnyAttr* Label? (TryBlockModifier | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList PrefixExpr = - Attr* op:('-' | '!' | '*') Expr + AnyAttr* op:('-' | '!' | '*') Expr BinExpr = - Attr* + AnyAttr* lhs:Expr op:( '||' | '&&' @@ -540,118 +546,118 @@ BinExpr = rhs:Expr CastExpr = - Attr* Expr 'as' Type + AnyAttr* Expr 'as' Type ParenExpr = - Attr* '(' Attr* Expr ')' + AnyAttr* '(' AnyAttr* Expr ')' ArrayExpr = - Attr* '[' Attr* ( + AnyAttr* '[' AnyAttr* ( (Expr (',' Expr)* ','?)? | Expr ';' Expr ) ']' IndexExpr = - Attr* base:Expr '[' index:Expr ']' + AnyAttr* base:Expr '[' index:Expr ']' TupleExpr = - Attr* '(' Attr* fields:(Expr (',' Expr)* ','?)? ')' + AnyAttr* '(' AnyAttr* fields:(Expr (',' Expr)* ','?)? ')' RecordExpr = Path RecordExprFieldList RecordExprFieldList = '{' - Attr* + AnyAttr* fields:(RecordExprField (',' RecordExprField)* ','?)? ('..' spread:Expr?)? '}' RecordExprField = - Attr* (NameRef ':')? Expr + AnyAttr* (NameRef ':')? Expr CallExpr = - Attr* Expr ArgList + AnyAttr* Expr ArgList ArgList = '(' args:(Expr (',' Expr)* ','?)? ')' MethodCallExpr = - Attr* receiver:Expr '.' NameRef GenericArgList? ArgList + AnyAttr* receiver:Expr '.' NameRef GenericArgList? ArgList FieldExpr = - Attr* Expr '.' NameRef + AnyAttr* Expr '.' NameRef ClosureExpr = - Attr* ForBinder? 'const'? 'static'? 'async'? 'gen'? 'move'? ParamList RetType? + AnyAttr* ForBinder? 'const'? 'static'? 'async'? 'gen'? 'move'? ParamList RetType? body:Expr ForBinder = 'for' GenericParamList IfExpr = - Attr* 'if' condition:Expr then_branch:BlockExpr + AnyAttr* 'if' condition:Expr then_branch:BlockExpr ('else' else_branch:(IfExpr | BlockExpr))? LoopExpr = - Attr* Label? 'loop' + AnyAttr* Label? 'loop' loop_body:BlockExpr ForExpr = - Attr* Label? 'for' Pat 'in' iterable:Expr + AnyAttr* Label? 'for' Pat 'in' iterable:Expr loop_body:BlockExpr WhileExpr = - Attr* Label? 'while' condition:Expr + AnyAttr* Label? 'while' condition:Expr loop_body:BlockExpr Label = Lifetime ':' BreakExpr = - Attr* 'break' Lifetime? Expr? + AnyAttr* 'break' Lifetime? Expr? ContinueExpr = - Attr* 'continue' Lifetime? + AnyAttr* 'continue' Lifetime? RangeExpr = - Attr* start:Expr? op:('..' | '..=') end:Expr? + AnyAttr* start:Expr? op:('..' | '..=') end:Expr? MatchExpr = - Attr* 'match' Expr MatchArmList + AnyAttr* 'match' Expr MatchArmList MatchArmList = '{' - Attr* + AnyAttr* arms:MatchArm* '}' MatchArm = - Attr* Pat guard:MatchGuard? '=>' Expr ','? + AnyAttr* Pat guard:MatchGuard? '=>' Expr ','? MatchGuard = 'if' condition:Expr ReturnExpr = - Attr* 'return' Expr? + AnyAttr* 'return' Expr? BecomeExpr = - Attr* 'become' Expr + AnyAttr* 'become' Expr YieldExpr = - Attr* 'yield' Expr? + AnyAttr* 'yield' Expr? YeetExpr = - Attr* 'do' 'yeet' Expr? + AnyAttr* 'do' 'yeet' Expr? LetExpr = - Attr* 'let' Pat '=' Expr + AnyAttr* 'let' Pat '=' Expr UnderscoreExpr = - Attr* '_' + AnyAttr* '_' AwaitExpr = - Attr* Expr '.' 'await' + AnyAttr* Expr '.' 'await' //*************************// // Types // @@ -768,7 +774,7 @@ LiteralPat = '-'? Literal IdentPat = - Attr* 'ref'? 'mut'? Name ('@' Pat)? + AnyAttr* 'ref'? 'mut'? Name ('@' Pat)? WildcardPat = '_' @@ -794,7 +800,7 @@ RecordPatFieldList = '}' RecordPatField = - Attr* (NameRef ':')? Pat + AnyAttr* (NameRef ':')? Pat TupleStructPat = Path '(' fields:(Pat (',' Pat)* ','?)? ')' @@ -818,7 +824,7 @@ BoxPat = 'box' Pat RestPat = - Attr* '..' + AnyAttr* '..' MacroPat = MacroCall diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs index 855b5a80a5f6d..2f1fad2dc7e31 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs @@ -29,13 +29,11 @@ pub use self::{ TypeOrConstParam, VisibilityKind, }, operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, - token_ext::{ - AnyString, CommentKind, CommentPlacement, CommentShape, IsString, QuoteOffsets, Radix, - }, + token_ext::{AnyComment, AnyString, CommentKind, CommentShape, IsString, QuoteOffsets, Radix}, traits::{ - AttrDocCommentIter, DocCommentIter, HasArgList, HasAttrs, HasDocComments, HasGenericArgs, - HasGenericParams, HasLoopBody, HasModuleItem, HasName, HasTypeBounds, HasVisibility, - attrs_including_inner, + AttrsIter, HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, + HasModuleItem, HasName, HasTypeBounds, HasVisibility, attrs_including_inner, + attrs_with_doc_including_inner, }, }; @@ -170,6 +168,14 @@ mod support { } } +#[cfg(test)] +fn doc_comment_text(owner: impl HasAttrs) -> Option { + use itertools::Itertools; + + let docs = owner.doc_comments().map(|comment| comment.text().to_owned()).join("\n"); + if docs.is_empty() { None } else { Some(docs) } +} + #[test] fn assert_ast_is_dyn_compatible() { fn _f(_: &dyn AstNode, _: &dyn HasName) {} @@ -187,7 +193,7 @@ fn test_doc_comment_none() { .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); - assert!(module.doc_comments().doc_comment_text().is_none()); + assert!(doc_comment_text(module).is_none()); } #[test] @@ -203,7 +209,7 @@ fn test_outer_doc_comment_of_items() { .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); - assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap()); + assert_eq!(" doc", doc_comment_text(module).unwrap()); } #[test] @@ -219,7 +225,7 @@ fn test_inner_doc_comment_of_items() { .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); - assert!(module.doc_comments().doc_comment_text().is_none()); + assert!(doc_comment_text(module).is_none()); } #[test] @@ -234,7 +240,7 @@ fn test_doc_comment_of_statics() { .ok() .unwrap(); let st = file.syntax().descendants().find_map(Static::cast).unwrap(); - assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap()); + assert_eq!(" Number of levels", doc_comment_text(st).unwrap()); } #[test] @@ -256,7 +262,7 @@ fn test_doc_comment_preserves_indents() { let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!( " doc1\n ```\n fn foo() {\n // ...\n }\n ```", - module.doc_comments().doc_comment_text().unwrap() + doc_comment_text(module).unwrap() ); } @@ -275,7 +281,7 @@ fn test_doc_comment_preserves_newlines() { .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); - assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap()); + assert_eq!(" this\n is\n mod\n foo", doc_comment_text(module).unwrap()); } #[test] @@ -290,7 +296,7 @@ fn test_doc_comment_single_line_block_strips_suffix() { .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); - assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap()); + assert_eq!(" this is mod foo", doc_comment_text(module).unwrap()); } #[test] @@ -305,7 +311,7 @@ fn test_doc_comment_single_line_block_strips_suffix_whitespace() { .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); - assert_eq!(" this is mod foo ", module.doc_comments().doc_comment_text().unwrap()); + assert_eq!(" this is mod foo ", doc_comment_text(module).unwrap()); } #[test] @@ -326,7 +332,7 @@ fn test_doc_comment_multi_line_block_strips_suffix() { let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!( "\n this\n is\n mod foo\n ", - module.doc_comments().doc_comment_text().unwrap() + doc_comment_text(module).unwrap() ); } @@ -340,7 +346,7 @@ fn test_comments_preserve_trailing_whitespace() { let def = file.syntax().descendants().find_map(Struct::cast).unwrap(); assert_eq!( " Representation of a Realm. \n In the specification these are called Realm Records.", - def.doc_comments().doc_comment_text().unwrap() + doc_comment_text(def).unwrap() ); } @@ -357,7 +363,7 @@ fn test_four_slash_line_comment() { .ok() .unwrap(); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); - assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap()); + assert_eq!(" doc comment", doc_comment_text(module).unwrap()); } #[test] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs index 852b13fc7a3d2..e34cfa7460735 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit.rs @@ -1,7 +1,7 @@ //! This module contains functions for editing syntax trees. As the trees are //! immutable, all function here return a fresh copy of the tree, instead of //! doing an in-place modification. -use parser::T; +use parser::{SyntaxKind::DOC_COMMENT, T}; use std::{ fmt, iter::{self, once}, @@ -165,7 +165,7 @@ pub trait AttrsOwnerEdit: ast::HasAttrs { let mut remove_next_ws = false; for child in self.syntax().children_with_tokens() { match child.kind() { - ATTR | COMMENT => { + ATTR | COMMENT | DOC_COMMENT => { remove_next_ws = true; editor.delete(child); continue; diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs index b44150f86842c..ced9163f661af 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/expr_ext.rs @@ -12,6 +12,7 @@ use crate::{ operators::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp}, support, }, + match_ast, }; use super::RangeItem; @@ -56,11 +57,11 @@ impl AstNode for ElseBranch { } fn cast(syntax: SyntaxNode) -> Option { - if let Some(block_expr) = ast::BlockExpr::cast(syntax.clone()) { - Some(Self::Block(block_expr)) - } else { - ast::IfExpr::cast(syntax).map(Self::IfExpr) - } + match_ast!(match syntax { + ast::BlockExpr(it) => Some(Self::Block(it)), + ast::IfExpr(it) => Some(Self::IfExpr(it)), + _ => None, + }) } fn syntax(&self) -> &SyntaxNode { @@ -342,33 +343,20 @@ impl ast::Literal { pub fn kind(&self) -> LiteralKind { let token = self.token(); - if let Some(t) = ast::IntNumber::cast(token.clone()) { - return LiteralKind::IntNumber(t); - } - if let Some(t) = ast::FloatNumber::cast(token.clone()) { - return LiteralKind::FloatNumber(t); - } - if let Some(t) = ast::String::cast(token.clone()) { - return LiteralKind::String(t); - } - if let Some(t) = ast::ByteString::cast(token.clone()) { - return LiteralKind::ByteString(t); - } - if let Some(t) = ast::CString::cast(token.clone()) { - return LiteralKind::CString(t); - } - if let Some(t) = ast::Char::cast(token.clone()) { - return LiteralKind::Char(t); - } - if let Some(t) = ast::Byte::cast(token.clone()) { - return LiteralKind::Byte(t); - } - - match token.kind() { - T![true] => LiteralKind::Bool(true), - T![false] => LiteralKind::Bool(false), - _ => unreachable!(), - } + match_ast!(match token { + ast::IntNumber(t) => LiteralKind::IntNumber(t), + ast::FloatNumber(t) => LiteralKind::FloatNumber(t), + ast::String(t) => LiteralKind::String(t), + ast::ByteString(t) => LiteralKind::ByteString(t), + ast::CString(t) => LiteralKind::CString(t), + ast::Char(t) => LiteralKind::Char(t), + ast::Byte(t) => LiteralKind::Byte(t), + _ => match token.kind() { + T![true] => LiteralKind::Bool(true), + T![false] => LiteralKind::Bool(false), + _ => unreachable!(), + }, + }) } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index 5fa56cf33c5d8..ad52d82787278 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -468,7 +468,6 @@ pub struct Const { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Const {} -impl ast::HasDocComments for Const {} impl ast::HasGenericParams for Const {} impl ast::HasName for Const {} impl ast::HasVisibility for Const {} @@ -552,6 +551,19 @@ impl DerefPat { #[inline] pub fn deref_token(&self) -> Option { support::token(&self.syntax, T![deref]) } } +pub struct DocComment { + pub(crate) syntax: SyntaxNode, +} +impl DocComment { + #[inline] + pub fn inner_doc_comment_token(&self) -> Option { + support::token(&self.syntax, T![inner_doc_comment]) + } + #[inline] + pub fn outer_doc_comment_token(&self) -> Option { + support::token(&self.syntax, T![outer_doc_comment]) + } +} pub struct DynTraitType { pub(crate) syntax: SyntaxNode, } @@ -565,7 +577,6 @@ pub struct Enum { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Enum {} -impl ast::HasDocComments for Enum {} impl ast::HasGenericParams for Enum {} impl ast::HasName for Enum {} impl ast::HasVisibility for Enum {} @@ -588,7 +599,6 @@ pub struct ExternBlock { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for ExternBlock {} -impl ast::HasDocComments for ExternBlock {} impl ExternBlock { #[inline] pub fn abi(&self) -> Option { support::child(&self.syntax) } @@ -601,7 +611,6 @@ pub struct ExternCrate { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for ExternCrate {} -impl ast::HasDocComments for ExternCrate {} impl ast::HasVisibility for ExternCrate {} impl ExternCrate { #[inline] @@ -643,7 +652,6 @@ pub struct Fn { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Fn {} -impl ast::HasDocComments for Fn {} impl ast::HasGenericParams for Fn {} impl ast::HasName for Fn {} impl ast::HasVisibility for Fn {} @@ -805,7 +813,6 @@ pub struct Impl { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Impl {} -impl ast::HasDocComments for Impl {} impl ast::HasGenericParams for Impl {} impl ast::HasVisibility for Impl {} impl Impl { @@ -1002,7 +1009,6 @@ pub struct MacroCall { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for MacroCall {} -impl ast::HasDocComments for MacroCall {} impl MacroCall { #[inline] pub fn path(&self) -> Option { support::child(&self.syntax) } @@ -1017,7 +1023,6 @@ pub struct MacroDef { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for MacroDef {} -impl ast::HasDocComments for MacroDef {} impl ast::HasName for MacroDef {} impl ast::HasVisibility for MacroDef {} impl MacroDef { @@ -1047,7 +1052,6 @@ pub struct MacroRules { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for MacroRules {} -impl ast::HasDocComments for MacroRules {} impl ast::HasName for MacroRules {} impl ast::HasVisibility for MacroRules {} impl MacroRules { @@ -1141,7 +1145,6 @@ pub struct Module { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Module {} -impl ast::HasDocComments for Module {} impl ast::HasName for Module {} impl ast::HasVisibility for Module {} impl Module { @@ -1472,7 +1475,6 @@ pub struct RecordField { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for RecordField {} -impl ast::HasDocComments for RecordField {} impl ast::HasName for RecordField {} impl ast::HasVisibility for RecordField {} impl RecordField { @@ -1667,7 +1669,6 @@ pub struct SourceFile { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for SourceFile {} -impl ast::HasDocComments for SourceFile {} impl ast::HasModuleItem for SourceFile {} impl SourceFile { #[inline] @@ -1681,7 +1682,6 @@ pub struct Static { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Static {} -impl ast::HasDocComments for Static {} impl ast::HasName for Static {} impl ast::HasVisibility for Static {} impl Static { @@ -1720,7 +1720,6 @@ pub struct Struct { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Struct {} -impl ast::HasDocComments for Struct {} impl ast::HasGenericParams for Struct {} impl ast::HasName for Struct {} impl ast::HasVisibility for Struct {} @@ -1762,7 +1761,6 @@ pub struct Trait { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Trait {} -impl ast::HasDocComments for Trait {} impl ast::HasGenericParams for Trait {} impl ast::HasName for Trait {} impl ast::HasTypeBounds for Trait {} @@ -1822,7 +1820,6 @@ pub struct TupleField { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for TupleField {} -impl ast::HasDocComments for TupleField {} impl ast::HasVisibility for TupleField {} impl TupleField { #[inline] @@ -1880,7 +1877,6 @@ pub struct TypeAlias { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for TypeAlias {} -impl ast::HasDocComments for TypeAlias {} impl ast::HasGenericParams for TypeAlias {} impl ast::HasName for TypeAlias {} impl ast::HasTypeBounds for TypeAlias {} @@ -1979,7 +1975,6 @@ pub struct Union { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Union {} -impl ast::HasDocComments for Union {} impl ast::HasGenericParams for Union {} impl ast::HasName for Union {} impl ast::HasVisibility for Union {} @@ -2006,7 +2001,6 @@ pub struct Use { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Use {} -impl ast::HasDocComments for Use {} impl ast::HasVisibility for Use {} impl Use { #[inline] @@ -2059,7 +2053,6 @@ pub struct Variant { pub(crate) syntax: SyntaxNode, } impl ast::HasAttrs for Variant {} -impl ast::HasDocComments for Variant {} impl ast::HasName for Variant {} impl ast::HasVisibility for Variant {} impl Variant { @@ -2171,11 +2164,16 @@ pub enum Adt { Union(Union), } impl ast::HasAttrs for Adt {} -impl ast::HasDocComments for Adt {} impl ast::HasGenericParams for Adt {} impl ast::HasName for Adt {} impl ast::HasVisibility for Adt {} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AnyAttr { + Attr(Attr), + DocComment(DocComment), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AsmOperand { AsmConst(AsmConst), @@ -2200,7 +2198,6 @@ pub enum AssocItem { TypeAlias(TypeAlias), } impl ast::HasAttrs for AssocItem {} -impl ast::HasDocComments for AssocItem {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum CfgPredicate { @@ -2257,7 +2254,6 @@ pub enum ExternItem { TypeAlias(TypeAlias), } impl ast::HasAttrs for ExternItem {} -impl ast::HasDocComments for ExternItem {} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum FieldList { @@ -2374,7 +2370,6 @@ pub enum VariantDef { Variant(Variant), } impl ast::HasAttrs for VariantDef {} -impl ast::HasDocComments for VariantDef {} impl ast::HasName for VariantDef {} impl ast::HasVisibility for VariantDef {} pub struct AnyHasArgList { @@ -2395,15 +2390,6 @@ impl AnyHasAttrs { AnyHasAttrs { syntax: node.syntax().clone() } } } -pub struct AnyHasDocComments { - pub(crate) syntax: SyntaxNode, -} -impl AnyHasDocComments { - #[inline] - pub fn new(node: T) -> AnyHasDocComments { - AnyHasDocComments { syntax: node.syntax().clone() } - } -} pub struct AnyHasGenericArgs { pub(crate) syntax: SyntaxNode, } @@ -3683,6 +3669,38 @@ impl fmt::Debug for DerefPat { f.debug_struct("DerefPat").field("syntax", &self.syntax).finish() } } +impl AstNode for DocComment { + #[inline] + fn kind() -> SyntaxKind + where + Self: Sized, + { + DOC_COMMENT + } + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == DOC_COMMENT } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl hash::Hash for DocComment { + fn hash(&self, state: &mut H) { self.syntax.hash(state); } +} +impl Eq for DocComment {} +impl PartialEq for DocComment { + fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } +} +impl Clone for DocComment { + fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } +} +impl fmt::Debug for DocComment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("DocComment").field("syntax", &self.syntax).finish() + } +} impl AstNode for DynTraitType { #[inline] fn kind() -> SyntaxKind @@ -7909,6 +7927,34 @@ impl AstNode for Adt { } } } +impl From for AnyAttr { + #[inline] + fn from(node: Attr) -> AnyAttr { AnyAttr::Attr(node) } +} +impl From for AnyAttr { + #[inline] + fn from(node: DocComment) -> AnyAttr { AnyAttr::DocComment(node) } +} +impl AstNode for AnyAttr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { matches!(kind, ATTR | DOC_COMMENT) } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + ATTR => AnyAttr::Attr(Attr { syntax }), + DOC_COMMENT => AnyAttr::DocComment(DocComment { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AnyAttr::Attr(it) => &it.syntax, + AnyAttr::DocComment(it) => &it.syntax, + } + } +} impl From for AsmOperand { #[inline] fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) } @@ -9455,136 +9501,6 @@ impl From for AnyHasAttrs { #[inline] fn from(node: YieldExpr) -> AnyHasAttrs { AnyHasAttrs { syntax: node.syntax } } } -impl ast::HasDocComments for AnyHasDocComments {} -impl AstNode for AnyHasDocComments { - #[inline] - fn can_cast(kind: SyntaxKind) -> bool { - matches!( - kind, - CONST - | ENUM - | EXTERN_BLOCK - | EXTERN_CRATE - | FN - | IMPL - | MACRO_CALL - | MACRO_DEF - | MACRO_RULES - | MODULE - | RECORD_FIELD - | SOURCE_FILE - | STATIC - | STRUCT - | TRAIT - | TUPLE_FIELD - | TYPE_ALIAS - | UNION - | USE - | VARIANT - ) - } - #[inline] - fn cast(syntax: SyntaxNode) -> Option { - Self::can_cast(syntax.kind()).then_some(AnyHasDocComments { syntax }) - } - #[inline] - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} -impl hash::Hash for AnyHasDocComments { - fn hash(&self, state: &mut H) { self.syntax.hash(state); } -} -impl Eq for AnyHasDocComments {} -impl PartialEq for AnyHasDocComments { - fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } -} -impl Clone for AnyHasDocComments { - fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } -} -impl fmt::Debug for AnyHasDocComments { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AnyHasDocComments").field("syntax", &self.syntax).finish() - } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Const) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Enum) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: ExternBlock) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: ExternCrate) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Fn) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Impl) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: MacroCall) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: MacroDef) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: MacroRules) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Module) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: RecordField) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: SourceFile) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Static) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Struct) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Trait) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: TupleField) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: TypeAlias) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Union) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Use) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} -impl From for AnyHasDocComments { - #[inline] - fn from(node: Variant) -> AnyHasDocComments { AnyHasDocComments { syntax: node.syntax } } -} impl ast::HasGenericArgs for AnyHasGenericArgs {} impl AstNode for AnyHasGenericArgs { #[inline] @@ -10066,6 +9982,11 @@ impl std::fmt::Display for Adt { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AnyAttr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AsmOperand { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -10336,6 +10257,11 @@ impl std::fmt::Display for DerefPat { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for DocComment { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for DynTraitType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs index 672e2fd233e48..eef314175ae1c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/node_ext.rs @@ -7,14 +7,14 @@ use std::{fmt, iter::successors}; use itertools::Itertools; use parser::SyntaxKind; -use rowan::{GreenNodeData, GreenTokenData}; +use rowan::{GreenNodeData, GreenTokenData, TextSize}; use smallvec::{SmallVec, smallvec}; use crate::{ NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, ast::{ - self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, - HasTypeBounds, SyntaxNode, support, + self, AnyComment, AstNode, AstToken, CommentShape, HasAttrs, HasGenericArgs, + HasGenericParams, HasName, HasTypeBounds, SyntaxNode, support, }, syntax_editor::SyntaxEditor, }; @@ -271,6 +271,55 @@ impl ast::Attr { } } +impl ast::DocComment { + // `///` or `/**` or `//!` or `/*!`, all are 3 chars. + pub const PREFIX_LEN: TextSize = TextSize::new(3); + + pub fn kind(&self) -> AttrKind { + match self.inner_doc_comment_token() { + Some(_) => AttrKind::Inner, + None => AttrKind::Outer, + } + } + + pub fn token(&self) -> AnyComment { + self.syntax + .first_token() + .and_then(ast::AnyComment::cast) + .expect("`ast::DocComment` must have a comment token") + } + + pub fn shape(&self) -> CommentShape { + CommentShape::from_text(self.text_with_markers()) + } + + /// Returns the text with the `/**...*/` or `/*!...*/` or `///...` or `//!...` markers. + pub fn text_with_markers(&self) -> &str { + text_of_first_token(&self.syntax) + } + + /// Returns the textual content of a doc comment node as a single string with prefix and suffix removed. + pub fn text(&self) -> &str { + let shape = self.shape(); + let text = &self.text_with_markers()[Self::PREFIX_LEN.into()..]; + if shape == CommentShape::Block { + // The `*/` may not exist because of recovery. + text.strip_suffix("*/").unwrap_or(text) + } else { + text + } + } +} + +impl ast::AnyAttr { + pub fn kind(&self) -> AttrKind { + match self { + ast::AnyAttr::Attr(it) => it.kind(), + ast::AnyAttr::DocComment(it) => it.kind(), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq)] pub enum PathSegmentKind { Name(ast::NameRef), @@ -1129,8 +1178,6 @@ impl ast::HasLoopBody for ast::WhileExpr { } } -impl ast::HasAttrs for ast::AnyHasDocComments {} - impl From for ast::Item { fn from(it: ast::Adt) -> Self { match it { diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs index bb0f53db24739..07240265da193 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/token_ext.rs @@ -3,6 +3,7 @@ use std::ops::Range; use std::{borrow::Cow, num::ParseIntError}; +use parser::SyntaxKind; use rustc_literal_escaper::{ EscapeError, MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, @@ -10,47 +11,23 @@ use rustc_literal_escaper::{ use stdx::always; use crate::{ - TextRange, TextSize, - ast::{self, AstToken}, + SyntaxToken, TextRange, TextSize, + ast::{self, AstToken, AttrKind}, }; impl ast::Comment { - pub fn kind(&self) -> CommentKind { - CommentKind::from_text(self.text()) - } - - pub fn is_doc(&self) -> bool { - self.kind().doc.is_some() - } - - pub fn is_inner(&self) -> bool { - self.kind().doc == Some(CommentPlacement::Inner) - } - - pub fn is_outer(&self) -> bool { - self.kind().doc == Some(CommentPlacement::Outer) - } - - pub fn prefix(&self) -> &'static str { - self.kind().prefix() + pub fn shape(&self) -> CommentShape { + CommentShape::from_text(self.text()) } - /// Returns the textual content of a doc comment node as a single string with prefix and suffix - /// removed, plus the offset of the returned string from the beginning of the comment. - pub fn doc_comment(&self) -> Option<(&str, TextSize)> { - let kind = self.kind(); - match kind { - CommentKind { shape, doc: Some(_) } => { - let prefix = kind.prefix(); - let text = &self.text()[prefix.len()..]; - let text = if shape == CommentShape::Block { - text.strip_suffix("*/").unwrap_or(text) - } else { - text - }; - Some((text, TextSize::of(prefix))) - } - _ => None, + /// Returns the text without the `//` or `/*...*/` markers. + pub fn text_without_markers(&self) -> &str { + let text = self.text(); + let shape = CommentShape::from_text(text); + let text = &text[2..]; + match shape { + CommentShape::Block => text.strip_suffix("*/").unwrap_or(text), + CommentShape::Line => text, } } } @@ -58,7 +35,20 @@ impl ast::Comment { #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct CommentKind { pub shape: CommentShape, - pub doc: Option, + pub doc: Option, +} + +impl CommentKind { + pub fn prefix(&self) -> &'static str { + match (self.shape, self.doc) { + (CommentShape::Line, None) => "//", + (CommentShape::Line, Some(AttrKind::Inner)) => "//!", + (CommentShape::Line, Some(AttrKind::Outer)) => "///", + (CommentShape::Block, None) => "/*", + (CommentShape::Block, Some(AttrKind::Inner)) => "/*!", + (CommentShape::Block, Some(AttrKind::Outer)) => "/**", + } + } } #[derive(Debug, PartialEq, Eq, Clone, Copy)] @@ -68,6 +58,11 @@ pub enum CommentShape { } impl CommentShape { + #[inline] + pub fn from_text(text: &str) -> CommentShape { + if text.starts_with("/*") { CommentShape::Block } else { CommentShape::Line } + } + pub fn is_line(self) -> bool { self == CommentShape::Line } @@ -77,37 +72,80 @@ impl CommentShape { } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum CommentPlacement { - Inner, - Outer, +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AnyComment { + syntax: SyntaxToken, } -impl CommentKind { - const BY_PREFIX: [(&'static str, CommentKind); 9] = [ - ("/**/", CommentKind { shape: CommentShape::Block, doc: None }), - ("/***", CommentKind { shape: CommentShape::Block, doc: None }), - ("////", CommentKind { shape: CommentShape::Line, doc: None }), - ("///", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Outer) }), - ("//!", CommentKind { shape: CommentShape::Line, doc: Some(CommentPlacement::Inner) }), - ("/**", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Outer) }), - ("/*!", CommentKind { shape: CommentShape::Block, doc: Some(CommentPlacement::Inner) }), - ("//", CommentKind { shape: CommentShape::Line, doc: None }), - ("/*", CommentKind { shape: CommentShape::Block, doc: None }), - ]; - - pub(crate) fn from_text(text: &str) -> CommentKind { - let &(_prefix, kind) = CommentKind::BY_PREFIX - .iter() - .find(|&(prefix, _kind)| text.starts_with(prefix)) - .unwrap(); - kind +impl AstToken for AnyComment { + fn can_cast(kind: SyntaxKind) -> bool + where + Self: Sized, + { + matches!( + kind, + SyntaxKind::COMMENT | SyntaxKind::INNER_DOC_COMMENT | SyntaxKind::OUTER_DOC_COMMENT + ) + } + + fn cast(syntax: SyntaxToken) -> Option + where + Self: Sized, + { + if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } + } + + fn syntax(&self) -> &SyntaxToken { + &self.syntax + } +} + +impl AnyComment { + pub fn shape(&self) -> CommentShape { + CommentShape::from_text(self.text_with_markers()) + } + + pub fn doc_kind(&self) -> Option { + match self.syntax.kind() { + SyntaxKind::COMMENT => None, + SyntaxKind::INNER_DOC_COMMENT => Some(AttrKind::Inner), + SyntaxKind::OUTER_DOC_COMMENT => Some(AttrKind::Outer), + _ => unreachable!(), + } + } + + pub fn kind(&self) -> CommentKind { + CommentKind { shape: self.shape(), doc: self.doc_kind() } } pub fn prefix(&self) -> &'static str { - let &(prefix, _) = - CommentKind::BY_PREFIX.iter().rev().find(|(_, kind)| kind == self).unwrap(); - prefix + self.kind().prefix() + } + + pub fn is_inner(&self) -> bool { + self.doc_kind() == Some(AttrKind::Inner) + } + + pub fn is_outer(&self) -> bool { + self.doc_kind() == Some(AttrKind::Outer) + } + + /// Returns the text with the `/*...*/` or `//...` or `/**...*/` or `/*!...*/` or `///...` or `//!...` markers. + pub fn text_with_markers(&self) -> &str { + self.syntax.text() + } + + /// Returns the textual content of a doc comment node as a single string with prefix and suffix removed. + pub fn text(&self) -> &str { + let shape = self.shape(); + let prefix_len = if self.doc_kind().is_some() { 3 } else { 2 }; + let text = &self.text_with_markers()[prefix_len..]; + if shape == CommentShape::Block { + // The `*/` may not exist because of recovery. + text.strip_suffix("*/").unwrap_or(text) + } else { + text + } } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs index 6fe5abb84e22b..59e1835b02d60 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs @@ -4,10 +4,9 @@ use either::Either; use crate::{ - SyntaxElement, SyntaxNode, SyntaxToken, T, - ast::{self, AstChildren, AstNode, AstToken, support}, + SyntaxNode, SyntaxToken, T, + ast::{self, AstChildren, AstNode, support}, match_ast, - syntax_node::SyntaxElementChildren, }; pub trait HasName: AstNode { @@ -74,6 +73,14 @@ pub trait HasAttrs: AstNode { support::children(self.syntax()) } + fn doc_comments(&self) -> AstChildren { + support::children(self.syntax()) + } + + fn attrs_with_doc(&self) -> AstChildren { + support::children(self.syntax()) + } + /// This may return the same node as called with (with `SourceFile`). The caller has the responsibility /// to avoid duplicate attributes. fn inner_attributes_node(&self) -> Option { @@ -102,68 +109,42 @@ pub trait HasAttrs: AstNode { /// Returns all attributes of this node, including inner attributes that may not be directly under this node /// but under a child. -pub fn attrs_including_inner(owner: &dyn HasAttrs) -> impl Iterator + Clone { - owner.attrs().filter(|attr| attr.kind().is_outer()).chain( +pub fn attrs_with_doc_including_inner( + owner: &dyn HasAttrs, +) -> impl Iterator + Clone { + owner.attrs_with_doc().filter(|attr| attr.kind().is_outer()).chain( owner .inner_attributes_node() .into_iter() - .flat_map(|node| support::children::(&node)) + .flat_map(|node| support::children::(&node)) .filter(|attr| attr.kind().is_inner()), ) } -pub trait HasDocComments: HasAttrs { - fn doc_comments(&self) -> DocCommentIter { - DocCommentIter { iter: self.syntax().children_with_tokens() } - } -} - -impl DocCommentIter { - pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> DocCommentIter { - DocCommentIter { iter: syntax_node.children_with_tokens() } - } - - #[cfg(test)] - pub fn doc_comment_text(self) -> Option { - let docs = itertools::Itertools::join( - &mut self.filter_map(|comment| comment.doc_comment().map(|it| it.0.to_owned())), - "\n", - ); - if docs.is_empty() { None } else { Some(docs) } - } +pub fn attrs_including_inner(owner: &dyn HasAttrs) -> impl Iterator + Clone { + AttrsIter::new(attrs_with_doc_including_inner(owner)) } -pub struct DocCommentIter { - iter: SyntaxElementChildren, +#[derive(Clone)] +pub struct AttrsIter { + inner: I, } -impl Iterator for DocCommentIter { - type Item = ast::Comment; - fn next(&mut self) -> Option { - self.iter.by_ref().find_map(|el| { - el.into_token().and_then(ast::Comment::cast).filter(ast::Comment::is_doc) - }) +impl> AttrsIter { + #[inline] + pub fn new(inner: I) -> Self { + Self { inner } } } -pub struct AttrDocCommentIter { - iter: SyntaxElementChildren, -} +impl> Iterator for AttrsIter { + type Item = ast::Attr; -impl AttrDocCommentIter { - pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> AttrDocCommentIter { - AttrDocCommentIter { iter: syntax_node.children_with_tokens() } - } -} - -impl Iterator for AttrDocCommentIter { - type Item = Either; + #[inline] fn next(&mut self) -> Option { - self.iter.find_map(|el| match el { - SyntaxElement::Node(node) => ast::Attr::cast(node).map(Either::Left), - SyntaxElement::Token(tok) => { - ast::Comment::cast(tok).filter(ast::Comment::is_doc).map(Either::Right) - } + self.inner.find_map(|attr| match attr { + ast::AnyAttr::Attr(it) => Some(it), + ast::AnyAttr::DocComment(_) => None, }) } } diff --git a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs index 5f193f01bc73b..df1d6a1713d19 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/parsing/reparsing.rs @@ -384,14 +384,6 @@ fn baz $0$0 () {} " \t\t\n\n", 2, ); - do_check( - r" -/// foo $0$0omment -mod { } -", - "c", - 14, - ); do_check( r#" fn -> &str { "Hello$0$0" } diff --git a/src/tools/rust-analyzer/crates/syntax/src/validation/block.rs b/src/tools/rust-analyzer/crates/syntax/src/validation/block.rs index a140153884281..00d87ba0e6876 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/validation/block.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/validation/block.rs @@ -9,7 +9,8 @@ use crate::{ pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec) { if let Some(parent) = block.syntax().parent() { match parent.kind() { - FN | EXPR_STMT | STMT_LIST | MACRO_STMTS | LOOP_EXPR | WHILE_EXPR | FOR_EXPR => { + FN | EXPR_STMT | STMT_LIST | MACRO_STMTS | LOOP_EXPR | WHILE_EXPR | FOR_EXPR + | TUPLE_EXPR | ARRAY_EXPR | ARG_LIST => { return; } _ => {} diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast index f56af1a5c05b4..c75c1362116bd 100644 --- a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast @@ -1,5 +1,5 @@ -SOURCE_FILE@0..611 - FN@0..610 +SOURCE_FILE@0..1186 + FN@0..1185 FN_KW@0..2 "fn" WHITESPACE@2..3 " " NAME@3..8 @@ -8,8 +8,8 @@ SOURCE_FILE@0..611 L_PAREN@8..9 "(" R_PAREN@9..10 ")" WHITESPACE@10..11 " " - BLOCK_EXPR@11..610 - STMT_LIST@11..610 + BLOCK_EXPR@11..1185 + STMT_LIST@11..1185 L_CURLY@11..12 "{" WHITESPACE@12..17 "\n " LET_STMT@17..129 @@ -40,7 +40,8 @@ SOURCE_FILE@0..611 R_PAREN@81..82 ")" R_BRACK@82..83 "]" WHITESPACE@83..92 "\n " - COMMENT@92..122 "//! Nor are ModuleDoc ..." + DOC_COMMENT@92..122 + INNER_DOC_COMMENT@92..122 "//! Nor are ModuleDoc ..." WHITESPACE@122..127 "\n " R_CURLY@127..128 "}" SEMICOLON@128..129 ";" @@ -86,7 +87,8 @@ SOURCE_FILE@0..611 R_PAREN@210..211 ")" R_BRACK@211..212 "]" WHITESPACE@212..221 "\n " - COMMENT@221..251 "//! Nor are ModuleDoc ..." + DOC_COMMENT@221..251 + INNER_DOC_COMMENT@221..251 "//! Nor are ModuleDoc ..." WHITESPACE@251..256 "\n " R_CURLY@256..257 "}" WHITESPACE@257..262 "\n " @@ -116,7 +118,8 @@ SOURCE_FILE@0..611 R_PAREN@300..301 ")" R_BRACK@301..302 "]" WHITESPACE@302..311 "\n " - COMMENT@311..341 "//! Nor are ModuleDoc ..." + DOC_COMMENT@311..341 + INNER_DOC_COMMENT@311..341 "//! Nor are ModuleDoc ..." WHITESPACE@341..346 "\n " R_CURLY@346..347 "}" WHITESPACE@347..353 "\n " @@ -143,50 +146,206 @@ SOURCE_FILE@0..611 R_PAREN@428..429 ")" R_BRACK@429..430 "]" WHITESPACE@430..439 "\n " - COMMENT@439..468 "//! So are ModuleDoc ..." + DOC_COMMENT@439..468 + INNER_DOC_COMMENT@439..468 "//! So are ModuleDoc ..." WHITESPACE@468..473 "\n " R_CURLY@473..474 "}" WHITESPACE@474..479 "\n " - FOR_EXPR@479..608 - FOR_KW@479..482 "for" - WHITESPACE@482..483 " " - WILDCARD_PAT@483..484 - UNDERSCORE@483..484 "_" - WHITESPACE@484..485 " " - IN_KW@485..487 "in" - WHITESPACE@487..488 " " - RANGE_EXPR@488..492 - LITERAL@488..489 - INT_NUMBER@488..489 "0" - DOT2@489..491 ".." - LITERAL@491..492 - INT_NUMBER@491..492 "1" - WHITESPACE@492..493 " " - BLOCK_EXPR@493..608 - STMT_LIST@493..608 - L_CURLY@493..494 "{" - WHITESPACE@494..503 "\n " - ATTR@503..564 - POUND@503..504 "#" - BANG@504..505 "!" - L_BRACK@505..506 "[" - TOKEN_TREE_META@506..563 - PATH@506..509 - PATH_SEGMENT@506..509 - NAME_REF@506..509 - IDENT@506..509 "doc" - TOKEN_TREE@509..563 - L_PAREN@509..510 "(" - STRING@510..562 "\"This is fine, `for` ..." - R_PAREN@562..563 ")" - R_BRACK@563..564 "]" - WHITESPACE@564..573 "\n " - COMMENT@573..602 "//! So are ModuleDoc ..." - WHITESPACE@602..607 "\n " - R_CURLY@607..608 "}" - WHITESPACE@608..609 "\n" - R_CURLY@609..610 "}" - WHITESPACE@610..611 "\n" + EXPR_STMT@479..608 + FOR_EXPR@479..608 + FOR_KW@479..482 "for" + WHITESPACE@482..483 " " + WILDCARD_PAT@483..484 + UNDERSCORE@483..484 "_" + WHITESPACE@484..485 " " + IN_KW@485..487 "in" + WHITESPACE@487..488 " " + RANGE_EXPR@488..492 + LITERAL@488..489 + INT_NUMBER@488..489 "0" + DOT2@489..491 ".." + LITERAL@491..492 + INT_NUMBER@491..492 "1" + WHITESPACE@492..493 " " + BLOCK_EXPR@493..608 + STMT_LIST@493..608 + L_CURLY@493..494 "{" + WHITESPACE@494..503 "\n " + ATTR@503..564 + POUND@503..504 "#" + BANG@504..505 "!" + L_BRACK@505..506 "[" + TOKEN_TREE_META@506..563 + PATH@506..509 + PATH_SEGMENT@506..509 + NAME_REF@506..509 + IDENT@506..509 "doc" + TOKEN_TREE@509..563 + L_PAREN@509..510 "(" + STRING@510..562 "\"This is fine, `for` ..." + R_PAREN@562..563 ")" + R_BRACK@563..564 "]" + WHITESPACE@564..573 "\n " + DOC_COMMENT@573..602 + INNER_DOC_COMMENT@573..602 "//! So are ModuleDoc ..." + WHITESPACE@602..607 "\n " + R_CURLY@607..608 "}" + WHITESPACE@608..613 "\n " + LET_STMT@613..768 + LET_KW@613..616 "let" + WHITESPACE@616..617 " " + IDENT_PAT@617..618 + NAME@617..618 + IDENT@617..618 "t" + WHITESPACE@618..619 " " + EQ@619..620 "=" + WHITESPACE@620..621 " " + TUPLE_EXPR@621..767 + L_PAREN@621..622 "(" + WHITESPACE@622..631 "\n " + BLOCK_EXPR@631..760 + STMT_LIST@631..760 + L_CURLY@631..632 "{" + WHITESPACE@632..645 "\n " + ATTR@645..708 + POUND@645..646 "#" + BANG@646..647 "!" + L_BRACK@647..648 "[" + TOKEN_TREE_META@648..707 + PATH@648..651 + PATH_SEGMENT@648..651 + NAME_REF@648..651 + IDENT@648..651 "doc" + TOKEN_TREE@651..707 + L_PAREN@651..652 "(" + STRING@652..706 "\"This is fine, tuple ..." + R_PAREN@706..707 ")" + R_BRACK@707..708 "]" + WHITESPACE@708..721 "\n " + DOC_COMMENT@721..750 + INNER_DOC_COMMENT@721..750 "//! So are ModuleDoc ..." + WHITESPACE@750..759 "\n " + R_CURLY@759..760 "}" + COMMA@760..761 "," + WHITESPACE@761..766 "\n " + R_PAREN@766..767 ")" + SEMICOLON@767..768 ";" + WHITESPACE@768..773 "\n " + LET_STMT@773..928 + LET_KW@773..776 "let" + WHITESPACE@776..777 " " + IDENT_PAT@777..778 + NAME@777..778 + IDENT@777..778 "a" + WHITESPACE@778..779 " " + EQ@779..780 "=" + WHITESPACE@780..781 " " + ARRAY_EXPR@781..927 + L_BRACK@781..782 "[" + WHITESPACE@782..791 "\n " + BLOCK_EXPR@791..920 + STMT_LIST@791..920 + L_CURLY@791..792 "{" + WHITESPACE@792..805 "\n " + ATTR@805..868 + POUND@805..806 "#" + BANG@806..807 "!" + L_BRACK@807..808 "[" + TOKEN_TREE_META@808..867 + PATH@808..811 + PATH_SEGMENT@808..811 + NAME_REF@808..811 + IDENT@808..811 "doc" + TOKEN_TREE@811..867 + L_PAREN@811..812 "(" + STRING@812..866 "\"This is fine, array ..." + R_PAREN@866..867 ")" + R_BRACK@867..868 "]" + WHITESPACE@868..881 "\n " + DOC_COMMENT@881..910 + INNER_DOC_COMMENT@881..910 "//! So are ModuleDoc ..." + WHITESPACE@910..919 "\n " + R_CURLY@919..920 "}" + COMMA@920..921 "," + WHITESPACE@921..926 "\n " + R_BRACK@926..927 "]" + SEMICOLON@927..928 ";" + WHITESPACE@928..933 "\n " + EXPR_STMT@933..1054 + CALL_EXPR@933..1053 + PATH_EXPR@933..934 + PATH@933..934 + PATH_SEGMENT@933..934 + NAME_REF@933..934 + IDENT@933..934 "g" + ARG_LIST@934..1053 + L_PAREN@934..935 "(" + BLOCK_EXPR@935..1052 + STMT_LIST@935..1052 + L_CURLY@935..936 "{" + WHITESPACE@936..945 "\n " + ATTR@945..1008 + POUND@945..946 "#" + BANG@946..947 "!" + L_BRACK@947..948 "[" + TOKEN_TREE_META@948..1007 + PATH@948..951 + PATH_SEGMENT@948..951 + NAME_REF@948..951 + IDENT@948..951 "doc" + TOKEN_TREE@951..1007 + L_PAREN@951..952 "(" + STRING@952..1006 "\"This is fine, call a ..." + R_PAREN@1006..1007 ")" + R_BRACK@1007..1008 "]" + WHITESPACE@1008..1017 "\n " + DOC_COMMENT@1017..1046 + INNER_DOC_COMMENT@1017..1046 "//! So are ModuleDoc ..." + WHITESPACE@1046..1051 "\n " + R_CURLY@1051..1052 "}" + R_PAREN@1052..1053 ")" + SEMICOLON@1053..1054 ";" + WHITESPACE@1054..1059 "\n " + EXPR_STMT@1059..1183 + METHOD_CALL_EXPR@1059..1182 + PATH_EXPR@1059..1060 + PATH@1059..1060 + PATH_SEGMENT@1059..1060 + NAME_REF@1059..1060 + IDENT@1059..1060 "s" + DOT@1060..1061 "." + NAME_REF@1061..1062 + IDENT@1061..1062 "m" + ARG_LIST@1062..1182 + L_PAREN@1062..1063 "(" + BLOCK_EXPR@1063..1181 + STMT_LIST@1063..1181 + L_CURLY@1063..1064 "{" + WHITESPACE@1064..1073 "\n " + ATTR@1073..1137 + POUND@1073..1074 "#" + BANG@1074..1075 "!" + L_BRACK@1075..1076 "[" + TOKEN_TREE_META@1076..1136 + PATH@1076..1079 + PATH_SEGMENT@1076..1079 + NAME_REF@1076..1079 + IDENT@1076..1079 "doc" + TOKEN_TREE@1079..1136 + L_PAREN@1079..1080 "(" + STRING@1080..1135 "\"This is fine, method ..." + R_PAREN@1135..1136 ")" + R_BRACK@1136..1137 "]" + WHITESPACE@1137..1146 "\n " + DOC_COMMENT@1146..1175 + INNER_DOC_COMMENT@1146..1175 "//! So are ModuleDoc ..." + WHITESPACE@1175..1180 "\n " + R_CURLY@1180..1181 "}" + R_PAREN@1181..1182 ")" + SEMICOLON@1182..1183 ";" + WHITESPACE@1183..1184 "\n" + R_CURLY@1184..1185 "}" + WHITESPACE@1185..1186 "\n" error 39..83: A block in this position cannot accept inner attributes error 152..171: A block in this position cannot accept inner attributes error 180..212: A block in this position cannot accept inner attributes diff --git a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs index 65bb7e1914248..870be17c07c1c 100644 --- a/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs +++ b/src/tools/rust-analyzer/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs @@ -20,4 +20,24 @@ fn block() { #![doc("This is fine, `for` bodies accept inner attributes")] //! So are ModuleDoc comments } + let t = ( + { + #![doc("This is fine, tuple elements accept inner attributes")] + //! So are ModuleDoc comments + }, + ); + let a = [ + { + #![doc("This is fine, array elements accept inner attributes")] + //! So are ModuleDoc comments + }, + ]; + g({ + #![doc("This is fine, call arguments accept inner attributes")] + //! So are ModuleDoc comments + }); + s.m({ + #![doc("This is fine, method call arguments accept attributes")] + //! So are ModuleDoc comments + }); } diff --git a/src/tools/rust-analyzer/docs/book/README.md b/src/tools/rust-analyzer/docs/book/README.md index cd4d8783a4d20..229180226999d 100644 --- a/src/tools/rust-analyzer/docs/book/README.md +++ b/src/tools/rust-analyzer/docs/book/README.md @@ -7,7 +7,7 @@ The rust analyzer manual uses [mdbook](https://rust-lang.github.io/mdBook/). To run the documentation site locally: ```bash -cargo install mdbook +cargo install --locked mdbook cargo xtask codegen cd docs/book mdbook serve diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/architecture.md b/src/tools/rust-analyzer/docs/book/src/contributing/architecture.md index 50f60bcdccc1b..8b6ea56c85165 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/architecture.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/architecture.md @@ -143,7 +143,7 @@ Reading the docs of the `base_db::input` module should be useful: everything els **Architecture Invariant:** particularities of the build system are *not* the part of the ground state. In particular, `base-db` knows nothing about cargo. -For example, `cfg` flags are a part of `base_db`, but `feature`s are not. +For example, `cfg` flags are a part of `base-db`, but `feature`s are not. A `foo` feature is a Cargo-level concept, which is lowered by Cargo to `--cfg feature=foo` argument on the command line. The `CrateGraph` structure is used to represent the dependencies between the crates abstractly. diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/guide.md b/src/tools/rust-analyzer/docs/book/src/contributing/guide.md index 9e944bfe0fc4d..f985986f9635c 100644 --- a/src/tools/rust-analyzer/docs/book/src/contributing/guide.md +++ b/src/tools/rust-analyzer/docs/book/src/contributing/guide.md @@ -67,12 +67,12 @@ Next, let's talk about what the inputs to the `Analysis` are, precisely. rust-analyzer never does any I/O itself, all inputs get passed explicitly via the `AnalysisHost::apply_change` method, which accepts a single argument, a -`Change`. [`Change`] is a wrapper for `FileChange` that adds proc-macro knowledge. -[`FileChange`] is a builder for a single change "transaction", so it suffices -to study its methods to understand all the input data. +[`ChangeWithProcMacros`]. [`ChangeWithProcMacros`] is a wrapper for `FileChange` +that adds proc-macro knowledge. [`FileChange`] is a builder for a single change +"transaction", so it suffices to study its methods to understand all the input data. -[`Change`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/hir-expand/src/change.rs#L10-L42 -[`FileChange`]: https://github.com/rust-lang/rust-analyzer/blob/2024-01-01/crates/base-db/src/change.rs#L14-L78 +[`ChangeWithProcMacros`]: https://github.com/rust-lang/rust-analyzer/blob/2026-08-03/crates/hir-expand/src/change.rs#L9-L42 +[`FileChange`]: https://github.com/rust-lang/rust-analyzer/blob/2026-08-03/crates/base-db/src/change.rs#L18-L99 The `change_file` method controls the set of the input files, where each file has an integer id (`FileId`, picked by the client) and text (`Option>`). diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version index 9ff8b0c27d19c..18fea436747c7 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -f7d782a3be46d6bb4b9792fe69a61db389ba1769 +32d94cc9be3f6e6c3fa1deaea9e0ab93c4980dba diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs index 257429c42661f..0553234a5c898 100644 --- a/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs +++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar.rs @@ -672,6 +672,8 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String { [string] => { $crate::SyntaxKind::STRING }; [shebang] => { $crate::SyntaxKind::SHEBANG }; [frontmatter] => { $crate::SyntaxKind::FRONTMATTER }; + [inner_doc_comment] => { $crate::SyntaxKind::INNER_DOC_COMMENT }; + [outer_doc_comment] => { $crate::SyntaxKind::OUTER_DOC_COMMENT }; } impl ::core::marker::Copy for SyntaxKind {} @@ -938,7 +940,13 @@ fn lower_rule(acc: &mut Vec, grammar: &Grammar, label: Option<&String>, r Rule::Rep(inner) => { if let Rule::Node(node) = &**inner { let ty = grammar[*node].name.clone(); - let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty))); + let name = label.cloned().unwrap_or_else(|| { + if ty == "AnyAttr" { + "attrs".to_owned() + } else { + pluralize(&to_lower_snake_case(&ty)) + } + }); let field = Field::Node { name, ty, cardinality: Cardinality::Many }; acc.push(field); return; @@ -1089,35 +1097,6 @@ fn extract_struct_traits(ast: &mut AstSrc) { extract_struct_trait(node, name, methods); } } - - let nodes_with_doc_comments = [ - "SourceFile", - "Fn", - "Struct", - "Union", - "RecordField", - "TupleField", - "Enum", - "Variant", - "Trait", - "Module", - "Static", - "Const", - "TypeAlias", - "Impl", - "ExternBlock", - "ExternCrate", - "MacroCall", - "MacroRules", - "MacroDef", - "Use", - ]; - - for node in &mut ast.nodes { - if nodes_with_doc_comments.contains(&&*node.name) { - node.traits.push("HasDocComments".into()); - } - } } fn extract_struct_trait(node: &mut AstNodeSrc, trait_name: &str, methods: &[&str]) { diff --git a/src/tools/rust-analyzer/xtask/src/main.rs b/src/tools/rust-analyzer/xtask/src/main.rs index 764cc09ded6c4..dbfc39534c673 100644 --- a/src/tools/rust-analyzer/xtask/src/main.rs +++ b/src/tools/rust-analyzer/xtask/src/main.rs @@ -72,7 +72,7 @@ fn run_fuzzer(sh: &Shell) -> anyhow::Result<()> { let _d = sh.push_dir("./crates/syntax"); let _e = sh.push_env("RUSTUP_TOOLCHAIN", "nightly"); if cmd!(sh, "cargo fuzz --help").read().is_err() { - cmd!(sh, "cargo install cargo-fuzz").run()?; + cmd!(sh, "cargo install --locked cargo-fuzz").run()?; }; // Expecting nightly rustc diff --git a/tests/assembly-llvm/x86-vendor-intrinsics.rs b/tests/assembly-llvm/x86-vendor-intrinsics.rs new file mode 100644 index 0000000000000..b600b74c66ecd --- /dev/null +++ b/tests/assembly-llvm/x86-vendor-intrinsics.rs @@ -0,0 +1,21 @@ +// Output differs depending on ABI so we need to match the full target. +//@ only-x86_64-unknown-linux-gnu +//@ assembly-output: emit-asm +//@ compile-flags: -Ctarget-feature=-sse3 -C opt-level=3 + +// Regression test for various cases where we used to compile x86 vendor intrinsics in a suboptimal +// way. + +#![crate_type = "lib"] + +use std::arch::x86_64::*; + +// CHECK-LABEL: test_packus_epi16: +#[unsafe(no_mangle)] +#[target_feature(enable = "sse2")] +extern "C" fn test_packus_epi16(a: __m128i, b: __m128i) -> __m128i { + // CHECK: .cfi_startproc + // CHECK-NEXT: packuswb + // CHECK-NEXT: ret + _mm_packus_epi16(a, b) +} diff --git a/tests/rustdoc-ui/lints/bare-urls.fixed b/tests/rustdoc-ui/lints/bare-urls.fixed index 996214b5ff14f..b18aae11c77cf 100644 --- a/tests/rustdoc-ui/lints/bare-urls.fixed +++ b/tests/rustdoc-ui/lints/bare-urls.fixed @@ -92,3 +92,7 @@ pub fn trailing_period() {} /// ] //~^ ERROR this URL is not a hyperlink pub fn lint_with_brackets() {} + +/// See +//~^ ERROR this URL is not a hyperlink +pub fn hippo() {} diff --git a/tests/rustdoc-ui/lints/bare-urls.rs b/tests/rustdoc-ui/lints/bare-urls.rs index 9b4fe68e00322..fb39ec6b6ccbd 100644 --- a/tests/rustdoc-ui/lints/bare-urls.rs +++ b/tests/rustdoc-ui/lints/bare-urls.rs @@ -92,3 +92,7 @@ pub fn trailing_period() {} /// https://bloob.blob] //~^ ERROR this URL is not a hyperlink pub fn lint_with_brackets() {} + +/// See https://en.wikipedia.org/wiki/Rust_(programming_language) +//~^ ERROR this URL is not a hyperlink +pub fn hippo() {} diff --git a/tests/rustdoc-ui/lints/bare-urls.stderr b/tests/rustdoc-ui/lints/bare-urls.stderr index 05ddd2ed42ab1..a3a291e8e4bca 100644 --- a/tests/rustdoc-ui/lints/bare-urls.stderr +++ b/tests/rustdoc-ui/lints/bare-urls.stderr @@ -364,5 +364,17 @@ help: use an automatic link instead LL | /// ] | + + -error: aborting due to 30 previous errors +error: this URL is not a hyperlink + --> $DIR/bare-urls.rs:96:9 + | +LL | /// See https://en.wikipedia.org/wiki/Rust_(programming_language) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: bare URLs are not automatically turned into clickable links +help: use an automatic link instead + | +LL | /// See + | + + + +error: aborting due to 31 previous errors