Skip to content

Commit

Permalink
Merge branch 'master' into ironcev/declarative-and-checked-annotations
Browse files Browse the repository at this point in the history
  • Loading branch information
ironcev authored Mar 11, 2025
2 parents 40bab02 + 8b3d822 commit 86bd805
Show file tree
Hide file tree
Showing 16 changed files with 1,349 additions and 2 deletions.
1 change: 1 addition & 0 deletions forc-plugins/forc-migrate/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod cli;
mod migrations;
mod matching;
mod modifying;
mod visiting;

use std::fmt::Display;
use std::io::{self, Write};
Expand Down
4 changes: 4 additions & 0 deletions forc-plugins/forc-migrate/src/matching/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
//! accept `&&TElement` or `&&mut TElement` so that can be easily passed to
//! [Iterator::filter] function.
//!
//! For the cases when migrations do target individual expressions, and do not need
//! to inspect a larger scope, the visitor pattern is still supported and available
//! via the tree visitors that are defined in [super::visiting].
//!
//! ## Matching elements in trees
//!
//! Functions matching on lexed trees are coming in two variants, immutable and mutable.
Expand Down
2 changes: 2 additions & 0 deletions forc-plugins/forc-migrate/src/migrations/demo.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! This module contains demo migrations used for learning and testing the migration tool.
#![allow(deprecated)]

use std::vec;

