From 23576769d151167870a1454ce112f4f9d7271d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20Ron=C4=8Devi=C4=87?= Date: Tue, 7 Jan 2025 12:55:03 +0100 Subject: [PATCH] Refactor `swayfmt` to support arbitrary lexed trees (#6806) ## Description This PR refactors `swayfmt` to be able generate code from arbitrary lexed trees. Arbitrary means a lexed tree that can be fully or partially created in-memory by manipulating the tree structure in code. It does not need to necessarily be backed by source code. This is needed to be able to reuse `swayfmt` for generating source code from tools that analyze and modify Sway code, as explained in #6779. This PR makes the `swayfmt` independent of spans backed by the source code, by doing the following changes: - Keywords are rendered by using theirs `AS_STR` associated constants. - The `Token` trait is extended with the `AS_STR` associated constant, and tokens are rendered by using that constant. - `Ident`s are rendered by using theirs `as_str()` methods. This method takes the textual implementation from `Ident::name_override_opt` if it is provided, and ignores the span in that case. - `Literal`s are rendered based on the literal value and not their spans. The exception are numeric literals that do not have empty spans. Those are considered to be backed by source code and are rendered as written in code, e.g., `1_000_000u64`. The PR also fixes some existing bugs in `swayfmt`: - `use path::{single_import,};` will be formatted as `use path::single_import;` - `use path::{a,b,};` will be formatted as `use path::{a, b};` - partial addresses #6802 by rendering annotations for final values. - partial addresses #6805 by properly rendering final values, but still not using the expected field alignment. The PR also removes the `path` parameter from the `parse_file`. It was used only to provide an unnecessary dummy `source_id` which was always pointing to the package root file. Closes #6779. ## 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) - [x] 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. --- Cargo.lock | 1 - .../advanced_storage_variables/src/main.sw | 2 +- examples/converting_types/src/byte_arrays.sw | 2 +- examples/converting_types/src/bytes.sw | 2 +- examples/wallet_smart_contract/src/main.sw | 2 +- forc-plugins/forc-fmt/src/main.rs | 33 +- sway-ast/src/expr/mod.rs | 12 + sway-ast/src/expr/op_code.rs | 8 + sway-ast/src/keywords.rs | 94 +++-- sway-ast/src/punctuated.rs | 13 + sway-lib-std/src/prelude.sw | 2 +- sway-lsp/src/capabilities/formatting.rs | 2 +- sway-types/src/ast.rs | 4 +- sway-types/src/span.rs | 4 + swayfmt/Cargo.toml | 1 - swayfmt/src/comments.rs | 12 +- swayfmt/src/formatter/mod.rs | 24 +- swayfmt/src/formatter/shape.rs | 6 + swayfmt/src/items/item_abi/mod.rs | 11 +- swayfmt/src/items/item_configurable/mod.rs | 46 +-- swayfmt/src/items/item_const.rs | 21 +- swayfmt/src/items/item_enum/mod.rs | 55 ++- swayfmt/src/items/item_fn/mod.rs | 44 ++- swayfmt/src/items/item_impl/mod.rs | 11 +- swayfmt/src/items/item_storage/mod.rs | 30 +- swayfmt/src/items/item_struct/mod.rs | 70 ++-- swayfmt/src/items/item_trait/mod.rs | 21 +- swayfmt/src/items/item_trait_type.rs | 17 +- swayfmt/src/items/item_type_alias.rs | 15 +- swayfmt/src/items/item_use/mod.rs | 87 +++-- swayfmt/src/items/item_use/tests.rs | 41 +++ swayfmt/src/module/mod.rs | 25 +- swayfmt/src/module/submodule.rs | 14 +- swayfmt/src/parse.rs | 11 +- swayfmt/src/utils/language/attribute.rs | 30 +- swayfmt/src/utils/language/expr/abi_cast.rs | 4 +- swayfmt/src/utils/language/expr/asm_block.rs | 17 +- swayfmt/src/utils/language/expr/assignable.rs | 33 +- .../src/utils/language/expr/collections.rs | 15 +- .../src/utils/language/expr/conditional.rs | 37 +- swayfmt/src/utils/language/expr/mod.rs | 197 +++++------ .../src/utils/language/expr/struct_field.rs | 16 +- swayfmt/src/utils/language/literal.rs | 56 ++- swayfmt/src/utils/language/path.rs | 33 +- swayfmt/src/utils/language/pattern.rs | 29 +- swayfmt/src/utils/language/punctuated.rs | 41 +-- swayfmt/src/utils/language/statement.rs | 27 +- swayfmt/src/utils/language/ty.rs | 115 +++---- swayfmt/src/utils/language/where_clause.rs | 25 +- swayfmt/src/utils/map/byte_span.rs | 4 +- swayfmt/src/utils/map/newline.rs | 7 +- swayfmt/tests/mod.rs | 322 +++++++++++++++++- 52 files changed, 1100 insertions(+), 651 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d6df20e9c24..d3c21b0d08b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7869,7 +7869,6 @@ dependencies = [ "serde_ignored", "similar", "sway-ast", - "sway-core", "sway-error", "sway-parse", "sway-types", diff --git a/examples/advanced_storage_variables/src/main.sw b/examples/advanced_storage_variables/src/main.sw index 75054ab7149..c1afb9f4790 100644 --- a/examples/advanced_storage_variables/src/main.sw +++ b/examples/advanced_storage_variables/src/main.sw @@ -1,6 +1,6 @@ contract; -use std::{bytes::Bytes, string::String,}; +use std::{bytes::Bytes, string::String}; // ANCHOR: temp_hash_import use std::hash::Hash; diff --git a/examples/converting_types/src/byte_arrays.sw b/examples/converting_types/src/byte_arrays.sw index d98931c163f..41c1ef543ba 100644 --- a/examples/converting_types/src/byte_arrays.sw +++ b/examples/converting_types/src/byte_arrays.sw @@ -1,7 +1,7 @@ library; // ANCHOR: to_byte_array_import -use std::array_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*,}; +use std::array_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*}; // ANCHOR_END: to_byte_array_import pub fn to_byte_array() { diff --git a/examples/converting_types/src/bytes.sw b/examples/converting_types/src/bytes.sw index 5460352b10d..3537eca8a51 100644 --- a/examples/converting_types/src/bytes.sw +++ b/examples/converting_types/src/bytes.sw @@ -1,7 +1,7 @@ library; // ANCHOR: to_bytes_import -use std::{bytes::Bytes, bytes_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*,}}; +use std::{bytes::Bytes, bytes_conversions::{b256::*, u16::*, u256::*, u32::*, u64::*}}; // ANCHOR_END: to_bytes_import pub fn convert_to_bytes() { diff --git a/examples/wallet_smart_contract/src/main.sw b/examples/wallet_smart_contract/src/main.sw index 9343e818bdf..5ab406a6f3c 100644 --- a/examples/wallet_smart_contract/src/main.sw +++ b/examples/wallet_smart_contract/src/main.sw @@ -1,7 +1,7 @@ // ANCHOR: full_wallet contract; -use std::{asset::transfer, call_frames::msg_asset_id, context::msg_amount,}; +use std::{asset::transfer, call_frames::msg_asset_id, context::msg_amount}; // ANCHOR: abi_import use wallet_abi::Wallet; diff --git a/forc-plugins/forc-fmt/src/main.rs b/forc-plugins/forc-fmt/src/main.rs index 6a818ff245a..fffefbbd840 100644 --- a/forc-plugins/forc-fmt/src/main.rs +++ b/forc-plugins/forc-fmt/src/main.rs @@ -15,7 +15,6 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use sway_core::{BuildConfig, BuildTarget}; use sway_utils::{constants, find_parent_manifest_dir, get_sway_files, is_sway_file}; use swayfmt::Formatter; use taplo::formatter as taplo_fmt; @@ -77,13 +76,8 @@ fn run() -> Result<()> { if let Some(f) = app.file.as_ref() { let file_path = &PathBuf::from(f); - // If we're formatting a single file, find the nearest manifest if within a project. - // Otherwise, we simply provide 'None' to format_file(). - let manifest_file = find_parent_manifest_dir(file_path) - .map(|path| path.join(constants::MANIFEST_FILE_NAME)); - if is_sway_file(file_path) { - format_file(&app, file_path.to_path_buf(), manifest_file, &mut formatter)?; + format_file(&app, file_path.to_path_buf(), &mut formatter)?; return Ok(()); } @@ -142,12 +136,7 @@ fn get_sway_dirs(workspace_dir: PathBuf) -> Vec { /// - Ok(true) if executed successfully and formatted, /// - Ok(false) if executed successfully and not formatted, /// - Err if it fails to execute at all. -fn format_file( - app: &App, - file: PathBuf, - manifest_file: Option, - formatter: &mut Formatter, -) -> Result { +fn format_file(app: &App, file: PathBuf, formatter: &mut Formatter) -> Result { let file = file.canonicalize()?; if is_file_dirty(&file) { bail!( @@ -160,14 +149,7 @@ fn format_file( if let Ok(file_content) = fs::read_to_string(&file) { let mut edited = false; let file_content: Arc = Arc::from(file_content); - let build_config = manifest_file.map(|f| { - BuildConfig::root_from_file_name_and_manifest_path( - file.clone(), - f, - BuildTarget::default(), - ) - }); - match Formatter::format(formatter, file_content.clone(), build_config.as_ref()) { + match Formatter::format(formatter, file_content.clone()) { Ok(formatted_content) => { if app.check { if *file_content != formatted_content { @@ -213,12 +195,7 @@ fn format_workspace_at_dir(app: &App, workspace: &WorkspaceManifestFile, dir: &P for entry in read_dir.filter_map(|res| res.ok()) { let path = entry.path(); if is_sway_file(&path) { - format_file( - app, - path, - Some(workspace.dir().to_path_buf()), - &mut formatter, - )?; + format_file(app, path, &mut formatter)?; } } } @@ -295,7 +272,7 @@ fn format_pkg_at_dir(app: &App, dir: &Path, formatter: &mut Formatter) -> Result let mut contains_edits = false; for file in files { - contains_edits |= format_file(app, file, Some(manifest_file.clone()), formatter)?; + contains_edits |= format_file(app, file, formatter)?; } // format manifest using taplo formatter contains_edits |= format_manifest(app, manifest_file)?; diff --git a/sway-ast/src/expr/mod.rs b/sway-ast/src/expr/mod.rs index f27798c2d7f..52999a63dcf 100644 --- a/sway-ast/src/expr/mod.rs +++ b/sway-ast/src/expr/mod.rs @@ -306,6 +306,18 @@ impl ReassignmentOpVariant { ReassignmentOpVariant::ShrEquals => "rsh", } } + + pub fn as_str(&self) -> &'static str { + match self { + ReassignmentOpVariant::Equals => EqToken::AS_STR, + ReassignmentOpVariant::AddEquals => AddEqToken::AS_STR, + ReassignmentOpVariant::SubEquals => SubEqToken::AS_STR, + ReassignmentOpVariant::MulEquals => StarEqToken::AS_STR, + ReassignmentOpVariant::DivEquals => DivEqToken::AS_STR, + ReassignmentOpVariant::ShlEquals => ShlEqToken::AS_STR, + ReassignmentOpVariant::ShrEquals => ShrEqToken::AS_STR, + } + } } #[derive(Clone, Debug, Serialize)] diff --git a/sway-ast/src/expr/op_code.rs b/sway-ast/src/expr/op_code.rs index acfc54411eb..83c6d319743 100644 --- a/sway-ast/src/expr/op_code.rs +++ b/sway-ast/src/expr/op_code.rs @@ -123,6 +123,14 @@ macro_rules! define_op_codes ( } } + pub fn op_code_as_str(&self) -> &'static str { + match self { + $(Instruction::$op_name { .. } => { + $s + },)* + } + } + #[allow(clippy::vec_init_then_push)] pub fn register_arg_idents(&self) -> Vec { match self { diff --git a/sway-ast/src/keywords.rs b/sway-ast/src/keywords.rs index 0e3ea802da0..236b07cb8aa 100644 --- a/sway-ast/src/keywords.rs +++ b/sway-ast/src/keywords.rs @@ -98,10 +98,13 @@ pub trait Token: Spanned + Sized { /// Punctuations that will not follow the token. const NOT_FOLLOWED_BY: &'static [PunctKind]; + + /// What the string representation of the token is when lexing. + const AS_STR: &'static str; } macro_rules! define_token ( - ($ty_name:ident, $description:literal, [$($punct_kinds:ident),*], [$($not_followed_by:ident),*]) => { + ($ty_name:ident, $description:literal, $as_str:literal, [$($punct_kinds:ident),*], [$($not_followed_by:ident),*]) => { #[derive(Clone, Debug, Serialize)] pub struct $ty_name { span: Span, @@ -132,6 +135,7 @@ macro_rules! define_token ( const PUNCT_KINDS: &'static [PunctKind] = &[$(PunctKind::$punct_kinds,)*]; const NOT_FOLLOWED_BY: &'static [PunctKind] = &[$(PunctKind::$not_followed_by,)*]; + const AS_STR: &'static str = $as_str; } impl From<$ty_name> for Ident { @@ -142,93 +146,121 @@ macro_rules! define_token ( }; ); -define_token!(SemicolonToken, "a semicolon", [Semicolon], []); +define_token!(SemicolonToken, "a semicolon", ";", [Semicolon], []); define_token!( ForwardSlashToken, "a forward slash", + "/", [ForwardSlash], [Equals] ); define_token!( DoubleColonToken, "a double colon (::)", + "::", [Colon, Colon], [Colon] ); -define_token!(StarToken, "an asterisk (*)", [Star], [Equals]); -define_token!(DoubleStarToken, "`**`", [Star, Star], []); -define_token!(CommaToken, "a comma", [Comma], []); -define_token!(ColonToken, "a colon", [Colon], [Colon]); +define_token!(StarToken, "an asterisk (*)", "*", [Star], [Equals]); +define_token!(DoubleStarToken, "`**`", "**", [Star, Star], []); +define_token!(CommaToken, "a comma", ",", [Comma], []); +define_token!(ColonToken, "a colon", ":", [Colon], [Colon]); define_token!( RightArrowToken, "`->`", + "->", [Sub, GreaterThan], [GreaterThan, Equals] ); -define_token!(LessThanToken, "`<`", [LessThan], [LessThan, Equals]); +define_token!(LessThanToken, "`<`", "<", [LessThan], [LessThan, Equals]); define_token!( GreaterThanToken, "`>`", + ">", [GreaterThan], [GreaterThan, Equals] ); -define_token!(OpenAngleBracketToken, "`<`", [LessThan], []); -define_token!(CloseAngleBracketToken, "`>`", [GreaterThan], []); -define_token!(EqToken, "`=`", [Equals], [GreaterThan, Equals]); -define_token!(AddEqToken, "`+=`", [Add, Equals], []); -define_token!(SubEqToken, "`-=`", [Sub, Equals], []); -define_token!(StarEqToken, "`*=`", [Star, Equals], []); -define_token!(DivEqToken, "`/=`", [ForwardSlash, Equals], []); -define_token!(ShlEqToken, "`<<=`", [LessThan, LessThan, Equals], []); -define_token!(ShrEqToken, "`>>=`", [GreaterThan, GreaterThan, Equals], []); +define_token!(OpenAngleBracketToken, "`<`", "<", [LessThan], []); +define_token!(CloseAngleBracketToken, "`>`", ">", [GreaterThan], []); +define_token!(EqToken, "`=`", "=", [Equals], [GreaterThan, Equals]); +define_token!(AddEqToken, "`+=`", "+=", [Add, Equals], []); +define_token!(SubEqToken, "`-=`", "-=", [Sub, Equals], []); +define_token!(StarEqToken, "`*=`", "*=", [Star, Equals], []); +define_token!(DivEqToken, "`/=`", "/=", [ForwardSlash, Equals], []); +define_token!(ShlEqToken, "`<<=`", "<<=", [LessThan, LessThan, Equals], []); +define_token!( + ShrEqToken, + "`>>=`", + ">>=", + [GreaterThan, GreaterThan, Equals], + [] +); define_token!( FatRightArrowToken, "`=>`", + "=>", [Equals, GreaterThan], [GreaterThan, Equals] ); -define_token!(DotToken, "`.`", [Dot], []); -define_token!(DoubleDotToken, "`..`", [Dot, Dot], [Dot]); -define_token!(BangToken, "`!`", [Bang], [Equals]); -define_token!(PercentToken, "`%`", [Percent], []); -define_token!(AddToken, "`+`", [Add], [Equals]); -define_token!(SubToken, "`-`", [Sub], [Equals]); +define_token!(DotToken, "`.`", ".", [Dot], []); +define_token!(DoubleDotToken, "`..`", "..", [Dot, Dot], [Dot]); +define_token!(BangToken, "`!`", "!", [Bang], [Equals]); +define_token!(PercentToken, "`%`", "%", [Percent], []); +define_token!(AddToken, "`+`", "+", [Add], [Equals]); +define_token!(SubToken, "`-`", "-", [Sub], [Equals]); define_token!( ShrToken, "`>>`", + ">>", [GreaterThan, GreaterThan], [GreaterThan, Equals] ); -define_token!(ShlToken, "`<<`", [LessThan, LessThan], [LessThan, Equals]); -define_token!(AmpersandToken, "`&`", [Ampersand], [Ampersand]); -define_token!(CaretToken, "`^`", [Caret], []); -define_token!(PipeToken, "`|`", [Pipe], [Pipe]); +define_token!( + ShlToken, + "`<<`", + "<<", + [LessThan, LessThan], + [LessThan, Equals] +); +define_token!(AmpersandToken, "`&`", "&", [Ampersand], [Ampersand]); +define_token!(CaretToken, "`^`", "^", [Caret], []); +define_token!(PipeToken, "`|`", "|", [Pipe], [Pipe]); define_token!( DoubleEqToken, "`==`", + "==", [Equals, Equals], [Equals, GreaterThan] ); -define_token!(BangEqToken, "`!=`", [Bang, Equals], [Equals, GreaterThan]); +define_token!( + BangEqToken, + "`!=`", + "!=", + [Bang, Equals], + [Equals, GreaterThan] +); define_token!( GreaterThanEqToken, "`>=`", + ">=", [GreaterThan, Equals], [Equals, GreaterThan] ); define_token!( LessThanEqToken, "`<=`", + "<=", [LessThan, Equals], [Equals, GreaterThan] ); define_token!( DoubleAmpersandToken, "`&&`", + "&&", [Ampersand, Ampersand], [Ampersand] ); -define_token!(DoublePipeToken, "`||`", [Pipe, Pipe], [Pipe]); -define_token!(UnderscoreToken, "`_`", [Underscore], [Underscore]); -define_token!(HashToken, "`#`", [Sharp], []); -define_token!(HashBangToken, "`#!`", [Sharp, Bang], []); +define_token!(DoublePipeToken, "`||`", "||", [Pipe, Pipe], [Pipe]); +define_token!(UnderscoreToken, "`_`", "_", [Underscore], [Underscore]); +define_token!(HashToken, "`#`", "#", [Sharp], []); +define_token!(HashBangToken, "`#!`", "#!", [Sharp, Bang], []); diff --git a/sway-ast/src/punctuated.rs b/sway-ast/src/punctuated.rs index 9aa88c64495..af6cf423f89 100644 --- a/sway-ast/src/punctuated.rs +++ b/sway-ast/src/punctuated.rs @@ -20,6 +20,19 @@ impl Punctuated { final_value_opt: Some(Box::new(value)), } } + + /// Returns true if the [Punctuated] ends with the punctuation token. + /// E.g., `fn fun(x: u64, y: u64,)`. + pub fn has_trailing_punctuation(&self) -> bool { + !self.value_separator_pairs.is_empty() && self.final_value_opt.is_none() + } + + /// Returns true if the [Punctuated] has neither value separator pairs, + /// nor the final value. + /// E.g., `fn fun()`. + pub fn is_empty(&self) -> bool { + self.value_separator_pairs.is_empty() && self.final_value_opt.is_none() + } } impl IntoIterator for Punctuated { diff --git a/sway-lib-std/src/prelude.sw b/sway-lib-std/src/prelude.sw index f8d42cf0044..b86eb26ec31 100644 --- a/sway-lib-std/src/prelude.sw +++ b/sway-lib-std/src/prelude.sw @@ -27,7 +27,7 @@ pub use ::revert::{require, revert, revert_with_log}; pub use ::convert::From; // Primitive conversions -pub use ::primitive_conversions::{b256::*, str::*, u16::*, u256::*, u32::*, u64::*, u8::*,}; +pub use ::primitive_conversions::{b256::*, str::*, u16::*, u256::*, u32::*, u64::*, u8::*}; // Logging pub use ::logging::log; diff --git a/sway-lsp/src/capabilities/formatting.rs b/sway-lsp/src/capabilities/formatting.rs index e4b1596991b..06d5b58b59b 100644 --- a/sway-lsp/src/capabilities/formatting.rs +++ b/sway-lsp/src/capabilities/formatting.rs @@ -24,7 +24,7 @@ pub fn get_page_text_edit( ) -> Result { // we only format if code is correct let formatted_code = formatter - .format(text.clone(), None) + .format(text.clone()) .map_err(LanguageServerError::FormatError)?; let text_lines_count = text.split('\n').count(); diff --git a/sway-types/src/ast.rs b/sway-types/src/ast.rs index b137ad9bd09..4db1c3b6c7c 100644 --- a/sway-types/src/ast.rs +++ b/sway-types/src/ast.rs @@ -6,14 +6,14 @@ pub enum Delimiter { } impl Delimiter { - pub fn as_open_char(self) -> char { + pub const fn as_open_char(self) -> char { match self { Delimiter::Parenthesis => '(', Delimiter::Brace => '{', Delimiter::Bracket => '[', } } - pub fn as_close_char(self) -> char { + pub const fn as_close_char(self) -> char { match self { Delimiter::Parenthesis => ')', Delimiter::Brace => '}', diff --git a/sway-types/src/span.rs b/sway-types/src/span.rs index 99536c080ee..5ea94b3c2a0 100644 --- a/sway-types/src/span.rs +++ b/sway-types/src/span.rs @@ -203,6 +203,10 @@ impl Span { pub fn is_dummy(&self) -> bool { self.eq(&DUMMY_SPAN) } + + pub fn is_empty(&self) -> bool { + self.start == self.end + } } impl fmt::Debug for Span { diff --git a/swayfmt/Cargo.toml b/swayfmt/Cargo.toml index 85749c30ab2..71eb6d60824 100644 --- a/swayfmt/Cargo.toml +++ b/swayfmt/Cargo.toml @@ -16,7 +16,6 @@ ropey.workspace = true serde = { workspace = true, features = ["derive"] } serde_ignored.workspace = true sway-ast.workspace = true -sway-core.workspace = true sway-error.workspace = true sway-parse.workspace = true sway-types.workspace = true diff --git a/swayfmt/src/comments.rs b/swayfmt/src/comments.rs index e891ca15666..dfd5590d7f7 100644 --- a/swayfmt/src/comments.rs +++ b/swayfmt/src/comments.rs @@ -76,15 +76,18 @@ fn write_trailing_comment( } /// Given a range, writes comments contained within the range. This function -/// removes comments that are written here from the CommentMap for later use. +/// removes comments that are written here from the [CommentMap] for later use. /// -/// Most comment formatting should be done using `rewrite_with_comments` in +/// Most comment formatting should be done using [rewrite_with_comments] in /// the context of the AST, but in some cases (eg. at the end of module) we require this function. /// /// Returns: /// `Ok(true)` on successful execution with comments written, /// `Ok(false)` on successful execution and if there are no comments within the given range, /// `Err` if a FormatterError was encountered. +/// +/// The `range` can be an empty [Range], or have its start being greater then its end. +/// This is to support formatting arbitrary lexed trees, that are not necessarily backed by source code. pub fn write_comments( formatted_code: &mut FormattedCode, range: Range, @@ -127,7 +130,7 @@ pub fn write_comments( // We do a trim and truncate here to ensure that only a single whitespace separates // the inlined comment from the previous token. formatted_code.truncate(formatted_code.trim_end().len()); - write!(formatted_code, " {} ", comment.span().as_str(),)?; + write!(formatted_code, " {} ", comment.span().as_str())?; } CommentKind::Multilined => { write!( @@ -157,6 +160,9 @@ pub fn write_comments( /// This takes a given AST node's unformatted span, its leaf spans and its formatted code (a string) and /// parses the equivalent formatted version to get its leaf spans. We traverse the spaces between both /// formatted and unformatted leaf spans to find possible comments and inserts them between. +/// +/// The `unformatted_span` can be an empty [Span]. This is to support formatting arbitrary lexed trees, +/// that are not necessarily backed by source code. pub fn rewrite_with_comments( formatter: &mut Formatter, unformatted_span: Span, diff --git a/swayfmt/src/formatter/mod.rs b/swayfmt/src/formatter/mod.rs index 96ead29f09d..0802d0c7727 100644 --- a/swayfmt/src/formatter/mod.rs +++ b/swayfmt/src/formatter/mod.rs @@ -8,7 +8,8 @@ pub use crate::{ error::{ConfigError, FormatterError}, }; use std::{borrow::Cow, fmt::Write, path::Path, sync::Arc}; -use sway_core::BuildConfig; +use sway_ast::attribute::Annotated; +use sway_ast::Module; use sway_types::{SourceEngine, Spanned}; pub(crate) mod shape; @@ -80,10 +81,14 @@ impl Formatter { Ok(self) } - pub fn format( + pub fn format(&mut self, src: Arc) -> Result { + let annotated_module = parse_file(src)?; + self.format_module(&annotated_module) + } + + pub fn format_module( &mut self, - src: Arc, - build_config: Option<&BuildConfig>, + annotated_module: &Annotated, ) -> Result { // apply the width heuristics settings from the `Config` self.shape.apply_width_heuristics( @@ -92,9 +97,11 @@ impl Formatter { .heuristics_pref .to_width_heuristics(self.config.whitespace.max_width), ); - let src = src.trim(); - let path = build_config.map(|build_config| build_config.canonical_root_module()); + // Get the original trimmed source code. + let module_kind_span = annotated_module.value.kind.span(); + let src = module_kind_span.src().trim(); + // Formatted code will be pushed here with raw newline style. // Which means newlines are not converted into system-specific versions until `apply_newline_style()`. // Use the length of src as a hint of the memory size needed for `raw_formatted_code`, @@ -103,7 +110,6 @@ impl Formatter { self.with_comments_context(src)?; - let annotated_module = parse_file(&self.source_engine, Arc::from(src), path.clone())?; annotated_module.format(&mut raw_formatted_code, self)?; let mut formatted_code = String::from(&raw_formatted_code); @@ -117,14 +123,13 @@ impl Formatter { // Add newline sequences handle_newlines( - &self.source_engine, Arc::from(src), &annotated_module.value, Arc::from(formatted_code.clone()), - path, &mut formatted_code, self, )?; + // Replace newlines with specified `NewlineStyle` apply_newline_style( self.config.whitespace.newline_style, @@ -137,6 +142,7 @@ impl Formatter { Ok(formatted_code) } + pub(crate) fn with_shape(&mut self, new_shape: Shape, f: F) -> O where F: FnOnce(&mut Self) -> O, diff --git a/swayfmt/src/formatter/shape.rs b/swayfmt/src/formatter/shape.rs index 0630c73e851..9c9def85548 100644 --- a/swayfmt/src/formatter/shape.rs +++ b/swayfmt/src/formatter/shape.rs @@ -163,6 +163,12 @@ pub(crate) enum LineStyle { Multiline, } +impl LineStyle { + pub fn is_multiline(&self) -> bool { + matches!(self, Self::Multiline) + } +} + impl Default for LineStyle { fn default() -> Self { Self::Normal diff --git a/swayfmt/src/items/item_abi/mod.rs b/swayfmt/src/items/item_abi/mod.rs index ddf1fabe22f..e47fa3b269d 100644 --- a/swayfmt/src/items/item_abi/mod.rs +++ b/swayfmt/src/items/item_abi/mod.rs @@ -8,7 +8,10 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::{keywords::Token, ItemAbi}; +use sway_ast::{ + keywords::{AbiToken, ColonToken, Keyword, Token}, + ItemAbi, +}; use sway_types::{ast::Delimiter, Spanned}; #[cfg(test)] @@ -22,12 +25,12 @@ impl Format for ItemAbi { ) -> Result<(), FormatterError> { let start_len = formatted_code.len(); // `abi name` - write!(formatted_code, "{} ", self.abi_token.span().as_str())?; + write!(formatted_code, "{} ", AbiToken::AS_STR)?; self.name.format(formatted_code, formatter)?; // ` : super_trait + super_trait` - if let Some((colon_token, traits)) = &self.super_traits { - write!(formatted_code, " {} ", colon_token.ident().as_str())?; + if let Some((_colon_token, traits)) = &self.super_traits { + write!(formatted_code, " {} ", ColonToken::AS_STR)?; traits.format(formatted_code, formatter)?; } diff --git a/swayfmt/src/items/item_configurable/mod.rs b/swayfmt/src/items/item_configurable/mod.rs index 550b8916d8d..c870b537a1d 100644 --- a/swayfmt/src/items/item_configurable/mod.rs +++ b/swayfmt/src/items/item_configurable/mod.rs @@ -10,7 +10,10 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::{keywords::Token, ConfigurableField, ItemConfigurable}; +use sway_ast::{ + keywords::{ColonToken, ConfigurableToken, EqToken, Keyword, Token}, + CommaToken, ConfigurableField, ItemConfigurable, +}; use sway_types::{ast::Delimiter, Spanned}; #[cfg(test)] @@ -28,11 +31,7 @@ impl Format for ItemConfigurable { .with_code_line_from(LineStyle::Multiline, ExprKind::default()), |formatter| -> Result<(), FormatterError> { // Add configurable token - write!( - formatted_code, - "{}", - self.configurable_token.span().as_str() - )?; + write!(formatted_code, "{}", ConfigurableToken::AS_STR)?; let fields = self.fields.get(); // Handle opening brace @@ -44,19 +43,18 @@ impl Format for ItemConfigurable { match formatter.config.structures.field_alignment { FieldAlignment::AlignFields(configurable_field_align_threshold) => { writeln!(formatted_code)?; - let value_pairs = &fields + let configurable_fields = &fields .value_separator_pairs .iter() - // TODO: Handle annotations instead of stripping them - .map(|(configurable_field, comma_token)| { - (&configurable_field.value, comma_token) - }) + // TODO: Handle annotations instead of stripping them. + // See: https://github.com/FuelLabs/sway/issues/6802 + .map(|(configurable_field, _comma_token)| &configurable_field.value) .collect::>(); // In first iteration we are going to be collecting the lengths of the // struct fields. - let field_length: Vec = value_pairs + let field_length: Vec = configurable_fields .iter() - .map(|(configurable_field, _)| configurable_field.name.as_str().len()) + .map(|configurable_field| configurable_field.name.as_str().len()) .collect(); // Find the maximum length in the `field_length` vector that is still @@ -72,11 +70,10 @@ impl Format for ItemConfigurable { } }); - let value_pairs_iter = value_pairs.iter().enumerate(); - for (field_index, (configurable_field, comma_token)) in - value_pairs_iter.clone() + for (field_index, configurable_field) in + configurable_fields.iter().enumerate() { - write!(formatted_code, "{}", &formatter.indent_to_str()?)?; + write!(formatted_code, "{}", formatter.indent_to_str()?)?; // Add name configurable_field.name.format(formatted_code, formatter)?; @@ -94,24 +91,17 @@ impl Format for ItemConfigurable { } } // Add `:`, `ty` & `CommaToken` - write!( - formatted_code, - " {} ", - configurable_field.colon_token.ident().as_str(), - )?; + write!(formatted_code, " {} ", ColonToken::AS_STR)?; configurable_field.ty.format(formatted_code, formatter)?; - write!( - formatted_code, - " {} ", - configurable_field.eq_token.ident().as_str() - )?; + write!(formatted_code, " {} ", EqToken::AS_STR)?; configurable_field .initializer .format(formatted_code, formatter)?; - writeln!(formatted_code, "{}", comma_token.ident().as_str())?; + writeln!(formatted_code, "{}", CommaToken::AS_STR)?; } if let Some(final_value) = &fields.final_value_opt { final_value.format(formatted_code, formatter)?; + writeln!(formatted_code)?; } } FieldAlignment::Off => fields.format(formatted_code, formatter)?, diff --git a/swayfmt/src/items/item_const.rs b/swayfmt/src/items/item_const.rs index e853f2a97dc..0d78ef24eeb 100644 --- a/swayfmt/src/items/item_const.rs +++ b/swayfmt/src/items/item_const.rs @@ -4,7 +4,10 @@ use crate::{ utils::map::byte_span::{ByteSpan, LeafSpans}, }; use std::fmt::Write; -use sway_ast::{keywords::Token, ItemConst}; +use sway_ast::{ + keywords::{ColonToken, ConstToken, EqToken, Keyword, SemicolonToken, Token}, + ItemConst, PubToken, +}; use sway_types::Spanned; impl Format for ItemConst { @@ -17,26 +20,26 @@ impl Format for ItemConst { let start_len = formatted_code.len(); // Check if visibility token exists if so add it. - if let Some(visibility_token) = &self.visibility { - write!(formatted_code, "{} ", visibility_token.span().as_str())?; + if self.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } // Add the const token - write!(formatted_code, "{} ", self.const_token.span().as_str())?; + write!(formatted_code, "{} ", ConstToken::AS_STR)?; // Add name of the const self.name.format(formatted_code, formatter)?; // Check if ty exists - if let Some((colon_token, ty)) = &self.ty_opt { + if let Some((_colon_token, ty)) = &self.ty_opt { // Add colon - write!(formatted_code, "{} ", colon_token.ident().as_str())?; + write!(formatted_code, "{} ", ColonToken::AS_STR)?; ty.format(formatted_code, formatter)?; } // Check if ` = ` exists - if let Some(eq_token) = &self.eq_token_opt { - write!(formatted_code, " {} ", eq_token.ident().as_str())?; + if self.eq_token_opt.is_some() { + write!(formatted_code, " {} ", EqToken::AS_STR)?; } // Check if expression exists @@ -44,7 +47,7 @@ impl Format for ItemConst { expr.format(formatted_code, formatter)?; } - write!(formatted_code, "{}", self.semicolon_token.ident().as_str())?; + write!(formatted_code, "{}", SemicolonToken::AS_STR)?; rewrite_with_comments::( formatter, diff --git a/swayfmt/src/items/item_enum/mod.rs b/swayfmt/src/items/item_enum/mod.rs index 561c2b436f6..11fa3d6c78d 100644 --- a/swayfmt/src/items/item_enum/mod.rs +++ b/swayfmt/src/items/item_enum/mod.rs @@ -11,11 +11,11 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::ItemEnum; -use sway_types::{ - ast::{Delimiter, PunctKind}, - Spanned, +use sway_ast::{ + keywords::{ColonToken, EnumToken, Keyword, Token}, + CommaToken, ItemEnum, PubToken, }; +use sway_types::{ast::Delimiter, Spanned}; #[cfg(test)] mod tests; @@ -34,11 +34,11 @@ impl Format for ItemEnum { // Required for comment formatting let start_len = formatted_code.len(); // If there is a visibility token add it to the formatted_code with a ` ` after it. - if let Some(visibility) = &self.visibility { - write!(formatted_code, "{} ", visibility.span().as_str())?; + if self.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } // Add enum token and name - write!(formatted_code, "{} ", self.enum_token.span().as_str())?; + write!(formatted_code, "{} ", EnumToken::AS_STR)?; self.name.format(formatted_code, formatter)?; // Format `GenericParams`, if any if let Some(generics) = &self.generics { @@ -55,21 +55,22 @@ impl Format for ItemEnum { match formatter.config.structures.field_alignment { FieldAlignment::AlignFields(enum_variant_align_threshold) => { writeln!(formatted_code)?; - let value_pairs = &fields + let type_fields = &fields .value_separator_pairs .iter() - // TODO: Handle annotations instead of stripping them - .map(|pair| (&pair.0.value, &pair.1)) + // TODO: Handle annotations instead of stripping them. + // See: https://github.com/FuelLabs/sway/issues/6802 + .map(|(type_field, _comma_token)| &type_field.value) .collect::>(); // In first iteration we are going to be collecting the lengths of the enum variants. - let variant_length: Vec = value_pairs + let variants_lengths: Vec = type_fields .iter() - .map(|(type_field, _)| type_field.name.as_str().len()) + .map(|type_field| type_field.name.as_str().len()) .collect(); - // Find the maximum length in the variant_length vector that is still smaller than enum_field_align_threshold. + // Find the maximum length that is still smaller than the align threshold. let mut max_valid_variant_length = 0; - variant_length.iter().for_each(|length| { + variants_lengths.iter().for_each(|length| { if *length > max_valid_variant_length && *length < enum_variant_align_threshold { @@ -77,13 +78,12 @@ impl Format for ItemEnum { } }); - let value_pairs_iter = value_pairs.iter().enumerate(); - for (var_index, (type_field, comma_token)) in value_pairs_iter.clone() { - write!(formatted_code, "{}", &formatter.indent_to_str()?)?; + for (var_index, type_field) in type_fields.iter().enumerate() { + write!(formatted_code, "{}", formatter.indent_to_str()?)?; // Add name type_field.name.format(formatted_code, formatter)?; - let current_variant_length = variant_length[var_index]; + let current_variant_length = variants_lengths[var_index]; if current_variant_length < max_valid_variant_length { // We need to add alignment between : and ty // max_valid_variant_length: the length of the variant that we are taking as a reference to align @@ -96,23 +96,13 @@ impl Format for ItemEnum { } } // Add `:`, ty & `CommaToken` - write!( - formatted_code, - " {} ", - type_field.colon_token.span().as_str(), - )?; + write!(formatted_code, " {} ", ColonToken::AS_STR)?; type_field.ty.format(formatted_code, formatter)?; - writeln!(formatted_code, "{}", comma_token.span().as_str())?; + writeln!(formatted_code, "{}", CommaToken::AS_STR)?; } if let Some(final_value) = &fields.final_value_opt { - // TODO: Handle annotation - let final_value = &final_value.value; - writeln!( - formatted_code, - "{}{}", - final_value.span().as_str(), - PunctKind::Comma.as_char(), - )?; + final_value.format(formatted_code, formatter)?; + writeln!(formatted_code)?; } } FieldAlignment::Off => fields.format(formatted_code, formatter)?, @@ -164,6 +154,7 @@ impl CurlyBrace for ItemEnum { Ok(()) } } + impl LeafSpans for ItemEnum { fn leaf_spans(&self) -> Vec { let mut collected_spans = Vec::new(); diff --git a/swayfmt/src/items/item_fn/mod.rs b/swayfmt/src/items/item_fn/mod.rs index 596f37efad2..235fc5a2179 100644 --- a/swayfmt/src/items/item_fn/mod.rs +++ b/swayfmt/src/items/item_fn/mod.rs @@ -11,8 +11,10 @@ use crate::{ }; use std::fmt::Write; use sway_ast::{ - keywords::{MutToken, RefToken, SelfToken, Token}, - FnArg, FnArgs, FnSignature, ItemFn, + keywords::{ + ColonToken, FnToken, Keyword, MutToken, RefToken, RightArrowToken, SelfToken, Token, + }, + CommaToken, FnArg, FnArgs, FnSignature, ItemFn, PubToken, }; use sway_types::{ast::Delimiter, Spanned}; @@ -152,11 +154,11 @@ fn format_fn_sig( formatter: &mut Formatter, ) -> Result<(), FormatterError> { // `pub ` - if let Some(visibility_token) = &fn_sig.visibility { - write!(formatted_code, "{} ", visibility_token.span().as_str())?; + if fn_sig.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } // `fn ` + name - write!(formatted_code, "{} ", fn_sig.fn_token.span().as_str())?; + write!(formatted_code, "{} ", FnToken::AS_STR)?; fn_sig.name.format(formatted_code, formatter)?; // `` if let Some(generics) = &fn_sig.generics { @@ -169,12 +171,8 @@ fn format_fn_sig( // `)` FnSignature::close_parenthesis(formatted_code, formatter)?; // `return_type_opt` - if let Some((right_arrow, ty)) = &fn_sig.return_type_opt { - write!( - formatted_code, - " {} ", - right_arrow.ident().as_str() // `->` - )?; + if let Some((_right_arrow, ty)) = &fn_sig.return_type_opt { + write!(formatted_code, " {} ", RightArrowToken::AS_STR)?; ty.format(formatted_code, formatter)?; // `Ty` } // `WhereClause` @@ -196,7 +194,7 @@ fn format_fn_args( FnArgs::Static(args) => match formatter.shape.code_line.line_style { LineStyle::Multiline => { formatter.shape.code_line.update_expr_new_line(true); - if !args.value_separator_pairs.is_empty() || args.final_value_opt.is_some() { + if !args.is_empty() { formatter.indent(); args.format(formatted_code, formatter)?; formatter.unindent(); @@ -218,9 +216,9 @@ fn format_fn_args( write!(formatted_code, "\n{}", formatter.indent_to_str()?)?; format_self(self_token, ref_self, mutable_self, formatted_code)?; // `args_opt` - if let Some((comma, args)) = args_opt { + if let Some((_comma, args)) = args_opt { // `, ` - write!(formatted_code, "{}", comma.ident().as_str())?; + write!(formatted_code, "{}", CommaToken::AS_STR)?; // `Punctuated` args.format(formatted_code, formatter)?; } @@ -228,9 +226,9 @@ fn format_fn_args( _ => { format_self(self_token, ref_self, mutable_self, formatted_code)?; // `args_opt` - if let Some((comma, args)) = args_opt { + if let Some((_comma, args)) = args_opt { // `, ` - write!(formatted_code, "{} ", comma.ident().as_str())?; + write!(formatted_code, "{} ", CommaToken::AS_STR)?; // `Punctuated` args.format(formatted_code, formatter)?; } @@ -243,21 +241,21 @@ fn format_fn_args( } fn format_self( - self_token: &SelfToken, + _self_token: &SelfToken, ref_self: &Option, mutable_self: &Option, formatted_code: &mut FormattedCode, ) -> Result<(), FormatterError> { // `ref ` - if let Some(ref_token) = ref_self { - write!(formatted_code, "{} ", ref_token.span().as_str())?; + if ref_self.is_some() { + write!(formatted_code, "{} ", RefToken::AS_STR)?; } // `mut ` - if let Some(mut_token) = mutable_self { - write!(formatted_code, "{} ", mut_token.span().as_str())?; + if mutable_self.is_some() { + write!(formatted_code, "{} ", MutToken::AS_STR)?; } // `self` - write!(formatted_code, "{}", self_token.span().as_str())?; + write!(formatted_code, "{}", SelfToken::AS_STR)?; Ok(()) } @@ -289,7 +287,7 @@ impl Format for FnArg { ) -> Result<(), FormatterError> { self.pattern.format(formatted_code, formatter)?; // `: ` - write!(formatted_code, "{} ", self.colon_token.span().as_str())?; + write!(formatted_code, "{} ", ColonToken::AS_STR)?; write_comments( formatted_code, diff --git a/swayfmt/src/items/item_impl/mod.rs b/swayfmt/src/items/item_impl/mod.rs index 164f157ba2c..fcadf3b040c 100644 --- a/swayfmt/src/items/item_impl/mod.rs +++ b/swayfmt/src/items/item_impl/mod.rs @@ -8,7 +8,10 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::{ItemImpl, ItemImplItem}; +use sway_ast::{ + keywords::{ForToken, ImplToken, Keyword}, + ItemImpl, ItemImplItem, +}; use sway_types::{ast::Delimiter, Spanned}; #[cfg(test)] @@ -23,14 +26,14 @@ impl Format for ItemImpl { // Required for comment formatting let start_len = formatted_code.len(); - write!(formatted_code, "{}", self.impl_token.span().as_str())?; + write!(formatted_code, "{}", ImplToken::AS_STR)?; if let Some(generic_params) = &self.generic_params_opt { generic_params.format(formatted_code, formatter)?; } write!(formatted_code, " ")?; - if let Some((path_type, for_token)) = &self.trait_opt { + if let Some((path_type, _for_token)) = &self.trait_opt { path_type.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", for_token.span().as_str())?; + write!(formatted_code, " {} ", ForToken::AS_STR)?; } self.ty.format(formatted_code, formatter)?; if let Some(where_clause) = &self.where_clause_opt { diff --git a/swayfmt/src/items/item_storage/mod.rs b/swayfmt/src/items/item_storage/mod.rs index c6fa0fdf189..acbcf5937c3 100644 --- a/swayfmt/src/items/item_storage/mod.rs +++ b/swayfmt/src/items/item_storage/mod.rs @@ -11,7 +11,10 @@ use crate::{ }, }; use std::{collections::HashMap, fmt::Write}; -use sway_ast::{keywords::Token, ItemStorage, StorageEntry, StorageField}; +use sway_ast::{ + keywords::{ColonToken, EqToken, Keyword, StorageToken, Token}, + CommaToken, ItemStorage, StorageEntry, StorageField, +}; use sway_types::{ast::Delimiter, IdentUnique, Spanned}; #[cfg(test)] @@ -32,7 +35,7 @@ impl Format for ItemStorage { let start_len = formatted_code.len(); // Add storage token - write!(formatted_code, "{}", self.storage_token.span().as_str())?; + write!(formatted_code, "{}", StorageToken::AS_STR)?; let entries = self.entries.get(); // Handle opening brace @@ -47,7 +50,8 @@ impl Format for ItemStorage { let value_pairs = &entries .value_separator_pairs .iter() - // TODO: Handle annotations instead of stripping them + // TODO: Handle annotations instead of stripping them. + // See: https://github.com/FuelLabs/sway/issues/6802 .map(|(storage_field, comma_token)| (&storage_field.value, comma_token)) .collect::>(); // In first iteration we are going to be collecting the lengths of the @@ -114,7 +118,7 @@ impl Format for ItemStorage { ItemStorage::open_curly_brace(formatted_code, formatter)?; writeln!(formatted_code)?; - for (e, comma_token) in + for (e, _comma_token) in namespace.clone().into_inner().value_separator_pairs { format_entry( @@ -124,7 +128,7 @@ impl Format for ItemStorage { field_lengths, max_valid_field_length, )?; - writeln!(formatted_code, "{}", comma_token.ident().as_str())?; + writeln!(formatted_code, "{}", CommaToken::AS_STR)?; } if let Some(final_value) = &namespace.clone().into_inner().final_value_opt @@ -159,17 +163,9 @@ impl Format for ItemStorage { } } // Add `:`, `ty` & `CommaToken` - write!( - formatted_code, - " {} ", - storage_field.colon_token.ident().as_str(), - )?; + write!(formatted_code, " {} ", ColonToken::AS_STR)?; storage_field.ty.format(formatted_code, formatter)?; - write!( - formatted_code, - " {} ", - storage_field.eq_token.ident().as_str() - )?; + write!(formatted_code, " {} ", EqToken::AS_STR)?; storage_field .initializer .format(formatted_code, formatter)?; @@ -177,7 +173,7 @@ impl Format for ItemStorage { Ok(()) } - for (storage_entry, comma_token) in value_pairs.iter().clone() { + for (storage_entry, _comma_token) in value_pairs.iter().clone() { format_entry( formatted_code, formatter, @@ -185,7 +181,7 @@ impl Format for ItemStorage { &field_lengths, max_valid_field_length, )?; - writeln!(formatted_code, "{}", comma_token.ident().as_str())?; + writeln!(formatted_code, "{}", CommaToken::AS_STR)?; } if let Some(final_value) = &entries.final_value_opt { format_entry( diff --git a/swayfmt/src/items/item_struct/mod.rs b/swayfmt/src/items/item_struct/mod.rs index 8ebefab2898..40946639ebb 100644 --- a/swayfmt/src/items/item_struct/mod.rs +++ b/swayfmt/src/items/item_struct/mod.rs @@ -11,7 +11,10 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::ItemStruct; +use sway_ast::{ + keywords::{ColonToken, Keyword, StructToken, Token}, + CommaToken, ItemStruct, PubToken, +}; use sway_types::{ast::Delimiter, Spanned}; #[cfg(test)] @@ -31,11 +34,11 @@ impl Format for ItemStruct { // Required for comment formatting let start_len = formatted_code.len(); // If there is a visibility token add it to the formatted_code with a ` ` after it. - if let Some(visibility) = &self.visibility { - write!(formatted_code, "{} ", visibility.span().as_str())?; + if self.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } // Add struct token and name - write!(formatted_code, "{} ", self.struct_token.span().as_str())?; + write!(formatted_code, "{} ", StructToken::AS_STR)?; self.name.format(formatted_code, formatter)?; // Format `GenericParams`, if any if let Some(generics) = &self.generics { @@ -52,7 +55,7 @@ impl Format for ItemStruct { // Handle opening brace Self::open_curly_brace(formatted_code, formatter)?; - if fields.final_value_opt.is_none() && fields.value_separator_pairs.is_empty() { + if fields.is_empty() { write_comments(formatted_code, self.span().into(), formatter)?; } @@ -60,74 +63,67 @@ impl Format for ItemStruct { // Determine alignment tactic match formatter.config.structures.field_alignment { - FieldAlignment::AlignFields(enum_variant_align_threshold) => { + FieldAlignment::AlignFields(struct_field_align_threshold) => { writeln!(formatted_code)?; - let value_pairs = &fields + let type_fields = &fields .value_separator_pairs .iter() - // TODO: Handle annotations instead of stripping them - .map(|(type_field, comma_token)| (&type_field.value, comma_token)) + // TODO: Handle annotations instead of stripping them. + // See: https://github.com/FuelLabs/sway/issues/6802 + .map(|(type_field, _comma_token)| &type_field.value) .collect::>(); - // In first iteration we are going to be collecting the lengths of the struct variants. + // In first iteration we are going to be collecting the lengths of the struct fields. // We need to include the `pub` keyword in the length, if the field is public, // together with one space character between the `pub` and the name. - let variant_length: Vec = value_pairs + let fields_lengths: Vec = type_fields .iter() - .map(|(type_field, _)| { + .map(|type_field| { type_field .visibility .as_ref() - // We don't want to hard code the token here to `pub` or just hardcode 4. - // This is in case we introduce e.g. `pub(crate)` one day. - .map_or(0, |token| token.span().as_str().len() + 1) + .map_or(0, |_pub_token| PubToken::AS_STR.len() + 1) + type_field.name.as_str().len() }) .collect(); - // Find the maximum length in the variant_length vector that is still smaller than struct_field_align_threshold. - let mut max_valid_variant_length = 0; - variant_length.iter().for_each(|length| { - if *length > max_valid_variant_length - && *length < enum_variant_align_threshold + // Find the maximum length that is still smaller than the align threshold. + let mut max_valid_field_length = 0; + fields_lengths.iter().for_each(|length| { + if *length > max_valid_field_length + && *length < struct_field_align_threshold { - max_valid_variant_length = *length; + max_valid_field_length = *length; } }); - let value_pairs_iter = value_pairs.iter().enumerate(); - for (var_index, (type_field, comma_token)) in value_pairs_iter.clone() { + for (var_index, type_field) in type_fields.iter().enumerate() { write!(formatted_code, "{}", formatter.indent_to_str()?)?; // If there is a visibility token add it to the formatted_code with a ` ` after it. - if let Some(visibility) = &type_field.visibility { - write!(formatted_code, "{} ", visibility.span().as_str())?; + if type_field.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } // Add name type_field.name.format(formatted_code, formatter)?; - let current_variant_length = variant_length[var_index]; - if current_variant_length < max_valid_variant_length { + let current_field_length = fields_lengths[var_index]; + if current_field_length < max_valid_field_length { // We need to add alignment between : and ty // max_valid_variant_length: the length of the variant that we are taking as a reference to align // current_variant_length: the length of the current variant that we are trying to format let mut required_alignment = - max_valid_variant_length - current_variant_length; + max_valid_field_length - current_field_length; while required_alignment != 0 { write!(formatted_code, " ")?; required_alignment -= 1; } } // Add `:`, ty & `CommaToken` - write!( - formatted_code, - " {} ", - type_field.colon_token.span().as_str(), - )?; + write!(formatted_code, " {} ", ColonToken::AS_STR)?; type_field.ty.format(formatted_code, formatter)?; - writeln!(formatted_code, "{}", comma_token.span().as_str())?; + writeln!(formatted_code, "{}", CommaToken::AS_STR)?; } if let Some(final_value) = &fields.final_value_opt { - // TODO: Handle annotation - let final_value = &final_value.value; - write!(formatted_code, "{}", final_value.span().as_str())?; + final_value.format(formatted_code, formatter)?; + writeln!(formatted_code)?; } } FieldAlignment::Off => { diff --git a/swayfmt/src/items/item_trait/mod.rs b/swayfmt/src/items/item_trait/mod.rs index 10f94eee8b4..32caff67128 100644 --- a/swayfmt/src/items/item_trait/mod.rs +++ b/swayfmt/src/items/item_trait/mod.rs @@ -8,7 +8,10 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::{keywords::Token, ItemTrait, ItemTraitItem, Traits}; +use sway_ast::{ + keywords::{AddToken, ColonToken, Keyword, Token, TraitToken}, + ItemTrait, ItemTraitItem, PubToken, Traits, +}; use sway_types::{ast::Delimiter, Spanned}; #[cfg(test)] @@ -23,23 +26,23 @@ impl Format for ItemTrait { // Required for comment formatting let start_len = formatted_code.len(); // `pub ` - if let Some(pub_token) = &self.visibility { - write!(formatted_code, "{} ", pub_token.span().as_str())?; + if self.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } // `trait name` write!( formatted_code, "{} {}", - self.trait_token.span().as_str(), - self.name.span().as_str() + TraitToken::AS_STR, + self.name.as_str(), )?; // `` if let Some(generics) = &self.generics { generics.format(formatted_code, formatter)?; } // `: super_trait + super_trait` - if let Some((colon_token, traits)) = &self.super_traits { - write!(formatted_code, "{} ", colon_token.ident().as_str())?; + if let Some((_colon_token, traits)) = &self.super_traits { + write!(formatted_code, "{} ", ColonToken::AS_STR)?; traits.format(formatted_code, formatter)?; } // `where` @@ -145,8 +148,8 @@ impl Format for Traits { // additional `PathType`s // // ` + PathType` - for (add_token, path_type) in self.suffixes.iter() { - write!(formatted_code, " {} ", add_token.span().as_str())?; + for (_add_token, path_type) in self.suffixes.iter() { + write!(formatted_code, " {} ", AddToken::AS_STR)?; path_type.format(formatted_code, formatter)?; } diff --git a/swayfmt/src/items/item_trait_type.rs b/swayfmt/src/items/item_trait_type.rs index f3af2251b2f..eb363c1ef7b 100644 --- a/swayfmt/src/items/item_trait_type.rs +++ b/swayfmt/src/items/item_trait_type.rs @@ -4,7 +4,10 @@ use crate::{ utils::map::byte_span::{ByteSpan, LeafSpans}, }; use std::fmt::Write; -use sway_ast::{keywords::Token, TraitType}; +use sway_ast::{ + keywords::{EqToken, Keyword, SemicolonToken, Token, TypeToken}, + TraitType, +}; use sway_types::Spanned; impl Format for TraitType { @@ -16,15 +19,15 @@ impl Format for TraitType { // Required for comment formatting let start_len = formatted_code.len(); - // Add the const token - write!(formatted_code, "{} ", self.type_token.span().as_str())?; + // Add the type token + write!(formatted_code, "{} ", TypeToken::AS_STR)?; - // Add name of the const + // Add name of the type self.name.format(formatted_code, formatter)?; // Check if ` = ` exists - if let Some(eq_token) = &self.eq_token_opt { - write!(formatted_code, " {} ", eq_token.ident().as_str())?; + if self.eq_token_opt.is_some() { + write!(formatted_code, " {} ", EqToken::AS_STR)?; } // Check if ty exists @@ -32,7 +35,7 @@ impl Format for TraitType { ty.format(formatted_code, formatter)?; } - write!(formatted_code, "{}", self.semicolon_token.ident().as_str())?; + write!(formatted_code, "{}", SemicolonToken::AS_STR)?; rewrite_with_comments::( formatter, diff --git a/swayfmt/src/items/item_type_alias.rs b/swayfmt/src/items/item_type_alias.rs index 4870baeb031..86221a70f5b 100644 --- a/swayfmt/src/items/item_type_alias.rs +++ b/swayfmt/src/items/item_type_alias.rs @@ -4,7 +4,10 @@ use crate::{ utils::map::byte_span::{ByteSpan, LeafSpans}, }; use std::fmt::Write; -use sway_ast::{keywords::Token, ItemTypeAlias}; +use sway_ast::{ + keywords::{EqToken, Keyword, SemicolonToken, Token, TypeToken}, + ItemTypeAlias, PubToken, +}; use sway_types::Spanned; impl Format for ItemTypeAlias { @@ -17,24 +20,24 @@ impl Format for ItemTypeAlias { let start_len = formatted_code.len(); // Check if visibility token exists if so add it. - if let Some(visibility_token) = &self.visibility { - write!(formatted_code, "{} ", visibility_token.span().as_str())?; + if self.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } // Add the `type` token - write!(formatted_code, "{} ", self.type_token.span().as_str())?; + write!(formatted_code, "{} ", TypeToken::AS_STR)?; // Add name of the type alias self.name.format(formatted_code, formatter)?; // Add the `=` token - write!(formatted_code, " {} ", self.eq_token.ident().as_str())?; + write!(formatted_code, " {} ", EqToken::AS_STR)?; // Format and add `ty` self.ty.format(formatted_code, formatter)?; // Add the `;` token - write!(formatted_code, "{}", self.semicolon_token.ident().as_str())?; + write!(formatted_code, "{}", SemicolonToken::AS_STR)?; rewrite_with_comments::( formatter, diff --git a/swayfmt/src/items/item_use/mod.rs b/swayfmt/src/items/item_use/mod.rs index ce304183495..838248fb1c7 100644 --- a/swayfmt/src/items/item_use/mod.rs +++ b/swayfmt/src/items/item_use/mod.rs @@ -9,11 +9,11 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::{CommaToken, ItemUse, UseTree}; -use sway_types::{ - ast::{Delimiter, PunctKind}, - Spanned, +use sway_ast::{ + keywords::{AsToken, Keyword, SemicolonToken, StarToken, Token, UseToken}, + CommaToken, DoubleColonToken, ItemUse, PubToken, UseTree, }; +use sway_types::{ast::Delimiter, Spanned}; #[cfg(test)] mod tests; @@ -64,23 +64,40 @@ impl Format for UseTree { match self { Self::Group { imports } => { // check for only one import - if imports.inner.value_separator_pairs.is_empty() { + if imports.inner.value_separator_pairs.is_empty() + && !formatter.shape.code_line.line_style.is_multiline() + { + // we can have: path::{single_import} if let Some(single_import) = &imports.inner.final_value_opt { single_import.format(formatted_code, formatter)?; } + } else if imports.inner.value_separator_pairs.len() == 1 + && imports.inner.has_trailing_punctuation() + && !formatter.shape.code_line.line_style.is_multiline() + { + // but we can also have: path::{single_import,} + // note that in the case of multiline we want to keep the trailing comma + let single_import = &imports + .inner + .value_separator_pairs + .first() + .expect("the `if` condition ensures the existence of the first element") + .0; + single_import.format(formatted_code, formatter)?; } else { Self::open_curly_brace(formatted_code, formatter)?; // sort group imports let imports = imports.get(); let value_pairs = &imports.value_separator_pairs; - let mut commas: Vec<&CommaToken> = Vec::new(); + // track how many commas we have, to simplify checking for trailing element or trailing comma + let mut commas: Vec<()> = Vec::new(); let mut ord_vec: Vec = value_pairs .iter() .map( - |(use_tree, comma_token)| -> Result { + |(use_tree, _comma_token)| -> Result { let mut buf = FormattedCode::new(); use_tree.format(&mut buf, formatter)?; - commas.push(comma_token); + commas.push(()); // we have a comma token Ok(buf) }, ) @@ -102,15 +119,16 @@ impl Format for UseTree { a.to_lowercase().cmp(&b.to_lowercase()) } }); - for (use_tree, comma) in ord_vec.iter_mut().zip(commas.iter()) { - write!(use_tree, "{}", comma.span().as_str())?; + // zip will take only the parts of `ord_vec` before the last comma + for (use_tree, _) in ord_vec.iter_mut().zip(commas.iter()) { + write!(use_tree, "{}", CommaToken::AS_STR)?; } match formatter.shape.code_line.line_style { LineStyle::Multiline => { if imports.final_value_opt.is_some() { if let Some(last) = ord_vec.iter_mut().last() { - write!(last, "{}", PunctKind::Comma.as_char())?; + write!(last, "{}", CommaToken::AS_STR)?; } } @@ -122,39 +140,48 @@ impl Format for UseTree { )?; } _ => { - write!(formatted_code, "{}", ord_vec.join(" "))?; + if imports.has_trailing_punctuation() { + // remove the trailing punctuation + write!( + formatted_code, + "{}", + ord_vec.join(" ").trim_end_matches(',') + )?; + } else { + write!(formatted_code, "{}", ord_vec.join(" "))?; + } } } Self::close_curly_brace(formatted_code, formatter)?; } } - Self::Name { name } => write!(formatted_code, "{}", name.span().as_str())?, + Self::Name { name } => write!(formatted_code, "{}", name.as_str())?, Self::Rename { name, - as_token, + as_token: _, alias, } => { write!( formatted_code, "{} {} {}", - name.span().as_str(), - as_token.span().as_str(), - alias.span().as_str() + name.as_str(), + AsToken::AS_STR, + alias.as_str(), )?; } - Self::Glob { star_token } => { - write!(formatted_code, "{}", star_token.span().as_str())?; + Self::Glob { star_token: _ } => { + write!(formatted_code, "{}", StarToken::AS_STR)?; } Self::Path { prefix, - double_colon_token, + double_colon_token: _, suffix, } => { write!( formatted_code, "{}{}", - prefix.span().as_str(), - double_colon_token.span().as_str() + prefix.as_str(), + DoubleColonToken::AS_STR, )?; suffix.format(formatted_code, formatter)?; } @@ -208,19 +235,15 @@ fn format_use_stmt( formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - if let Some(pub_token) = &item_use.visibility { - write!(formatted_code, "{} ", pub_token.span().as_str())?; + if item_use.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } - write!(formatted_code, "{} ", item_use.use_token.span().as_str())?; - if let Some(root_import) = &item_use.root_import { - write!(formatted_code, "{}", root_import.span().as_str())?; + write!(formatted_code, "{} ", UseToken::AS_STR)?; + if item_use.root_import.is_some() { + write!(formatted_code, "{}", DoubleColonToken::AS_STR)?; } item_use.tree.format(formatted_code, formatter)?; - write!( - formatted_code, - "{}", - item_use.semicolon_token.span().as_str() - )?; + write!(formatted_code, "{}", SemicolonToken::AS_STR)?; Ok(()) } diff --git a/swayfmt/src/items/item_use/tests.rs b/swayfmt/src/items/item_use/tests.rs index 0b0a207bc37..64794080d65 100644 --- a/swayfmt/src/items/item_use/tests.rs +++ b/swayfmt/src/items/item_use/tests.rs @@ -10,6 +10,15 @@ fmt_test_item!(multiline "use foo::{ };", out_of_order "use foo::{yxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, quux, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx};" ); + +fmt_test_item!(multiline_with_trailing_comma "use foo::{ + quux, + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, +};", + out_of_order "use foo::{yxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, quux, xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,};" +); + fmt_test_item!(multiline_nested "use foo::{ Quux::{ a, @@ -22,10 +31,42 @@ fmt_test_item!(multiline_nested "use foo::{ out_of_order "use foo::{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, Quux::{b, a, C}, yxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx};" ); +fmt_test_item!(multiline_nested_with_trailing_comma "use foo::{ + Quux::{ + a, + b, + C, + }, + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, + yxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, +};", + out_of_order "use foo::{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, Quux::{b, a, C,}, yxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,};" +); + fmt_test_item!(single_line_sort "use foo::{bar, baz, Quux::{a, b, C}};", out_of_order "use foo::{baz, Quux::{b, a, C}, bar};" ); +fmt_test_item!(single_line_sort_with_trailing_comma "use foo::{bar, baz, Quux::{a, b, C}};", + out_of_order "use foo::{baz, Quux::{b, a, C,}, bar,};" +); + fmt_test_item!(single_import_without_braces "use std::tx::tx_id;", braced_single_import "use std::tx::{tx_id};" ); + +fmt_test_item!(single_import_without_braces_with_trailing_comma "use std::tx::tx_id;", + braced_single_import "use std::tx::{tx_id,};" +); + +fmt_test_item!(single_import_multiline_with_braces "use std::tx::{ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, +};", + braced_single_import "use std::tx::{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx};" +); + +fmt_test_item!(single_import_multiline_with_braces_with_trailing_comma "use std::tx::{ + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, +};", + braced_single_import "use std::tx::{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx,};" +); diff --git a/swayfmt/src/module/mod.rs b/swayfmt/src/module/mod.rs index db49d4f806c..de95433281c 100644 --- a/swayfmt/src/module/mod.rs +++ b/swayfmt/src/module/mod.rs @@ -4,7 +4,12 @@ use crate::{ utils::map::byte_span::{self, ByteSpan, LeafSpans}, }; use std::fmt::Write; -use sway_ast::{Item, ItemKind, Module, ModuleKind}; +use sway_ast::{ + keywords::{ + ContractToken, Keyword, LibraryToken, PredicateToken, ScriptToken, SemicolonToken, Token, + }, + Item, ItemKind, Module, ModuleKind, +}; use sway_types::Spanned; pub(crate) mod item; @@ -18,7 +23,7 @@ impl Format for Module { ) -> Result<(), FormatterError> { write_comments(formatted_code, 0..self.span().start(), formatter)?; self.kind.format(formatted_code, formatter)?; - writeln!(formatted_code, "{}", self.semicolon_token.span().as_str())?; + writeln!(formatted_code, "{}", SemicolonToken::AS_STR)?; // Format comments between module kind declaration and rest of items if !self.items.is_empty() { @@ -69,17 +74,17 @@ impl Format for ModuleKind { _formatter: &mut Formatter, ) -> Result<(), FormatterError> { match self { - ModuleKind::Script { script_token } => { - write!(formatted_code, "{}", script_token.span().as_str())? + ModuleKind::Script { script_token: _ } => { + write!(formatted_code, "{}", ScriptToken::AS_STR)? } - ModuleKind::Contract { contract_token } => { - write!(formatted_code, "{}", contract_token.span().as_str())? + ModuleKind::Contract { contract_token: _ } => { + write!(formatted_code, "{}", ContractToken::AS_STR)? } - ModuleKind::Predicate { predicate_token } => { - write!(formatted_code, "{}", predicate_token.span().as_str())? + ModuleKind::Predicate { predicate_token: _ } => { + write!(formatted_code, "{}", PredicateToken::AS_STR)? } - ModuleKind::Library { library_token } => { - write!(formatted_code, "{}", library_token.span().as_str())?; + ModuleKind::Library { library_token: _ } => { + write!(formatted_code, "{}", LibraryToken::AS_STR)?; } }; diff --git a/swayfmt/src/module/submodule.rs b/swayfmt/src/module/submodule.rs index 0c2a6c4d31b..6edb91ab574 100644 --- a/swayfmt/src/module/submodule.rs +++ b/swayfmt/src/module/submodule.rs @@ -3,7 +3,11 @@ use crate::{ utils::map::byte_span::{ByteSpan, LeafSpans}, }; use std::fmt::Write; -use sway_ast::submodule::Submodule; +use sway_ast::{ + keywords::{Keyword, ModToken, SemicolonToken, Token}, + submodule::Submodule, + PubToken, +}; use sway_types::Spanned; impl Format for Submodule { @@ -12,12 +16,12 @@ impl Format for Submodule { formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - if let Some(pub_token) = &self.visibility { - write!(formatted_code, "{} ", pub_token.span().as_str())?; + if self.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } - write!(formatted_code, "{} ", self.mod_token.span().as_str())?; + write!(formatted_code, "{} ", ModToken::AS_STR)?; self.name.format(formatted_code, formatter)?; - writeln!(formatted_code, "{}", self.semicolon_token.span().as_str())?; + writeln!(formatted_code, "{}", SemicolonToken::AS_STR)?; Ok(()) } } diff --git a/swayfmt/src/parse.rs b/swayfmt/src/parse.rs index 9d4ccadbbfb..17befe22cc3 100644 --- a/swayfmt/src/parse.rs +++ b/swayfmt/src/parse.rs @@ -1,9 +1,7 @@ use crate::{error::ParseFileError, Formatter, FormatterError}; -use std::path::PathBuf; use std::sync::Arc; use sway_ast::{attribute::Annotated, token::CommentedTokenStream, Module}; use sway_error::handler::{ErrorEmitted, Handler}; -use sway_types::SourceEngine; fn with_handler( run: impl FnOnce(&Handler) -> Result, @@ -16,13 +14,8 @@ fn with_handler( .ok_or(ParseFileError(errors)) } -pub fn parse_file( - source_engine: &SourceEngine, - src: Arc, - path: Option>, -) -> Result, ParseFileError> { - let source_id = path.map(|p| source_engine.get_source_id(p.as_ref())); - with_handler(|h| sway_parse::parse_file(h, src, source_id)) +pub fn parse_file(src: Arc) -> Result, ParseFileError> { + with_handler(|h| sway_parse::parse_file(h, src, None)) } pub fn lex(input: &Arc) -> Result { diff --git a/swayfmt/src/utils/language/attribute.rs b/swayfmt/src/utils/language/attribute.rs index daa6b7e5611..b9662bf8d56 100644 --- a/swayfmt/src/utils/language/attribute.rs +++ b/swayfmt/src/utils/language/attribute.rs @@ -8,12 +8,12 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::attribute::{Annotated, Attribute, AttributeArg, AttributeDecl, AttributeHashKind}; -use sway_types::{ - ast::{Delimiter, PunctKind}, - constants::DOC_COMMENT_ATTRIBUTE_NAME, - Spanned, +use sway_ast::{ + attribute::{Annotated, Attribute, AttributeArg, AttributeDecl, AttributeHashKind}, + keywords::{HashToken, Token}, + CommaToken, }; +use sway_types::{ast::Delimiter, constants::DOC_COMMENT_ATTRIBUTE_NAME, Spanned}; impl Format for Annotated { fn format( @@ -56,11 +56,12 @@ impl Format for AttributeArg { fn format( &self, formatted_code: &mut FormattedCode, - _formatter: &mut Formatter, + formatter: &mut Formatter, ) -> Result<(), FormatterError> { - write!(formatted_code, "{}", self.name.span().as_str())?; + write!(formatted_code, "{}", self.name.as_str())?; if let Some(value) = &self.value { - write!(formatted_code, " = {}", value.span().as_str())?; + write!(formatted_code, " = ")?; + value.format(formatted_code, formatter)?; } Ok(()) @@ -114,11 +115,12 @@ impl Format for AttributeDecl { // invariant: attribute lists cannot be empty // `#` - let hash_type_token_span = match &self.hash_kind { - AttributeHashKind::Inner(_) => Err(FormatterError::HashBangAttributeError), - AttributeHashKind::Outer(hash_token) => Ok(hash_token.span()), + match &self.hash_kind { + AttributeHashKind::Inner(_) => return Err(FormatterError::HashBangAttributeError), + AttributeHashKind::Outer(_hash_token) => { + write!(formatted_code, "{}", HashToken::AS_STR)?; + } }; - write!(formatted_code, "{}", hash_type_token_span?.as_str())?; // `[` Self::open_square_bracket(formatted_code, formatter)?; let mut regular_attrs = regular_attrs.iter().peekable(); @@ -127,7 +129,7 @@ impl Format for AttributeDecl { formatter.shape.with_default_code_line(), |formatter| -> Result<(), FormatterError> { // name e.g. `storage` - write!(formatted_code, "{}", attr.name.span().as_str())?; + write!(formatted_code, "{}", attr.name.as_str())?; if let Some(args) = &attr.args { // `(` Self::open_parenthesis(formatted_code, formatter)?; @@ -141,7 +143,7 @@ impl Format for AttributeDecl { )?; // do not put a separator after the last attribute if regular_attrs.peek().is_some() { - write!(formatted_code, "{} ", PunctKind::Comma.as_char())?; + write!(formatted_code, "{} ", CommaToken::AS_STR)?; } } // `]\n` diff --git a/swayfmt/src/utils/language/expr/abi_cast.rs b/swayfmt/src/utils/language/expr/abi_cast.rs index 14493f3f4d3..5c6545891ab 100644 --- a/swayfmt/src/utils/language/expr/abi_cast.rs +++ b/swayfmt/src/utils/language/expr/abi_cast.rs @@ -6,7 +6,7 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::AbiCastArgs; +use sway_ast::{keywords::Token, AbiCastArgs, CommaToken}; use sway_types::{ast::Delimiter, Spanned}; impl Format for AbiCastArgs { @@ -17,7 +17,7 @@ impl Format for AbiCastArgs { ) -> Result<(), FormatterError> { Self::open_parenthesis(formatted_code, formatter)?; self.name.format(formatted_code, formatter)?; - write!(formatted_code, "{} ", self.comma_token.span().as_str())?; + write!(formatted_code, "{} ", CommaToken::AS_STR)?; self.address.format(formatted_code, formatter)?; Self::close_parenthesis(formatted_code, formatter)?; diff --git a/swayfmt/src/utils/language/expr/asm_block.rs b/swayfmt/src/utils/language/expr/asm_block.rs index 19f92f5ea68..458bfec669f 100644 --- a/swayfmt/src/utils/language/expr/asm_block.rs +++ b/swayfmt/src/utils/language/expr/asm_block.rs @@ -9,6 +9,7 @@ use crate::{ use std::fmt::Write; use sway_ast::{ expr::asm::{AsmBlock, AsmBlockContents, AsmFinalExpr, AsmRegisterDeclaration}, + keywords::{AsmToken, ColonToken, Keyword, SemicolonToken, Token}, Instruction, }; use sway_types::{ast::Delimiter, Spanned}; @@ -56,7 +57,7 @@ fn format_asm_block( formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - write!(formatted_code, "{}", asm_block.asm_token.span().as_str())?; + write!(formatted_code, "{}", AsmToken::AS_STR)?; formatter.with_shape( formatter.shape.with_default_code_line(), @@ -174,8 +175,8 @@ impl Format for AsmRegisterDeclaration { formatter: &mut Formatter, ) -> Result<(), FormatterError> { self.register.format(formatted_code, formatter)?; - if let Some((colon_token, expr)) = &self.value_opt { - write!(formatted_code, "{} ", colon_token.span().as_str())?; + if let Some((_colon_token, expr)) = &self.value_opt { + write!(formatted_code, "{} ", ColonToken::AS_STR)?; expr.format(formatted_code, formatter)?; } @@ -189,7 +190,7 @@ impl Format for Instruction { formatted_code: &mut FormattedCode, _formatter: &mut Formatter, ) -> Result<(), FormatterError> { - write!(formatted_code, "{}", &self.op_code_ident().as_str())?; + write!(formatted_code, "{}", self.op_code_as_str())?; for arg in self.register_arg_idents() { write!(formatted_code, " {}", arg.as_str())? } @@ -206,10 +207,10 @@ impl Format for AsmBlockContents { formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - for (instruction, semicolon_token) in self.instructions.iter() { + for (instruction, _semicolon_token) in self.instructions.iter() { write!(formatted_code, "{}", formatter.indent_to_str()?)?; instruction.format(formatted_code, formatter)?; - writeln!(formatted_code, "{}", semicolon_token.span().as_str())? + writeln!(formatted_code, "{}", SemicolonToken::AS_STR)? } if let Some(final_expr) = &self.final_expr_opt { if formatter.shape.code_line.line_style == LineStyle::Multiline { @@ -232,8 +233,8 @@ impl Format for AsmFinalExpr { formatter: &mut Formatter, ) -> Result<(), FormatterError> { self.register.format(formatted_code, formatter)?; - if let Some((colon_token, ty)) = &self.ty_opt { - write!(formatted_code, "{} ", colon_token.span().as_str())?; + if let Some((_colon_token, ty)) = &self.ty_opt { + write!(formatted_code, "{} ", ColonToken::AS_STR)?; ty.format(formatted_code, formatter)?; } diff --git a/swayfmt/src/utils/language/expr/assignable.rs b/swayfmt/src/utils/language/expr/assignable.rs index b5e278464dd..1df99ad70cf 100644 --- a/swayfmt/src/utils/language/expr/assignable.rs +++ b/swayfmt/src/utils/language/expr/assignable.rs @@ -6,7 +6,12 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::{assignable::ElementAccess, expr::ReassignmentOp, Assignable, Expr}; +use sway_ast::{ + assignable::ElementAccess, + expr::ReassignmentOp, + keywords::{DotToken, StarToken, Token}, + Assignable, Expr, +}; use sway_types::Spanned; impl Format for ElementAccess { @@ -27,26 +32,21 @@ impl Format for ElementAccess { } ElementAccess::FieldProjection { target, - dot_token, + dot_token: _, name, } => { target.format(formatted_code, formatter)?; - write!(formatted_code, "{}", dot_token.span().as_str())?; + write!(formatted_code, "{}", DotToken::AS_STR)?; name.format(formatted_code, formatter)?; } ElementAccess::TupleFieldProjection { target, - dot_token, - field: _, - field_span, + dot_token: _, + field, + field_span: _, } => { target.format(formatted_code, formatter)?; - write!( - formatted_code, - "{}{}", - dot_token.span().as_str(), - field_span.as_str() - )?; + write!(formatted_code, "{}{}", DotToken::AS_STR, field)?; } } Ok(()) @@ -63,8 +63,11 @@ impl Format for Assignable { Assignable::ElementAccess(element_access) => { element_access.format(formatted_code, formatter)? } - Assignable::Deref { star_token, expr } => { - write!(formatted_code, "{}", star_token.span().as_str())?; + Assignable::Deref { + star_token: _, + expr, + } => { + write!(formatted_code, "{}", StarToken::AS_STR)?; expr.format(formatted_code, formatter)?; } } @@ -78,7 +81,7 @@ impl Format for ReassignmentOp { formatted_code: &mut FormattedCode, _formatter: &mut Formatter, ) -> Result<(), FormatterError> { - write!(formatted_code, " {} ", self.span.as_str())?; + write!(formatted_code, " {} ", self.variant.as_str())?; Ok(()) } } diff --git a/swayfmt/src/utils/language/expr/collections.rs b/swayfmt/src/utils/language/expr/collections.rs index f175329bbd9..af34214f21c 100644 --- a/swayfmt/src/utils/language/expr/collections.rs +++ b/swayfmt/src/utils/language/expr/collections.rs @@ -6,7 +6,10 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::{ExprArrayDescriptor, ExprTupleDescriptor}; +use sway_ast::{ + keywords::{SemicolonToken, Token}, + CommaToken, ExprArrayDescriptor, ExprTupleDescriptor, +}; use sway_types::{ast::Delimiter, Spanned}; impl Format for ExprTupleDescriptor { @@ -20,18 +23,18 @@ impl Format for ExprTupleDescriptor { Self::Nil => {} Self::Cons { head, - comma_token, + comma_token: _, tail, } => match formatter.shape.code_line.line_style { LineStyle::Multiline => { write!(formatted_code, "{}", formatter.indent_to_str()?)?; head.format(formatted_code, formatter)?; - write!(formatted_code, "{}", comma_token.span().as_str())?; + write!(formatted_code, "{}", CommaToken::AS_STR)?; tail.format(formatted_code, formatter)?; } _ => { head.format(formatted_code, formatter)?; - write!(formatted_code, "{} ", comma_token.span().as_str())?; + write!(formatted_code, "{} ", CommaToken::AS_STR)?; tail.format(formatted_code, formatter)?; } }, @@ -91,11 +94,11 @@ impl Format for ExprArrayDescriptor { } Self::Repeat { value, - semicolon_token, + semicolon_token: _, length, } => { value.format(formatted_code, formatter)?; - write!(formatted_code, "{} ", semicolon_token.span().as_str())?; + write!(formatted_code, "{} ", SemicolonToken::AS_STR)?; length.format(formatted_code, formatter)?; } } diff --git a/swayfmt/src/utils/language/expr/conditional.rs b/swayfmt/src/utils/language/expr/conditional.rs index f21e71c133d..b97d5896c47 100644 --- a/swayfmt/src/utils/language/expr/conditional.rs +++ b/swayfmt/src/utils/language/expr/conditional.rs @@ -10,7 +10,11 @@ use crate::{ }, }; use std::{fmt::Write, ops::Range}; -use sway_ast::{expr::LoopControlFlow, IfCondition, IfExpr, MatchBranch, MatchBranchKind}; +use sway_ast::{ + expr::LoopControlFlow, + keywords::{ElseToken, EqToken, FatRightArrowToken, IfToken, Keyword, LetToken, Token}, + CommaToken, IfCondition, IfExpr, MatchBranch, MatchBranchKind, +}; use sway_types::{ast::Delimiter, Spanned}; impl Format for IfExpr { @@ -125,7 +129,7 @@ fn format_if_condition( formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - write!(formatted_code, "{} ", if_expr.if_token.span().as_str())?; + write!(formatted_code, "{} ", IfToken::AS_STR)?; if formatter.shape.code_line.line_style == LineStyle::Multiline { formatter.indent(); if_expr.condition.format(formatted_code, formatter)?; @@ -181,11 +185,11 @@ fn format_else_opt( )?; if comments_written { - write!(else_if_str, "{}", formatter.indent_to_str()?,)?; + write!(else_if_str, "{}", formatter.indent_to_str()?)?; } else { write!(else_if_str, " ")?; } - write!(else_if_str, "{}", else_token.span().as_str())?; + write!(else_if_str, "{}", ElseToken::AS_STR)?; match &control_flow { LoopControlFlow::Continue(if_expr) => { write!(else_if_str, " ")?; @@ -263,14 +267,14 @@ impl Format for IfCondition { expr.format(formatted_code, formatter)?; } Self::Let { - let_token, + let_token: _, lhs, - eq_token, + eq_token: _, rhs, } => { - write!(formatted_code, "{} ", let_token.span().as_str())?; + write!(formatted_code, "{} ", LetToken::AS_STR)?; lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", eq_token.span().as_str())?; + write!(formatted_code, " {} ", EqToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } } @@ -286,11 +290,7 @@ impl Format for MatchBranch { formatter: &mut Formatter, ) -> Result<(), FormatterError> { self.pattern.format(formatted_code, formatter)?; - write!( - formatted_code, - " {} ", - self.fat_right_arrow_token.span().as_str() - )?; + write!(formatted_code, " {} ", FatRightArrowToken::AS_STR)?; self.kind.format(formatted_code, formatter)?; Ok(()) @@ -348,13 +348,16 @@ impl Format for MatchBranchKind { write!(formatted_code, "{}", formatter.indent_to_str()?)?; } Self::close_curly_brace(formatted_code, formatter)?; - if let Some(comma_token) = comma_token_opt { - write!(formatted_code, "{}", comma_token.span().as_str())?; + if comma_token_opt.is_some() { + write!(formatted_code, "{}", CommaToken::AS_STR)?; } } - Self::Expr { expr, comma_token } => { + Self::Expr { + expr, + comma_token: _, + } => { expr.format(formatted_code, formatter)?; - write!(formatted_code, "{}", comma_token.span().as_str())?; + write!(formatted_code, "{}", CommaToken::AS_STR)?; } } diff --git a/swayfmt/src/utils/language/expr/mod.rs b/swayfmt/src/utils/language/expr/mod.rs index a65eb3bc527..9fd5005a24a 100644 --- a/swayfmt/src/utils/language/expr/mod.rs +++ b/swayfmt/src/utils/language/expr/mod.rs @@ -10,11 +10,8 @@ use crate::{ }; use std::fmt::Write; use sway_ast::{ - brackets::Parens, - keywords::{CommaToken, DotToken}, - punctuated::Punctuated, - Braces, CodeBlockContents, Expr, ExprStructField, IfExpr, MatchBranch, PathExpr, - PathExprSegment, + brackets::Parens, keywords::*, punctuated::Punctuated, Braces, CodeBlockContents, Expr, + ExprStructField, IfExpr, MatchBranch, PathExpr, PathExprSegment, }; use sway_types::{ast::Delimiter, Spanned}; @@ -79,7 +76,7 @@ fn two_parts_expr( )?; } _ => { - write!(formatted_code, " {} ", operator,)?; + write!(formatted_code, " {} ", operator)?; } } write!(formatted_code, "{}", rhs_code)?; @@ -99,8 +96,8 @@ impl Format for Expr { } Self::Path(path) => path.format(formatted_code, formatter)?, Self::Literal(lit) => lit.format(formatted_code, formatter)?, - Self::AbiCast { abi_token, args } => { - write!(formatted_code, "{}", abi_token.span().as_str())?; + Self::AbiCast { abi_token: _, args } => { + write!(formatted_code, "{}", AbiToken::AS_STR)?; args.get().format(formatted_code, formatter)?; } Self::Struct { path, fields } => { @@ -205,7 +202,7 @@ impl Format for Expr { .get_line_style(None, Some(body_width), &formatter.config); if formatter.shape.code_line.line_style == LineStyle::Multiline { - // Expr needs to be splitten into multiple lines + // Expr needs to be split into multiple lines array_descriptor.format(formatted_code, formatter)?; } else { // Expr fits in a single line @@ -218,10 +215,10 @@ impl Format for Expr { } Self::Asm(asm_block) => asm_block.format(formatted_code, formatter)?, Self::Return { - return_token, + return_token: _, expr_opt, } => { - write!(formatted_code, "{}", return_token.span().as_str())?; + write!(formatted_code, "{}", ReturnToken::AS_STR)?; if let Some(expr) = &expr_opt { write!(formatted_code, " ")?; expr.format(formatted_code, formatter)?; @@ -229,11 +226,11 @@ impl Format for Expr { } Self::If(if_expr) => if_expr.format(formatted_code, formatter)?, Self::Match { - match_token, + match_token: _, value, branches, } => { - write!(formatted_code, "{} ", match_token.span().as_str())?; + write!(formatted_code, "{} ", MatchToken::AS_STR)?; value.format(formatted_code, formatter)?; write!(formatted_code, " ")?; if !branches.get().is_empty() { @@ -250,7 +247,7 @@ impl Format for Expr { } } Self::While { - while_token, + while_token: _, condition, block, } => { @@ -259,7 +256,7 @@ impl Format for Expr { .shape .with_code_line_from(LineStyle::Normal, ExprKind::Function), |formatter| -> Result<(), FormatterError> { - write!(formatted_code, "{} ", while_token.span().as_str())?; + write!(formatted_code, "{} ", WhileToken::AS_STR)?; condition.format(formatted_code, formatter)?; IfExpr::open_curly_brace(formatted_code, formatter)?; block.get().format(formatted_code, formatter)?; @@ -269,8 +266,8 @@ impl Format for Expr { )?; } Self::For { - for_token, - in_token, + for_token: _, + in_token: _, value_pattern, iterator, block, @@ -280,9 +277,9 @@ impl Format for Expr { .shape .with_code_line_from(LineStyle::Normal, ExprKind::Function), |formatter| -> Result<(), FormatterError> { - write!(formatted_code, "{} ", for_token.span().as_str())?; + write!(formatted_code, "{} ", ForToken::AS_STR)?; value_pattern.format(formatted_code, formatter)?; - write!(formatted_code, "{} ", in_token.span().as_str())?; + write!(formatted_code, "{} ", InToken::AS_STR)?; iterator.format(formatted_code, formatter)?; IfExpr::open_curly_brace(formatted_code, formatter)?; block.get().format(formatted_code, formatter)?; @@ -305,7 +302,7 @@ impl Format for Expr { Self::open_parenthesis(formatted_code, formatter)?; let (_, args_str) = write_function_call_arguments(args.get(), formatter)?; - write!(formatted_code, "{}", args_str,)?; + write!(formatted_code, "{}", args_str)?; Self::close_parenthesis(formatted_code, formatter)?; Ok(()) @@ -381,7 +378,7 @@ impl Format for Expr { } Self::FieldProjection { target, - dot_token, + dot_token: _, name, } => { let prev_length = formatted_code.len(); @@ -398,124 +395,124 @@ impl Format for Expr { formatted_code, "\n{}{}", formatter.indent_to_str()?, - dot_token.span().as_str() + DotToken::AS_STR, )?; name.format(formatted_code, formatter)?; formatter.unindent(); } else { - write!(formatted_code, "{}", dot_token.span().as_str())?; + write!(formatted_code, "{}", DotToken::AS_STR)?; name.format(formatted_code, formatter)?; } } Self::TupleFieldProjection { target, - dot_token, - field: _, - field_span, + dot_token: _, + field, + field_span: _, } => { target.format(formatted_code, formatter)?; - write!( - formatted_code, - "{}{}", - dot_token.span().as_str(), - field_span.as_str(), - )?; + write!(formatted_code, "{}{}", DotToken::AS_STR, field)?; } Self::Ref { - ampersand_token, + ampersand_token: _, mut_token, expr, } => { - // TODO-IG: Add unit tests. - write!(formatted_code, "{}", ampersand_token.span().as_str())?; - if let Some(mut_token) = mut_token { - write!(formatted_code, "{} ", mut_token.span().as_str())?; + write!(formatted_code, "{}", AmpersandToken::AS_STR)?; + if mut_token.is_some() { + write!(formatted_code, "{} ", MutToken::AS_STR)?; } expr.format(formatted_code, formatter)?; } - Self::Deref { star_token, expr } => { - write!(formatted_code, "{}", star_token.span().as_str())?; + Self::Deref { + star_token: _, + expr, + } => { + write!(formatted_code, "{}", StarToken::AS_STR)?; expr.format(formatted_code, formatter)?; } - Self::Not { bang_token, expr } => { - write!(formatted_code, "{}", bang_token.span().as_str())?; + Self::Not { + bang_token: _, + expr, + } => { + write!(formatted_code, "{}", BangToken::AS_STR)?; expr.format(formatted_code, formatter)?; } Self::Pow { lhs, - double_star_token, + double_star_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", double_star_token.span().as_str())?; + write!(formatted_code, " {} ", DoubleStarToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::Mul { lhs, - star_token, + star_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", star_token.span().as_str())?; + write!(formatted_code, " {} ", StarToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::Div { lhs, - forward_slash_token, + forward_slash_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", forward_slash_token.span().as_str())?; + write!(formatted_code, " {} ", ForwardSlashToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::Modulo { lhs, - percent_token, + percent_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", percent_token.span().as_str())?; + write!(formatted_code, " {} ", PercentToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::Add { lhs, - add_token, + add_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", add_token.span().as_str())?; + write!(formatted_code, " {} ", AddToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::Sub { lhs, - sub_token, + sub_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", sub_token.span().as_str())?; + write!(formatted_code, " {} ", SubToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::Shl { lhs, - shl_token, + shl_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", shl_token.span().as_str())?; + write!(formatted_code, " {} ", ShlToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::Shr { lhs, - shr_token, + shr_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", shr_token.span().as_str())?; + write!(formatted_code, " {} ", ShrToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::BitAnd { lhs, - ampersand_token, + ampersand_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; @@ -525,18 +522,18 @@ impl Format for Expr { formatted_code, "\n{}{} ", formatter.indent_to_str()?, - ampersand_token.span().as_str() + AmpersandToken::AS_STR, )?; } _ => { - write!(formatted_code, " {} ", ampersand_token.span().as_str())?; + write!(formatted_code, " {} ", AmpersandToken::AS_STR)?; } } rhs.format(formatted_code, formatter)?; } Self::BitXor { lhs, - caret_token, + caret_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; @@ -546,18 +543,18 @@ impl Format for Expr { formatted_code, "\n{}{} ", formatter.indent_to_str()?, - caret_token.span().as_str() + CaretToken::AS_STR, )?; } _ => { - write!(formatted_code, " {} ", caret_token.span().as_str())?; + write!(formatted_code, " {} ", CaretToken::AS_STR)?; } } rhs.format(formatted_code, formatter)?; } Self::BitOr { lhs, - pipe_token, + pipe_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; @@ -567,81 +564,77 @@ impl Format for Expr { formatted_code, "\n{}{} ", formatter.indent_to_str()?, - pipe_token.span().as_str() + PipeToken::AS_STR, )?; } _ => { - write!(formatted_code, " {} ", pipe_token.span().as_str())?; + write!(formatted_code, " {} ", PipeToken::AS_STR)?; } } rhs.format(formatted_code, formatter)?; } Self::Equal { lhs, - double_eq_token, + double_eq_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", double_eq_token.span().as_str())?; + write!(formatted_code, " {} ", DoubleEqToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::NotEqual { lhs, - bang_eq_token, + bang_eq_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", bang_eq_token.span().as_str())?; + write!(formatted_code, " {} ", BangEqToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::LessThan { lhs, - less_than_token, + less_than_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", less_than_token.span().as_str())?; + write!(formatted_code, " {} ", LessThanToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::GreaterThan { lhs, - greater_than_token, + greater_than_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", greater_than_token.span().as_str())?; + write!(formatted_code, " {} ", GreaterThanToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::LessThanEq { lhs, - less_than_eq_token, + less_than_eq_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", less_than_eq_token.span().as_str())?; + write!(formatted_code, " {} ", LessThanEqToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::GreaterThanEq { lhs, - greater_than_eq_token, + greater_than_eq_token: _, rhs, } => { lhs.format(formatted_code, formatter)?; - write!( - formatted_code, - " {} ", - greater_than_eq_token.span().as_str() - )?; + write!(formatted_code, " {} ", GreaterThanEqToken::AS_STR)?; rhs.format(formatted_code, formatter)?; } Self::LogicalAnd { lhs, - double_ampersand_token, + double_ampersand_token: _, rhs, } => { two_parts_expr( lhs, - double_ampersand_token.span().as_str(), + DoubleAmpersandToken::AS_STR, rhs, formatted_code, formatter, @@ -649,16 +642,10 @@ impl Format for Expr { } Self::LogicalOr { lhs, - double_pipe_token, + double_pipe_token: _, rhs, } => { - two_parts_expr( - lhs, - double_pipe_token.span().as_str(), - rhs, - formatted_code, - formatter, - )?; + two_parts_expr(lhs, DoublePipeToken::AS_STR, rhs, formatted_code, formatter)?; } Self::Reassignment { assignable, @@ -669,11 +656,11 @@ impl Format for Expr { reassignment_op.format(formatted_code, formatter)?; expr.format(formatted_code, formatter)?; } - Self::Break { break_token } => { - write!(formatted_code, "{}", break_token.span().as_str())?; + Self::Break { break_token: _ } => { + write!(formatted_code, "{}", BreakToken::AS_STR)?; } - Self::Continue { continue_token } => { - write!(formatted_code, "{}", continue_token.span().as_str())?; + Self::Continue { continue_token: _ } => { + write!(formatted_code, "{}", ContinueToken::AS_STR)?; } } @@ -888,7 +875,7 @@ where fn format_method_call( target: &Expr, - dot_token: &DotToken, + _dot_token: &DotToken, path_seg: &PathExprSegment, contract_args_opt: &Option>>, args: &Parens>, @@ -906,7 +893,7 @@ fn format_method_call( write!(formatted_code, "\n{}", formatter.indent_to_str()?)?; } - write!(formatted_code, "{}", dot_token.span().as_str())?; + write!(formatted_code, "{}", DotToken::AS_STR)?; path_seg.format(formatted_code, formatter)?; if let Some(contract_args) = &contract_args_opt { @@ -927,7 +914,7 @@ fn format_method_call( Expr::open_parenthesis(formatted_code, formatter)?; let (args_inline, args_str) = write_function_call_arguments(args.get(), formatter)?; - write!(formatted_code, "{}", args_str,)?; + write!(formatted_code, "{}", args_str)?; Expr::close_parenthesis(formatted_code, formatter)?; if formatter.shape.code_line.expr_new_line { @@ -942,15 +929,15 @@ fn get_field_width( ) -> Result<(usize, usize), FormatterError> { let mut largest_field: usize = 0; let mut body_width: usize = 3; // this is taking into account the opening brace, the following space and the ending brace. - for (field, comma_token) in &fields.value_separator_pairs { + for (field, _comma_token) in &fields.value_separator_pairs { let mut field_length = field.field_name.as_str().chars().count(); - if let Some((colon_token, expr)) = &field.expr_opt { + if let Some((_colon_token, expr)) = &field.expr_opt { let mut buf = String::new(); - write!(buf, "{} ", colon_token.span().as_str())?; + write!(buf, "{} ", ColonToken::AS_STR)?; expr.format(&mut buf, formatter)?; field_length += buf.chars().count(); } - field_length += comma_token.span().as_str().chars().count(); + field_length += CommaToken::AS_STR.chars().count(); body_width += &field_length + 1; // accounting for the following space if field_length > largest_field { @@ -959,9 +946,9 @@ fn get_field_width( } if let Some(final_value) = &fields.final_value_opt { let mut field_length = final_value.field_name.as_str().chars().count(); - if let Some((colon_token, expr)) = &final_value.expr_opt { + if let Some((_colon_token, expr)) = &final_value.expr_opt { let mut buf = String::new(); - write!(buf, "{} ", colon_token.span().as_str())?; + write!(buf, "{} ", ColonToken::AS_STR)?; expr.format(&mut buf, formatter)?; field_length += buf.chars().count(); } diff --git a/swayfmt/src/utils/language/expr/struct_field.rs b/swayfmt/src/utils/language/expr/struct_field.rs index 0a51864cc6c..99764c34226 100644 --- a/swayfmt/src/utils/language/expr/struct_field.rs +++ b/swayfmt/src/utils/language/expr/struct_field.rs @@ -10,7 +10,10 @@ use crate::{ }, }; use std::fmt::Write; -use sway_ast::ExprStructField; +use sway_ast::{ + keywords::{ColonToken, Token}, + ExprStructField, +}; use sway_types::{ast::Delimiter, Spanned}; impl Format for ExprStructField { @@ -19,8 +22,8 @@ impl Format for ExprStructField { formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - write!(formatted_code, "{}", self.field_name.span().as_str())?; - if let Some((colon_token, expr)) = &self.expr_opt { + write!(formatted_code, "{}", self.field_name.as_str())?; + if let Some((_colon_token, expr)) = &self.expr_opt { formatter.with_shape( formatter .shape @@ -37,12 +40,7 @@ impl Format for ExprStructField { } else { expr_str }; - write!( - formatted_code, - "{} {}", - colon_token.span().as_str(), - expr_str - )?; + write!(formatted_code, "{} {}", ColonToken::AS_STR, expr_str)?; Ok(()) }, )?; diff --git a/swayfmt/src/utils/language/literal.rs b/swayfmt/src/utils/language/literal.rs index 3918f4de7f6..8fecedae9eb 100644 --- a/swayfmt/src/utils/language/literal.rs +++ b/swayfmt/src/utils/language/literal.rs @@ -3,7 +3,7 @@ use crate::{ utils::map::byte_span::{ByteSpan, LeafSpans}, }; use std::fmt::Write; -use sway_ast::Literal; +use sway_ast::{literal::LitBoolType, LitIntType, Literal}; impl Format for Literal { fn format( @@ -12,16 +12,56 @@ impl Format for Literal { _formatter: &mut Formatter, ) -> Result<(), FormatterError> { match self { - // TODO: do more digging into `Literal` and see if there is more formatting to do. - Self::String(lit_string) => write!(formatted_code, "{}", lit_string.span.as_str())?, - Self::Char(lit_char) => write!(formatted_code, "{}", lit_char.span.as_str())?, + Self::String(lit_string) => write!(formatted_code, "\"{}\"", lit_string.parsed)?, + Self::Char(lit_char) => write!(formatted_code, "\'{}\'", lit_char.parsed)?, Self::Int(lit_int) => { - write!(formatted_code, "{}", lit_int.span.as_str())?; - if let Some((_, ty_span)) = &lit_int.ty_opt { - write!(formatted_code, "{}", ty_span.as_str())?; + // It is tricky to support formatting of `LitInt` for an arbitrary `LitInt` + // that is potentially not backed by source code, but constructed in-memory. + // + // E.g., a constructed `LitInt` representing 1000 can have only the numeric value + // (LitInt::parsed) specified, in which case we can simply output the value. + // If it has the type specified (LitInt::ty_opt), we can output the type next to the + // value, e.g., 1000u16. + // But a `LitInt` backed by code can have an arbitrary representation that includes + // underscores. E.g., 1_00_00__u16. In that case we need to preserve the original + // representation. + // + // The taken approach is the following. If the length of the `LitInt::span` is zero, + // we assume that it is not backed by source code and render the canonical representation, + // 1000u16 in the above example. Otherwise, we assume that it is backed by source code + // and use the actual spans to obtain the strings. + + if lit_int.span.is_empty() { + write!(formatted_code, "{}", lit_int.parsed)?; + if let Some((int_type, _)) = &lit_int.ty_opt { + let int_type = match int_type { + LitIntType::U8 => "u8", + LitIntType::U16 => "u16", + LitIntType::U32 => "u32", + LitIntType::U64 => "u64", + LitIntType::U256 => "u256", + LitIntType::I8 => "i8", + LitIntType::I16 => "i16", + LitIntType::I32 => "i32", + LitIntType::I64 => "i64", + }; + write!(formatted_code, "{}", int_type)?; + } + } else { + write!(formatted_code, "{}", lit_int.span.as_str())?; + if let Some((_, ty_span)) = &lit_int.ty_opt { + write!(formatted_code, "{}", ty_span.as_str())?; + } } } - Self::Bool(lit_bool) => write!(formatted_code, "{}", lit_bool.span.as_str())?, + Self::Bool(lit_bool) => write!( + formatted_code, + "{}", + match lit_bool.kind { + LitBoolType::True => "true", + LitBoolType::False => "false", + } + )?, } Ok(()) } diff --git a/swayfmt/src/utils/language/path.rs b/swayfmt/src/utils/language/path.rs index f8575f185b0..6d5a84ded1d 100644 --- a/swayfmt/src/utils/language/path.rs +++ b/swayfmt/src/utils/language/path.rs @@ -7,7 +7,8 @@ use crate::{ }; use std::{fmt::Write, vec}; use sway_ast::{ - keywords::Token, PathExpr, PathExprSegment, PathType, PathTypeSegment, QualifiedPathRoot, + keywords::{AsToken, Keyword, Token}, + DoubleColonToken, PathExpr, PathExprSegment, PathType, PathTypeSegment, QualifiedPathRoot, }; use sway_types::Spanned; @@ -17,7 +18,7 @@ impl Format for PathExpr { formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - if let Some((qualified_path_root, double_colon_token)) = &self.root_opt { + if let Some((qualified_path_root, _double_colon_token)) = &self.root_opt { if let Some(root) = &qualified_path_root { open_angle_bracket(formatted_code)?; root.clone() @@ -25,11 +26,11 @@ impl Format for PathExpr { .format(formatted_code, formatter)?; close_angle_bracket(formatted_code)?; } - write!(formatted_code, "{}", double_colon_token.ident().as_str())?; + write!(formatted_code, "{}", DoubleColonToken::AS_STR)?; } self.prefix.format(formatted_code, formatter)?; - for (double_colon_token, path_expr_segment) in self.suffix.iter() { - write!(formatted_code, "{}", double_colon_token.span().as_str())?; + for (_double_colon_token, path_expr_segment) in self.suffix.iter() { + write!(formatted_code, "{}", DoubleColonToken::AS_STR)?; path_expr_segment.format(formatted_code, formatter)?; } @@ -46,8 +47,8 @@ impl Format for PathExprSegment { // name self.name.format(formatted_code, formatter)?; // generics `::` - if let Some((double_colon_token, generic_args)) = &self.generics_opt { - write!(formatted_code, "{}", double_colon_token.span().as_str())?; + if let Some((_double_colon_token, generic_args)) = &self.generics_opt { + write!(formatted_code, "{}", DoubleColonToken::AS_STR)?; generic_args.format(formatted_code, formatter)?; } @@ -62,8 +63,8 @@ impl Format for QualifiedPathRoot { formatter: &mut Formatter, ) -> Result<(), FormatterError> { self.ty.format(formatted_code, formatter)?; - let (as_token, path_type) = &self.as_trait; - write!(formatted_code, " {} ", as_token.span().as_str())?; + let (_as_token, path_type) = &self.as_trait; + write!(formatted_code, " {} ", AsToken::AS_STR)?; path_type.format(formatted_code, formatter)?; Ok(()) @@ -76,7 +77,7 @@ impl Format for PathType { formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - if let Some((root_opt, double_colon_token)) = &self.root_opt { + if let Some((root_opt, _double_colon_token)) = &self.root_opt { if let Some(qualified_path_root) = &root_opt { open_angle_bracket(formatted_code)?; qualified_path_root @@ -85,11 +86,11 @@ impl Format for PathType { .format(formatted_code, formatter)?; close_angle_bracket(formatted_code)?; } - write!(formatted_code, "{}", double_colon_token.span().as_str())?; + write!(formatted_code, "{}", DoubleColonToken::AS_STR)?; } self.prefix.format(formatted_code, formatter)?; - for (double_colon_token, path_type_segment) in self.suffix.iter() { - write!(formatted_code, "{}", double_colon_token.span().as_str())?; + for (_double_colon_token, path_type_segment) in self.suffix.iter() { + write!(formatted_code, "{}", DoubleColonToken::AS_STR)?; path_type_segment.format(formatted_code, formatter)?; } @@ -104,11 +105,11 @@ impl Format for PathTypeSegment { formatter: &mut Formatter, ) -> Result<(), FormatterError> { // name - write!(formatted_code, "{}", self.name.span().as_str())?; + write!(formatted_code, "{}", self.name.as_str())?; // generics `::` if let Some((double_colon_opt, generic_args)) = &self.generics_opt { - if let Some(double_colon_token) = &double_colon_opt { - write!(formatted_code, "{}", double_colon_token.span().as_str())?; + if double_colon_opt.is_some() { + write!(formatted_code, "{}", DoubleColonToken::AS_STR)?; } generic_args.format(formatted_code, formatter)?; } diff --git a/swayfmt/src/utils/language/pattern.rs b/swayfmt/src/utils/language/pattern.rs index 5c95b2e0ba3..655db700939 100644 --- a/swayfmt/src/utils/language/pattern.rs +++ b/swayfmt/src/utils/language/pattern.rs @@ -10,6 +10,7 @@ use crate::{ }; use std::fmt::Write; use sway_ast::{ + keywords::{ColonToken, DoubleDotToken, Keyword, MutToken, RefToken, Token, UnderscoreToken}, Braces, CommaToken, ExprTupleDescriptor, PathExpr, Pattern, PatternStructField, Punctuated, }; use sway_types::{ast::Delimiter, Spanned}; @@ -30,19 +31,19 @@ impl Format for Pattern { formatted_code.push_str(" | "); rhs.format(formatted_code, formatter)?; } - Self::Wildcard { underscore_token } => { - formatted_code.push_str(underscore_token.span().as_str()) - } + Self::Wildcard { + underscore_token: _, + } => formatted_code.push_str(UnderscoreToken::AS_STR), Self::Var { reference, mutable, name, } => { - if let Some(ref_token) = reference { - write!(formatted_code, "{} ", ref_token.span().as_str())?; + if reference.is_some() { + write!(formatted_code, "{} ", RefToken::AS_STR)?; } - if let Some(mut_token) = mutable { - write!(formatted_code, "{} ", mut_token.span().as_str())?; + if mutable.is_some() { + write!(formatted_code, "{} ", MutToken::AS_STR)?; } name.format(formatted_code, formatter)?; } @@ -188,16 +189,16 @@ impl Format for PatternStructField { formatter: &mut Formatter, ) -> Result<(), FormatterError> { match self { - Self::Rest { token } => { - write!(formatted_code, "{}", token.span().as_str())?; + Self::Rest { token: _ } => { + write!(formatted_code, "{}", DoubleDotToken::AS_STR)?; } Self::Field { field_name, pattern_opt, } => { - write!(formatted_code, "{}", field_name.span().as_str())?; - if let Some((colon_token, pattern)) = pattern_opt { - write!(formatted_code, "{} ", colon_token.span().as_str())?; + write!(formatted_code, "{}", field_name.as_str())?; + if let Some((_colon_token, pattern)) = pattern_opt { + write!(formatted_code, "{} ", ColonToken::AS_STR)?; pattern.format(formatted_code, formatter)?; } } @@ -213,12 +214,12 @@ fn get_field_width( ) -> Result<(usize, usize), FormatterError> { let mut largest_field: usize = 0; let mut body_width: usize = 3; // this is taking into account the opening brace, the following space and the ending brace. - for (field, comma_token) in &fields.value_separator_pairs { + for (field, _comma_token) in &fields.value_separator_pairs { let mut field_str = FormattedCode::new(); field.format(&mut field_str, formatter)?; let mut field_length = field_str.chars().count(); - field_length += comma_token.span().as_str().chars().count(); + field_length += CommaToken::AS_STR.chars().count(); body_width += &field_length + 1; // accounting for the following space if field_length > largest_field { diff --git a/swayfmt/src/utils/language/punctuated.rs b/swayfmt/src/utils/language/punctuated.rs index a06a6c68e55..ff488a60864 100644 --- a/swayfmt/src/utils/language/punctuated.rs +++ b/swayfmt/src/utils/language/punctuated.rs @@ -8,8 +8,9 @@ use crate::{ }; use std::fmt::Write; use sway_ast::{ - keywords::CommaToken, punctuated::Punctuated, ConfigurableField, ItemStorage, StorageEntry, - StorageField, TypeField, + keywords::{ColonToken, CommaToken, EqToken, InToken, Keyword, Token}, + punctuated::Punctuated, + ConfigurableField, ItemStorage, PubToken, StorageEntry, StorageField, TypeField, }; use sway_types::{ast::PunctKind, Ident, Spanned}; @@ -27,7 +28,7 @@ where formatted_code: &mut FormattedCode, formatter: &mut Formatter, ) -> Result<(), FormatterError> { - if !self.value_separator_pairs.is_empty() || self.final_value_opt.is_some() { + if !self.is_empty() { match formatter.shape.code_line.line_style { LineStyle::Normal => { write!( @@ -55,7 +56,7 @@ where if !formatted_code.ends_with('\n') { writeln!(formatted_code)?; } - if !self.value_separator_pairs.is_empty() || self.final_value_opt.is_some() { + if !self.is_empty() { formatter.write_indent_into_buffer(formatted_code)?; } @@ -191,8 +192,8 @@ impl Format for Ident { _formatter: &mut Formatter, ) -> Result<(), FormatterError> { match self.is_raw_ident() { - true => write!(formatted_code, "{}{}", RAW_MODIFIER, self.span().as_str())?, - false => write!(formatted_code, "{}", self.span().as_str())?, + true => write!(formatted_code, "{}{}", RAW_MODIFIER, self.as_str())?, + false => write!(formatted_code, "{}", self.as_str())?, } Ok(()) @@ -206,14 +207,14 @@ impl Format for TypeField { formatter: &mut Formatter, ) -> Result<(), FormatterError> { // If there is a visibility token add it to the formatted_code with a ` ` after it. - if let Some(visibility) = &self.visibility { - write!(formatted_code, "{} ", visibility.span().as_str())?; + if self.visibility.is_some() { + write!(formatted_code, "{} ", PubToken::AS_STR)?; } write!( formatted_code, "{}{} ", - self.name.span().as_str(), - self.colon_token.span().as_str(), + self.name.as_str(), + ColonToken::AS_STR, )?; self.ty.format(formatted_code, formatter)?; @@ -233,11 +234,11 @@ impl Format for ConfigurableField { write!( formatted_code, "{}{} ", - self.name.span().as_str(), - self.colon_token.span().as_str(), + self.name.as_str(), + ColonToken::AS_STR, )?; self.ty.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", self.eq_token.span().as_str())?; + write!(formatted_code, " {} ", EqToken::AS_STR)?; Ok(()) }, @@ -258,17 +259,17 @@ impl Format for StorageField { formatter.with_shape( formatter.shape.with_default_code_line(), |formatter| -> Result<(), FormatterError> { - write!(formatted_code, "{}", self.name.span().as_str())?; - if let Some(in_token) = &self.in_token { - write!(formatted_code, " {}", in_token.span().as_str())?; + write!(formatted_code, "{}", self.name.as_str())?; + if self.in_token.is_some() { + write!(formatted_code, " {} ", InToken::AS_STR)?; } if let Some(key_expr) = &self.key_expr { - write!(formatted_code, " {}", key_expr.span().as_str())?; + key_expr.format(formatted_code, formatter)?; } - write!(formatted_code, "{} ", self.colon_token.span().as_str())?; + write!(formatted_code, "{} ", ColonToken::AS_STR)?; self.ty.format(formatted_code, formatter)?; - write!(formatted_code, " {} ", self.eq_token.span().as_str())?; + write!(formatted_code, " {} ", EqToken::AS_STR)?; Ok(()) }, @@ -328,7 +329,7 @@ impl Format for CommaToken { formatted_code: &mut FormattedCode, _formatter: &mut Formatter, ) -> Result<(), FormatterError> { - write!(formatted_code, "{}", self.span().as_str())?; + write!(formatted_code, "{}", CommaToken::AS_STR)?; Ok(()) } diff --git a/swayfmt/src/utils/language/statement.rs b/swayfmt/src/utils/language/statement.rs index ffbcd443f66..1359161bf2c 100644 --- a/swayfmt/src/utils/language/statement.rs +++ b/swayfmt/src/utils/language/statement.rs @@ -3,7 +3,10 @@ use crate::{ utils::map::byte_span::{ByteSpan, LeafSpans}, }; use std::fmt::Write; -use sway_ast::{Expr, Parens, Punctuated, Statement, StatementLet}; +use sway_ast::{ + keywords::{ColonToken, EqToken, Keyword, LetToken, SemicolonToken, Token}, + Expr, Parens, Punctuated, Statement, StatementLet, +}; use sway_types::{Span, Spanned}; impl Format for Statement { @@ -20,7 +23,7 @@ impl Format for Statement { } /// Remove arguments from the expression if the expression is a method call if -/// the method is a simple two path call (foo.bar()). This needed because in +/// the method is a simple two path call (foo.bar()). This is needed because in /// method calls of two parts they are never broke into multiple lines. /// Arguments however can be broken into multiple lines, and that is handled /// by `write_function_call_arguments` @@ -117,11 +120,11 @@ fn format_statement( } else { expr.format(formatted_code, formatter)?; } - if let Some(semicolon) = semicolon_token_opt { + if semicolon_token_opt.is_some() { if formatter.shape.code_line.line_style == LineStyle::Inline { - write!(formatted_code, "{}", semicolon.span().as_str())?; + write!(formatted_code, "{}", SemicolonToken::AS_STR)?; } else { - writeln!(formatted_code, "{}", semicolon.span().as_str())?; + writeln!(formatted_code, "{}", SemicolonToken::AS_STR)?; } } } @@ -140,24 +143,24 @@ impl Format for StatementLet { formatter: &mut Formatter, ) -> Result<(), FormatterError> { // `let ` - write!(formatted_code, "{} ", self.let_token.span().as_str())?; + write!(formatted_code, "{} ", LetToken::AS_STR)?; // pattern self.pattern.format(formatted_code, formatter)?; // `: Ty` - if let Some(ty) = &self.ty_opt { - write!(formatted_code, "{} ", ty.0.span().as_str())?; - ty.1.format(formatted_code, formatter)?; + if let Some((_colon_token, ty)) = &self.ty_opt { + write!(formatted_code, "{} ", ColonToken::AS_STR)?; + ty.format(formatted_code, formatter)?; } // ` = ` - write!(formatted_code, " {} ", self.eq_token.span().as_str())?; + write!(formatted_code, " {} ", EqToken::AS_STR)?; // expr self.expr.format(formatted_code, formatter)?; if formatter.shape.code_line.line_style == LineStyle::Inline { // `;` - write!(formatted_code, "{}", self.semicolon_token.span().as_str())?; + write!(formatted_code, "{}", SemicolonToken::AS_STR)?; } else { // `;\n` - writeln!(formatted_code, "{}", self.semicolon_token.span().as_str())?; + writeln!(formatted_code, "{}", SemicolonToken::AS_STR)?; } Ok(()) diff --git a/swayfmt/src/utils/language/ty.rs b/swayfmt/src/utils/language/ty.rs index a509cca1567..327ed8165cc 100644 --- a/swayfmt/src/utils/language/ty.rs +++ b/swayfmt/src/utils/language/ty.rs @@ -6,8 +6,12 @@ use std::fmt::Write; use sway_ast::{ brackets::SquareBrackets, expr::Expr, - keywords::{AmpersandToken, MutToken, PtrToken, SliceToken, StrToken, Token, UnderscoreToken}, + keywords::{ + AmpersandToken, BangToken, Keyword, MutToken, PtrToken, SemicolonToken, SliceToken, + StrToken, Token, UnderscoreToken, + }, ty::{Ty, TyArrayDescriptor, TyTupleDescriptor}, + CommaToken, }; use sway_types::{ast::Delimiter, Spanned}; @@ -24,39 +28,35 @@ impl Format for Ty { write!(formatted_code, "{}", Delimiter::Bracket.as_close_char())?; Ok(()) } - Self::Infer { underscore_token } => format_infer(formatted_code, underscore_token), + Self::Infer { + underscore_token: _, + } => format_infer(formatted_code), Self::Path(path_ty) => path_ty.format(formatted_code, formatter), Self::StringSlice(_) => { - write!(formatted_code, "str")?; + write!(formatted_code, "{}", StrToken::AS_STR)?; Ok(()) } - Self::StringArray { str_token, length } => { - format_str(formatted_code, str_token.clone(), length.clone()) - } + Self::StringArray { + str_token: _, + length, + } => format_str(formatted_code, formatter, length.clone()), Self::Tuple(tup_descriptor) => { write!(formatted_code, "{}", Delimiter::Parenthesis.as_open_char())?; tup_descriptor.get().format(formatted_code, formatter)?; write!(formatted_code, "{}", Delimiter::Parenthesis.as_close_char())?; Ok(()) } - Self::Ptr { ptr_token, ty } => { - format_ptr(formatted_code, ptr_token.clone(), ty.clone()) - } + Self::Ptr { ptr_token: _, ty } => format_ptr(formatted_code, formatter, ty.clone()), Self::Slice { slice_token, ty } => { - format_slice(formatted_code, slice_token.clone(), ty.clone()) + format_slice(formatted_code, formatter, slice_token, ty.clone()) } Self::Ref { - ampersand_token, + ampersand_token: _, mut_token, ty, - } => format_ref( - formatted_code, - ampersand_token.clone(), - mut_token.clone(), - ty.clone(), - ), - Self::Never { bang_token } => { - write!(formatted_code, "{}", bang_token.span().as_str(),)?; + } => format_ref(formatted_code, formatter, mut_token, ty), + Self::Never { bang_token: _ } => { + write!(formatted_code, "{}", BangToken::AS_STR)?; Ok(()) } } @@ -64,11 +64,9 @@ impl Format for Ty { } /// Simply inserts a `_` token to the `formatted_code`. -fn format_infer( - formatted_code: &mut FormattedCode, - underscore_token: &UnderscoreToken, -) -> Result<(), FormatterError> { - formatted_code.push_str(underscore_token.ident().as_str()); +fn format_infer(formatted_code: &mut FormattedCode) -> Result<(), FormatterError> { + formatted_code.push_str(UnderscoreToken::AS_STR); + Ok(()) } @@ -79,78 +77,73 @@ impl Format for TyArrayDescriptor { formatter: &mut Formatter, ) -> Result<(), FormatterError> { self.ty.format(formatted_code, formatter)?; - write!(formatted_code, "{} ", self.semicolon_token.span().as_str())?; + write!(formatted_code, "{} ", SemicolonToken::AS_STR)?; self.length.format(formatted_code, formatter)?; + Ok(()) } } fn format_str( formatted_code: &mut FormattedCode, - str_token: StrToken, + formatter: &mut Formatter, length: SquareBrackets>, ) -> Result<(), FormatterError> { - write!( - formatted_code, - "{}{}{}{}", - str_token.span().as_str(), - Delimiter::Bracket.as_open_char(), - length.into_inner().span().as_str(), - Delimiter::Bracket.as_close_char() - )?; + write!(formatted_code, "{}", StrToken::AS_STR)?; + write!(formatted_code, "{}", Delimiter::Bracket.as_open_char())?; + length.into_inner().format(formatted_code, formatter)?; + write!(formatted_code, "{}", Delimiter::Bracket.as_close_char())?; + Ok(()) } fn format_ptr( formatted_code: &mut FormattedCode, - ptr_token: PtrToken, + formatter: &mut Formatter, ty: SquareBrackets>, ) -> Result<(), FormatterError> { - write!( - formatted_code, - "{}[{}]", - ptr_token.span().as_str(), - ty.into_inner().span().as_str() - )?; + write!(formatted_code, "{}", PtrToken::AS_STR)?; + write!(formatted_code, "{}", Delimiter::Bracket.as_open_char())?; + ty.into_inner().format(formatted_code, formatter)?; + write!(formatted_code, "{}", Delimiter::Bracket.as_close_char())?; + Ok(()) } fn format_slice( formatted_code: &mut FormattedCode, - slice_token: Option, + formatter: &mut Formatter, + slice_token: &Option, ty: SquareBrackets>, ) -> Result<(), FormatterError> { - if let Some(slice_token) = slice_token { - write!( - formatted_code, - "{}[{}]", - slice_token.span().as_str(), - ty.into_inner().span().as_str() - )?; - } else { - write!(formatted_code, "[{}]", ty.into_inner().span().as_str())?; + if slice_token.is_some() { + write!(formatted_code, "{}", SliceToken::AS_STR)?; } + write!(formatted_code, "{}", Delimiter::Bracket.as_open_char())?; + ty.into_inner().format(formatted_code, formatter)?; + write!(formatted_code, "{}", Delimiter::Bracket.as_close_char())?; Ok(()) } fn format_ref( formatted_code: &mut FormattedCode, - ampersand_token: AmpersandToken, - mut_token: Option, - ty: Box, + formatter: &mut Formatter, + mut_token: &Option, + ty: &Ty, ) -> Result<(), FormatterError> { write!( formatted_code, - "{}{}{}", - ampersand_token.span().as_str(), - if let Some(mut_token) = mut_token { - format!("{} ", mut_token.span().as_str()) + "{}{}", + AmpersandToken::AS_STR, + if mut_token.is_some() { + format!("{} ", MutToken::AS_STR) } else { "".to_string() }, - ty.span().as_str() )?; + ty.format(formatted_code, formatter)?; + Ok(()) } @@ -162,7 +155,7 @@ impl Format for TyTupleDescriptor { ) -> Result<(), FormatterError> { if let TyTupleDescriptor::Cons { head, - comma_token, + comma_token: _, tail, } = self { @@ -170,7 +163,7 @@ impl Format for TyTupleDescriptor { formatter.shape.with_default_code_line(), |formatter| -> Result<(), FormatterError> { head.format(formatted_code, formatter)?; - write!(formatted_code, "{} ", comma_token.ident().as_str())?; + write!(formatted_code, "{} ", CommaToken::AS_STR)?; tail.format(formatted_code, formatter)?; Ok(()) diff --git a/swayfmt/src/utils/language/where_clause.rs b/swayfmt/src/utils/language/where_clause.rs index 6a365370572..805bac01b86 100644 --- a/swayfmt/src/utils/language/where_clause.rs +++ b/swayfmt/src/utils/language/where_clause.rs @@ -3,8 +3,11 @@ use crate::{ utils::map::byte_span::{ByteSpan, LeafSpans}, }; use std::fmt::Write; -use sway_ast::{WhereBound, WhereClause}; -use sway_types::{ast::PunctKind, Spanned}; +use sway_ast::{ + keywords::{ColonToken, Keyword, Token, WhereToken}, + CommaToken, WhereBound, WhereClause, +}; +use sway_types::Spanned; impl Format for WhereClause { fn format( @@ -15,8 +18,8 @@ impl Format for WhereClause { writeln!( formatted_code, "{}{}", - &formatter.indent_to_str()?, - self.where_token.span().as_str(), + formatter.indent_to_str()?, + WhereToken::AS_STR, )?; formatter.indent(); // We should add a multiline field to `Shape` @@ -27,18 +30,19 @@ impl Format for WhereClause { // ``` // let value_pairs = self.bounds.value_separator_pairs.clone(); - for (bound, comma_token) in value_pairs.iter() { + for (bound, _comma_token) in value_pairs.iter() { // `WhereBound` bound.format(formatted_code, formatter)?; // `CommaToken` - writeln!(formatted_code, "{}", comma_token.span().as_str())?; + writeln!(formatted_code, "{}", CommaToken::AS_STR)?; } if let Some(final_value) = &self.bounds.final_value_opt { final_value.format(formatted_code, formatter)?; - writeln!(formatted_code, "{}", PunctKind::Comma.as_char())?; + writeln!(formatted_code, "{}", CommaToken::AS_STR)?; } // reset indent formatter.unindent(); + Ok(()) } } @@ -52,11 +56,12 @@ impl Format for WhereBound { write!( formatted_code, "{}{}{} ", - &formatter.indent_to_str()?, // `Indent` - self.ty_name.span().as_str(), // `Ident` - self.colon_token.span().as_str(), // `ColonToken` + formatter.indent_to_str()?, + self.ty_name.as_str(), + ColonToken::AS_STR, )?; self.bounds.format(formatted_code, formatter)?; + Ok(()) } } diff --git a/swayfmt/src/utils/map/byte_span.rs b/swayfmt/src/utils/map/byte_span.rs index 36578739dcf..fec51afdc1c 100644 --- a/swayfmt/src/utils/map/byte_span.rs +++ b/swayfmt/src/utils/map/byte_span.rs @@ -49,8 +49,8 @@ impl ByteSpan { impl Ord for ByteSpan { fn cmp(&self, other: &Self) -> Ordering { - // If the starting position is the same encapsulatig span (i.e, wider one) should come - // first + // If the starting position is the same encapsulating span (i.e, wider one) should come + // first. match self.start.cmp(&other.start) { Ordering::Equal => other.end.cmp(&self.end), ord => ord, diff --git a/swayfmt/src/utils/map/newline.rs b/swayfmt/src/utils/map/newline.rs index 769b05e7ad3..9600248beee 100644 --- a/swayfmt/src/utils/map/newline.rs +++ b/swayfmt/src/utils/map/newline.rs @@ -1,8 +1,7 @@ use anyhow::Result; use ropey::{str_utils::byte_to_char_idx, Rope}; -use std::{collections::BTreeMap, fmt::Display, path::PathBuf, sync::Arc}; +use std::{collections::BTreeMap, fmt::Display, sync::Arc}; use sway_ast::Module; -use sway_types::SourceEngine; use crate::{ constants::NEW_LINE, @@ -106,11 +105,9 @@ fn newline_map_from_src(unformatted_input: &str) -> Result, unformatted_module: &Module, formatted_input: Arc, - path: Option>, formatted_code: &mut FormattedCode, formatter: &Formatter, ) -> Result<(), FormatterError> { @@ -123,7 +120,7 @@ pub fn handle_newlines( // formatting the code a second time will still produce the same result. let newline_map = newline_map_from_src(&unformatted_input)?; // After the formatting existing items should be the same (type of the item) but their spans will be changed since we applied formatting to them. - let formatted_module = parse_file(source_engine, formatted_input, path)?.value; + let formatted_module = parse_file(formatted_input)?.value; // Actually find & insert the newline sequences add_newlines( newline_map, diff --git a/swayfmt/tests/mod.rs b/swayfmt/tests/mod.rs index 890995ef338..c3369d150c3 100644 --- a/swayfmt/tests/mod.rs +++ b/swayfmt/tests/mod.rs @@ -6,11 +6,11 @@ use test_macros::assert_eq_pretty; /// Takes a configured formatter as input and formats a given input and checks the actual output against an /// expected output. There are two format passes to ensure that the received output does not change on a second pass. fn check_with_formatter(unformatted: &str, expected: &str, formatter: &mut Formatter) { - let first_formatted = Formatter::format(formatter, Arc::from(unformatted), None).unwrap(); + let first_formatted = Formatter::format(formatter, Arc::from(unformatted)).unwrap(); assert_eq_pretty!(first_formatted, expected); let second_formatted = - Formatter::format(formatter, Arc::from(first_formatted.clone()), None).unwrap(); + Formatter::format(formatter, Arc::from(first_formatted.clone())).unwrap(); assert_eq_pretty!(second_formatted, first_formatted); } @@ -74,7 +74,33 @@ fn struct_alignment() { contract; pub struct Foo { barbazfoo: u64, - baz : bool, + baz : bool, + } + "#}, + indoc! {r#" + contract; + pub struct Foo { + barbazfoo : u64, + baz : bool, + } + "#}, + &mut formatter, + ); +} + +#[test] +#[ignore = "Bug in `swayfmt`. Activate this test once https://github.com/FuelLabs/sway/issues/6805 is fixed."] +fn struct_alignment_without_trailing_comma() { + // The last struct field does not have trailing comma. + let mut formatter = Formatter::default(); + formatter.config.structures.field_alignment = FieldAlignment::AlignFields(40); + + check_with_formatter( + indoc! {r#" + contract; + pub struct Foo { + barbazfoo: u64, + baz : bool } "#}, indoc! {r#" @@ -98,7 +124,51 @@ fn struct_alignment_with_public_fields() { contract; pub struct Foo { barbazfoo: u64, - pub baz : bool, + pub baz : bool, + } + "#}, + indoc! {r#" + contract; + pub struct Foo { + barbazfoo : u64, + pub baz : bool, + } + "#}, + &mut formatter, + ); + + check_with_formatter( + indoc! {r#" + contract; + pub struct Foo { + pub barbazfoo: u64, + baz : bool, + } + "#}, + indoc! {r#" + contract; + pub struct Foo { + pub barbazfoo : u64, + baz : bool, + } + "#}, + &mut formatter, + ); +} + +#[test] +#[ignore = "Bug in `swayfmt`. Activate this test once https://github.com/FuelLabs/sway/issues/6805 is fixed."] +fn struct_alignment_with_public_fields_without_trailing_comma() { + // The last struct field does not have trailing comma. + let mut formatter = Formatter::default(); + formatter.config.structures.field_alignment = FieldAlignment::AlignFields(40); + + check_with_formatter( + indoc! {r#" + contract; + pub struct Foo { + barbazfoo: u64, + pub baz : bool } "#}, indoc! {r#" @@ -116,7 +186,7 @@ fn struct_alignment_with_public_fields() { contract; pub struct Foo { pub barbazfoo: u64, - baz : bool, + baz : bool } "#}, indoc! {r#" @@ -157,7 +227,34 @@ fn struct_public_fields() { } #[test] -fn struct_ending_comma() { +fn struct_public_fields_without_trailing_comma() { + // The last struct field does not have trailing comma. + let mut formatter = Formatter::default(); + formatter.config.structures.field_alignment = FieldAlignment::Off; + + check_with_formatter( + indoc! {r#" + contract; + pub struct Foo { + pub barbaz: T, + foo: u64, + pub baz : bool + } + "#}, + indoc! {r#" + contract; + pub struct Foo { + pub barbaz: T, + foo: u64, + pub baz: bool, + } + "#}, + &mut formatter, + ); +} + +#[test] +fn struct_add_ending_comma() { check( indoc! {r#" contract; @@ -183,10 +280,10 @@ fn enum_without_variant_alignment() { contract; enum Color { - Blue: (), Green: (), - Red: (), - Silver: (), - Grey: () } + Blue: (), Green: (), + Red: (), + Silver: () , + Grey: () , } "#}, indoc! {r#" contract; @@ -204,7 +301,6 @@ fn enum_without_variant_alignment() { #[test] fn enum_with_variant_alignment() { - // Creating a config with enum_variant_align_threshold that exceeds longest variant length let mut formatter = Formatter::default(); formatter.config.structures.field_alignment = FieldAlignment::AlignFields(20); check_with_formatter( @@ -212,10 +308,68 @@ fn enum_with_variant_alignment() { contract; enum Color { - Blue: (), Green: (), - Red: (), + Blue: (), Green: (), + Red: (), + Silver: () , + Grey: () , } + "#}, + indoc! {r#" + contract; + + enum Color { + Blue : (), + Green : (), + Red : (), + Silver : (), + Grey : (), + } + "#}, + &mut formatter, + ); +} + +#[test] +fn enum_without_variant_alignment_without_trailing_comma() { + // The last enum variant does not have trailing comma. + check( + indoc! {r#" + contract; + + enum Color { + Blue: (), Green : (), + Red : (), + Silver: () , + Grey: () } + "#}, + indoc! {r#" + contract; + + enum Color { + Blue: (), + Green: (), + Red: (), Silver: (), - Grey: (), } + Grey: (), + } + "#}, + ); +} + +#[test] +#[ignore = "Bug in `swayfmt`. Activate this test once https://github.com/FuelLabs/sway/issues/6805 is fixed."] +fn enum_with_variant_alignment_without_trailing_comma() { + // The last enum variant does not have trailing comma. + let mut formatter = Formatter::default(); + formatter.config.structures.field_alignment = FieldAlignment::AlignFields(20); + check_with_formatter( + indoc! {r#" + contract; + + enum Color { + Blue: (), Green : (), + Red : (), + Silver: () , + Grey: () } "#}, indoc! {r#" contract; @@ -232,6 +386,119 @@ fn enum_with_variant_alignment() { ); } +#[test] +fn configurable_without_alignment() { + check( + indoc! {r#" + contract; + + configurable { + Blue: u64 = 0, Green: u64 = 0, + Red: u64=0, + Silver: u64= 0, + Grey: u64 =0, } + "#}, + indoc! {r#" + contract; + + configurable { + Blue: u64 = 0, + Green: u64 = 0, + Red: u64 = 0, + Silver: u64 = 0, + Grey: u64 = 0, + } + "#}, + ); +} + +#[test] +fn configurable_with_alignment() { + let mut formatter = Formatter::default(); + formatter.config.structures.field_alignment = FieldAlignment::AlignFields(20); + check_with_formatter( + indoc! {r#" + contract; + + configurable { + Blue: u64 = 0, Green: u64 = 0, + Red: u64=0, + Silver: u64= 0, + Grey: u64 =0, } + "#}, + indoc! {r#" + contract; + + configurable { + Blue : u64 = 0, + Green : u64 = 0, + Red : u64 = 0, + Silver : u64 = 0, + Grey : u64 = 0, + } + "#}, + &mut formatter, + ); +} + +#[test] +fn configurable_without_alignment_without_trailing_comma() { + // The last configurable does not have trailing comma. + check( + indoc! {r#" + contract; + + configurable { + Blue: u64 = 0, Green: u64 = 0, + Red: u64=0, + Silver: u64= 0, + Grey: u64 =0 } + "#}, + indoc! {r#" + contract; + + configurable { + Blue: u64 = 0, + Green: u64 = 0, + Red: u64 = 0, + Silver: u64 = 0, + Grey: u64 = 0, + } + "#}, + ); +} + +#[test] +#[ignore = "Bug in `swayfmt`. Activate this test once https://github.com/FuelLabs/sway/issues/6805 is fixed."] +fn configurable_with_alignment_without_trailing_comma() { + // The last configurable does not have trailing comma. + let mut formatter = Formatter::default(); + formatter.config.structures.field_alignment = FieldAlignment::AlignFields(20); + check_with_formatter( + indoc! {r#" + contract; + + configurable { + Blue: u64 = 0, Green: u64 = 0, + Red: u64=0, + Silver: u64= 0, + Grey: u64 =0 } + "#}, + indoc! {r#" + contract; + + configurable { + Blue : u64 = 0, + Green : u64 = 0, + Red : u64 = 0, + Silver : u64 = 0, + Grey : u64 = 0, + } + "#}, + &mut formatter, + ); +} + #[test] fn item_abi_with_generics_and_attributes() { check( @@ -3155,3 +3422,30 @@ fn retain_in_keyword() { "#}, ); } + +#[test] +fn tuple_field_access() { + check( + indoc! {r#" + contract; + + fn fun() { + let t = (1, 1); + let a = t . 0; + let b = t + . + 1 + ; + } + "#}, + indoc! {r#" + contract; + + fn fun() { + let t = (1, 1); + let a = t.0; + let b = t.1; + } + "#}, + ); +}