Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions src/Compiler/SyntaxTree/LexFilter.fs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ type Context =
// Indicates we're processing the second part of a match, after the 'with'
// First bool indicates "was this 'with' followed immediately by a '|'"?
| CtxtMatchClauses of bool * Position
| CtxtAttribute of Position * bool

member c.StartPos =
match c with
Expand All @@ -75,6 +76,7 @@ type Context =
| CtxtWithAsAugment p
| CtxtMatchClauses (_, p) | CtxtIf p | CtxtMatch p | CtxtFor p | CtxtWhile p | CtxtWhen p | CtxtFunction p | CtxtFun p | CtxtTry p | CtxtThen p | CtxtElse p | CtxtVanilla (p, _)
| CtxtSeqBlock (_, p, _) -> p
| CtxtAttribute(position, b) -> position

member c.StartCol = c.StartPos.Column

Expand All @@ -96,6 +98,7 @@ type Context =
| CtxtMemberBody _ -> "body"
| CtxtSeqBlock (b, p, _addBlockEnd) -> sprintf "seqblock(%s, %s)" (match b with FirstInSeqBlock -> "first" | NotFirstInSeqBlock -> "subsequent") (stringOfPos p)
| CtxtMatchClauses _ -> "matching"
| CtxtAttribute _ -> "attribute"

| CtxtIf _ -> "if"
| CtxtMatch _ -> "match"
Expand Down Expand Up @@ -774,6 +777,11 @@ type LexFilterImpl (
PositionWithColumn(limitCtxt.StartPos, limitCtxt.StartCol + 1)

| _, CtxtSeqBlock _ :: rest when not strict -> undentationLimit strict rest
// Add special handling for attribute contexts
| _, CtxtAttribute(attrPos, _) :: _ ->
// For content inside attribute lists, just require they're at the
// same level or indented from the attribute start
PositionWithColumn(attrPos, attrPos.Column)
| _, CtxtParen _ :: rest when not strict -> undentationLimit strict rest

// 'begin match' limited by minimum of two
Expand Down Expand Up @@ -1529,6 +1537,43 @@ type LexFilterImpl (
returnToken tokenLexbufState token

match token, offsideStack with
// Handle attribute start - push new attribute context
| LBRACK_LESS, _ ->
if debug then dprintf "LBRACK_LESS, pushing CtxtAttribute, tokenStartPos = %a\n" outputPos tokenStartPos
pushCtxt tokenTup (CtxtAttribute(tokenStartPos, false))
pushCtxtSeqBlock tokenTup NoAddBlockEnd
returnToken tokenLexbufState token

// Handle semicolons in attribute lists
| SEMICOLON, CtxtAttribute(attrPos, _) :: _ ->
// Update the attribute context to indicate we've seen a semicolon
replaceCtxt tokenTup (CtxtAttribute(attrPos, true))
returnToken tokenLexbufState token

// Handle implicit semicolons between attributes on separate lines
| token, CtxtAttribute(attrPos, hadSemicolon) :: _ when
(match token with IDENT _ -> true | _ -> false) && // Is an identifier (start of attribute)
not hadSemicolon && // No explicit semicolon
tokenStartPos.OriginalLine > attrPos.OriginalLine && // On a new line
tokenStartCol >= attrPos.Column -> // Properly aligned
if debug then dprintf "Attribute on new line without semicolon at col %d, inserting implicit SEMICOLON\n" tokenStartCol
// Record that we're inserting a semicolon
replaceCtxt tokenTup (CtxtAttribute(attrPos, true))
// Insert a semicolon before the attribute
delayToken tokenTup
insertTokenFromPrevPosToCurrentPos SEMICOLON

// Handle end of attribute list
| GREATER_RBRACK, CtxtAttribute _ :: _ ->
if debug then dprintf "GREATER_RBRACK, popping CtxtAttribute\n"
popCtxt()
returnToken tokenLexbufState token

// Offside rule for attributes
| _, CtxtAttribute(attrPos, _) :: _ when tokenStartCol < attrPos.Column ->
if debug then dprintf "Token at column %d is offside from ATTRIBUTE with attrPos %a! popping context\n" tokenStartCol outputPos attrPos
popCtxt()
reprocess()
// inserted faux tokens need no other processing
| _ when tokensThatNeedNoProcessingCount > 0 ->
tokensThatNeedNoProcessingCount <- tokensThatNeedNoProcessingCount - 1
Expand Down
1 change: 0 additions & 1 deletion src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -1533,7 +1533,6 @@ attributeListElements:
| attributeListElements seps attribute
{ $1 @ [$3] }


/* One custom attribute */
attribute:
/* A custom attribute */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

[<MyAttribute(foo ="bar"); MyAttribute(foo ="bar")>]
do ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
ImplFile
(ParsedImplFileInput
("/root/Attribute/MultipleAttributes01.fs", false,
QualifiedNameOfFile MultipleAttributes01, [], [],
[SynModuleOrNamespace
([MultipleAttributes01], false, AnonModule,
[Attributes
([{ Attributes =
[{ TypeName = SynLongIdent ([MyAttribute], [], [None])
ArgExpr =
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Equality], [],
[Some (OriginalNotation "=")]), None,
(2,18--2,19)), Ident foo, (2,14--2,19)),
Const
(String ("bar", Regular, (2,19--2,24)),
(2,19--2,24)), (2,14--2,24)), (2,13--2,14),
Some (2,24--2,25), (2,13--2,25))
Target = None
AppliesToGetterAndSetter = false
Range = (2,2--2,25) };
{ TypeName = SynLongIdent ([MyAttribute], [], [None])
ArgExpr =
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Equality], [],
[Some (OriginalNotation "=")]), None,
(2,43--2,44)), Ident foo, (2,39--2,44)),
Const
(String ("bar", Regular, (2,44--2,49)),
(2,44--2,49)), (2,39--2,49)), (2,38--2,39),
Some (2,49--2,50), (2,38--2,50))
Target = None
AppliesToGetterAndSetter = false
Range = (2,27--2,50) }]
Range = (2,0--2,52) }], (2,0--2,52));
Expr (Do (Const (Unit, (3,3--3,5)), (3,0--3,5)), (3,0--3,5))],
PreXmlDocEmpty, [], None, (2,0--4,0), { LeadingKeyword = None })],
(true, true), { ConditionalDirectives = []
CodeComments = [] }, set []))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

