Skip to content

Commit d4eb364

Browse files
committed
Implement trees visitors and migrations for TryFrom<Bytes> for b256
1 parent 9739ec7 commit d4eb364

File tree

16 files changed

+1347
-2
lines changed

16 files changed

+1347
-2
lines changed

forc-plugins/forc-migrate/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ pub mod cli;
33
mod migrations;
44
mod matching;
55
mod modifying;
6+
mod visiting;
67

78
use std::fmt::Display;
89
use std::io::{self, Write};

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

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
//! accept `&&TElement` or `&&mut TElement` so that can be easily passed to
4545
//! [Iterator::filter] function.
4646
//!
47+
//! For the cases when migrations do target individual expressions, and do not need
48+
//! to inspect a larger scope, the visitor pattern is still supported and available
49+
//! via the tree visitors that are defined in [super::visiting].
50+
//!
4751
//! ## Matching elements in trees
4852
//!
4953
//! Functions matching on lexed trees are coming in two variants, immutable and mutable.

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

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! This module contains demo migrations used for learning and testing the migration tool.
22
3+
#![allow(deprecated)]
4+
35
use std::vec;
46

57
use crate::{

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

+11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod demo;
1212
mod partial_eq;
1313
mod references;
1414
mod storage_domains;
15+
mod try_from_bytes_for_b256;
1516

1617
use std::collections::HashSet;
1718

@@ -273,6 +274,8 @@ pub(crate) enum MigrationStepKind {
273274
/// A convenient method for visiting all the modules within a program.
274275
/// The `visitor` will be called for every module, and the method will return the
275276
/// [Vec] containing the results of all the individual visitor calls.
277+
#[deprecated(note = "use `crate::visiting::ProgramVisitor/Mut::visit_program()` instead")]
278+
#[allow(deprecated)]
276279
pub(crate) fn visit_all_modules<T>(
277280
program_info: &ProgramInfo,
278281
dry_run: DryRun,
@@ -292,6 +295,8 @@ pub(crate) fn visit_all_modules<T>(
292295
/// [Vec] containing the results of all the individual visitor calls.
293296
///
294297
/// Visitors can mutate the [LexedProgram].
298+
#[deprecated(note = "use `crate::visiting::ProgramVisitor/Mut::visit_program()` instead")]
299+
#[allow(deprecated)]
295300
pub(crate) fn visit_all_modules_mut<T>(
296301
program_info: &mut MutProgramInfo,
297302
dry_run: DryRun,
@@ -315,6 +320,8 @@ pub(crate) fn visit_all_modules_mut<T>(
315320
[visit_modules] [ModuleVisitorFn] [&type] [&value] [iter];
316321
[visit_modules_mut] [ModuleVisitorMutFn] [&mut type] [&mut value] [iter_mut];
317322
)]
323+
#[deprecated(note = "use `crate::visiting::ProgramVisitor/Mut::visit_program()` instead")]
324+
#[allow(deprecated)]
318325
pub(crate) fn __visit_modules<T>(
319326
engines: &Engines,
320327
lexed_module: __ref_type([LexedModule]),
@@ -492,4 +499,8 @@ const MIGRATION_STEPS: MigrationSteps = &[
492499
self::partial_eq::REMOVE_DEPRECATED_EQ_TRAIT_IMPLEMENTATIONS,
493500
],
494501
),
502+
(
503+
Feature::TryFromBytesForB256,
504+
&[self::try_from_bytes_for_b256::REPLACE_B256_FROM_BYTES_TO_TRY_FROM_BYTES_STEP],
505+
),
495506
];

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

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(deprecated)]
2+
13
use std::vec;
24

35
use crate::{

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

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#![allow(deprecated)]
2+
13
use std::vec;
24

35
use crate::migrations::{visit_all_modules_mut, MutProgramInfo};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use crate::{
2+
migrations::MutProgramInfo,
3+
modifying::*,
4+
visiting::{
5+
InvalidateTypedElement, LexedFnCallInfoMut, ProgramVisitorMut, TreesVisitorMut,
6+
TyFnCallInfo, VisitingContext,
7+
},
8+
};
9+
use anyhow::{bail, Ok, Result};
10+
use sway_ast::Expr;
11+
use sway_core::language::{ty::TyExpression, CallPath};
12+
use sway_types::{Span, Spanned};
13+
14+
use super::{ContinueMigrationProcess, DryRun, MigrationStep, MigrationStepKind};
15+
16+
// NOTE: We do not fully support cases when `b256::from` is nested within another `b256::from`.
17+
// E.g.: `b256::from(Bytes::from(b256::from(nested_bytes)))`.
18+
// In such cases, only the outermost `b256::from` will be migrated.
19+
// In practice, this does not happen.
20+
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()`",
24+
duration: 0,
25+
kind: MigrationStepKind::CodeModification(
26+
replace_b256_from_bytes_to_try_from_bytes_step,
27+
&[],
28+
ContinueMigrationProcess::IfNoManualMigrationActionsNeeded,
29+
),
30+
help: &[
31+
"Migration will replace all the calls to `b256::from(<bytes>)` with",
32+
"`b256::try_from(<bytes>).unwrap()`.",
33+
" ",
34+
"E.g.:",
35+
" let result = b256::from(some_bytes);",
36+
"will become:",
37+
" let result = b256::try_from(some_bytes).unwrap();",
38+
],
39+
};
40+
41+
fn replace_b256_from_bytes_to_try_from_bytes_step(
42+
program_info: &mut MutProgramInfo,
43+
dry_run: DryRun,
44+
) -> Result<Vec<Span>> {
45+
struct Visitor;
46+
impl TreesVisitorMut<Span> for Visitor {
47+
fn visit_fn_call(
48+
&mut self,
49+
ctx: &VisitingContext,
50+
lexed_fn_call: &mut Expr,
51+
ty_fn_call: Option<&TyExpression>,
52+
output: &mut Vec<Span>,
53+
) -> Result<InvalidateTypedElement> {
54+
let lexed_fn_call_info = LexedFnCallInfoMut::new(lexed_fn_call)?;
55+
let ty_fn_call_info = ty_fn_call
56+
.map(|ty_fn_call| TyFnCallInfo::new(ctx.engines.de(), ty_fn_call))
57+
.transpose()?;
58+
59+
// We need the typed info in order to ensure that the `from` function
60+
// is really the `b256::from(Bytes)` function.
61+
let Some(ty_fn_call_info) = ty_fn_call_info else {
62+
return Ok(InvalidateTypedElement::No);
63+
};
64+
65+
// Note that neither the implementing for type not the trait are a
66+
// part of the `from` function call path. All associated `from` functions
67+
// in the `std::bytes` will have the same call path.
68+
// We will filter further below to target exactly the `<From<Bytes> for b256>::from`.
69+
let from_call_path = CallPath::fullpath(&["std", "bytes", "from"]);
70+
71+
let Some(implementing_for_type_id) = ty_fn_call_info.fn_decl.implementing_for_typeid
72+
else {
73+
return Ok(InvalidateTypedElement::No);
74+
};
75+
76+
// This check is sufficient. The only `from` in `std::bytes` that
77+
// satisfies it is the `<From<Bytes> for b256>::from`.
78+
if !(ty_fn_call_info.fn_decl.call_path == from_call_path
79+
&& implementing_for_type_id == ctx.engines.te().id_of_b256())
80+
{
81+
return Ok(InvalidateTypedElement::No);
82+
}
83+
84+
// We have found a `b256::from(Bytes)` call.
85+
output.push(lexed_fn_call_info.func.span());
86+
87+
if ctx.dry_run == DryRun::Yes {
88+
return Ok(InvalidateTypedElement::No);
89+
}
90+
91+
let lexed_from_call_path = match lexed_fn_call {
92+
Expr::FuncApp { func, args: _ } => match func.as_mut() {
93+
Expr::Path(path_expr) => path_expr,
94+
_ => bail!("`func` of the `lexed_fn_call` must be of the variant `Expr::Path`."),
95+
},
96+
_ => bail!("`lexed_fn_call` must be of the variant `Expr::FuncApp`."),
97+
};
98+
99+
// Rename the call to `from` to `try_from`.
100+
let from_ident = lexed_from_call_path.last_segment_mut();
101+
modify(from_ident).set_name("try_from");
102+
103+
// The call to `try_from` becomes the target of the `unwrap` method call.
104+
let target = lexed_fn_call.clone();
105+
let insert_span = Span::empty_at_end(&target.span());
106+
*lexed_fn_call = New::method_call(insert_span, target, "unwrap");
107+
108+
Ok(InvalidateTypedElement::Yes)
109+
}
110+
}
111+
112+
ProgramVisitorMut::visit_program(program_info, dry_run, &mut Visitor {})
113+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
use sway_ast::{
2+
keywords::{DotToken, Token},
3+
Expr, Parens, PathExprSegment, Punctuated,
4+
};
5+
use sway_types::{Ident, Span};
6+
7+
use crate::assert_insert_span;
8+
9+
use super::New;
10+
11+
impl New {
12+
/// Creates an [Expr] representing a call to a non-generic method with the name `method_name`.
13+
/// The method does not accepts any arguments.
14+
pub(crate) fn method_call<S: AsRef<str> + ?Sized>(
15+
insert_span: Span,
16+
target: Expr,
17+
method_name: &S,
18+
) -> Expr {
19+
assert_insert_span!(insert_span);
20+
21+
Expr::MethodCall {
22+
target: Box::new(target),
23+
dot_token: DotToken::new(insert_span.clone()),
24+
path_seg: PathExprSegment {
25+
name: Ident::new_with_override(method_name.as_ref().into(), insert_span.clone()),
26+
generics_opt: None,
27+
},
28+
contract_args_opt: None,
29+
args: Parens {
30+
inner: Punctuated {
31+
value_separator_pairs: vec![],
32+
final_value_opt: None,
33+
},
34+
span: insert_span,
35+
},
36+
}
37+
}
38+
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ use sway_types::Span;
55

66
mod annotated;
77
mod attribute;
8+
mod expr;
89
mod function;
910
mod literal;
1011
mod module;
12+
mod path_expression_segment;
1113
mod storage_field;
1214

1315
/// A wrapper around a lexed tree element that will be modified.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
use sway_ast::PathExprSegment;
2+
use sway_types::{Ident, Spanned};
3+
4+
use super::Modifier;
5+
6+
impl Modifier<'_, PathExprSegment> {
7+
pub(crate) fn set_name<S: AsRef<str> + ?Sized>(&mut self, name: &S) -> &mut Self {
8+
// We preserve the current span of the name.
9+
let insert_span = self.element.name.span();
10+
self.element.name = Ident::new_with_override(name.as_ref().into(), insert_span);
11+
12+
self
13+
}
14+
}

0 commit comments

Comments
 (0)