Skip to content

Commit 1f407fa

Browse files
authored
Add migration for Bytes::into(self) -> b256 to Bytes::try_into (#7009)
## Description This PR adds migration step for migrating `<bytes>.into()` calls to `<bytes>.try_into().unwrap`, as explained in the Breaking Changes chapter of #6994. ## 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.
1 parent 8b3d822 commit 1f407fa

File tree

2 files changed

+111
-11
lines changed

2 files changed

+111
-11
lines changed

forc-plugins/forc-migrate/src/migrations/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,9 @@ const MIGRATION_STEPS: MigrationSteps = &[
501501
),
502502
(
503503
Feature::TryFromBytesForB256,
504-
&[self::try_from_bytes_for_b256::REPLACE_B256_FROM_BYTES_TO_TRY_FROM_BYTES_STEP],
504+
&[
505+
self::try_from_bytes_for_b256::REPLACE_B256_FROM_BYTES_WITH_TRY_FROM_BYTES_STEP,
506+
self::try_from_bytes_for_b256::REPLACE_BYTES_INTO_B256_WITH_TRY_INTO_B256_STEP,
507+
],
505508
),
506509
];

forc-plugins/forc-migrate/src/migrations/try_from_bytes_for_b256.rs

+107-10
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,37 @@ use crate::{
22
migrations::MutProgramInfo,
33
modifying::*,
44
visiting::{
5-
InvalidateTypedElement, LexedFnCallInfoMut, ProgramVisitorMut, TreesVisitorMut,
6-
TyFnCallInfo, VisitingContext,
5+
InvalidateTypedElement, LexedFnCallInfoMut, LexedMethodCallInfoMut, ProgramVisitorMut,
6+
TreesVisitorMut, TyFnCallInfo, TyMethodCallInfo, VisitingContext,
77
},
88
};
99
use anyhow::{bail, Ok, Result};
1010
use sway_ast::Expr;
11-
use sway_core::language::{ty::TyExpression, CallPath};
11+
use sway_core::{
12+
language::{ty::TyExpression, CallPath},
13+
TypeInfo,
14+
};
1215
use sway_types::{Span, Spanned};
1316

1417
use super::{ContinueMigrationProcess, DryRun, MigrationStep, MigrationStepKind};
1518

1619
// NOTE: We do not fully support cases when `b256::from` is nested within another `b256::from`.
1720
// E.g.: `b256::from(Bytes::from(b256::from(nested_bytes)))`.
1821
// In such cases, only the outermost `b256::from` will be migrated.
22+
// The same is with `Bytes::into`.
1923
// In practice, this does not happen.
2024

