mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 23:34:48 +00:00
Auto merge of #100304 - matthiaskrgr:rollup-gs56vlw, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #100163 (Refactor: remove an unnecessary string search) - #100212 (Remove more Clean trait implementations) - #100238 (Further improve error message for E0081) - #100268 (Add regression test for #79148) - #100294 (Update Duration::as_secs doc to point to as_secs_f64/32 for including fractional part) - #100303 (⬆️ rust-analyzer) Failed merges: - #100281 (Remove more Clean trait implementations) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
5af97e8b0b
@ -32,7 +32,6 @@ use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
|||||||
use rustc_trait_selection::traits::{self, ObligationCtxt};
|
use rustc_trait_selection::traits::{self, ObligationCtxt};
|
||||||
use rustc_ty_utils::representability::{self, Representability};
|
use rustc_ty_utils::representability::{self, Representability};
|
||||||
|
|
||||||
use std::iter;
|
|
||||||
use std::ops::ControlFlow;
|
use std::ops::ControlFlow;
|
||||||
|
|
||||||
pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
|
pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
|
||||||
@ -1494,76 +1493,109 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len());
|
detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);
|
||||||
// This tracks the previous variant span (in the loop) incase we need it for diagnostics
|
|
||||||
let mut prev_variant_span: Span = DUMMY_SP;
|
|
||||||
for ((_, discr), v) in iter::zip(def.discriminants(tcx), vs) {
|
|
||||||
// Check for duplicate discriminant values
|
|
||||||
if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) {
|
|
||||||
let variant_did = def.variant(VariantIdx::new(i)).def_id;
|
|
||||||
let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local());
|
|
||||||
let variant_i = tcx.hir().expect_variant(variant_i_hir_id);
|
|
||||||
let i_span = match variant_i.disr_expr {
|
|
||||||
Some(ref expr) => tcx.hir().span(expr.hir_id),
|
|
||||||
None => tcx.def_span(variant_did),
|
|
||||||
};
|
|
||||||
let span = match v.disr_expr {
|
|
||||||
Some(ref expr) => tcx.hir().span(expr.hir_id),
|
|
||||||
None => v.span,
|
|
||||||
};
|
|
||||||
let display_discr = format_discriminant_overflow(tcx, v, discr);
|
|
||||||
let display_discr_i = format_discriminant_overflow(tcx, variant_i, disr_vals[i]);
|
|
||||||
let no_disr = v.disr_expr.is_none();
|
|
||||||
let mut err = struct_span_err!(
|
|
||||||
tcx.sess,
|
|
||||||
sp,
|
|
||||||
E0081,
|
|
||||||
"discriminant value `{}` assigned more than once",
|
|
||||||
discr,
|
|
||||||
);
|
|
||||||
|
|
||||||
err.span_label(i_span, format!("first assignment of {display_discr_i}"));
|
|
||||||
err.span_label(span, format!("second assignment of {display_discr}"));
|
|
||||||
|
|
||||||
if no_disr {
|
|
||||||
err.span_label(
|
|
||||||
prev_variant_span,
|
|
||||||
format!(
|
|
||||||
"assigned discriminant for `{}` was incremented from this discriminant",
|
|
||||||
v.ident
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
err.emit();
|
|
||||||
}
|
|
||||||
|
|
||||||
disr_vals.push(discr);
|
|
||||||
prev_variant_span = v.span;
|
|
||||||
}
|
|
||||||
|
|
||||||
check_representable(tcx, sp, def_id);
|
check_representable(tcx, sp, def_id);
|
||||||
check_transparent(tcx, sp, def);
|
check_transparent(tcx, sp, def);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In the case that a discriminant is both a duplicate and an overflowing literal,
|
/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
|
||||||
/// we insert both the assigned discriminant and the literal it overflowed from into the formatted
|
fn detect_discriminant_duplicate<'tcx>(
|
||||||
/// output. Otherwise we format the discriminant normally.
|
|
||||||
fn format_discriminant_overflow<'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
variant: &hir::Variant<'_>,
|
mut discrs: Vec<(VariantIdx, Discr<'tcx>)>,
|
||||||
dis: Discr<'tcx>,
|
vs: &'tcx [hir::Variant<'tcx>],
|
||||||
) -> String {
|
self_span: Span,
|
||||||
if let Some(expr) = &variant.disr_expr {
|
) {
|
||||||
let body = &tcx.hir().body(expr.body).value;
|
// Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate.
|
||||||
if let hir::ExprKind::Lit(lit) = &body.kind
|
// Here `idx` refers to the order of which the discriminant appears, and its index in `vs`
|
||||||
|
let report = |dis: Discr<'tcx>,
|
||||||
|
idx: usize,
|
||||||
|
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>| {
|
||||||
|
let var = &vs[idx]; // HIR for the duplicate discriminant
|
||||||
|
let (span, display_discr) = match var.disr_expr {
|
||||||
|
Some(ref expr) => {
|
||||||
|
// In the case the discriminant is both a duplicate and overflowed, let the user know
|
||||||
|
if let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind
|
||||||
&& let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
|
&& let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
|
||||||
&& dis.val != *lit_value
|
&& *lit_value != dis.val
|
||||||
{
|
{
|
||||||
return format!("`{dis}` (overflowed from `{lit_value}`)");
|
(tcx.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)"))
|
||||||
|
// Otherwise, format the value as-is
|
||||||
|
} else {
|
||||||
|
(tcx.hir().span(expr.hir_id), format!("`{dis}`"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// At this point we know this discriminant is a duplicate, and was not explicitly
|
||||||
|
// assigned by the user. Here we iterate backwards to fetch the HIR for the last
|
||||||
|
// explictly assigned discriminant, and letting the user know that this was the
|
||||||
|
// increment startpoint, and how many steps from there leading to the duplicate
|
||||||
|
if let Some((n, hir::Variant { span, ident, .. })) =
|
||||||
|
vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some())
|
||||||
|
{
|
||||||
|
let ve_ident = var.ident;
|
||||||
|
let n = n + 1;
|
||||||
|
let sp = if n > 1 { "variants" } else { "variant" };
|
||||||
|
|
||||||
|
err.span_label(
|
||||||
|
*span,
|
||||||
|
format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
(vs[idx].span, format!("`{dis}`"))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
err.span_label(span, format!("{display_discr} assigned here"));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Here we loop through the discriminants, comparing each discriminant to another.
|
||||||
|
// When a duplicate is detected, we instatiate an error and point to both
|
||||||
|
// initial and duplicate value. The duplicate discriminant is then discarded by swapping
|
||||||
|
// it with the last element and decrementing the `vec.len` (which is why we have to evaluate
|
||||||
|
// `discrs.len()` anew every iteration, and why this could be tricky to do in a functional
|
||||||
|
// style as we are mutating `discrs` on the fly).
|
||||||
|
let mut i = 0;
|
||||||
|
while i < discrs.len() {
|
||||||
|
let hir_var_i_idx = discrs[i].0.index();
|
||||||
|
let mut error: Option<DiagnosticBuilder<'_, _>> = None;
|
||||||
|
|
||||||
|
let mut o = i + 1;
|
||||||
|
while o < discrs.len() {
|
||||||
|
let hir_var_o_idx = discrs[o].0.index();
|
||||||
|
|
||||||
|
if discrs[i].1.val == discrs[o].1.val {
|
||||||
|
let err = error.get_or_insert_with(|| {
|
||||||
|
let mut ret = struct_span_err!(
|
||||||
|
tcx.sess,
|
||||||
|
self_span,
|
||||||
|
E0081,
|
||||||
|
"discriminant value `{}` assigned more than once",
|
||||||
|
discrs[i].1,
|
||||||
|
);
|
||||||
|
|
||||||
|
report(discrs[i].1, hir_var_i_idx, &mut ret);
|
||||||
|
|
||||||
|
ret
|
||||||
|
});
|
||||||
|
|
||||||
|
report(discrs[o].1, hir_var_o_idx, err);
|
||||||
|
|
||||||
|
// Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty
|
||||||
|
discrs[o] = *discrs.last().unwrap();
|
||||||
|
discrs.pop();
|
||||||
|
} else {
|
||||||
|
o += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
format!("`{dis}`")
|
if let Some(mut e) = error {
|
||||||
|
e.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn check_type_params_are_used<'tcx>(
|
pub(super) fn check_type_params_are_used<'tcx>(
|
||||||
|
@ -50,7 +50,6 @@ use rustc_span::hygiene::DesugaringKind;
|
|||||||
use rustc_span::lev_distance::find_best_match_for_name;
|
use rustc_span::lev_distance::find_best_match_for_name;
|
||||||
use rustc_span::source_map::{Span, Spanned};
|
use rustc_span::source_map::{Span, Spanned};
|
||||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||||
use rustc_span::{BytePos, Pos};
|
|
||||||
use rustc_target::spec::abi::Abi::RustIntrinsic;
|
use rustc_target::spec::abi::Abi::RustIntrinsic;
|
||||||
use rustc_trait_selection::infer::InferCtxtExt;
|
use rustc_trait_selection::infer::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::{self, ObligationCauseCode};
|
use rustc_trait_selection::traits::{self, ObligationCauseCode};
|
||||||
@ -2398,13 +2397,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
expr,
|
expr,
|
||||||
Some(span),
|
Some(span),
|
||||||
);
|
);
|
||||||
} else {
|
} else if let ty::RawPtr(ty_and_mut) = expr_t.kind()
|
||||||
let mut found = false;
|
|
||||||
|
|
||||||
if let ty::RawPtr(ty_and_mut) = expr_t.kind()
|
|
||||||
&& let ty::Adt(adt_def, _) = ty_and_mut.ty.kind()
|
&& let ty::Adt(adt_def, _) = ty_and_mut.ty.kind()
|
||||||
{
|
&& let ExprKind::Field(base_expr, _) = expr.kind
|
||||||
if adt_def.variants().len() == 1
|
&& adt_def.variants().len() == 1
|
||||||
&& adt_def
|
&& adt_def
|
||||||
.variants()
|
.variants()
|
||||||
.iter()
|
.iter()
|
||||||
@ -2414,22 +2410,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
.iter()
|
.iter()
|
||||||
.any(|f| f.ident(self.tcx) == field)
|
.any(|f| f.ident(self.tcx) == field)
|
||||||
{
|
{
|
||||||
if let Some(dot_loc) = expr_snippet.rfind('.') {
|
err.multipart_suggestion(
|
||||||
found = true;
|
|
||||||
err.span_suggestion(
|
|
||||||
expr.span.with_hi(expr.span.lo() + BytePos::from_usize(dot_loc)),
|
|
||||||
"to access the field, dereference first",
|
"to access the field, dereference first",
|
||||||
format!("(*{})", &expr_snippet[0..dot_loc]),
|
vec![
|
||||||
|
(base_expr.span.shrink_to_lo(), "(*".to_string()),
|
||||||
|
(base_expr.span.shrink_to_hi(), ")".to_string()),
|
||||||
|
],
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
err.help("methods are immutable and cannot be assigned to");
|
err.help("methods are immutable and cannot be assigned to");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
err.emit();
|
err.emit();
|
||||||
}
|
}
|
||||||
|
@ -112,7 +112,6 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
|||||||
use rustc_hir::intravisit::Visitor;
|
use rustc_hir::intravisit::Visitor;
|
||||||
use rustc_hir::{HirIdMap, ImplicitSelfKind, Node};
|
use rustc_hir::{HirIdMap, ImplicitSelfKind, Node};
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_index::vec::Idx;
|
|
||||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
|
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};
|
||||||
|
@ -318,19 +318,11 @@ impl Duration {
|
|||||||
/// assert_eq!(duration.as_secs(), 5);
|
/// assert_eq!(duration.as_secs(), 5);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// To determine the total number of seconds represented by the `Duration`,
|
/// To determine the total number of seconds represented by the `Duration`
|
||||||
/// use `as_secs` in combination with [`subsec_nanos`]:
|
/// including the fractional part, use [`as_secs_f64`] or [`as_secs_f32`]
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use std::time::Duration;
|
|
||||||
///
|
|
||||||
/// let duration = Duration::new(5, 730023852);
|
|
||||||
///
|
|
||||||
/// assert_eq!(5.730023852,
|
|
||||||
/// duration.as_secs() as f64
|
|
||||||
/// + duration.subsec_nanos() as f64 * 1e-9);
|
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
|
/// [`as_secs_f32`]: Duration::as_secs_f64
|
||||||
|
/// [`as_secs_f64`]: Duration::as_secs_f32
|
||||||
/// [`subsec_nanos`]: Duration::subsec_nanos
|
/// [`subsec_nanos`]: Duration::subsec_nanos
|
||||||
#[stable(feature = "duration", since = "1.3.0")]
|
#[stable(feature = "duration", since = "1.3.0")]
|
||||||
#[rustc_const_stable(feature = "duration_consts", since = "1.32.0")]
|
#[rustc_const_stable(feature = "duration_consts", since = "1.32.0")]
|
||||||
|
@ -476,7 +476,7 @@ where
|
|||||||
let mut ty_to_fn: FxHashMap<Type, (PolyTrait, Option<Type>)> = Default::default();
|
let mut ty_to_fn: FxHashMap<Type, (PolyTrait, Option<Type>)> = Default::default();
|
||||||
|
|
||||||
for p in clean_where_predicates {
|
for p in clean_where_predicates {
|
||||||
let (orig_p, p) = (p, p.clean(self.cx));
|
let (orig_p, p) = (p, clean_predicate(p, self.cx));
|
||||||
if p.is_none() {
|
if p.is_none() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -259,12 +259,14 @@ pub(crate) fn clean_middle_region<'tcx>(region: ty::Region<'tcx>) -> Option<Life
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Clean<'tcx, Option<WherePredicate>> for hir::WherePredicate<'tcx> {
|
fn clean_where_predicate<'tcx>(
|
||||||
fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<WherePredicate> {
|
predicate: &hir::WherePredicate<'tcx>,
|
||||||
if !self.in_where_clause() {
|
cx: &mut DocContext<'tcx>,
|
||||||
|
) -> Option<WherePredicate> {
|
||||||
|
if !predicate.in_where_clause() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(match *self {
|
Some(match *predicate {
|
||||||
hir::WherePredicate::BoundPredicate(ref wbp) => {
|
hir::WherePredicate::BoundPredicate(ref wbp) => {
|
||||||
let bound_params = wbp
|
let bound_params = wbp
|
||||||
.bound_generic_params
|
.bound_generic_params
|
||||||
@ -296,12 +298,13 @@ impl<'tcx> Clean<'tcx, Option<WherePredicate>> for hir::WherePredicate<'tcx> {
|
|||||||
rhs: clean_ty(wrp.rhs_ty, cx).into(),
|
rhs: clean_ty(wrp.rhs_ty, cx).into(),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Clean<'tcx, Option<WherePredicate>> for ty::Predicate<'tcx> {
|
pub(crate) fn clean_predicate<'tcx>(
|
||||||
fn clean(&self, cx: &mut DocContext<'tcx>) -> Option<WherePredicate> {
|
predicate: ty::Predicate<'tcx>,
|
||||||
let bound_predicate = self.kind();
|
cx: &mut DocContext<'tcx>,
|
||||||
|
) -> Option<WherePredicate> {
|
||||||
|
let bound_predicate = predicate.kind();
|
||||||
match bound_predicate.skip_binder() {
|
match bound_predicate.skip_binder() {
|
||||||
ty::PredicateKind::Trait(pred) => {
|
ty::PredicateKind::Trait(pred) => {
|
||||||
clean_poly_trait_predicate(bound_predicate.rebind(pred), cx)
|
clean_poly_trait_predicate(bound_predicate.rebind(pred), cx)
|
||||||
@ -319,7 +322,6 @@ impl<'tcx> Clean<'tcx, Option<WherePredicate>> for ty::Predicate<'tcx> {
|
|||||||
| ty::PredicateKind::ConstEquate(..)
|
| ty::PredicateKind::ConstEquate(..)
|
||||||
| ty::PredicateKind::TypeWellFormedFromEnv(..) => panic!("not user writable"),
|
| ty::PredicateKind::TypeWellFormedFromEnv(..) => panic!("not user writable"),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clean_poly_trait_predicate<'tcx>(
|
fn clean_poly_trait_predicate<'tcx>(
|
||||||
@ -594,7 +596,11 @@ impl<'tcx> Clean<'tcx, Generics> for hir::Generics<'tcx> {
|
|||||||
|
|
||||||
let mut generics = Generics {
|
let mut generics = Generics {
|
||||||
params,
|
params,
|
||||||
where_predicates: self.predicates.iter().filter_map(|x| x.clean(cx)).collect(),
|
where_predicates: self
|
||||||
|
.predicates
|
||||||
|
.iter()
|
||||||
|
.filter_map(|x| clean_where_predicate(x, cx))
|
||||||
|
.collect(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Some duplicates are generated for ?Sized bounds between type params and where
|
// Some duplicates are generated for ?Sized bounds between type params and where
|
||||||
@ -695,7 +701,7 @@ fn clean_ty_generics<'tcx>(
|
|||||||
|
|
||||||
if let Some(param_idx) = param_idx {
|
if let Some(param_idx) = param_idx {
|
||||||
if let Some(b) = impl_trait.get_mut(¶m_idx.into()) {
|
if let Some(b) = impl_trait.get_mut(¶m_idx.into()) {
|
||||||
let p: WherePredicate = p.clean(cx)?;
|
let p: WherePredicate = clean_predicate(*p, cx)?;
|
||||||
|
|
||||||
b.extend(
|
b.extend(
|
||||||
p.get_bounds()
|
p.get_bounds()
|
||||||
@ -752,7 +758,7 @@ fn clean_ty_generics<'tcx>(
|
|||||||
// Now that `cx.impl_trait_bounds` is populated, we can process
|
// Now that `cx.impl_trait_bounds` is populated, we can process
|
||||||
// remaining predicates which could contain `impl Trait`.
|
// remaining predicates which could contain `impl Trait`.
|
||||||
let mut where_predicates =
|
let mut where_predicates =
|
||||||
where_predicates.into_iter().flat_map(|p| p.clean(cx)).collect::<Vec<_>>();
|
where_predicates.into_iter().flat_map(|p| clean_predicate(*p, cx)).collect::<Vec<_>>();
|
||||||
|
|
||||||
// Type parameters have a Sized bound by default unless removed with
|
// Type parameters have a Sized bound by default unless removed with
|
||||||
// ?Sized. Scan through the predicates and mark any type parameter with
|
// ?Sized. Scan through the predicates and mark any type parameter with
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
enum Eu64 {
|
enum Eu64 {
|
||||||
//~^ ERROR discriminant value `0` assigned more than once
|
//~^ ERROR discriminant value `0` assigned more than once
|
||||||
Au64 = 0,
|
Au64 = 0,
|
||||||
//~^NOTE first assignment of `0`
|
//~^NOTE `0` assigned here
|
||||||
Bu64 = 0x8000_0000_0000_0000
|
Bu64 = 0x8000_0000_0000_0000
|
||||||
//~^NOTE second assignment of `0` (overflowed from `9223372036854775808`)
|
//~^NOTE `0` (overflowed from `9223372036854775808`) assigned here
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -5,10 +5,10 @@ LL | enum Eu64 {
|
|||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
LL |
|
LL |
|
||||||
LL | Au64 = 0,
|
LL | Au64 = 0,
|
||||||
| - first assignment of `0`
|
| - `0` assigned here
|
||||||
LL |
|
LL |
|
||||||
LL | Bu64 = 0x8000_0000_0000_0000
|
LL | Bu64 = 0x8000_0000_0000_0000
|
||||||
| --------------------- second assignment of `0` (overflowed from `9223372036854775808`)
|
| --------------------- `0` (overflowed from `9223372036854775808`) assigned here
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
enum Enum {
|
enum Enum {
|
||||||
//~^ ERROR discriminant value `3` assigned more than once
|
//~^ ERROR discriminant value `3` assigned more than once
|
||||||
P = 3,
|
P = 3,
|
||||||
//~^ NOTE first assignment of `3`
|
//~^ NOTE `3` assigned here
|
||||||
X = 3,
|
X = 3,
|
||||||
//~^ NOTE second assignment of `3`
|
//~^ NOTE `3` assigned here
|
||||||
Y = 5
|
Y = 5
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -11,20 +11,43 @@ enum Enum {
|
|||||||
enum EnumOverflowRepr {
|
enum EnumOverflowRepr {
|
||||||
//~^ ERROR discriminant value `1` assigned more than once
|
//~^ ERROR discriminant value `1` assigned more than once
|
||||||
P = 257,
|
P = 257,
|
||||||
//~^ NOTE first assignment of `1` (overflowed from `257`)
|
//~^ NOTE `1` (overflowed from `257`) assigned here
|
||||||
X = 513,
|
X = 513,
|
||||||
//~^ NOTE second assignment of `1` (overflowed from `513`)
|
//~^ NOTE `1` (overflowed from `513`) assigned here
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(i8)]
|
#[repr(i8)]
|
||||||
enum NegDisEnum {
|
enum NegDisEnum {
|
||||||
//~^ ERROR discriminant value `-1` assigned more than once
|
//~^ ERROR discriminant value `-1` assigned more than once
|
||||||
First = -1,
|
First = -1,
|
||||||
//~^ NOTE first assignment of `-1`
|
//~^ NOTE `-1` assigned here
|
||||||
Second = -2,
|
Second = -2,
|
||||||
//~^ NOTE assigned discriminant for `Last` was incremented from this discriminant
|
//~^ NOTE discriminant for `Last` incremented from this startpoint (`Second` + 1 variant later => `Last` = -1)
|
||||||
Last,
|
Last,
|
||||||
//~^ NOTE second assignment of `-1`
|
//~^ NOTE `-1` assigned here
|
||||||
|
}
|
||||||
|
|
||||||
|
enum MultipleDuplicates {
|
||||||
|
//~^ ERROR discriminant value `0` assigned more than once
|
||||||
|
//~^^ ERROR discriminant value `-2` assigned more than once
|
||||||
|
V0,
|
||||||
|
//~^ NOTE `0` assigned here
|
||||||
|
V1 = 0,
|
||||||
|
//~^ NOTE `0` assigned here
|
||||||
|
V2,
|
||||||
|
V3,
|
||||||
|
V4 = 0,
|
||||||
|
//~^ NOTE `0` assigned here
|
||||||
|
V5 = -2,
|
||||||
|
//~^ NOTE discriminant for `V7` incremented from this startpoint (`V5` + 2 variants later => `V7` = 0)
|
||||||
|
//~^^ NOTE `-2` assigned here
|
||||||
|
V6,
|
||||||
|
V7,
|
||||||
|
//~^ NOTE `0` assigned here
|
||||||
|
V8 = -3,
|
||||||
|
//~^ NOTE discriminant for `V9` incremented from this startpoint (`V8` + 1 variant later => `V9` = -2)
|
||||||
|
V9,
|
||||||
|
//~^ NOTE `-2` assigned here
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -5,10 +5,10 @@ LL | enum Enum {
|
|||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
LL |
|
LL |
|
||||||
LL | P = 3,
|
LL | P = 3,
|
||||||
| - first assignment of `3`
|
| - `3` assigned here
|
||||||
LL |
|
LL |
|
||||||
LL | X = 3,
|
LL | X = 3,
|
||||||
| - second assignment of `3`
|
| - `3` assigned here
|
||||||
|
|
||||||
error[E0081]: discriminant value `1` assigned more than once
|
error[E0081]: discriminant value `1` assigned more than once
|
||||||
--> $DIR/E0081.rs:11:1
|
--> $DIR/E0081.rs:11:1
|
||||||
@ -17,10 +17,10 @@ LL | enum EnumOverflowRepr {
|
|||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^
|
||||||
LL |
|
LL |
|
||||||
LL | P = 257,
|
LL | P = 257,
|
||||||
| --- first assignment of `1` (overflowed from `257`)
|
| --- `1` (overflowed from `257`) assigned here
|
||||||
LL |
|
LL |
|
||||||
LL | X = 513,
|
LL | X = 513,
|
||||||
| --- second assignment of `1` (overflowed from `513`)
|
| --- `1` (overflowed from `513`) assigned here
|
||||||
|
|
||||||
error[E0081]: discriminant value `-1` assigned more than once
|
error[E0081]: discriminant value `-1` assigned more than once
|
||||||
--> $DIR/E0081.rs:20:1
|
--> $DIR/E0081.rs:20:1
|
||||||
@ -29,14 +29,50 @@ LL | enum NegDisEnum {
|
|||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
LL |
|
LL |
|
||||||
LL | First = -1,
|
LL | First = -1,
|
||||||
| -- first assignment of `-1`
|
| -- `-1` assigned here
|
||||||
LL |
|
LL |
|
||||||
LL | Second = -2,
|
LL | Second = -2,
|
||||||
| ----------- assigned discriminant for `Last` was incremented from this discriminant
|
| ----------- discriminant for `Last` incremented from this startpoint (`Second` + 1 variant later => `Last` = -1)
|
||||||
LL |
|
LL |
|
||||||
LL | Last,
|
LL | Last,
|
||||||
| ---- second assignment of `-1`
|
| ---- `-1` assigned here
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error[E0081]: discriminant value `0` assigned more than once
|
||||||
|
--> $DIR/E0081.rs:30:1
|
||||||
|
|
|
||||||
|
LL | enum MultipleDuplicates {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
...
|
||||||
|
LL | V0,
|
||||||
|
| -- `0` assigned here
|
||||||
|
LL |
|
||||||
|
LL | V1 = 0,
|
||||||
|
| - `0` assigned here
|
||||||
|
...
|
||||||
|
LL | V4 = 0,
|
||||||
|
| - `0` assigned here
|
||||||
|
LL |
|
||||||
|
LL | V5 = -2,
|
||||||
|
| ------- discriminant for `V7` incremented from this startpoint (`V5` + 2 variants later => `V7` = 0)
|
||||||
|
...
|
||||||
|
LL | V7,
|
||||||
|
| -- `0` assigned here
|
||||||
|
|
||||||
|
error[E0081]: discriminant value `-2` assigned more than once
|
||||||
|
--> $DIR/E0081.rs:30:1
|
||||||
|
|
|
||||||
|
LL | enum MultipleDuplicates {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
...
|
||||||
|
LL | V5 = -2,
|
||||||
|
| -- `-2` assigned here
|
||||||
|
...
|
||||||
|
LL | V8 = -3,
|
||||||
|
| ------- discriminant for `V9` incremented from this startpoint (`V8` + 1 variant later => `V9` = -2)
|
||||||
|
LL |
|
||||||
|
LL | V9,
|
||||||
|
| -- `-2` assigned here
|
||||||
|
|
||||||
|
error: aborting due to 5 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0081`.
|
For more information about this error, try `rustc --explain E0081`.
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
const N: isize = 1;
|
|
||||||
|
|
||||||
enum Foo {
|
|
||||||
//~^ ERROR discriminant value `1` assigned more than once
|
|
||||||
//~| ERROR discriminant value `1` assigned more than once
|
|
||||||
//~| ERROR discriminant value `1` assigned more than once
|
|
||||||
A = 1,
|
|
||||||
B = 1,
|
|
||||||
C = 0,
|
|
||||||
D,
|
|
||||||
|
|
||||||
E = N,
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
@ -1,40 +0,0 @@
|
|||||||
error[E0081]: discriminant value `1` assigned more than once
|
|
||||||
--> $DIR/issue-15524.rs:3:1
|
|
||||||
|
|
|
||||||
LL | enum Foo {
|
|
||||||
| ^^^^^^^^
|
|
||||||
...
|
|
||||||
LL | A = 1,
|
|
||||||
| - first assignment of `1`
|
|
||||||
LL | B = 1,
|
|
||||||
| - second assignment of `1`
|
|
||||||
|
|
||||||
error[E0081]: discriminant value `1` assigned more than once
|
|
||||||
--> $DIR/issue-15524.rs:3:1
|
|
||||||
|
|
|
||||||
LL | enum Foo {
|
|
||||||
| ^^^^^^^^
|
|
||||||
...
|
|
||||||
LL | A = 1,
|
|
||||||
| - first assignment of `1`
|
|
||||||
LL | B = 1,
|
|
||||||
LL | C = 0,
|
|
||||||
| ----- assigned discriminant for `D` was incremented from this discriminant
|
|
||||||
LL | D,
|
|
||||||
| - second assignment of `1`
|
|
||||||
|
|
||||||
error[E0081]: discriminant value `1` assigned more than once
|
|
||||||
--> $DIR/issue-15524.rs:3:1
|
|
||||||
|
|
|
||||||
LL | enum Foo {
|
|
||||||
| ^^^^^^^^
|
|
||||||
...
|
|
||||||
LL | A = 1,
|
|
||||||
| - first assignment of `1`
|
|
||||||
...
|
|
||||||
LL | E = N,
|
|
||||||
| - second assignment of `1`
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0081`.
|
|
19
src/test/ui/proc-macro/auxiliary/re-export.rs
Normal file
19
src/test/ui/proc-macro/auxiliary/re-export.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// force-host
|
||||||
|
// no-prefer-dynamic
|
||||||
|
|
||||||
|
#![crate_type = "proc-macro"]
|
||||||
|
|
||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn cause_ice(_: TokenStream) -> TokenStream {
|
||||||
|
"
|
||||||
|
enum IceCause {
|
||||||
|
Variant,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use IceCause::Variant;
|
||||||
|
".parse().unwrap()
|
||||||
|
}
|
10
src/test/ui/proc-macro/issue-79148.rs
Normal file
10
src/test/ui/proc-macro/issue-79148.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// aux-build:re-export.rs
|
||||||
|
// edition:2018
|
||||||
|
|
||||||
|
extern crate re_export;
|
||||||
|
|
||||||
|
use re_export::cause_ice;
|
||||||
|
|
||||||
|
cause_ice!(); //~ ERROR `Variant` is only public within the crate, and cannot be re-exported outside
|
||||||
|
|
||||||
|
fn main() {}
|
16
src/test/ui/proc-macro/issue-79148.stderr
Normal file
16
src/test/ui/proc-macro/issue-79148.stderr
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
error[E0364]: `Variant` is only public within the crate, and cannot be re-exported outside
|
||||||
|
--> $DIR/issue-79148.rs:8:1
|
||||||
|
|
|
||||||
|
LL | cause_ice!();
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
note: consider marking `Variant` as `pub` in the imported module
|
||||||
|
--> $DIR/issue-79148.rs:8:1
|
||||||
|
|
|
||||||
|
LL | cause_ice!();
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
= note: this error originates in the macro `cause_ice` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0364`.
|
@ -1,12 +0,0 @@
|
|||||||
// Black and White have the same discriminator value ...
|
|
||||||
|
|
||||||
enum Color {
|
|
||||||
//~^ ERROR discriminant value `0` assigned more than once
|
|
||||||
Red = 0xff0000,
|
|
||||||
Green = 0x00ff00,
|
|
||||||
Blue = 0x0000ff,
|
|
||||||
Black = 0x000000,
|
|
||||||
White = 0x000000,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() { }
|
|
@ -1,14 +0,0 @@
|
|||||||
error[E0081]: discriminant value `0` assigned more than once
|
|
||||||
--> $DIR/tag-variant-disr-dup.rs:3:1
|
|
||||||
|
|
|
||||||
LL | enum Color {
|
|
||||||
| ^^^^^^^^^^
|
|
||||||
...
|
|
||||||
LL | Black = 0x000000,
|
|
||||||
| -------- first assignment of `0`
|
|
||||||
LL | White = 0x000000,
|
|
||||||
| -------- second assignment of `0`
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0081`.
|
|
@ -2,9 +2,12 @@ error[E0615]: attempted to take value of method `read` on type `*mut Foo`
|
|||||||
--> $DIR/issue-91210-ptr-method.rs:10:7
|
--> $DIR/issue-91210-ptr-method.rs:10:7
|
||||||
|
|
|
|
||||||
LL | x.read = 4;
|
LL | x.read = 4;
|
||||||
| - ^^^^ method, not a field
|
| ^^^^ method, not a field
|
||||||
| |
|
|
|
||||||
| help: to access the field, dereference first: `(*x)`
|
help: to access the field, dereference first
|
||||||
|
|
|
||||||
|
LL | (*x).read = 4;
|
||||||
|
| ++ +
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ Before submitting, please make sure that you're not running into one of these kn
|
|||||||
Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
|
Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
**rust-analyzer version**: (eg. output of "Rust Analyzer: Show RA Version" command)
|
**rust-analyzer version**: (eg. output of "rust-analyzer: Show RA Version" command, accessible in VSCode via <kbd>Ctrl/⌘</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd>)
|
||||||
|
|
||||||
**rustc version**: (eg. output of `rustc -V`)
|
**rustc version**: (eg. output of `rustc -V`)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ env:
|
|||||||
FETCH_DEPTH: 0 # pull in the tags for the version string
|
FETCH_DEPTH: 0 # pull in the tags for the version string
|
||||||
MACOSX_DEPLOYMENT_TARGET: 10.15
|
MACOSX_DEPLOYMENT_TARGET: 10.15
|
||||||
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
|
||||||
|
CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
dist:
|
dist:
|
||||||
@ -36,6 +37,9 @@ jobs:
|
|||||||
- os: ubuntu-18.04
|
- os: ubuntu-18.04
|
||||||
target: aarch64-unknown-linux-gnu
|
target: aarch64-unknown-linux-gnu
|
||||||
code-target: linux-arm64
|
code-target: linux-arm64
|
||||||
|
- os: ubuntu-18.04
|
||||||
|
target: arm-unknown-linux-gnueabihf
|
||||||
|
code-target: linux-armhf
|
||||||
- os: macos-11
|
- os: macos-11
|
||||||
target: x86_64-apple-darwin
|
target: x86_64-apple-darwin
|
||||||
code-target: darwin-x64
|
code-target: darwin-x64
|
||||||
@ -67,13 +71,17 @@ jobs:
|
|||||||
node-version: 14.x
|
node-version: 14.x
|
||||||
|
|
||||||
- name: Update apt repositories
|
- name: Update apt repositories
|
||||||
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf'
|
||||||
run: sudo apt-get update
|
run: sudo apt-get update
|
||||||
|
|
||||||
- name: Install target toolchain
|
- name: Install AArch64 target toolchain
|
||||||
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||||
run: sudo apt-get install gcc-aarch64-linux-gnu
|
run: sudo apt-get install gcc-aarch64-linux-gnu
|
||||||
|
|
||||||
|
- name: Install ARM target toolchain
|
||||||
|
if: matrix.target == 'arm-unknown-linux-gnueabihf'
|
||||||
|
run: sudo apt-get install gcc-arm-linux-gnueabihf
|
||||||
|
|
||||||
- name: Dist
|
- name: Dist
|
||||||
run: cargo xtask dist --client-patch-version ${{ github.run_number }}
|
run: cargo xtask dist --client-patch-version ${{ github.run_number }}
|
||||||
|
|
||||||
@ -204,6 +212,10 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: dist-aarch64-unknown-linux-gnu
|
name: dist-aarch64-unknown-linux-gnu
|
||||||
path: dist
|
path: dist
|
||||||
|
- uses: actions/download-artifact@v1
|
||||||
|
with:
|
||||||
|
name: dist-arm-unknown-linux-gnueabihf
|
||||||
|
path: dist
|
||||||
- uses: actions/download-artifact@v1
|
- uses: actions/download-artifact@v1
|
||||||
with:
|
with:
|
||||||
name: dist-x86_64-pc-windows-msvc
|
name: dist-x86_64-pc-windows-msvc
|
||||||
|
@ -43,7 +43,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Rust analyzer is primarily distributed under the terms of both the MIT
|
rust-analyzer is primarily distributed under the terms of both the MIT
|
||||||
license and the Apache License (Version 2.0).
|
license and the Apache License (Version 2.0).
|
||||||
|
|
||||||
See LICENSE-APACHE and LICENSE-MIT for details.
|
See LICENSE-APACHE and LICENSE-MIT for details.
|
||||||
|
@ -57,6 +57,7 @@ pub struct FlycheckHandle {
|
|||||||
// XXX: drop order is significant
|
// XXX: drop order is significant
|
||||||
sender: Sender<Restart>,
|
sender: Sender<Restart>,
|
||||||
_thread: jod_thread::JoinHandle,
|
_thread: jod_thread::JoinHandle,
|
||||||
|
id: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlycheckHandle {
|
impl FlycheckHandle {
|
||||||
@ -72,18 +73,22 @@ impl FlycheckHandle {
|
|||||||
.name("Flycheck".to_owned())
|
.name("Flycheck".to_owned())
|
||||||
.spawn(move || actor.run(receiver))
|
.spawn(move || actor.run(receiver))
|
||||||
.expect("failed to spawn thread");
|
.expect("failed to spawn thread");
|
||||||
FlycheckHandle { sender, _thread: thread }
|
FlycheckHandle { id, sender, _thread: thread }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Schedule a re-start of the cargo check worker.
|
/// Schedule a re-start of the cargo check worker.
|
||||||
pub fn update(&self) {
|
pub fn update(&self) {
|
||||||
self.sender.send(Restart).unwrap();
|
self.sender.send(Restart).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> usize {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
/// Request adding a diagnostic with fixes included to a file
|
/// Request adding a diagnostic with fixes included to a file
|
||||||
AddDiagnostic { workspace_root: AbsPathBuf, diagnostic: Diagnostic },
|
AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic },
|
||||||
|
|
||||||
/// Request check progress notification to client
|
/// Request check progress notification to client
|
||||||
Progress {
|
Progress {
|
||||||
@ -96,8 +101,9 @@ pub enum Message {
|
|||||||
impl fmt::Debug for Message {
|
impl fmt::Debug for Message {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Message::AddDiagnostic { workspace_root, diagnostic } => f
|
Message::AddDiagnostic { id, workspace_root, diagnostic } => f
|
||||||
.debug_struct("AddDiagnostic")
|
.debug_struct("AddDiagnostic")
|
||||||
|
.field("id", id)
|
||||||
.field("workspace_root", workspace_root)
|
.field("workspace_root", workspace_root)
|
||||||
.field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code))
|
.field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code))
|
||||||
.finish(),
|
.finish(),
|
||||||
@ -183,7 +189,7 @@ impl FlycheckActor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::CheckEvent(None) => {
|
Event::CheckEvent(None) => {
|
||||||
tracing::debug!("flycheck finished");
|
tracing::debug!(flycheck_id = self.id, "flycheck finished");
|
||||||
|
|
||||||
// Watcher finished
|
// Watcher finished
|
||||||
let cargo_handle = self.cargo_handle.take().unwrap();
|
let cargo_handle = self.cargo_handle.take().unwrap();
|
||||||
@ -203,6 +209,7 @@ impl FlycheckActor {
|
|||||||
|
|
||||||
CargoMessage::Diagnostic(msg) => {
|
CargoMessage::Diagnostic(msg) => {
|
||||||
self.send(Message::AddDiagnostic {
|
self.send(Message::AddDiagnostic {
|
||||||
|
id: self.id,
|
||||||
workspace_root: self.workspace_root.clone(),
|
workspace_root: self.workspace_root.clone(),
|
||||||
diagnostic: msg,
|
diagnostic: msg,
|
||||||
});
|
});
|
||||||
|
@ -451,7 +451,7 @@ impl HasChildSource<LocalTypeOrConstParamId> for GenericDefId {
|
|||||||
if let GenericDefId::TraitId(id) = *self {
|
if let GenericDefId::TraitId(id) = *self {
|
||||||
let trait_ref = id.lookup(db).source(db).value;
|
let trait_ref = id.lookup(db).source(db).value;
|
||||||
let idx = idx_iter.next().unwrap();
|
let idx = idx_iter.next().unwrap();
|
||||||
params.insert(idx, Either::Right(trait_ref))
|
params.insert(idx, Either::Right(trait_ref));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(generic_params_list) = generic_params_list {
|
if let Some(generic_params_list) = generic_params_list {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
//! unaffected, so we don't have to recompute name resolution results or item data (see `data.rs`).
|
//! unaffected, so we don't have to recompute name resolution results or item data (see `data.rs`).
|
||||||
//!
|
//!
|
||||||
//! The `ItemTree` for the currently open file can be displayed by using the VS Code command
|
//! The `ItemTree` for the currently open file can be displayed by using the VS Code command
|
||||||
//! "Rust Analyzer: Debug ItemTree".
|
//! "rust-analyzer: Debug ItemTree".
|
||||||
//!
|
//!
|
||||||
//! Compared to rustc's architecture, `ItemTree` has properties from both rustc's AST and HIR: many
|
//! Compared to rustc's architecture, `ItemTree` has properties from both rustc's AST and HIR: many
|
||||||
//! syntax-level Rust features are already desugared to simpler forms in the `ItemTree`, but name
|
//! syntax-level Rust features are already desugared to simpler forms in the `ItemTree`, but name
|
||||||
|
@ -224,7 +224,7 @@ pub(crate) fn field_visibilities_query(
|
|||||||
let resolver = variant_id.module(db).resolver(db);
|
let resolver = variant_id.module(db).resolver(db);
|
||||||
let mut res = ArenaMap::default();
|
let mut res = ArenaMap::default();
|
||||||
for (field_id, field_data) in var_data.fields().iter() {
|
for (field_id, field_data) in var_data.fields().iter() {
|
||||||
res.insert(field_id, field_data.visibility.resolve(db, &resolver))
|
res.insert(field_id, field_data.visibility.resolve(db, &resolver));
|
||||||
}
|
}
|
||||||
Arc::new(res)
|
Arc::new(res)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ use std::mem;
|
|||||||
use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
|
use mbe::{SyntheticToken, SyntheticTokenId, TokenMap};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode, HasLoopBody},
|
||||||
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
|
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange,
|
||||||
};
|
};
|
||||||
use tt::Subtree;
|
use tt::Subtree;
|
||||||
@ -142,8 +142,59 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ast::WhileExpr(it) => {
|
||||||
|
if it.condition().is_none() {
|
||||||
|
// insert placeholder token after the while token
|
||||||
|
let while_token = match it.while_token() {
|
||||||
|
Some(t) => t,
|
||||||
|
None => continue,
|
||||||
|
};
|
||||||
|
append.insert(while_token.into(), vec![
|
||||||
|
SyntheticToken {
|
||||||
|
kind: SyntaxKind::IDENT,
|
||||||
|
text: "__ra_fixup".into(),
|
||||||
|
range: end_range,
|
||||||
|
id: EMPTY_ID,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
if it.loop_body().is_none() {
|
||||||
|
append.insert(node.clone().into(), vec![
|
||||||
|
SyntheticToken {
|
||||||
|
kind: SyntaxKind::L_CURLY,
|
||||||
|
text: "{".into(),
|
||||||
|
range: end_range,
|
||||||
|
id: EMPTY_ID,
|
||||||
|
},
|
||||||
|
SyntheticToken {
|
||||||
|
kind: SyntaxKind::R_CURLY,
|
||||||
|
text: "}".into(),
|
||||||
|
range: end_range,
|
||||||
|
id: EMPTY_ID,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ast::LoopExpr(it) => {
|
||||||
|
if it.loop_body().is_none() {
|
||||||
|
append.insert(node.clone().into(), vec![
|
||||||
|
SyntheticToken {
|
||||||
|
kind: SyntaxKind::L_CURLY,
|
||||||
|
text: "{".into(),
|
||||||
|
range: end_range,
|
||||||
|
id: EMPTY_ID,
|
||||||
|
},
|
||||||
|
SyntheticToken {
|
||||||
|
kind: SyntaxKind::R_CURLY,
|
||||||
|
text: "}".into(),
|
||||||
|
range: end_range,
|
||||||
|
id: EMPTY_ID,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
},
|
||||||
// FIXME: foo::
|
// FIXME: foo::
|
||||||
// FIXME: for, loop, match etc.
|
// FIXME: for, match etc.
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,6 +427,61 @@ fn foo() {
|
|||||||
// the {} gets parsed as the condition, I think?
|
// the {} gets parsed as the condition, I think?
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fn foo () {if {} {}}
|
fn foo () {if {} {}}
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fixup_while_1() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
while
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn foo () {while __ra_fixup {}}
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fixup_while_2() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
while foo
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn foo () {while foo {}}
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn fixup_while_3() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
while {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn foo () {while __ra_fixup {}}
|
||||||
|
"#]],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fixup_loop() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo() {
|
||||||
|
loop
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
fn foo () {loop {}}
|
||||||
"#]],
|
"#]],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -381,6 +381,7 @@ pub mod known {
|
|||||||
bitor,
|
bitor,
|
||||||
bitxor_assign,
|
bitxor_assign,
|
||||||
bitxor,
|
bitxor,
|
||||||
|
branch,
|
||||||
deref_mut,
|
deref_mut,
|
||||||
deref,
|
deref,
|
||||||
div_assign,
|
div_assign,
|
||||||
@ -396,6 +397,7 @@ pub mod known {
|
|||||||
not,
|
not,
|
||||||
owned_box,
|
owned_box,
|
||||||
partial_ord,
|
partial_ord,
|
||||||
|
poll,
|
||||||
r#fn,
|
r#fn,
|
||||||
rem_assign,
|
rem_assign,
|
||||||
rem,
|
rem,
|
||||||
|
@ -10,13 +10,13 @@ use chalk_ir::{
|
|||||||
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
|
cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind,
|
||||||
};
|
};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp},
|
expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Statement, UnaryOp},
|
||||||
generics::TypeOrConstParamData,
|
generics::TypeOrConstParamData,
|
||||||
path::{GenericArg, GenericArgs},
|
path::{GenericArg, GenericArgs},
|
||||||
resolver::resolver_for_expr,
|
resolver::resolver_for_expr,
|
||||||
ConstParamId, FieldId, FunctionId, ItemContainerId, Lookup,
|
ConstParamId, FieldId, ItemContainerId, Lookup,
|
||||||
};
|
};
|
||||||
use hir_expand::name::{name, Name};
|
use hir_expand::name::Name;
|
||||||
use stdx::always;
|
use stdx::always;
|
||||||
use syntax::ast::RangeOp;
|
use syntax::ast::RangeOp;
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ use crate::{
|
|||||||
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
|
const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode,
|
||||||
},
|
},
|
||||||
mapping::{from_chalk, ToChalk},
|
mapping::{from_chalk, ToChalk},
|
||||||
method_resolution::{self, VisibleFromModule},
|
method_resolution::{self, lang_names_for_bin_op, VisibleFromModule},
|
||||||
primitive::{self, UintTy},
|
primitive::{self, UintTy},
|
||||||
static_lifetime, to_chalk_trait_id,
|
static_lifetime, to_chalk_trait_id,
|
||||||
utils::{generics, Generics},
|
utils::{generics, Generics},
|
||||||
@ -947,7 +947,9 @@ impl<'a> InferenceContext<'a> {
|
|||||||
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
|
let lhs_ty = self.infer_expr(lhs, &lhs_expectation);
|
||||||
let rhs_ty = self.table.new_type_var();
|
let rhs_ty = self.table.new_type_var();
|
||||||
|
|
||||||
let func = self.resolve_binop_method(op);
|
let func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| {
|
||||||
|
self.db.trait_data(self.resolve_lang_item(lang_item)?.as_trait()?).method_by_name(&name)
|
||||||
|
});
|
||||||
let func = match func {
|
let func = match func {
|
||||||
Some(func) => func,
|
Some(func) => func,
|
||||||
None => {
|
None => {
|
||||||
@ -1473,55 +1475,4 @@ impl<'a> InferenceContext<'a> {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_binop_method(&self, op: BinaryOp) -> Option<FunctionId> {
|
|
||||||
let (name, lang_item) = match op {
|
|
||||||
BinaryOp::LogicOp(_) => return None,
|
|
||||||
BinaryOp::ArithOp(aop) => match aop {
|
|
||||||
ArithOp::Add => (name!(add), name!(add)),
|
|
||||||
ArithOp::Mul => (name!(mul), name!(mul)),
|
|
||||||
ArithOp::Sub => (name!(sub), name!(sub)),
|
|
||||||
ArithOp::Div => (name!(div), name!(div)),
|
|
||||||
ArithOp::Rem => (name!(rem), name!(rem)),
|
|
||||||
ArithOp::Shl => (name!(shl), name!(shl)),
|
|
||||||
ArithOp::Shr => (name!(shr), name!(shr)),
|
|
||||||
ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
|
|
||||||
ArithOp::BitOr => (name!(bitor), name!(bitor)),
|
|
||||||
ArithOp::BitAnd => (name!(bitand), name!(bitand)),
|
|
||||||
},
|
|
||||||
BinaryOp::Assignment { op: Some(aop) } => match aop {
|
|
||||||
ArithOp::Add => (name!(add_assign), name!(add_assign)),
|
|
||||||
ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
|
|
||||||
ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
|
|
||||||
ArithOp::Div => (name!(div_assign), name!(div_assign)),
|
|
||||||
ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
|
|
||||||
ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
|
|
||||||
ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
|
|
||||||
ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
|
|
||||||
ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
|
|
||||||
ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
|
|
||||||
},
|
|
||||||
BinaryOp::CmpOp(cop) => match cop {
|
|
||||||
CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
|
|
||||||
CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
|
|
||||||
CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
|
|
||||||
(name!(le), name!(partial_ord))
|
|
||||||
}
|
|
||||||
CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
|
|
||||||
(name!(lt), name!(partial_ord))
|
|
||||||
}
|
|
||||||
CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
|
|
||||||
(name!(ge), name!(partial_ord))
|
|
||||||
}
|
|
||||||
CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
|
|
||||||
(name!(gt), name!(partial_ord))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
BinaryOp::Assignment { op: None } => return None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let trait_ = self.resolve_lang_item(lang_item)?.as_trait()?;
|
|
||||||
|
|
||||||
self.db.trait_data(trait_).method_by_name(&name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1126,7 +1126,7 @@ pub(crate) fn field_types_query(
|
|||||||
let ctx =
|
let ctx =
|
||||||
TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
|
TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable);
|
||||||
for (field_id, field_data) in var_data.fields().iter() {
|
for (field_id, field_data) in var_data.fields().iter() {
|
||||||
res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)))
|
res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref)));
|
||||||
}
|
}
|
||||||
Arc::new(res)
|
Arc::new(res)
|
||||||
}
|
}
|
||||||
|
@ -336,7 +336,7 @@ impl InherentImpls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inherent_impl_crates_query(
|
pub(crate) fn inherent_impl_crates_query(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
fp: TyFingerprint,
|
fp: TyFingerprint,
|
||||||
@ -419,6 +419,55 @@ pub fn def_crates(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn lang_names_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, Name)> {
|
||||||
|
use hir_expand::name;
|
||||||
|
use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering};
|
||||||
|
Some(match op {
|
||||||
|
BinaryOp::LogicOp(_) => return None,
|
||||||
|
BinaryOp::ArithOp(aop) => match aop {
|
||||||
|
ArithOp::Add => (name!(add), name!(add)),
|
||||||
|
ArithOp::Mul => (name!(mul), name!(mul)),
|
||||||
|
ArithOp::Sub => (name!(sub), name!(sub)),
|
||||||
|
ArithOp::Div => (name!(div), name!(div)),
|
||||||
|
ArithOp::Rem => (name!(rem), name!(rem)),
|
||||||
|
ArithOp::Shl => (name!(shl), name!(shl)),
|
||||||
|
ArithOp::Shr => (name!(shr), name!(shr)),
|
||||||
|
ArithOp::BitXor => (name!(bitxor), name!(bitxor)),
|
||||||
|
ArithOp::BitOr => (name!(bitor), name!(bitor)),
|
||||||
|
ArithOp::BitAnd => (name!(bitand), name!(bitand)),
|
||||||
|
},
|
||||||
|
BinaryOp::Assignment { op: Some(aop) } => match aop {
|
||||||
|
ArithOp::Add => (name!(add_assign), name!(add_assign)),
|
||||||
|
ArithOp::Mul => (name!(mul_assign), name!(mul_assign)),
|
||||||
|
ArithOp::Sub => (name!(sub_assign), name!(sub_assign)),
|
||||||
|
ArithOp::Div => (name!(div_assign), name!(div_assign)),
|
||||||
|
ArithOp::Rem => (name!(rem_assign), name!(rem_assign)),
|
||||||
|
ArithOp::Shl => (name!(shl_assign), name!(shl_assign)),
|
||||||
|
ArithOp::Shr => (name!(shr_assign), name!(shr_assign)),
|
||||||
|
ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)),
|
||||||
|
ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)),
|
||||||
|
ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)),
|
||||||
|
},
|
||||||
|
BinaryOp::CmpOp(cop) => match cop {
|
||||||
|
CmpOp::Eq { negated: false } => (name!(eq), name!(eq)),
|
||||||
|
CmpOp::Eq { negated: true } => (name!(ne), name!(eq)),
|
||||||
|
CmpOp::Ord { ordering: Ordering::Less, strict: false } => {
|
||||||
|
(name!(le), name!(partial_ord))
|
||||||
|
}
|
||||||
|
CmpOp::Ord { ordering: Ordering::Less, strict: true } => {
|
||||||
|
(name!(lt), name!(partial_ord))
|
||||||
|
}
|
||||||
|
CmpOp::Ord { ordering: Ordering::Greater, strict: false } => {
|
||||||
|
(name!(ge), name!(partial_ord))
|
||||||
|
}
|
||||||
|
CmpOp::Ord { ordering: Ordering::Greater, strict: true } => {
|
||||||
|
(name!(gt), name!(partial_ord))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BinaryOp::Assignment { op: None } => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Look up the method with the given name.
|
/// Look up the method with the given name.
|
||||||
pub(crate) fn lookup_method(
|
pub(crate) fn lookup_method(
|
||||||
ty: &Canonical<Ty>,
|
ty: &Canonical<Ty>,
|
||||||
|
@ -357,6 +357,26 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||||||
self.imp.resolve_method_call(call).map(Function::from)
|
self.imp.resolve_method_call(call).map(Function::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
|
||||||
|
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<Function> {
|
||||||
|
self.imp.resolve_prefix_expr(prefix_expr).map(Function::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<Function> {
|
||||||
|
self.imp.resolve_index_expr(index_expr).map(Function::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<Function> {
|
||||||
|
self.imp.resolve_bin_expr(bin_expr).map(Function::from)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<Function> {
|
||||||
|
self.imp.resolve_try_expr(try_expr).map(Function::from)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
||||||
self.imp.resolve_method_call_as_callable(call)
|
self.imp.resolve_method_call_as_callable(call)
|
||||||
}
|
}
|
||||||
@ -1066,6 +1086,26 @@ impl<'db> SemanticsImpl<'db> {
|
|||||||
self.analyze(call.syntax())?.resolve_method_call(self.db, call)
|
self.analyze(call.syntax())?.resolve_method_call(self.db, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
|
||||||
|
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option<FunctionId> {
|
||||||
|
self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option<FunctionId> {
|
||||||
|
self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option<FunctionId> {
|
||||||
|
self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option<FunctionId> {
|
||||||
|
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
||||||
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
|
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
|
||||||
}
|
}
|
||||||
|
@ -25,15 +25,20 @@ use hir_def::{
|
|||||||
Lookup, ModuleDefId, VariantId,
|
Lookup, ModuleDefId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name::AsName, HirFileId, InFile,
|
builtin_fn_macro::BuiltinFnLikeExpander,
|
||||||
|
hygiene::Hygiene,
|
||||||
|
name,
|
||||||
|
name::{AsName, Name},
|
||||||
|
HirFileId, InFile,
|
||||||
};
|
};
|
||||||
use hir_ty::{
|
use hir_ty::{
|
||||||
diagnostics::{
|
diagnostics::{
|
||||||
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
|
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
|
||||||
UnsafeExpr,
|
UnsafeExpr,
|
||||||
},
|
},
|
||||||
method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution,
|
method_resolution::{self, lang_names_for_bin_op},
|
||||||
TyExt, TyKind, TyLoweringContext,
|
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
|
||||||
|
TyLoweringContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
@ -255,8 +260,90 @@ impl SourceAnalyzer {
|
|||||||
) -> Option<FunctionId> {
|
) -> Option<FunctionId> {
|
||||||
let expr_id = self.expr_id(db, &call.clone().into())?;
|
let expr_id = self.expr_id(db, &call.clone().into())?;
|
||||||
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
|
let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?;
|
||||||
let f_in_impl = self.resolve_impl_method(db, f_in_trait, &substs);
|
|
||||||
f_in_impl.or(Some(f_in_trait))
|
Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, &substs))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_await_to_poll(
|
||||||
|
&self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
await_expr: &ast::AwaitExpr,
|
||||||
|
) -> Option<FunctionId> {
|
||||||
|
let ty = self.ty_of_expr(db, &await_expr.expr()?.into())?;
|
||||||
|
|
||||||
|
let op_fn = db
|
||||||
|
.lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())?
|
||||||
|
.as_function()?;
|
||||||
|
let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
|
||||||
|
|
||||||
|
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_prefix_expr(
|
||||||
|
&self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
prefix_expr: &ast::PrefixExpr,
|
||||||
|
) -> Option<FunctionId> {
|
||||||
|
let lang_item_name = match prefix_expr.op_kind()? {
|
||||||
|
ast::UnaryOp::Deref => name![deref],
|
||||||
|
ast::UnaryOp::Not => name![not],
|
||||||
|
ast::UnaryOp::Neg => name![neg],
|
||||||
|
};
|
||||||
|
let ty = self.ty_of_expr(db, &prefix_expr.expr()?.into())?;
|
||||||
|
|
||||||
|
let op_fn = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
|
||||||
|
let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
|
||||||
|
|
||||||
|
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_index_expr(
|
||||||
|
&self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
index_expr: &ast::IndexExpr,
|
||||||
|
) -> Option<FunctionId> {
|
||||||
|
let base_ty = self.ty_of_expr(db, &index_expr.base()?.into())?;
|
||||||
|
let index_ty = self.ty_of_expr(db, &index_expr.index()?.into())?;
|
||||||
|
|
||||||
|
let lang_item_name = name![index];
|
||||||
|
|
||||||
|
let op_fn = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?;
|
||||||
|
let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn)
|
||||||
|
.push(base_ty.clone())
|
||||||
|
.push(index_ty.clone())
|
||||||
|
.build();
|
||||||
|
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_bin_expr(
|
||||||
|
&self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
binop_expr: &ast::BinExpr,
|
||||||
|
) -> Option<FunctionId> {
|
||||||
|
let op = binop_expr.op_kind()?;
|
||||||
|
let lhs = self.ty_of_expr(db, &binop_expr.lhs()?.into())?;
|
||||||
|
let rhs = self.ty_of_expr(db, &binop_expr.rhs()?.into())?;
|
||||||
|
|
||||||
|
let op_fn = lang_names_for_bin_op(op)
|
||||||
|
.and_then(|(name, lang_item)| self.lang_trait_fn(db, &lang_item, &name))?;
|
||||||
|
let substs =
|
||||||
|
hir_ty::TyBuilder::subst_for_def(db, op_fn).push(lhs.clone()).push(rhs.clone()).build();
|
||||||
|
|
||||||
|
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_try_expr(
|
||||||
|
&self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
try_expr: &ast::TryExpr,
|
||||||
|
) -> Option<FunctionId> {
|
||||||
|
let ty = self.ty_of_expr(db, &try_expr.expr()?.into())?;
|
||||||
|
|
||||||
|
let op_fn =
|
||||||
|
db.lang_item(self.resolver.krate(), name![branch].to_smol_str())?.as_function()?;
|
||||||
|
let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build();
|
||||||
|
|
||||||
|
Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_field(
|
pub(crate) fn resolve_field(
|
||||||
@ -666,6 +753,29 @@ impl SourceAnalyzer {
|
|||||||
let fun_data = db.function_data(func);
|
let fun_data = db.function_data(func);
|
||||||
method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
|
method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_impl_method_or_trait_def(
|
||||||
|
&self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
func: FunctionId,
|
||||||
|
substs: &Substitution,
|
||||||
|
) -> FunctionId {
|
||||||
|
self.resolve_impl_method(db, func, substs).unwrap_or(func)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lang_trait_fn(
|
||||||
|
&self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
lang_trait: &Name,
|
||||||
|
method_name: &Name,
|
||||||
|
) -> Option<FunctionId> {
|
||||||
|
db.trait_data(db.lang_item(self.resolver.krate(), lang_trait.to_smol_str())?.as_trait()?)
|
||||||
|
.method_by_name(method_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
|
||||||
|
self.infer.as_ref()?.type_of_expr.get(self.expr_id(db, &expr)?)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scope_for(
|
fn scope_for(
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use hir::{HasSource, InFile};
|
use hir::{HasSource, HirDisplay, InFile};
|
||||||
use ide_db::assists::{AssistId, AssistKind};
|
use ide_db::assists::{AssistId, AssistKind};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, edit::IndentLevel},
|
ast::{self, make, HasArgList},
|
||||||
AstNode, TextSize,
|
match_ast, AstNode, SyntaxNode,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::assist_context::{AssistContext, Assists};
|
use crate::assist_context::{AssistContext, Assists};
|
||||||
@ -32,8 +32,8 @@ use crate::assist_context::{AssistContext, Assists};
|
|||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
|
||||||
let path_expr: ast::PathExpr = ctx.find_node_at_offset()?;
|
let path: ast::Path = ctx.find_node_at_offset()?;
|
||||||
let path = path_expr.path()?;
|
let parent = path_parent(&path)?;
|
||||||
|
|
||||||
if ctx.sema.resolve_path(&path).is_some() {
|
if ctx.sema.resolve_path(&path).is_some() {
|
||||||
// No need to generate anything if the path resolves
|
// No need to generate anything if the path resolves
|
||||||
@ -50,26 +50,71 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>)
|
|||||||
ctx.sema.resolve_path(&path.qualifier()?)
|
ctx.sema.resolve_path(&path.qualifier()?)
|
||||||
{
|
{
|
||||||
let target = path.syntax().text_range();
|
let target = path.syntax().text_range();
|
||||||
return add_variant_to_accumulator(acc, ctx, target, e, &name_ref);
|
return add_variant_to_accumulator(acc, ctx, target, e, &name_ref, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum PathParent {
|
||||||
|
PathExpr(ast::PathExpr),
|
||||||
|
RecordExpr(ast::RecordExpr),
|
||||||
|
PathPat(ast::PathPat),
|
||||||
|
UseTree(ast::UseTree),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PathParent {
|
||||||
|
fn syntax(&self) -> &SyntaxNode {
|
||||||
|
match self {
|
||||||
|
PathParent::PathExpr(it) => it.syntax(),
|
||||||
|
PathParent::RecordExpr(it) => it.syntax(),
|
||||||
|
PathParent::PathPat(it) => it.syntax(),
|
||||||
|
PathParent::UseTree(it) => it.syntax(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_field_list(&self, ctx: &AssistContext<'_>) -> Option<ast::FieldList> {
|
||||||
|
let scope = ctx.sema.scope(self.syntax())?;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
PathParent::PathExpr(it) => {
|
||||||
|
if let Some(call_expr) = it.syntax().parent().and_then(ast::CallExpr::cast) {
|
||||||
|
make_tuple_field_list(call_expr, ctx, &scope)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PathParent::RecordExpr(it) => make_record_field_list(it, ctx, &scope),
|
||||||
|
PathParent::UseTree(_) | PathParent::PathPat(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_parent(path: &ast::Path) -> Option<PathParent> {
|
||||||
|
let parent = path.syntax().parent()?;
|
||||||
|
|
||||||
|
match_ast! {
|
||||||
|
match parent {
|
||||||
|
ast::PathExpr(it) => Some(PathParent::PathExpr(it)),
|
||||||
|
ast::RecordExpr(it) => Some(PathParent::RecordExpr(it)),
|
||||||
|
ast::PathPat(it) => Some(PathParent::PathPat(it)),
|
||||||
|
ast::UseTree(it) => Some(PathParent::UseTree(it)),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn add_variant_to_accumulator(
|
fn add_variant_to_accumulator(
|
||||||
acc: &mut Assists,
|
acc: &mut Assists,
|
||||||
ctx: &AssistContext<'_>,
|
ctx: &AssistContext<'_>,
|
||||||
target: syntax::TextRange,
|
target: syntax::TextRange,
|
||||||
adt: hir::Enum,
|
adt: hir::Enum,
|
||||||
name_ref: &ast::NameRef,
|
name_ref: &ast::NameRef,
|
||||||
|
parent: PathParent,
|
||||||
) -> Option<()> {
|
) -> Option<()> {
|
||||||
let db = ctx.db();
|
let db = ctx.db();
|
||||||
let InFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node(db)?;
|
let InFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node(db)?;
|
||||||
let enum_indent = IndentLevel::from_node(&enum_node.syntax());
|
|
||||||
|
|
||||||
let variant_list = enum_node.variant_list()?;
|
|
||||||
let offset = variant_list.syntax().text_range().end() - TextSize::of('}');
|
|
||||||
let empty_enum = variant_list.variants().next().is_none();
|
|
||||||
|
|
||||||
acc.add(
|
acc.add(
|
||||||
AssistId("generate_enum_variant", AssistKind::Generate),
|
AssistId("generate_enum_variant", AssistKind::Generate),
|
||||||
@ -77,18 +122,80 @@ fn add_variant_to_accumulator(
|
|||||||
target,
|
target,
|
||||||
|builder| {
|
|builder| {
|
||||||
builder.edit_file(file_id.original_file(db));
|
builder.edit_file(file_id.original_file(db));
|
||||||
let text = format!(
|
let node = builder.make_mut(enum_node);
|
||||||
"{maybe_newline}{indent_1}{name},\n{enum_indent}",
|
let variant = make_variant(ctx, name_ref, parent);
|
||||||
maybe_newline = if empty_enum { "\n" } else { "" },
|
node.variant_list().map(|it| it.add_variant(variant.clone_for_update()));
|
||||||
indent_1 = IndentLevel(1),
|
|
||||||
name = name_ref,
|
|
||||||
enum_indent = enum_indent
|
|
||||||
);
|
|
||||||
builder.insert(offset, text)
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_variant(
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
name_ref: &ast::NameRef,
|
||||||
|
parent: PathParent,
|
||||||
|
) -> ast::Variant {
|
||||||
|
let field_list = parent.make_field_list(ctx);
|
||||||
|
make::variant(make::name(&name_ref.text()), field_list)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_record_field_list(
|
||||||
|
record: &ast::RecordExpr,
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
scope: &hir::SemanticsScope<'_>,
|
||||||
|
) -> Option<ast::FieldList> {
|
||||||
|
let fields = record.record_expr_field_list()?.fields();
|
||||||
|
let record_fields = fields.map(|field| {
|
||||||
|
let name = name_from_field(&field);
|
||||||
|
|
||||||
|
let ty = field
|
||||||
|
.expr()
|
||||||
|
.and_then(|it| expr_ty(ctx, it, scope))
|
||||||
|
.unwrap_or_else(make::ty_placeholder);
|
||||||
|
|
||||||
|
make::record_field(None, name, ty)
|
||||||
|
});
|
||||||
|
Some(make::record_field_list(record_fields).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name_from_field(field: &ast::RecordExprField) -> ast::Name {
|
||||||
|
let text = match field.name_ref() {
|
||||||
|
Some(it) => it.to_string(),
|
||||||
|
None => name_from_field_shorthand(field).unwrap_or("unknown".to_string()),
|
||||||
|
};
|
||||||
|
make::name(&text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option<String> {
|
||||||
|
let path = match field.expr()? {
|
||||||
|
ast::Expr::PathExpr(path_expr) => path_expr.path(),
|
||||||
|
_ => None,
|
||||||
|
}?;
|
||||||
|
Some(path.as_single_name_ref()?.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_tuple_field_list(
|
||||||
|
call_expr: ast::CallExpr,
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
scope: &hir::SemanticsScope<'_>,
|
||||||
|
) -> Option<ast::FieldList> {
|
||||||
|
let args = call_expr.arg_list()?.args();
|
||||||
|
let tuple_fields = args.map(|arg| {
|
||||||
|
let ty = expr_ty(ctx, arg, &scope).unwrap_or_else(make::ty_placeholder);
|
||||||
|
make::tuple_field(None, ty)
|
||||||
|
});
|
||||||
|
Some(make::tuple_field_list(tuple_fields).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_ty(
|
||||||
|
ctx: &AssistContext<'_>,
|
||||||
|
arg: ast::Expr,
|
||||||
|
scope: &hir::SemanticsScope<'_>,
|
||||||
|
) -> Option<ast::Type> {
|
||||||
|
let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?;
|
||||||
|
let text = ty.display_source_code(ctx.db(), scope.module().into()).ok()?;
|
||||||
|
Some(make::ty(&text))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::tests::{check_assist, check_assist_not_applicable};
|
use crate::tests::{check_assist, check_assist_not_applicable};
|
||||||
@ -221,6 +328,236 @@ mod m {
|
|||||||
fn main() {
|
fn main() {
|
||||||
m::Foo::Baz
|
m::Foo::Baz
|
||||||
}
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_single_element_tuple() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
enum Foo {}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar$0(true)
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar(bool),
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar(true)
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_single_element_tuple_unknown_type() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
enum Foo {}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar$0(x)
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar(_),
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar(x)
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_multi_element_tuple() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
struct Struct {}
|
||||||
|
enum Foo {}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar$0(true, x, Struct {})
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
struct Struct {}
|
||||||
|
enum Foo {
|
||||||
|
Bar(bool, _, Struct),
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar(true, x, Struct {})
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_record() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
enum Foo {}
|
||||||
|
fn main() {
|
||||||
|
Foo::$0Bar { x: true }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar { x: bool },
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar { x: true }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_record_unknown_type() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
enum Foo {}
|
||||||
|
fn main() {
|
||||||
|
Foo::$0Bar { x: y }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar { x: _ },
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar { x: y }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_record_field_shorthand() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
enum Foo {}
|
||||||
|
fn main() {
|
||||||
|
let x = true;
|
||||||
|
Foo::$0Bar { x }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar { x: bool },
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
let x = true;
|
||||||
|
Foo::Bar { x }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_record_field_shorthand_unknown_type() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
enum Foo {}
|
||||||
|
fn main() {
|
||||||
|
Foo::$0Bar { x }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar { x: _ },
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar { x }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn associated_record_field_multiple_fields() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
struct Struct {}
|
||||||
|
enum Foo {}
|
||||||
|
fn main() {
|
||||||
|
Foo::$0Bar { x, y: x, s: Struct {} }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
struct Struct {}
|
||||||
|
enum Foo {
|
||||||
|
Bar { x: _, y: _, s: Struct },
|
||||||
|
}
|
||||||
|
fn main() {
|
||||||
|
Foo::Bar { x, y: x, s: Struct {} }
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn use_tree() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
//- /main.rs
|
||||||
|
mod foo;
|
||||||
|
use foo::Foo::Bar$0;
|
||||||
|
|
||||||
|
//- /foo.rs
|
||||||
|
enum Foo {}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn not_applicable_for_path_type() {
|
||||||
|
check_assist_not_applicable(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
enum Foo {}
|
||||||
|
impl Foo::Bar$0 {}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_pat() {
|
||||||
|
check_assist(
|
||||||
|
generate_enum_variant,
|
||||||
|
r"
|
||||||
|
enum Foo {}
|
||||||
|
fn foo(x: Foo) {
|
||||||
|
match x {
|
||||||
|
Foo::Bar$0 =>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
}
|
||||||
|
fn foo(x: Foo) {
|
||||||
|
match x {
|
||||||
|
Foo::Bar =>
|
||||||
|
}
|
||||||
|
}
|
||||||
",
|
",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ impl RootDatabase {
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Memory Usage (Clears Database)**
|
// | VS Code | **rust-analyzer: Memory Usage (Clears Database)**
|
||||||
// |===
|
// |===
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[]
|
||||||
pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
|
pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> {
|
||||||
|
@ -127,10 +127,12 @@ impl Definition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: IdentClass as a name no longer fits
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum IdentClass {
|
pub enum IdentClass {
|
||||||
NameClass(NameClass),
|
NameClass(NameClass),
|
||||||
NameRefClass(NameRefClass),
|
NameRefClass(NameRefClass),
|
||||||
|
Operator(OperatorClass),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IdentClass {
|
impl IdentClass {
|
||||||
@ -147,6 +149,11 @@ impl IdentClass {
|
|||||||
.map(IdentClass::NameClass)
|
.map(IdentClass::NameClass)
|
||||||
.or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
|
.or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
|
||||||
},
|
},
|
||||||
|
ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator),
|
||||||
|
ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator),
|
||||||
|
ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator),
|
||||||
|
ast::PrefixExpr(prefix_expr) => OperatorClass::classify_prefix(sema,&prefix_expr).map(IdentClass::Operator),
|
||||||
|
ast::TryExpr(try_expr) => OperatorClass::classify_try(sema,&try_expr).map(IdentClass::Operator),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,6 +191,33 @@ impl IdentClass {
|
|||||||
res.push(Definition::Local(local_ref));
|
res.push(Definition::Local(local_ref));
|
||||||
res.push(Definition::Field(field_ref));
|
res.push(Definition::Field(field_ref));
|
||||||
}
|
}
|
||||||
|
IdentClass::Operator(
|
||||||
|
OperatorClass::Await(func)
|
||||||
|
| OperatorClass::Prefix(func)
|
||||||
|
| OperatorClass::Bin(func)
|
||||||
|
| OperatorClass::Index(func)
|
||||||
|
| OperatorClass::Try(func),
|
||||||
|
) => res.push(Definition::Function(func)),
|
||||||
|
}
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn definitions_no_ops(self) -> ArrayVec<Definition, 2> {
|
||||||
|
let mut res = ArrayVec::new();
|
||||||
|
match self {
|
||||||
|
IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
|
||||||
|
res.push(it)
|
||||||
|
}
|
||||||
|
IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
|
||||||
|
res.push(Definition::Local(local_def));
|
||||||
|
res.push(Definition::Field(field_ref));
|
||||||
|
}
|
||||||
|
IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
|
||||||
|
IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
|
||||||
|
res.push(Definition::Local(local_ref));
|
||||||
|
res.push(Definition::Field(field_ref));
|
||||||
|
}
|
||||||
|
IdentClass::Operator(_) => (),
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@ -332,6 +366,52 @@ impl NameClass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum OperatorClass {
|
||||||
|
Await(Function),
|
||||||
|
Prefix(Function),
|
||||||
|
Index(Function),
|
||||||
|
Try(Function),
|
||||||
|
Bin(Function),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OperatorClass {
|
||||||
|
pub fn classify_await(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
await_expr: &ast::AwaitExpr,
|
||||||
|
) -> Option<OperatorClass> {
|
||||||
|
sema.resolve_await_to_poll(await_expr).map(OperatorClass::Await)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn classify_prefix(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
prefix_expr: &ast::PrefixExpr,
|
||||||
|
) -> Option<OperatorClass> {
|
||||||
|
sema.resolve_prefix_expr(prefix_expr).map(OperatorClass::Prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn classify_try(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
try_expr: &ast::TryExpr,
|
||||||
|
) -> Option<OperatorClass> {
|
||||||
|
sema.resolve_try_expr(try_expr).map(OperatorClass::Try)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn classify_index(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
index_expr: &ast::IndexExpr,
|
||||||
|
) -> Option<OperatorClass> {
|
||||||
|
sema.resolve_index_expr(index_expr).map(OperatorClass::Index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn classify_bin(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
bin_expr: &ast::BinExpr,
|
||||||
|
) -> Option<OperatorClass> {
|
||||||
|
sema.resolve_bin_expr(bin_expr).map(OperatorClass::Bin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than
|
/// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than
|
||||||
/// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a
|
/// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a
|
||||||
/// reference most of the time, but there are a couple of annoying exceptions.
|
/// reference most of the time, but there are a couple of annoying exceptions.
|
||||||
|
@ -57,7 +57,7 @@
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Structural Search Replace**
|
// | VS Code | **rust-analyzer: Structural Search Replace**
|
||||||
// |===
|
// |===
|
||||||
//
|
//
|
||||||
// Also available as an assist, by writing a comment containing the structural
|
// Also available as an assist, by writing a comment containing the structural
|
||||||
|
@ -7,7 +7,7 @@ use ide_db::{
|
|||||||
search::FileReference,
|
search::FileReference,
|
||||||
FxIndexMap, RootDatabase,
|
FxIndexMap, RootDatabase,
|
||||||
};
|
};
|
||||||
use syntax::{ast, AstNode, SyntaxKind::NAME, TextRange};
|
use syntax::{ast, AstNode, SyntaxKind::IDENT, TextRange};
|
||||||
|
|
||||||
use crate::{goto_definition, FilePosition, NavigationTarget, RangeInfo, TryToNav};
|
use crate::{goto_definition, FilePosition, NavigationTarget, RangeInfo, TryToNav};
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
|
|||||||
let file = sema.parse(file_id);
|
let file = sema.parse(file_id);
|
||||||
let file = file.syntax();
|
let file = file.syntax();
|
||||||
let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
|
let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind {
|
||||||
NAME => 1,
|
IDENT => 1,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
})?;
|
})?;
|
||||||
let mut calls = CallLocations::default();
|
let mut calls = CallLocations::default();
|
||||||
|
@ -19,7 +19,7 @@ pub struct ExpandedMacro {
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Expand macro recursively**
|
// | VS Code | **rust-analyzer: Expand macro recursively**
|
||||||
// |===
|
// |===
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[]
|
||||||
@ -32,7 +32,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
|
|||||||
_ => 0,
|
_ => 0,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// due to how Rust Analyzer works internally, we need to special case derive attributes,
|
// due to how rust-analyzer works internally, we need to special case derive attributes,
|
||||||
// otherwise they might not get found, e.g. here with the cursor at $0 `#[attr]` would expand:
|
// otherwise they might not get found, e.g. here with the cursor at $0 `#[attr]` would expand:
|
||||||
// ```
|
// ```
|
||||||
// #[attr]
|
// #[attr]
|
||||||
|
@ -39,7 +39,11 @@ pub(crate) fn goto_definition(
|
|||||||
| T![super]
|
| T![super]
|
||||||
| T![crate]
|
| T![crate]
|
||||||
| T![Self]
|
| T![Self]
|
||||||
| COMMENT => 2,
|
| COMMENT => 4,
|
||||||
|
// index and prefix ops
|
||||||
|
T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
|
||||||
|
kind if kind.is_keyword() => 2,
|
||||||
|
T!['('] | T![')'] => 2,
|
||||||
kind if kind.is_trivia() => 0,
|
kind if kind.is_trivia() => 0,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
})?;
|
})?;
|
||||||
@ -1628,6 +1632,122 @@ macro_rules! foo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foo!(bar$0);
|
foo!(bar$0);
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_await_poll() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: future
|
||||||
|
|
||||||
|
struct MyFut;
|
||||||
|
|
||||||
|
impl core::future::Future for MyFut {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(
|
||||||
|
//^^^^
|
||||||
|
self: std::pin::Pin<&mut Self>,
|
||||||
|
cx: &mut std::task::Context<'_>
|
||||||
|
) -> std::task::Poll<Self::Output>
|
||||||
|
{
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
MyFut.await$0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_try_op() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: try
|
||||||
|
|
||||||
|
struct Struct;
|
||||||
|
|
||||||
|
impl core::ops::Try for Struct {
|
||||||
|
fn branch(
|
||||||
|
//^^^^^^
|
||||||
|
self
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
Struct?$0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_index_op() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: index
|
||||||
|
|
||||||
|
struct Struct;
|
||||||
|
|
||||||
|
impl core::ops::Index<usize> for Struct {
|
||||||
|
fn index(
|
||||||
|
//^^^^^
|
||||||
|
self
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
Struct[0]$0;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_prefix_op() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: deref
|
||||||
|
|
||||||
|
struct Struct;
|
||||||
|
|
||||||
|
impl core::ops::Deref for Struct {
|
||||||
|
fn deref(
|
||||||
|
//^^^^^
|
||||||
|
self
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
$0*Struct;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_bin_op() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: add
|
||||||
|
|
||||||
|
struct Struct;
|
||||||
|
|
||||||
|
impl core::ops::Add for Struct {
|
||||||
|
fn add(
|
||||||
|
//^^^
|
||||||
|
self
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
Struct +$0 Struct;
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ pub(crate) fn goto_implementation(
|
|||||||
|
|
||||||
let original_token =
|
let original_token =
|
||||||
pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
|
pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
|
||||||
IDENT | T![self] => 1,
|
IDENT | T![self] | INT_NUMBER => 1,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
})?;
|
})?;
|
||||||
let range = original_token.text_range();
|
let range = original_token.text_range();
|
||||||
|
@ -333,7 +333,8 @@ fn cover_range(r0: Option<TextRange>, r1: Option<TextRange>) -> Option<TextRange
|
|||||||
fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
|
fn find_defs(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> FxHashSet<Definition> {
|
||||||
sema.descend_into_macros(token)
|
sema.descend_into_macros(token)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|token| IdentClass::classify_token(sema, &token).map(IdentClass::definitions))
|
.filter_map(|token| IdentClass::classify_token(sema, &token))
|
||||||
|
.map(IdentClass::definitions_no_ops)
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use either::Either;
|
|||||||
use hir::{HasSource, Semantics};
|
use hir::{HasSource, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::FileRange,
|
base_db::FileRange,
|
||||||
defs::{Definition, IdentClass},
|
defs::{Definition, IdentClass, OperatorClass},
|
||||||
famous_defs::FamousDefs,
|
famous_defs::FamousDefs,
|
||||||
helpers::pick_best_token,
|
helpers::pick_best_token,
|
||||||
FxIndexSet, RootDatabase,
|
FxIndexSet, RootDatabase,
|
||||||
@ -101,7 +101,10 @@ pub(crate) fn hover(
|
|||||||
let offset = range.start();
|
let offset = range.start();
|
||||||
|
|
||||||
let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
|
let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind {
|
||||||
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 3,
|
IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 4,
|
||||||
|
// index and prefix ops
|
||||||
|
T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3,
|
||||||
|
kind if kind.is_keyword() => 2,
|
||||||
T!['('] | T![')'] => 2,
|
T!['('] | T![')'] => 2,
|
||||||
kind if kind.is_trivia() => 0,
|
kind if kind.is_trivia() => 0,
|
||||||
_ => 1,
|
_ => 1,
|
||||||
@ -136,6 +139,11 @@ pub(crate) fn hover(
|
|||||||
.filter_map(|token| {
|
.filter_map(|token| {
|
||||||
let node = token.parent()?;
|
let node = token.parent()?;
|
||||||
let class = IdentClass::classify_token(sema, token)?;
|
let class = IdentClass::classify_token(sema, token)?;
|
||||||
|
if let IdentClass::Operator(OperatorClass::Await(_)) = class {
|
||||||
|
// It's better for us to fall back to the keyword hover here,
|
||||||
|
// rendering poll is very confusing
|
||||||
|
return None;
|
||||||
|
}
|
||||||
Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
|
Some(class.definitions().into_iter().zip(iter::once(node).cycle()))
|
||||||
})
|
})
|
||||||
.flatten()
|
.flatten()
|
||||||
@ -232,10 +240,12 @@ fn hover_type_fallback(
|
|||||||
token: &SyntaxToken,
|
token: &SyntaxToken,
|
||||||
original_token: &SyntaxToken,
|
original_token: &SyntaxToken,
|
||||||
) -> Option<RangeInfo<HoverResult>> {
|
) -> Option<RangeInfo<HoverResult>> {
|
||||||
let node = token
|
let node =
|
||||||
.parent_ancestors()
|
token.parent_ancestors().take_while(|it| !ast::Item::can_cast(it.kind())).find(|n| {
|
||||||
.take_while(|it| !ast::Item::can_cast(it.kind()))
|
ast::Expr::can_cast(n.kind())
|
||||||
.find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?;
|
|| ast::Pat::can_cast(n.kind())
|
||||||
|
|| ast::Type::can_cast(n.kind())
|
||||||
|
})?;
|
||||||
|
|
||||||
let expr_or_pat = match_ast! {
|
let expr_or_pat = match_ast! {
|
||||||
match node {
|
match node {
|
||||||
|
@ -5051,3 +5051,37 @@ fn f() {
|
|||||||
```"#]],
|
```"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hover_deref() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- minicore: deref
|
||||||
|
|
||||||
|
struct Struct(usize);
|
||||||
|
|
||||||
|
impl core::ops::Deref for Struct {
|
||||||
|
type Target = usize;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn f() {
|
||||||
|
$0*Struct(0);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
***
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test::Struct
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn deref(&self) -> &Self::Target
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -100,7 +100,7 @@ pub enum InlayTooltip {
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Toggle inlay hints*
|
// | VS Code | **rust-analyzer: Toggle inlay hints*
|
||||||
// |===
|
// |===
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
|
// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
|
||||||
|
@ -28,7 +28,7 @@ pub struct JoinLinesConfig {
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Join lines**
|
// | VS Code | **rust-analyzer: Join lines**
|
||||||
// |===
|
// |===
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[]
|
||||||
|
@ -12,7 +12,7 @@ use syntax::{
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Find matching brace**
|
// | VS Code | **rust-analyzer: Find matching brace**
|
||||||
// |===
|
// |===
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[]
|
||||||
|
@ -90,7 +90,7 @@ pub(crate) fn moniker(
|
|||||||
.descend_into_macros(original_token.clone())
|
.descend_into_macros(original_token.clone())
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|token| {
|
.filter_map(|token| {
|
||||||
IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| {
|
IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| {
|
||||||
it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
|
it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -19,8 +19,8 @@ pub enum Direction {
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Move item up**
|
// | VS Code | **rust-analyzer: Move item up**
|
||||||
// | VS Code | **Rust Analyzer: Move item down**
|
// | VS Code | **rust-analyzer: Move item down**
|
||||||
// |===
|
// |===
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[]
|
||||||
|
@ -18,7 +18,7 @@ use crate::NavigationTarget;
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Locate parent module**
|
// | VS Code | **rust-analyzer: Locate parent module**
|
||||||
// |===
|
// |===
|
||||||
//
|
//
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[]
|
||||||
|
@ -116,7 +116,7 @@ impl Runnable {
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Run**
|
// | VS Code | **rust-analyzer: Run**
|
||||||
// |===
|
// |===
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[]
|
||||||
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
||||||
@ -202,7 +202,7 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Peek related tests**
|
// | VS Code | **rust-analyzer: Peek related tests**
|
||||||
// |===
|
// |===
|
||||||
pub(crate) fn related_tests(
|
pub(crate) fn related_tests(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
@ -373,11 +373,13 @@ pub(crate) fn runnable_impl(
|
|||||||
let adt_name = ty.as_adt()?.name(sema.db);
|
let adt_name = ty.as_adt()?.name(sema.db);
|
||||||
let mut ty_args = ty.type_arguments().peekable();
|
let mut ty_args = ty.type_arguments().peekable();
|
||||||
let params = if ty_args.peek().is_some() {
|
let params = if ty_args.peek().is_some() {
|
||||||
format!("<{}>", ty_args.format_with(", ", |ty, cb| cb(&ty.display(sema.db))))
|
format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db))))
|
||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
};
|
};
|
||||||
let test_id = TestId::Path(format!("{}{}", adt_name, params));
|
let mut test_id = format!("{}{}", adt_name, params);
|
||||||
|
test_id.retain(|c| c != ' ');
|
||||||
|
let test_id = TestId::Path(test_id);
|
||||||
|
|
||||||
Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg })
|
Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg })
|
||||||
}
|
}
|
||||||
@ -441,10 +443,11 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> {
|
|||||||
format_to!(
|
format_to!(
|
||||||
path,
|
path,
|
||||||
"<{}>",
|
"<{}>",
|
||||||
ty_args.format_with(", ", |ty, cb| cb(&ty.display(db)))
|
ty_args.format_with(",", |ty, cb| cb(&ty.display(db)))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
format_to!(path, "::{}", def_name);
|
format_to!(path, "::{}", def_name);
|
||||||
|
path.retain(|c| c != ' ');
|
||||||
return Some(path);
|
return Some(path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2067,13 +2070,23 @@ mod tests {
|
|||||||
$0
|
$0
|
||||||
struct Foo<T, U>;
|
struct Foo<T, U>;
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// ```
|
||||||
impl<T, U> Foo<T, U> {
|
impl<T, U> Foo<T, U> {
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// ````
|
/// ````
|
||||||
fn t() {}
|
fn t() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ```
|
||||||
|
/// ```
|
||||||
|
impl Foo<Foo<(), ()>, ()> {
|
||||||
|
/// ```
|
||||||
|
/// ```
|
||||||
|
fn t() {}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
&[DocTest],
|
&[DocTest, DocTest, DocTest, DocTest],
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
[
|
[
|
||||||
Runnable {
|
Runnable {
|
||||||
@ -2082,12 +2095,64 @@ impl<T, U> Foo<T, U> {
|
|||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
full_range: 47..85,
|
full_range: 20..103,
|
||||||
|
focus_range: 47..56,
|
||||||
|
name: "impl",
|
||||||
|
kind: Impl,
|
||||||
|
},
|
||||||
|
kind: DocTest {
|
||||||
|
test_id: Path(
|
||||||
|
"Foo<T,U>",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
cfg: None,
|
||||||
|
},
|
||||||
|
Runnable {
|
||||||
|
use_name_in_title: false,
|
||||||
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
full_range: 63..101,
|
||||||
name: "t",
|
name: "t",
|
||||||
},
|
},
|
||||||
kind: DocTest {
|
kind: DocTest {
|
||||||
test_id: Path(
|
test_id: Path(
|
||||||
"Foo<T, U>::t",
|
"Foo<T,U>::t",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
cfg: None,
|
||||||
|
},
|
||||||
|
Runnable {
|
||||||
|
use_name_in_title: false,
|
||||||
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
full_range: 105..188,
|
||||||
|
focus_range: 126..146,
|
||||||
|
name: "impl",
|
||||||
|
kind: Impl,
|
||||||
|
},
|
||||||
|
kind: DocTest {
|
||||||
|
test_id: Path(
|
||||||
|
"Foo<Foo<(),()>,()>",
|
||||||
|
),
|
||||||
|
},
|
||||||
|
cfg: None,
|
||||||
|
},
|
||||||
|
Runnable {
|
||||||
|
use_name_in_title: false,
|
||||||
|
nav: NavigationTarget {
|
||||||
|
file_id: FileId(
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
full_range: 153..186,
|
||||||
|
name: "t",
|
||||||
|
},
|
||||||
|
kind: DocTest {
|
||||||
|
test_id: Path(
|
||||||
|
"Foo<Foo<(),()>,()>::t",
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
cfg: None,
|
cfg: None,
|
||||||
|
@ -12,7 +12,7 @@ use ide_db::{
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Shuffle Crate Graph**
|
// | VS Code | **rust-analyzer: Shuffle Crate Graph**
|
||||||
// |===
|
// |===
|
||||||
pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
|
pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) {
|
||||||
let crate_graph = db.crate_graph();
|
let crate_graph = db.crate_graph();
|
||||||
|
@ -204,7 +204,7 @@ impl StaticIndex<'_> {
|
|||||||
|
|
||||||
fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Definition> {
|
fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option<Definition> {
|
||||||
for token in sema.descend_into_macros(token) {
|
for token in sema.descend_into_macros(token) {
|
||||||
let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions);
|
let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
|
||||||
if let Some(&[x]) = def.as_deref() {
|
if let Some(&[x]) = def.as_deref() {
|
||||||
return Some(x);
|
return Some(x);
|
||||||
} else {
|
} else {
|
||||||
|
@ -29,7 +29,7 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Status**
|
// | VS Code | **rust-analyzer: Status**
|
||||||
// |===
|
// |===
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[]
|
||||||
pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
|
pub(crate) fn status(db: &RootDatabase, file_id: Option<FileId>) -> String {
|
||||||
|
@ -958,7 +958,7 @@ pub struct Struct;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
all(unix, not(target_pointer_width = "64")),
|
not(all(unix, target_pointer_width = "64")),
|
||||||
ignore = "depends on `DefaultHasher` outputs"
|
ignore = "depends on `DefaultHasher` outputs"
|
||||||
)]
|
)]
|
||||||
fn test_rainbow_highlighting() {
|
fn test_rainbow_highlighting() {
|
||||||
|
@ -12,7 +12,7 @@ use syntax::{
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Show Syntax Tree**
|
// | VS Code | **rust-analyzer: Show Syntax Tree**
|
||||||
// |===
|
// |===
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[]
|
||||||
pub(crate) fn syntax_tree(
|
pub(crate) fn syntax_tree(
|
||||||
|
@ -16,7 +16,7 @@ use ide_db::{
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: View Crate Graph**
|
// | VS Code | **rust-analyzer: View Crate Graph**
|
||||||
// |===
|
// |===
|
||||||
pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> {
|
pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result<String, String> {
|
||||||
let crate_graph = db.crate_graph();
|
let crate_graph = db.crate_graph();
|
||||||
|
@ -8,7 +8,7 @@ use syntax::{algo::find_node_at_offset, ast, AstNode};
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: View Hir**
|
// | VS Code | **rust-analyzer: View Hir**
|
||||||
// |===
|
// |===
|
||||||
// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[]
|
// image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[]
|
||||||
pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
|
pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String {
|
||||||
|
@ -9,7 +9,7 @@ use ide_db::RootDatabase;
|
|||||||
// |===
|
// |===
|
||||||
// | Editor | Action Name
|
// | Editor | Action Name
|
||||||
//
|
//
|
||||||
// | VS Code | **Rust Analyzer: Debug ItemTree**
|
// | VS Code | **rust-analyzer: Debug ItemTree**
|
||||||
// |===
|
// |===
|
||||||
pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
|
pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String {
|
||||||
db.file_item_tree(file_id.into()).pretty_print()
|
db.file_item_tree(file_id.into()).pretty_print()
|
||||||
|
@ -106,6 +106,14 @@ impl AsRef<Path> for AbsPath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ToOwned for AbsPath {
|
||||||
|
type Owned = AbsPathBuf;
|
||||||
|
|
||||||
|
fn to_owned(&self) -> Self::Owned {
|
||||||
|
AbsPathBuf(self.0.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a Path> for &'a AbsPath {
|
impl<'a> TryFrom<&'a Path> for &'a AbsPath {
|
||||||
type Error = &'a Path;
|
type Error = &'a Path;
|
||||||
fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> {
|
fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> {
|
||||||
|
@ -60,7 +60,7 @@ impl MacroDylib {
|
|||||||
|
|
||||||
let info = version::read_dylib_info(&path)?;
|
let info = version::read_dylib_info(&path)?;
|
||||||
if info.version.0 < 1 || info.version.1 < 47 {
|
if info.version.0 < 1 || info.version.1 < 47 {
|
||||||
let msg = format!("proc-macro {} built by {:#?} is not supported by Rust Analyzer, please update your rust version.", path.display(), info);
|
let msg = format!("proc-macro {} built by {:#?} is not supported by rust-analyzer, please update your Rust version.", path.display(), info);
|
||||||
return Err(io::Error::new(io::ErrorKind::InvalidData, msg));
|
return Err(io::Error::new(io::ErrorKind::InvalidData, msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47).
|
//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47).
|
||||||
//!
|
//!
|
||||||
//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple
|
//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple
|
||||||
//! interface the rest of rust analyzer can use to talk to the macro
|
//! interface the rest of rust-analyzer can use to talk to the macro
|
||||||
//! provider.
|
//! provider.
|
||||||
//!
|
//!
|
||||||
//! # Adding a new ABI
|
//! # Adding a new ABI
|
||||||
|
@ -19,7 +19,7 @@ use crate::{utf8_stdout, ManifestPath};
|
|||||||
/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
|
/// [`CargoWorkspace`] represents the logical structure of, well, a Cargo
|
||||||
/// workspace. It pretty closely mirrors `cargo metadata` output.
|
/// workspace. It pretty closely mirrors `cargo metadata` output.
|
||||||
///
|
///
|
||||||
/// Note that internally, rust analyzer uses a different structure:
|
/// Note that internally, rust-analyzer uses a different structure:
|
||||||
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
|
/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
|
||||||
/// while this knows about `Packages` & `Targets`: purely cargo-related
|
/// while this knows about `Packages` & `Targets`: purely cargo-related
|
||||||
/// concepts.
|
/// concepts.
|
||||||
|
@ -8,7 +8,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
|||||||
|
|
||||||
use crate::lsp_ext;
|
use crate::lsp_ext;
|
||||||
|
|
||||||
pub(crate) type CheckFixes = Arc<FxHashMap<FileId, Vec<Fix>>>;
|
pub(crate) type CheckFixes = Arc<FxHashMap<usize, FxHashMap<FileId, Vec<Fix>>>>;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct DiagnosticsMapConfig {
|
pub struct DiagnosticsMapConfig {
|
||||||
@ -22,7 +22,7 @@ pub(crate) struct DiagnosticCollection {
|
|||||||
// FIXME: should be FxHashMap<FileId, Vec<ra_id::Diagnostic>>
|
// FIXME: should be FxHashMap<FileId, Vec<ra_id::Diagnostic>>
|
||||||
pub(crate) native: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
|
pub(crate) native: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
|
||||||
// FIXME: should be Vec<flycheck::Diagnostic>
|
// FIXME: should be Vec<flycheck::Diagnostic>
|
||||||
pub(crate) check: FxHashMap<FileId, Vec<lsp_types::Diagnostic>>,
|
pub(crate) check: FxHashMap<usize, FxHashMap<FileId, Vec<lsp_types::Diagnostic>>>,
|
||||||
pub(crate) check_fixes: CheckFixes,
|
pub(crate) check_fixes: CheckFixes,
|
||||||
changes: FxHashSet<FileId>,
|
changes: FxHashSet<FileId>,
|
||||||
}
|
}
|
||||||
@ -35,9 +35,19 @@ pub(crate) struct Fix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DiagnosticCollection {
|
impl DiagnosticCollection {
|
||||||
pub(crate) fn clear_check(&mut self) {
|
pub(crate) fn clear_check(&mut self, flycheck_id: usize) {
|
||||||
|
if let Some(it) = Arc::make_mut(&mut self.check_fixes).get_mut(&flycheck_id) {
|
||||||
|
it.clear();
|
||||||
|
}
|
||||||
|
if let Some(it) = self.check.get_mut(&flycheck_id) {
|
||||||
|
self.changes.extend(it.drain().map(|(key, _value)| key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn clear_check_all(&mut self) {
|
||||||
Arc::make_mut(&mut self.check_fixes).clear();
|
Arc::make_mut(&mut self.check_fixes).clear();
|
||||||
self.changes.extend(self.check.drain().map(|(key, _value)| key))
|
self.changes
|
||||||
|
.extend(self.check.values_mut().flat_map(|it| it.drain().map(|(key, _value)| key)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_native_for(&mut self, file_id: FileId) {
|
pub(crate) fn clear_native_for(&mut self, file_id: FileId) {
|
||||||
@ -47,11 +57,12 @@ impl DiagnosticCollection {
|
|||||||
|
|
||||||
pub(crate) fn add_check_diagnostic(
|
pub(crate) fn add_check_diagnostic(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
flycheck_id: usize,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
diagnostic: lsp_types::Diagnostic,
|
diagnostic: lsp_types::Diagnostic,
|
||||||
fix: Option<Fix>,
|
fix: Option<Fix>,
|
||||||
) {
|
) {
|
||||||
let diagnostics = self.check.entry(file_id).or_default();
|
let diagnostics = self.check.entry(flycheck_id).or_default().entry(file_id).or_default();
|
||||||
for existing_diagnostic in diagnostics.iter() {
|
for existing_diagnostic in diagnostics.iter() {
|
||||||
if are_diagnostics_equal(existing_diagnostic, &diagnostic) {
|
if are_diagnostics_equal(existing_diagnostic, &diagnostic) {
|
||||||
return;
|
return;
|
||||||
@ -59,7 +70,7 @@ impl DiagnosticCollection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let check_fixes = Arc::make_mut(&mut self.check_fixes);
|
let check_fixes = Arc::make_mut(&mut self.check_fixes);
|
||||||
check_fixes.entry(file_id).or_default().extend(fix);
|
check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().extend(fix);
|
||||||
diagnostics.push(diagnostic);
|
diagnostics.push(diagnostic);
|
||||||
self.changes.insert(file_id);
|
self.changes.insert(file_id);
|
||||||
}
|
}
|
||||||
@ -89,7 +100,8 @@ impl DiagnosticCollection {
|
|||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
) -> impl Iterator<Item = &lsp_types::Diagnostic> {
|
) -> impl Iterator<Item = &lsp_types::Diagnostic> {
|
||||||
let native = self.native.get(&file_id).into_iter().flatten();
|
let native = self.native.get(&file_id).into_iter().flatten();
|
||||||
let check = self.check.get(&file_id).into_iter().flatten();
|
let check =
|
||||||
|
self.check.values().filter_map(move |it| it.get(&file_id)).into_iter().flatten();
|
||||||
native.chain(check)
|
native.chain(check)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use std::{sync::Arc, time::Instant};
|
|||||||
use crossbeam_channel::{unbounded, Receiver, Sender};
|
use crossbeam_channel::{unbounded, Receiver, Sender};
|
||||||
use flycheck::FlycheckHandle;
|
use flycheck::FlycheckHandle;
|
||||||
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
|
use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId};
|
||||||
use ide_db::base_db::{CrateId, FileLoader, SourceDatabase};
|
use ide_db::base_db::{CrateId, FileLoader, SourceDatabase, SourceDatabaseExt};
|
||||||
use lsp_types::{SemanticTokens, Url};
|
use lsp_types::{SemanticTokens, Url};
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use proc_macro_api::ProcMacroServer;
|
use proc_macro_api::ProcMacroServer;
|
||||||
@ -176,7 +176,7 @@ impl GlobalState {
|
|||||||
|
|
||||||
pub(crate) fn process_changes(&mut self) -> bool {
|
pub(crate) fn process_changes(&mut self) -> bool {
|
||||||
let _p = profile::span("GlobalState::process_changes");
|
let _p = profile::span("GlobalState::process_changes");
|
||||||
let mut fs_changes = Vec::new();
|
let mut fs_refresh_changes = Vec::new();
|
||||||
// A file was added or deleted
|
// A file was added or deleted
|
||||||
let mut has_structure_changes = false;
|
let mut has_structure_changes = false;
|
||||||
|
|
||||||
@ -192,15 +192,14 @@ impl GlobalState {
|
|||||||
if let Some(path) = vfs.file_path(file.file_id).as_path() {
|
if let Some(path) = vfs.file_path(file.file_id).as_path() {
|
||||||
let path = path.to_path_buf();
|
let path = path.to_path_buf();
|
||||||
if reload::should_refresh_for_change(&path, file.change_kind) {
|
if reload::should_refresh_for_change(&path, file.change_kind) {
|
||||||
self.fetch_workspaces_queue
|
fs_refresh_changes.push((path, file.file_id));
|
||||||
.request_op(format!("vfs file change: {}", path.display()));
|
|
||||||
}
|
}
|
||||||
fs_changes.push((path, file.change_kind));
|
|
||||||
if file.is_created_or_deleted() {
|
if file.is_created_or_deleted() {
|
||||||
has_structure_changes = true;
|
has_structure_changes = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear native diagnostics when their file gets deleted
|
||||||
if !file.exists() {
|
if !file.exists() {
|
||||||
self.diagnostics.clear_native_for(file.file_id);
|
self.diagnostics.clear_native_for(file.file_id);
|
||||||
}
|
}
|
||||||
@ -226,7 +225,16 @@ impl GlobalState {
|
|||||||
|
|
||||||
self.analysis_host.apply_change(change);
|
self.analysis_host.apply_change(change);
|
||||||
|
|
||||||
let raw_database = &self.analysis_host.raw_database();
|
{
|
||||||
|
let raw_database = self.analysis_host.raw_database();
|
||||||
|
let workspace_structure_change =
|
||||||
|
fs_refresh_changes.into_iter().find(|&(_, file_id)| {
|
||||||
|
!raw_database.source_root(raw_database.file_source_root(file_id)).is_library
|
||||||
|
});
|
||||||
|
if let Some((path, _)) = workspace_structure_change {
|
||||||
|
self.fetch_workspaces_queue
|
||||||
|
.request_op(format!("workspace vfs file change: {}", path.display()));
|
||||||
|
}
|
||||||
self.proc_macro_changed =
|
self.proc_macro_changed =
|
||||||
changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
|
changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {
|
||||||
let crates = raw_database.relevant_crates(file.file_id);
|
let crates = raw_database.relevant_crates(file.file_id);
|
||||||
@ -234,6 +242,8 @@ impl GlobalState {
|
|||||||
|
|
||||||
crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
|
crates.iter().any(|&krate| crate_graph[krate].is_proc_macro)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1094,7 +1094,9 @@ pub(crate) fn handle_code_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fixes from `cargo check`.
|
// Fixes from `cargo check`.
|
||||||
for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() {
|
for fix in
|
||||||
|
snap.check_fixes.values().filter_map(|it| it.get(&frange.file_id)).into_iter().flatten()
|
||||||
|
{
|
||||||
// FIXME: this mapping is awkward and shouldn't exist. Refactor
|
// FIXME: this mapping is awkward and shouldn't exist. Refactor
|
||||||
// `snap.check_fixes` to not convert to LSP prematurely.
|
// `snap.check_fixes` to not convert to LSP prematurely.
|
||||||
let intersect_fix_range = fix
|
let intersect_fix_range = fix
|
||||||
|
@ -6,8 +6,8 @@
|
|||||||
//! code here exercise this specific completion, and thus have a fast
|
//! code here exercise this specific completion, and thus have a fast
|
||||||
//! edit/compile/test cycle.
|
//! edit/compile/test cycle.
|
||||||
//!
|
//!
|
||||||
//! Note that "Rust Analyzer: Run" action does not allow running a single test
|
//! Note that "rust-analyzer: Run" action does not allow running a single test
|
||||||
//! in release mode in VS Code. There's however "Rust Analyzer: Copy Run Command Line"
|
//! in release mode in VS Code. There's however "rust-analyzer: Copy Run Command Line"
|
||||||
//! which you can use to paste the command in terminal and add `--release` manually.
|
//! which you can use to paste the command in terminal and add `--release` manually.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -2,13 +2,15 @@
|
|||||||
//! requests/replies and notifications back to the client.
|
//! requests/replies and notifications back to the client.
|
||||||
use std::{
|
use std::{
|
||||||
fmt,
|
fmt,
|
||||||
|
ops::Deref,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use always_assert::always;
|
use always_assert::always;
|
||||||
use crossbeam_channel::{select, Receiver};
|
use crossbeam_channel::{select, Receiver};
|
||||||
use ide_db::base_db::{SourceDatabaseExt, VfsPath};
|
use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
|
||||||
|
use itertools::Itertools;
|
||||||
use lsp_server::{Connection, Notification, Request};
|
use lsp_server::{Connection, Notification, Request};
|
||||||
use lsp_types::notification::Notification as _;
|
use lsp_types::notification::Notification as _;
|
||||||
use vfs::{ChangeKind, FileId};
|
use vfs::{ChangeKind, FileId};
|
||||||
@ -371,7 +373,7 @@ impl GlobalState {
|
|||||||
let _p = profile::span("GlobalState::handle_event/flycheck");
|
let _p = profile::span("GlobalState::handle_event/flycheck");
|
||||||
loop {
|
loop {
|
||||||
match task {
|
match task {
|
||||||
flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => {
|
flycheck::Message::AddDiagnostic { id, workspace_root, diagnostic } => {
|
||||||
let snap = self.snapshot();
|
let snap = self.snapshot();
|
||||||
let diagnostics =
|
let diagnostics =
|
||||||
crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
|
crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
|
||||||
@ -383,6 +385,7 @@ impl GlobalState {
|
|||||||
for diag in diagnostics {
|
for diag in diagnostics {
|
||||||
match url_to_file_id(&self.vfs.read().0, &diag.url) {
|
match url_to_file_id(&self.vfs.read().0, &diag.url) {
|
||||||
Ok(file_id) => self.diagnostics.add_check_diagnostic(
|
Ok(file_id) => self.diagnostics.add_check_diagnostic(
|
||||||
|
id,
|
||||||
file_id,
|
file_id,
|
||||||
diag.diagnostic,
|
diag.diagnostic,
|
||||||
diag.fix,
|
diag.fix,
|
||||||
@ -400,7 +403,7 @@ impl GlobalState {
|
|||||||
flycheck::Message::Progress { id, progress } => {
|
flycheck::Message::Progress { id, progress } => {
|
||||||
let (state, message) = match progress {
|
let (state, message) = match progress {
|
||||||
flycheck::Progress::DidStart => {
|
flycheck::Progress::DidStart => {
|
||||||
self.diagnostics.clear_check();
|
self.diagnostics.clear_check(id);
|
||||||
(Progress::Begin, None)
|
(Progress::Begin, None)
|
||||||
}
|
}
|
||||||
flycheck::Progress::DidCheckCrate(target) => {
|
flycheck::Progress::DidCheckCrate(target) => {
|
||||||
@ -444,7 +447,10 @@ impl GlobalState {
|
|||||||
let memdocs_added_or_removed = self.mem_docs.take_changes();
|
let memdocs_added_or_removed = self.mem_docs.take_changes();
|
||||||
|
|
||||||
if self.is_quiescent() {
|
if self.is_quiescent() {
|
||||||
if !was_quiescent {
|
if !was_quiescent
|
||||||
|
&& !self.fetch_workspaces_queue.op_requested()
|
||||||
|
&& !self.fetch_build_data_queue.op_requested()
|
||||||
|
{
|
||||||
for flycheck in &self.flycheck {
|
for flycheck in &self.flycheck {
|
||||||
flycheck.update();
|
flycheck.update();
|
||||||
}
|
}
|
||||||
@ -734,15 +740,78 @@ impl GlobalState {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})?
|
})?
|
||||||
.on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
|
.on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
|
||||||
for flycheck in &this.flycheck {
|
let mut updated = false;
|
||||||
flycheck.update();
|
if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) {
|
||||||
|
let (vfs, _) = &*this.vfs.read();
|
||||||
|
if let Some(file_id) = vfs.file_id(&vfs_path) {
|
||||||
|
let analysis = this.analysis_host.analysis();
|
||||||
|
// Crates containing or depending on the saved file
|
||||||
|
let crate_ids: Vec<_> = analysis
|
||||||
|
.crate_for(file_id)?
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|id| {
|
||||||
|
this.analysis_host
|
||||||
|
.raw_database()
|
||||||
|
.crate_graph()
|
||||||
|
.transitive_rev_deps(id)
|
||||||
|
})
|
||||||
|
.sorted()
|
||||||
|
.unique()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let crate_root_paths: Vec<_> = crate_ids
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&crate_id| {
|
||||||
|
analysis
|
||||||
|
.crate_root(crate_id)
|
||||||
|
.map(|file_id| {
|
||||||
|
vfs.file_path(file_id).as_path().map(ToOwned::to_owned)
|
||||||
|
})
|
||||||
|
.transpose()
|
||||||
|
})
|
||||||
|
.collect::<ide::Cancellable<_>>()?;
|
||||||
|
let crate_root_paths: Vec<_> =
|
||||||
|
crate_root_paths.iter().map(Deref::deref).collect();
|
||||||
|
|
||||||
|
// Find all workspaces that have at least one target containing the saved file
|
||||||
|
let workspace_ids =
|
||||||
|
this.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
|
||||||
|
project_model::ProjectWorkspace::Cargo { cargo, .. } => {
|
||||||
|
cargo.packages().any(|pkg| {
|
||||||
|
cargo[pkg].targets.iter().any(|&it| {
|
||||||
|
crate_root_paths.contains(&cargo[it].root.as_path())
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if let Ok(abs_path) = from_proto::abs_path(¶ms.text_document.uri) {
|
project_model::ProjectWorkspace::Json { project, .. } => project
|
||||||
|
.crates()
|
||||||
|
.any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)),
|
||||||
|
project_model::ProjectWorkspace::DetachedFiles { .. } => false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Find and trigger corresponding flychecks
|
||||||
|
for flycheck in &this.flycheck {
|
||||||
|
for (id, _) in workspace_ids.clone() {
|
||||||
|
if id == flycheck.id() {
|
||||||
|
updated = true;
|
||||||
|
flycheck.update();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(abs_path) = vfs_path.as_path() {
|
||||||
if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) {
|
if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) {
|
||||||
this.fetch_workspaces_queue
|
this.fetch_workspaces_queue
|
||||||
.request_op(format!("DidSaveTextDocument {}", abs_path.display()));
|
.request_op(format!("DidSaveTextDocument {}", abs_path.display()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if !updated {
|
||||||
|
for flycheck in &this.flycheck {
|
||||||
|
flycheck.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?
|
})?
|
||||||
.on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
|
.on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
|
||||||
|
@ -196,10 +196,7 @@ impl GlobalState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Err(error) = self.fetch_build_data_error() {
|
if let Err(error) = self.fetch_build_data_error() {
|
||||||
self.show_and_log_error(
|
self.show_and_log_error("failed to run build scripts".to_string(), Some(error));
|
||||||
"rust-analyzer failed to run build scripts".to_string(),
|
|
||||||
Some(error),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let workspaces = self
|
let workspaces = self
|
||||||
@ -308,6 +305,7 @@ impl GlobalState {
|
|||||||
|
|
||||||
if self.proc_macro_clients.is_empty() {
|
if self.proc_macro_clients.is_empty() {
|
||||||
if let Some((path, args)) = self.config.proc_macro_srv() {
|
if let Some((path, args)) = self.config.proc_macro_srv() {
|
||||||
|
tracing::info!("Spawning proc-macro servers");
|
||||||
self.proc_macro_clients = self
|
self.proc_macro_clients = self
|
||||||
.workspaces
|
.workspaces
|
||||||
.iter()
|
.iter()
|
||||||
@ -316,20 +314,20 @@ impl GlobalState {
|
|||||||
let mut path = path.clone();
|
let mut path = path.clone();
|
||||||
|
|
||||||
if let ProjectWorkspace::Cargo { sysroot, .. } = ws {
|
if let ProjectWorkspace::Cargo { sysroot, .. } = ws {
|
||||||
tracing::info!("Found a cargo workspace...");
|
tracing::debug!("Found a cargo workspace...");
|
||||||
if let Some(sysroot) = sysroot.as_ref() {
|
if let Some(sysroot) = sysroot.as_ref() {
|
||||||
tracing::info!("Found a cargo workspace with a sysroot...");
|
tracing::debug!("Found a cargo workspace with a sysroot...");
|
||||||
let server_path =
|
let server_path =
|
||||||
sysroot.root().join("libexec").join(&standalone_server_name);
|
sysroot.root().join("libexec").join(&standalone_server_name);
|
||||||
if std::fs::metadata(&server_path).is_ok() {
|
if std::fs::metadata(&server_path).is_ok() {
|
||||||
tracing::info!(
|
tracing::debug!(
|
||||||
"And the server exists at {}",
|
"And the server exists at {}",
|
||||||
server_path.display()
|
server_path.display()
|
||||||
);
|
);
|
||||||
path = server_path;
|
path = server_path;
|
||||||
args = vec![];
|
args = vec![];
|
||||||
} else {
|
} else {
|
||||||
tracing::info!(
|
tracing::debug!(
|
||||||
"And the server does not exist at {}",
|
"And the server does not exist at {}",
|
||||||
server_path.display()
|
server_path.display()
|
||||||
);
|
);
|
||||||
@ -337,14 +335,10 @@ impl GlobalState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!(
|
tracing::info!(?args, "Using proc-macro server at {}", path.display(),);
|
||||||
"Using proc-macro server at {} with args {:?}",
|
|
||||||
path.display(),
|
|
||||||
args
|
|
||||||
);
|
|
||||||
ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
|
ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| {
|
||||||
let error = format!(
|
let error = format!(
|
||||||
"Failed to run proc_macro_srv from path {}, error: {:?}",
|
"Failed to run proc-macro server from path {}, error: {:?}",
|
||||||
path.display(),
|
path.display(),
|
||||||
err
|
err
|
||||||
);
|
);
|
||||||
@ -458,7 +452,7 @@ impl GlobalState {
|
|||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => {
|
None => {
|
||||||
self.flycheck = Vec::new();
|
self.flycheck = Vec::new();
|
||||||
self.diagnostics.clear_check();
|
self.diagnostics.clear_check_all();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -621,7 +615,10 @@ pub(crate) fn load_proc_macro(
|
|||||||
};
|
};
|
||||||
let expander: Arc<dyn ProcMacroExpander> =
|
let expander: Arc<dyn ProcMacroExpander> =
|
||||||
if dummy_replace.iter().any(|replace| &**replace == name) {
|
if dummy_replace.iter().any(|replace| &**replace == name) {
|
||||||
Arc::new(DummyExpander)
|
match kind {
|
||||||
|
ProcMacroKind::Attr => Arc::new(IdentityExpander),
|
||||||
|
_ => Arc::new(EmptyExpander),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Arc::new(Expander(expander))
|
Arc::new(Expander(expander))
|
||||||
};
|
};
|
||||||
@ -647,11 +644,11 @@ pub(crate) fn load_proc_macro(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dummy identity expander, used for proc-macros that are deliberately ignored by the user.
|
/// Dummy identity expander, used for attribute proc-macros that are deliberately ignored by the user.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct DummyExpander;
|
struct IdentityExpander;
|
||||||
|
|
||||||
impl ProcMacroExpander for DummyExpander {
|
impl ProcMacroExpander for IdentityExpander {
|
||||||
fn expand(
|
fn expand(
|
||||||
&self,
|
&self,
|
||||||
subtree: &tt::Subtree,
|
subtree: &tt::Subtree,
|
||||||
@ -661,27 +658,46 @@ pub(crate) fn load_proc_macro(
|
|||||||
Ok(subtree.clone())
|
Ok(subtree.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Empty expander, used for proc-macros that are deliberately ignored by the user.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct EmptyExpander;
|
||||||
|
|
||||||
|
impl ProcMacroExpander for EmptyExpander {
|
||||||
|
fn expand(
|
||||||
|
&self,
|
||||||
|
_: &tt::Subtree,
|
||||||
|
_: Option<&tt::Subtree>,
|
||||||
|
_: &Env,
|
||||||
|
) -> Result<tt::Subtree, ProcMacroExpansionError> {
|
||||||
|
Ok(tt::Subtree::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
|
pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool {
|
||||||
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
|
const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"];
|
||||||
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
|
const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"];
|
||||||
let file_name = path.file_name().unwrap_or_default();
|
|
||||||
|
|
||||||
if file_name == "Cargo.toml" || file_name == "Cargo.lock" {
|
let file_name = match path.file_name().unwrap_or_default().to_str() {
|
||||||
|
Some(it) => it,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let "Cargo.toml" | "Cargo.lock" = file_name {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if change_kind == ChangeKind::Modify {
|
if change_kind == ChangeKind::Modify {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// .cargo/config{.toml}
|
||||||
if path.extension().unwrap_or_default() != "rs" {
|
if path.extension().unwrap_or_default() != "rs" {
|
||||||
if (file_name == "config.toml" || file_name == "config")
|
let is_cargo_config = matches!(file_name, "config.toml" | "config")
|
||||||
&& path.parent().map(|parent| parent.as_ref().ends_with(".cargo")) == Some(true)
|
&& path.parent().map(|parent| parent.as_ref().ends_with(".cargo")).unwrap_or(false);
|
||||||
{
|
return is_cargo_config;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) {
|
if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,8 @@ use xshell::cmd;
|
|||||||
fn check_code_formatting() {
|
fn check_code_formatting() {
|
||||||
let sh = &Shell::new().unwrap();
|
let sh = &Shell::new().unwrap();
|
||||||
sh.change_dir(sourcegen::project_root());
|
sh.change_dir(sourcegen::project_root());
|
||||||
sh.set_var("RUSTUP_TOOLCHAIN", "stable");
|
|
||||||
|
|
||||||
let out = cmd!(sh, "rustfmt --version").read().unwrap();
|
let out = cmd!(sh, "rustup run stable rustfmt --version").read().unwrap();
|
||||||
if !out.contains("stable") {
|
if !out.contains("stable") {
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to run rustfmt from toolchain 'stable'. \
|
"Failed to run rustfmt from toolchain 'stable'. \
|
||||||
@ -23,9 +22,9 @@ fn check_code_formatting() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let res = cmd!(sh, "cargo fmt -- --check").run();
|
let res = cmd!(sh, "rustup run stable cargo fmt -- --check").run();
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
let _ = cmd!(sh, "cargo fmt").run();
|
let _ = cmd!(sh, "rustup run stable cargo fmt").run();
|
||||||
}
|
}
|
||||||
res.unwrap()
|
res.unwrap()
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ impl fmt::Display for Location {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_rustfmt(sh: &Shell) {
|
fn ensure_rustfmt(sh: &Shell) {
|
||||||
let version = cmd!(sh, "rustfmt --version").read().unwrap_or_default();
|
let version = cmd!(sh, "rustup run stable rustfmt --version").read().unwrap_or_default();
|
||||||
if !version.contains("stable") {
|
if !version.contains("stable") {
|
||||||
panic!(
|
panic!(
|
||||||
"Failed to run rustfmt from toolchain 'stable'. \
|
"Failed to run rustfmt from toolchain 'stable'. \
|
||||||
@ -147,10 +147,12 @@ fn ensure_rustfmt(sh: &Shell) {
|
|||||||
|
|
||||||
pub fn reformat(text: String) -> String {
|
pub fn reformat(text: String) -> String {
|
||||||
let sh = Shell::new().unwrap();
|
let sh = Shell::new().unwrap();
|
||||||
sh.set_var("RUSTUP_TOOLCHAIN", "stable");
|
|
||||||
ensure_rustfmt(&sh);
|
ensure_rustfmt(&sh);
|
||||||
let rustfmt_toml = project_root().join("rustfmt.toml");
|
let rustfmt_toml = project_root().join("rustfmt.toml");
|
||||||
let mut stdout = cmd!(sh, "rustfmt --config-path {rustfmt_toml} --config fn_single_line=true")
|
let mut stdout = cmd!(
|
||||||
|
sh,
|
||||||
|
"rustup run stable rustfmt --config-path {rustfmt_toml} --config fn_single_line=true"
|
||||||
|
)
|
||||||
.stdin(text)
|
.stdin(text)
|
||||||
.read()
|
.read()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -11,7 +11,7 @@ use crate::{
|
|||||||
ted::{self, Position},
|
ted::{self, Position},
|
||||||
AstNode, AstToken, Direction,
|
AstNode, AstToken, Direction,
|
||||||
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
|
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
|
||||||
SyntaxNode,
|
SyntaxNode, SyntaxToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::HasName;
|
use super::HasName;
|
||||||
@ -506,19 +506,7 @@ impl ast::RecordExprFieldList {
|
|||||||
|
|
||||||
let position = match self.fields().last() {
|
let position = match self.fields().last() {
|
||||||
Some(last_field) => {
|
Some(last_field) => {
|
||||||
let comma = match last_field
|
let comma = get_or_insert_comma_after(last_field.syntax());
|
||||||
.syntax()
|
|
||||||
.siblings_with_tokens(Direction::Next)
|
|
||||||
.filter_map(|it| it.into_token())
|
|
||||||
.find(|it| it.kind() == T![,])
|
|
||||||
{
|
|
||||||
Some(it) => it,
|
|
||||||
None => {
|
|
||||||
let comma = ast::make::token(T![,]);
|
|
||||||
ted::insert(Position::after(last_field.syntax()), &comma);
|
|
||||||
comma
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Position::after(comma)
|
Position::after(comma)
|
||||||
}
|
}
|
||||||
None => match self.l_curly_token() {
|
None => match self.l_curly_token() {
|
||||||
@ -579,19 +567,8 @@ impl ast::RecordPatFieldList {
|
|||||||
|
|
||||||
let position = match self.fields().last() {
|
let position = match self.fields().last() {
|
||||||
Some(last_field) => {
|
Some(last_field) => {
|
||||||
let comma = match last_field
|
let syntax = last_field.syntax();
|
||||||
.syntax()
|
let comma = get_or_insert_comma_after(syntax);
|
||||||
.siblings_with_tokens(Direction::Next)
|
|
||||||
.filter_map(|it| it.into_token())
|
|
||||||
.find(|it| it.kind() == T![,])
|
|
||||||
{
|
|
||||||
Some(it) => it,
|
|
||||||
None => {
|
|
||||||
let comma = ast::make::token(T![,]);
|
|
||||||
ted::insert(Position::after(last_field.syntax()), &comma);
|
|
||||||
comma
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Position::after(comma)
|
Position::after(comma)
|
||||||
}
|
}
|
||||||
None => match self.l_curly_token() {
|
None => match self.l_curly_token() {
|
||||||
@ -606,12 +583,53 @@ impl ast::RecordPatFieldList {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken {
|
||||||
|
let comma = match syntax
|
||||||
|
.siblings_with_tokens(Direction::Next)
|
||||||
|
.filter_map(|it| it.into_token())
|
||||||
|
.find(|it| it.kind() == T![,])
|
||||||
|
{
|
||||||
|
Some(it) => it,
|
||||||
|
None => {
|
||||||
|
let comma = ast::make::token(T![,]);
|
||||||
|
ted::insert(Position::after(syntax), &comma);
|
||||||
|
comma
|
||||||
|
}
|
||||||
|
};
|
||||||
|
comma
|
||||||
|
}
|
||||||
|
|
||||||
impl ast::StmtList {
|
impl ast::StmtList {
|
||||||
pub fn push_front(&self, statement: ast::Stmt) {
|
pub fn push_front(&self, statement: ast::Stmt) {
|
||||||
ted::insert(Position::after(self.l_curly_token().unwrap()), statement.syntax());
|
ted::insert(Position::after(self.l_curly_token().unwrap()), statement.syntax());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ast::VariantList {
|
||||||
|
pub fn add_variant(&self, variant: ast::Variant) {
|
||||||
|
let (indent, position) = match self.variants().last() {
|
||||||
|
Some(last_item) => (
|
||||||
|
IndentLevel::from_node(last_item.syntax()),
|
||||||
|
Position::after(get_or_insert_comma_after(last_item.syntax())),
|
||||||
|
),
|
||||||
|
None => match self.l_curly_token() {
|
||||||
|
Some(l_curly) => {
|
||||||
|
normalize_ws_between_braces(self.syntax());
|
||||||
|
(IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly))
|
||||||
|
}
|
||||||
|
None => (IndentLevel::single(), Position::last_child_of(self.syntax())),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let elements: Vec<SyntaxElement<_>> = vec![
|
||||||
|
make::tokens::whitespace(&format!("{}{}", "\n", indent)).into(),
|
||||||
|
variant.syntax().clone().into(),
|
||||||
|
ast::make::token(T![,]).into(),
|
||||||
|
];
|
||||||
|
ted::insert_all(position, elements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
|
fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> {
|
||||||
let l = node
|
let l = node
|
||||||
.children_with_tokens()
|
.children_with_tokens()
|
||||||
@ -661,6 +679,9 @@ impl<N: AstNode + Clone> Indent for N {}
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
use stdx::trim_indent;
|
||||||
|
use test_utils::assert_eq_text;
|
||||||
|
|
||||||
use crate::SourceFile;
|
use crate::SourceFile;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -714,4 +735,100 @@ mod tests {
|
|||||||
}",
|
}",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_variant_to_empty_enum() {
|
||||||
|
let variant = make::variant(make::name("Bar"), None).clone_for_update();
|
||||||
|
|
||||||
|
check_add_variant(
|
||||||
|
r#"
|
||||||
|
enum Foo {}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
variant,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_variant_to_non_empty_enum() {
|
||||||
|
let variant = make::variant(make::name("Baz"), None).clone_for_update();
|
||||||
|
|
||||||
|
check_add_variant(
|
||||||
|
r#"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
Baz,
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
variant,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_variant_with_tuple_field_list() {
|
||||||
|
let variant = make::variant(
|
||||||
|
make::name("Baz"),
|
||||||
|
Some(ast::FieldList::TupleFieldList(make::tuple_field_list(std::iter::once(
|
||||||
|
make::tuple_field(None, make::ty("bool")),
|
||||||
|
)))),
|
||||||
|
)
|
||||||
|
.clone_for_update();
|
||||||
|
|
||||||
|
check_add_variant(
|
||||||
|
r#"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
Baz(bool),
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
variant,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_variant_with_record_field_list() {
|
||||||
|
let variant = make::variant(
|
||||||
|
make::name("Baz"),
|
||||||
|
Some(ast::FieldList::RecordFieldList(make::record_field_list(std::iter::once(
|
||||||
|
make::record_field(None, make::name("x"), make::ty("bool")),
|
||||||
|
)))),
|
||||||
|
)
|
||||||
|
.clone_for_update();
|
||||||
|
|
||||||
|
check_add_variant(
|
||||||
|
r#"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum Foo {
|
||||||
|
Bar,
|
||||||
|
Baz { x: bool },
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
variant,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) {
|
||||||
|
let enum_ = ast_mut_from_text::<ast::Enum>(before);
|
||||||
|
enum_.variant_list().map(|it| it.add_variant(variant));
|
||||||
|
let after = enum_.to_string();
|
||||||
|
assert_eq_text!(&trim_indent(expected.trim()), &trim_indent(&after.trim()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -745,7 +745,10 @@ pub fn tuple_field(visibility: Option<ast::Visibility>, ty: ast::Type) -> ast::T
|
|||||||
pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant {
|
pub fn variant(name: ast::Name, field_list: Option<ast::FieldList>) -> ast::Variant {
|
||||||
let field_list = match field_list {
|
let field_list = match field_list {
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
Some(it) => format!("{}", it),
|
Some(it) => match it {
|
||||||
|
ast::FieldList::RecordFieldList(record) => format!(" {}", record),
|
||||||
|
ast::FieldList::TupleFieldList(tuple) => format!("{}", tuple),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
ast_from_text(&format!("enum f {{ {}{} }}", name, field_list))
|
ast_from_text(&format!("enum f {{ {}{} }}", name, field_list))
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
//! Syntax Tree library used throughout the rust analyzer.
|
//! Syntax Tree library used throughout the rust-analyzer.
|
||||||
//!
|
//!
|
||||||
//! Properties:
|
//! Properties:
|
||||||
//! - easy and fast incremental re-parsing
|
//! - easy and fast incremental re-parsing
|
||||||
|
@ -40,12 +40,15 @@ impl loader::Handle for NotifyHandle {
|
|||||||
.expect("failed to spawn thread");
|
.expect("failed to spawn thread");
|
||||||
NotifyHandle { sender, _thread: thread }
|
NotifyHandle { sender, _thread: thread }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_config(&mut self, config: loader::Config) {
|
fn set_config(&mut self, config: loader::Config) {
|
||||||
self.sender.send(Message::Config(config)).unwrap();
|
self.sender.send(Message::Config(config)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invalidate(&mut self, path: AbsPathBuf) {
|
fn invalidate(&mut self, path: AbsPathBuf) {
|
||||||
self.sender.send(Message::Invalidate(path)).unwrap();
|
self.sender.send(Message::Invalidate(path)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>> {
|
fn load_sync(&mut self, path: &AbsPath) -> Option<Vec<u8>> {
|
||||||
read(path)
|
read(path)
|
||||||
}
|
}
|
||||||
@ -70,6 +73,7 @@ impl NotifyActor {
|
|||||||
fn new(sender: loader::Sender) -> NotifyActor {
|
fn new(sender: loader::Sender) -> NotifyActor {
|
||||||
NotifyActor { sender, watched_entries: Vec::new(), watcher: None }
|
NotifyActor { sender, watched_entries: Vec::new(), watcher: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> {
|
fn next_event(&self, receiver: &Receiver<Message>) -> Option<Event> {
|
||||||
let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver);
|
let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver);
|
||||||
select! {
|
select! {
|
||||||
@ -77,9 +81,10 @@ impl NotifyActor {
|
|||||||
recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())),
|
recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(mut self, inbox: Receiver<Message>) {
|
fn run(mut self, inbox: Receiver<Message>) {
|
||||||
while let Some(event) = self.next_event(&inbox) {
|
while let Some(event) = self.next_event(&inbox) {
|
||||||
tracing::debug!("vfs-notify event: {:?}", event);
|
tracing::debug!(?event, "vfs-notify event");
|
||||||
match event {
|
match event {
|
||||||
Event::Message(msg) => match msg {
|
Event::Message(msg) => match msg {
|
||||||
Message::Config(config) => {
|
Message::Config(config) => {
|
||||||
|
@ -82,7 +82,7 @@ There's **"Run Extension (Debug Build)"** launch configuration for this in VS Co
|
|||||||
In general, I use one of the following workflows for fixing bugs and implementing features:
|
In general, I use one of the following workflows for fixing bugs and implementing features:
|
||||||
|
|
||||||
If the problem concerns only internal parts of rust-analyzer (i.e. I don't need to touch the `rust-analyzer` crate or TypeScript code), there is a unit-test for it.
|
If the problem concerns only internal parts of rust-analyzer (i.e. I don't need to touch the `rust-analyzer` crate or TypeScript code), there is a unit-test for it.
|
||||||
So, I use **Rust Analyzer: Run** action in VS Code to run this single test, and then just do printf-driven development/debugging.
|
So, I use **rust-analyzer: Run** action in VS Code to run this single test, and then just do printf-driven development/debugging.
|
||||||
As a sanity check after I'm done, I use `cargo xtask install --server` and **Reload Window** action in VS Code to verify that the thing works as I expect.
|
As a sanity check after I'm done, I use `cargo xtask install --server` and **Reload Window** action in VS Code to verify that the thing works as I expect.
|
||||||
|
|
||||||
If the problem concerns only the VS Code extension, I use **Run Installed Extension** launch configuration from `launch.json`.
|
If the problem concerns only the VS Code extension, I use **Run Installed Extension** launch configuration from `launch.json`.
|
||||||
@ -152,11 +152,11 @@ To log all communication between the server and the client, there are two choice
|
|||||||
|
|
||||||
There are also several VS Code commands which might be of interest:
|
There are also several VS Code commands which might be of interest:
|
||||||
|
|
||||||
* `Rust Analyzer: Status` shows some memory-usage statistics.
|
* `rust-analyzer: Status` shows some memory-usage statistics.
|
||||||
|
|
||||||
* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection.
|
* `rust-analyzer: Syntax Tree` shows syntax tree of the current file/selection.
|
||||||
|
|
||||||
* `Rust Analyzer: View Hir` shows the HIR expressions within the function containing the cursor.
|
* `rust-analyzer: View Hir` shows the HIR expressions within the function containing the cursor.
|
||||||
|
|
||||||
You can hover over syntax nodes in the opened text file to see the appropriate
|
You can hover over syntax nodes in the opened text file to see the appropriate
|
||||||
rust code that it refers to and the rust editor will also highlight the proper
|
rust code that it refers to and the rust editor will also highlight the proper
|
||||||
|
@ -371,7 +371,7 @@ That is, rust-analyzer requires unwinding.
|
|||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
Rust Analyzer has three interesting [system boundaries](https://www.tedinski.com/2018/04/10/making-tests-a-positive-influence-on-design.html) to concentrate tests on.
|
rust-analyzer has three interesting [system boundaries](https://www.tedinski.com/2018/04/10/making-tests-a-positive-influence-on-design.html) to concentrate tests on.
|
||||||
|
|
||||||
The outermost boundary is the `rust-analyzer` crate, which defines an LSP interface in terms of stdio.
|
The outermost boundary is the `rust-analyzer` crate, which defines an LSP interface in terms of stdio.
|
||||||
We do integration testing of this component, by feeding it with a stream of LSP requests and checking responses.
|
We do integration testing of this component, by feeding it with a stream of LSP requests and checking responses.
|
||||||
|
@ -63,7 +63,7 @@ Next, let's talk about what the inputs to the `Analysis` are, precisely.
|
|||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
|
|
||||||
Rust Analyzer never does any I/O itself, all inputs get passed explicitly via
|
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
|
the `AnalysisHost::apply_change` method, which accepts a single argument, a
|
||||||
`Change`. [`Change`] is a builder for a single change
|
`Change`. [`Change`] is a builder for a single change
|
||||||
"transaction", so it suffices to study its methods to understand all of the
|
"transaction", so it suffices to study its methods to understand all of the
|
||||||
|
@ -479,7 +479,7 @@ You can follow instructions for installing <<rust-analyzer-language-server-binar
|
|||||||
== Troubleshooting
|
== Troubleshooting
|
||||||
|
|
||||||
Start with looking at the rust-analyzer version.
|
Start with looking at the rust-analyzer version.
|
||||||
Try **Rust Analyzer: Show RA Version** in VS Code (using **Command Palette** feature typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the command line.
|
Try **rust-analyzer: Show RA Version** in VS Code (using **Command Palette** feature typically activated by Ctrl+Shift+P) or `rust-analyzer --version` in the command line.
|
||||||
If the date is more than a week ago, it's better to update rust-analyzer version.
|
If the date is more than a week ago, it's better to update rust-analyzer version.
|
||||||
|
|
||||||
The next thing to check would be panic messages in rust-analyzer's log.
|
The next thing to check would be panic messages in rust-analyzer's log.
|
||||||
@ -492,7 +492,7 @@ To fully capture LSP messages between the editor and the server, set `"rust-anal
|
|||||||
The root cause for many "`nothing works`" problems is that rust-analyzer fails to understand the project structure.
|
The root cause for many "`nothing works`" problems is that rust-analyzer fails to understand the project structure.
|
||||||
To debug that, first note the `rust-analyzer` section in the status bar.
|
To debug that, first note the `rust-analyzer` section in the status bar.
|
||||||
If it has an error icon and red, that's the problem (hover will have somewhat helpful error message).
|
If it has an error icon and red, that's the problem (hover will have somewhat helpful error message).
|
||||||
**Rust Analyzer: Status** prints dependency information for the current file.
|
**rust-analyzer: Status** prints dependency information for the current file.
|
||||||
Finally, `RA_LOG=project_model=debug` enables verbose logs during project loading.
|
Finally, `RA_LOG=project_model=debug` enables verbose logs during project loading.
|
||||||
|
|
||||||
If rust-analyzer outright crashes, try running `rust-analyzer analysis-stats /path/to/project/directory/` on the command line.
|
If rust-analyzer outright crashes, try running `rust-analyzer analysis-stats /path/to/project/directory/` on the command line.
|
||||||
|
@ -99,142 +99,142 @@
|
|||||||
{
|
{
|
||||||
"command": "rust-analyzer.syntaxTree",
|
"command": "rust-analyzer.syntaxTree",
|
||||||
"title": "Show Syntax Tree",
|
"title": "Show Syntax Tree",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.viewHir",
|
"command": "rust-analyzer.viewHir",
|
||||||
"title": "View Hir",
|
"title": "View Hir",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.viewFileText",
|
"command": "rust-analyzer.viewFileText",
|
||||||
"title": "View File Text (as seen by the server)",
|
"title": "View File Text (as seen by the server)",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.viewItemTree",
|
"command": "rust-analyzer.viewItemTree",
|
||||||
"title": "Debug ItemTree",
|
"title": "Debug ItemTree",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.viewCrateGraph",
|
"command": "rust-analyzer.viewCrateGraph",
|
||||||
"title": "View Crate Graph",
|
"title": "View Crate Graph",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.viewFullCrateGraph",
|
"command": "rust-analyzer.viewFullCrateGraph",
|
||||||
"title": "View Crate Graph (Full)",
|
"title": "View Crate Graph (Full)",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.expandMacro",
|
"command": "rust-analyzer.expandMacro",
|
||||||
"title": "Expand macro recursively",
|
"title": "Expand macro recursively",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.matchingBrace",
|
"command": "rust-analyzer.matchingBrace",
|
||||||
"title": "Find matching brace",
|
"title": "Find matching brace",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.parentModule",
|
"command": "rust-analyzer.parentModule",
|
||||||
"title": "Locate parent module",
|
"title": "Locate parent module",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.joinLines",
|
"command": "rust-analyzer.joinLines",
|
||||||
"title": "Join lines",
|
"title": "Join lines",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.run",
|
"command": "rust-analyzer.run",
|
||||||
"title": "Run",
|
"title": "Run",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.copyRunCommandLine",
|
"command": "rust-analyzer.copyRunCommandLine",
|
||||||
"title": "Copy Run Command Line",
|
"title": "Copy Run Command Line",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.debug",
|
"command": "rust-analyzer.debug",
|
||||||
"title": "Debug",
|
"title": "Debug",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.newDebugConfig",
|
"command": "rust-analyzer.newDebugConfig",
|
||||||
"title": "Generate launch configuration",
|
"title": "Generate launch configuration",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.analyzerStatus",
|
"command": "rust-analyzer.analyzerStatus",
|
||||||
"title": "Status",
|
"title": "Status",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.memoryUsage",
|
"command": "rust-analyzer.memoryUsage",
|
||||||
"title": "Memory Usage (Clears Database)",
|
"title": "Memory Usage (Clears Database)",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.shuffleCrateGraph",
|
"command": "rust-analyzer.shuffleCrateGraph",
|
||||||
"title": "Shuffle Crate Graph",
|
"title": "Shuffle Crate Graph",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.reloadWorkspace",
|
"command": "rust-analyzer.reloadWorkspace",
|
||||||
"title": "Reload workspace",
|
"title": "Reload workspace",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.reload",
|
"command": "rust-analyzer.reload",
|
||||||
"title": "Restart server",
|
"title": "Restart server",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.onEnter",
|
"command": "rust-analyzer.onEnter",
|
||||||
"title": "Enhanced enter key",
|
"title": "Enhanced enter key",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.ssr",
|
"command": "rust-analyzer.ssr",
|
||||||
"title": "Structural Search Replace",
|
"title": "Structural Search Replace",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.serverVersion",
|
"command": "rust-analyzer.serverVersion",
|
||||||
"title": "Show RA Version",
|
"title": "Show RA Version",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.toggleInlayHints",
|
"command": "rust-analyzer.toggleInlayHints",
|
||||||
"title": "Toggle inlay hints",
|
"title": "Toggle inlay hints",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.openDocs",
|
"command": "rust-analyzer.openDocs",
|
||||||
"title": "Open docs under cursor",
|
"title": "Open docs under cursor",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.openCargoToml",
|
"command": "rust-analyzer.openCargoToml",
|
||||||
"title": "Open Cargo.toml",
|
"title": "Open Cargo.toml",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.peekTests",
|
"command": "rust-analyzer.peekTests",
|
||||||
"title": "Peek related tests",
|
"title": "Peek related tests",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.moveItemUp",
|
"command": "rust-analyzer.moveItemUp",
|
||||||
"title": "Move item up",
|
"title": "Move item up",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "rust-analyzer.moveItemDown",
|
"command": "rust-analyzer.moveItemDown",
|
||||||
"title": "Move item down",
|
"title": "Move item down",
|
||||||
"category": "Rust Analyzer"
|
"category": "rust-analyzer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"keybindings": [
|
"keybindings": [
|
||||||
@ -256,7 +256,7 @@
|
|||||||
],
|
],
|
||||||
"configuration": {
|
"configuration": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "Rust Analyzer",
|
"title": "rust-analyzer",
|
||||||
"properties": {
|
"properties": {
|
||||||
"rust-analyzer.cargoRunner": {
|
"rust-analyzer.cargoRunner": {
|
||||||
"type": [
|
"type": [
|
||||||
@ -380,6 +380,11 @@
|
|||||||
"default": false,
|
"default": false,
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"rust-analyzer.typing.continueCommentsOnNewline": {
|
||||||
|
"markdownDescription": "Whether to prefix newlines after comments with the corresponding comment prefix.",
|
||||||
|
"default": true,
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"$generated-start": {},
|
"$generated-start": {},
|
||||||
"rust-analyzer.assist.expressionFillDefault": {
|
"rust-analyzer.assist.expressionFillDefault": {
|
||||||
"markdownDescription": "Placeholder expression to use for missing expressions in assists.",
|
"markdownDescription": "Placeholder expression to use for missing expressions in assists.",
|
||||||
|
@ -16,9 +16,13 @@ export class Config {
|
|||||||
readonly extensionId = "rust-lang.rust-analyzer";
|
readonly extensionId = "rust-lang.rust-analyzer";
|
||||||
|
|
||||||
readonly rootSection = "rust-analyzer";
|
readonly rootSection = "rust-analyzer";
|
||||||
private readonly requiresWorkspaceReloadOpts = ["serverPath", "server"].map(
|
private readonly requiresWorkspaceReloadOpts = [
|
||||||
(opt) => `${this.rootSection}.${opt}`
|
"serverPath",
|
||||||
);
|
"server",
|
||||||
|
// FIXME: This shouldn't be here, changing this setting should reload
|
||||||
|
// `continueCommentsOnNewline` behavior without restart
|
||||||
|
"typing",
|
||||||
|
].map((opt) => `${this.rootSection}.${opt}`);
|
||||||
private readonly requiresReloadOpts = [
|
private readonly requiresReloadOpts = [
|
||||||
"cargo",
|
"cargo",
|
||||||
"procMacro",
|
"procMacro",
|
||||||
@ -140,6 +144,10 @@ export class Config {
|
|||||||
return this.get<boolean>("restartServerOnConfigChange");
|
return this.get<boolean>("restartServerOnConfigChange");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get typingContinueCommentsOnNewline() {
|
||||||
|
return this.get<boolean>("typing.continueCommentsOnNewline");
|
||||||
|
}
|
||||||
|
|
||||||
get debug() {
|
get debug() {
|
||||||
let sourceFileMap = this.get<Record<string, string> | "auto">("debug.sourceFileMap");
|
let sourceFileMap = this.get<Record<string, string> | "auto">("debug.sourceFileMap");
|
||||||
if (sourceFileMap !== "auto") {
|
if (sourceFileMap !== "auto") {
|
||||||
|
@ -84,7 +84,9 @@ async function tryActivate(context: vscode.ExtensionContext): Promise<RustAnalyz
|
|||||||
|
|
||||||
warnAboutExtensionConflicts();
|
warnAboutExtensionConflicts();
|
||||||
|
|
||||||
|
if (config.typingContinueCommentsOnNewline) {
|
||||||
ctx.pushCleanup(configureLanguage());
|
ctx.pushCleanup(configureLanguage());
|
||||||
|
}
|
||||||
|
|
||||||
vscode.workspace.onDidChangeConfiguration(
|
vscode.workspace.onDidChangeConfiguration(
|
||||||
(_) =>
|
(_) =>
|
||||||
|
@ -12,7 +12,7 @@ use std::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
mod map;
|
mod map;
|
||||||
pub use map::ArenaMap;
|
pub use map::{ArenaMap, Entry, OccupiedEntry, VacantEntry};
|
||||||
|
|
||||||
/// The raw index of a value in an arena.
|
/// The raw index of a value in an arena.
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
@ -208,6 +208,16 @@ impl<T> Arena<T> {
|
|||||||
Arena { data: Vec::new() }
|
Arena { data: Vec::new() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new empty arena with specific capacity.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let arena: la_arena::Arena<i32> = la_arena::Arena::with_capacity(42);
|
||||||
|
/// assert!(arena.is_empty());
|
||||||
|
/// ```
|
||||||
|
pub fn with_capacity(capacity: usize) -> Arena<T> {
|
||||||
|
Arena { data: Vec::with_capacity(capacity) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Empties the arena, removing all contained values.
|
/// Empties the arena, removing all contained values.
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -11,12 +11,52 @@ pub struct ArenaMap<IDX, V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T, V> ArenaMap<Idx<T>, V> {
|
impl<T, V> ArenaMap<Idx<T>, V> {
|
||||||
|
/// Creates a new empty map.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self { v: Vec::new(), _ty: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new empty map with specific capacity.
|
||||||
|
pub fn with_capacity(capacity: usize) -> Self {
|
||||||
|
Self { v: Vec::with_capacity(capacity), _ty: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reserves capacity for at least additional more elements to be inserted in the map.
|
||||||
|
pub fn reserve(&mut self, additional: usize) {
|
||||||
|
self.v.reserve(additional);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the map, removing all elements.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.v.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shrinks the capacity of the map as much as possible.
|
||||||
|
pub fn shrink_to_fit(&mut self) {
|
||||||
|
let min_len = self.v.iter().rposition(|slot| slot.is_some()).map_or(0, |i| i + 1);
|
||||||
|
self.v.truncate(min_len);
|
||||||
|
self.v.shrink_to_fit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns whether the map contains a value for the specified index.
|
||||||
|
pub fn contains_idx(&self, idx: Idx<T>) -> bool {
|
||||||
|
matches!(self.v.get(Self::to_idx(idx)), Some(Some(_)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes an index from the map, returning the value at the index if the index was previously in the map.
|
||||||
|
pub fn remove(&mut self, idx: Idx<T>) -> Option<V> {
|
||||||
|
self.v.get_mut(Self::to_idx(idx))?.take()
|
||||||
|
}
|
||||||
|
|
||||||
/// Inserts a value associated with a given arena index into the map.
|
/// Inserts a value associated with a given arena index into the map.
|
||||||
pub fn insert(&mut self, idx: Idx<T>, t: V) {
|
///
|
||||||
|
/// If the map did not have this index present, None is returned.
|
||||||
|
/// Otherwise, the value is updated, and the old value is returned.
|
||||||
|
pub fn insert(&mut self, idx: Idx<T>, t: V) -> Option<V> {
|
||||||
let idx = Self::to_idx(idx);
|
let idx = Self::to_idx(idx);
|
||||||
|
|
||||||
self.v.resize_with((idx + 1).max(self.v.len()), || None);
|
self.v.resize_with((idx + 1).max(self.v.len()), || None);
|
||||||
self.v[idx] = Some(t);
|
self.v[idx].replace(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the value associated with the provided index
|
/// Returns a reference to the value associated with the provided index
|
||||||
@ -46,6 +86,16 @@ impl<T, V> ArenaMap<Idx<T>, V> {
|
|||||||
self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
|
self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the given key's corresponding entry in the map for in-place manipulation.
|
||||||
|
pub fn entry(&mut self, idx: Idx<T>) -> Entry<'_, Idx<T>, V> {
|
||||||
|
let idx = Self::to_idx(idx);
|
||||||
|
self.v.resize_with((idx + 1).max(self.v.len()), || None);
|
||||||
|
match &mut self.v[idx] {
|
||||||
|
slot @ Some(_) => Entry::Occupied(OccupiedEntry { slot, _ty: PhantomData }),
|
||||||
|
slot @ None => Entry::Vacant(VacantEntry { slot, _ty: PhantomData }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn to_idx(idx: Idx<T>) -> usize {
|
fn to_idx(idx: Idx<T>) -> usize {
|
||||||
u32::from(idx.into_raw()) as usize
|
u32::from(idx.into_raw()) as usize
|
||||||
}
|
}
|
||||||
@ -70,6 +120,119 @@ impl<T, V> std::ops::IndexMut<Idx<V>> for ArenaMap<Idx<V>, T> {
|
|||||||
|
|
||||||
impl<T, V> Default for ArenaMap<Idx<V>, T> {
|
impl<T, V> Default for ArenaMap<Idx<V>, T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
ArenaMap { v: Vec::new(), _ty: PhantomData }
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, V> Extend<(Idx<V>, T)> for ArenaMap<Idx<V>, T> {
|
||||||
|
fn extend<I: IntoIterator<Item = (Idx<V>, T)>>(&mut self, iter: I) {
|
||||||
|
iter.into_iter().for_each(move |(k, v)| {
|
||||||
|
self.insert(k, v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, V> FromIterator<(Idx<V>, T)> for ArenaMap<Idx<V>, T> {
|
||||||
|
fn from_iter<I: IntoIterator<Item = (Idx<V>, T)>>(iter: I) -> Self {
|
||||||
|
let mut this = Self::new();
|
||||||
|
this.extend(iter);
|
||||||
|
this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A view into a single entry in a map, which may either be vacant or occupied.
|
||||||
|
///
|
||||||
|
/// This `enum` is constructed from the [`entry`] method on [`ArenaMap`].
|
||||||
|
///
|
||||||
|
/// [`entry`]: ArenaMap::entry
|
||||||
|
pub enum Entry<'a, IDX, V> {
|
||||||
|
/// A vacant entry.
|
||||||
|
Vacant(VacantEntry<'a, IDX, V>),
|
||||||
|
/// An occupied entry.
|
||||||
|
Occupied(OccupiedEntry<'a, IDX, V>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, IDX, V> Entry<'a, IDX, V> {
|
||||||
|
/// Ensures a value is in the entry by inserting the default if empty, and returns a mutable reference to
|
||||||
|
/// the value in the entry.
|
||||||
|
pub fn or_insert(self, default: V) -> &'a mut V {
|
||||||
|
match self {
|
||||||
|
Self::Vacant(ent) => ent.insert(default),
|
||||||
|
Self::Occupied(ent) => ent.into_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ensures a value is in the entry by inserting the result of the default function if empty, and returns
|
||||||
|
/// a mutable reference to the value in the entry.
|
||||||
|
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
|
||||||
|
match self {
|
||||||
|
Self::Vacant(ent) => ent.insert(default()),
|
||||||
|
Self::Occupied(ent) => ent.into_mut(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides in-place mutable access to an occupied entry before any potential inserts into the map.
|
||||||
|
pub fn and_modify<F: FnOnce(&mut V)>(mut self, f: F) -> Self {
|
||||||
|
if let Self::Occupied(ent) = &mut self {
|
||||||
|
f(ent.get_mut());
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, IDX, V> Entry<'a, IDX, V>
|
||||||
|
where
|
||||||
|
V: Default,
|
||||||
|
{
|
||||||
|
/// Ensures a value is in the entry by inserting the default value if empty, and returns a mutable reference
|
||||||
|
/// to the value in the entry.
|
||||||
|
pub fn or_default(self) -> &'a mut V {
|
||||||
|
self.or_insert_with(Default::default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A view into an vacant entry in a [`ArenaMap`]. It is part of the [`Entry`] enum.
|
||||||
|
pub struct VacantEntry<'a, IDX, V> {
|
||||||
|
slot: &'a mut Option<V>,
|
||||||
|
_ty: PhantomData<IDX>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, IDX, V> VacantEntry<'a, IDX, V> {
|
||||||
|
/// Sets the value of the entry with the `VacantEntry`’s key, and returns a mutable reference to it.
|
||||||
|
pub fn insert(self, value: V) -> &'a mut V {
|
||||||
|
self.slot.insert(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A view into an occupied entry in a [`ArenaMap`]. It is part of the [`Entry`] enum.
|
||||||
|
pub struct OccupiedEntry<'a, IDX, V> {
|
||||||
|
slot: &'a mut Option<V>,
|
||||||
|
_ty: PhantomData<IDX>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, IDX, V> OccupiedEntry<'a, IDX, V> {
|
||||||
|
/// Gets a reference to the value in the entry.
|
||||||
|
pub fn get(&self) -> &V {
|
||||||
|
self.slot.as_ref().expect("Occupied")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a mutable reference to the value in the entry.
|
||||||
|
pub fn get_mut(&mut self) -> &mut V {
|
||||||
|
self.slot.as_mut().expect("Occupied")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts the entry into a mutable reference to its value.
|
||||||
|
pub fn into_mut(self) -> &'a mut V {
|
||||||
|
self.slot.as_mut().expect("Occupied")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the value of the entry with the `OccupiedEntry`’s key, and returns the entry’s old value.
|
||||||
|
pub fn insert(&mut self, value: V) -> V {
|
||||||
|
self.slot.replace(value).expect("Occupied")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes the value of the entry out of the map, and returns it.
|
||||||
|
pub fn remove(self) -> V {
|
||||||
|
self.slot.take().expect("Occupied")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ impl flags::Promote {
|
|||||||
let date = date_iso(sh)?;
|
let date = date_iso(sh)?;
|
||||||
let branch = format!("rust-analyzer-{date}");
|
let branch = format!("rust-analyzer-{date}");
|
||||||
cmd!(sh, "git switch -c {branch}").run()?;
|
cmd!(sh, "git switch -c {branch}").run()?;
|
||||||
cmd!(sh, "git subtree pull -P src/tools/rust-analyzer rust-analyzer master").run()?;
|
cmd!(sh, "git subtree pull -m ':arrow_up: rust-analyzer' -P src/tools/rust-analyzer rust-analyzer release").run()?;
|
||||||
|
|
||||||
if !self.dry_run {
|
if !self.dry_run {
|
||||||
cmd!(sh, "git push -u origin {branch}").run()?;
|
cmd!(sh, "git push -u origin {branch}").run()?;
|
||||||
|
Loading…
Reference in New Issue
Block a user