mirror of
https://github.com/FuelLabs/sway.git
synced 2025-07-07 21:25:21 +00:00
Add migration for renaming existing panic
identifiers to r#panic
(#7231)
Some checks are pending
CI / verifications-complete (push) Blocked by required conditions
CI / check-dependency-version-formats (push) Waiting to run
CI / get-fuel-core-version (push) Waiting to run
CI / build-reference-examples (push) Waiting to run
CI / forc-fmt-check-sway-lib-std (push) Waiting to run
CI / forc-fmt-check-panic (push) Waiting to run
CI / build-forc-test-project (push) Waiting to run
CI / cargo-build-workspace (push) Waiting to run
CI / cargo-clippy (push) Waiting to run
CI / cargo-toml-fmt-check (push) Waiting to run
CI / cargo-test-forc-node (push) Blocked by required conditions
CI / cargo-test-sway-lsp (push) Waiting to run
CI / cargo-test-forc (push) Waiting to run
CI / cargo-test-workspace (push) Waiting to run
CI / cargo-unused-deps-check (push) Waiting to run
Codspeed Benchmarks / benchmarks (push) Waiting to run
CI / check-forc-manifest-version (push) Waiting to run
CI / build-sway-lib-std (push) Waiting to run
CI / build-sway-examples (push) Waiting to run
CI / forc-fmt-check-sway-examples (push) Waiting to run
CI / check-sdk-harness-test-suite-compatibility (push) Waiting to run
CI / build-mdbook (push) Waiting to run
CI / build-forc-doc-sway-lib-std (push) Waiting to run
CI / cargo-fmt-check (push) Waiting to run
CI / cargo-run-e2e-test (push) Blocked by required conditions
CI / cargo-run-e2e-test-release (push) Blocked by required conditions
CI / cargo-run-e2e-test-evm (push) Waiting to run
CI / cargo-test-lib-std (push) Waiting to run
CI / forc-run-benchmarks (push) Waiting to run
CI / forc-unit-tests (push) Waiting to run
CI / forc-pkg-fuels-deps-check (push) Waiting to run
CI / cargo-test-forc-debug (push) Blocked by required conditions
CI / cargo-test-forc-client (push) Blocked by required conditions
CI / notify-slack-on-failure (push) Blocked by required conditions
CI / pre-publish-check (push) Waiting to run
CI / publish (push) Blocked by required conditions
CI / publish-sway-lib-std (push) Blocked by required conditions
CI / Build and upload forc binaries to release (push) Blocked by required conditions
github pages / deploy (push) Waiting to run
Some checks are pending
CI / verifications-complete (push) Blocked by required conditions
CI / check-dependency-version-formats (push) Waiting to run
CI / get-fuel-core-version (push) Waiting to run
CI / build-reference-examples (push) Waiting to run
CI / forc-fmt-check-sway-lib-std (push) Waiting to run
CI / forc-fmt-check-panic (push) Waiting to run
CI / build-forc-test-project (push) Waiting to run
CI / cargo-build-workspace (push) Waiting to run
CI / cargo-clippy (push) Waiting to run
CI / cargo-toml-fmt-check (push) Waiting to run
CI / cargo-test-forc-node (push) Blocked by required conditions
CI / cargo-test-sway-lsp (push) Waiting to run
CI / cargo-test-forc (push) Waiting to run
CI / cargo-test-workspace (push) Waiting to run
CI / cargo-unused-deps-check (push) Waiting to run
Codspeed Benchmarks / benchmarks (push) Waiting to run
CI / check-forc-manifest-version (push) Waiting to run
CI / build-sway-lib-std (push) Waiting to run
CI / build-sway-examples (push) Waiting to run
CI / forc-fmt-check-sway-examples (push) Waiting to run
CI / check-sdk-harness-test-suite-compatibility (push) Waiting to run
CI / build-mdbook (push) Waiting to run
CI / build-forc-doc-sway-lib-std (push) Waiting to run
CI / cargo-fmt-check (push) Waiting to run
CI / cargo-run-e2e-test (push) Blocked by required conditions
CI / cargo-run-e2e-test-release (push) Blocked by required conditions
CI / cargo-run-e2e-test-evm (push) Waiting to run
CI / cargo-test-lib-std (push) Waiting to run
CI / forc-run-benchmarks (push) Waiting to run
CI / forc-unit-tests (push) Waiting to run
CI / forc-pkg-fuels-deps-check (push) Waiting to run
CI / cargo-test-forc-debug (push) Blocked by required conditions
CI / cargo-test-forc-client (push) Blocked by required conditions
CI / notify-slack-on-failure (push) Blocked by required conditions
CI / pre-publish-check (push) Waiting to run
CI / publish (push) Blocked by required conditions
CI / publish-sway-lib-std (push) Blocked by required conditions
CI / Build and upload forc binaries to release (push) Blocked by required conditions
github pages / deploy (push) Waiting to run
## Description This PR: - implements the [Rename existing "panic" identifiers](https://github.com/FuelLabs/sway/issues/6765) migration step. - further extends the migration infrastructure by covering visiting additional expressions and declarations. As with all previous migrations, a pragmatic approach is taken between the invested effort and the gained coverage. The goal is to support migrating the use cases most probable in real-life code. Non-supported cases are documented in code comments. The PR is the last step in fully implementing #6765. ## Checklist - [x] I have linked to any relevant issues. - [x] I have commented my code, particularly in hard-to-understand areas. - [ ] I have updated the documentation where relevant (API docs, the reference, and the Sway book). - [ ] If my change requires substantial documentation changes, I have [requested support from the DevRel team](https://github.com/FuelLabs/devrel-requests/issues/new/choose) - [ ] I have added tests that prove my fix is effective or that my feature works. - [ ] I have added (or requested a maintainer to add) the necessary `Breaking*` or `New Feature` labels where relevant. - [x] I have done my best to ensure that my PR adheres to [the Fuel Labs Code Review Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md). - [x] I have requested a review from the relevant team or maintainers.
This commit is contained in:
parent
52bd44d510
commit
cd89f62ece
7 changed files with 796 additions and 110 deletions
|
@ -96,14 +96,11 @@ pub(crate) fn compile_package<'a>(
|
|||
let manifest_dir = build_instructions.manifest_dir()?;
|
||||
let manifest = ManifestFile::from_dir(manifest_dir.clone())?;
|
||||
let pkg_manifest = get_pkg_manifest_file(&manifest)?;
|
||||
let pkg_name = pkg_manifest.project_name();
|
||||
|
||||
println_action_green(
|
||||
"Compiling",
|
||||
&format!(
|
||||
"{} ({})",
|
||||
pkg_manifest.project_name(),
|
||||
manifest.dir().to_string_lossy()
|
||||
),
|
||||
&format!("{} ({})", pkg_name, manifest.dir().to_string_lossy()),
|
||||
);
|
||||
|
||||
let member_manifests = manifest.member_manifests()?;
|
||||
|
@ -149,6 +146,7 @@ pub(crate) fn compile_package<'a>(
|
|||
};
|
||||
|
||||
return Ok(ProgramInfo {
|
||||
pkg_name: pkg_name.to_string(),
|
||||
lexed_program: programs.lexed,
|
||||
ty_program,
|
||||
engines,
|
||||
|
|
389
forc-plugins/forc-migrate/src/migrations/error_type.rs
Normal file
389
forc-plugins/forc-migrate/src/migrations/error_type.rs
Normal file
|
@ -0,0 +1,389 @@
|
|||
#![allow(deprecated)]
|
||||
|
||||
use std::{sync::Arc, vec};
|
||||
|
||||
use crate::{internal_error, migrations::MutProgramInfo, modifying::*, visiting::*};
|
||||
use anyhow::{bail, Ok, Result};
|
||||
use sway_ast::{
|
||||
assignable::ElementAccess, expr, Assignable, Expr, ItemFn, ItemStruct, StatementLet,
|
||||
};
|
||||
use sway_core::language::{
|
||||
ty::{
|
||||
TyExpression, TyExpressionVariant, TyFunctionDecl, TyReassignmentTarget, TyStructDecl,
|
||||
TyVariableDecl,
|
||||
},
|
||||
CallPathType,
|
||||
};
|
||||
use sway_types::{Ident, Span, Spanned};
|
||||
|
||||
use super::{ContinueMigrationProcess, DryRun, MigrationStep, MigrationStepKind};
|
||||
|
||||
// NOTE: We assume idiomatic usage of the identifier `panic`. This means we support
|
||||
// its migration only if it is used as a function name, struct field, or variable name.
|
||||
// E.g., renaming `panic` in `struct panic { ... }` is not supported,
|
||||
// as it is not an idiomatic usage.
|
||||
|
||||
// NOTE: We don't have infrastructure in place for searching for usages of a symbol.
|
||||
// Ideally, if we had it, we would use such infrastructure to rename symbol usages
|
||||
// when its definition get renamed.
|
||||
// Luckily, for this particular migration, it is sufficient to visit specific expression,
|
||||
// like, e.g., function calls, and rename them.
|
||||
|
||||
// NOTE: We don't support renaming modules named `panic`. The reason is that we have the `str`
|
||||
// module in the standard library, signaling that using keywords as module names is acceptable.
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(super) const RENAME_EXISTING_PANIC_IDENTIFIERS_TO_R_PANIC_STEP: MigrationStep = MigrationStep {
|
||||
title: "Rename existing `panic` identifiers to `r#panic`",
|
||||
duration: 0,
|
||||
kind: MigrationStepKind::CodeModification(
|
||||
rename_existing_panic_identifiers_to_r_panic_step,
|
||||
&[],
|
||||
ContinueMigrationProcess::IfNoManualMigrationActionsNeeded,
|
||||
),
|
||||
help: &[
|
||||
"Migration will rename existing `panic` identifiers in struct fields,",
|
||||
"function names and arguments, and variable names to `r#panic`.",
|
||||
" ",
|
||||
"E.g., `let panic = 42;` will become `let r#panic = 42;`.",
|
||||
],
|
||||
};
|
||||
|
||||
fn rename_existing_panic_identifiers_to_r_panic_step(
|
||||
program_info: &mut MutProgramInfo,
|
||||
dry_run: DryRun,
|
||||
) -> Result<Vec<Span>> {
|
||||
struct Visitor;
|
||||
impl TreesVisitorMut<Span> for Visitor {
|
||||
fn visit_fn_decl(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_fn: &mut ItemFn,
|
||||
_ty_fn: Option<Arc<TyFunctionDecl>>,
|
||||
output: &mut Vec<Span>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
// First, let's check the arguments.
|
||||
for lexed_arg in lexed_fn.fn_signature.arguments.inner.args_mut() {
|
||||
let arg_name = match &mut lexed_arg.pattern {
|
||||
sway_ast::Pattern::Var { name, .. } => name,
|
||||
// A valid identifier in a function argument pattern can only be a variable,
|
||||
// never an enum variant. So we know that this `ident` is a variable.
|
||||
sway_ast::Pattern::AmbiguousSingleIdent(ident) => ident,
|
||||
_ => continue,
|
||||
};
|
||||
|
||||
if arg_name.as_raw_ident_str() != "panic" {
|
||||
continue;
|
||||
}
|
||||
|
||||
output.push(arg_name.span());
|
||||
|
||||
if ctx.dry_run == DryRun::Yes {
|
||||
continue;
|
||||
}
|
||||
|
||||
*arg_name = Ident::new_with_raw(arg_name.span(), true);
|
||||
}
|
||||
|
||||
// Then, the function name.
|
||||
if lexed_fn.fn_signature.name.as_raw_ident_str() != "panic" {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
output.push(lexed_fn.fn_signature.name.span());
|
||||
|
||||
if ctx.dry_run == DryRun::Yes {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
modify(lexed_fn).set_name("r#panic");
|
||||
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
|
||||
fn visit_struct_decl(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_struct: &mut ItemStruct,
|
||||
_ty_struct: Option<Arc<TyStructDecl>>,
|
||||
output: &mut Vec<Span>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
for lexed_field in lexed_struct.fields.inner.iter_mut() {
|
||||
let field_name = &mut lexed_field.value.name;
|
||||
|
||||
if field_name.as_raw_ident_str() != "panic" {
|
||||
continue;
|
||||
}
|
||||
|
||||
output.push(field_name.span());
|
||||
|
||||
if ctx.dry_run == DryRun::Yes {
|
||||
continue;
|
||||
}
|
||||
|
||||
*field_name = Ident::new_with_raw(field_name.span(), true);
|
||||
}
|
||||
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
|
||||
fn visit_fn_call(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_fn_call: &mut Expr,
|
||||
ty_fn_call: Option<&TyExpression>,
|
||||
output: &mut Vec<Span>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
// We report the occurrences only if it is not a dry-run.
|
||||
if ctx.dry_run == DryRun::Yes {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
let Some(ty_fn_call) = ty_fn_call else {
|
||||
// Without the typed function call, we cannot proceed
|
||||
// because we cannot check if the function is actually defined in the current package.
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
};
|
||||
|
||||
let Expr::FuncApp { func, args: _ } = lexed_fn_call else {
|
||||
bail!(internal_error("`lexed_fn_call` is not an `Expr::FuncApp`."));
|
||||
};
|
||||
|
||||
let Expr::Path(path) = &mut **func else {
|
||||
// We are interested only in function calls that are paths.
|
||||
// Only such calls can be renamed.
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
};
|
||||
|
||||
let last_segment = path.last_segment_mut();
|
||||
|
||||
if last_segment.name.as_raw_ident_str() != "panic" {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
// Check if the function is actually defined in the current package.
|
||||
let TyExpressionVariant::FunctionApplication { fn_ref, .. } = &ty_fn_call.expression
|
||||
else {
|
||||
bail!(internal_error(
|
||||
"`ty_fn_call` is not a `TyExpressionVariant::FunctionApplication`."
|
||||
));
|
||||
};
|
||||
|
||||
let ty_fn = ctx.engines.de().get_function(fn_ref.id());
|
||||
// We need the full path to the function to ensure it is defined in the current package.
|
||||
if ty_fn.call_path.callpath_type != CallPathType::Full {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
let Some(fn_pkg_name) = ty_fn.call_path.prefixes.first() else {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
};
|
||||
|
||||
if fn_pkg_name.as_str() != ctx.pkg_name {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
output.push(last_segment.span());
|
||||
|
||||
modify(last_segment).set_name("r#panic");
|
||||
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
|
||||
fn visit_method_call(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_method_call: &mut Expr,
|
||||
ty_method_call: Option<&TyExpression>,
|
||||
output: &mut Vec<Span>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
// We report the occurrences only if it is not a dry-run.
|
||||
if ctx.dry_run == DryRun::Yes {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
let Some(ty_method_call) = ty_method_call else {
|
||||
// Without the typed method call, we cannot proceed
|
||||
// because we cannot check if the method is actually defined in the current package.
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
};
|
||||
|
||||
let Expr::MethodCall {
|
||||
path_seg, args: _, ..
|
||||
} = lexed_method_call
|
||||
else {
|
||||
bail!(internal_error(
|
||||
"`lexed_method_call` is not an `Expr::MethodCall`."
|
||||
));
|
||||
};
|
||||
|
||||
if path_seg.name.as_raw_ident_str() != "panic" {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
// Check if the method is actually defined in the current package.
|
||||
let TyExpressionVariant::FunctionApplication { fn_ref, .. } =
|
||||
&ty_method_call.expression
|
||||
else {
|
||||
bail!(internal_error(
|
||||
"`ty_method_call` is not a `TyExpressionVariant::FunctionApplication`."
|
||||
));
|
||||
};
|
||||
|
||||
let ty_method = ctx.engines.de().get_function(fn_ref.id());
|
||||
// We need the full path to the function to ensure it is defined in the current package.
|
||||
if ty_method.call_path.callpath_type != CallPathType::Full {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
let Some(fn_pkg_name) = ty_method.call_path.prefixes.first() else {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
};
|
||||
|
||||
if fn_pkg_name.as_str() != ctx.pkg_name {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
output.push(path_seg.span());
|
||||
|
||||
modify(path_seg).set_name("r#panic");
|
||||
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
|
||||
fn visit_statement_let(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_let: &mut StatementLet,
|
||||
_ty_var_decl: Option<&TyVariableDecl>,
|
||||
output: &mut Vec<Span>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
let var_name = match &mut lexed_let.pattern {
|
||||
sway_ast::Pattern::Var { name, .. } => name,
|
||||
// A valid identifier in a variable name pattern can only be a variable,
|
||||
// never an enum variant. So we know that this `ident` is a variable.
|
||||
sway_ast::Pattern::AmbiguousSingleIdent(ident) => ident,
|
||||
_ => {
|
||||
// NOTE: We don't support renaming `panic` in patterns other than variables,
|
||||
// e.g., in deconstruction patterns.
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
};
|
||||
|
||||
if var_name.as_raw_ident_str() != "panic" {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
output.push(var_name.span());
|
||||
|
||||
if ctx.dry_run == DryRun::Yes {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
*var_name = Ident::new_with_raw(var_name.span(), true);
|
||||
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
|
||||
fn visit_expr(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_expr: &mut Expr,
|
||||
_ty_expr: Option<&TyExpression>,
|
||||
output: &mut Vec<Span>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
// We report the occurrences only if it is not a dry-run.
|
||||
if ctx.dry_run == DryRun::Yes {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
let var_names = match lexed_expr {
|
||||
Expr::Path(path) if path.suffix.is_empty() => vec![&mut path.prefix.name],
|
||||
Expr::Struct { fields, .. } => fields
|
||||
.inner
|
||||
.iter_mut()
|
||||
.map(|field| &mut field.field_name)
|
||||
.collect(),
|
||||
Expr::FieldProjection { name, .. } => vec![name],
|
||||
_ => vec![],
|
||||
};
|
||||
|
||||
for var_name in var_names
|
||||
.into_iter()
|
||||
.filter(|n| n.as_raw_ident_str() == "panic")
|
||||
{
|
||||
output.push(var_name.span());
|
||||
|
||||
*var_name = Ident::new_with_raw(var_name.span(), true);
|
||||
}
|
||||
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
|
||||
fn visit_reassignment(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
_lexed_op: &mut expr::ReassignmentOp,
|
||||
lexed_lhs: &mut Assignable,
|
||||
_ty_lhs: Option<&TyReassignmentTarget>,
|
||||
_lexed_rhs: &mut Expr,
|
||||
_ty_rhs: Option<&TyExpression>,
|
||||
output: &mut Vec<Span>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
// On the LHS, we support renaming `panic` only in these cases:
|
||||
// - Variable names, e.g., `let panic = 42;`
|
||||
// - Single field access, e.g., `let x.panic = 42;`
|
||||
// But occurrences in, e.g., `foo[panic].x = 42;` will not be renamed.
|
||||
// Full traversal of reassignments' LHS will be done as a part of migration
|
||||
// infrastructure in the future.
|
||||
|
||||
// We report the occurrences only if it is not a dry-run.
|
||||
if ctx.dry_run == DryRun::Yes {
|
||||
return Ok(InvalidateTypedElement::No);
|
||||
}
|
||||
|
||||
let var_names = match lexed_lhs {
|
||||
Assignable::ElementAccess(element_access) => match element_access {
|
||||
ElementAccess::Var(name) => vec![name],
|
||||
ElementAccess::FieldProjection {
|
||||
target: element_access,
|
||||
name,
|
||||
..
|
||||
} => {
|
||||
let mut names = vec![name];
|
||||
if let ElementAccess::Var(name) = &mut **element_access {
|
||||
names.push(name)
|
||||
};
|
||||
names
|
||||
}
|
||||
ElementAccess::TupleFieldProjection {
|
||||
target: element_access,
|
||||
..
|
||||
}
|
||||
| ElementAccess::Index {
|
||||
target: element_access,
|
||||
..
|
||||
} => match &mut **element_access {
|
||||
ElementAccess::Var(name) => vec![name],
|
||||
_ => vec![],
|
||||
},
|
||||
_ => vec![],
|
||||
},
|
||||
Assignable::Deref { .. } => vec![],
|
||||
};
|
||||
|
||||
for var_name in var_names
|
||||
.into_iter()
|
||||
.filter(|n| n.as_raw_ident_str() == "panic")
|
||||
{
|
||||
output.push(var_name.span());
|
||||
|
||||
*var_name = Ident::new_with_raw(var_name.span(), true);
|
||||
}
|
||||
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
}
|
||||
|
||||
ProgramVisitorMut::visit_program(program_info, dry_run, &mut Visitor {})
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
//! the migration tool.
|
||||
|
||||
mod demo;
|
||||
mod error_type;
|
||||
mod merge_core_std;
|
||||
mod partial_eq;
|
||||
mod references;
|
||||
|
@ -34,6 +35,8 @@ use sway_types::Span;
|
|||
use crate::internal_error;
|
||||
|
||||
pub(crate) struct ProgramInfo<'a> {
|
||||
/// The name of the current package being migrated.
|
||||
pub pkg_name: String,
|
||||
pub lexed_program: Arc<LexedProgram>,
|
||||
pub ty_program: Arc<TyProgram>,
|
||||
pub engines: &'a Engines,
|
||||
|
@ -44,6 +47,8 @@ pub(crate) struct ProgramInfo<'a> {
|
|||
/// [TyProgram] and the [Engines]. It is used in migrations
|
||||
/// that modify the source code by altering the lexed program.
|
||||
pub(crate) struct MutProgramInfo<'a> {
|
||||
/// The name of the current package being migrated.
|
||||
pub pkg_name: &'a str,
|
||||
pub lexed_program: &'a mut LexedProgram,
|
||||
pub ty_program: &'a TyProgram,
|
||||
pub engines: &'a Engines,
|
||||
|
@ -52,7 +57,11 @@ pub(crate) struct MutProgramInfo<'a> {
|
|||
impl ProgramInfo<'_> {
|
||||
pub(crate) fn as_mut(&mut self) -> MutProgramInfo {
|
||||
MutProgramInfo {
|
||||
lexed_program: Arc::get_mut(&mut self.lexed_program).unwrap(),
|
||||
pkg_name: &self.pkg_name,
|
||||
// Because the `ProgramsCacheEntry` clones the `programs`, the compilation will always
|
||||
// result in two strong `Arc` references to the `lexed_program`.
|
||||
// Therefore, we must use `Arc::make_mut` to get the copy-on-write behavior.
|
||||
lexed_program: Arc::make_mut(&mut self.lexed_program),
|
||||
ty_program: &self.ty_program,
|
||||
engines: self.engines,
|
||||
}
|
||||
|
@ -485,4 +494,7 @@ fn assert_migration_steps_consistency(migration_steps: MigrationSteps) {
|
|||
|
||||
/// The list of the migration steps, grouped by the Sway feature that causes
|
||||
/// the breaking changes behind the migration steps.
|
||||
const MIGRATION_STEPS: MigrationSteps = &[];
|
||||
const MIGRATION_STEPS: MigrationSteps = &[(
|
||||
Feature::ErrorType,
|
||||
&[error_type::RENAME_EXISTING_PANIC_IDENTIFIERS_TO_R_PANIC_STEP],
|
||||
)];
|
||||
|
|
|
@ -7,9 +7,10 @@ use std::sync::Arc;
|
|||
|
||||
use duplicate::duplicate_item;
|
||||
use sway_ast::{
|
||||
expr::{LoopControlFlow, ReassignmentOpVariant},
|
||||
CodeBlockContents, Expr, IfCondition, IfExpr, ItemFn, ItemImpl, ItemImplItem, ItemKind,
|
||||
ItemUse, PathExprSegment, Statement, StatementLet,
|
||||
expr::{LoopControlFlow, ReassignmentOp, ReassignmentOpVariant},
|
||||
keywords::*,
|
||||
Assignable, CodeBlockContents, Expr, IfCondition, IfExpr, ItemFn, ItemImpl, ItemImplItem,
|
||||
ItemKind, ItemStruct, ItemUse, PathExprSegment, Statement, StatementLet,
|
||||
};
|
||||
use sway_core::{
|
||||
decl_engine::DeclEngine,
|
||||
|
@ -17,8 +18,9 @@ use sway_core::{
|
|||
lexed::LexedModule,
|
||||
ty::{
|
||||
self, TyAstNodeContent, TyCodeBlock, TyDecl, TyExpression, TyExpressionVariant,
|
||||
TyFunctionDecl, TyImplSelfOrTrait, TyIntrinsicFunctionKind, TyModule, TySideEffect,
|
||||
TySideEffectVariant, TyTraitItem, TyUseStatement, TyVariableDecl,
|
||||
TyFunctionDecl, TyImplSelfOrTrait, TyIntrinsicFunctionKind, TyModule,
|
||||
TyReassignmentTarget, TySideEffect, TySideEffectVariant, TyStructDecl, TyTraitItem,
|
||||
TyUseStatement, TyVariableDecl,
|
||||
},
|
||||
CallPath,
|
||||
},
|
||||
|
@ -32,6 +34,8 @@ use crate::{
|
|||
};
|
||||
|
||||
pub(crate) struct VisitingContext<'a> {
|
||||
/// The name of the current package being migrated.
|
||||
pub pkg_name: &'a str,
|
||||
pub engines: &'a Engines,
|
||||
pub dry_run: DryRun,
|
||||
}
|
||||
|
@ -97,7 +101,16 @@ pub(crate) trait __TreesVisitor<O> {
|
|||
) -> Result<InvalidateTypedElement> {
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
fn visit_fn(
|
||||
fn visit_struct_decl(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_struct: __ref_type([ItemStruct]),
|
||||
ty_struct: Option<Arc<TyStructDecl>>,
|
||||
output: &mut Vec<O>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
fn visit_fn_decl(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_fn: __ref_type([ItemFn]),
|
||||
|
@ -169,6 +182,32 @@ pub(crate) trait __TreesVisitor<O> {
|
|||
) -> Result<InvalidateTypedElement> {
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn visit_reassignment(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
lexed_op: __ref_type([ReassignmentOp]),
|
||||
lexed_lhs: __ref_type([Assignable]),
|
||||
ty_lhs: Option<&TyReassignmentTarget>,
|
||||
lexed_rhs: __ref_type([Expr]),
|
||||
ty_rhs: Option<&TyExpression>,
|
||||
output: &mut Vec<O>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn visit_binary_op(
|
||||
&mut self,
|
||||
ctx: &VisitingContext,
|
||||
op: &'static str,
|
||||
lexed_lhs: __ref_type([Expr]),
|
||||
ty_lhs: Option<&TyExpression>,
|
||||
lexed_rhs: __ref_type([Expr]),
|
||||
ty_rhs: Option<&TyExpression>,
|
||||
output: &mut Vec<O>,
|
||||
) -> Result<InvalidateTypedElement> {
|
||||
Ok(InvalidateTypedElement::No)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -190,6 +229,8 @@ impl __ProgramVisitor {
|
|||
V: __TreesVisitor<O>,
|
||||
{
|
||||
let ctx = VisitingContext {
|
||||
#[allow(clippy::needless_borrow)] // Clippy lint false positive. Actually, a Clippy bug.
|
||||
pkg_name: &program_info.pkg_name,
|
||||
engines: program_info.engines,
|
||||
dry_run,
|
||||
};
|
||||
|
@ -257,15 +298,35 @@ impl __ProgramVisitor {
|
|||
.find_map(|node| match &node.content {
|
||||
TyAstNodeContent::SideEffect(TySideEffect {
|
||||
side_effect: TySideEffectVariant::UseStatement(ty_use),
|
||||
}) => Some(ty_use),
|
||||
}) if ty_use.span == item_use.span() => Some(ty_use),
|
||||
_ => None,
|
||||
})
|
||||
});
|
||||
|
||||
visitor.visit_use(ctx, item_use, ty_use, output)?;
|
||||
}
|
||||
ItemKind::Struct(_item_struct) => {
|
||||
// TODO: Implement visiting `struct`.
|
||||
ItemKind::Struct(item_struct) => {
|
||||
let ty_struct_decl = ty_module.and_then(|ty_module| {
|
||||
ty_module
|
||||
.all_nodes
|
||||
.iter()
|
||||
.find_map(|node| match &node.content {
|
||||
TyAstNodeContent::Declaration(TyDecl::StructDecl(
|
||||
ty_struct_decl,
|
||||
)) => {
|
||||
let ty_struct_decl =
|
||||
ctx.engines.de().get_struct(&ty_struct_decl.decl_id);
|
||||
if ty_struct_decl.span == item_struct.span() {
|
||||
Some(ty_struct_decl)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
});
|
||||
|
||||
visitor.visit_struct_decl(ctx, item_struct, ty_struct_decl, output)?;
|
||||
}
|
||||
ItemKind::Enum(_item_enum) => {
|
||||
// TODO: Implement visiting `enum`.
|
||||
|
@ -288,7 +349,7 @@ impl __ProgramVisitor {
|
|||
})
|
||||
});
|
||||
|
||||
Self::visit_fn(ctx, item_fn, ty_fn, visitor, output)?;
|
||||
Self::visit_fn_decl(ctx, item_fn, ty_fn, visitor, output)?;
|
||||
}
|
||||
ItemKind::Trait(_item_trait) => {
|
||||
// TODO: Implement visiting `trait`.
|
||||
|
@ -335,7 +396,7 @@ impl __ProgramVisitor {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_fn<V, O>(
|
||||
fn visit_fn_decl<V, O>(
|
||||
ctx: &VisitingContext,
|
||||
lexed_fn: __ref_type([ItemFn]),
|
||||
ty_fn: Option<Arc<TyFunctionDecl>>,
|
||||
|
@ -345,7 +406,7 @@ impl __ProgramVisitor {
|
|||
where
|
||||
V: __TreesVisitor<O>,
|
||||
{
|
||||
let ty_fn = match visitor.visit_fn(ctx, lexed_fn, ty_fn.clone(), output)? {
|
||||
let ty_fn = match visitor.visit_fn_decl(ctx, lexed_fn, ty_fn.clone(), output)? {
|
||||
InvalidateTypedElement::Yes => None,
|
||||
InvalidateTypedElement::No => ty_fn,
|
||||
};
|
||||
|
@ -393,7 +454,7 @@ impl __ProgramVisitor {
|
|||
})
|
||||
});
|
||||
|
||||
Self::visit_fn(ctx, item_fn, ty_item_fn, visitor, output)?;
|
||||
Self::visit_fn_decl(ctx, item_fn, ty_item_fn, visitor, output)?;
|
||||
}
|
||||
ItemImplItem::Const(_item_const) => {
|
||||
// TODO: Implement visiting `associated consts`.
|
||||
|
@ -517,6 +578,38 @@ impl __ProgramVisitor {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_binary_op<V, O>(
|
||||
ctx: &VisitingContext,
|
||||
op: &'static str,
|
||||
lexed_lhs: __ref_type([Expr]),
|
||||
lexed_rhs: __ref_type([Expr]),
|
||||
visitor: &mut V,
|
||||
output: &mut Vec<O>,
|
||||
) -> Result<()>
|
||||
where
|
||||
V: __TreesVisitor<O>,
|
||||
{
|
||||
// TODO: Implement getting typed LHS and RHS when visiting operands' expressions.
|
||||
// We need to properly handle the desugaring.
|
||||
// E.g., `x + func(1, 2);`
|
||||
// will be desugared into `add(x, func(1, 2));`
|
||||
// When visiting the operands in the lexed tree, in the typed tree
|
||||
// we need to skip the operator method call, like `add` in the above example,
|
||||
// and provide the typed arguments instead.
|
||||
let ty_lhs = None;
|
||||
let ty_rhs = None;
|
||||
|
||||
match visitor.visit_binary_op(ctx, op, lexed_lhs, ty_lhs, lexed_rhs, ty_rhs, output)? {
|
||||
InvalidateTypedElement::No => (ty_lhs, ty_rhs),
|
||||
InvalidateTypedElement::Yes => (None, None),
|
||||
};
|
||||
|
||||
Self::visit_expr(ctx, lexed_lhs, ty_lhs, visitor, output)?;
|
||||
Self::visit_expr(ctx, lexed_rhs, ty_rhs, visitor, output)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn visit_expr<V, O>(
|
||||
ctx: &VisitingContext,
|
||||
lexed_expr: __ref_type([Expr]),
|
||||
|
@ -550,9 +643,7 @@ impl __ProgramVisitor {
|
|||
Expr::Error(..) => {
|
||||
bail!(internal_error("`Expr::Error` cannot happen, because `forc migrate` analyzes only successfully compiled programs."));
|
||||
}
|
||||
Expr::Path(_path_expr) => {
|
||||
// TODO: Implement visiting `path_expr`.
|
||||
}
|
||||
Expr::Path(_path_expr) => {}
|
||||
Expr::Literal(_literal) => {}
|
||||
Expr::AbiCast { args, .. } => {
|
||||
let ty_abi_cast_expr = ty_expr
|
||||
|
@ -690,8 +781,13 @@ impl __ProgramVisitor {
|
|||
|
||||
Self::visit_args(ctx, lexed_expr, ty_expr, false, visitor, output)?;
|
||||
}
|
||||
Expr::Index { target: _, arg: _ } => {
|
||||
// TODO: Implement visiting `array[index]`.
|
||||
Expr::Index { target, arg } => {
|
||||
// TODO: Implement extracting typed elements for `array[index]`.
|
||||
let ty_target = None;
|
||||
let ty_arg = None;
|
||||
|
||||
Self::visit_expr(ctx, target.__as_ref(), ty_target, visitor, output)?;
|
||||
Self::visit_expr(ctx, arg.inner.__as_ref(), ty_arg, visitor, output)?;
|
||||
}
|
||||
Expr::MethodCall {
|
||||
target: _,
|
||||
|
@ -711,19 +807,25 @@ impl __ProgramVisitor {
|
|||
Self::visit_args(ctx, lexed_expr, ty_expr, true, visitor, output)?;
|
||||
}
|
||||
Expr::FieldProjection {
|
||||
target: _,
|
||||
target,
|
||||
dot_token: _,
|
||||
name: _,
|
||||
} => {
|
||||
// TODO: Implement visiting `struct.field`.
|
||||
// TODO: Implement extracting typed target for `struct.field`.
|
||||
let ty_target = None;
|
||||
|
||||
Self::visit_expr(ctx, target.__as_ref(), ty_target, visitor, output)?;
|
||||
}
|
||||
Expr::TupleFieldProjection {
|
||||
target: _,
|
||||
target,
|
||||
dot_token: _,
|
||||
field: _,
|
||||
field_span: _,
|
||||
} => {
|
||||
// TODO: Implement visiting `tuple.0`.
|
||||
// TODO: Implement extracting typed target for `tuple.N`.
|
||||
let ty_target = None;
|
||||
|
||||
Self::visit_expr(ctx, target.__as_ref(), ty_target, visitor, output)?;
|
||||
}
|
||||
Expr::Ref {
|
||||
ampersand_token: _,
|
||||
|
@ -740,174 +842,327 @@ impl __ProgramVisitor {
|
|||
}
|
||||
Expr::Not {
|
||||
bang_token: _,
|
||||
expr: _,
|
||||
expr,
|
||||
} => {
|
||||
// TODO: Implement visiting `not`.
|
||||
// TODO: Implement extracting typed expressions when visiting `not`.
|
||||
let ty_expr = None;
|
||||
Self::visit_expr(ctx, expr.__as_ref(), ty_expr, visitor, output)?;
|
||||
}
|
||||
Expr::Mul {
|
||||
lhs: _,
|
||||
lhs,
|
||||
star_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `mul`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<StarToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Div {
|
||||
lhs: _,
|
||||
lhs,
|
||||
forward_slash_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `div`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<ForwardSlashToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Pow {
|
||||
lhs: _,
|
||||
lhs,
|
||||
double_star_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `pow`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<DoubleStarToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Modulo {
|
||||
lhs: _,
|
||||
lhs,
|
||||
percent_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `modulo`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<PercentToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Add {
|
||||
lhs: _,
|
||||
lhs,
|
||||
add_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `add`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<AddToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Sub {
|
||||
lhs: _,
|
||||
lhs,
|
||||
sub_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `sub`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<SubToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Shl {
|
||||
lhs: _,
|
||||
lhs,
|
||||
shl_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `<<`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<ShlToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Shr {
|
||||
lhs: _,
|
||||
lhs,
|
||||
shr_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `>>`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<ShrToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::BitAnd {
|
||||
lhs: _,
|
||||
lhs,
|
||||
ampersand_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `bit_and`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<AmpersandToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::BitXor {
|
||||
lhs: _,
|
||||
lhs,
|
||||
caret_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `bit_xor`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<CaretToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::BitOr {
|
||||
lhs: _,
|
||||
lhs,
|
||||
pipe_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `bit_or`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<PipeToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Equal {
|
||||
lhs: _,
|
||||
lhs,
|
||||
double_eq_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `==`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<DoubleEqToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::NotEqual {
|
||||
lhs: _,
|
||||
lhs,
|
||||
bang_eq_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `!=`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<BangEqToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::LessThan {
|
||||
lhs: _,
|
||||
lhs,
|
||||
less_than_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `<`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<LessThanToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::GreaterThan {
|
||||
lhs: _,
|
||||
lhs,
|
||||
greater_than_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `>`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<GreaterThanToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::LessThanEq {
|
||||
lhs: _,
|
||||
lhs,
|
||||
less_than_eq_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `<=`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<LessThanEqToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::GreaterThanEq {
|
||||
lhs: _,
|
||||
lhs,
|
||||
greater_than_eq_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `>=`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<GreaterThanEqToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::LogicalAnd {
|
||||
lhs: _,
|
||||
lhs,
|
||||
double_ampersand_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `logical_and`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<DoubleAmpersandToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::LogicalOr {
|
||||
lhs: _,
|
||||
lhs,
|
||||
double_pipe_token: _,
|
||||
rhs: _,
|
||||
rhs,
|
||||
} => {
|
||||
// TODO: Implement visiting `logical_or`.
|
||||
Self::visit_binary_op(
|
||||
ctx,
|
||||
<DoublePipeToken as Token>::AS_STR,
|
||||
lhs.__as_ref(),
|
||||
rhs.__as_ref(),
|
||||
visitor,
|
||||
output,
|
||||
)?;
|
||||
}
|
||||
Expr::Reassignment {
|
||||
assignable: _,
|
||||
assignable,
|
||||
reassignment_op,
|
||||
expr,
|
||||
} => {
|
||||
let ty_reassignment = ty_expr
|
||||
.map(|ty_expr| match &ty_expr.expression {
|
||||
ty::TyExpressionVariant::Reassignment(ty_reassignment) => {
|
||||
Ok(ty_reassignment.as_ref())
|
||||
}
|
||||
_ => bail!(invalid_ty_expression_variant(
|
||||
"Reassignment",
|
||||
"Reassignment"
|
||||
)),
|
||||
})
|
||||
.transpose()?;
|
||||
let ty_lhs = ty_reassignment.map(|ty_reassignment| &ty_reassignment.lhs);
|
||||
let ty_rhs = ty_reassignment.map(|ty_reassignment| &ty_reassignment.rhs);
|
||||
|
||||
let (_ty_lhs, ty_rhs) = match visitor.visit_reassignment(
|
||||
ctx,
|
||||
reassignment_op,
|
||||
assignable,
|
||||
ty_lhs,
|
||||
expr.__as_ref(),
|
||||
ty_rhs,
|
||||
output,
|
||||
)? {
|
||||
InvalidateTypedElement::Yes => (None, None),
|
||||
InvalidateTypedElement::No => (ty_lhs, ty_rhs),
|
||||
};
|
||||
|
||||
match reassignment_op.variant {
|
||||
ReassignmentOpVariant::Equals => {
|
||||
let ty_reassignment_rhs = ty_expr
|
||||
.map(|ty_expr| match &ty_expr.expression {
|
||||
ty::TyExpressionVariant::Reassignment(ty_reassignment) => {
|
||||
Ok(&ty_reassignment.rhs)
|
||||
}
|
||||
_ => bail!(invalid_ty_expression_variant(
|
||||
"Reassignment",
|
||||
"Reassignment"
|
||||
)),
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
Self::visit_expr(ctx, expr, ty_reassignment_rhs, visitor, output)?;
|
||||
// TODO: Implement visiting expressions in the reassignment LHS.
|
||||
Self::visit_expr(ctx, expr, ty_rhs, visitor, output)?;
|
||||
}
|
||||
_ => {
|
||||
// TODO: Implement getting `ty_expr` when visiting `compound reassignments`.
|
||||
// We need to properly handle the desugaring.
|
||||
// E.g., `x += func(1, 2);`
|
||||
// will be desugared into `x = x.add(func(1, 2));`
|
||||
// will be desugared into `x = add(x, func(1, 2));`
|
||||
// When visiting the RHS in the lexed tree, we need to skip the
|
||||
// operator method call in the typed tree.
|
||||
// operator method call in the typed tree, and provide the
|
||||
// typed arguments instead.
|
||||
// To provide visiting without loosing the information about compound
|
||||
// reassignment, we will need to have a dedicated `visit_reassignment`
|
||||
// method.
|
||||
// TODO: Implement visiting expressions in the reassignment LHS.
|
||||
Self::visit_expr(ctx, expr, None, visitor, output)?;
|
||||
}
|
||||
}
|
||||
|
@ -929,8 +1184,8 @@ impl __ProgramVisitor {
|
|||
where
|
||||
V: __TreesVisitor<O>,
|
||||
{
|
||||
match &lexed_if.condition {
|
||||
IfCondition::Expr(_) => {
|
||||
match __ref([lexed_if.condition]) {
|
||||
IfCondition::Expr(lexed_condition) => {
|
||||
let ty_if = ty_if_expr
|
||||
.map(|ty_expr| match &ty_expr.expression {
|
||||
ty::TyExpressionVariant::IfExp {
|
||||
|
@ -945,7 +1200,7 @@ impl __ProgramVisitor {
|
|||
_ => bail!(invalid_ty_expression_variant("IfExpr", "If")),
|
||||
})
|
||||
.transpose()?;
|
||||
let _ty_if_condition = ty_if.map(|ty_if| ty_if.0);
|
||||
let ty_if_condition = ty_if.map(|ty_if| ty_if.0);
|
||||
let ty_if_then = ty_if
|
||||
.map(|ty_if| match &ty_if.1.expression {
|
||||
ty::TyExpressionVariant::CodeBlock(ty_code_block) => Ok(ty_code_block),
|
||||
|
@ -957,7 +1212,7 @@ impl __ProgramVisitor {
|
|||
.transpose()?;
|
||||
let ty_if_else = ty_if.and_then(|ty_if| ty_if.2);
|
||||
|
||||
// TODO: Implement visiting `if_condition`.
|
||||
visitor.visit_expr(ctx, lexed_condition.__as_ref(), ty_if_condition, output)?;
|
||||
|
||||
Self::visit_block(
|
||||
ctx,
|
||||
|
|
|
@ -131,6 +131,38 @@ pub enum FnArgs {
|
|||
},
|
||||
}
|
||||
|
||||
impl FnArgs {
|
||||
/// Returns all the [FnArg]s, from the function signature defined by `self`.
|
||||
///
|
||||
/// If the `self` is [FnArgs::NonStatic], the first `self` argument is not
|
||||
/// returned, because it is not an [FnArg].
|
||||
pub fn args(&self) -> Vec<&FnArg> {
|
||||
match self {
|
||||
Self::Static(punctuated) => punctuated.iter().collect(),
|
||||
Self::NonStatic { args_opt, .. } => args_opt
|
||||
.as_ref()
|
||||
.map_or(vec![], |(_comma_token, punctuated)| {
|
||||
punctuated.iter().collect()
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns all the [FnArg]s, from the function signature defined by `self`.
|
||||
///
|
||||
/// If the `self` is [FnArgs::NonStatic], the first `self` argument is not
|
||||
/// returned, because it is not an [FnArg].
|
||||
pub fn args_mut(&mut self) -> Vec<&mut FnArg> {
|
||||
match self {
|
||||
Self::Static(punctuated) => punctuated.iter_mut().collect(),
|
||||
Self::NonStatic { args_opt, .. } => args_opt
|
||||
.as_mut()
|
||||
.map_or(vec![], |(_comma_token, punctuated)| {
|
||||
punctuated.iter_mut().collect()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub struct FnArg {
|
||||
pub pattern: Pattern,
|
||||
|
|
|
@ -22,7 +22,7 @@ impl Format for ExprStructField {
|
|||
formatted_code: &mut FormattedCode,
|
||||
formatter: &mut Formatter,
|
||||
) -> Result<(), FormatterError> {
|
||||
write!(formatted_code, "{}", self.field_name.as_str())?;
|
||||
write!(formatted_code, "{}", self.field_name.as_raw_ident_str())?;
|
||||
if let Some((_colon_token, expr)) = &self.expr_opt {
|
||||
formatter.with_shape(
|
||||
formatter
|
||||
|
|
|
@ -213,7 +213,7 @@ impl Format for TypeField {
|
|||
write!(
|
||||
formatted_code,
|
||||
"{}{} ",
|
||||
self.name.as_str(),
|
||||
self.name.as_raw_ident_str(),
|
||||
ColonToken::AS_STR,
|
||||
)?;
|
||||
self.ty.format(formatted_code, formatter)?;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue