5612: Remove TypeAscriptionOwner r=matklad a=matklad



bors r+
🤖

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-07-30 18:53:36 +00:00 committed by GitHub
commit 134d3c3c50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 64 additions and 90 deletions

View file

@ -1,6 +1,6 @@
use hir::HirDisplay;
use ra_syntax::{
ast::{self, AstNode, LetStmt, NameOwner, TypeAscriptionOwner},
ast::{self, AstNode, LetStmt, NameOwner},
TextRange,
};
@ -22,11 +22,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
// }
// ```
pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let stmt = ctx.find_node_at_offset::<LetStmt>()?;
let module = ctx.sema.scope(stmt.syntax()).module()?;
let expr = stmt.initializer()?;
let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;
let module = ctx.sema.scope(let_stmt.syntax()).module()?;
let expr = let_stmt.initializer()?;
// Must be a binding
let pat = match stmt.pat()? {
let pat = match let_stmt.pat()? {
ast::Pat::BindPat(bind_pat) => bind_pat,
_ => return None,
};
@ -34,8 +34,8 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
// The binding must have a name
let name = pat.name()?;
let name_range = name.syntax().text_range();
let stmt_range = stmt.syntax().text_range();
let eq_range = stmt.eq_token()?.text_range();
let stmt_range = let_stmt.syntax().text_range();
let eq_range = let_stmt.eq_token()?.text_range();
// Assist should only be applicable if cursor is between 'let' and '='
let let_range = TextRange::new(stmt_range.start(), eq_range.start());
let cursor_in_range = let_range.contains_range(ctx.frange.range);
@ -44,7 +44,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio
}
// Assist not applicable if the type has already been specified
// and it has no placeholders
let ascribed_ty = stmt.ascribed_type();
let ascribed_ty = let_stmt.ty();
if let Some(ty) = &ascribed_ty {
if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
return None;

View file

@ -1,9 +1,6 @@
use hir::Adt;
use ra_syntax::{
ast::{
self, AstNode, GenericParamsOwner, NameOwner, StructKind, TypeAscriptionOwner,
VisibilityOwner,
},
ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
T,
};
use stdx::{format_to, SepBy};
@ -54,9 +51,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
let params = field_list
.fields()
.filter_map(|f| {
Some(format!("{}: {}", f.name()?.syntax(), f.ascribed_type()?.syntax()))
})
.filter_map(|f| Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax())))
.sep_by(", ");
let fields = field_list.fields().filter_map(|f| f.name()).sep_by(", ");

View file

