Auto merge of #150702 - lnicola:sync-from-ra, r=lnicola

`rust-analyzer` subtree update

Subtree update of `rust-analyzer` to https://github.com/rust-lang/rust-analyzer/commit/6a1246b69ca761480b9278df019f717b549cface.

Created using https://github.com/rust-lang/josh-sync.

r? `@ghost`
This commit is contained in:
bors
2026-01-05 22:21:47 +00:00
60 changed files with 766 additions and 368 deletions
@@ -26,6 +26,9 @@ pub struct EditionedFileIdData {
krate: Crate,
}
// FIXME: This poses an invalidation problem, if one constructs an `EditionedFileId` with a
// different crate then whatever the input of a memo used, it will invalidate the memo causing
// it to recompute even if the crate is not really used.
/// We like to include the origin crate in an `EditionedFileId` (for use in the item tree),
/// but this poses us a problem.
///
@@ -57,8 +57,7 @@ pub fn new(mut ctx: span::SyntaxContext) -> Self {
Self(ctx)
}
// FIXME: Inline this
pub(crate) fn lookup(self) -> SyntaxContext {
pub(crate) fn syntax_context(self) -> SyntaxContext {
self.0
}
@@ -73,7 +72,8 @@ pub(crate) fn is_root(self) -> bool {
pub type PatPtr = AstPtr<ast::Pat>;
pub type PatSource = InFile<PatPtr>;
pub type LabelPtr = AstPtr<ast::Label>;
/// BlockExpr -> Desugared label from try block
pub type LabelPtr = AstPtr<Either<ast::Label, ast::BlockExpr>>;
pub type LabelSource = InFile<LabelPtr>;
pub type FieldPtr = AstPtr<ast::RecordExprField>;
@@ -942,7 +942,7 @@ pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] {
}
pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> {
let src = node.map(AstPtr::new);
let src = node.map(AstPtr::new).map(AstPtr::wrap_left);
self.expr_only()?.label_map.get(&src).cloned()
}
@@ -11,7 +11,7 @@
ExpandError, ExpandErrorKind, ExpandResult, HirFileId, InFile, Lookup, MacroCallId,
eager::EagerCallBackFn, mod_path::ModPath, span_map::SpanMap,
};
use span::{AstIdMap, Edition, SyntaxContext};
use span::{AstIdMap, SyntaxContext};
use syntax::ast::HasAttrs;
use syntax::{AstNode, Parse, ast};
use triomphe::Arc;
@@ -75,11 +75,6 @@ pub(super) fn is_cfg_enabled(
AttrFlags::is_cfg_enabled_for(owner, cfg_options)
}
pub(super) fn call_syntax_ctx(&self) -> SyntaxContext {
// FIXME:
SyntaxContext::root(Edition::CURRENT_FIXME)
}
pub(super) fn enter_expand<T: ast::AstNode>(
&mut self,
db: &dyn DefDatabase,
@@ -1096,7 +1096,7 @@ fn maybe_collect_expr(&mut self, expr: ast::Expr) -> Option<ExprId> {
ast::Expr::WhileExpr(e) => self.collect_while_loop(syntax_ptr, e),
ast::Expr::ForExpr(e) => self.collect_for_loop(syntax_ptr, e),
ast::Expr::CallExpr(e) => {
// FIXME: Remove this once we drop support for <1.86, https://github.com/rust-lang/rust/commit/ac9cb908ac4301dfc25e7a2edee574320022ae2c
// FIXME(MINIMUM_SUPPORTED_TOOLCHAIN_VERSION): Remove this once we drop support for <1.86, https://github.com/rust-lang/rust/commit/ac9cb908ac4301dfc25e7a2edee574320022ae2c
let is_rustc_box = {
let attrs = e.attrs();
attrs.filter_map(|it| it.as_simple_atom()).any(|it| it == "rustc_box")
@@ -1649,7 +1649,7 @@ fn with_binding_owner(&mut self, create_expr: impl FnOnce(&mut Self) -> ExprId)
fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput);
let label = self.generate_new_name();
let label = self.alloc_label_desugared(Label { name: label });
let label = self.alloc_label_desugared(Label { name: label }, AstPtr::new(&e).wrap_right());
let old_label = self.current_try_block_label.replace(label);
let ptr = AstPtr::new(&e).upcast();
@@ -2319,7 +2319,6 @@ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatI
ast::Pat::SlicePat(p) => {
let SlicePatComponents { prefix, slice, suffix } = p.components();
// FIXME properly handle `RestPat`
Pat::Slice {
prefix: prefix.into_iter().map(|p| self.collect_pat(p, binding_list)).collect(),
slice: slice.map(|p| self.collect_pat(p, binding_list)),
@@ -2399,7 +2398,6 @@ fn collect_pat(&mut self, pat: ast::Pat, binding_list: &mut BindingList) -> PatI
};
let start = range_part_lower(p.start());
let end = range_part_lower(p.end());
// FIXME: Exclusive ended pattern range is stabilised
match p.op_kind() {
Some(range_type) => Pat::Range { start, end, range_type },
None => Pat::Missing,
@@ -2519,9 +2517,9 @@ fn resolve_label(
let mut hygiene_info = if hygiene_id.is_root() {
None
} else {
hygiene_id.lookup().outer_expn(self.db).map(|expansion| {
hygiene_id.syntax_context().outer_expn(self.db).map(|expansion| {
let expansion = self.db.lookup_intern_macro_call(expansion.into());
(hygiene_id.lookup().parent(self.db), expansion.def)
(hygiene_id.syntax_context().parent(self.db), expansion.def)
})
};
let name = Name::new_lifetime(&lifetime.text());
@@ -2727,17 +2725,17 @@ fn missing_pat(&mut self) -> PatId {
self.store.pats.alloc(Pat::Missing)
}
fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
fn alloc_label(&mut self, label: Label, ptr: AstPtr<ast::Label>) -> LabelId {
self.alloc_label_desugared(label, ptr.wrap_left())
}
fn alloc_label_desugared(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
let src = self.expander.in_file(ptr);
let id = self.store.labels.alloc(label);
self.store.label_map_back.insert(id, src);
self.store.label_map.insert(src, id);
id
}
// FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
fn alloc_label_desugared(&mut self, label: Label) -> LabelId {
self.store.labels.alloc(label)
}
fn is_lowering_awaitable_block(&self) -> &Awaitable {
self.awaitable_context.as_ref().unwrap_or(&Awaitable::No("unknown"))
@@ -3,6 +3,7 @@
use base_db::FxIndexSet;
use hir_expand::name::Name;
use intern::{Symbol, sym};
use span::SyntaxContext;
use syntax::{AstPtr, AstToken as _, ast};
use crate::{
@@ -49,7 +50,7 @@ pub(super) fn collect_format_args(
self.expand_macros_to_string(template.clone()).map(|it| (it, template))
}) {
Some(((s, is_direct_literal), template)) => {
let call_ctx = self.expander.call_syntax_ctx();
let call_ctx = SyntaxContext::root(self.def_map.edition());
let hygiene = self.hygiene_id_for(s.syntax().text_range());
let fmt = format_args::parse(
&s,
@@ -20,8 +20,7 @@
/// Data about a generic type parameter (to a function, struct, impl, ...).
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
pub struct TypeParamData {
/// [`None`] only if the type ref is an [`crate::type_ref::TypeRef::ImplTrait`]. FIXME: Might be better to just
/// make it always be a value, giving impl trait a special name.
/// [`None`] only if the type ref is an [`crate::type_ref::TypeRef::ImplTrait`].
pub name: Option<Name>,
pub default: Option<TypeRefId>,
pub provenance: TypeParamProvenance,
@@ -1,8 +1,6 @@
//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.
use std::fmt::Write;
use hir_expand::name::Name;
use intern::Symbol;
use la_arena::Idx;
@@ -10,12 +8,11 @@
use crate::{
LifetimeParamId, TypeParamId,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr_store::{
ExpressionStore,
path::{GenericArg, Path},
},
hir::{ExprId, Literal},
hir::ExprId,
};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
@@ -275,56 +272,3 @@ pub fn as_path<'a>(&self, map: &'a ExpressionStore) -> Option<(&'a Path, TraitBo
pub struct ConstRef {
pub expr: ExprId,
}
/// A literal constant value
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum LiteralConstRef {
Int(i128),
UInt(u128),
Bool(bool),
Char(char),
/// Case of an unknown value that rustc might know but we don't
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
// constants
// https://github.com/rust-lang/rust-analyzer/pull/8813#issuecomment-840679177
// https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/Handling.20non.20evaluatable.20constants'.20equality/near/238386348
Unknown,
}
impl LiteralConstRef {
pub fn builtin_type(&self) -> BuiltinType {
match self {
LiteralConstRef::UInt(_) | LiteralConstRef::Unknown => {
BuiltinType::Uint(BuiltinUint::U128)
}
LiteralConstRef::Int(_) => BuiltinType::Int(BuiltinInt::I128),
LiteralConstRef::Char(_) => BuiltinType::Char,
LiteralConstRef::Bool(_) => BuiltinType::Bool,
}
}
}
impl From<Literal> for LiteralConstRef {
fn from(literal: Literal) -> Self {
match literal {
Literal::Char(c) => Self::Char(c),
Literal::Bool(flag) => Self::Bool(flag),
Literal::Int(num, _) => Self::Int(num),
Literal::Uint(num, _) => Self::UInt(num),
_ => Self::Unknown,
}
}
}
impl std::fmt::Display for LiteralConstRef {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
LiteralConstRef::Int(num) => num.fmt(f),
LiteralConstRef::UInt(num) => num.fmt(f),
LiteralConstRef::Bool(flag) => flag.fmt(f),
LiteralConstRef::Char(c) => write!(f, "'{c}'"),
LiteralConstRef::Unknown => f.write_char('_'),
}
}
}
@@ -158,7 +158,7 @@ pub struct ItemScope {
/// declared.
declarations: ThinVec<ModuleDefId>,
impls: ThinVec<ImplId>,
impls: ThinVec<(ImplId, /* trait impl */ bool)>,
builtin_derive_impls: ThinVec<BuiltinDeriveImplId>,
extern_blocks: ThinVec<ExternBlockId>,
unnamed_consts: ThinVec<ConstId>,
@@ -327,7 +327,15 @@ pub fn use_decls(&self) -> impl ExactSizeIterator<Item = UseId> + '_ {
}
pub fn impls(&self) -> impl ExactSizeIterator<Item = ImplId> + '_ {
self.impls.iter().copied()
self.impls.iter().map(|&(id, _)| id)
}
pub fn trait_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
self.impls.iter().filter(|&&(_, is_trait_impl)| is_trait_impl).map(|&(id, _)| id)
}
pub fn inherent_impls(&self) -> impl Iterator<Item = ImplId> + '_ {
self.impls.iter().filter(|&&(_, is_trait_impl)| !is_trait_impl).map(|&(id, _)| id)
}
pub fn builtin_derive_impls(&self) -> impl ExactSizeIterator<Item = BuiltinDeriveImplId> + '_ {
@@ -472,8 +480,8 @@ pub(crate) fn get_legacy_macro(&self, name: &Name) -> Option<&[MacroId]> {
self.legacy_macros.get(name).map(|it| &**it)
}
pub(crate) fn define_impl(&mut self, imp: ImplId) {
self.impls.push(imp);
pub(crate) fn define_impl(&mut self, imp: ImplId, is_trait_impl: bool) {
self.impls.push((imp, is_trait_impl));
}
pub(crate) fn define_builtin_derive_impl(&mut self, imp: BuiltinDeriveImplId) {
@@ -614,7 +614,9 @@ pub struct Trait {
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Impl {}
pub struct Impl {
pub is_trait_impl: bool,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TypeAlias {
@@ -271,7 +271,7 @@ fn lower_impl(&mut self, impl_def: &ast::Impl) -> ItemTreeAstId<Impl> {
let ast_id = self.source_ast_id_map.ast_id(impl_def);
// Note that trait impls don't get implicit `Self` unlike traits, because here they are a
// type alias rather than a type parameter, so this is handled by the resolver.
let res = Impl {};
let res = Impl { is_trait_impl: impl_def.trait_().is_some() };
self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Impl(res));
ast_id
}
@@ -258,7 +258,7 @@ fn print_mod_item(&mut self, item: ModItemId) {
w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition));
}
ModItemId::Impl(ast_id) => {
let Impl {} = &self.tree[ast_id];
let Impl { is_trait_impl: _ } = &self.tree[ast_id];
self.print_ast_id(ast_id.erase());
w!(self, "impl {{ ... }}");
}
@@ -40,8 +40,12 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option<Box<LangIt
let crate_def_map = crate_def_map(db, krate);
if !crate_def_map.is_unstable_feature_enabled(&sym::lang_items) {
return None;
}
for (_, module_data) in crate_def_map.modules() {
for impl_def in module_data.scope.impls() {
for impl_def in module_data.scope.inherent_impls() {
lang_items.collect_lang_item(db, impl_def);
for &(_, assoc) in impl_def.impl_items(db).items.iter() {
match assoc {
@@ -16,7 +16,7 @@
use std::{any::TypeId, iter, ops::Range, sync};
use base_db::{RootQueryDb, SourceDatabase};
use base_db::RootQueryDb;
use expect_test::Expect;
use hir_expand::{
AstId, ExpansionInfo, InFile, MacroCallId, MacroCallKind, MacroKind,
@@ -387,7 +387,7 @@ fn pretty_print_macro_expansion(
impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
subtree: &TopSubtree,
_: Option<&TopSubtree>,
_: &base_db::Env,
@@ -2028,7 +2028,9 @@ fn collect(&mut self, items: &[ModItemId], container: ItemContainerId) {
let impl_id =
ImplLoc { container: module_id, id: InFile::new(self.file_id(), imp) }
.intern(db);
self.def_collector.def_map.modules[self.module_id].scope.define_impl(impl_id)
self.def_collector.def_map.modules[self.module_id]
.scope
.define_impl(impl_id, self.item_tree[imp].is_trait_impl)
}
ModItemId::Function(id) => {
let it = &self.item_tree[id];
@@ -267,7 +267,6 @@ pub(super) fn resolve_path_fp_with_macro_single(
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
// FIXME there must be a nicer way to write this condition
PathKind::Plain | PathKind::Abs
if self.data.edition == Edition::Edition2015
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
@@ -383,7 +382,6 @@ pub(super) fn resolve_path_fp_in_all_preludes(
// plain import or absolute path in 2015: crate-relative with
// fallback to extern prelude (with the simplification in
// rust-lang/rust#57745)
// FIXME there must be a nicer way to write this condition
PathKind::Plain | PathKind::Abs
if self.data.edition == Edition::Edition2015
&& (path.kind == PathKind::Abs || mode == ResolveMode::Import) =>
@@ -950,7 +950,7 @@ fn hygiene_info(
hygiene_id: HygieneId,
) -> Option<(SyntaxContext, MacroDefId)> {
if !hygiene_id.is_root() {
let ctx = hygiene_id.lookup();
let ctx = hygiene_id.syntax_context();
ctx.outer_expn(db).map(|expansion| {
let expansion = db.lookup_intern_macro_call(expansion.into());
(ctx.parent(db), expansion.def)
@@ -9,8 +9,8 @@
use crate::{
AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, HirFileId, MacroCallId,
MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
EagerExpander, EditionedFileId, ExpandError, ExpandResult, ExpandTo, FileRange, HirFileId,
MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
attrs::Meta,
builtin::pseudo_derive_attr_expansion,
cfg_process::attr_macro_input_to_token_tree,
@@ -61,6 +61,9 @@ pub trait ExpandDatabase: RootQueryDb {
#[salsa::lru(1024)]
fn ast_id_map(&self, file_id: HirFileId) -> Arc<AstIdMap>;
#[salsa::transparent]
fn resolve_span(&self, span: Span) -> FileRange;
#[salsa::transparent]
fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode;
@@ -158,6 +161,13 @@ fn syntax_context(db: &dyn ExpandDatabase, file: HirFileId, edition: Edition) ->
}
}
fn resolve_span(db: &dyn ExpandDatabase, Span { range, anchor, ctx: _ }: Span) -> FileRange {
let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id);
let anchor_offset =
db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
FileRange { file_id, range: range + anchor_offset }
}
/// This expands the given macro call, but with different arguments. This is
/// used for completion, where we want to see what 'would happen' if we insert a
/// token. The `token_to_map` mapped down into the expansion, with the mapped
@@ -901,11 +901,8 @@ pub fn map_range_up_once(
let span = self.exp_map.span_at(token.start());
match &self.arg_map {
SpanMap::RealSpanMap(_) => {
let file_id =
EditionedFileId::from_span_guess_origin(db, span.anchor.file_id).into();
let anchor_offset =
db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start();
InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] }
let range = db.resolve_span(span);
InFile { file_id: range.file_id.into(), value: smallvec::smallvec![range.range] }
}
SpanMap::ExpansionSpanMap(arg_map) => {
let Some(arg_node) = &self.arg.value else {
@@ -947,7 +944,7 @@ pub fn map_node_range_up_rooted(
range: TextRange,
) -> Option<FileRange> {
let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
let Span { range, anchor, ctx: _ } = spans.next()?;
let Span { range, anchor, ctx } = spans.next()?;
let mut start = range.start();
let mut end = range.end();
@@ -958,10 +955,7 @@ pub fn map_node_range_up_rooted(
start = start.min(span.range.start());
end = end.max(span.range.end());
}
let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id);
let anchor_offset =
db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
Some(FileRange { file_id, range: TextRange::new(start, end) + anchor_offset })
Some(db.resolve_span(Span { range: TextRange::new(start, end), anchor, ctx }))
}
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
@@ -984,10 +978,7 @@ pub fn map_node_range_up(
start = start.min(span.range.start());
end = end.max(span.range.end());
}
let file_id = EditionedFileId::from_span_guess_origin(db, anchor.file_id);
let anchor_offset =
db.ast_id_map(file_id.into()).get_erased(anchor.ast_id).text_range().start();
Some((FileRange { file_id, range: TextRange::new(start, end) + anchor_offset }, ctx))
Some((db.resolve_span(Span { range: TextRange::new(start, end), anchor, ctx }), ctx))
}
/// Looks up the span at the given offset.
@@ -997,10 +988,7 @@ pub fn span_for_offset(
offset: TextSize,
) -> (FileRange, SyntaxContext) {
let span = exp_map.span_at(offset);
let file_id = EditionedFileId::from_span_guess_origin(db, span.anchor.file_id);
let anchor_offset =
db.ast_id_map(file_id.into()).get_erased(span.anchor.ast_id).text_range().start();
(FileRange { file_id, range: span.range + anchor_offset }, span.ctx)
(db.resolve_span(span), span.ctx)
}
/// In Rust, macros expand token trees to token trees. When we want to turn a
@@ -4,7 +4,7 @@
use std::any::Any;
use std::{panic::RefUnwindSafe, sync};
use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError, SourceDatabase};
use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError};
use intern::Symbol;
use rustc_hash::FxHashMap;
use span::Span;
@@ -25,7 +25,7 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe + Any {
/// [`ProcMacroKind::Attr`]), environment variables, and span information.
fn expand(
&self,
db: &dyn SourceDatabase,
db: &dyn ExpandDatabase,
subtree: &tt::TopSubtree,
attrs: Option<&tt::TopSubtree>,
env: &Env,
@@ -7,9 +7,9 @@
use hir_def::{
ConstId, EnumVariantId, GeneralConstId, HasModule, StaticId,
attrs::AttrFlags,
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
expr_store::Body,
hir::{Expr, ExprId},
type_ref::LiteralConstRef,
hir::{Expr, ExprId, Literal},
};
use hir_expand::Lookup;
use rustc_type_ir::inherent::IntoKind;
@@ -23,7 +23,7 @@
mir::{MirEvalError, MirLowerError},
next_solver::{
Const, ConstBytes, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs,
ParamEnv, StoredConst, StoredGenericArgs, Ty, ValueConst,
StoredConst, StoredGenericArgs, Ty, ValueConst,
},
traits::StoredParamEnvAndCrate,
};
@@ -81,47 +81,122 @@ fn from(value: MirEvalError) -> Self {
/// Interns a constant scalar with the given type
pub fn intern_const_ref<'a>(
db: &'a dyn HirDatabase,
value: &LiteralConstRef,
value: &Literal,
ty: Ty<'a>,
krate: Crate,
_krate: Crate,
) -> Const<'a> {
let interner = DbInterner::new_no_crate(db);
let layout = db
.layout_of_ty(ty.store(), ParamEnvAndCrate { param_env: ParamEnv::empty(), krate }.store());
let kind = match value {
LiteralConstRef::Int(i) => {
// FIXME: We should handle failure of layout better.
let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
&Literal::Uint(i, builtin_ty)
if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Uint) =>
{
let memory = match ty.as_builtin() {
Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint {
BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>,
BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()),
BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()),
BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()),
BuiltinUint::U128 => Box::new((i).to_le_bytes()),
BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()),
},
_ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
};
rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes {
memory: i.to_le_bytes()[0..size].into(),
memory_map: MemoryMap::default(),
},
ConstBytes { memory, memory_map: MemoryMap::default() },
))
}
LiteralConstRef::UInt(i) => {
let size = layout.map(|it| it.size.bytes_usize()).unwrap_or(16);
&Literal::Int(i, None)
if ty
.as_builtin()
.is_some_and(|builtin_ty| matches!(builtin_ty, BuiltinType::Uint(_))) =>
{
let memory = match ty.as_builtin() {
Some(BuiltinType::Uint(builtin_uint)) => match builtin_uint {
BuiltinUint::U8 => Box::new([i as u8]) as Box<[u8]>,
BuiltinUint::U16 => Box::new((i as u16).to_le_bytes()),
BuiltinUint::U32 => Box::new((i as u32).to_le_bytes()),
BuiltinUint::U64 => Box::new((i as u64).to_le_bytes()),
BuiltinUint::U128 => Box::new((i as u128).to_le_bytes()),
BuiltinUint::Usize => Box::new((i as usize).to_le_bytes()),
},
_ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
};
rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes {
memory: i.to_le_bytes()[0..size].into(),
memory_map: MemoryMap::default(),
},
ConstBytes { memory, memory_map: MemoryMap::default() },
))
}
LiteralConstRef::Bool(b) => rustc_type_ir::ConstKind::Value(ValueConst::new(
&Literal::Int(i, builtin_ty)
if builtin_ty.is_none() || ty.as_builtin() == builtin_ty.map(BuiltinType::Int) =>
{
let memory = match ty.as_builtin() {
Some(BuiltinType::Int(builtin_int)) => match builtin_int {
BuiltinInt::I8 => Box::new([i as u8]) as Box<[u8]>,
BuiltinInt::I16 => Box::new((i as i16).to_le_bytes()),
BuiltinInt::I32 => Box::new((i as i32).to_le_bytes()),
BuiltinInt::I64 => Box::new((i as i64).to_le_bytes()),
BuiltinInt::I128 => Box::new((i).to_le_bytes()),
BuiltinInt::Isize => Box::new((i as isize).to_le_bytes()),
},
_ => return Const::new(interner, rustc_type_ir::ConstKind::Error(ErrorGuaranteed)),
};
rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes { memory, memory_map: MemoryMap::default() },
))
}
Literal::Float(float_type_wrapper, builtin_float)
if builtin_float.is_none()
|| ty.as_builtin() == builtin_float.map(BuiltinType::Float) =>
{
let memory = match ty.as_builtin().unwrap() {
BuiltinType::Float(builtin_float) => match builtin_float {
// FIXME:
hir_def::builtin_type::BuiltinFloat::F16 => Box::new([0u8; 2]) as Box<[u8]>,
hir_def::builtin_type::BuiltinFloat::F32 => {
Box::new(float_type_wrapper.to_f32().to_le_bytes())
}
hir_def::builtin_type::BuiltinFloat::F64 => {
Box::new(float_type_wrapper.to_f64().to_le_bytes())
}
// FIXME:
hir_def::builtin_type::BuiltinFloat::F128 => Box::new([0; 16]),
},
_ => unreachable!(),
};
rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes { memory, memory_map: MemoryMap::default() },
))
}
Literal::Bool(b) if ty.is_bool() => rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes { memory: Box::new([*b as u8]), memory_map: MemoryMap::default() },
)),
LiteralConstRef::Char(c) => rustc_type_ir::ConstKind::Value(ValueConst::new(
Literal::Char(c) if ty.is_char() => rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes {
memory: (*c as u32).to_le_bytes().into(),
memory_map: MemoryMap::default(),
},
)),
LiteralConstRef::Unknown => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
Literal::String(symbol) if ty.is_str() => rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes {
memory: symbol.as_str().as_bytes().into(),
memory_map: MemoryMap::default(),
},
)),
Literal::ByteString(items) if ty.as_slice().is_some_and(|ty| ty.is_u8()) => {
rustc_type_ir::ConstKind::Value(ValueConst::new(
ty,
ConstBytes { memory: items.clone(), memory_map: MemoryMap::default() },
))
}
// FIXME
Literal::CString(_items) => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
_ => rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
};
Const::new(interner, kind)
}
@@ -130,7 +205,15 @@ pub fn intern_const_ref<'a>(
pub fn usize_const<'db>(db: &'db dyn HirDatabase, value: Option<u128>, krate: Crate) -> Const<'db> {
intern_const_ref(
db,
&value.map_or(LiteralConstRef::Unknown, LiteralConstRef::UInt),
&match value {
Some(value) => Literal::Uint(value, Some(BuiltinUint::Usize)),
None => {
return Const::new(
DbInterner::new_no_crate(db),
rustc_type_ir::ConstKind::Error(ErrorGuaranteed),
);
}
},
Ty::new_uint(DbInterner::new_no_crate(db), rustc_type_ir::UintTy::Usize),
krate,
)
@@ -2209,6 +2209,7 @@ fn boxes() {
check_number(
r#"
//- minicore: coerce_unsized, deref_mut, slice
#![feature(lang_items)]
use core::ops::{Deref, DerefMut};
use core::{marker::Unsize, ops::CoerceUnsized};
@@ -2346,6 +2347,7 @@ fn c_string() {
check_number(
r#"
//- minicore: index, slice
#![feature(lang_items)]
#[lang = "CStr"]
pub struct CStr {
inner: [u8]
@@ -2360,6 +2362,7 @@ pub struct CStr {
check_number(
r#"
//- minicore: index, slice
#![feature(lang_items)]
#[lang = "CStr"]
pub struct CStr {
inner: [u8]
@@ -10,7 +10,8 @@
use base_db::{Crate, FxIndexMap};
use either::Either;
use hir_def::{
FindPathConfig, GenericDefId, HasModule, LocalFieldId, Lookup, ModuleDefId, ModuleId, TraitId,
FindPathConfig, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, ModuleDefId,
ModuleId, TraitId,
db::DefDatabase,
expr_store::{ExpressionStore, path::Path},
find_path::{self, PrefixKind},
@@ -66,6 +67,7 @@
pub trait HirWrite: fmt::Write {
fn start_location_link(&mut self, _location: ModuleDefId) {}
fn start_location_link_generic(&mut self, _location: GenericParamId) {}
fn end_location_link(&mut self) {}
}
@@ -147,6 +149,10 @@ pub fn start_location_link(&mut self, location: ModuleDefId) {
self.fmt.start_location_link(location);
}
pub fn start_location_link_generic(&mut self, location: GenericParamId) {
self.fmt.start_location_link_generic(location);
}
pub fn end_location_link(&mut self) {
self.fmt.end_location_link();
}
@@ -686,7 +692,9 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
ConstKind::Param(param) => {
let generics = generics(f.db, param.id.parent());
let param_data = &generics[param.id.local_id()];
f.start_location_link_generic(param.id.into());
write!(f, "{}", param_data.name().unwrap().display(f.db, f.edition()))?;
f.end_location_link();
Ok(())
}
ConstKind::Value(const_bytes) => render_const_scalar(
@@ -1489,6 +1497,7 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>)
match param_data {
TypeOrConstParamData::TypeParamData(p) => match p.provenance {
TypeParamProvenance::TypeParamList | TypeParamProvenance::TraitSelf => {
f.start_location_link_generic(param.id.into());
write!(
f,
"{}",
@@ -1496,7 +1505,8 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>)
.clone()
.unwrap_or_else(Name::missing)
.display(f.db, f.edition())
)?
)?;
f.end_location_link();
}
TypeParamProvenance::ArgumentImplTrait => {
let bounds = GenericPredicates::query_all(f.db, param.id.parent())
@@ -1519,7 +1529,9 @@ fn hir_fmt(&self, f @ &mut HirFormatter { db, .. }: &mut HirFormatter<'_, 'db>)
}
},
TypeOrConstParamData::ConstParamData(p) => {
f.start_location_link_generic(param.id.into());
write!(f, "{}", p.name.display(f.db, f.edition()))?;
f.end_location_link();
}
}
}
@@ -2031,7 +2043,9 @@ fn hir_fmt(&self, f: &mut HirFormatter<'_, 'db>) -> Result {
RegionKind::ReEarlyParam(param) => {
let generics = generics(f.db, param.id.parent);
let param_data = &generics[param.id.local_id];
f.start_location_link_generic(param.id.into());
write!(f, "{}", param_data.name.display(f.db, f.edition()))?;
f.end_location_link();
Ok(())
}
RegionKind::ReBound(BoundVarIndexKind::Bound(db), idx) => {
@@ -27,8 +27,8 @@
resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs},
signatures::{FunctionSignature, TraitFlags, TypeAliasFlags},
type_ref::{
ConstRef, LifetimeRefId, LiteralConstRef, PathId, TraitBoundModifier,
TraitRef as HirTraitRef, TypeBound, TypeRef, TypeRefId,
ConstRef, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound,
TypeRef, TypeRefId,
},
};
use hir_expand::name::Name;
@@ -281,21 +281,9 @@ pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -
hir_def::hir::Expr::Path(path) => {
self.path_to_const(path).unwrap_or_else(|| unknown_const(const_type))
}
hir_def::hir::Expr::Literal(literal) => intern_const_ref(
self.db,
&match *literal {
hir_def::hir::Literal::Float(_, _)
| hir_def::hir::Literal::String(_)
| hir_def::hir::Literal::ByteString(_)
| hir_def::hir::Literal::CString(_) => LiteralConstRef::Unknown,
hir_def::hir::Literal::Char(c) => LiteralConstRef::Char(c),
hir_def::hir::Literal::Bool(b) => LiteralConstRef::Bool(b),
hir_def::hir::Literal::Int(val, _) => LiteralConstRef::Int(val),
hir_def::hir::Literal::Uint(val, _) => LiteralConstRef::UInt(val),
},
const_type,
self.resolver.krate(),
),
hir_def::hir::Expr::Literal(literal) => {
intern_const_ref(self.db, literal, const_type, self.resolver.krate())
}
hir_def::hir::Expr::UnaryOp { expr: inner_expr, op: hir_def::hir::UnaryOp::Neg } => {
if let hir_def::hir::Expr::Literal(literal) = &self.store[*inner_expr] {
// Only handle negation for signed integers and floats
@@ -304,7 +292,7 @@ pub(crate) fn lower_const(&mut self, const_ref: ConstRef, const_type: Ty<'db>) -
if let Some(negated_literal) = literal.clone().negate() {
intern_const_ref(
self.db,
&negated_literal.into(),
&negated_literal,
const_type,
self.resolver.krate(),
)
@@ -1319,7 +1307,7 @@ fn type_for_struct_constructor(
db: &dyn HirDatabase,
def: StructId,
) -> Option<StoredEarlyBinder<StoredTy>> {
let struct_data = def.fields(db);
let struct_data = db.struct_signature(def);
match struct_data.shape {
FieldsShape::Record => None,
FieldsShape::Unit => Some(type_for_adt(db, def.into())),
@@ -609,12 +609,7 @@ fn collect(
map: &mut FxHashMap<SimplifiedType, Vec<ImplId>>,
) {
for (_module_id, module_data) in def_map.modules() {
for impl_id in module_data.scope.impls() {
let data = db.impl_signature(impl_id);
if data.target_trait.is_some() {
continue;
}
for impl_id in module_data.scope.inherent_impls() {
let interner = DbInterner::new_no_crate(db);
let self_ty = db.impl_self_ty(impl_id);
let self_ty = self_ty.instantiate_identity();
@@ -730,7 +725,11 @@ fn collect(
map: &mut FxHashMap<TraitId, OneTraitImplsBuilder>,
) {
for (_module_id, module_data) in def_map.modules() {
for impl_id in module_data.scope.impls() {
for impl_id in module_data.scope.trait_impls() {
let trait_ref = match db.impl_trait(impl_id) {
Some(tr) => tr.instantiate_identity(),
None => continue,
};
// Reservation impls should be ignored during trait resolution, so we never need
// them during type analysis. See rust-lang/rust#64631 for details.
//
@@ -742,10 +741,6 @@ fn collect(
{
continue;
}
let trait_ref = match db.impl_trait(impl_id) {
Some(tr) => tr.instantiate_identity(),
None => continue,
};
let self_ty = trait_ref.self_ty();
let interner = DbInterner::new_no_crate(db);
let entry = map.entry(trait_ref.def_id.0).or_default();
@@ -878,9 +878,11 @@ pub fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
self.tainted_by_errors.set(Some(e));
}
#[instrument(level = "debug", skip(self), ret)]
pub fn take_opaque_types(&self) -> Vec<(OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
self.inner.borrow_mut().opaque_type_storage.take_opaque_types().collect()
#[instrument(level = "debug", skip(self))]
pub fn take_opaque_types(
&self,
) -> impl IntoIterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> + use<'db> {
self.inner.borrow_mut().opaque_type_storage.take_opaque_types()
}
#[instrument(level = "debug", skip(self), ret)]
@@ -61,7 +61,7 @@ pub(crate) fn is_empty(&self) -> bool {
pub(crate) fn take_opaque_types(
&mut self,
) -> impl Iterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> {
) -> impl IntoIterator<Item = (OpaqueTypeKey<'db>, OpaqueHiddenType<'db>)> + use<'db> {
let OpaqueTypeStorage { opaque_types, duplicate_entries } = self;
std::mem::take(opaque_types).into_iter().chain(std::mem::take(duplicate_entries))
}
@@ -383,6 +383,11 @@ pub fn is_bool(self) -> bool {
matches!(self.kind(), TyKind::Bool)
}
#[inline]
pub fn is_char(self) -> bool {
matches!(self.kind(), TyKind::Char)
}
/// A scalar type is one that denotes an atomic datum, with no sub-components.
/// (A RawPtr is scalar because it represents a non-managed pointer, so its
/// contents are abstract to rustc.)
@@ -422,6 +427,11 @@ pub fn is_unit(self) -> bool {
matches!(self.kind(), TyKind::Tuple(tys) if tys.is_empty())
}
#[inline]
pub fn is_u8(self) -> bool {
matches!(self.kind(), TyKind::Uint(UintTy::U8))
}
#[inline]
pub fn is_raw_ptr(self) -> bool {
matches!(self.kind(), TyKind::RawPtr(..))
@@ -456,6 +466,14 @@ pub fn as_adt(self) -> Option<(AdtId, GenericArgs<'db>)> {
}
}
#[inline]
pub fn as_slice(self) -> Option<Ty<'db>> {
match self.kind() {
TyKind::Slice(ty) => Some(ty),
_ => None,
}
}
#[inline]
pub fn ty_vid(self) -> Option<TyVid> {
match self.kind() {
@@ -132,14 +132,13 @@ fn baz() -> i32 {
"trait_environment_query",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
"expr_scopes_shim",
"InferenceResult::for_body_",
"function_signature_shim",
"function_signature_with_source_map_shim",
"AttrFlags::query_",
"body_shim",
"body_with_source_map_shim",
"trait_environment_query",
@@ -149,6 +148,7 @@ fn baz() -> i32 {
"InferenceResult::for_body_",
"function_signature_shim",
"function_signature_with_source_map_shim",
"AttrFlags::query_",
"body_shim",
"body_with_source_map_shim",
"trait_environment_query",
@@ -197,13 +197,13 @@ fn baz() -> i32 {
"body_with_source_map_shim",
"body_shim",
"AttrFlags::query_",
"AttrFlags::query_",
"function_signature_with_source_map_shim",
"function_signature_shim",
"body_with_source_map_shim",
"body_shim",
"InferenceResult::for_body_",
"expr_scopes_shim",
"AttrFlags::query_",
"function_signature_with_source_map_shim",
"function_signature_shim",
"body_with_source_map_shim",
@@ -245,8 +245,6 @@ fn bar() -> f32 {
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@@ -284,9 +282,6 @@ pub struct NewStruct {
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@@ -324,8 +319,6 @@ fn bar() -> f32 {
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@@ -364,12 +357,6 @@ pub enum SomeEnum {
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"EnumVariants::of_",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@@ -407,8 +394,6 @@ fn bar() -> f32 {
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@@ -444,8 +429,6 @@ fn bar() -> f32 {
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@@ -487,9 +470,6 @@ pub struct SomeStruct {
"TraitImpls::for_crate_",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
]
"#]],
);
@@ -533,18 +513,6 @@ pub fn new(value: i32) -> Self {
"crate_local_def_map",
"TraitImpls::for_crate_",
"crate_lang_items",
"AttrFlags::query_",
"ImplItems::of_",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"impl_trait_with_diagnostics_query",
"impl_signature_shim",
"impl_signature_with_source_map_shim",
"impl_self_ty_with_diagnostics_query",
"struct_signature_shim",
"struct_signature_with_source_map_shim",
]
"#]],
);
@@ -616,8 +584,6 @@ fn main() {
"trait_environment_query",
"lang_items",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
@@ -630,18 +596,18 @@ fn main() {
"expr_scopes_shim",
"struct_signature_shim",
"struct_signature_with_source_map_shim",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
"value_ty_query",
"VariantFields::firewall_",
"VariantFields::query_",
"InherentImpls::for_crate_",
"impl_signature_shim",
"impl_signature_with_source_map_shim",
"callable_item_signature_query",
"TraitImpls::for_crate_and_deps_",
"TraitImpls::for_crate_",
"impl_trait_with_diagnostics_query",
"impl_signature_shim",
"impl_signature_with_source_map_shim",
"impl_self_ty_with_diagnostics_query",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
]
"#]],
@@ -710,9 +676,6 @@ fn main() {
"body_with_source_map_shim",
"body_shim",
"crate_lang_items",
"AttrFlags::query_",
"AttrFlags::query_",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
"GenericPredicates::query_with_diagnostics_",
"ImplTraits::return_type_impl_traits_",
@@ -722,15 +685,16 @@ fn main() {
"ImplTraits::return_type_impl_traits_",
"expr_scopes_shim",
"struct_signature_with_source_map_shim",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
"VariantFields::query_",
"InherentImpls::for_crate_",
"impl_signature_with_source_map_shim",
"impl_signature_shim",
"callable_item_signature_query",
"TraitImpls::for_crate_",
"impl_signature_with_source_map_shim",
"impl_signature_shim",
"impl_trait_with_diagnostics_query",
"impl_self_ty_with_diagnostics_query",
"AttrFlags::query_",
"GenericPredicates::query_with_diagnostics_",
]
"#]],
@@ -794,6 +794,8 @@ fn foo(params: &[i32]) {
fn box_pattern() {
check_infer(
r#"
#![feature(lang_items)]
pub struct Global;
#[lang = "owned_box"]
pub struct Box<T, A = Global>(T);
@@ -805,13 +807,13 @@ fn foo(params: Box<i32>) {
}
"#,
expect![[r#"
83..89 'params': Box<i32, Global>
101..155 '{ ... } }': ()
107..153 'match ... }': ()
113..119 'params': Box<i32, Global>
130..141 'box integer': Box<i32, Global>
134..141 'integer': i32
145..147 '{}': ()
108..114 'params': Box<i32, Global>
126..180 '{ ... } }': ()
132..178 'match ... }': ()
138..144 'params': Box<i32, Global>
155..166 'box integer': Box<i32, Global>
159..166 'integer': i32
170..172 '{}': ()
"#]],
);
check_infer(
@@ -831,7 +833,6 @@ fn foo(params: Box<i32>) {
76..122 'match ... }': ()
82..88 'params': Box<i32>
99..110 'box integer': Box<i32>
103..110 'integer': i32
114..116 '{}': ()
"#]],
);
@@ -1142,6 +1143,7 @@ fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {}
fn var_args() {
check_types(
r#"
#![feature(lang_items)]
#[lang = "va_list"]
pub struct VaListImpl<'f>;
fn my_fn(foo: ...) {}
@@ -1156,6 +1158,7 @@ fn my_fn2(bar: u32, foo: ...) {}
fn var_args_cond() {
check_types(
r#"
#![feature(lang_items)]
#[lang = "va_list"]
pub struct VaListImpl<'f>;
fn my_fn(bar: u32, #[cfg(FALSE)] foo: ..., #[cfg(not(FALSE))] foo: u32) {
@@ -2374,6 +2374,7 @@ fn rust_destruct_option_clone() {
check_types(
r#"
//- minicore: option, drop
#![feature(lang_items)]
fn test(o: &Option<i32>) {
o.my_clone();
//^^^^^^^^^^^^ Option<i32>
@@ -234,6 +234,7 @@ fn main() {
// toolchains <= 1.88.0, before sized-hierarchy.
check_no_mismatches(
r#"
#![feature(lang_items)]
#[lang = "sized"]
pub trait Sized {}
@@ -2702,6 +2702,8 @@ fn box_into_vec() {
check_infer(
r#"
//- /core.rs crate:core
#![feature(lang_items)]
#[lang = "sized"]
pub trait Sized {}
@@ -2745,22 +2747,22 @@ trait B{}
impl B for Astruct {}
"#,
expect![[r#"
614..618 'self': Box<[T], A>
647..679 '{ ... }': Vec<T, A>
693..863 '{ ...])); }': ()
703..706 'vec': Vec<i32, Global>
709..724 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
709..755 '<[_]>:...i32]))': Vec<i32, Global>
725..754 '#[rust...1i32])': Box<[i32; 1], Global>
747..753 '[1i32]': [i32; 1]
748..752 '1i32': i32
765..766 'v': Vec<Box<dyn B + 'static, Global>, Global>
786..803 '<[_]> ...to_vec': fn into_vec<Box<dyn B + '?, Global>, Global>(Box<[Box<dyn B + '?, Global>], Global>) -> Vec<Box<dyn B + '?, Global>, Global>
786..860 '<[_]> ...ct)]))': Vec<Box<dyn B + '?, Global>, Global>
804..859 '#[rust...uct)])': Box<[Box<dyn B + '?, Global>; 1], Global>
826..858 '[#[rus...ruct)]': [Box<dyn B + '?, Global>; 1]
827..857 '#[rust...truct)': Box<Astruct, Global>
849..856 'Astruct': Astruct
639..643 'self': Box<[T], A>
672..704 '{ ... }': Vec<T, A>
718..888 '{ ...])); }': ()
728..731 'vec': Vec<i32, Global>
734..749 '<[_]>::into_vec': fn into_vec<i32, Global>(Box<[i32], Global>) -> Vec<i32, Global>
734..780 '<[_]>:...i32]))': Vec<i32, Global>
750..779 '#[rust...1i32])': Box<[i32; 1], Global>
772..778 '[1i32]': [i32; 1]
773..777 '1i32': i32
790..791 'v': Vec<Box<dyn B + 'static, Global>, Global>
811..828 '<[_]> ...to_vec': fn into_vec<Box<dyn B + '?, Global>, Global>(Box<[Box<dyn B + '?, Global>], Global>) -> Vec<Box<dyn B + '?, Global>, Global>
811..885 '<[_]> ...ct)]))': Vec<Box<dyn B + '?, Global>, Global>
829..884 '#[rust...uct)])': Box<[Box<dyn B + '?, Global>; 1], Global>
851..883 '[#[rus...ruct)]': [Box<dyn B + '?, Global>; 1]
852..882 '#[rust...truct)': Box<Astruct, Global>
874..881 'Astruct': Astruct
"#]],
)
}
@@ -3647,6 +3649,8 @@ fn main() {
fn cstring_literals() {
check_types(
r#"
#![feature(lang_items)]
#[lang = "CStr"]
pub struct CStr;
@@ -41,7 +41,6 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Variances
)]
fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariancesOf {
tracing::debug!("variances_of(def={:?})", def);
let interner = DbInterner::new_no_crate(db);
match def {
GenericDefId::FunctionId(_) => (),
GenericDefId::AdtId(adt) => {
@@ -55,15 +54,17 @@ fn variances_of_query(db: &dyn HirDatabase, def: GenericDefId) -> StoredVariance
}
}
}
_ => return VariancesOf::empty(interner).store(),
_ => return VariancesOf::empty(DbInterner::new_no_crate(db)).store(),
}
let generics = generics(db, def);
let count = generics.len();
if count == 0 {
return VariancesOf::empty(interner).store();
return VariancesOf::empty(DbInterner::new_no_crate(db)).store();
}
let variances = Context { generics, variances: vec![Variance::Bivariant; count], db }.solve();
let variances =
Context { generics, variances: vec![Variance::Bivariant; count].into_boxed_slice(), db }
.solve();
VariancesOf::new_from_slice(&variances).store()
}
@@ -113,11 +114,11 @@ pub(crate) fn variances_of_cycle_initial(
struct Context<'db> {
db: &'db dyn HirDatabase,
generics: Generics,
variances: Vec<Variance>,
variances: Box<[Variance]>,
}
impl<'db> Context<'db> {
fn solve(mut self) -> Vec<Variance> {
fn solve(mut self) -> Box<[Variance]> {
tracing::debug!("solve(generics={:?})", self.generics);
match self.generics.def() {
GenericDefId::AdtId(adt) => {
@@ -330,7 +330,7 @@ fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
let (_body, source_map) = db.body_with_source_map(self.parent);
let src = source_map.label_syntax(self.label_id);
let root = src.file_syntax(db);
Some(src.map(|ast| ast.to_node(&root)))
src.map(|ast| ast.to_node(&root).left()).transpose()
}
}
+51 -10
View File
@@ -50,9 +50,9 @@
use hir_def::{
AdtId, AssocItemId, AssocItemLoc, BuiltinDeriveImplId, CallableDefId, ConstId, ConstParamId,
DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FunctionId, GenericDefId,
GenericParamId, HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup,
MacroExpander, MacroId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId,
TypeOrConstParamId, TypeParamId, UnionId,
HasModule, ImplId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander,
MacroId, StaticId, StructId, SyntheticSyntax, TupleId, TypeAliasId, TypeOrConstParamId,
TypeParamId, UnionId,
attrs::AttrFlags,
builtin_derive::BuiltinDeriveImplMethod,
expr_store::{ExpressionStoreDiagnostics, ExpressionStoreSourceMap},
@@ -150,7 +150,7 @@
visibility::Visibility,
// FIXME: This is here since some queries take it as input that are used
// outside of hir.
{ModuleDefId, TraitId},
{GenericParamId, ModuleDefId, TraitId},
},
hir_expand::{
EditionedFileId, ExpandResult, HirFileId, MacroCallId, MacroKind,
@@ -803,22 +803,21 @@ pub fn diagnostics<'db>(
emit_def_diagnostic(db, acc, diag, edition, loc.container.krate(db));
}
if impl_signature.target_trait.is_none()
&& !is_inherent_impl_coherent(db, def_map, impl_id)
{
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 !impl_def.check_orphan_rules(db) {
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_ = impl_def.trait_(db);
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() && impl_signature.target_trait.is_some();
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.
@@ -921,6 +920,48 @@ pub fn diagnostics<'db>(
}
}
// 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 def_map = crate_def_map(db, krate);
if def_map.is_unstable_feature_enabled(&sym::specialization)
|| def_map.is_unstable_feature_enabled(&sym::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 db.function_signature(*fn_).is_default() {
return false;
}
}
}
true
});
}
}
if !missing.is_empty() {
acc.push(
TraitImplMissingAssocItems {
@@ -1,6 +1,11 @@
use itertools::Itertools;
use syntax::{
SyntaxKind::WHITESPACE,
ast::{AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make},
ast::{
AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make,
syntax_factory::SyntaxFactory,
},
syntax_editor::Element,
};
use crate::{AssistContext, AssistId, Assists};
@@ -131,8 +136,10 @@ pub(crate) fn move_arm_cond_to_match_guard(
AssistId::refactor_rewrite("move_arm_cond_to_match_guard"),
"Move condition to match guard",
replace_node.text_range(),
|edit| {
edit.delete(match_arm.syntax().text_range());
|builder| {
let make = SyntaxFactory::without_mappings();
let mut replace_arms = vec![];
// Dedent if if_expr is in a BlockExpr
let dedent = if needs_dedent {
cov_mark::hit!(move_guard_ifelse_in_block);
@@ -141,47 +148,30 @@ pub(crate) fn move_arm_cond_to_match_guard(
cov_mark::hit!(move_guard_ifelse_else_block);
0
};
let then_arm_end = match_arm.syntax().text_range().end();
let indent_level = match_arm.indent_level();
let spaces = indent_level;
let mut first = true;
for (cond, block) in conds_blocks {
if !first {
edit.insert(then_arm_end, format!("\n{spaces}"));
} else {
first = false;
}
let guard = format!("{match_pat} if {cond} => ");
edit.insert(then_arm_end, guard);
let only_expr = block.statements().next().is_none();
match &block.tail_expr() {
Some(then_expr) if only_expr => {
edit.insert(then_arm_end, then_expr.syntax().text());
edit.insert(then_arm_end, ",");
}
_ => {
let to_insert = block.dedent(dedent.into()).syntax().text();
edit.insert(then_arm_end, to_insert)
}
}
let expr = match block.tail_expr() {
Some(then_expr) if only_expr => then_expr,
_ => block.dedent(dedent.into()).into(),
};
let guard = make.match_guard(cond);
let new_arm = make.match_arm(match_pat.clone(), Some(guard), expr);
replace_arms.push(new_arm);
}
if let Some(e) = tail {
if let Some(block) = tail {
cov_mark::hit!(move_guard_ifelse_else_tail);
let guard = format!("\n{spaces}{match_pat} => ");
edit.insert(then_arm_end, guard);
let only_expr = e.statements().next().is_none();
match &e.tail_expr() {
let only_expr = block.statements().next().is_none();
let expr = match block.tail_expr() {
Some(expr) if only_expr => {
cov_mark::hit!(move_guard_ifelse_expr_only);
edit.insert(then_arm_end, expr.syntax().text());
edit.insert(then_arm_end, ",");
expr
}
_ => {
let to_insert = e.dedent(dedent.into()).syntax().text();
edit.insert(then_arm_end, to_insert)
}
}
_ => block.dedent(dedent.into()).into(),
};
let new_arm = make.match_arm(match_pat, None, expr);
replace_arms.push(new_arm);
} else {
// There's no else branch. Add a pattern without guard, unless the following match
// arm is `_ => ...`
@@ -193,9 +183,21 @@ pub(crate) fn move_arm_cond_to_match_guard(
{
cov_mark::hit!(move_guard_ifelse_has_wildcard);
}
_ => edit.insert(then_arm_end, format!("\n{spaces}{match_pat} => {{}}")),
_ => {
let block_expr = make.expr_empty_block().into();
replace_arms.push(make.match_arm(match_pat, None, block_expr));
}
}
}
let mut edit = builder.make_editor(match_arm.syntax());
let newline = make.whitespace(&format!("\n{indent_level}"));
let replace_arms = replace_arms.iter().map(|it| it.syntax().syntax_element());
let replace_arms = Itertools::intersperse(replace_arms, newline.syntax_element());
edit.replace_with_many(match_arm.syntax(), replace_arms.collect());
builder.add_file_edits(ctx.vfs_file_id(), edit);
},
)
}
@@ -781,8 +781,8 @@ fn main() {
}
"#,
expect![[r#"
fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED
ct SPECIAL_CONST (use dep::test_mod::TestTrait) u8 DEPRECATED
fn weird_function() (use dep::test_mod::TestTrait) fn() DEPRECATED
me random_method() (use dep::test_mod::TestTrait) fn(&self) DEPRECATED
"#]],
);
@@ -44,7 +44,7 @@
/// `vec.as_slice()` -> `slice`
/// `args.into_config()` -> `config`
/// `bytes.to_vec()` -> `vec`
const USELESS_METHOD_PREFIXES: &[&str] = &["into_", "as_", "to_"];
const USELESS_METHOD_PREFIXES: &[&str] = &["try_into_", "into_", "as_", "to_"];
/// Useless methods that are stripped from expression
///
@@ -64,6 +64,7 @@ unsafe impl Unsafe for () {}
fn drop_may_dangle() {
check_diagnostics(
r#"
#![feature(lang_items)]
#[lang = "drop"]
trait Drop {}
struct S<T>;
@@ -156,4 +156,22 @@ impl Trait for dyn OtherTrait {}
"#,
)
}
#[test]
fn no_false_positive_on_specialization() {
check_diagnostics(
r#"
#![feature(specialization)]
pub trait Foo {
fn foo();
}
impl<T> Foo for T {
default fn foo() {}
}
impl Foo for bool {}
"#,
);
}
}
@@ -4089,6 +4089,7 @@ fn foo() {
let fo$0o = async { S };
}
//- /core.rs crate:core
#![feature(lang_items)]
pub mod future {
#[lang = "future_trait"]
pub trait Future {}
@@ -5,8 +5,8 @@
use either::Either;
use hir::{
ClosureStyle, DisplayTarget, EditionedFileId, HasVisibility, HirDisplay, HirDisplayError,
HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym,
ClosureStyle, DisplayTarget, EditionedFileId, GenericParam, GenericParamId, HasVisibility,
HirDisplay, HirDisplayError, HirWrite, InRealFile, ModuleDef, ModuleDefId, Semantics, sym,
};
use ide_db::{
FileRange, MiniCore, RootDatabase, famous_defs::FamousDefs, text_edit::TextEditBuilder,
@@ -709,6 +709,21 @@ fn start_location_link(&mut self, def: ModuleDefId) {
});
}
fn start_location_link_generic(&mut self, def: GenericParamId) {
never!(self.location.is_some(), "location link is already started");
self.make_new_part();
self.location = Some(if self.resolve {
LazyProperty::Lazy
} else {
LazyProperty::Computed({
let Some(location) = GenericParam::from(def).try_to_nav(self.sema) else { return };
let location = location.call_site();
FileRange { file_id: location.file_id, range: location.focus_or_full_range() }
})
});
}
fn end_location_link(&mut self) {
self.make_new_part();
}
@@ -183,7 +183,8 @@ mod tests {
use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig};
use crate::inlay_hints::tests::{
DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_no_edit, check_with_config,
DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_expect, check_no_edit,
check_with_config,
};
#[track_caller]
@@ -1255,4 +1256,130 @@ fn f(&self) {
"#,
);
}
#[test]
fn type_param_inlay_hint_has_location_link() {
check_expect(
InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
r#"
fn identity<T>(t: T) -> T {
let x = t;
x
}
"#,
expect![[r#"
[
(
36..37,
[
InlayHintLabelPart {
text: "T",
linked_location: Some(
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 12..13,
},
),
),
tooltip: "",
},
],
),
]
"#]],
);
}
#[test]
fn const_param_inlay_hint_has_location_link() {
check_expect(
InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
r#"
fn f<const N: usize>() {
let x = [0; N];
}
"#,
expect![[r#"
[
(
33..34,
[
"[i32; ",
InlayHintLabelPart {
text: "N",
linked_location: Some(
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 11..12,
},
),
),
tooltip: "",
},
"]",
],
),
]
"#]],
);
}
#[test]
fn lifetime_param_inlay_hint_has_location_link() {
check_expect(
InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
r#"
struct S<'lt>(*mut &'lt ());
fn f<'a>() {
let x = S::<'a>(loop {});
}
"#,
expect![[r#"
[
(
51..52,
[
InlayHintLabelPart {
text: "S",
linked_location: Some(
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 7..8,
},
),
),
tooltip: "",
},
"<",
InlayHintLabelPart {
text: "'a",
linked_location: Some(
Computed(
FileRangeWrapper {
file_id: FileId(
0,
),
range: 35..37,
},
),
),
tooltip: "",
},
">",
],
),
]
"#]],
);
}
}
@@ -143,7 +143,7 @@ fn foo<T>() {}
file_id: FileId(
1,
),
range: 446..451,
range: 470..475,
},
),
),
@@ -38,7 +38,8 @@
};
use crate::{
Analysis, FilePosition, HighlightedRange, NavigationTarget, TryToNav, highlight_related,
Analysis, FilePosition, HighlightedRange, NavigationTarget, TryToNav,
doc_links::token_as_doc_comment, highlight_related,
};
/// Result of a reference search operation.
@@ -211,6 +212,13 @@ pub(crate) fn find_defs(
syntax: &SyntaxNode,
offset: TextSize,
) -> Option<Vec<Definition>> {
if let Some(token) = syntax.token_at_offset(offset).left_biased()
&& let Some(doc_comment) = token_as_doc_comment(&token)
{
return doc_comment
.get_definition_with_descend_at(sema, offset, |def, _, _| Some(vec![def]));
}
let token = syntax.token_at_offset(offset).find(|t| {
matches!(
t.kind(),
@@ -785,6 +793,23 @@ fn main() {
);
}
#[test]
fn test_find_all_refs_in_comments() {
check(
r#"
struct Foo;
/// $0[`Foo`] is just above
struct Bar;
"#,
expect![[r#"
Foo Struct FileId(0) 0..11 7..10
(no references)
"#]],
);
}
#[test]
fn search_filters_by_range() {
check(
@@ -334,7 +334,7 @@ pub const fn new() -> Self {
impl<T: Internable + ?Sized> InternStorage<T> {
pub(crate) fn get(&self) -> &InternMap<T> {
self.map.get_or_init(DashMap::default)
self.map.get_or_init(|| DashMap::with_capacity_and_hasher(1024, Default::default()))
}
}
@@ -292,7 +292,12 @@ pub const fn new() -> Self {
impl<T: SliceInternable> InternSliceStorage<T> {
pub(crate) fn get(&self) -> &InternMap<T> {
self.map.get_or_init(DashMap::default)
self.map.get_or_init(|| {
DashMap::with_capacity_and_hasher(
(64 * 1024) / std::mem::size_of::<T::SliceType>(),
Default::default(),
)
})
}
}
@@ -297,6 +297,7 @@ pub(super) fn prefill() -> DashMap<Symbol, (), BuildHasherDefault<FxHasher>> {
iterator,
keyword,
lang,
lang_items,
le,
Left,
len,
@@ -11,15 +11,16 @@
use std::{any::Any, collections::hash_map::Entry, mem, path::Path, sync};
use crossbeam_channel::{Receiver, unbounded};
use hir_expand::proc_macro::{
ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacroLoadResult,
ProcMacrosBuilder,
use hir_expand::{
db::ExpandDatabase,
proc_macro::{
ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacroLoadResult,
ProcMacrosBuilder,
},
};
use ide_db::{
ChangeWithProcMacros, FxHashMap, RootDatabase,
base_db::{
CrateGraphBuilder, Env, ProcMacroLoadingError, SourceDatabase, SourceRoot, SourceRootId,
},
base_db::{CrateGraphBuilder, Env, ProcMacroLoadingError, SourceRoot, SourceRootId},
prime_caches,
};
use itertools::Itertools;
@@ -31,7 +32,8 @@
},
};
use project_model::{CargoConfig, PackageRoot, ProjectManifest, ProjectWorkspace};
use span::Span;
use span::{Span, SpanAnchor, SyntaxContext};
use tt::{TextRange, TextSize};
use vfs::{
AbsPath, AbsPathBuf, FileId, VfsPath,
file_set::FileSetConfig,
@@ -530,7 +532,7 @@ fn expander_to_proc_macro(
impl ProcMacroExpander for Expander {
fn expand(
&self,
db: &dyn SourceDatabase,
db: &dyn ExpandDatabase,
subtree: &tt::TopSubtree,
attrs: Option<&tt::TopSubtree>,
env: &Env,
@@ -540,11 +542,44 @@ fn expand(
current_dir: String,
) -> Result<tt::TopSubtree, ProcMacroExpansionError> {
let mut cb = |req| match req {
SubRequest::SourceText { file_id, start, end } => {
let file = FileId::from_raw(file_id);
let text = db.file_text(file).text(db);
let slice = text.get(start as usize..end as usize).map(ToOwned::to_owned);
Ok(SubResponse::SourceTextResult { text: slice })
SubRequest::LocalFilePath { file_id } => {
let file_id = FileId::from_raw(file_id);
let source_root_id = db.file_source_root(file_id).source_root_id(db);
let source_root = db.source_root(source_root_id).source_root(db);
let name = source_root
.path_for_file(&file_id)
.and_then(|path| path.as_path())
.map(|path| path.to_string());
Ok(SubResponse::LocalFilePathResult { name })
}
SubRequest::SourceText { file_id, ast_id, start, end } => {
let ast_id = span::ErasedFileAstId::from_raw(ast_id);
let editioned_file_id = span::EditionedFileId::from_raw(file_id);
let span = Span {
range: TextRange::new(TextSize::from(start), TextSize::from(end)),
anchor: SpanAnchor { file_id: editioned_file_id, ast_id },
ctx: SyntaxContext::root(editioned_file_id.edition()),
};
let range = db.resolve_span(span);
let source = db.file_text(range.file_id.file_id(db)).text(db);
let text = source
.get(usize::from(range.range.start())..usize::from(range.range.end()))
.map(ToOwned::to_owned);
Ok(SubResponse::SourceTextResult { text })
}
SubRequest::FilePath { file_id } => {
let file_id = FileId::from_raw(file_id);
let source_root_id = db.file_source_root(file_id).source_root_id(db);
let source_root = db.source_root(source_root_id).source_root(db);
let name = source_root
.path_for_file(&file_id)
.and_then(|path| path.as_path())
.map(|path| path.to_string())
.unwrap_or_default();
Ok(SubResponse::FilePathResult { name })
}
};
match self.0.expand(
@@ -10,12 +10,16 @@
#[derive(Debug, Serialize, Deserialize)]
pub enum SubRequest {
SourceText { file_id: u32, start: u32, end: u32 },
FilePath { file_id: u32 },
SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 },
LocalFilePath { file_id: u32 },
}
#[derive(Debug, Serialize, Deserialize)]
pub enum SubResponse {
FilePathResult { name: String },
SourceTextResult { text: Option<String> },
LocalFilePathResult { name: Option<String> },
}
#[derive(Debug, Serialize, Deserialize)]
@@ -18,6 +18,7 @@ clap = {version = "4.5.42", default-features = false, features = ["std"]}
[features]
default = []
# default = ["sysroot-abi"]
sysroot-abi = ["proc-macro-srv/sysroot-abi", "proc-macro-api/sysroot-abi"]
in-rust-tree = ["proc-macro-srv/in-rust-tree", "sysroot-abi"]
@@ -0,0 +1,65 @@
# proc-macro-srv-cli
A standalone binary for the `proc-macro-srv` crate that provides procedural macro expansion for rust-analyzer.
## Overview
rust-analyzer uses a RPC (via stdio) client-server architecture for procedural macro expansion. This is necessary because:
1. Proc macros are dynamic libraries that can segfault, bringing down the entire process, so running them out of process allows rust-analyzer to recover from fatal errors.
2. Proc macro dylibs are compiled against a specific rustc version and require matching internal APIs to load and execute, as such having this binary shipped as a rustup component allows us to always match the rustc version irrespective of the rust-analyzer version used.
## The `sysroot-abi` Feature
**The `sysroot-abi` feature is required for the binary to actually function.** Without it, the binary will return an error:
```
proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function
```
This feature is necessary because the proc-macro server needs access to unstable rustc internals (`proc_macro_internals`, `proc_macro_diagnostic`, `proc_macro_span`) which are only available on nightly or with `RUSTC_BOOTSTRAP=1`.
rust-analyzer is a stable toolchain project though, so the feature flag is used to have it remain compilable on stable by default.
### Building
```bash
# Using nightly toolchain
cargo build -p proc-macro-srv-cli --features sysroot-abi
# Or with RUSTC_BOOTSTRAP on stable
RUSTC_BOOTSTRAP=1 cargo build -p proc-macro-srv-cli --features sysroot-abi
```
### Installing the proc-macro server
For local testing purposes, you can install the proc-macro server using the xtask command:
```bash
# Recommended: use the xtask command
cargo xtask install --proc-macro-server
```
## Testing
```bash
cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api
```
The tests use a test proc macro dylib built by the `proc-macro-test` crate, which compiles a small proc macro implementation during build time.
**Note**: Tests only compile on nightly toolchains (or with `RUSTC_BOOTSTRAP=1`).
## Usage
The binary requires the `RUST_ANALYZER_INTERNALS_DO_NOT_USE` environment variable to be set. This is intentional—the binary is an implementation detail of rust-analyzer and its API is still unstable:
```bash
RUST_ANALYZER_INTERNALS_DO_NOT_USE=1 rust-analyzer-proc-macro-srv --version
```
## Related Crates
- `proc-macro-srv`: The core server library that handles loading dylibs and expanding macros, but not the RPC protocol.
- `proc-macro-api`: The client library used by rust-analyzer to communicate with this server as well as the protocol definitions.
- `proc-macro-test`: Test harness with sample proc macros for testing
- `proc-macro-srv-cli`: The actual server binary that handles the RPC protocol.
@@ -81,7 +81,6 @@ fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::Pro
}
bidirectional::Request::ApiVersionCheck {} => {
// bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION).write::<_, C>(stdout)
send_response::<C>(
&stdout,
bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION),
@@ -167,28 +166,57 @@ struct ProcMacroClientHandle<'a, C: Codec> {
buf: &'a mut C::Buf,
}
impl<C: Codec> proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> {
fn source_text(&mut self, file_id: u32, start: u32, end: u32) -> Option<String> {
let req = bidirectional::BidirectionalMessage::SubRequest(
bidirectional::SubRequest::SourceText { file_id, start, end },
);
impl<'a, C: Codec> ProcMacroClientHandle<'a, C> {
fn roundtrip(
&mut self,
req: bidirectional::SubRequest,
) -> Option<bidirectional::BidirectionalMessage> {
let msg = bidirectional::BidirectionalMessage::SubRequest(req);
if req.write::<_, C>(&mut self.stdout.lock()).is_err() {
if msg.write::<_, C>(&mut self.stdout.lock()).is_err() {
return None;
}
let msg = match bidirectional::BidirectionalMessage::read::<_, C>(
&mut self.stdin.lock(),
self.buf,
) {
Ok(Some(msg)) => msg,
_ => return None,
};
match bidirectional::BidirectionalMessage::read::<_, C>(&mut self.stdin.lock(), self.buf) {
Ok(Some(msg)) => Some(msg),
_ => None,
}
}
}
match msg {
bidirectional::BidirectionalMessage::SubResponse(
impl<C: Codec> proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> {
fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String {
match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) {
Some(bidirectional::BidirectionalMessage::SubResponse(
bidirectional::SubResponse::FilePathResult { name },
)) => name,
_ => String::new(),
}
}
fn source_text(
&mut self,
proc_macro_srv::span::Span { range, anchor, ctx: _ }: proc_macro_srv::span::Span,
) -> Option<String> {
match self.roundtrip(bidirectional::SubRequest::SourceText {
file_id: anchor.file_id.as_u32(),
ast_id: anchor.ast_id.into_raw(),
start: range.start().into(),
end: range.end().into(),
}) {
Some(bidirectional::BidirectionalMessage::SubResponse(
bidirectional::SubResponse::SourceTextResult { text },
) => text,
)) => text,
_ => None,
}
}
fn local_file(&mut self, file_id: proc_macro_srv::span::FileId) -> Option<String> {
match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id: file_id.index() })
{
Some(bidirectional::BidirectionalMessage::SubResponse(
bidirectional::SubResponse::LocalFilePathResult { name },
)) => name,
_ => None,
}
}
@@ -53,6 +53,7 @@
pub use crate::server_impl::token_id::SpanId;
pub use proc_macro::Delimiter;
pub use span;
pub use crate::bridge::*;
pub use crate::server_impl::literal_from_str;
@@ -94,7 +95,9 @@ pub fn join_spans(&self, first: Span, second: Span) -> Option<Span> {
pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send);
pub trait ProcMacroClientInterface {
fn source_text(&mut self, file_id: u32, start: u32, end: u32) -> Option<String>;
fn file(&mut self, file_id: span::FileId) -> String;
fn source_text(&mut self, span: Span) -> Option<String>;
fn local_file(&mut self, file_id: span::FileId) -> Option<String>;
}
const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024;
@@ -127,13 +127,11 @@ impl server::Span for RaSpanServer<'_> {
fn debug(&mut self, span: Self::Span) -> String {
format!("{:?}", span)
}
fn file(&mut self, _: Self::Span) -> String {
// FIXME
String::new()
fn file(&mut self, span: Self::Span) -> String {
self.callback.as_mut().map(|cb| cb.file(span.anchor.file_id.file_id())).unwrap_or_default()
}
fn local_file(&mut self, _: Self::Span) -> Option<String> {
// FIXME
None
fn local_file(&mut self, span: Self::Span) -> Option<String> {
self.callback.as_mut().and_then(|cb| cb.local_file(span.anchor.file_id.file_id()))
}
fn save_span(&mut self, _span: Self::Span) -> usize {
// FIXME, quote is incompatible with third-party tools
@@ -152,11 +150,7 @@ fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
/// See PR:
/// https://github.com/rust-lang/rust/pull/55780
fn source_text(&mut self, span: Self::Span) -> Option<String> {
let file_id = span.anchor.file_id;
let start: u32 = span.range.start().into();
let end: u32 = span.range.end().into();
self.callback.as_mut()?.source_text(file_id.file_id().index(), start, end)
self.callback.as_mut()?.source_text(span)
}
fn parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
@@ -16,6 +16,9 @@
extern crate ra_ap_rustc_type_ir as rustc_type_ir;
/*
If you bump this, grep for `FIXME(MINIMUM_SUPPORTED_TOOLCHAIN_VERSION)` to check for old support code we can drop
*/
/// Any toolchain less than this version will likely not work with rust-analyzer built from this revision.
pub const MINIMUM_SUPPORTED_TOOLCHAIN_VERSION: semver::Version = semver::Version {
major: 1,
@@ -19,9 +19,8 @@
//! # The Call-site Hierarchy
//!
//! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer.
use std::fmt;
use crate::Edition;
use std::fmt;
/// A syntax context describes a hierarchy tracking order of macro definitions.
#[cfg(feature = "salsa")]
@@ -738,7 +738,7 @@ fn parse_crate(
impl ProcMacroExpander for IdentityProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
subtree: &TopSubtree,
_: Option<&TopSubtree>,
_: &Env,
@@ -761,7 +761,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for Issue18089ProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
subtree: &TopSubtree,
_: Option<&TopSubtree>,
_: &Env,
@@ -797,7 +797,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
_: &TopSubtree,
attrs: Option<&TopSubtree>,
_: &Env,
@@ -821,7 +821,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for Issue18840ProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
fn_: &TopSubtree,
_: Option<&TopSubtree>,
_: &Env,
@@ -858,7 +858,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for MirrorProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
input: &TopSubtree,
_: Option<&TopSubtree>,
_: &Env,
@@ -897,7 +897,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for ShortenProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
input: &TopSubtree,
_: Option<&TopSubtree>,
_: &Env,
@@ -942,7 +942,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for Issue17479ProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
subtree: &TopSubtree,
_: Option<&TopSubtree>,
_: &Env,
@@ -973,7 +973,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for Issue18898ProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
subtree: &TopSubtree,
_: Option<&TopSubtree>,
_: &Env,
@@ -1027,7 +1027,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for DisallowCfgProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
subtree: &TopSubtree,
_: Option<&TopSubtree>,
_: &Env,
@@ -1059,7 +1059,7 @@ fn eq_dyn(&self, other: &dyn ProcMacroExpander) -> bool {
impl ProcMacroExpander for GenerateSuffixedTypeProcMacroExpander {
fn expand(
&self,
_: &dyn SourceDatabase,
_: &dyn ExpandDatabase,
subtree: &TopSubtree,
_attrs: Option<&TopSubtree>,
_env: &Env,
@@ -80,6 +80,7 @@
//! offset_of:
#![rustc_coherence_is_core]
#![feature(lang_items)]
pub mod marker {
// region:sized
+3 -3
View File
@@ -5584,9 +5584,9 @@
}
},
"node_modules/qs": {
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
"version": "6.14.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {