GithubHelp home page GithubHelp logo

verusfmt's People

Contributors

jaybosamiya avatar parno avatar pratapsingh1729 avatar tjhance avatar utaal avatar utaal-b avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

verusfmt's Issues

Formatting of multi-line `seq!` macro

verusfmt 0.2.8 formats:

use vstd::prelude::*;

verus! {

proof fn f() {
    let s =
        seq![
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];
}

} // verus!

This occurs with two lines or more. With only one you get:

proof fn f() {
    let s = seq![
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    ];
}

Bug in inline comment handling after an attribute

Before:

#[verifier(external_body)] // comment breaks it! 
pub exec fn foo(a: usize) -> (b: usize)
    requires a > 5
    ensures b > 5
{
    a + 2
}

After:

#[verifier(external_body)]  // comment breaks it!pub exec fn foo(a: usize) -> (b: usize)
    requires
        a > 5,
    ensures
        b > 5,
{
    a + 2
}

The newline after the inline comment is gone.

Report filename when formatting multiple files

verusfmt accepts multiple files on the command line. However, when one of them contains an error, we only report the line number, without reporting the filename, leaving it ambiguous where the error lies.

odd newline & indentation choices

From vstd/storage_protocol.rs:

    forall|q: P|
        #![all_triggers]
        P::op(p1, q).inv() ==> P::op(p2, q).inv() && P::op(p1, q).interp().dom().disjoint(b1.dom())
            && P::op(p2, q).interp().dom().disjoint(b2.dom()) && P::op(
            p1, 
            q,  
        ).interp().union_prefer_right(b1) =~= P::op(p2, q).interp().union_prefer_right(b2)

very strange indentation choices here

`verusfmt` doesn't handle borrows of identifiers that start with the characters `mut`

Input:

verus! {
pub exec fn foo(mut_state: &mut Blah) {
    let a = { b(&mut_state.c) };
}
} // verus!

Error:

Error:  --> 7:27
  |
7 |     let a = { b(&mut_state.c) };
  |                           ^---
  |
  = expected COMMENT, bang_str, colons_str, generic_arg_list, or record_expr_field_list

The error goes away and the code is correctly formatted if I do any of the following:

  • remove the borrow: let a = { b(mut_state.c) };
  • remove the characters mut_: let a = { b(&state.c) };
  • prepend something before mut: let a = { b(&xmut_state.c) };

I would guess this is something to do with the parsing of &mut? rustfmt is able to handle the analogous Rust code:

pub fn foo(mut_state: &mut Blah) {
    let a = { b(&mut_state) };
}

Unnecessary newline in same-file `mod`ules

Consider

verus! { mod foo { fn foo() { } } }

It currently gets formatted to:

verus! {

mod foo {
    fn foo() {
    }

}

} // verus!

It would be preferable if it were formatted to

verus! {

mod foo {
    fn foo() {
    }
}

} // verus!

Non-idempotency of macro items in presence of in-file `mod`ules

verusfmt seems to want to keep moving macro items further to the right on each run

mod foo {
    verus! {

    bar! {
        baz
    }

    } // verus!
}
$ cargo run -- temp.rs
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
     Running `target/debug/verusfmt temp.rs`

$ cargo run -- --check temp.rs
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s
     Running `target/debug/verusfmt --check temp.rs`
   0.062335083s ERROR Input found not to be well formatted
--- temp.rs
+++ temp.rs.formatted
@@ -2,8 +2,8 @@
     verus! {

     bar! {
-            baz
-        }
+                baz
+            }

     } // verus!
 }

Error:   × invalid formatting

$ cargo run -- temp.rs
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
     Running `target/debug/verusfmt temp.rs`

$ cargo run -- --check temp.rs
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s
     Running `target/debug/verusfmt --check temp.rs`
   0.075480083s ERROR Input found not to be well formatted
--- temp.rs
+++ temp.rs.formatted
@@ -2,8 +2,8 @@
     verus! {

     bar! {
-                baz
-            }
+                    baz
+                }

     } // verus!
 }

