@@ -478,8 +478,9 @@ private List<TopLevelItem> parseTopLevelItem(List<CppLexerToken> leadingComments
478478 int startPos = pos ;
479479 while (!isAtEnd () && !checkPunct (";" )) advance ();
480480 matchPunct (";" );
481+ // Reconstruct without spaces, up to but not including the ";" we consumed
481482 StringBuilder raw = new StringBuilder ();
482- for (int i = startPos ; i < pos ; i ++) { if (i > startPos ) raw .append (" " ); raw .append (tokens .get (i ).text ()); }
483+ for (int i = startPos ; i < pos - 1 ; i ++) { if (i > startPos ) raw .append (" " ); raw .append (tokens .get (i ).text ()); }
483484 raw .append (";" );
484485 return List .of (new PreprocessorLine (raw .toString (), tokens .get (startPos ).line (), tokens .get (startPos ).col (), leadingComments ));
485486 }
@@ -891,22 +892,17 @@ private List<TopLevelItem> parseClassMember(List<CppLexerToken> leadingComments,
891892 // friend declaration: "friend class Foo;" or "friend Box<U> makeBox(U v);"
892893 if (checkKeyword ("friend" )) {
893894 int startPos = pos ; advance (); // consume friend
894- // Consume to ; skipping over <> and ()
895- int _fd = 0 ;
896- while (!isAtEnd ()) {
897- if (checkPunct ("(" ) || checkOp ("<" )) { _fd ++; advance (); }
898- else if (checkPunct (")" ) || checkOp (">" )) { _fd --; advance (); }
899- else if (checkOp (">>" )) { _fd -= 2 ; advance (); }
900- else if (checkPunct (";" ) && _fd == 0 ) { advance (); break ; }
901- else if (checkPunct ("{" ) && _fd == 0 ) {
902- // friend function with body
903- int bd = 1 ; advance ();
904- while (!isAtEnd () && bd > 0 ) { if (checkPunct ("{" )) bd ++; else if (checkPunct ("}" )) bd --; advance (); }
905- break ;
906- }
907- else advance ();
895+ // "friend class/struct Foo" -- forward decl, consume verbatim
896+ if (checkKeyword ("class" ) || checkKeyword ("struct" )) {
897+ while (!isAtEnd () && !checkPunct (";" )) advance ();
898+ matchPunct (";" );
899+ return List .of (new PreprocessorLine ("// friend class" , tokens .get (startPos ).line (), tokens .get (startPos ).col (), leadingComments ));
908900 }
909- return List .of (new PreprocessorLine ("// friend" , tokens .get (startPos ).line (), tokens .get (startPos ).col (), leadingComments ));
901+ // "friend RetType funcName(...)" or "friend RetType operator...()" -- parse as function
902+ // Consume template params if present
903+ List <String > friendTemplateParams = List .of ();
904+ if (matchKeyword ("template" )) friendTemplateParams = parseTemplateParamList ();
905+ return parseFunctionOrVariable (leadingComments , friendTemplateParams , false );
910906 }
911907 // explicit(...): conditional explicit specifier (C++20)
912908 if (checkKeyword ("explicit" ) && pos + 1 < tokens .size () && tokens .get (pos + 1 ).isPunct ("(" )) {
@@ -2293,6 +2289,27 @@ private Expr parsePostfix() {
22932289 expr = new Identifier ("::" + member ,
22942290 peek ().line (), peek ().col (), List .of ());
22952291 }
2292+ // Template args in expression: "AutoParam<42>::value"
2293+ } else if (checkOp ("<" ) && expr instanceof Identifier eid && looksLikeTemplateArgList ()) {
2294+ int argStart = pos ;
2295+ advance (); // consume <
2296+ int depth = 1 , pd = 0 , bd = 0 ;
2297+ while (!isAtEnd () && depth > 0 ) {
2298+ if (checkPunct ("(" ) || checkPunct ("[" )) pd ++;
2299+ else if (checkPunct (")" ) || checkPunct ("]" )) pd --;
2300+ else if (checkPunct ("{" )) bd ++;
2301+ else if (checkPunct ("}" )) bd --;
2302+ else if (pd == 0 && bd == 0 ) {
2303+ if (checkOp ("<" )) depth ++;
2304+ else if (checkOp (">" )) { depth --; if (depth == 0 ) { advance (); break ; } }
2305+ else if (checkOp (">>" )) { depth -= 2 ; splitTrailingShiftIntoTwoCloseAngles (); if (depth <= 0 ) { advance (); break ; } }
2306+ }
2307+ if (depth > 0 ) advance ();
2308+ }
2309+ StringBuilder targs = new StringBuilder (eid .name ()).append ("<" );
2310+ for (int i = argStart + 1 ; i < pos - 1 ; i ++) targs .append (tokens .get (i ).text ());
2311+ targs .append (">" );
2312+ expr = new Identifier (targs .toString (), eid .line (), eid .col (), List .of ());
22962313 } else if (checkOp ("->*" )) {
22972314 // "->*" lexed as a single token
22982315 CppLexerToken t = advance ();
0 commit comments