#![allow(dead_code)]
use crate::{
migrations::{MutProgramInfo, Occurrence},
modifying::*,
visiting::{
InvalidateTypedElement, LexedFnCallInfoMut, LexedMethodCallInfoMut, ProgramVisitorMut,
TreesVisitorMut, TyFnCallInfo, TyMethodCallInfo, VisitingContext,
},
};
use anyhow::{bail, Ok, Result};
use sway_ast::Expr;
use sway_core::{
language::{ty::TyExpression, CallPath},
TypeInfo,
};
use sway_types::{Span, Spanned};
use super::{ContinueMigrationProcess, DryRun, MigrationStep, MigrationStepKind};
pub(super) const REPLACE_B256_FROM_BYTES_WITH_TRY_FROM_BYTES_STEP: MigrationStep = MigrationStep {
title: "Replace `b256::from(<bytes>)` calls with `b256::try_from(<bytes>).unwrap()`",
duration: 0,
kind: MigrationStepKind::CodeModification(
replace_b256_from_bytes_with_try_from_bytes_step,
&[],
ContinueMigrationProcess::IfNoManualMigrationActionsNeeded,
),
help: &[
"Migration will replace all the `b256::from(<bytes>)` calls",
"with `b256::try_from(<bytes>).unwrap()`.",
" ",
"E.g.:",
" let result = b256::from(some_bytes);",
"will become:",
" let result = b256::try_from(some_bytes).unwrap();",
],
};
pub(super) const REPLACE_BYTES_INTO_B256_WITH_TRY_INTO_B256_STEP: MigrationStep = MigrationStep {
title: "Replace `<bytes>.into()` calls with `<bytes>.try_into().unwrap()`",
duration: 0,
kind: MigrationStepKind::CodeModification(
replace_bytes_into_b256_with_try_into_b256_step,
&[],
ContinueMigrationProcess::IfNoManualMigrationActionsNeeded,
),
help: &[
"Migration will replace all the `<bytes>.into()` calls resulting in \"b256\"",
"with `<bytes>.try_into().unwrap()`.",
" ",
"E.g.:",
" let result: b256 = some_bytes.into();",
"will become:",
" let result: b256 = some_bytes.try_into().unwrap();",
],
};
fn replace_b256_from_bytes_with_try_from_bytes_step(
program_info: &mut MutProgramInfo,
dry_run: DryRun,
) -> Result<Vec<Occurrence>> {
struct Visitor;
impl TreesVisitorMut<Occurrence> for Visitor {
fn visit_fn_call(
&mut self,
ctx: &VisitingContext,
lexed_fn_call: &mut Expr,
ty_fn_call: Option<&TyExpression>,
output: &mut Vec<Occurrence>,
) -> Result<InvalidateTypedElement> {
let lexed_fn_call_info = LexedFnCallInfoMut::new(lexed_fn_call)?;
let ty_fn_call_info = ty_fn_call
.map(|ty_fn_call| TyFnCallInfo::new(ctx.engines.de(), ty_fn_call))
.transpose()?;
let Some(ty_fn_call_info) = ty_fn_call_info else {
return Ok(InvalidateTypedElement::No);
};
let Some(implementing_for_type_id) = ty_fn_call_info.fn_decl.implementing_for else {
return Ok(InvalidateTypedElement::No);
};
let from_call_path = CallPath::fullpath(&["std", "bytes", "from"]);
if !(ty_fn_call_info.fn_decl.call_path == from_call_path
&& implementing_for_type_id == ctx.engines.te().id_of_b256())
{
return Ok(InvalidateTypedElement::No);
}
output.push(lexed_fn_call_info.func.span().into());
if ctx.dry_run == DryRun::Yes {
return Ok(InvalidateTypedElement::No);
}
let lexed_from_call_path = match lexed_fn_call {
Expr::FuncApp { func, args: _ } => match func.as_mut() {
Expr::Path(path_expr) => path_expr,
_ => {
bail!("`func` of the `lexed_fn_call` must be of the variant `Expr::Path`.")
}
},
_ => bail!("`lexed_fn_call` must be of the variant `Expr::FuncApp`."),
};
let from_ident = lexed_from_call_path.last_segment_mut();
modify(from_ident).set_name("try_from");
let target = lexed_fn_call.clone();
let insert_span = Span::empty_at_end(&target.span());
*lexed_fn_call = New::method_call(insert_span, target, "unwrap");
Ok(InvalidateTypedElement::Yes)
}
}
ProgramVisitorMut::visit_program(program_info, dry_run, &mut Visitor {})
}
fn replace_bytes_into_b256_with_try_into_b256_step(
program_info: &mut MutProgramInfo,
dry_run: DryRun,
) -> Result<Vec<Occurrence>> {
struct Visitor;
impl TreesVisitorMut<Occurrence> for Visitor {
fn visit_method_call(
&mut self,
ctx: &VisitingContext,
lexed_method_call: &mut Expr,
ty_method_call: Option<&TyExpression>,
output: &mut Vec<Occurrence>,
) -> Result<InvalidateTypedElement> {
let lexed_method_call_info = LexedMethodCallInfoMut::new(lexed_method_call)?;
let ty_method_call_info = ty_method_call
.map(|ty_method_call| TyMethodCallInfo::new(ctx.engines.de(), ty_method_call))
.transpose()?;
let Some(ty_method_call_info) = ty_method_call_info else {
return Ok(InvalidateTypedElement::No);
};
let method_return_type = ctx
.engines
.te()
.get(ty_method_call_info.fn_decl.return_type.type_id);
let method_target_is_bytes_struct = match ctx
.engines
.te()
.get(ty_method_call_info.parent_type_id)
.as_ref()
{
TypeInfo::Struct(decl_id) => {
let struct_decl = ctx.engines.de().get_struct(decl_id);
struct_decl.call_path == CallPath::fullpath(&["std", "bytes", "Bytes"])
}
_ => false,
};
if !(ty_method_call_info.fn_decl.name.as_str() == "into"
&& matches!(method_return_type.as_ref(), TypeInfo::B256)
&& method_target_is_bytes_struct)
{
return Ok(InvalidateTypedElement::No);
}
output.push(lexed_method_call_info.path_seg.span().into());
if ctx.dry_run == DryRun::Yes {
return Ok(InvalidateTypedElement::No);
}
modify(lexed_method_call_info.path_seg).set_name("try_into");
let target = lexed_method_call.clone();
let insert_span = Span::empty_at_end(&target.span());
*lexed_method_call = New::method_call(insert_span, target, "unwrap");
Ok(InvalidateTypedElement::Yes)
}
}
ProgramVisitorMut::visit_program(program_info, dry_run, &mut Visitor {})
}