ocamlformat icon indicating copy to clipboard operation
ocamlformat copied to clipboard

Unexpected parens on type definitions with [@foo]

Open Wilfred opened this issue 6 years ago • 1 comments

Given the following code:

module Foo = struct
     type t = int
end

type foo_t = Foo.t [@visitors.opaque]

type apple = Apple [@visitors.opaque]

type fruit = Orange | Pear [@visitors.opaque]

ocamlformat produces:

module Foo = struct
  type t = int
end

type foo_t = (Foo.t[@visitors.opaque])

type apple = Apple [@visitors.opaque]

type fruit = Orange | Pear [@visitors.opaque]

I don't understand why it's added parentheses around Foo.t here.

Wilfred avatar Jan 23 '19 14:01 Wilfred

This is due to a current deficiency in the internal implementation. The core parenthesization logic was designed without taking attributes fully into account. In particular, there are more forms of "context" in which a type (or expression, etc.) can appear that might need to be distinguished when attributes are present. The current implementation is focused on determining which parentheses are needed to eliminate the ambiguity of parsing, but I overlooked the ambiguity of attribute placement. So in code such as:

type t = Foo of (t [@foo]) [@foo] [@@foo]

type foo_t = (Foo.t [@visitors.opaque])

the "context" for t in the first declaration is the module item and for Foo.t in the second the "context" is also the full module item. There is no context form for the intermediate "argument of a constructor". So since, as the first example above shows, arguments of constructors need to parenthesized, cases like the second end up being parenthesized as well, even though not strictly necessary.

TL;DR: it would be possible to refine the formatter to avoid some extraneous parentheses around attributed types, but the change is involved.

jberdine avatar Jan 24 '19 23:01 jberdine