21-
#[allow(dead_code)]
22-
pub(super) const REPLACE_B256_FROM_BYTES_TO_TRY_FROM_BYTES_STEP: MigrationStep = MigrationStep {
23-
title: "Replace calls to `b256::from(<bytes>)` with `b256::try_from(<bytes>).unwrap()`",
25+
pub(super) const REPLACE_B256_FROM_BYTES_WITH_TRY_FROM_BYTES_STEP: MigrationStep = MigrationStep {
26+
title: "Replace `b256::from(<bytes>)` calls with `b256::try_from(<bytes>).unwrap()`",
2427
duration: 0,
2528
kind: MigrationStepKind::CodeModification(
26-
replace_b256_from_bytes_to_try_from_bytes_step,
29+
replace_b256_from_bytes_with_try_from_bytes_step,
2730
&[],
2831
ContinueMigrationProcess::IfNoManualMigrationActionsNeeded,
2932
),
3033
help: &[
31-
"Migration will replace all the calls to `b256::from(<bytes>)` with",
32-
"`b256::try_from(<bytes>).unwrap()`.",
34+
"Migration will replace all the `b256::from(<bytes>)` calls",
35+
"with `b256::try_from(<bytes>).unwrap()`.",
3336
" ",
3437
"E.g.:",
3538
" let result = b256::from(some_bytes);",
@@ -38,7 +41,26 @@ pub(super) const REPLACE_B256_FROM_BYTES_TO_TRY_FROM_BYTES_STEP: MigrationStep =
3841
],
3942
};
4043

41-
fn replace_b256_from_bytes_to_try_from_bytes_step(
44+
pub(super) const REPLACE_BYTES_INTO_B256_WITH_TRY_INTO_B256_STEP: MigrationStep = MigrationStep {
45+
title: "Replace `<bytes>.into()` calls with `<bytes>.try_into().unwrap()`",
46+
duration: 0,
47+
kind: MigrationStepKind::CodeModification(
48+
replace_bytes_into_b256_with_try_into_b256_step,
49+
&[],
50+
ContinueMigrationProcess::IfNoManualMigrationActionsNeeded,
51+
),
52+
help: &[
53+
"Migration will replace all the `<bytes>.into()` calls resulting in \"b256\"",
54+
"with `<bytes>.try_into().unwrap()`.",
55+
" ",
56+
"E.g.:",
57+
" let result: b256 = some_bytes.into();",
58+
"will become:",
59+
" let result: b256 = some_bytes.try_into().unwrap();",
60+
],
61+
};
62+
63+
fn replace_b256_from_bytes_with_try_from_bytes_step(
4264
program_info: &mut MutProgramInfo,
4365
dry_run: DryRun,
4466
) -> Result<Vec<Span>> {
@@ -113,3 +135,78 @@ fn replace_b256_from_bytes_to_try_from_bytes_step(
113135

114136
ProgramVisitorMut::visit_program(program_info, dry_run, &mut Visitor {})
115137
}
138+
139+
fn replace_bytes_into_b256_with_try_into_b256_step(
140+
program_info: &mut MutProgramInfo,
141+
dry_run: DryRun,
142+
) -> Result<Vec<Span>> {
143+
struct Visitor;
144+
impl TreesVisitorMut<Span> for Visitor {
145+
fn visit_method_call(
146+
&mut self,
147+
ctx: &VisitingContext,
148+
lexed_method_call: &mut Expr,
149+
ty_method_call: Option<&TyExpression>,
150+
output: &mut Vec<Span>,
151+
) -> Result<InvalidateTypedElement> {
152+
let lexed_method_call_info = LexedMethodCallInfoMut::new(lexed_method_call)?;
153+
let ty_method_call_info = ty_method_call
154+
.map(|ty_method_call| TyMethodCallInfo::new(ctx.engines.de(), ty_method_call))
155+
.transpose()?;
156+
157+
// We need the typed info in order to ensure that the `into` function
158+
// is really the `Bytes::into(self) -> b256` function.
159+
let Some(ty_method_call_info) = ty_method_call_info else {
160+
return Ok(InvalidateTypedElement::No);
161+
};
162+
163+
let method_return_type = ctx
164+
.engines
165+
.te()
166+
.get(ty_method_call_info.fn_decl.return_type.type_id);
167+
let method_target_is_bytes_struct = match ctx
168+
.engines
169+
.te()
170+
.get(ty_method_call_info.parent_type_id)
171+
.as_ref()
172+
{
173+
TypeInfo::Struct(decl_id) => {
174+
let struct_decl = ctx.engines.de().get_struct(decl_id);
175+
struct_decl.call_path == CallPath::fullpath(&["std", "bytes", "Bytes"])
176+
}
177+
_ => false,
178+
};
179+
180+
if !(ty_method_call_info.fn_decl.name.as_str() == "into"
181+
&& matches!(method_return_type.as_ref(), TypeInfo::B256)
182+
&& method_target_is_bytes_struct)
183+
{
184+
return Ok(InvalidateTypedElement::No);
185+
}
186+
187+
// We have found a `Bytes::into(self) -> b256` call.
188+
output.push(lexed_method_call_info.path_seg.span());
189+
190+
if ctx.dry_run == DryRun::Yes {
191+
return Ok(InvalidateTypedElement::No);
192+
}
193+
194+
let lexed_into_path = match lexed_method_call {
195+
Expr::MethodCall { path_seg, .. } => path_seg,
196+
_ => bail!("`lexed_method_call` must be of the variant `Expr::MethodCall`."),
197+
};
198+
199+
// Rename the call to `into` to `try_into`.
200+
modify(lexed_into_path).set_name("try_into");
201+
202+
// The call to `try_into` becomes the target of the `unwrap` method call.
203+
let target = lexed_method_call.clone();
204+
let insert_span = Span::empty_at_end(&target.span());
205+
*lexed_method_call = New::method_call(insert_span, target, "unwrap");
206+
207+
Ok(InvalidateTypedElement::Yes)
208+
}
209+
}
210+
211+
ProgramVisitorMut::visit_program(program_info, dry_run, &mut Visitor {})
212+
}

0 commit comments

Comments
 (0)