@ -1,5 +1,5 @@
use ra_syntax::{
ast::{self, GenericParamsOwner, NameOwner, TypeAscriptionOwner},
ast::{self, GenericParamsOwner, NameOwner},
AstNode, SyntaxKind, TextRange, TextSize,
};
use rustc_hash::FxHashSet;
@ -67,7 +67,7 @@ fn generate_fn_def_assist(
// otherwise, if there's a single reference parameter without a named liftime, use that
let fn_params_without_lifetime: Vec<_> = param_list
.params()
.filter_map(|param| match param.ascribed_type() {
.filter_map(|param| match param.ty() {
Some(ast::TypeRef::ReferenceType(ascribed_type))
if ascribed_type.lifetime_token() == None =>
{

View file

@ -8,7 +8,7 @@ use hir_expand::{
InFile,
};
use ra_arena::{map::ArenaMap, Arena};
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner, VisibilityOwner};
use ra_syntax::ast::{self, NameOwner, VisibilityOwner};
use crate::{
body::{CfgExpander, LowerCtx},
@ -251,7 +251,7 @@ fn lower_struct(
|| Either::Right(fd.clone()),
|| FieldData {
name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
type_ref: TypeRef::from_ast_opt(&ctx, fd.ascribed_type()),
type_ref: TypeRef::from_ast_opt(&ctx, fd.ty()),
visibility: RawVisibility::from_ast(db, ast.with_value(fd.visibility())),
},
);

View file

@ -11,7 +11,7 @@ use ra_arena::Arena;
use ra_syntax::{
ast::{
self, ArgListOwner, ArrayExprKind, LiteralKind, LoopBodyOwner, ModuleItemOwner, NameOwner,
SlicePatComponents, TypeAscriptionOwner,
SlicePatComponents,
},
AstNode, AstPtr,
};
@ -466,8 +466,7 @@ impl ExprCollector<'_> {
if let Some(pl) = e.param_list() {
for param in pl.params() {
let pat = self.collect_pat_opt(param.pat());
let type_ref =
param.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx(), it));
let type_ref = param.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
args.push(pat);
arg_types.push(type_ref);
}
@ -607,8 +606,7 @@ impl ExprCollector<'_> {
.map(|s| match s {
ast::Stmt::LetStmt(stmt) => {
let pat = self.collect_pat_opt(stmt.pat());
let type_ref =
stmt.ascribed_type().map(|it| TypeRef::from_ast(&self.ctx(), it));
let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
let initializer = stmt.initializer().map(|e| self.collect_expr(e));
Statement::Let { pat, type_ref, initializer }
}

View file

@ -13,7 +13,7 @@ use std::{
sync::Arc,
};
use ast::{AstNode, AttrsOwner, NameOwner, StructKind, TypeAscriptionOwner};
use ast::{AstNode, AttrsOwner, NameOwner, StructKind};
use either::Either;
use hir_expand::{
ast_id_map::FileAstId,

View file

@ -209,7 +209,7 @@ impl Ctx {
fn lower_record_field(&mut self, field: &ast::RecordField) -> Option<Field> {
let name = field.name()?.as_name();
let visibility = self.lower_visibility(field);
let type_ref = self.lower_type_ref_opt(field.ascribed_type());
let type_ref = self.lower_type_ref_opt(field.ty());
let res = Field { name, type_ref, visibility };
Some(res)
}
@ -286,7 +286,7 @@ impl Ctx {
let mut has_self_param = false;
if let Some(param_list) = func.param_list() {
if let Some(self_param) = param_list.self_param() {
let self_type = match self_param.ascribed_type() {
let self_type = match self_param.ty() {
Some(type_ref) => TypeRef::from_ast(&self.body_ctx, type_ref),
None => {
let self_type = TypeRef::Path(name![Self].into());
@ -305,7 +305,7 @@ impl Ctx {
has_self_param = true;
}
for param in param_list.params() {
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ascribed_type());
let type_ref = TypeRef::from_ast_opt(&self.body_ctx, param.ty());
params.push(type_ref);
}
}
@ -370,7 +370,7 @@ impl Ctx {
fn lower_static(&mut self, static_: &ast::Static) -> Option<FileItemTreeId<Static>> {
let name = static_.name()?.as_name();
let type_ref = self.lower_type_ref_opt(static_.ascribed_type());
let type_ref = self.lower_type_ref_opt(static_.ty());
let visibility = self.lower_visibility(static_);
let mutable = static_.mut_token().is_some();
let ast_id = self.source_ast_id_map.ast_id(static_);
@ -380,7 +380,7 @@ impl Ctx {
fn lower_const(&mut self, konst: &ast::Const) -> FileItemTreeId<Const> {
let name = konst.name().map(|it| it.as_name());
let type_ref = self.lower_type_ref_opt(konst.ascribed_type());
let type_ref = self.lower_type_ref_opt(konst.ty());
let visibility = self.lower_visibility(konst);
let ast_id = self.source_ast_id_map.ast_id(konst);
let res = Const { name, visibility, type_ref, ast_id };

View file

@ -9,7 +9,7 @@ use hir_expand::{
hygiene::Hygiene,
name::{name, AsName},
};
use ra_syntax::ast::{self, AstNode, TypeAscriptionOwner, TypeBoundsOwner};
use ra_syntax::ast::{self, AstNode, TypeBoundsOwner};
use super::AssociatedTypeBinding;
use crate::{
@ -189,7 +189,7 @@ fn lower_generic_args_from_fn_path(
if let Some(params) = params {
let mut param_types = Vec::new();
for param in params.params() {
let type_ref = TypeRef::from_ast_opt(&ctx, param.ascribed_type());
let type_ref = TypeRef::from_ast_opt(&ctx, param.ty());
param_types.push(type_ref);
}
let arg = GenericArg::Type(TypeRef::Tuple(param_types));

View file

@ -1,7 +1,7 @@
//! HIR for references to types. Paths in these are not yet resolved. They can
//! be directly created from an ast::TypeRef, without further queries.
use ra_syntax::ast::{self, TypeAscriptionOwner};
use ra_syntax::ast::{self};
use crate::{body::LowerCtx, path::Path};
@ -124,10 +124,7 @@ impl TypeRef {
is_varargs = param.dotdotdot_token().is_some();
}
pl.params()
.map(|p| p.ascribed_type())
.map(|it| TypeRef::from_ast_opt(&ctx, it))
.collect()
pl.params().map(|p| p.ty()).map(|it| TypeRef::from_ast_opt(&ctx, it)).collect()
} else {
Vec::new()
};

View file

@ -1,6 +1,6 @@
//! FIXME: write short doc here
use ra_syntax::ast::{self, AstNode, NameOwner, TypeAscriptionOwner, VisibilityOwner};
use ra_syntax::ast::{self, AstNode, NameOwner, VisibilityOwner};
use stdx::format_to;
pub(crate) trait ShortLabel {
@ -55,19 +55,19 @@ impl ShortLabel for ast::TypeAlias {
impl ShortLabel for ast::Const {
fn short_label(&self) -> Option<String> {
short_label_from_ascribed_node(self, "const ")
short_label_from_ty(self, self.ty(), "const ")
}
}
impl ShortLabel for ast::Static {
fn short_label(&self) -> Option<String> {
short_label_from_ascribed_node(self, "static ")
short_label_from_ty(self, self.ty(), "static ")
}
}
impl ShortLabel for ast::RecordField {
fn short_label(&self) -> Option<String> {
short_label_from_ascribed_node(self, "")
short_label_from_ty(self, self.ty(), "")
}
}
@ -77,13 +77,13 @@ impl ShortLabel for ast::Variant {
}
}
fn short_label_from_ascribed_node<T>(node: &T, prefix: &str) -> Option<String>
fn short_label_from_ty<T>(node: &T, ty: Option<ast::TypeRef>, prefix: &str) -> Option<String>
where
T: NameOwner + VisibilityOwner + TypeAscriptionOwner,
T: NameOwner + VisibilityOwner,
{
let mut buf = short_label_from_node(node, prefix)?;
if let Some(type_ref) = node.ascribed_type() {
if let Some(type_ref) = ty {
format_to!(buf, ": {}", type_ref.syntax());
}

View file

@ -1,5 +1,5 @@
use ra_syntax::{
ast::{self, AttrsOwner, GenericParamsOwner, NameOwner, TypeAscriptionOwner},
ast::{self, AttrsOwner, GenericParamsOwner, NameOwner},
match_ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, TextRange, WalkEvent,
};
@ -52,18 +52,11 @@ pub fn file_structure(file: &SourceFile) -> Vec<StructureNode> {
fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
fn decl<N: NameOwner + AttrsOwner>(node: N) -> Option<StructureNode> {
decl_with_detail(node, None)
}
fn decl_with_ascription<N: NameOwner + AttrsOwner + TypeAscriptionOwner>(
node: N,
) -> Option<StructureNode> {
let ty = node.ascribed_type();
decl_with_type_ref(node, ty)
decl_with_detail(&node, None)
}
fn decl_with_type_ref<N: NameOwner + AttrsOwner>(
node: N,
node: &N,
type_ref: Option<ast::TypeRef>,
) -> Option<StructureNode> {
let detail = type_ref.map(|type_ref| {
@ -75,7 +68,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
}
fn decl_with_detail<N: NameOwner + AttrsOwner>(
node: N,
node: &N,
detail: Option<String>,
) -> Option<StructureNode> {
let name = node.name()?;
@ -124,7 +117,7 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
collapse_ws(ret_type.syntax(), &mut detail);
}
decl_with_detail(it, Some(detail))
decl_with_detail(&it, Some(detail))
},
ast::Struct(it) => decl(it),
ast::Union(it) => decl(it),
@ -132,13 +125,10 @@ fn structure_node(node: &SyntaxNode) -> Option<StructureNode> {
ast::Variant(it) => decl(it),
ast::Trait(it) => decl(it),
ast::Module(it) => decl(it),
ast::TypeAlias(it) => {
let ty = it.type_ref();
decl_with_type_ref(it, ty)
},
ast::RecordField(it) => decl_with_ascription(it),
ast::Const(it) => decl_with_ascription(it),
ast::Static(it) => decl_with_ascription(it),
ast::TypeAlias(it) => decl_with_type_ref(&it, it.type_ref()),
ast::RecordField(it) => decl_with_type_ref(&it, it.ty()),
ast::Const(it) => decl_with_type_ref(&it, it.ty()),
ast::Static(it) => decl_with_type_ref(&it, it.ty()),
ast::Impl(it) => {
let target_type = it.target_type()?;
let target_trait = it.target_trait();

View file

@ -2,7 +2,7 @@ use hir::{Adt, Callable, HirDisplay, Semantics, Type};
use ra_ide_db::RootDatabase;
use ra_prof::profile;
use ra_syntax::{
ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
ast::{self, ArgListOwner, AstNode},
match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
};
use stdx::to_lower_snake_case;
@ -230,10 +230,10 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
match_ast! {
match node {
ast::LetStmt(it) => {
return it.ascribed_type().is_some()
return it.ty().is_some()
},
ast::Param(it) => {
return it.ascribed_type().is_some()
return it.ty().is_some()
},
ast::MatchArm(_it) => {
return pat_is_enum_variant(db, bind_pat, pat_ty);

View file

@ -7,7 +7,8 @@ use ra_ide_db::{
RootDatabase,
};
use ra_syntax::{
algo::find_node_at_offset, ast, ast::NameOwner, ast::TypeAscriptionOwner,
algo::find_node_at_offset,
ast::{self, NameOwner},
lex_single_valid_syntax_kind, match_ast, AstNode, SyntaxKind, SyntaxNode, SyntaxToken,
};
use ra_text_edit::TextEdit;
@ -155,7 +156,7 @@ fn rename_to_self(
return None; // method already has self param
}
let first_param = params.params().next()?;
let mutable = match first_param.ascribed_type() {
let mutable = match first_param.ty() {
Some(ast::TypeRef::ReferenceType(rt)) => rt.mut_token().is_some(),
_ => return None, // not renaming other types
};

View file

@ -35,12 +35,12 @@ pub struct Const {
impl ast::AttrsOwner for Const {}
impl ast::NameOwner for Const {}
impl ast::VisibilityOwner for Const {}
impl ast::TypeAscriptionOwner for Const {}
impl Const {
pub fn default_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![default]) }
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
pub fn underscore_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![_]) }
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<TypeRef> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn body(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
@ -148,11 +148,11 @@ pub struct Static {
impl ast::AttrsOwner for Static {}
impl ast::NameOwner for Static {}
impl ast::VisibilityOwner for Static {}
impl ast::TypeAscriptionOwner for Static {}
impl Static {
pub fn static_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![static]) }
pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<TypeRef> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn body(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
@ -361,7 +361,6 @@ pub struct SelfParam {
pub(crate) syntax: SyntaxNode,
}
impl ast::AttrsOwner for SelfParam {}
impl ast::TypeAscriptionOwner for SelfParam {}
impl SelfParam {
pub fn amp_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![&]) }
pub fn lifetime_token(&self) -> Option<SyntaxToken> {
@ -370,16 +369,17 @@ impl SelfParam {
pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
pub fn self_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![self]) }
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<TypeRef> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Param {
pub(crate) syntax: SyntaxNode,
}
impl ast::AttrsOwner for Param {}
impl ast::TypeAscriptionOwner for Param {}
impl Param {
pub fn pat(&self) -> Option<Pat> { support::child(&self.syntax) }
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<TypeRef> { support::child(&self.syntax) }
pub fn dotdotdot_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![...]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -414,9 +414,9 @@ pub struct RecordField {
impl ast::AttrsOwner for RecordField {}
impl ast::NameOwner for RecordField {}
impl ast::VisibilityOwner for RecordField {}
impl ast::TypeAscriptionOwner for RecordField {}
impl RecordField {
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<TypeRef> { support::child(&self.syntax) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TupleField {
@ -495,10 +495,10 @@ pub struct ConstParam {
}
impl ast::AttrsOwner for ConstParam {}
impl ast::NameOwner for ConstParam {}
impl ast::TypeAscriptionOwner for ConstParam {}
impl ConstParam {
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
pub fn colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<TypeRef> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn default_val(&self) -> Option<Expr> { support::child(&self.syntax) }
}
@ -1203,11 +1203,11 @@ pub struct LetStmt {
pub(crate) syntax: SyntaxNode,
}
impl ast::AttrsOwner for LetStmt {}
impl ast::TypeAscriptionOwner for LetStmt {}
impl LetStmt {
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 colon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![:]) }
pub fn ty(&self) -> Option<TypeRef> { support::child(&self.syntax) }
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
pub fn initializer(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }

View file

@ -9,12 +9,6 @@ use crate::{
SyntaxToken, T,
};
pub trait TypeAscriptionOwner: AstNode {
fn ascribed_type(&self) -> Option<ast::TypeRef> {
support::child(self.syntax())
}
}
pub trait NameOwner: AstNode {
fn name(&self) -> Option<ast::Name> {
support::child(self.syntax())

View file

@ -660,7 +660,6 @@ fn extract_struct_traits(ast: &mut AstSrc) {
("GenericParamsOwner", &["generic_param_list", "where_clause"]),
("TypeBoundsOwner", &["type_bound_list", "colon_token"]),
("ModuleItemOwner", &["items"]),
("TypeAscriptionOwner", &["ascribed_type"]),
("LoopBodyOwner", &["label", "loop_body"]),
("ArgListOwner", &["arg_list"]),
];

View file

@ -61,13 +61,13 @@ ParamList =
SelfParam =
Attr* (
('&' 'lifetime'?)? 'mut'? 'self'
| 'mut'? 'self' ':' ascribed_type:TypeRef
| 'mut'? 'self' ':' ty:TypeRef
)
Param =
Attr* (
Pat (':' ascribed_type:TypeRef)
| ascribed_type:TypeRef
Pat (':' ty:TypeRef)
| ty:TypeRef
| '...'
)
@ -88,7 +88,7 @@ RecordFieldList =
'{' fields:(RecordField (',' RecordField)* ','?)? '}'
RecordField =
Attr* Visibility? Name ':' ascribed_type:TypeRef
Attr* Visibility? Name ':' ty:TypeRef
TupleFieldList =
'(' fields:(TupleField (',' TupleField)* ','?)? ')'
@ -115,11 +115,11 @@ Union =
RecordFieldList
Const =
Attr* Visibility? 'default'? 'const' (Name | '_') ':' ascribed_type:TypeRef
Attr* Visibility? 'default'? 'const' (Name | '_') ':' ty:TypeRef
'=' body:Expr ';'
Static =
Attr* Visibility? 'static'? 'mut'? Name ':' ascribed_type:TypeRef
Attr* Visibility? 'static'? 'mut'? Name ':' ty:TypeRef
'=' body:Expr ';'
Trait =
@ -166,7 +166,7 @@ TypeParam =
('=' default_type:TypeRef)?
ConstParam =
Attr* 'const' Name ':' ascribed_type:TypeRef
Attr* 'const' Name ':' ty:TypeRef
('=' default_val:Expr)?
LifetimeParam =
@ -439,7 +439,7 @@ ExprStmt =
Attr* Expr ';'
LetStmt =
Attr* 'let' Pat (':' ascribed_type:TypeRef)
Attr* 'let' Pat (':' ty:TypeRef)
'=' initializer:Expr ';'
Path =