@@ -478,8 +478,16 @@ 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
481+ // Reconstruct: include template params prefix if present
482482 StringBuilder raw = new StringBuilder ();
483+ if (!templateParams .isEmpty ()) {
484+ raw .append ("template<" );
485+ for (int i = 0 ; i < templateParams .size (); i ++) {
486+ if (i > 0 ) raw .append (", " );
487+ raw .append (templateParams .get (i ));
488+ }
489+ raw .append ("> " );
490+ }
483491 for (int i = startPos ; i < pos - 1 ; i ++) { if (i > startPos ) raw .append (" " ); raw .append (tokens .get (i ).text ()); }
484492 raw .append (";" );
485493 return List .of (new PreprocessorLine (raw .toString (), tokens .get (startPos ).line (), tokens .get (startPos ).col (), leadingComments ));
@@ -712,6 +720,7 @@ private TypeDef parseTypeDef(List<CppLexerToken> leadingComments, List<String> t
712720 }
713721 // Also consume [[attributes]] before the name
714722 consumeAttributes ();
723+
715724 String name = expectIdentifier ().text ();
716725
717726 // Partial or explicit specialization: "template<typename T> struct Foo<T*>"
@@ -857,6 +866,26 @@ private List<TopLevelItem> parseClassMember(List<CppLexerToken> leadingComments,
857866 consumeLeadingComments ();
858867 }
859868 if (checkKeyword ("class" ) || checkKeyword ("struct" )) {
869+ // Anonymous struct: "struct { float x, y; } position;"
870+ if (pos + 1 < tokens .size () && tokens .get (pos + 1 ).isPunct ("{" )) {
871+ int anonStart = pos ;
872+ advance (); // consume struct/class
873+ int bd = 0 ;
874+ while (!isAtEnd ()) {
875+ if (checkPunct ("{" )) { bd ++; advance (); }
876+ else if (checkPunct ("}" )) { bd --; advance (); if (bd == 0 ) break ; }
877+ else advance ();
878+ }
879+ // pos is now after }, next is memberName then ;
880+ int beforeMember = pos ;
881+ String memberName = check (CppLexerTokenType .IDENTIFIER ) ? advance ().text () : "" ;
882+ matchPunct (";" );
883+ StringBuilder raw = new StringBuilder ();
884+ for (int i = anonStart ; i < beforeMember ; i ++) { if (i > anonStart ) raw .append (" " ); raw .append (tokens .get (i ).text ()); }
885+ if (!memberName .isEmpty ()) raw .append (" " ).append (memberName );
886+ raw .append (";" );
887+ return List .of (new PreprocessorLine (raw .toString (), tokens .get (anonStart ).line (), tokens .get (anonStart ).col (), leadingComments ));
888+ }
860889 return List .of (parseTypeDef (leadingComments , templateParams ));
861890 }
862891 if (checkKeyword ("enum" )) {
@@ -1075,7 +1104,8 @@ private List<TopLevelItem> parseFunctionOrVariable(List<CppLexerToken> leadingCo
10751104 matchKeyword ("inline" );
10761105 matchKeyword ("volatile" ); // consume volatile qualifier
10771106 boolean isConst = matchKeyword ("const" );
1078- boolean isConstexprFn = matchKeyword ("constexpr" ) || matchKeyword ("consteval" ); if (isConstexprFn ) isConst = true ;
1107+ boolean isConstexprFn = matchKeyword ("constexpr" ) || matchKeyword ("consteval" );
1108+ if (isConstexprFn && !isConst ) isConst = true ;
10791109 matchKeyword ("constinit" );
10801110 if (!isVirtual ) isVirtual = matchKeyword ("virtual" ); // constexpr virtual
10811111 if (!isStatic ) isStatic = matchKeyword ("static" );
@@ -1095,7 +1125,7 @@ private List<TopLevelItem> parseFunctionOrVariable(List<CppLexerToken> leadingCo
10951125 isConst , isStatic , start .line (), start .col (), leadingComments ));
10961126 }
10971127
1098- TypeRef type = parseTypeRef (isConst ); // pass leading const to mark return type correctly
1128+ TypeRef type = parseTypeRef (isConstexprFn ? false : isConst ); // constexpr should not mark return type as const
10991129 // Constructor: if ( follows the type with no name AND we have leading constexpr/virtual,
11001130 // the type name IS the function name (e.g. "constexpr RGBA(...)")
11011131 String name ;
@@ -1605,8 +1635,9 @@ private TypeRef parseTypeRef(boolean leadingConst) {
16051635 // set; we still MUST consume the token so parseQualifiedTypeName doesn't see
16061636 // "const" as the type name (e.g. "constexpr const char* p" -- constexpr sets
16071637 // leadingConst=true, then "const" must still be consumed before "char").
1608- matchKeyword ("const" );
1609- return parseTypeRefAfterConst (leadingConst );
1638+ // If const is explicitly present, always mark type as const regardless of leadingConst.
1639+ boolean hasExplicitConst = matchKeyword ("const" );
1640+ return parseTypeRefAfterConst (leadingConst || hasExplicitConst );
16101641 }
16111642
16121643 private TypeRef parseTypeRefAfterConst (boolean isConst ) {
@@ -1778,12 +1809,16 @@ private List<TypeRef> parseTemplateArgList() {
17781809 expectOp ("<" );
17791810 List <TypeRef > args = new ArrayList <>();
17801811 if (!checkOp (">" )) {
1781- args .add (parseTemplateArg ());
1782- matchPunct ("..." ); // trailing pack expansion: "Ts..."
1812+ TypeRef _a0 = parseTemplateArg ();
1813+ if (matchPunct ("..." ) && _a0 instanceof NamedType _nt0 )
1814+ _a0 = new NamedType (_nt0 .baseName () + "..." , _nt0 .templateArgs (), _nt0 .pointerDepth (), _nt0 .isReference (), _nt0 .isConst (), _nt0 .isRvalueRef ());
1815+ args .add (_a0 );
17831816 while (matchPunct ("," )) {
17841817 if (checkOp (">" ) || checkOp (">>" )) break ;
1785- args .add (parseTemplateArg ());
1786- matchPunct ("..." ); // trailing pack expansion
1818+ TypeRef _ai = parseTemplateArg ();
1819+ if (matchPunct ("..." ) && _ai instanceof NamedType _nti )
1820+ _ai = new NamedType (_nti .baseName () + "..." , _nti .templateArgs (), _nti .pointerDepth (), _nti .isReference (), _nti .isConst (), _nti .isRvalueRef ());
1821+ args .add (_ai );
17871822 }
17881823 }
17891824 // Note: ">>" closing two nested template lists at once (e.g.
@@ -1816,6 +1851,24 @@ private void splitTrailingShiftIntoTwoCloseAngles() {
18161851 tokens .add (pos + 1 , second );
18171852 }
18181853
1854+ /** Render a NamedType back to its source string including template args. */
1855+ private static String renderNamedTypeAsString (NamedType nt ) {
1856+ StringBuilder sb = new StringBuilder (nt .baseName ());
1857+ if (!nt .templateArgs ().isEmpty ()) {
1858+ sb .append ("<" );
1859+ for (int i = 0 ; i < nt .templateArgs ().size (); i ++) {
1860+ if (i > 0 ) sb .append (", " );
1861+ TypeRef a = nt .templateArgs ().get (i );
1862+ if (a instanceof NamedType na ) sb .append (renderNamedTypeAsString (na ));
1863+ else sb .append (a .toString ());
1864+ }
1865+ sb .append (">" );
1866+ }
1867+ if (nt .pointerDepth () > 0 ) sb .append ("*" .repeat (nt .pointerDepth ()));
1868+ if (nt .isReference ()) sb .append ("&" );
1869+ return sb .toString ();
1870+ }
1871+
18191872 private TypeRef parseTemplateArg () {
18201873 if (checkKeyword ("true" ) || checkKeyword ("false" )) {
18211874 String val = advance ().text ();
@@ -1876,6 +1929,32 @@ private TypeRef parseTemplateArg() {
18761929 return new NamedType ("sizeof(...)" , List .of (), 0 , false , false , false );
18771930 }
18781931 TypeRef maybeReturnType = parseTypeRef ();
1932+ // Compound boolean expression in template arg: "is_arithmetic_v<T> && !is_same_v<T,bool>"
1933+ // The && was consumed as rvalue-ref by parseTypeRef; check if ! or || follows
1934+ // The && was already consumed by parseTypeRef as rvalue-ref
1935+ boolean trailingRvalueRef = maybeReturnType instanceof NamedType ntrr && ntrr .isRvalueRef ();
1936+ if (trailingRvalueRef || checkOp ("||" ) || checkOp ("!" )) {
1937+ // Strip the falsely-consumed && from the type
1938+ if (trailingRvalueRef && maybeReturnType instanceof NamedType ntrr2 ) {
1939+ maybeReturnType = new NamedType (ntrr2 .baseName (), ntrr2 .templateArgs (),
1940+ ntrr2 .pointerDepth (), ntrr2 .isReference (), ntrr2 .isConst (), false );
1941+ }
1942+ StringBuilder expr = new StringBuilder (
1943+ maybeReturnType instanceof NamedType nt ? renderNamedTypeAsString (nt ) : "" );
1944+ if (trailingRvalueRef ) expr .append (" && " );
1945+ while (!isAtEnd ()) {
1946+ if (checkOp ("&&" )) { expr .append (" && " ); advance (); }
1947+ else if (checkOp ("||" )) { expr .append (" || " ); advance (); }
1948+ else if (checkOp ("!" )) { expr .append ("!" ); advance (); }
1949+ else if (checkOp (">" ) || checkOp (">>" ) || checkPunct ("," )) break ;
1950+ else if (checkPunct ("(" ) || check (CppLexerTokenType .IDENTIFIER )
1951+ || check (CppLexerTokenType .KEYWORD )) {
1952+ TypeRef sub = parseTypeRef ();
1953+ if (sub instanceof NamedType nts ) expr .append (renderNamedTypeAsString (nts ));
1954+ } else break ;
1955+ }
1956+ return new NamedType (expr .toString (), List .of (), 0 , false , false , false );
1957+ }
18791958 if (checkPunct ("(" )) {
18801959 return parseFunctionSignatureTail (maybeReturnType );
18811960 }
@@ -2259,9 +2338,13 @@ private Expr parseNew() {
22592338 List <Expr > args = new ArrayList <>();
22602339 if (matchPunct ("(" )) {
22612340 if (!checkPunct (")" )) {
2262- args .add (parseExpr ());
2341+ Expr a0 = parseExpr ();
2342+ if (matchPunct ("..." )) a0 = new PostfixExpr ("..." , a0 , a0 .line (), a0 .col (), List .of ());
2343+ args .add (a0 );
22632344 while (matchPunct ("," )) {
2264- args .add (parseExpr ());
2345+ Expr ai = parseExpr ();
2346+ if (matchPunct ("..." )) ai = new PostfixExpr ("..." , ai , ai .line (), ai .col (), List .of ());
2347+ args .add (ai );
22652348 }
22662349 }
22672350 expectPunct (")" );
0 commit comments