Skip to content

Commit

Permalink
Better token handling inside turbofish (FuelLabs#3883)
Browse files Browse the repository at this point in the history
This builds on top of FuelLabs#3847 and addresses two main issues:

1. This adds support for struct instantiation, expressions like
`B::<Option<u32>>{}` will now produce proper tokens inside turbofish.

2. The granularity of tokens is improved, instead of only producing one
token for the final top level type arguments, we resolve types
recursively inside of type arguments and produce appropriate tokens.
Expressions like `fun::<Option<Result<u32, u32>>>(Option::None);` will
produce different tokens for `Option` and `Result` that both point to
valid definitions.

As a sidenote, this also fixes an issue where the information produced
by type checking of call path bindings would get thrown away.
  • Loading branch information
IGI-111 authored Jan 27, 2023
1 parent d733408 commit d558727
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 67 deletions.
10 changes: 10 additions & 0 deletions sway-ast/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,16 @@ pub struct PathType {
pub suffix: Vec<(DoubleColonToken, PathTypeSegment)>,
}

impl PathType {
pub fn last_segment(&self) -> &PathTypeSegment {
self.suffix
.iter()
.map(|s| &s.1)
.last()
.unwrap_or(&self.prefix)
}
}

impl Spanned for PathType {
fn span(&self) -> Span {
let start = match &self.root_opt {
Expand Down
10 changes: 10 additions & 0 deletions sway-ast/src/ty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ impl Spanned for Ty {
}
}

impl Ty {
pub fn name_span(&self) -> Option<Span> {
if let Ty::Path(path_type) = self {
Some(path_type.last_segment().name.span())
} else {
None
}
}
}

#[derive(Clone, Debug)]
pub enum TyTupleDescriptor {
Nil,
Expand Down
8 changes: 7 additions & 1 deletion sway-core/src/language/ty/expression/expression_variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub enum TyExpressionVariant {
struct_name: Ident,
fields: Vec<TyStructExpressionField>,
span: Span,
type_binding: TypeBinding<()>,
},
CodeBlock(TyCodeBlock),
// a flag that this value will later be provided as a parameter, but is currently unknown
Expand Down Expand Up @@ -217,14 +218,19 @@ impl PartialEqWithEngines for TyExpressionVariant {
struct_name: l_struct_name,
fields: l_fields,
span: l_span,
type_binding: l_type_binding,
},
Self::StructExpression {
struct_name: r_struct_name,
fields: r_fields,
span: r_span,
type_binding: r_type_binding,
},
) => {
l_struct_name == r_struct_name && l_fields.eq(r_fields, engines) && l_span == r_span
l_struct_name == r_struct_name
&& l_fields.eq(r_fields, engines)
&& l_span == r_span
&& l_type_binding.eq(r_type_binding, engines)
}
(Self::CodeBlock(l0), Self::CodeBlock(r0)) => l0.eq(r0, engines),
(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ fn type_check_size_of_type(
type_id,
initial_type_id,
span: targ.span,
name_spans: targ.name_spans,
}],
span,
};
Expand Down Expand Up @@ -222,6 +223,7 @@ fn type_check_is_reference_type(
type_id,
initial_type_id,
span: targ.span,
name_spans: targ.name_spans,
}],
span,
};
Expand Down Expand Up @@ -479,6 +481,7 @@ fn type_check_gtf(
type_id,
initial_type_id,
span: targ.span,
name_spans: targ.name_spans,
}],
span,
},
Expand Down Expand Up @@ -711,6 +714,7 @@ fn type_check_state_store_word(
type_id,
initial_type_id,
span: span.clone(),
name_spans: targ.name_spans.clone(),
}
});
let intrinsic_function = ty::TyIntrinsicFunctionKind {
Expand Down Expand Up @@ -835,6 +839,7 @@ fn type_check_state_quad(
type_id,
initial_type_id,
span: span.clone(),
name_spans: targ.name_spans.clone(),
}
});
let intrinsic_function = ty::TyIntrinsicFunctionKind {
Expand Down Expand Up @@ -1180,6 +1185,7 @@ fn type_check_ptr_ops(
type_id,
initial_type_id,
span: targ.span,
name_spans: targ.name_spans,
}],
span,
},
Expand Down Expand Up @@ -1258,6 +1264,7 @@ fn type_check_smo(
type_id,
initial_type_id,
span: span.clone(),
name_spans: targ.name_spans.clone(),
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ fn type_check_tuple(
type_id: x.type_id,
initial_type_id: x.type_id,
span: span.clone(),
name_spans: None,
})
.collect(),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,7 @@ impl ty::TyExpression {
type_id: initial_type_id,
initial_type_id,
span: Span::dummy(),
name_spans: None,
}
});
let field_span = field.span();
Expand All @@ -864,6 +865,7 @@ impl ty::TyExpression {
type_id: typed_field.return_type,
initial_type_id: field_type.type_id,
span: typed_field.span.clone(),
name_spans: None,
});
typed_fields.push(typed_field);
}
Expand Down Expand Up @@ -1071,7 +1073,7 @@ impl ty::TyExpression {

fn type_check_delineated_path(
mut ctx: TypeCheckContext,
call_path_binding: TypeBinding<CallPath>,
unknown_call_path_binding: TypeBinding<CallPath>,
span: Span,
args: Option<Vec<Expression>>,
) -> CompileResult<ty::TyExpression> {
Expand All @@ -1088,7 +1090,7 @@ impl ty::TyExpression {
let mut module_probe_warnings = Vec::new();
let mut module_probe_errors = Vec::new();
let is_module = {
let call_path_binding = call_path_binding.clone();
let call_path_binding = unknown_call_path_binding.clone();
ctx.namespace
.check_submodule(
&[
Expand All @@ -1105,17 +1107,18 @@ impl ty::TyExpression {
let mut function_probe_warnings = Vec::new();
let mut function_probe_errors = Vec::new();
let maybe_function = {
let mut call_path_binding = call_path_binding.clone();
let mut call_path_binding = unknown_call_path_binding.clone();
TypeBinding::type_check_with_ident(&mut call_path_binding, ctx.by_ref())
.flat_map(|unknown_decl| unknown_decl.expect_function(decl_engine, &span))
.ok(&mut function_probe_warnings, &mut function_probe_errors)
.map(|func_decl| (func_decl, call_path_binding))
};

// Check if this could be an enum
let mut enum_probe_warnings = vec![];
let mut enum_probe_errors = vec![];
let maybe_enum = {
let call_path_binding = call_path_binding.clone();
let call_path_binding = unknown_call_path_binding.clone();
let enum_name = call_path_binding.inner.prefixes[0].clone();
let variant_name = call_path_binding.inner.suffix.clone();
let enum_call_path = call_path_binding.inner.rshift();
Expand All @@ -1129,14 +1132,14 @@ impl ty::TyExpression {
unknown_decl.expect_enum(decl_engine, &call_path_binding.span())
})
.ok(&mut enum_probe_warnings, &mut enum_probe_errors)
.map(|enum_decl| (enum_decl, enum_name, variant_name))
.map(|enum_decl| (enum_decl, enum_name, variant_name, call_path_binding))
};

// Check if this could be a constant
let mut const_probe_warnings = vec![];
let mut const_probe_errors = vec![];
let maybe_const = {
let mut call_path_binding = call_path_binding.clone();
let mut call_path_binding = unknown_call_path_binding.clone();
TypeBinding::type_check_with_ident(&mut call_path_binding, ctx.by_ref())
.flat_map(|unknown_decl| {
unknown_decl.expect_const(decl_engine, &call_path_binding.span())
Expand All @@ -1147,7 +1150,7 @@ impl ty::TyExpression {

// compare the results of the checks
let exp = match (is_module, maybe_function, maybe_enum, maybe_const) {
(false, None, Some((enum_decl, enum_name, variant_name)), None) => {
(false, None, Some((enum_decl, enum_name, variant_name, call_path_binding)), None) => {
warnings.append(&mut enum_probe_warnings);
errors.append(&mut enum_probe_errors);
check!(
Expand All @@ -1165,7 +1168,7 @@ impl ty::TyExpression {
errors
)
}
(false, Some(func_decl), None, None) => {
(false, Some((func_decl, call_path_binding)), None, None) => {
warnings.append(&mut function_probe_warnings);
errors.append(&mut function_probe_errors);
check!(
Expand Down Expand Up @@ -1194,8 +1197,8 @@ impl ty::TyExpression {
}
(false, None, None, None) => {
errors.push(CompileError::SymbolNotFound {
name: call_path_binding.inner.suffix.clone(),
span: call_path_binding.inner.suffix.span(),
name: unknown_call_path_binding.inner.suffix.clone(),
span: unknown_call_path_binding.inner.suffix.span(),
});
return err(warnings, errors);
}
Expand Down Expand Up @@ -1394,6 +1397,7 @@ impl ty::TyExpression {
TypeArgument {
type_id: unknown_type,
span: Span::dummy(),
name_spans: None,
initial_type_id: unknown_type,
},
Length::new(0, Span::dummy()),
Expand Down Expand Up @@ -1454,6 +1458,7 @@ impl ty::TyExpression {
TypeArgument {
type_id: elem_type,
span: Span::dummy(),
name_spans: None,
initial_type_id: elem_type,
},
Length::new(array_count, Span::dummy()),
Expand Down Expand Up @@ -1879,6 +1884,7 @@ mod tests {
TypeArgument {
type_id: type_engine.insert(&decl_engine, TypeInfo::Boolean),
span: Span::dummy(),
name_spans: None,
initial_type_id: type_engine.insert(&decl_engine, TypeInfo::Boolean),
},
Length::new(2, Span::dummy()),
Expand Down Expand Up @@ -2012,6 +2018,7 @@ mod tests {
TypeArgument {
type_id: type_engine.insert(&decl_engine, TypeInfo::Boolean),
span: Span::dummy(),
name_spans: None,
initial_type_id: type_engine.insert(&decl_engine, TypeInfo::Boolean),
},
Length::new(0, Span::dummy()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub(crate) fn instantiate_enum(
enum_name: Ident,
enum_variant_name: Ident,
args_opt: Option<Vec<Expression>>,
mut type_binding: TypeBinding<()>,
type_binding: TypeBinding<()>,
span: &Span,
) -> CompileResult<ty::TyExpression> {
let mut warnings = vec![];
Expand Down Expand Up @@ -47,17 +47,6 @@ pub(crate) fn instantiate_enum(
}
let args = args_opt.unwrap_or_default();

// Update type binding with the correct type information from the enum decl
for (type_arg, type_param) in type_binding
.type_arguments
.iter_mut()
.zip(enum_decl.type_parameters.iter())
{
type_arg.type_id = type_param.type_id;
type_arg.initial_type_id = type_param.initial_type_id;
// keep the type_arg span so the LSP knows where we are
}

// If there is an instantiator, it must match up with the type. If there is not an
// instantiator, then the type of the enum is necessarily the unit type.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{

pub(crate) fn struct_instantiation(
mut ctx: TypeCheckContext,
call_path_binding: TypeBinding<CallPath>,
mut call_path_binding: TypeBinding<CallPath>,
fields: Vec<StructExpressionField>,
span: Span,
) -> CompileResult<ty::TyExpression> {
Expand All @@ -21,13 +21,21 @@ pub(crate) fn struct_instantiation(
let decl_engine = ctx.decl_engine;
let engines = ctx.engines();

// We need the call_path_binding to have types that point to proper definitions so the LSP can
// look for them, but its types haven't been resolved yet.
// To that end we do a dummy type check which has the side effect of resolving the types.
let _ = TypeBinding::type_check_with_ident(&mut call_path_binding, ctx.by_ref());
// strip the CallPath as we're only really interested in the type arguments for the LSP
let type_binding = call_path_binding.clone().strip_inner();

let TypeBinding {
inner: CallPath {
prefixes, suffix, ..
},
type_arguments,
span: inner_span,
} = call_path_binding;

let type_info = match (suffix.as_str(), type_arguments.is_empty()) {
("Self", true) => TypeInfo::SelfType,
("Self", false) => {
Expand Down Expand Up @@ -114,6 +122,7 @@ pub(crate) fn struct_instantiation(
struct_name: struct_name.clone(),
fields: typed_fields,
span: inner_span,
type_binding,
},
return_type: type_id,
span,
Expand Down
Loading

0 comments on commit d558727

Please sign in to comment.