Error:   × invalid formatting

Non-idempotency for comment being moved to same line

Consider the following code:

verus! {
impl a {
    fn b()
    //
    ;
}
}

Formatting this is non-idempotent (it does settle to a steady state in 2 iterations though; the first time around, it just doesn't place enough spaces for some reason)

$ cargo run --release -- test.rs
    Finished release [optimized] target(s) in 0.08s
     Running `target/release/verusfmt test.rs`

$ cargo run --release -- --check test.rs
    Finished release [optimized] target(s) in 0.08s
     Running `target/release/verusfmt --check test.rs`
   0.045484917s ERROR Input found not to be well formatted
--- original
+++ formatted
@@ -1,7 +1,7 @@
 verus! {

 impl a {
-    fn b()//
+    fn b()  //
     ;
 }


Error: invalid formatting

It appears that the rules parsed/processed themselves seem to be the same in the --debug output:

$ # Remember to reset `test.rs` to the file at the start of this issue
$ cargo run --release -- --debug test.rs
    Finished release [optimized] target(s) in 0.07s
     Running `target/release/verusfmt --debug test.rs`
   0.044931166s  INFO Processing rule verus_macro_body
   0.044958041s  INFO Processing rule item
   0.044959375s  INFO Processing rule impl
   0.044960458s  INFO Processing rule impl_str
   0.044961666s  INFO Processing rule type
   0.044962541s  INFO Processing rule path_type
   0.044963333s  INFO Processing rule path
   0.044964083s  INFO Processing rule path_segment
   0.044964958s  INFO Processing rule name_ref
   0.044966000s  INFO Processing rule assoc_item_list
   0.044966833s  INFO Processing rule assoc_items
   0.044967791s  INFO Processing rule assoc_item
   0.044968583s  INFO Processing rule fn
   0.044969833s  INFO Processing rule fn_str
   0.044970750s  INFO Processing rule name
   0.044971583s  INFO Processing rule param_list
   0.045028083s  INFO Processing rule COMMENT
   0.045031583s  INFO Processing rule fn_qualifier
   0.045032875s  INFO Processing rule fn_terminator
   0.045033791s  INFO Processing rule semi_str

$ cargo run --release -- --debug --check test.rs
    Finished release [optimized] target(s) in 0.07s
     Running `target/release/verusfmt --debug --check test.rs`
   0.044528708s  INFO Processing rule verus_macro_body
   0.044565125s  INFO Processing rule item
   0.044566583s  INFO Processing rule impl
   0.044567833s  INFO Processing rule impl_str
   0.044569000s  INFO Processing rule type
   0.044569875s  INFO Processing rule path_type
   0.044570666s  INFO Processing rule path
   0.044571500s  INFO Processing rule path_segment
   0.044572458s  INFO Processing rule name_ref
   0.044573458s  INFO Processing rule assoc_item_list
   0.044574291s  INFO Processing rule assoc_items
   0.044575291s  INFO Processing rule assoc_item
   0.044576041s  INFO Processing rule fn
   0.044577333s  INFO Processing rule fn_str
   0.044578291s  INFO Processing rule name
   0.044579083s  INFO Processing rule param_list
   0.044644166s  INFO Processing rule COMMENT
   0.044647583s  INFO Processing rule fn_qualifier
   0.044648583s  INFO Processing rule fn_terminator
   0.044649375s  INFO Processing rule semi_str
   0.044755708s  INFO Found some differences in test.rs
   0.044770125s ERROR Input found not to be well formatted
--- original
+++ formatted
@@ -1,7 +1,7 @@
 verus! {

 impl a {
-    fn b()//
+    fn b()  //
     ;
 }


Error: invalid formatting

Mis-parsing requires clauses as generic arguments

In this program:

verus! {

fn test()
    requires i < 0, len > 0,
{
}    

} // verus!

We parse the requires clause as the path_expr i<0,len> followed by the unexpected token 0, rather than the intended meaning of two binary expressions, namely i < 0 and len > 0. I don't immediately see an easy fix.

Incorrect operator precedence for `&&&` and `forall`

We seem to have a misparse happening due to precedence of special bulleted ops and quantifiers. Minimized example is:

&&& forall|| f
&&& g

which parses as

- expr > expr_inner > prefix_expr
  - triple_and: "&&&"
  - expr > expr_inner > quantifier_expr
    - forall_str: "forall"
    - closure_param_list: "||"
    - expr
      - expr_inner > path_expr_no_generics > path_no_generics > path_segment_no_generics > name_ref > identifier: "f"
      - bin_expr_ops > bin_expr_ops_special > triple_and: "&&&"
      - expr > expr_inner > path_expr_no_generics > path_no_generics > path_segment_no_generics > name_ref > identifier: "g"

Instead, the g should not be considered in the body of the forall.


Original report by @jialin-li:

verusfmt's interaction with bulleted & and forall looks a little misleading, this is the result post formatting

        &&& forall|au|
            #![auto]
            deallocs.contains(au) ==> self.mini_allocator.can_remove(au)
            &&& post == EphemeralState {
                mini_allocator: self.mini_allocator.prune(deallocs),
                ..self
            }
            &&& new_dv == dv

this is also true for exists

||| exists|cut, addr|
            self.is_marshalled_state(dv, allocs, deallocs, cut, addr, post, new_dv)
            ||| self.is_allocator_fill(dv, allocs, deallocs, post, new_dv)
            ||| self.is_allocator_prune(dv, allocs, deallocs, post, new_dv)
            ||| self.is_no_op(dv, allocs, deallocs, post, new_dv)

Better handle CRLF normalization

If we format a properly-formatted file that has Windows newlines, then verusfmt replaces them with Unix newlines (at least on a Unix system (possibly most confusingly with WSL?)); however, this may cause confusion on verusfmt --check since literally every line will look visually identical in the diff. It would be nice if we can detect this as the reason, and provide a better error message rather than dump the whole file in the -/+ of the diff.

Core issue hit originally by @jaylorch, likely due to git's handling of newline changes.

Parser fails on lifetimes in the parameter list and return type

This example:

fn get(&'a self) -> (o: &'a V) {
    0
}

fails with:

1 | fn get(&'a self) -> (o: &'a V) {
  |                           ^---
  |
  = expected COMMENT, colon_str, dot_dot_eq_str, dot_dot_str, or pipe_str

but the following work:

fn get(&'a self) -> (o: V) {
    0
}
fn get(a:int) -> (o: &'a V) {
    0
}
fn get(&'a self, b:int) -> (o: &'a V) {
    0
}

And on its own, -> (o: &'a V) is correctly parsed as a ret_type.

This would be easier to debug if we could get a partial parse tree out of Pest when it fails.

Preservation of stanzas

It's a natural pattern to group lines into logical groupings. However, verusfmt collapses empty lines.

> cat verusfmt.rs
use vstd::prelude::*;

verus! {

fn f() {
    // First stanza.
    let a = 1;
    let b = a;

    // Second stanza.
    let c = a;
    let d = c;
}

} // verus!
> verusfmt --check verusfmt.rs
   0.054808000s ERROR Input found not to be well formatted
--- verusfmt.rs
+++ verusfmt.rs.formatted
@@ -6,7 +6,6 @@
     // First stanza.
     let a = 1;
     let b = a;
-
     // Second stanza.
     let c = a;
     let d = c;

Error:   × invalid formatting

Handling inline comments outside the main verus-body

The main parse_and_format has special handling for top-level comments and items, but this means those items don't benefit from/share code with the main formatting logic in to_doc. For example, when we process this code:

verus! {

pub type ReplicaId = usize; // $line_count$Trusted$

} // verus!

It gets parsed as a top-level type_alias item followed by a comment that gets treated as a top-level suffix comment in parse_and_format, which means we don't notice that it's been flagged as an inline comment. We could replicate that logic in parse_and_format, but I suspect we should instead try to simplify parse_and_format and rely more consistently on to_doc.

Allow marking a file as skipped

Currently, a file needs to be parse-able to be able to add a #[verusfmt::skip] declaration to an item. However, it should be possible to support a file-level #![verusfmt::skip] declaration that doesn't need any parser support (at least nothing any further than src/verus-minimal.pest).

This can help unblock new syntax or features in Verus that are being blocked by yet-to-be-added support in verusfmt. However, I am concerned that this might lead to files being accidentally left as unformatted for too long. One mitigation for this is to attempt a parsing check anyways if #![verusfmt::skip] is found, and if parsing succeeds, then throw in a warning saying that more specific #[verusfmt::skip]s are preferred. If we're being fancy, we can even suggest locations to insert those in based on the results of --check, but I think at minimum, an implementation of this should do a "warn if parsing succeeds" check.


CC: @Chris-Hawblitzel who asked if there was a way to skip an entire file

Wildcard type annotations on `let`-bindings are removed

Before:

use vstd::prelude::*;

verus! {

fn foo() {
    let x: _ = bar();
}

} // verus!

verusfmt output:

use vstd::prelude::*;

verus! {

fn foo() {
    let x:  = bar();
}

} // verus!

The wildcard type annotation on x is gone.

(This is minimized from a larger example in my codegen where the type annotation is on a tuple, but I want to annotate only one element of the tuple.)

Non-idempotent `verus!` inside `macro_rules!`

macro_rules! a {
    () => {
        verus! {
        }
    }
}
$ cargo run -- -Z idempotency-test foo.rs
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s
     Running `target/debug/verusfmt -Z idempotency-test foo.rs`
   0.115861292s ERROR 😱Formatting found to not be idempotent😱
--- foo.rs.formatted-once
+++ foo.rs.formatted-twice
@@ -1,6 +1,6 @@
 macro_rules! a {
     () => {
         verus! {
-                }
+                        }
     };
 }

Add support for locally opting out of verusfmt

In some (hopefully rare) cases, the developer has a better idea of how a term should be formatted. For example, here:

fn pow2() {
    assert(
        pow2(0) == 0x1 &&
        pow2(1) == 0x2 &&
        pow2(2) == 0x4 &&
        pow2(3) == 0x8 &&
        pow2(4) == 0x10 &&
        pow2(5) == 0x20 &&
        pow2(6) == 0x40 &&
...
        pow2(32) == 0x100000000 &&
        pow2(64) == 0x10000000000000000
   ) by(compute_only);
}

The formatting is intended to make it clear how the exponents are progressing, whereas verusfmt tries to fit as many terms as it can on each line. One way to support such cases is via functionality similar to rustfmt's #[rustfmt::skip] attribute.

Bug with macro calls

verus! {

pub exec fn foo() {
    let temp_owl__x23 = { let x: Vec<u8> = mk_vec_u8!(); x };
}

} // verus!

This breaks with the following error:

Error:  --> 4:58
  |
4 |     let temp_owl__x23 = { let x: Vec<u8> = mk_vec_u8!(); x };
  |                                                          ^---
  |
  = expected COMMENT, at_str, dot_str, dot_dot_str, dot_dot_eq_str, lbracket_str, question_str, semi_str, as_str, else_str, has_str, is_str, bin_expr_ops, or arg_list

It works fine if I make the mk_vec_u8!() into a normal function call, like mk_vec_u8(). Seems like something with the parser/grammar.

`impl TRAIT for TYPE`

On branch pretty.

Before:

impl OwlSpecSerialize for owlSpec_t {
open spec fn as_seq(self) -> Seq<u8> { serialize_owlSpec_t(self) }
}

After:

impl OwlSpecSerializefor owlSpec_t {
    open spec fn as_seq(self) -> Seq<u8> {
        serialize_owlSpec_t(self)
    }
}

Note the space before the for keyword is gone.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.