When using attributes with let-bindings the calculation for the beginning of the new statement is off by 1, and sometimes more: if the attribute includes parens, it requires these parens to start past the previous let-binding.
It is correct, however, for class members.
Repro steps
Let bindings repro
This throws FS0058 on "let"
module Literals =
[<Literal>] let First = 1
[<Literal>] let Second = 2
//..........^............. // location of the FS0058 warnings
Minimal indentation to prevent warning (which oddly suggests it is an inner let-binding, but this is not how it gets compiled)
module Literals =
[<Literal>] let First = 1
[<Literal>] let Second = 2
Let bindings with parens repro
module Literals =
[<CompiledName("Boolean")>] let boolean = true
[<CompiledName("MyString")>] let myString = "test"
//............^..............^.................... // locations of the FS0058 warnings
Minimal indentation to prevent both warnings (to prevent only the second, one space is enough):
module Literals =
[<CompiledName("Boolean")>] let boolean = true
[<CompiledName("MyString")>] let myString = "test"
This obviously makes this utterly unreadable.
Positive example of the same scenario with members
No warning is thrown when using the same scenario on members, the keywords, here static member can appear right underneath one another, as they should.
module Literals =
type LitTest =
[<CompiledName("TryMe1")>] static member TryMe() = ()
[<CompiledName("Really")>] static member TryMe2() = ()
Expected behavior
No warnings in either case.
Known workarounds
The workarounds are:
- Don't use attributes on let-bindings (may not be possible)
- Don't use them on the same line (lesser readability)
- Ignore the warning with
#nowarn "58", but this will, unfortunately, apply to the whole file
- (edit) Use
let [<attrib>] identifier instead of [<attrib>] let identifier; this may well be the best and easiest workaround (but I still believe it is an error in offset calculation for the original issue).
Related information
Tested with most recent (as of today) versions of F# 4.0 on VS2015, Update 3. Not tested yet with F# 4.1. I believe this behavior appeared on older versions of F# as well, so it may have been raised before, but I couldn't find it reported.
While not a huge issue of sorts, it is often desirable to be able to align declarations for readability. It just clutters the eye when you require multiple lines for simple constants (whereas most F# statements are beautifully succinct). Usually F# does an excellent job at it, but somehow it fails with the omnipresent let-binding in conjunction with attributes.
When using attributes with let-bindings the calculation for the beginning of the new statement is off by 1, and sometimes more: if the attribute includes parens, it requires these parens to start past the previous let-binding.
It is correct, however, for class members.
Repro steps
Let bindings repro
This throws FS0058 on "let"
Minimal indentation to prevent warning (which oddly suggests it is an inner let-binding, but this is not how it gets compiled)
Let bindings with parens repro
Minimal indentation to prevent both warnings (to prevent only the second, one space is enough):
This obviously makes this utterly unreadable.
Positive example of the same scenario with members
No warning is thrown when using the same scenario on members, the keywords, here
static membercan appear right underneath one another, as they should.Expected behavior
No warnings in either case.
Known workarounds
The workarounds are:
#nowarn "58", but this will, unfortunately, apply to the whole filelet [<attrib>] identifierinstead of[<attrib>] let identifier; this may well be the best and easiest workaround (but I still believe it is an error in offset calculation for the original issue).Related information
Tested with most recent (as of today) versions of F# 4.0 on VS2015, Update 3. Not tested yet with F# 4.1. I believe this behavior appeared on older versions of F# as well, so it may have been raised before, but I couldn't find it reported.
While not a huge issue of sorts, it is often desirable to be able to align declarations for readability. It just clutters the eye when you require multiple lines for simple constants (whereas most F# statements are beautifully succinct). Usually F# does an excellent job at it, but somehow it fails with the omnipresent let-binding in conjunction with attributes.