[<MyAttribute(foo ="bar")
MyAttribute(foo ="bar")>]
Comment on lines +2 to +3
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth having a test where the second one is not indented far enough? Would it be considered offsides? What about a mixture of semicolons and newlines?

do ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
ImplFile
(ParsedImplFileInput
("/root/Attribute/MultipleAttributes02.fs", false,
QualifiedNameOfFile MultipleAttributes02, [], [],
[SynModuleOrNamespace
([MultipleAttributes02], false, AnonModule,
[Attributes
([{ Attributes =
[{ TypeName = SynLongIdent ([MyAttribute], [], [None])
ArgExpr =
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Equality], [],
[Some (OriginalNotation "=")]), None,
(2,18--2,19)), Ident foo, (2,14--2,19)),
Const
(String ("bar", Regular, (2,19--2,24)),
(2,19--2,24)), (2,14--2,24)), (2,13--2,14),
Some (2,24--2,25), (2,13--2,25))
Target = None
AppliesToGetterAndSetter = false
Range = (2,2--2,25) }]
Range = (2,0--2,25) }], (2,0--2,25));
Expr
(App
(Atomic, false, Ident MyAttribute,
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Equality], [], [Some (OriginalNotation "=")]),
None, (3,18--3,19)), Ident foo, (3,14--3,19)),
Const
(String ("bar", Regular, (3,19--3,24)), (3,19--3,24)),
(3,14--3,24)), (3,13--3,14), Some (3,24--3,25),
(3,13--3,25)), (3,2--3,25)), (3,2--3,25))], PreXmlDocEmpty,
[], None, (2,0--3,25), { LeadingKeyword = None })], (true, true),
{ ConditionalDirectives = []
CodeComments = [] }, set []))

