From 1ddce7c53e8bd8ed476bb54a9be92b5775ebc8c5 Mon Sep 17 00:00:00 2001 From: Patrick Quist Date: Thu, 8 Oct 2026 17:43:55 +0200 Subject: [PATCH] Report a syntax error at its line, not its column TPasSyntaxTreeBuilder.Run turns an ESyntaxError into an ESyntaxTreeException, whose constructor takes the line first and the column second. It passed E.PosXY.X, E.PosXY.Y, but TmwBasePasLex.GetPosXY puts the column in X and the line in Y, so the two came out swapped. The EParserException path next to it was right; ParserMessage already passes Y, X. ESyntaxError is what ExpectedFatal raises, which is how an unexpected end of file is reported. A unit that stops after the `begin` of a routine on line 5 unit Broken; interface implementation procedure Run; begin was reported at line 6, col 5 instead of line 5, col 6. --- Source/DelphiAST.pas | 2 +- Test/UnitTests/DelphiAST.Tests.pas | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Source/DelphiAST.pas b/Source/DelphiAST.pas index 5ff7b5c..0b79f42 100644 --- a/Source/DelphiAST.pas +++ b/Source/DelphiAST.pas @@ -2672,7 +2672,7 @@ function TPasSyntaxTreeBuilder.Run(SourceStream: TStream): TSyntaxNode; on E: EParserException do raise ESyntaxTreeException.Create(E.Line, E.Col, Lexer.FileName, E.Message, Result); on E: ESyntaxError do - raise ESyntaxTreeException.Create(E.PosXY.X, E.PosXY.Y, Lexer.FileName, E.Message, Result); + raise ESyntaxTreeException.Create(E.PosXY.Y, E.PosXY.X, Lexer.FileName, E.Message, Result); else FreeAndNil(Result); raise; diff --git a/Test/UnitTests/DelphiAST.Tests.pas b/Test/UnitTests/DelphiAST.Tests.pas index c46a2bb..da6354a 100644 --- a/Test/UnitTests/DelphiAST.Tests.pas +++ b/Test/UnitTests/DelphiAST.Tests.pas @@ -258,6 +258,28 @@ procedure TestInvalidSyntax; AssertTrue(Raised, 'Invalid syntax did not raise ESyntaxTreeException'); end; +procedure TestUnexpectedEndOfFilePosition; +var + Root: TSyntaxNode; + Raised: Boolean; +begin + Root := nil; + Raised := False; + try + Root := ParseSource('unit Broken;' + sLineBreak + 'interface' + sLineBreak + + 'implementation' + sLineBreak + 'procedure Run;' + sLineBreak + 'begin'); + except + on E: ESyntaxTreeException do + begin + Raised := True; + AssertEquals(5, E.Line, 'The end of file is on line 5.'); + AssertEquals(6, E.Col, 'The end of file is after the 5 characters of "begin".'); + end; + end; + Root.Free; + AssertTrue(Raised, 'An unexpected end of file did not raise ESyntaxTreeException'); +end; + {$IFNDEF FPC} procedure TestBinarySerializationRoundTrip; var @@ -296,6 +318,7 @@ procedure RunAllTests; RunTest('AST.ConstantEndPosition', TestConstantEndPosition); RunTest('AST.VariableEndPosition', TestVariableEndPosition); RunTest('Parser.InvalidSyntax', TestInvalidSyntax); + RunTest('Parser.UnexpectedEndOfFilePosition', TestUnexpectedEndOfFilePosition); {$IFNDEF FPC} RunTest('Serialization.BinaryRoundTrip', TestBinarySerializationRoundTrip); {$ENDIF}