use crate::{
Expand Down
11 changes: 11 additions & 0 deletions forc-plugins/forc-migrate/src/migrations/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod demo;
mod partial_eq;
mod references;
mod storage_domains;
mod try_from_bytes_for_b256;

use std::collections::HashSet;

Expand Down Expand Up @@ -273,6 +274,8 @@ pub(crate) enum MigrationStepKind {
/// A convenient method for visiting all the modules within a program.
/// The `visitor` will be called for every module, and the method will return the
/// [Vec] containing the results of all the individual visitor calls.
#[deprecated(note = "use `crate::visiting::ProgramVisitor/Mut::visit_program()` instead")]
#[allow(deprecated)]
pub(crate) fn visit_all_modules<T>(
program_info: &ProgramInfo,
dry_run: DryRun,
Expand All @@ -292,6 +295,8 @@ pub(crate) fn visit_all_modules<T>(
/// [Vec] containing the results of all the individual visitor calls.
///
/// Visitors can mutate the [LexedProgram].
#[deprecated(note = "use `crate::visiting::ProgramVisitor/Mut::visit_program()` instead")]
#[allow(deprecated)]
pub(crate) fn visit_all_modules_mut<T>(
program_info: &mut MutProgramInfo,
dry_run: DryRun,
Expand All @@ -315,6 +320,8 @@ pub(crate) fn visit_all_modules_mut<T>(
[visit_modules] [ModuleVisitorFn] [&type] [&value] [iter];
[visit_modules_mut] [ModuleVisitorMutFn] [&mut type] [&mut value] [iter_mut];
)]
#[deprecated(note = "use `crate::visiting::ProgramVisitor/Mut::visit_program()` instead")]
#[allow(deprecated)]
pub(crate) fn __visit_modules<T>(
engines: &Engines,
lexed_module: __ref_type([LexedModule]),
Expand Down Expand Up @@ -492,4 +499,8 @@ const MIGRATION_STEPS: MigrationSteps = &[
self::partial_eq::REMOVE_DEPRECATED_EQ_TRAIT_IMPLEMENTATIONS,
],
),
(
Feature::TryFromBytesForB256,
&[self::try_from_bytes_for_b256::REPLACE_B256_FROM_BYTES_TO_TRY_FROM_BYTES_STEP],
),
];
2 changes: 2 additions & 0 deletions forc-plugins/forc-migrate/src/migrations/partial_eq.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(deprecated)]

use std::vec;

use crate::{
Expand Down
2 changes: 2 additions & 0 deletions forc-plugins/forc-migrate/src/migrations/references.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(deprecated)]

use std::vec;

use crate::migrations::{visit_all_modules_mut, MutProgramInfo};
Expand Down
115 changes: 115 additions & 0 deletions forc-plugins/forc-migrate/src/migrations/try_from_bytes_for_b256.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
use crate::{
migrations::MutProgramInfo,
modifying::*,
visiting::{
InvalidateTypedElement, LexedFnCallInfoMut, ProgramVisitorMut, TreesVisitorMut,
TyFnCallInfo, VisitingContext,
},
};
use anyhow::{bail, Ok, Result};
use sway_ast::Expr;
use sway_core::language::{ty::TyExpression, CallPath};
use sway_types::{Span, Spanned};

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

// NOTE: We do not fully support cases when `b256::from` is nested within another `b256::from`.
// E.g.: `b256::from(Bytes::from(b256::from(nested_bytes)))`.
// In such cases, only the outermost `b256::from` will be migrated.
// In practice, this does not happen.

#[allow(dead_code)]
pub(super) const REPLACE_B256_FROM_BYTES_TO_TRY_FROM_BYTES_STEP: MigrationStep = MigrationStep {
title: "Replace calls to `b256::from(<bytes>)` with `b256::try_from(<bytes>).unwrap()`",
duration: 0,
kind: MigrationStepKind::CodeModification(
replace_b256_from_bytes_to_try_from_bytes_step,
&[],
ContinueMigrationProcess::IfNoManualMigrationActionsNeeded,
),
help: &[
"Migration will replace all the calls to `b256::from(<bytes>)` with",
"`b256::try_from(<bytes>).unwrap()`.",
" ",
"E.g.:",
" let result = b256::from(some_bytes);",
"will become:",
" let result = b256::try_from(some_bytes).unwrap();",
],
};

fn replace_b256_from_bytes_to_try_from_bytes_step(
program_info: &mut MutProgramInfo,
dry_run: DryRun,
) -> Result<Vec<Span>> {
struct Visitor;
impl TreesVisitorMut<Span> for Visitor {
fn visit_fn_call(
&mut self,
ctx: &VisitingContext,
lexed_fn_call: &mut Expr,
ty_fn_call: Option<&TyExpression>,
output: &mut Vec<Span>,
) -> 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()?;

// We need the typed info in order to ensure that the `from` function
// is really the `b256::from(Bytes)` function.
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_typeid
else {
return Ok(InvalidateTypedElement::No);
};

// Note that neither the implementing for type not the trait are a
// part of the `from` function call path. All associated `from` functions
// in the `std::bytes` will have the same call path.
// We will filter further below to target exactly the `<From<Bytes> for b256>::from`.
let from_call_path = CallPath::fullpath(&["std", "bytes", "from"]);

// This check is sufficient. The only `from` in `std::bytes` that
// satisfies it is the `<From<Bytes> for b256>::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);
}

// We have found a `b256::from(Bytes)` call.
output.push(lexed_fn_call_info.func.span());

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`."),
};

// Rename the call to `from` to `try_from`.
let from_ident = lexed_from_call_path.last_segment_mut();
modify(from_ident).set_name("try_from");

// The call to `try_from` becomes the target of the `unwrap` method call.
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 {})
}
38 changes: 38 additions & 0 deletions forc-plugins/forc-migrate/src/modifying/expr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use sway_ast::{
keywords::{DotToken, Token},
Expr, Parens, PathExprSegment, Punctuated,
};
use sway_types::{Ident, Span};

use crate::assert_insert_span;

use super::New;

impl New {
/// Creates an [Expr] representing a call to a non-generic method with the name `method_name`.
/// The method does not accepts any arguments.
pub(crate) fn method_call<S: AsRef<str> + ?Sized>(
insert_span: Span,
target: Expr,
method_name: &S,
) -> Expr {
assert_insert_span!(insert_span);

Expr::MethodCall {
target: Box::new(target),
dot_token: DotToken::new(insert_span.clone()),
path_seg: PathExprSegment {
name: Ident::new_with_override(method_name.as_ref().into(), insert_span.clone()),
generics_opt: None,
},
contract_args_opt: None,
args: Parens {
inner: Punctuated {
value_separator_pairs: vec![],
final_value_opt: None,
},
span: insert_span,
},
}
}
}
2 changes: 2 additions & 0 deletions forc-plugins/forc-migrate/src/modifying/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ use sway_types::Span;

mod annotated;
mod attribute;
mod expr;
mod function;
mod literal;
mod module;
mod path_expression_segment;
mod storage_field;

/// A wrapper around a lexed tree element that will be modified.
Expand Down
14 changes: 14 additions & 0 deletions forc-plugins/forc-migrate/src/modifying/path_expression_segment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use sway_ast::PathExprSegment;
use sway_types::{Ident, Spanned};

use super::Modifier;

impl Modifier<'_, PathExprSegment> {
pub(crate) fn set_name<S: AsRef<str> + ?Sized>(&mut self, name: &S) -> &mut Self {
// We preserve the current span of the name.
let insert_span = self.element.name.span();
self.element.name = Ident::new_with_override(name.as_ref().into(), insert_span);

self
}
}
Loading

0 comments on commit 86bd805

Please sign in to comment.