11481: Display parameter names when hovering over a function pointer r=Veykril a=Vannevelj
Implements #11474
The idea is pretty straightforward: previously we constructed the hover based on just the parameter types, now we pass in the parameter names as well. I went for a quick-hit approach here but I expect someone will be able to point me to a better way of resolving the identifier.
I haven't figured out yet how to actually run my rust-analyzer locally so I can see it in action but the unit test indicates it should work.
Co-authored-by: Jeroen Vannevel <jer_vannevel@outlook.com>
Before submitting, please make sure that you're not running into one of these known issues:
1. extension doesn't load in VSCodium: #11080
- 2. VS Code Marketplace serves old stable version: #11098
- 3. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107
+ 2. on-the-fly diagnostics are mostly unimplemented (`cargo check` diagnostics will be shown when saving a file): #3107
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)
**rustc version**: (eg. output of `rustc -V`)
+
+**relevant settings**: (eg. client settings, or environment variables like `CARGO`, `RUSTUP_HOME` or `CARGO_HOME`)
[[package]]
name = "ungrammar"
-version = "1.14.9"
+version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66be59c2fd880e3d76d1a6cf6d34114008f1d8af2748d4ad9d39ea712f14fda9"
+checksum = "ed01567101450f7d600508e7680df6005ae4fe97119d79b0364cc5910ff39732"
[[package]]
name = "unicase"
use stdx::{format_to, impl_from};
use syntax::{
ast::{self, HasAttrs as _, HasDocComments, HasName},
- AstNode, AstPtr, SmolStr, SyntaxKind, SyntaxNodePtr,
+ AstNode, AstPtr, SmolStr, SyntaxNodePtr, T,
};
use tt::{Ident, Leaf, Literal, TokenTree};
DefDiagnosticKind::UnresolvedProcMacro { ast } => {
let mut precise_location = None;
- let (node, name) = match ast {
+ let (node, macro_name) = match ast {
MacroCallKind::FnLike { ast_id, .. } => {
let node = ast_id.to_node(db.upcast());
(ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))), None)
}
- MacroCallKind::Derive { ast_id, derive_name, .. } => {
+ MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => {
let node = ast_id.to_node(db.upcast());
// Compute the precise location of the macro name's token in the derive
// list.
- // FIXME: This does not handle paths to the macro, but neither does the
- // rest of r-a.
- let derive_attrs =
- node.attrs().filter_map(|attr| match attr.as_simple_call() {
- Some((name, args)) if name == "derive" => Some(args),
- _ => None,
- });
- 'outer: for attr in derive_attrs {
- let tokens =
- attr.syntax().children_with_tokens().filter_map(|elem| match elem {
- syntax::NodeOrToken::Node(_) => None,
+ let token = (|| {
+ let derive_attr = node.attrs().nth(*derive_attr_index as usize)?;
+ derive_attr
+ .syntax()
+ .children_with_tokens()
+ .filter_map(|elem| match elem {
syntax::NodeOrToken::Token(tok) => Some(tok),
- });
- for token in tokens {
- if token.kind() == SyntaxKind::IDENT && token.text() == &**derive_name {
- precise_location = Some(token.text_range());
- break 'outer;
- }
- }
- }
-
+ _ => None,
+ })
+ .group_by(|t| t.kind() == T![,])
+ .into_iter()
+ .filter(|&(comma, _)| !comma)
+ .nth(*derive_index as usize)
+ .and_then(|(_, mut g)| g.find(|t| t.kind() == T![ident]))
+ })();
+ precise_location = token.as_ref().map(|tok| tok.text_range());
(
ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&node))),
- Some(derive_name.clone()),
+ token.as_ref().map(ToString::to_string),
)
}
- MacroCallKind::Attr { ast_id, invoc_attr_index, attr_name, .. } => {
+ MacroCallKind::Attr { ast_id, invoc_attr_index, .. } => {
let node = ast_id.to_node(db.upcast());
let attr = node
.doc_comments_and_attrs()
.unwrap_or_else(|| panic!("cannot find attribute #{}", invoc_attr_index));
(
ast_id.with_value(SyntaxNodePtr::from(AstPtr::new(&attr))),
- Some(attr_name.clone()),
+ attr.path()
+ .and_then(|path| path.segment())
+ .and_then(|seg| seg.name_ref())
+ .as_ref()
+ .map(ToString::to_string),
)
}
};
- acc.push(
- UnresolvedProcMacro { node, precise_location, macro_name: name.map(Into::into) }
- .into(),
- );
+ acc.push(UnresolvedProcMacro { node, precise_location, macro_name }.into());
}
DefDiagnosticKind::UnresolvedMacroCall { ast, path } => {
db::DefDatabase,
expr::{
dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, Label, LabelId, Literal, MatchArm,
- MatchGuard, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
+ Pat, PatId, RecordFieldPat, RecordLitField, Statement,
},
intern::Interned,
item_scope::BuiltinShadowMode,
fn alloc_expr_desugared(&mut self, expr: Expr) -> ExprId {
self.make_expr(expr, Err(SyntheticSyntax))
}
- fn unit(&mut self) -> ExprId {
- self.alloc_expr_desugared(Expr::Tuple { exprs: Box::default() })
- }
fn missing_expr(&mut self) -> ExprId {
self.alloc_expr_desugared(Expr::Missing)
}
}
});
- let condition = match e.condition() {
- None => self.missing_expr(),
- Some(condition) => match condition.pat() {
- None => self.collect_expr_opt(condition.expr()),
- // if let -- desugar to match
- Some(pat) => {
- let pat = self.collect_pat(pat);
- let match_expr = self.collect_expr_opt(condition.expr());
- let placeholder_pat = self.missing_pat();
- let arms = vec![
- MatchArm { pat, expr: then_branch, guard: None },
- MatchArm {
- pat: placeholder_pat,
- expr: else_branch.unwrap_or_else(|| self.unit()),
- guard: None,
- },
- ]
- .into();
- return Some(
- self.alloc_expr(Expr::Match { expr: match_expr, arms }, syntax_ptr),
- );
- }
- },
- };
+ let condition = self.collect_expr_opt(e.condition());
self.alloc_expr(Expr::If { condition, then_branch, else_branch }, syntax_ptr)
}
+ ast::Expr::LetExpr(e) => {
+ let pat = self.collect_pat_opt(e.pat());
+ let expr = self.collect_expr_opt(e.expr());
+ self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
+ }
ast::Expr::BlockExpr(e) => match e.modifier() {
Some(ast::BlockModifier::Try(_)) => {
let body = self.collect_block(e);
let label = e.label().map(|label| self.collect_label(label));
let body = self.collect_block_opt(e.loop_body());
- let condition = match e.condition() {
- None => self.missing_expr(),
- Some(condition) => match condition.pat() {
- None => self.collect_expr_opt(condition.expr()),
- // if let -- desugar to match
- Some(pat) => {
- cov_mark::hit!(infer_resolve_while_let);
- let pat = self.collect_pat(pat);
- let match_expr = self.collect_expr_opt(condition.expr());
- let placeholder_pat = self.missing_pat();
- let break_ =
- self.alloc_expr_desugared(Expr::Break { expr: None, label: None });
- let arms = vec![
- MatchArm { pat, expr: body, guard: None },
- MatchArm { pat: placeholder_pat, expr: break_, guard: None },
- ]
- .into();
- let match_expr =
- self.alloc_expr_desugared(Expr::Match { expr: match_expr, arms });
- return Some(
- self.alloc_expr(Expr::Loop { body: match_expr, label }, syntax_ptr),
- );
- }
- },
- };
+ let condition = self.collect_expr_opt(e.condition());
self.alloc_expr(Expr::While { condition, body, label }, syntax_ptr)
}
self.check_cfg(&arm).map(|()| MatchArm {
pat: self.collect_pat_opt(arm.pat()),
expr: self.collect_expr_opt(arm.expr()),
- guard: arm.guard().map(|guard| match guard.pat() {
- Some(pat) => MatchGuard::IfLet {
- pat: self.collect_pat(pat),
- expr: self.collect_expr_opt(guard.expr()),
- },
- None => {
- MatchGuard::If { expr: self.collect_expr_opt(guard.expr()) }
- }
- }),
+ guard: arm
+ .guard()
+ .map(|guard| self.collect_expr_opt(guard.condition())),
})
})
.collect()
use crate::{
body::Body,
db::DefDatabase,
- expr::{Expr, ExprId, LabelId, MatchGuard, Pat, PatId, Statement},
+ expr::{Expr, ExprId, LabelId, Pat, PatId, Statement},
BlockId, DefWithBodyId,
};
fn new(body: &Body) -> ExprScopes {
let mut scopes =
ExprScopes { scopes: Arena::default(), scope_by_expr: FxHashMap::default() };
- let root = scopes.root_scope();
+ let mut root = scopes.root_scope();
scopes.add_params_bindings(body, root, &body.params);
- compute_expr_scopes(body.body_expr, body, &mut scopes, root);
+ compute_expr_scopes(body.body_expr, body, &mut scopes, &mut root);
scopes
}
match stmt {
Statement::Let { pat, initializer, else_branch, .. } => {
if let Some(expr) = initializer {
- compute_expr_scopes(*expr, body, scopes, scope);
+ compute_expr_scopes(*expr, body, scopes, &mut scope);
}
if let Some(expr) = else_branch {
- compute_expr_scopes(*expr, body, scopes, scope);
+ compute_expr_scopes(*expr, body, scopes, &mut scope);
}
scope = scopes.new_scope(scope);
scopes.add_bindings(body, scope, *pat);
}
Statement::Expr { expr, .. } => {
- compute_expr_scopes(*expr, body, scopes, scope);
+ compute_expr_scopes(*expr, body, scopes, &mut scope);
}
}
}
if let Some(expr) = tail {
- compute_expr_scopes(expr, body, scopes, scope);
+ compute_expr_scopes(expr, body, scopes, &mut scope);
}
}
-fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: ScopeId) {
+fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut ExprScopes, scope: &mut ScopeId) {
let make_label =
|label: &Option<LabelId>| label.map(|label| (label, body.labels[label].name.clone()));
- scopes.set_scope(expr, scope);
+ scopes.set_scope(expr, *scope);
match &body[expr] {
Expr::Block { statements, tail, id, label } => {
- let scope = scopes.new_block_scope(scope, *id, make_label(label));
+ let scope = scopes.new_block_scope(*scope, *id, make_label(label));
// Overwrite the old scope for the block expr, so that every block scope can be found
// via the block itself (important for blocks that only contain items, no expressions).
scopes.set_scope(expr, scope);
}
Expr::For { iterable, pat, body: body_expr, label } => {
compute_expr_scopes(*iterable, body, scopes, scope);
- let scope = scopes.new_labeled_scope(scope, make_label(label));
+ let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
scopes.add_bindings(body, scope, *pat);
- compute_expr_scopes(*body_expr, body, scopes, scope);
+ compute_expr_scopes(*body_expr, body, scopes, &mut scope);
}
Expr::While { condition, body: body_expr, label } => {
- let scope = scopes.new_labeled_scope(scope, make_label(label));
- compute_expr_scopes(*condition, body, scopes, scope);
- compute_expr_scopes(*body_expr, body, scopes, scope);
+ let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
+ compute_expr_scopes(*condition, body, scopes, &mut scope);
+ compute_expr_scopes(*body_expr, body, scopes, &mut scope);
}
Expr::Loop { body: body_expr, label } => {
- let scope = scopes.new_labeled_scope(scope, make_label(label));
- compute_expr_scopes(*body_expr, body, scopes, scope);
+ let mut scope = scopes.new_labeled_scope(*scope, make_label(label));
+ compute_expr_scopes(*body_expr, body, scopes, &mut scope);
}
Expr::Lambda { args, body: body_expr, .. } => {
- let scope = scopes.new_scope(scope);
+ let mut scope = scopes.new_scope(*scope);
scopes.add_params_bindings(body, scope, args);
- compute_expr_scopes(*body_expr, body, scopes, scope);
+ compute_expr_scopes(*body_expr, body, scopes, &mut scope);
}
Expr::Match { expr, arms } => {
compute_expr_scopes(*expr, body, scopes, scope);
for arm in arms.iter() {
- let mut scope = scopes.new_scope(scope);
+ let mut scope = scopes.new_scope(*scope);
scopes.add_bindings(body, scope, arm.pat);
- match arm.guard {
- Some(MatchGuard::If { expr: guard }) => {
- scopes.set_scope(guard, scope);
- compute_expr_scopes(guard, body, scopes, scope);
- }
- Some(MatchGuard::IfLet { pat, expr: guard }) => {
- scopes.set_scope(guard, scope);
- compute_expr_scopes(guard, body, scopes, scope);
- scope = scopes.new_scope(scope);
- scopes.add_bindings(body, scope, pat);
- }
- _ => {}
- };
- scopes.set_scope(arm.expr, scope);
- compute_expr_scopes(arm.expr, body, scopes, scope);
+ if let Some(guard) = arm.guard {
+ scope = scopes.new_scope(scope);
+ compute_expr_scopes(guard, body, scopes, &mut scope);
+ }
+ compute_expr_scopes(arm.expr, body, scopes, &mut scope);
+ }
+ }
+ &Expr::If { condition, then_branch, else_branch } => {
+ let mut then_branch_scope = scopes.new_scope(*scope);
+ compute_expr_scopes(condition, body, scopes, &mut then_branch_scope);
+ compute_expr_scopes(then_branch, body, scopes, &mut then_branch_scope);
+ if let Some(else_branch) = else_branch {
+ compute_expr_scopes(else_branch, body, scopes, scope);
}
}
+ &Expr::Let { pat, expr } => {
+ compute_expr_scopes(expr, body, scopes, scope);
+ *scope = scopes.new_scope(*scope);
+ scopes.add_bindings(body, *scope, pat);
+ }
e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
};
}
}
#[test]
- fn while_let_desugaring() {
- cov_mark::check!(infer_resolve_while_let);
+ fn while_let_adds_binding() {
do_check_local_name(
r#"
fn test() {
"#,
75,
);
+ do_check_local_name(
+ r#"
+fn test() {
+ let foo: Option<f32> = None;
+ while (((let Option::Some(_) = foo))) && let Option::Some(spam) = foo {
+ spam$0
+ }
+}
+"#,
+ 107,
+ );
+ }
+
+ #[test]
+ fn match_guard_if_let() {
+ do_check_local_name(
+ r#"
+fn test() {
+ let foo: Option<f32> = None;
+ match foo {
+ _ if let Option::Some(spam) = foo => spam$0,
+ }
+}
+"#,
+ 93,
+ );
+ }
+
+ #[test]
+ fn let_chains_can_reference_previous_lets() {
+ do_check_local_name(
+ r#"
+fn test() {
+ let foo: Option<i32> = None;
+ if let Some(spam) = foo && spa$0m > 1 && let Some(spam) = foo && spam > 1 {}
+}
+"#,
+ 61,
+ );
+ do_check_local_name(
+ r#"
+fn test() {
+ let foo: Option<i32> = None;
+ if let Some(spam) = foo && spam > 1 && let Some(spam) = foo && sp$0am > 1 {}
+}
+"#,
+ 100,
+ );
}
}
then_branch: ExprId,
else_branch: Option<ExprId>,
},
+ Let {
+ pat: PatId,
+ expr: ExprId,
+ },
Block {
id: BlockId,
statements: Box<[Statement]>,
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MatchArm {
pub pat: PatId,
- pub guard: Option<MatchGuard>,
+ pub guard: Option<ExprId>,
pub expr: ExprId,
}
-#[derive(Debug, Clone, Eq, PartialEq)]
-pub enum MatchGuard {
- If { expr: ExprId },
-
- IfLet { pat: PatId, expr: ExprId },
-}
-
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct RecordLitField {
pub name: Name,
f(else_branch);
}
}
+ Expr::Let { expr, .. } => {
+ f(*expr);
+ }
Expr::Block { statements, tail, .. } => {
for stmt in statements.iter() {
match stmt {
fn derive_macro_as_call_id(
item_attr: &AstIdWithPath<ast::Adt>,
derive_attr: AttrId,
+ derive_pos: u32,
db: &dyn db::DefDatabase,
krate: CrateId,
resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
) -> Result<MacroCallId, UnresolvedMacro> {
let def: MacroDefId = resolver(item_attr.path.clone())
.ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
- let last_segment = item_attr
- .path
- .segments()
- .last()
- .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?;
let res = def.as_lazy_macro(
db.upcast(),
krate,
MacroCallKind::Derive {
ast_id: item_attr.ast_id,
- derive_name: last_segment.to_string().into_boxed_str(),
+ derive_index: derive_pos,
derive_attr_index: derive_attr.ast_index,
},
);
krate: CrateId,
def: MacroDefId,
) -> MacroCallId {
- let attr_path = &item_attr.path;
- let last_segment = attr_path.segments().last().expect("empty attribute path");
let mut arg = match macro_attr.input.as_deref() {
Some(attr::AttrInput::TokenTree(tt, map)) => (tt.clone(), map.clone()),
_ => Default::default(),
krate,
MacroCallKind::Attr {
ast_id: item_attr.ast_id,
- attr_name: last_segment.to_string().into_boxed_str(),
- attr_args: arg,
+ attr_args: Arc::new(arg),
invoc_attr_index: macro_attr.id.ast_index,
},
);
check(
r#"
macro_rules! stmts {
- () => { let _ = 0; }
+ () => { fn foo() {} }
}
fn f() { let _ = stmts!/*+errors*/(); }
"#,
expect![[r#"
macro_rules! stmts {
- () => { let _ = 0; }
+ () => { fn foo() {} }
}
fn f() { let _ = /* parse error: expected expression */
-let _ = 0;; }
+fn foo() {}; }
"#]],
)
}
fn resolve_macros(&mut self) -> ReachedFixedPoint {
let mut macros = std::mem::take(&mut self.unresolved_macros);
let mut resolved = Vec::new();
+ let mut push_resolved = |directive: &MacroDirective, call_id| {
+ resolved.push((directive.module_id, directive.depth, directive.container, call_id));
+ };
let mut res = ReachedFixedPoint::Yes;
macros.retain(|directive| {
let resolver = |path| {
&mut |_err| (),
);
if let Ok(Ok(call_id)) = call_id {
- resolved.push((
- directive.module_id,
- call_id,
- directive.depth,
- directive.container,
- ));
+ push_resolved(directive, call_id);
res = ReachedFixedPoint::No;
return false;
}
let call_id = derive_macro_as_call_id(
ast_id,
*derive_attr,
+ *derive_pos as u32,
self.db,
self.def_map.krate,
&resolver,
*derive_pos,
);
- resolved.push((
- directive.module_id,
- call_id,
- directive.depth,
- directive.container,
- ));
+ push_resolved(directive, call_id);
res = ReachedFixedPoint::No;
return false;
}
.scope
.add_attr_macro_invoc(ast_id, call_id);
- resolved.push((
- directive.module_id,
- call_id,
- directive.depth,
- directive.container,
- ));
+ push_resolved(directive, call_id);
res = ReachedFixedPoint::No;
return false;
}
// Attribute resolution can add unresolved macro invocations, so concatenate the lists.
self.unresolved_macros.extend(macros);
- for (module_id, macro_call_id, depth, container) in resolved {
+ for (module_id, depth, container, macro_call_id) in resolved {
self.collect_macro_expansion(module_id, macro_call_id, depth, container);
}
ast::Item::cast(node.clone())?
.attrs()
.take(derive_attr_index as usize + 1)
+ // FIXME
.filter(|attr| attr.simple_name().as_deref() == Some("derive"))
.map(|it| it.syntax().clone())
.collect()
},
Derive {
ast_id: AstId<ast::Adt>,
- derive_name: Box<str>,
/// Syntactical index of the invoking `#[derive]` attribute.
///
/// Outer attributes are counted first, then inner attributes. This does not support
/// out-of-line modules, which may have attributes spread across 2 files!
derive_attr_index: u32,
+ /// Index of the derive macro in the derive attribute
+ derive_index: u32,
},
Attr {
ast_id: AstId<ast::Item>,
- attr_name: Box<str>,
- attr_args: (tt::Subtree, mbe::TokenMap),
+ attr_args: Arc<(tt::Subtree, mbe::TokenMap)>,
/// Syntactical index of the invoking `#[attribute]`.
///
/// Outer attributes are counted first, then inner attributes. This does not support
let token_range = token.value.text_range();
match &loc.kind {
- MacroCallKind::Attr { attr_args: (_, map), invoc_attr_index, .. } => {
+ MacroCallKind::Attr { attr_args, invoc_attr_index, .. } => {
let attr = item
.doc_comments_and_attrs()
.nth(*invoc_attr_index as usize)
let relative_range =
token.value.text_range().checked_sub(attr_input_start)?;
// shift by the item's tree's max id
- let token_id =
- self.macro_arg_shift.shift(map.token_by_range(relative_range)?);
+ let token_id = self
+ .macro_arg_shift
+ .shift(attr_args.1.token_by_range(relative_range)?);
Some(token_id)
}
_ => None,
// Attributes are a bit special for us, they have two inputs, the input tokentree and the annotated item.
let (token_map, tt) = match &loc.kind {
- MacroCallKind::Attr { attr_args: (_, arg_token_map), .. } => {
+ MacroCallKind::Attr { attr_args, .. } => {
// try unshifting the the token id, if unshifting fails, the token resides in the non-item attribute input
// note that the `TokenExpander::map_id_up` earlier only unshifts for declarative macros, so we don't double unshift with this
match self.macro_arg_shift.unshift(token_id) {
Some(unshifted) => {
token_id = unshifted;
- (arg_token_map, self.attr_input_or_mac_def.clone()?.syntax().cloned())
+ (&attr_args.1, self.attr_input_or_mac_def.clone()?.syntax().cloned())
}
None => (&self.macro_arg.1, self.arg.clone()),
}
MACRO_TYPE => ExpandTo::Type,
ARG_LIST | TRY_EXPR | TUPLE_EXPR | PAREN_EXPR | ARRAY_EXPR | FOR_EXPR | PATH_EXPR
- | CLOSURE_EXPR | CONDITION | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM
- | MATCH_GUARD | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR
- | FIELD_EXPR | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR
- | BIN_EXPR => ExpandTo::Expr,
+ | CLOSURE_EXPR | BREAK_EXPR | RETURN_EXPR | MATCH_EXPR | MATCH_ARM | MATCH_GUARD
+ | RECORD_EXPR_FIELD | CALL_EXPR | INDEX_EXPR | METHOD_CALL_EXPR | FIELD_EXPR
+ | AWAIT_EXPR | CAST_EXPR | REF_EXPR | PREFIX_EXPR | RANGE_EXPR | BIN_EXPR
+ | LET_EXPR => ExpandTo::Expr,
LET_STMT => {
// FIXME: Handle LHS Pattern
ExpandTo::Expr
use chalk_ir::{cast::Cast, fold::Shift, Mutability, TyVariableKind};
use hir_def::{
- expr::{
- ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, MatchGuard, Ordering, Statement,
- UnaryOp,
- },
+ expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp},
path::{GenericArg, GenericArgs},
resolver::resolver_for_expr,
FieldId, FunctionId, ItemContainerId, Lookup,
coerce.complete()
}
+ &Expr::Let { pat, expr } => {
+ let input_ty = self.infer_expr(expr, &Expectation::none());
+ self.infer_pat(pat, &input_ty, BindingMode::default());
+ TyKind::Scalar(Scalar::Bool).intern(Interner)
+ }
Expr::Block { statements, tail, label, id: _ } => {
let old_resolver = mem::replace(
&mut self.resolver,
for arm in arms.iter() {
self.diverges = Diverges::Maybe;
let _pat_ty = self.infer_pat(arm.pat, &input_ty, BindingMode::default());
- match arm.guard {
- Some(MatchGuard::If { expr: guard_expr }) => {
- self.infer_expr(
- guard_expr,
- &Expectation::has_type(
- TyKind::Scalar(Scalar::Bool).intern(Interner),
- ),
- );
- }
- Some(MatchGuard::IfLet { expr, pat }) => {
- let input_ty = self.infer_expr(expr, &Expectation::none());
- let _pat_ty = self.infer_pat(pat, &input_ty, BindingMode::default());
- }
- _ => {}
+ if let Some(guard_expr) = arm.guard {
+ self.infer_expr(
+ guard_expr,
+ &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)),
+ );
}
let arm_ty = self.infer_expr_inner(arm.expr, &expected);
use hir_def::{
expr::{BindingAnnotation, Expr, Literal, Pat, PatId, RecordFieldPat},
path::Path,
+ type_ref::ConstScalar,
};
use hir_expand::name::Name;
Adjust, Adjustment, AutoBorrow, BindingMode, Expectation, InferenceContext, TypeMismatch,
},
lower::lower_to_chalk_mutability,
- static_lifetime, Interner, Substitution, Ty, TyBuilder, TyExt, TyKind,
+ static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
+ TyKind,
};
impl<'a> InferenceContext<'a> {
self.infer_pat(pat_id, &elem_ty, default_bm);
}
- let pat_ty = match expected.kind(Interner) {
- TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
- _ => TyKind::Slice(elem_ty),
- }
- .intern(Interner);
if let &Some(slice_pat_id) = slice {
- self.infer_pat(slice_pat_id, &pat_ty, default_bm);
+ let rest_pat_ty = match expected.kind(Interner) {
+ TyKind::Array(_, length) => {
+ let length = match length.data(Interner).value {
+ ConstValue::Concrete(ConcreteConst {
+ interned: ConstScalar::Usize(length),
+ }) => length.checked_sub((prefix.len() + suffix.len()) as u64),
+ _ => None,
+ };
+ TyKind::Array(elem_ty.clone(), crate::consteval::usize_const(length))
+ }
+ _ => TyKind::Slice(elem_ty.clone()),
+ }
+ .intern(Interner);
+ self.infer_pat(slice_pat_id, &rest_pat_ty, default_bm);
}
- pat_ty
+ match expected.kind(Interner) {
+ TyKind::Array(_, const_) => TyKind::Array(elem_ty, const_.clone()),
+ _ => TyKind::Slice(elem_ty),
+ }
+ .intern(Interner)
}
Pat::Wild => expected.clone(),
Pat::Range { start, end } => {
!0..6 '1isize': isize
!0..6 '1isize': isize
!0..6 '1isize': isize
- !0..6 '1isize': isize
39..442 '{ ...!(); }': ()
73..94 'spam!(...am!())': {unknown}
100..119 'for _ ...!() {}': ()
117..119 '{}': ()
124..134 '|| spam!()': || -> isize
140..156 'while ...!() {}': ()
+ 146..153 'spam!()': bool
154..156 '{}': ()
161..174 'break spam!()': !
180..194 'return spam!()': !
!0..6 '1isize': isize
!0..6 '1isize': isize
!0..6 '1isize': isize
- !0..6 '1isize': isize
53..456 '{ ...!(); }': ()
87..108 'spam!(...am!())': {unknown}
114..133 'for _ ...!() {}': ()
131..133 '{}': ()
138..148 '|| spam!()': || -> isize
154..170 'while ...!() {}': ()
+ 160..167 'spam!()': bool
168..170 '{}': ()
175..188 'break spam!()': !
194..208 'return spam!()': !
139..140 'g': {unknown}
143..144 'e': {unknown}
157..204 'if let... }': ()
+ 160..175 'let [val] = opt': bool
164..169 '[val]': [{unknown}]
165..168 'val': {unknown}
172..175 'opt': [{unknown}]
190..191 'h': {unknown}
194..197 'val': {unknown}
210..236 'if let...rue {}': ()
+ 213..233 'let x ... &true': bool
217..225 'x @ true': &bool
221..225 'true': bool
221..225 'true': bool
37..38 'x': &i32
46..208 '{ ...) {} }': ()
52..75 'if let...y() {}': ()
+ 55..72 'let "f... any()': bool
59..64 '"foo"': &str
59..64 '"foo"': &str
67..70 'any': fn any<&str>() -> &str
67..72 'any()': &str
73..75 '{}': ()
80..99 'if let...y() {}': ()
+ 83..96 'let 1 = any()': bool
87..88 '1': i32
87..88 '1': i32
91..94 'any': fn any<i32>() -> i32
91..96 'any()': i32
97..99 '{}': ()
104..126 'if let...y() {}': ()
+ 107..123 'let 1u... any()': bool
111..115 '1u32': u32
111..115 '1u32': u32
118..121 'any': fn any<u32>() -> u32
118..123 'any()': u32
124..126 '{}': ()
131..153 'if let...y() {}': ()
+ 134..150 'let 1f... any()': bool
138..142 '1f32': f32
138..142 '1f32': f32
145..148 'any': fn any<f32>() -> f32
145..150 'any()': f32
151..153 '{}': ()
158..179 'if let...y() {}': ()
+ 161..176 'let 1.0 = any()': bool
165..168 '1.0': f64
165..168 '1.0': f64
171..174 'any': fn any<f64>() -> f64
171..176 'any()': f64
177..179 '{}': ()
184..206 'if let...y() {}': ()
+ 187..203 'let tr... any()': bool
191..195 'true': bool
191..195 'true': bool
198..201 'any': fn any<bool>() -> bool
8..9 'x': &i32
17..75 '{ ...2 {} }': ()
23..45 'if let...u32 {}': ()
+ 26..42 'let 1....= 2u32': bool
30..35 '1..76': u32
38..42 '2u32': u32
43..45 '{}': ()
50..73 'if let...u32 {}': ()
+ 53..70 'let 1....= 2u32': bool
57..63 '1..=76': u32
66..70 '2u32': u32
71..73 '{}': ()
);
}
+#[test]
+fn slice_pattern_correctly_handles_array_length() {
+ check_infer(
+ r#"
+fn main() {
+ let [head, middle @ .., tail, tail2] = [1, 2, 3, 4, 5];
+}
+ "#,
+ expect![[r#"
+ 10..73 '{ ... 5]; }': ()
+ 20..52 '[head,...tail2]': [i32; 5]
+ 21..25 'head': i32
+ 27..38 'middle @ ..': [i32; 2]
+ 36..38 '..': [i32; 2]
+ 40..44 'tail': i32
+ 46..51 'tail2': i32
+ 55..70 '[1, 2, 3, 4, 5]': [i32; 5]
+ 56..57 '1': i32
+ 59..60 '2': i32
+ 62..63 '3': i32
+ 65..66 '4': i32
+ 68..69 '5': i32
+ "#]],
+ );
+}
+
#[test]
fn pattern_lookup_in_value_ns() {
check_types(
176..193 'Thing ...1i32 }': Thing<i32>
187..191 '1i32': i32
199..240 'if let... }': ()
+ 202..221 'let Th... } = z': bool
206..217 'Thing { t }': Thing<i32>
214..215 't': i32
220..221 'z': Thing<i32>
algo, ast,
display::{fn_as_proc_macro_label, macro_label},
match_ast, AstNode, Direction,
- SyntaxKind::{CONDITION, LET_STMT},
+ SyntaxKind::{LET_EXPR, LET_STMT},
SyntaxToken, T,
};
let let_kw = if ident
.syntax()
.parent()
- .map_or(false, |p| p.kind() == LET_STMT || p.kind() == CONDITION)
+ .map_or(false, |p| p.kind() == LET_STMT || p.kind() == LET_EXPR)
{
"let "
} else {
let expr = match_ast! {
match let_node {
ast::LetStmt(it) => it.initializer(),
- ast::Condition(it) => it.expr(),
+ ast::LetExpr(it) => it.expr(),
_ => None,
}
}?;
match node {
ast::LetStmt(it) => return it.ty().is_some(),
ast::Param(it) => return it.ty().is_some(),
- ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
- ast::IfExpr(it) => {
- return it.condition().and_then(|condition| condition.pat()).is_some()
- && pat_is_enum_variant(db, bind_pat, pat_ty);
- },
- ast::WhileExpr(it) => {
- return it.condition().and_then(|condition| condition.pat()).is_some()
- && pat_is_enum_variant(db, bind_pat, pat_ty);
- },
+ ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
+ ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
+ ast::IfExpr(_) => return false,
+ ast::WhileExpr(_) => return false,
ast::ForExpr(it) => {
// We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
// Type of expr should be iterable.
use ide_db::{
helpers::{
for_each_tail_expr,
- node_ext::{block_as_lone_tail, preorder_expr},
+ node_ext::{block_as_lone_tail, is_pattern_cond, preorder_expr},
FamousDefs,
},
RootDatabase,
return None;
}
- let cond = expr.condition().filter(|cond| !cond.is_pattern_cond())?;
- let cond = cond.expr()?;
+ let cond = expr.condition().filter(|cond| !is_pattern_cond(cond.clone()))?;
let then = expr.then_branch()?;
let else_ = match expr.else_branch()? {
ast::ElseBranch::Block(b) => b,
_ => receiver,
};
let if_expr = make::expr_if(
- make::condition(cond, None),
+ cond,
closure_body.reset_indent(),
Some(ast::ElseBranch::Block(make::block_expr(None, Some(none_path)))),
)
use std::iter::once;
+use ide_db::helpers::node_ext::{is_pattern_cond, single_let};
use syntax::{
ast::{
self,
let cond = if_expr.condition()?;
// Check if there is an IfLet that we can handle.
- let if_let_pat = match cond.pat() {
- None => None, // No IfLet, supported.
- Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
- let path = pat.path()?;
- if path.qualifier().is_some() {
- return None;
- }
+ let (if_let_pat, cond_expr) = if is_pattern_cond(cond.clone()) {
+ let let_ = single_let(cond)?;
+ match let_.pat() {
+ Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
+ let path = pat.path()?;
+ if path.qualifier().is_some() {
+ return None;
+ }
- let bound_ident = pat.fields().next().unwrap();
- if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
- return None;
- }
+ let bound_ident = pat.fields().next().unwrap();
+ if !ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
+ return None;
+ }
- Some((path, bound_ident))
+ (Some((path, bound_ident)), let_.expr()?)
+ }
+ _ => return None, // Unsupported IfLet.
}
- Some(_) => return None, // Unsupported IfLet.
+ } else {
+ (None, cond)
};
- let cond_expr = cond.expr()?;
let then_block = if_expr.then_branch()?;
let then_block = then_block.stmt_list()?;
let then_branch =
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
let cond = invert_boolean_expression(cond_expr);
- make::expr_if(make::condition(cond, None), then_branch, None)
- .indent(if_indent_level)
+ make::expr_if(cond, then_branch, None).indent(if_indent_level)
};
new_expr.syntax().clone_for_update()
}
use std::iter::once;
+use ide_db::helpers::node_ext::is_pattern_cond;
use syntax::{
ast::{
self,
let while_expr = while_kw.parent().and_then(ast::WhileExpr::cast)?;
let while_body = while_expr.loop_body()?;
let while_cond = while_expr.condition()?;
- let while_cond_expr = while_cond.expr()?;
let target = while_expr.syntax().text_range();
acc.add(
let break_block =
make::block_expr(once(make::expr_stmt(make::expr_break(None)).into()), None)
.indent(while_indent_level);
- let block_expr = match while_cond.pat() {
- Some(_) => {
- let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
- let stmts = once(make::expr_stmt(if_expr).into());
- make::block_expr(stmts, None)
- }
- None => {
- let if_cond = make::condition(invert_boolean_expression(while_cond_expr), None);
- let if_expr = make::expr_if(if_cond, break_block, None);
- let stmts =
- once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
- make::block_expr(stmts, while_body.tail_expr())
- }
+ let block_expr = if is_pattern_cond(while_cond.clone()) {
+ let if_expr = make::expr_if(while_cond, while_body, Some(break_block.into()));
+ let stmts = once(make::expr_stmt(if_expr).into());
+ make::block_expr(stmts, None)
+ } else {
+ let if_cond = invert_boolean_expression(while_cond);
+ let if_expr = make::expr_if(if_cond, break_block, None);
+ let stmts = once(make::expr_stmt(if_expr).into()).chain(while_body.statements());
+ make::block_expr(stmts, while_body.tail_expr())
};
let replacement = make::expr_loop(block_expr.indent(while_indent_level));
let stmt = make::expr_stmt(action);
let block = make::block_expr(iter::once(stmt.into()), None);
let controlflow_break_path = make::path_from_text("ControlFlow::Break");
- let condition = make::condition(
+ let condition = make::expr_let(
+ make::tuple_struct_pat(
+ controlflow_break_path,
+ iter::once(make::wildcard_pat().into()),
+ )
+ .into(),
call_expr,
- Some(
- make::tuple_struct_pat(
- controlflow_break_path,
- iter::once(make::wildcard_pat().into()),
- )
- .into(),
- ),
);
- make::expr_if(condition, block, None)
+ make::expr_if(condition.into(), block, None)
}
FlowHandler::IfOption { action } => {
let path = make::ext::ident_path("Some");
let value_pat = make::ext::simple_ident_pat(make::name("value"));
let pattern = make::tuple_struct_pat(path, iter::once(value_pat.into()));
- let cond = make::condition(call_expr, Some(pattern.into()));
+ let cond = make::expr_let(pattern.into(), call_expr);
let value = make::expr_path(make::ext::ident_path("value"));
let action_expr = action.make_result_handler(Some(value));
let action_stmt = make::expr_stmt(action_expr);
let then = make::block_expr(iter::once(action_stmt.into()), None);
- make::expr_if(cond, then, None)
+ make::expr_if(cond.into(), then, None)
}
FlowHandler::MatchOption { none } => {
let some_name = "value";
+use ide_db::helpers::node_ext::is_pattern_cond;
use syntax::{
ast::{self, AstNode},
T,
return None;
}
+ let cond = expr.condition()?;
// This assist should not apply for if-let.
- if expr.condition()?.is_pattern_cond() {
+ if is_pattern_cond(cond.clone()) {
return None;
}
- let cond = expr.condition()?.expr()?;
let then_node = expr.then_branch()?.syntax().clone();
let else_block = match expr.else_branch()? {
ast::ElseBranch::Block(it) => it,
use syntax::{
- ast::{
- edit::AstNodeEdit, make, AstNode, BlockExpr, Condition, ElseBranch, Expr, IfExpr, MatchArm,
- Pat,
- },
+ ast::{edit::AstNodeEdit, make, AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat},
SyntaxKind::WHITESPACE,
};
}
let space_before_guard = guard.syntax().prev_sibling_or_token();
- // FIXME: support `if let` guards too
- if guard.let_token().is_some() {
- return None;
- }
- let guard_condition = guard.expr()?;
+ let guard_condition = guard.condition()?;
let arm_expr = match_arm.expr()?;
- let if_expr = make::expr_if(
- make::condition(guard_condition, None),
- make::block_expr(None, Some(arm_expr.clone())),
- None,
- )
- .indent(arm_expr.indent_level());
+ let if_expr =
+ make::expr_if(guard_condition, make::block_expr(None, Some(arm_expr.clone())), None)
+ .indent(arm_expr.indent_level());
let target = guard.syntax().text_range();
acc.add(
)
}
-// Parses an if-else-if chain to get the conditons and the then branches until we encounter an else
+// Parses an if-else-if chain to get the conditions and the then branches until we encounter an else
// branch or the end.
-fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Condition, BlockExpr)>, Option<BlockExpr>)> {
+fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option<BlockExpr>)> {
let mut conds_blocks = Vec::new();
let mut curr_if = if_expr;
let tail = loop {
let cond = curr_if.condition()?;
- // Not support moving if let to arm guard
- if cond.is_pattern_cond() {
- return None;
- }
conds_blocks.push((cond, curr_if.then_branch()?));
match curr_if.else_branch() {
Some(ElseBranch::IfExpr(e)) => {
);
}
+ #[test]
+ fn move_let_guard_to_arm_body_works() {
+ check_assist(
+ move_guard_to_arm_body,
+ r#"
+fn main() {
+ match 92 {
+ x $0if (let 1 = x) => false,
+ _ => true
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 92 {
+ x => if (let 1 = x) {
+ false
+ },
+ _ => true
+ }
+}
+"#,
+ );
+ }
+
#[test]
fn move_guard_to_arm_body_works_complex_match() {
check_assist(
}
#[test]
- fn move_arm_cond_to_match_guard_if_let_not_works() {
- check_assist_not_applicable(
+ fn move_arm_cond_to_match_guard_if_let_works() {
+ check_assist(
move_arm_cond_to_match_guard,
r#"
fn main() {
match 92 {
- x => if let 62 = x { $0false },
+ x => if let 62 = x && true { $0false },
+ _ => true
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 92 {
+ x if let 62 = x && true => false,
_ => true
}
}
#[test]
fn move_arm_cond_to_match_guard_elseif_iflet() {
- check_assist_not_applicable(
+ check_assist(
move_arm_cond_to_match_guard,
r#"
fn main() {
4
},
}
-}
-"#,
- )
+}"#,
+ r#"
+fn main() {
+ match 92 {
+ 3 => 0,
+ x if x > 10 => 1,
+ x if x > 5 => 2,
+ x if let 4 = 4 => {
+ 42;
+ 3
+ }
+ x => 4,
+ }
+}"#,
+ );
}
#[test]
use std::iter::{self, successors};
use either::Either;
-use ide_db::{defs::NameClass, ty_filter::TryEnum, RootDatabase};
+use ide_db::{
+ defs::NameClass,
+ helpers::node_ext::{is_pattern_cond, single_let},
+ ty_filter::TryEnum,
+ RootDatabase,
+};
use syntax::{
ast::{
self,
None
}
});
- let scrutinee_to_be_expr = if_expr.condition()?.expr()?;
+ let scrutinee_to_be_expr = if_expr.condition()?;
+ let scrutinee_to_be_expr = match single_let(scrutinee_to_be_expr.clone()) {
+ Some(cond) => cond.expr()?,
+ None => scrutinee_to_be_expr,
+ };
let mut pat_seen = false;
let mut cond_bodies = Vec::new();
for if_expr in if_exprs {
let cond = if_expr.condition()?;
- let expr = cond.expr()?;
- let cond = match cond.pat() {
- Some(pat) => {
+ let cond = match single_let(cond.clone()) {
+ Some(let_) => {
+ let pat = let_.pat()?;
+ let expr = let_.expr()?;
+ // FIXME: If one `let` is wrapped in parentheses and the second is not,
+ // we'll exit here.
if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
// Only if all condition expressions are equal we can merge them into a match
return None;
pat_seen = true;
Either::Left(pat)
}
- None => Either::Right(expr),
+ // Multiple `let`, unsupported.
+ None if is_pattern_cond(cond.clone()) => return None,
+ None => Either::Right(cond),
};
let body = if_expr.then_branch()?;
cond_bodies.push((cond, body));
}
}
- let condition = make::condition(scrutinee, Some(if_let_pat));
+ let condition = make::expr_let(if_let_pat, scrutinee);
let then_block = make_block_expr(then_expr.reset_indent());
let else_expr = if is_empty_expr(&else_expr) { None } else { Some(else_expr) };
let if_let_expr = make::expr_if(
- condition,
+ condition.into(),
then_block,
else_expr.map(make_block_expr).map(ast::ElseBranch::Block),
)
)
}
+ #[test]
+ fn test_if_let_with_match_let_chain() {
+ check_assist_not_applicable(
+ replace_if_let_with_match,
+ r#"
+fn main() {
+ if $0let true = true && let Some(1) = None {}
+}
+"#,
+ )
+ }
+
#[test]
fn test_if_let_with_match_basic() {
check_assist(
let block =
make::ext::empty_block_expr().indent(IndentLevel::from_node(let_stmt.syntax()));
- let if_ = make::expr_if(make::condition(init, Some(pat)), block, None);
+ let if_ = make::expr_if(make::expr_let(pat, init).into(), block, None);
let stmt = make::expr_stmt(if_);
edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt));
}
if let hir::Adt::Struct(strukt) = ctx.expected_type.as_ref()?.as_adt()? {
- let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
+ if ctx.path_qual().is_none() {
+ let module = if let Some(module) = ctx.module { module } else { strukt.module(ctx.db) };
+ let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
- let path = module.find_use_path(ctx.db, hir::ModuleDef::from(strukt));
-
- acc.add_struct_literal(ctx, strukt, path, None);
+ acc.add_struct_literal(ctx, strukt, path, None);
+ }
}
Some(())
(ty, name)
},
+ ast::LetExpr(it) => {
+ cov_mark::hit!(expected_type_if_let_without_leading_char);
+ let ty = it.pat()
+ .and_then(|pat| self.sema.type_of_pat(&pat))
+ .or_else(|| it.expr().and_then(|it| self.sema.type_of_expr(&it)))
+ .map(TypeInfo::original);
+ (ty, None)
+ },
ast::ArgList(_) => {
cov_mark::hit!(expected_type_fn_param);
ActiveParameter::at_token(
(ty, None)
},
ast::IfExpr(it) => {
- cov_mark::hit!(expected_type_if_let_without_leading_char);
let ty = it.condition()
- .and_then(|cond| cond.expr())
.and_then(|e| self.sema.type_of_expr(&e))
.map(TypeInfo::original);
(ty, None)
return (PatternRefutability::Irrefutable, has_type_ascription)
},
ast::MatchArm(_) => PatternRefutability::Refutable,
- ast::Condition(_) => PatternRefutability::Refutable,
+ ast::LetExpr(_) => PatternRefutability::Refutable,
ast::ForExpr(_) => PatternRefutability::Irrefutable,
_ => PatternRefutability::Irrefutable,
}
| ast::Expr::TryExpr(_)
| ast::Expr::TupleExpr(_)
| ast::Expr::WhileExpr(_)
+ | ast::Expr::LetExpr(_)
| ast::Expr::YieldExpr(_) => cb(expr),
}
}
_ => false,
}
}
+
+/// Returns the `let` only if there is exactly one (that is, `let pat = expr`
+/// or `((let pat = expr))`, but not `let pat = expr && expr` or `non_let_expr`).
+pub fn single_let(expr: ast::Expr) -> Option<ast::LetExpr> {
+ match expr {
+ ast::Expr::ParenExpr(expr) => expr.expr().and_then(single_let),
+ ast::Expr::LetExpr(expr) => Some(expr),
+ _ => None,
+ }
+}
+
+pub fn is_pattern_cond(expr: ast::Expr) -> bool {
+ match expr {
+ ast::Expr::BinExpr(expr)
+ if expr.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) =>
+ {
+ expr.lhs()
+ .map(is_pattern_cond)
+ .or_else(|| expr.rhs().map(is_pattern_cond))
+ .unwrap_or(false)
+ }
+ ast::Expr::ParenExpr(expr) => expr.expr().map_or(false, is_pattern_cond),
+ ast::Expr::LetExpr(_) => true,
+ _ => false,
+ }
+}
expr_bp(p, None, r, 1);
}
+/// Parses the expression in `let pattern = expression`.
+/// It needs to be parsed with lower precedence than `&&`, so that
+/// `if let true = true && false` is parsed as `if (let true = true) && (true)`
+/// and not `if let true = (true && true)`.
+fn expr_let(p: &mut Parser) {
+ let r = Restrictions { forbid_structs: true, prefer_stmt: false };
+ expr_bp(p, None, r, 5);
+}
+
pub(super) fn stmt(p: &mut Parser, semicolon: Semicolon) {
if p.eat(T![;]) {
return;
T![%] if p.at(T![%=]) => (1, T![%=]),
T![%] => (11, T![%]),
T![&] if p.at(T![&=]) => (1, T![&=]),
+ // If you update this, remember to update `expr_let()` too.
T![&] if p.at(T![&&]) => (4, T![&&]),
T![&] => (8, T![&]),
T![/] if p.at(T![/=]) => (1, T![/=]),
closure_expr(p)
}
T![if] => if_expr(p),
+ T![let] => let_expr(p),
T![loop] => loop_expr(p, None),
T![box] => box_expr(p, None),
assert!(p.at(T![if]));
let m = p.start();
p.bump(T![if]);
- condition(p);
+ expr_no_struct(p);
block_expr(p);
if p.at(T![else]) {
p.bump(T![else]);
assert!(p.at(T![while]));
let m = m.unwrap_or_else(|| p.start());
p.bump(T![while]);
- condition(p);
+ expr_no_struct(p);
block_expr(p);
m.complete(p, WHILE_EXPR)
}
m.complete(p, FOR_EXPR)
}
-// test cond
-// fn foo() { if let Some(_) = None {} }
-// fn bar() {
-// if let Some(_) | Some(_) = None {}
-// if let | Some(_) = None {}
-// while let Some(_) | Some(_) = None {}
-// while let | Some(_) = None {}
+// test let_expr
+// fn foo() {
+// if let Some(_) = None && true {}
+// while 1 == 5 && (let None = None) {}
// }
-fn condition(p: &mut Parser) {
+fn let_expr(p: &mut Parser) -> CompletedMarker {
let m = p.start();
- if p.eat(T![let]) {
- patterns::pattern_top(p);
- p.expect(T![=]);
- }
- expr_no_struct(p);
- m.complete(p, CONDITION);
+ p.bump(T![let]);
+ patterns::pattern_top(p);
+ p.expect(T![=]);
+ expr_let(p);
+ m.complete(p, LET_EXPR)
}
// test match_expr
assert!(p.at(T![if]));
let m = p.start();
p.bump(T![if]);
- if p.eat(T![let]) {
- patterns::pattern_top(p);
- p.expect(T![=]);
- }
expr(p);
m.complete(p, MATCH_GUARD)
}
CLOSURE_EXPR,
IF_EXPR,
WHILE_EXPR,
- CONDITION,
LOOP_EXPR,
FOR_EXPR,
CONTINUE_EXPR,
STMT_LIST,
RETURN_EXPR,
YIELD_EXPR,
+ LET_EXPR,
MATCH_EXPR,
MATCH_ARM_LIST,
MATCH_ARM,
TopEntryPoint::Expr,
"let _ = 0;",
expect![[r#"
- ERROR
- LET_KW "let"
- WHITESPACE " "
- UNDERSCORE "_"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- INT_NUMBER "0"
- SEMICOLON ";"
- error 0: expected expression
- "#]],
+ ERROR
+ LET_EXPR
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "0"
+ SEMICOLON ";"
+ "#]],
);
}
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
fn foo() {
- let foo =
+ let foo = 11
let bar = 1;
let
let baz = 92;
IDENT "foo"
WHITESPACE " "
EQ "="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "11"
WHITESPACE "\n "
LET_STMT
LET_KW "let"
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
-error 24: expected expression
-error 24: expected SEMICOLON
-error 49: expected pattern
-error 49: expected SEMICOLON
-error 75: expected pattern
-error 75: expected SEMICOLON
-error 98: expected pattern
-error 98: expected SEMICOLON
-error 124: expected pattern
-error 124: expected SEMICOLON
+error 27: expected SEMICOLON
+error 52: expected pattern
+error 52: expected SEMICOLON
+error 78: expected pattern
+error 78: expected SEMICOLON
+error 101: expected pattern
+error 101: expected SEMICOLON
+error 127: expected pattern
+error 127: expected SEMICOLON
ERROR
PLUS "+"
WHITESPACE " "
- EXPR_STMT
- TUPLE_EXPR
- L_PAREN "("
- FOR_EXPR
- FOR_KW "for"
- PATH_PAT
- PATH
- PATH_SEGMENT
- L_ANGLE "<"
- ERROR
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- WHITESPACE " "
+ TUPLE_EXPR
+ L_PAREN "("
+ FOR_EXPR
+ FOR_KW "for"
+ PATH_PAT
+ PATH
+ PATH_SEGMENT
+ L_ANGLE "<"
+ ERROR
+ LIFETIME_IDENT "'a"
+ R_ANGLE ">"
+ WHITESPACE " "
+ BIN_EXPR
BIN_EXPR
BIN_EXPR
BIN_EXPR
- BIN_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Trait"
- L_ANGLE "<"
- ERROR
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- ERROR
- R_PAREN ")"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- PAREN_EXPR
- L_PAREN "("
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
- IDENT "Copy"
+ IDENT "Trait"
+ L_ANGLE "<"
+ ERROR
+ LIFETIME_IDENT "'a"
+ R_ANGLE ">"
+ ERROR
R_PAREN ")"
- R_ANGLE ">"
- ERROR
- SEMICOLON ";"
- WHITESPACE "\n "
- LET_STMT
- LET_KW "let"
- WHITESPACE " "
- WILDCARD_PAT
- UNDERSCORE "_"
- COLON ":"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ PAREN_EXPR
+ L_PAREN "("
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Copy"
+ R_PAREN ")"
+ R_ANGLE ">"
+ ERROR
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_EXPR
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ ERROR
+ COLON ":"
WHITESPACE " "
- DYN_TRAIT_TYPE
- TYPE_BOUND_LIST
- TYPE_BOUND
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Box"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- TYPE_ARG
- PAREN_TYPE
- L_PAREN "("
- FOR_TYPE
- FOR_KW "for"
- GENERIC_PARAM_LIST
- L_ANGLE "<"
- LIFETIME_PARAM
- LIFETIME
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Trait"
- GENERIC_ARG_LIST
- L_ANGLE "<"
- LIFETIME_ARG
- LIFETIME
- LIFETIME_IDENT "'a"
- R_ANGLE ">"
- R_PAREN ")"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- TYPE_BOUND
- L_PAREN "("
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Copy"
- R_PAREN ")"
- WHITESPACE " "
- PLUS "+"
- WHITESPACE " "
- TYPE_BOUND
+ BIN_EXPR
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Box"
+ L_ANGLE "<"
+ TUPLE_EXPR
L_PAREN "("
- QUESTION "?"
- PATH_TYPE
+ FOR_EXPR
+ FOR_KW "for"
+ PATH_PAT
+ PATH
+ PATH_SEGMENT
+ L_ANGLE "<"
+ ERROR
+ LIFETIME_IDENT "'a"
+ R_ANGLE ">"
+ WHITESPACE " "
+ BIN_EXPR
+ BIN_EXPR
+ BIN_EXPR
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Trait"
+ L_ANGLE "<"
+ ERROR
+ LIFETIME_IDENT "'a"
+ R_ANGLE ">"
+ ERROR
+ R_PAREN ")"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ PAREN_EXPR
+ L_PAREN "("
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Copy"
+ R_PAREN ")"
+ WHITESPACE " "
+ PLUS "+"
+ WHITESPACE " "
+ PAREN_EXPR
+ L_PAREN "("
+ ERROR
+ QUESTION "?"
+ PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "Sized"
R_PAREN ")"
- ERROR
- R_ANGLE ">"
- SEMICOLON ";"
+ R_ANGLE ">"
+ ERROR
+ SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"
error 179: expected expression
error 180: expected a block
error 180: expected COMMA
-error 180: expected expression
-error 180: expected R_PAREN
-error 180: expected SEMICOLON
-error 215: expected COMMA
-error 215: expected R_ANGLE
-error 235: expected SEMICOLON
-error 235: expected expression
+error 190: expected EQ
+error 190: expected expression
+error 191: expected COMMA
+error 201: expected type
+error 204: expected IN_KW
+error 211: expected expression
+error 214: expected expression
+error 228: expected expression
+error 229: expected R_PAREN
+error 229: expected a block
+error 229: expected COMMA
+error 236: expected expression
+error 237: expected COMMA
+error 237: expected expression
+error 237: expected R_PAREN
+++ /dev/null
-SOURCE_FILE@0..197
- FN@0..37
- FN_KW@0..2 "fn"
- WHITESPACE@2..3 " "
- NAME@3..6
- IDENT@3..6 "foo"
- PARAM_LIST@6..8
- L_PAREN@6..7 "("
- R_PAREN@7..8 ")"
- WHITESPACE@8..9 " "
- BLOCK_EXPR@9..37
- STMT_LIST@9..37
- L_CURLY@9..10 "{"
- WHITESPACE@10..11 " "
- IF_EXPR@11..35
- IF_KW@11..13 "if"
- WHITESPACE@13..14 " "
- CONDITION@14..32
- LET_KW@14..17 "let"
- WHITESPACE@17..18 " "
- TUPLE_STRUCT_PAT@18..25
- PATH@18..22
- PATH_SEGMENT@18..22
- NAME_REF@18..22
- IDENT@18..22 "Some"
- L_PAREN@22..23 "("
- WILDCARD_PAT@23..24
- UNDERSCORE@23..24 "_"
- R_PAREN@24..25 ")"
- WHITESPACE@25..26 " "
- EQ@26..27 "="
- WHITESPACE@27..28 " "
- PATH_EXPR@28..32
- PATH@28..32
- PATH_SEGMENT@28..32
- NAME_REF@28..32
- IDENT@28..32 "None"
- WHITESPACE@32..33 " "
- BLOCK_EXPR@33..35
- STMT_LIST@33..35
- L_CURLY@33..34 "{"
- R_CURLY@34..35 "}"
- WHITESPACE@35..36 " "
- R_CURLY@36..37 "}"
- WHITESPACE@37..38 "\n"
- FN@38..196
- FN_KW@38..40 "fn"
- WHITESPACE@40..41 " "
- NAME@41..44
- IDENT@41..44 "bar"
- PARAM_LIST@44..46
- L_PAREN@44..45 "("
- R_PAREN@45..46 ")"
- WHITESPACE@46..47 " "
- BLOCK_EXPR@47..196
- STMT_LIST@47..196
- L_CURLY@47..48 "{"
- WHITESPACE@48..53 "\n "
- EXPR_STMT@53..87
- IF_EXPR@53..87
- IF_KW@53..55 "if"
- WHITESPACE@55..56 " "
- CONDITION@56..84
- LET_KW@56..59 "let"
- WHITESPACE@59..60 " "
- OR_PAT@60..77
- TUPLE_STRUCT_PAT@60..67
- PATH@60..64
- PATH_SEGMENT@60..64
- NAME_REF@60..64
- IDENT@60..64 "Some"
- L_PAREN@64..65 "("
- WILDCARD_PAT@65..66
- UNDERSCORE@65..66 "_"
- R_PAREN@66..67 ")"
- WHITESPACE@67..68 " "
- PIPE@68..69 "|"
- WHITESPACE@69..70 " "
- TUPLE_STRUCT_PAT@70..77
- PATH@70..74
- PATH_SEGMENT@70..74
- NAME_REF@70..74
- IDENT@70..74 "Some"
- L_PAREN@74..75 "("
- WILDCARD_PAT@75..76
- UNDERSCORE@75..76 "_"
- R_PAREN@76..77 ")"
- WHITESPACE@77..78 " "
- EQ@78..79 "="
- WHITESPACE@79..80 " "
- PATH_EXPR@80..84
- PATH@80..84
- PATH_SEGMENT@80..84
- NAME_REF@80..84
- IDENT@80..84 "None"
- WHITESPACE@84..85 " "
- BLOCK_EXPR@85..87
- STMT_LIST@85..87
- L_CURLY@85..86 "{"
- R_CURLY@86..87 "}"
- WHITESPACE@87..92 "\n "
- EXPR_STMT@92..118
- IF_EXPR@92..118
- IF_KW@92..94 "if"
- WHITESPACE@94..95 " "
- CONDITION@95..115
- LET_KW@95..98 "let"
- WHITESPACE@98..99 " "
- PIPE@99..100 "|"
- WHITESPACE@100..101 " "
- TUPLE_STRUCT_PAT@101..108
- PATH@101..105
- PATH_SEGMENT@101..105
- NAME_REF@101..105
- IDENT@101..105 "Some"
- L_PAREN@105..106 "("
- WILDCARD_PAT@106..107
- UNDERSCORE@106..107 "_"
- R_PAREN@107..108 ")"
- WHITESPACE@108..109 " "
- EQ@109..110 "="
- WHITESPACE@110..111 " "
- PATH_EXPR@111..115
- PATH@111..115
- PATH_SEGMENT@111..115
- NAME_REF@111..115
- IDENT@111..115 "None"
- WHITESPACE@115..116 " "
- BLOCK_EXPR@116..118
- STMT_LIST@116..118
- L_CURLY@116..117 "{"
- R_CURLY@117..118 "}"
- WHITESPACE@118..123 "\n "
- EXPR_STMT@123..160
- WHILE_EXPR@123..160
- WHILE_KW@123..128 "while"
- WHITESPACE@128..129 " "
- CONDITION@129..157
- LET_KW@129..132 "let"
- WHITESPACE@132..133 " "
- OR_PAT@133..150
- TUPLE_STRUCT_PAT@133..140
- PATH@133..137
- PATH_SEGMENT@133..137
- NAME_REF@133..137
- IDENT@133..137 "Some"
- L_PAREN@137..138 "("
- WILDCARD_PAT@138..139
- UNDERSCORE@138..139 "_"
- R_PAREN@139..140 ")"
- WHITESPACE@140..141 " "
- PIPE@141..142 "|"
- WHITESPACE@142..143 " "
- TUPLE_STRUCT_PAT@143..150
- PATH@143..147
- PATH_SEGMENT@143..147
- NAME_REF@143..147
- IDENT@143..147 "Some"
- L_PAREN@147..148 "("
- WILDCARD_PAT@148..149
- UNDERSCORE@148..149 "_"
- R_PAREN@149..150 ")"
- WHITESPACE@150..151 " "
- EQ@151..152 "="
- WHITESPACE@152..153 " "
- PATH_EXPR@153..157
- PATH@153..157
- PATH_SEGMENT@153..157
- NAME_REF@153..157
- IDENT@153..157 "None"
- WHITESPACE@157..158 " "
- BLOCK_EXPR@158..160
- STMT_LIST@158..160
- L_CURLY@158..159 "{"
- R_CURLY@159..160 "}"
- WHITESPACE@160..165 "\n "
- WHILE_EXPR@165..194
- WHILE_KW@165..170 "while"
- WHITESPACE@170..171 " "
- CONDITION@171..191
- LET_KW@171..174 "let"
- WHITESPACE@174..175 " "
- PIPE@175..176 "|"
- WHITESPACE@176..177 " "
- TUPLE_STRUCT_PAT@177..184
- PATH@177..181
- PATH_SEGMENT@177..181
- NAME_REF@177..181
- IDENT@177..181 "Some"
- L_PAREN@181..182 "("
- WILDCARD_PAT@182..183
- UNDERSCORE@182..183 "_"
- R_PAREN@183..184 ")"
- WHITESPACE@184..185 " "
- EQ@185..186 "="
- WHITESPACE@186..187 " "
- PATH_EXPR@187..191
- PATH@187..191
- PATH_SEGMENT@187..191
- NAME_REF@187..191
- IDENT@187..191 "None"
- WHITESPACE@191..192 " "
- BLOCK_EXPR@192..194
- STMT_LIST@192..194
- L_CURLY@192..193 "{"
- R_CURLY@193..194 "}"
- WHITESPACE@194..195 "\n"
- R_CURLY@195..196 "}"
- WHITESPACE@196..197 "\n"
+++ /dev/null
-fn foo() { if let Some(_) = None {} }
-fn bar() {
- if let Some(_) | Some(_) = None {}
- if let | Some(_) = None {}
- while let Some(_) | Some(_) = None {}
- while let | Some(_) = None {}
-}
+++ /dev/null
-SOURCE_FILE
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "foo"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE " "
- IF_EXPR
- IF_KW "if"
- WHITESPACE " "
- CONDITION
- LET_KW "let"
- WHITESPACE " "
- TUPLE_STRUCT_PAT
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- L_PAREN "("
- WILDCARD_PAT
- UNDERSCORE "_"
- R_PAREN ")"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "None"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE " "
- R_CURLY "}"
- WHITESPACE "\n"
- FN
- FN_KW "fn"
- WHITESPACE " "
- NAME
- IDENT "bar"
- PARAM_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE "\n "
- EXPR_STMT
- IF_EXPR
- IF_KW "if"
- WHITESPACE " "
- CONDITION
- LET_KW "let"
- WHITESPACE " "
- OR_PAT
- TUPLE_STRUCT_PAT
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- L_PAREN "("
- WILDCARD_PAT
- UNDERSCORE "_"
- R_PAREN ")"
- WHITESPACE " "
- PIPE "|"
- WHITESPACE " "
- TUPLE_STRUCT_PAT
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- L_PAREN "("
- WILDCARD_PAT
- UNDERSCORE "_"
- R_PAREN ")"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "None"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE "\n "
- EXPR_STMT
- IF_EXPR
- IF_KW "if"
- WHITESPACE " "
- CONDITION
- LET_KW "let"
- WHITESPACE " "
- PIPE "|"
- WHITESPACE " "
- TUPLE_STRUCT_PAT
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- L_PAREN "("
- WILDCARD_PAT
- UNDERSCORE "_"
- R_PAREN ")"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "None"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE "\n "
- EXPR_STMT
- WHILE_EXPR
- WHILE_KW "while"
- WHITESPACE " "
- CONDITION
- LET_KW "let"
- WHITESPACE " "
- OR_PAT
- TUPLE_STRUCT_PAT
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- L_PAREN "("
- WILDCARD_PAT
- UNDERSCORE "_"
- R_PAREN ")"
- WHITESPACE " "
- PIPE "|"
- WHITESPACE " "
- TUPLE_STRUCT_PAT
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- L_PAREN "("
- WILDCARD_PAT
- UNDERSCORE "_"
- R_PAREN ")"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "None"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE "\n "
- WHILE_EXPR
- WHILE_KW "while"
- WHITESPACE " "
- CONDITION
- LET_KW "let"
- WHITESPACE " "
- PIPE "|"
- WHITESPACE " "
- TUPLE_STRUCT_PAT
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "Some"
- L_PAREN "("
- WILDCARD_PAT
- UNDERSCORE "_"
- R_PAREN ")"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "None"
- WHITESPACE " "
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- R_CURLY "}"
- WHITESPACE "\n"
- R_CURLY "}"
- WHITESPACE "\n"
--- /dev/null
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ IF_EXPR
+ IF_KW "if"
+ WHITESPACE " "
+ BIN_EXPR
+ LET_EXPR
+ LET_KW "let"
+ WHITESPACE " "
+ TUPLE_STRUCT_PAT
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Some"
+ L_PAREN "("
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "None"
+ WHITESPACE " "
+ AMP2 "&&"
+ WHITESPACE " "
+ LITERAL
+ TRUE_KW "true"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n "
+ WHILE_EXPR
+ WHILE_KW "while"
+ WHITESPACE " "
+ BIN_EXPR
+ BIN_EXPR
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE " "
+ EQ2 "=="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "5"
+ WHITESPACE " "
+ AMP2 "&&"
+ WHITESPACE " "
+ PAREN_EXPR
+ L_PAREN "("
+ LET_EXPR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "None"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "None"
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
--- /dev/null
+fn foo() {
+ if let Some(_) = None && true {}
+ while 1 == 5 && (let None = None) {}
+}
--- /dev/null
+SOURCE_FILE
+ FN
+ FN_KW "fn"
+ WHITESPACE " "
+ NAME
+ IDENT "foo"
+ PARAM_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE "\n "
+ EXPR_STMT
+ IF_EXPR
+ IF_KW "if"
+ WHITESPACE " "
+ BIN_EXPR
+ LET_EXPR
+ LET_KW "let"
+ WHITESPACE " "
+ TUPLE_STRUCT_PAT
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "Some"
+ L_PAREN "("
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ R_PAREN ")"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "None"
+ WHITESPACE " "
+ AMP2 "&&"
+ WHITESPACE " "
+ LITERAL
+ TRUE_KW "true"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n "
+ WHILE_EXPR
+ WHILE_KW "while"
+ WHITESPACE " "
+ BIN_EXPR
+ BIN_EXPR
+ LITERAL
+ INT_NUMBER "1"
+ WHITESPACE " "
+ EQ2 "=="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "5"
+ WHITESPACE " "
+ AMP2 "&&"
+ WHITESPACE " "
+ PAREN_EXPR
+ L_PAREN "("
+ LET_EXPR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "None"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "None"
+ R_PAREN ")"
+ WHITESPACE " "
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ WHITESPACE "\n"
+ R_CURLY "}"
+ WHITESPACE "\n"
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
+ LET_EXPR
LET_KW "let"
WHITESPACE " "
TUPLE_STRUCT_PAT
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE " "
- LITERAL
- TRUE_KW "true"
- WHITESPACE " "
- R_CURLY "}"
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ TRUE_KW "true"
+ WHITESPACE " "
+ R_CURLY "}"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- LITERAL
- FALSE_KW "false"
+ LITERAL
+ FALSE_KW "false"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "S"
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "S"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- BLOCK_EXPR
- STMT_LIST
- L_CURLY "{"
- WHITESPACE " "
- LITERAL
- TRUE_KW "true"
- WHITESPACE " "
- R_CURLY "}"
+ BLOCK_EXPR
+ STMT_LIST
+ L_CURLY "{"
+ WHITESPACE " "
+ LITERAL
+ TRUE_KW "true"
+ WHITESPACE " "
+ R_CURLY "}"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- BREAK_EXPR
- BREAK_KW "break"
+ BREAK_EXPR
+ BREAK_KW "break"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- BREAK_EXPR
- BREAK_KW "break"
+ BREAK_EXPR
+ BREAK_KW "break"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHITESPACE " "
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
MATCH_GUARD
IF_KW "if"
WHITESPACE " "
- LET_KW "let"
- WHITESPACE " "
- IDENT_PAT
- NAME
- IDENT "foo"
- WHITESPACE " "
- EQ "="
- WHITESPACE " "
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "bar"
+ LET_EXPR
+ LET_KW "let"
+ WHITESPACE " "
+ IDENT_PAT
+ NAME
+ IDENT "foo"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
WHITESPACE " "
FAT_ARROW "=>"
WHITESPACE " "
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "condition_not_met"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "condition_not_met"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "condition_not_met"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "condition_not_met"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "foo"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "foo"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "bar"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
+ CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "bar"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- PREFIX_EXPR
- BANG "!"
- METHOD_CALL_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "x"
- DOT "."
- NAME_REF
- IDENT "get"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
+ PREFIX_EXPR
+ BANG "!"
+ METHOD_CALL_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "x"
+ DOT "."
+ NAME_REF
+ IDENT "get"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- PAREN_EXPR
- L_PAREN "("
- RETURN_EXPR
- RETURN_KW "return"
- R_PAREN ")"
+ PAREN_EXPR
+ L_PAREN "("
+ RETURN_EXPR
+ RETURN_KW "return"
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- PAREN_EXPR
- L_PAREN "("
- RETURN_EXPR
- RETURN_KW "return"
- R_PAREN ")"
+ PAREN_EXPR
+ L_PAREN "("
+ RETURN_EXPR
+ RETURN_KW "return"
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- PAREN_EXPR
- L_PAREN "("
- RETURN_EXPR
- RETURN_KW "return"
- R_PAREN ")"
+ PAREN_EXPR
+ L_PAREN "("
+ RETURN_EXPR
+ RETURN_KW "return"
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- PAREN_EXPR
- L_PAREN "("
- RETURN_EXPR
- RETURN_KW "return"
- R_PAREN ")"
+ PAREN_EXPR
+ L_PAREN "("
+ RETURN_EXPR
+ RETURN_KW "return"
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- PAREN_EXPR
- L_PAREN "("
- RETURN_EXPR
- RETURN_KW "return"
- R_PAREN ")"
+ PAREN_EXPR
+ L_PAREN "("
+ RETURN_EXPR
+ RETURN_KW "return"
+ R_PAREN ")"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- BREAK_EXPR
- BREAK_KW "break"
+ BREAK_EXPR
+ BREAK_KW "break"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- BIN_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i"
- WHITESPACE " "
- EQ2 "=="
- WHITESPACE " "
- LITERAL
- INT_NUMBER "1"
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i"
+ WHITESPACE " "
+ EQ2 "=="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "1"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- BIN_EXPR
- PATH_EXPR
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "u8"
- WHITESPACE " "
- NEQ "!="
- WHITESPACE " "
- LITERAL
- INT_NUMBER "0u8"
+ BIN_EXPR
+ PATH_EXPR
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "u8"
+ WHITESPACE " "
+ NEQ "!="
+ WHITESPACE " "
+ LITERAL
+ INT_NUMBER "0u8"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
+ LET_EXPR
LET_KW "let"
WHITESPACE " "
TUPLE_STRUCT_PAT
IF_EXPR
IF_KW "if"
WHITESPACE " "
- CONDITION
- BIN_EXPR
- CAST_EXPR
- METHOD_CALL_EXPR
- LITERAL
- FLOAT_NUMBER "1.0f32"
- DOT "."
- NAME_REF
- IDENT "floor"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- AS_KW "as"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i64"
+ BIN_EXPR
+ CAST_EXPR
+ METHOD_CALL_EXPR
+ LITERAL
+ FLOAT_NUMBER "1.0f32"
+ DOT "."
+ NAME_REF
+ IDENT "floor"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
WHITESPACE " "
- NEQ "!="
+ AS_KW "as"
WHITESPACE " "
- CAST_EXPR
- METHOD_CALL_EXPR
- LITERAL
- FLOAT_NUMBER "1.0f32"
- DOT "."
- NAME_REF
- IDENT "floor"
- ARG_LIST
- L_PAREN "("
- R_PAREN ")"
- WHITESPACE " "
- AS_KW "as"
- WHITESPACE " "
- PATH_TYPE
- PATH
- PATH_SEGMENT
- NAME_REF
- IDENT "i64"
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i64"
+ WHITESPACE " "
+ NEQ "!="
+ WHITESPACE " "
+ CAST_EXPR
+ METHOD_CALL_EXPR
+ LITERAL
+ FLOAT_NUMBER "1.0f32"
+ DOT "."
+ NAME_REF
+ IDENT "floor"
+ ARG_LIST
+ L_PAREN "("
+ R_PAREN ")"
+ WHITESPACE " "
+ AS_KW "as"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "i64"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
WHILE_EXPR
WHILE_KW "while"
WHITESPACE " "
- CONDITION
- LITERAL
- TRUE_KW "true"
+ LITERAL
+ TRUE_KW "true"
WHITESPACE " "
BLOCK_EXPR
STMT_LIST
pub kind: TargetKind,
/// Is this target a proc-macro
pub is_proc_macro: bool,
+ /// Required features of the target without which it won't build
+ pub required_features: Vec<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
root: AbsPathBuf::assert(PathBuf::from(&meta_tgt.src_path)),
kind: TargetKind::new(meta_tgt.kind.as_slice()),
is_proc_macro,
+ required_features: meta_tgt.required_features.clone(),
});
pkg_data.targets.push(tgt);
}
"hoverRange": true,
"joinLines": true,
"matchingBrace": true,
- "moveItems": true,
+ "moveItem": true,
"onEnter": true,
"openCargoToml": true,
"parentModule": true,
//! See `CargoTargetSpec`
+use std::mem;
+
use cfg::{CfgAtom, CfgExpr};
use ide::{FileId, RunnableKind, TestId};
use project_model::{self, ManifestPath, TargetKind};
pub(crate) package: String,
pub(crate) target: String,
pub(crate) target_kind: TargetKind,
+ pub(crate) required_features: Vec<String>,
}
impl CargoTargetSpec {
pub(crate) fn runnable_args(
snap: &GlobalStateSnapshot,
- spec: Option<CargoTargetSpec>,
+ mut spec: Option<CargoTargetSpec>,
kind: &RunnableKind,
cfg: &Option<CfgExpr>,
) -> Result<(Vec<String>, Vec<String>)> {
let mut args = Vec::new();
let mut extra_args = Vec::new();
+
+ let target_required_features =
+ spec.as_mut().map(|spec| mem::take(&mut spec.required_features)).unwrap_or(Vec::new());
+
match kind {
RunnableKind::Test { test_id, attr } => {
args.push("test".to_string());
let cargo_config = snap.config.cargo();
if cargo_config.all_features {
args.push("--all-features".to_string());
+
+ for feature in target_required_features {
+ args.push("--features".to_string());
+ args.push(feature);
+ }
} else {
let mut features = Vec::new();
if let Some(cfg) = cfg.as_ref() {
required_features(cfg, &mut features);
}
- for feature in cargo_config.features {
- features.push(feature.clone());
- }
+
+ features.extend(cargo_config.features);
+ features.extend(target_required_features);
+
features.dedup();
for feature in features {
args.push("--features".to_string());
package: cargo_ws.package_flag(package_data),
target: target_data.name.clone(),
target_kind: target_data.kind,
+ required_features: target_data.required_features.clone(),
};
Ok(Some(res))
expect-test = "1.2.0-pre.1"
proc-macro2 = "1.0.8"
quote = "1.0.2"
-ungrammar = "=1.14.9"
+ungrammar = "=1.15.0"
test_utils = { path = "../test_utils" }
sourcegen = { path = "../sourcegen" }
impl ast::HasAttrs for IfExpr {}
impl IfExpr {
pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
- pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) }
+ pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn else_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![else]) }
}
impl ast::HasLoopBody for WhileExpr {}
impl WhileExpr {
pub fn while_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![while]) }
- pub fn condition(&self) -> Option<Condition> { support::child(&self.syntax) }
+ pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
}
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct LetExpr {
+ pub(crate) syntax: SyntaxNode,
+}
+impl ast::HasAttrs for LetExpr {}
+impl LetExpr {
+ pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
+ pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
+ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
+ pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StmtList {
pub(crate) syntax: SyntaxNode,
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
}
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Condition {
- pub(crate) syntax: SyntaxNode,
-}
-impl Condition {
- pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
- pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
- pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
-}
-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MatchArmList {
pub(crate) syntax: SyntaxNode,
}
impl MatchGuard {
pub fn if_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![if]) }
- pub fn let_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![let]) }
- pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
- pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
- pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
+ pub fn condition(&self) -> Option<Expr> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
TupleExpr(TupleExpr),
WhileExpr(WhileExpr),
YieldExpr(YieldExpr),
+ LetExpr(LetExpr),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
+impl AstNode for LetExpr {
+ fn can_cast(kind: SyntaxKind) -> bool { kind == LET_EXPR }
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) {
+ Some(Self { syntax })
+ } else {
+ None
+ }
+ }
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
impl AstNode for StmtList {
fn can_cast(kind: SyntaxKind) -> bool { kind == STMT_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
}
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
-impl AstNode for Condition {
- fn can_cast(kind: SyntaxKind) -> bool { kind == CONDITION }
- fn cast(syntax: SyntaxNode) -> Option<Self> {
- if Self::can_cast(syntax.kind()) {
- Some(Self { syntax })
- } else {
- None
- }
- }
- fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
impl AstNode for MatchArmList {
fn can_cast(kind: SyntaxKind) -> bool { kind == MATCH_ARM_LIST }
fn cast(syntax: SyntaxNode) -> Option<Self> {
impl From<YieldExpr> for Expr {
fn from(node: YieldExpr) -> Expr { Expr::YieldExpr(node) }
}
+impl From<LetExpr> for Expr {
+ fn from(node: LetExpr) -> Expr { Expr::LetExpr(node) }
+}
impl AstNode for Expr {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
| INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MACRO_STMTS | MATCH_EXPR
| METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR
| RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR
- | YIELD_EXPR => true,
+ | YIELD_EXPR | LET_EXPR => true,
_ => false,
}
}
TUPLE_EXPR => Expr::TupleExpr(TupleExpr { syntax }),
WHILE_EXPR => Expr::WhileExpr(WhileExpr { syntax }),
YIELD_EXPR => Expr::YieldExpr(YieldExpr { syntax }),
+ LET_EXPR => Expr::LetExpr(LetExpr { syntax }),
_ => return None,
};
Some(res)
Expr::TupleExpr(it) => &it.syntax,
Expr::WhileExpr(it) => &it.syntax,
Expr::YieldExpr(it) => &it.syntax,
+ Expr::LetExpr(it) => &it.syntax,
}
}
}
| TUPLE_EXPR
| WHILE_EXPR
| YIELD_EXPR
+ | LET_EXPR
| STMT_LIST
| RECORD_EXPR_FIELD_LIST
| RECORD_EXPR_FIELD
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for LetExpr {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for StmtList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
std::fmt::Display::fmt(self.syntax(), f)
}
}
-impl std::fmt::Display for Condition {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- std::fmt::Display::fmt(self.syntax(), f)
- }
-}
impl std::fmt::Display for MatchArmList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
expr_from_text(&format!("match {} {}", expr, match_arm_list))
}
pub fn expr_if(
- condition: ast::Condition,
+ condition: ast::Expr,
then_branch: ast::BlockExpr,
else_branch: Option<ast::ElseBranch>,
) -> ast::Expr {
fn expr_from_text(text: &str) -> ast::Expr {
ast_from_text(&format!("const C: () = {};", text))
}
-
-pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition {
- match pattern {
- None => ast_from_text(&format!("const _: () = while {} {{}};", expr)),
- Some(pattern) => {
- ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
- }
- }
+pub fn expr_let(pattern: ast::Pat, expr: ast::Expr) -> ast::LetExpr {
+ ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
}
pub fn arg_list(args: impl IntoIterator<Item = ast::Expr>) -> ast::ArgList {
}
}
-impl ast::Condition {
- pub fn is_pattern_cond(&self) -> bool {
- self.let_token().is_some()
- }
-}
-
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FieldKind {
Name(ast::NameRef),
"CLOSURE_EXPR",
"IF_EXPR",
"WHILE_EXPR",
- "CONDITION",
"LOOP_EXPR",
"FOR_EXPR",
"CONTINUE_EXPR",
"STMT_LIST",
"RETURN_EXPR",
"YIELD_EXPR",
+ "LET_EXPR",
"MATCH_EXPR",
"MATCH_ARM_LIST",
"MATCH_ARM",
ast::PtrType(it) => validate_trait_object_ptr_ty(it, &mut errors),
ast::FnPtrType(it) => validate_trait_object_fn_ptr_ret_ty(it, &mut errors),
ast::MacroRules(it) => validate_macro_rules(it, &mut errors),
+ ast::LetExpr(it) => validate_let_expr(it, &mut errors),
_ => (),
}
}
errors.push(SyntaxError::new("const globals cannot be mutable", mut_token.text_range()));
}
}
+
+fn validate_let_expr(let_: ast::LetExpr, errors: &mut Vec<SyntaxError>) {
+ let mut token = let_.syntax().clone();
+ loop {
+ token = match token.parent() {
+ Some(it) => it,
+ None => break,
+ };
+
+ if ast::ParenExpr::can_cast(token.kind()) {
+ continue;
+ } else if let Some(it) = ast::BinExpr::cast(token.clone()) {
+ if it.op_kind() == Some(ast::BinaryOp::LogicOp(ast::LogicOp::And)) {
+ continue;
+ }
+ } else if ast::IfExpr::can_cast(token.kind())
+ || ast::WhileExpr::can_cast(token.kind())
+ || ast::MatchGuard::can_cast(token.kind())
+ {
+ // It must be part of the condition since the expressions are inside a block.
+ return;
+ }
+
+ break;
+ }
+ errors.push(SyntaxError::new(
+ "`let` expressions are not supported here",
+ let_.syntax().text_range(),
+ ));
+}
IF_EXPR@134..257
IF_KW@134..136 "if"
WHITESPACE@136..137 " "
- CONDITION@137..141
- LITERAL@137..141
- TRUE_KW@137..141 "true"
+ LITERAL@137..141
+ TRUE_KW@137..141 "true"
WHITESPACE@141..142 " "
BLOCK_EXPR@142..257
STMT_LIST@142..257
WHILE_EXPR@262..347
WHILE_KW@262..267 "while"
WHITESPACE@267..268 " "
- CONDITION@268..272
- LITERAL@268..272
- TRUE_KW@268..272 "true"
+ LITERAL@268..272
+ TRUE_KW@268..272 "true"
WHITESPACE@272..273 " "
BLOCK_EXPR@273..347
STMT_LIST@273..347
--- /dev/null
+SOURCE_FILE@0..282\r
+ FN@0..281\r
+ FN_KW@0..2 "fn"\r
+ WHITESPACE@2..3 " "\r
+ NAME@3..6\r
+ IDENT@3..6 "foo"\r
+ PARAM_LIST@6..8\r
+ L_PAREN@6..7 "("\r
+ R_PAREN@7..8 ")"\r
+ WHITESPACE@8..9 " "\r
+ BLOCK_EXPR@9..281\r
+ STMT_LIST@9..281\r
+ L_CURLY@9..10 "{"\r
+ WHITESPACE@10..15 "\n "\r
+ CONST@15..42\r
+ CONST_KW@15..20 "const"\r
+ WHITESPACE@20..21 " "\r
+ UNDERSCORE@21..22 "_"\r
+ COLON@22..23 ":"\r
+ WHITESPACE@23..24 " "\r
+ TUPLE_TYPE@24..26\r
+ L_PAREN@24..25 "("\r
+ R_PAREN@25..26 ")"\r
+ WHITESPACE@26..27 " "\r
+ EQ@27..28 "="\r
+ WHITESPACE@28..29 " "\r
+ LET_EXPR@29..41\r
+ LET_KW@29..32 "let"\r
+ WHITESPACE@32..33 " "\r
+ WILDCARD_PAT@33..34\r
+ UNDERSCORE@33..34 "_"\r
+ WHITESPACE@34..35 " "\r
+ EQ@35..36 "="\r
+ WHITESPACE@36..37 " "\r
+ PATH_EXPR@37..41\r
+ PATH@37..41\r
+ PATH_SEGMENT@37..41\r
+ NAME_REF@37..41\r
+ IDENT@37..41 "None"\r
+ SEMICOLON@41..42 ";"\r
+ WHITESPACE@42..48 "\n\n "\r
+ LET_STMT@48..83\r
+ LET_KW@48..51 "let"\r
+ WHITESPACE@51..52 " "\r
+ WILDCARD_PAT@52..53\r
+ UNDERSCORE@52..53 "_"\r
+ WHITESPACE@53..54 " "\r
+ EQ@54..55 "="\r
+ WHITESPACE@55..56 " "\r
+ IF_EXPR@56..82\r
+ IF_KW@56..58 "if"\r
+ WHITESPACE@58..59 " "\r
+ LITERAL@59..63\r
+ TRUE_KW@59..63 "true"\r
+ WHITESPACE@63..64 " "\r
+ BLOCK_EXPR@64..82\r
+ STMT_LIST@64..82\r
+ L_CURLY@64..65 "{"\r
+ WHITESPACE@65..66 " "\r
+ PAREN_EXPR@66..80\r
+ L_PAREN@66..67 "("\r
+ LET_EXPR@67..79\r
+ LET_KW@67..70 "let"\r
+ WHITESPACE@70..71 " "\r
+ WILDCARD_PAT@71..72\r
+ UNDERSCORE@71..72 "_"\r
+ WHITESPACE@72..73 " "\r
+ EQ@73..74 "="\r
+ WHITESPACE@74..75 " "\r
+ PATH_EXPR@75..79\r
+ PATH@75..79\r
+ PATH_SEGMENT@75..79\r
+ NAME_REF@75..79\r
+ IDENT@75..79 "None"\r
+ R_PAREN@79..80 ")"\r
+ WHITESPACE@80..81 " "\r
+ R_CURLY@81..82 "}"\r
+ SEMICOLON@82..83 ";"\r
+ WHITESPACE@83..89 "\n\n "\r
+ IF_EXPR@89..279\r
+ IF_KW@89..91 "if"\r
+ WHITESPACE@91..92 " "\r
+ BIN_EXPR@92..114\r
+ LITERAL@92..96\r
+ TRUE_KW@92..96 "true"\r
+ WHITESPACE@96..97 " "\r
+ AMP2@97..99 "&&"\r
+ WHITESPACE@99..100 " "\r
+ PAREN_EXPR@100..114\r
+ L_PAREN@100..101 "("\r
+ LET_EXPR@101..113\r
+ LET_KW@101..104 "let"\r
+ WHITESPACE@104..105 " "\r
+ WILDCARD_PAT@105..106\r
+ UNDERSCORE@105..106 "_"\r
+ WHITESPACE@106..107 " "\r
+ EQ@107..108 "="\r
+ WHITESPACE@108..109 " "\r
+ PATH_EXPR@109..113\r
+ PATH@109..113\r
+ PATH_SEGMENT@109..113\r
+ NAME_REF@109..113\r
+ IDENT@109..113 "None"\r
+ R_PAREN@113..114 ")"\r
+ WHITESPACE@114..115 " "\r
+ BLOCK_EXPR@115..279\r
+ STMT_LIST@115..279\r
+ L_CURLY@115..116 "{"\r
+ WHITESPACE@116..125 "\n "\r
+ EXPR_STMT@125..140\r
+ PAREN_EXPR@125..139\r
+ L_PAREN@125..126 "("\r
+ LET_EXPR@126..138\r
+ LET_KW@126..129 "let"\r
+ WHITESPACE@129..130 " "\r
+ WILDCARD_PAT@130..131\r
+ UNDERSCORE@130..131 "_"\r
+ WHITESPACE@131..132 " "\r
+ EQ@132..133 "="\r
+ WHITESPACE@133..134 " "\r
+ PATH_EXPR@134..138\r
+ PATH@134..138\r
+ PATH_SEGMENT@134..138\r
+ NAME_REF@134..138\r
+ IDENT@134..138 "None"\r
+ R_PAREN@138..139 ")"\r
+ SEMICOLON@139..140 ";"\r
+ WHITESPACE@140..149 "\n "\r
+ WHILE_EXPR@149..273\r
+ WHILE_KW@149..154 "while"\r
+ WHITESPACE@154..155 " "\r
+ LET_EXPR@155..167\r
+ LET_KW@155..158 "let"\r
+ WHITESPACE@158..159 " "\r
+ WILDCARD_PAT@159..160\r
+ UNDERSCORE@159..160 "_"\r
+ WHITESPACE@160..161 " "\r
+ EQ@161..162 "="\r
+ WHITESPACE@162..163 " "\r
+ PATH_EXPR@163..167\r
+ PATH@163..167\r
+ PATH_SEGMENT@163..167\r
+ NAME_REF@163..167\r
+ IDENT@163..167 "None"\r
+ WHITESPACE@167..168 " "\r
+ BLOCK_EXPR@168..273\r
+ STMT_LIST@168..273\r
+ L_CURLY@168..169 "{"\r
+ WHITESPACE@169..182 "\n "\r
+ MATCH_EXPR@182..263\r
+ MATCH_KW@182..187 "match"\r
+ WHITESPACE@187..188 " "\r
+ PATH_EXPR@188..192\r
+ PATH@188..192\r
+ PATH_SEGMENT@188..192\r
+ NAME_REF@188..192\r
+ IDENT@188..192 "None"\r
+ WHITESPACE@192..193 " "\r
+ MATCH_ARM_LIST@193..263\r
+ L_CURLY@193..194 "{"\r
+ WHITESPACE@194..211 "\n "\r
+ MATCH_ARM@211..249\r
+ WILDCARD_PAT@211..212\r
+ UNDERSCORE@211..212 "_"\r
+ WHITESPACE@212..213 " "\r
+ MATCH_GUARD@213..228\r
+ IF_KW@213..215 "if"\r
+ WHITESPACE@215..216 " "\r
+ LET_EXPR@216..228\r
+ LET_KW@216..219 "let"\r
+ WHITESPACE@219..220 " "\r
+ WILDCARD_PAT@220..221\r
+ UNDERSCORE@220..221 "_"\r
+ WHITESPACE@221..222 " "\r
+ EQ@222..223 "="\r
+ WHITESPACE@223..224 " "\r
+ PATH_EXPR@224..228\r
+ PATH@224..228\r
+ PATH_SEGMENT@224..228\r
+ NAME_REF@224..228\r
+ IDENT@224..228 "None"\r
+ WHITESPACE@228..229 " "\r
+ FAT_ARROW@229..231 "=>"\r
+ WHITESPACE@231..232 " "\r
+ BLOCK_EXPR@232..249\r
+ STMT_LIST@232..249\r
+ L_CURLY@232..233 "{"\r
+ WHITESPACE@233..234 " "\r
+ LET_STMT@234..247\r
+ LET_KW@234..237 "let"\r
+ WHITESPACE@237..238 " "\r
+ WILDCARD_PAT@238..239\r
+ UNDERSCORE@238..239 "_"\r
+ WHITESPACE@239..240 " "\r
+ EQ@240..241 "="\r
+ WHITESPACE@241..242 " "\r
+ PATH_EXPR@242..246\r
+ PATH@242..246\r
+ PATH_SEGMENT@242..246\r
+ NAME_REF@242..246\r
+ IDENT@242..246 "None"\r
+ SEMICOLON@246..247 ";"\r
+ WHITESPACE@247..248 " "\r
+ R_CURLY@248..249 "}"\r
+ WHITESPACE@249..262 "\n "\r
+ R_CURLY@262..263 "}"\r
+ WHITESPACE@263..272 "\n "\r
+ R_CURLY@272..273 "}"\r
+ WHITESPACE@273..278 "\n "\r
+ R_CURLY@278..279 "}"\r
+ WHITESPACE@279..280 "\n"\r
+ R_CURLY@280..281 "}"\r
+ WHITESPACE@281..282 "\n"\r
+error 29..41: `let` expressions are not supported here\r
+error 67..79: `let` expressions are not supported here\r
+error 126..138: `let` expressions are not supported here\r
--- /dev/null
+fn foo() {
+ const _: () = let _ = None;
+
+ let _ = if true { (let _ = None) };
+
+ if true && (let _ = None) {
+ (let _ = None);
+ while let _ = None {
+ match None {
+ _ if let _ = None => { let _ = None; }
+ }
+ }
+ }
+}
* same configurations as VSCode extension, `rust-analyzer.server.path`, `rust-analyzer.cargo.features` etc.
* same commands too, `rust-analyzer.analyzerStatus`, `rust-analyzer.ssr` etc.
* inlay hints for variables and method chaining, _Neovim Only_
- * semantic highlighting is not implemented yet
Note: for code actions, use `coc-codeaction-cursor` and `coc-codeaction-selected`; `coc-codeaction` and `coc-codeaction-line` are unlikely to be useful.
endif
----
-=== Sublime Text 3
+=== Sublime Text
-Prerequisites: You have installed the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
-
-You also need the `LSP` package.
-To install it:
+==== Sublime Text 4:
+* Follow the instructions in link:https://github.com/sublimelsp/LSP-rust-analyzer[LSP-rust-analyzer].
-1. If you've never installed a Sublime Text package, install Package Control:
- * Open the command palette (Win/Linux: `ctrl+shift+p`, Mac: `cmd+shift+p`)
- * Type `Install Package Control`, press enter
-2. In the command palette, run `Package control: Install package`, and in the list that pops up, type `LSP` and press enter.
+NOTE: Install link:https://packagecontrol.io/packages/LSP-file-watcher-chokidar[LSP-file-watcher-chokidar] to enable file watching (`workspace/didChangeWatchedFiles`).
-Finally, with your Rust project open, in the command palette, run `LSP: Enable Language Server In Project` or `LSP: Enable Language Server Globally`, then select `rust-analyzer` in the list that pops up to enable the rust-analyzer LSP.
-The latter means that rust-analyzer is enabled by default in Rust projects.
+==== Sublime Text 3:
+* Install the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>>.
+* Install the link:https://packagecontrol.io/packages/LSP[LSP package].
+* From the command palette, run `LSP: Enable Language Server Globally` and select `rust-analyzer`.
-If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the bottom bar, and after waiting a bit, functionality like tooltips on hovering over variables should become available.
+If it worked, you should see "rust-analyzer, Line X, Column Y" on the left side of the status bar, and after waiting a bit, functionalities like tooltips on hovering over variables should become available.
If you get an error saying `No such file or directory: 'rust-analyzer'`, see the <<rust-analyzer-language-server-binary,`rust-analyzer` binary>> section on installing the language server binary.