@@ -1488,6 +1488,13 @@ function parse_unary_prefix(ps::ParseState, has_unary_prefix=false)
14881488 end
14891489end
14901490
1491+ function maybe_parsed_macro_name (ps, processing_macro_name, mark)
1492+ if processing_macro_name
1493+ emit (ps, mark, K " macro_name" )
1494+ end
1495+ return false
1496+ end
1497+
14911498# Parses a chain of suffixes at function call precedence, leftmost binding
14921499# tightest. This handles
14931500# * Bracketed calls like a() b[] c{}
@@ -1506,13 +1513,13 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
15061513 return
15071514 end
15081515 processing_macro_name = is_macrocall
1516+ saw_misplaced_atsym = false
1517+ misplaced_atsym_mark = nothing
15091518 # source range of the @-prefixed part of a macro
15101519 macro_atname_range = nothing
15111520 # $A.@x ==> (macrocall (. ($ A) (macro_name x)))
15121521 maybe_strmac = true
1513- # We record the last component of chains of dot-separated identifiers so we
1514- # know which identifier was the macro name.
1515- macro_name_position = position (ps) # points to same output span as peek_behind
1522+ last_identifier_orig_kind = peek_behind (ps). orig_kind
15161523 while true
15171524 maybe_strmac_1 = false
15181525 t = peek_token (ps)
@@ -1534,14 +1541,14 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
15341541 # A.@var"#" a ==> (macrocall (. A (macro_name (var #))) a)
15351542 # @+x y ==> (macrocall (macro_name +) x y)
15361543 # [email protected] ==> (macrocall (. A (macro_name .)) x) 1537- processing_macro_name && emit (ps, mark, K " macro_name " )
1538- processing_macro_name = false
1544+ processing_macro_name = maybe_parsed_macro_name (
1545+ ps, processing_macro_name, mark)
15391546 let ps = with_space_sensitive (ps)
15401547 # Space separated macro arguments
15411548 # A.@foo a b ==> (macrocall (. A (macro_name foo)) a b)
15421549 # @A.foo a b ==> (macrocall (macro_name (. A foo)) a b)
15431550 n_args = parse_space_separated_exprs (ps)
1544- is_doc_macro = peek_behind (ps, macro_name_position) . orig_kind == K " doc"
1551+ is_doc_macro = last_identifier_orig_kind == K " doc"
15451552 if is_doc_macro && n_args == 1
15461553 # Parse extended @doc args on next line
15471554 # @doc x\ny ==> (macrocall (macro_name doc) x y)
@@ -1568,7 +1575,8 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
15681575 # f(a; b; c) ==> (call f a (parameters b) (parameters c))
15691576 # (a=1)() ==> (call (parens (= a 1)))
15701577 # f (a) ==> (call f (error-t) a)
1571- processing_macro_name && emit (ps, mark, K " macro_name" )
1578+ processing_macro_name = maybe_parsed_macro_name (
1579+ ps, processing_macro_name, mark)
15721580 processing_macro_name = false
15731581 bump_disallowed_space (ps)
15741582 bump (ps, TRIVIA_FLAG)
@@ -1586,12 +1594,11 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
15861594 # A.@x(y).z ==> (. (macrocall-p (. A (macro_name x)) y) z)
15871595 is_macrocall = false
15881596 # @f()() ==> (call (macrocall-p (macro_name f)))
1589- is_macrocall_on_entry = false
15901597 macro_atname_range = nothing
15911598 end
15921599 elseif k == K " ["
1593- processing_macro_name && emit (ps, mark, K " macro_name " )
1594- processing_macro_name = false
1600+ processing_macro_name = maybe_parsed_macro_name (
1601+ ps, processing_macro_name, mark)
15951602 m = position (ps)
15961603 # a [i] ==> (ref a (error-t) i)
15971604 bump_disallowed_space (ps)
@@ -1645,22 +1652,21 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
16451652 # Allow `@` in macrocall only in first and last position
16461653 # A.B.@x ==> (macrocall (. (. A B) (macro_name x)))
16471654 # @A.B.x ==> (macrocall (macro_name (. (. A B) x)))
1648- # [email protected] ==> (macrocall (macro_name ( . (. A B ( error-t)))) 1655+ # [email protected] ==> (macrocall (. (. A (error-t) B) (macro_name ( error-t) x ))) 16491656 emit_diagnostic (ps, macro_atname_range... ,
16501657 error= " `@` must appear on first or last macro name component" )
1651- bump (ps, TRIVIA_FLAG, error = " Unexpected `.` after macro name " )
1652- # Recover by treating the `@` as if it had been on the wole thing
1658+ # Recover by treating the `@` as if it had been on the last identifier
1659+ saw_misplaced_atsym = true
16531660 reset_node! (ps, macro_atname_range[2 ], kind= K " TOMBSTONE" )
1654- processing_macro_name = true
1655- else
1656- bump (ps, TRIVIA_FLAG)
1661+ reset_node! (ps, macro_atname_range[1 ], kind= K " error" )
16571662 end
1663+ bump (ps, TRIVIA_FLAG)
16581664 k = peek (ps)
16591665 if k == K " ("
16601666 if is_macrocall
1661- processing_macro_name && emit (ps, mark, K " macro_name" )
16621667 # Recover by pretending we do have the syntax
1663- processing_macro_name = false
1668+ processing_macro_name = maybe_parsed_macro_name (
1669+ ps, processing_macro_name, mark)
16641670 # @M.(x) ==> (macrocall (dotcall (macro_name M) (error-t) x))
16651671 bump_invisible (ps, K " error" , TRIVIA_FLAG)
16661672 emit_diagnostic (ps, mark,
@@ -1694,7 +1700,7 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
16941700 else
16951701 emit (ps, m, K " $" )
16961702 end
1697- macro_name_position = position (ps)
1703+ last_identifier_orig_kind = K " $ "
16981704 emit (ps, mark, K " ." )
16991705 elseif k == K " @"
17001706 # A macro call after some prefix A has been consumed
@@ -1708,7 +1714,7 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
17081714 bump (ps, TRIVIA_FLAG)
17091715 end
17101716 parse_macro_name (ps)
1711- macro_name_position = position (ps)
1717+ last_identifier_orig_kind = peek_behind (ps). orig_kind
17121718 ! is_macrocall && emit (ps, m, K " macro_name" )
17131719 macro_atname_range = (m, position (ps))
17141720 is_macrocall = true
@@ -1721,10 +1727,27 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
17211727 error= " the .' operator for transpose is discontinued" )
17221728 emit (ps, mark, K " dotcall" , POSTFIX_OP_FLAG)
17231729 else
1730+ if saw_misplaced_atsym
1731+ # If we saw a misplaced `@` earlier, this might be the place
1732+ # where it should have been. Opportunistically bump the
1733+ # zero-width error token here. If that's not right, we'll
1734+ # reset it later.
1735+ if misplaced_atsym_mark != = nothing
1736+ reset_node! (ps, misplaced_atsym_mark[1 ], kind= K " TOMBSTONE" )
1737+ reset_node! (ps, misplaced_atsym_mark[2 ], kind= K " TOMBSTONE" )
1738+ end
1739+ macro_name_mark = position (ps)
1740+ bump_invisible (ps, K " error" , TRIVIA_FLAG)
1741+ aterror_mark = position (ps)
1742+ end
17241743 # Field/property syntax
17251744 # f.x.y ==> (. (. f x) y)
17261745 parse_atom (ps, false )
1727- macro_name_position = position (ps)
1746+ if saw_misplaced_atsym
1747+ emit (ps, macro_name_mark, K " macro_name" )
1748+ misplaced_atsym_mark = (aterror_mark, position (ps))
1749+ end
1750+ last_identifier_orig_kind = peek_behind (ps). orig_kind
17281751 maybe_strmac_1 = true
17291752 emit (ps, mark, K " ." )
17301753 end
@@ -1734,8 +1757,8 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
17341757 bump (ps, remap_kind= K " Identifier" )
17351758 emit (ps, mark, K " call" , POSTFIX_OP_FLAG)
17361759 elseif k == K " {"
1737- processing_macro_name && emit (ps, mark, K " macro_name " )
1738- processing_macro_name = false
1760+ processing_macro_name = maybe_parsed_macro_name (
1761+ ps, processing_macro_name, mark)
17391762 # Type parameter curlies and macro calls
17401763 m = position (ps)
17411764 # S {a} ==> (curly S (error-t) a)
@@ -1758,7 +1781,7 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
17581781 elseif k in KSet " \" \"\"\" ` ``` " &&
17591782 ! preceding_whitespace (t) && maybe_strmac &&
17601783 (# Must mirror the logic in lex_quote() for consistency
1761- origk = peek_behind (ps, macro_name_position) . orig_kind ;
1784+ origk = last_identifier_orig_kind ;
17621785 origk == K " Identifier" || is_contextual_keyword (origk) || is_word_operator (origk))
17631786 # Custom string and command literals
17641787 # x"str" ==> (macrocall (macro_name_str x) (string-r "str"))
0 commit comments