(3,2)-(3,13) parse error Unexpected identifier in attribute list
(3,25)-(3,27) parse error Unexpected symbol '>]' in implementation file
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

[<MyAttribute(foo ="bar");
MyAttribute(foo ="bar")>]
do ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
ImplFile
(ParsedImplFileInput
("/root/Attribute/MultipleAttributes03.fs", false,
QualifiedNameOfFile MultipleAttributes03, [], [],
[SynModuleOrNamespace
([MultipleAttributes03], false, AnonModule,
[Attributes
([{ Attributes =
[{ TypeName = SynLongIdent ([MyAttribute], [], [None])
ArgExpr =
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Equality], [],
[Some (OriginalNotation "=")]), None,
(2,18--2,19)), Ident foo, (2,14--2,19)),
Const
(String ("bar", Regular, (2,19--2,24)),
(2,19--2,24)), (2,14--2,24)), (2,13--2,14),
Some (2,24--2,25), (2,13--2,25))
Target = None
AppliesToGetterAndSetter = false
Range = (2,2--2,25) };
{ TypeName = SynLongIdent ([MyAttribute], [], [None])
ArgExpr =
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Equality], [],
[Some (OriginalNotation "=")]), None,
(3,18--3,19)), Ident foo, (3,14--3,19)),
Const
(String ("bar", Regular, (3,19--3,24)),
(3,19--3,24)), (3,14--3,24)), (3,13--3,14),
Some (3,24--3,25), (3,13--3,25))
Target = None
AppliesToGetterAndSetter = false
Range = (3,2--3,25) }]
Range = (2,0--3,27) }], (2,0--3,27));
Expr (Do (Const (Unit, (4,3--4,5)), (4,0--4,5)), (4,0--4,5))],
PreXmlDocEmpty, [], None, (2,0--5,0), { LeadingKeyword = None })],
(true, true), { ConditionalDirectives = []
CodeComments = [] }, set []))
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

[<MyAttribute(foo ="bar");
MyAttribute(foo ="bar")>]
do ()
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
ImplFile
(ParsedImplFileInput
("/root/Attribute/MultipleAttributes04.fs", false,
QualifiedNameOfFile MultipleAttributes04, [], [],
[SynModuleOrNamespace
([MultipleAttributes04], false, AnonModule,
[Attributes
([{ Attributes =
[{ TypeName = SynLongIdent ([MyAttribute], [], [None])
ArgExpr =
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Equality], [],
[Some (OriginalNotation "=")]), None,
(2,18--2,19)), Ident foo, (2,14--2,19)),
Const
(String ("bar", Regular, (2,19--2,24)),
(2,19--2,24)), (2,14--2,24)), (2,13--2,14),
Some (2,24--2,25), (2,13--2,25))
Target = None
AppliesToGetterAndSetter = false
Range = (2,2--2,25) };
{ TypeName = SynLongIdent ([MyAttribute], [], [None])
ArgExpr =
Paren
(App
(NonAtomic, false,
App
(NonAtomic, true,
LongIdent
(false,
SynLongIdent
([op_Equality], [],
[Some (OriginalNotation "=")]), None,
(3,16--3,17)), Ident foo, (3,12--3,17)),
Const
(String ("bar", Regular, (3,17--3,22)),
(3,17--3,22)), (3,12--3,22)), (3,11--3,12),
Some (3,22--3,23), (3,11--3,23))
Target = None
AppliesToGetterAndSetter = false
Range = (3,0--3,23) }]
Range = (2,0--3,25) }], (2,0--3,25));
Expr (Do (Const (Unit, (4,3--4,5)), (4,0--4,5)), (4,0--4,5))],
PreXmlDocEmpty, [], None, (2,0--5,0), { LeadingKeyword = None })],
(true, true), { ConditionalDirectives = []
CodeComments = [] }, set []))
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

[<MyAttribute(foo ="bar");
MyAttribute(foo ="bar")
MyAttribute(foo ="bar")>]
do ()
Loading
Loading