fix(parser): enforce mandatory parentheses on statement-position calls (#148)
The "mandatory parentheses on zero-argument calls" rule (language- rationale.adoc) was enforced in expression position but NOT in statement position: a bare `Foo;`, `Obj.Method;`, or `Obj.Free;` used as a statement compiled silently, building a paren-less TProcCall / TMethodCallStmt. Issue #148 reported `tester.print;` (a unit's global object method call) being accepted without its mandatory (). The statement parser now raises the same "requires () for a call" diagnostic the inherited-call and expression-position paths already use, at the two fall-through sites: * bare unqualified call `Foo` with no '(' (the final ProcCall else-branch); * bare `Obj.Method` with no '(' and no further '.' chain. Field reads, field assignments, indexed writes, and '.'-chains are unaffected (only a terminating bare reference is rejected). Expression-position calls were already enforced. Enforcing the rule required the compiler, RTL, and stdlib to comply first (self-hosting): swept bare calls in uSemantic (Flush/RepairGenericInstances), runtime.arc/runtime.exc (_libc_abort), blaise.codegen.native.backend (FAsm.AppendLine), and a json.writer doc example. The test suite embedded many bare calls in inline program strings — all updated to carry (); adding () never changes behaviour since these were always calls. cp.test.parser.pas gains two parse-error tests (proc and method bare calls); the old TestProcCall_NoParens, which asserted the bug, is inverted. grammar.ebnf SubscriptMethodCall made parens mandatory and an example corrected; rationale notes statement-position enforcement.
This commit is contained in:
parent
a205c22136
commit
50b8d75e6d
|
|
@ -160,7 +160,7 @@ end;
|
|||
|
||||
procedure TNativeBackend.EmitBlank;
|
||||
begin
|
||||
FAsm.AppendLine;
|
||||
FAsm.AppendLine();
|
||||
end;
|
||||
|
||||
{ The record-return ABI classifier and its leaf predicates now live as shared
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ begin
|
|||
_libc_write(STDERR_FD, Msg, Int64(Len));
|
||||
NL := 10;
|
||||
_libc_write(STDERR_FD, @NL, 1);
|
||||
_libc_abort;
|
||||
_libc_abort();
|
||||
end;
|
||||
|
||||
procedure _AbstractMethodError;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ var
|
|||
ExcSlot: PPointer;
|
||||
begin
|
||||
if g_exc_top = nil then
|
||||
_libc_abort;
|
||||
_libc_abort();
|
||||
g_current_exception := Obj;
|
||||
ExcSlot := g_exc_top + OFS_EXCEPTION;
|
||||
ExcSlot^ := Obj;
|
||||
|
|
@ -245,7 +245,7 @@ begin
|
|||
_libc_write(2, Msg, Int64(Len));
|
||||
NL := 10;
|
||||
_libc_write(2, @NL, 1);
|
||||
_libc_abort;
|
||||
_libc_abort();
|
||||
end;
|
||||
|
||||
procedure _Raise_InvalidCast;
|
||||
|
|
|
|||
|
|
@ -3421,6 +3421,20 @@ begin
|
|||
if not Check(tkRParen) then
|
||||
ParseMethodCallArgList(MCall);
|
||||
Expect(tkRParen);
|
||||
end
|
||||
else if not Check(tkDot) then
|
||||
begin
|
||||
{ Bare 'Obj.Method' with no '(' and no further '.' chain — a
|
||||
parameterless method call written without its mandatory parentheses.
|
||||
(A following '.' is a post-call/field chain handled just below; a
|
||||
terminating bare reference is the error case.) Every call carries ()
|
||||
even with no arguments — see language-rationale.adoc, "Mandatory
|
||||
parentheses on zero-argument calls". }
|
||||
MCall.Free();
|
||||
MCall := nil;
|
||||
raise EParseError.Create(Format(
|
||||
'Bare reference to ''%s.%s'' requires () for a call at line %d col %d in %s',
|
||||
[Name, SecondIdent, Line, Col, FLexer.Filename]));
|
||||
end;
|
||||
{ Post-method chain: Name.Method(args).Field := value or .Method2(args) }
|
||||
if Check(tkDot) then
|
||||
|
|
@ -3595,11 +3609,13 @@ begin
|
|||
FCallNode.Free();
|
||||
Exit(Call);
|
||||
end;
|
||||
Call := TProcCall.Create();
|
||||
Call.Line := Line;
|
||||
Call.Col := Col;
|
||||
Call.Name := Name;
|
||||
Result := Call;
|
||||
{ Bare identifier in statement position with no '(' — a parameterless
|
||||
procedure/function call written without its mandatory parentheses.
|
||||
Every call carries () even with no arguments (see language-rationale.adoc,
|
||||
"Mandatory parentheses on zero-argument calls"). }
|
||||
raise EParseError.Create(Format(
|
||||
'Bare reference to ''%s'' requires () for a call at line %d col %d in %s',
|
||||
[Name, Line, Col, FLexer.Filename]));
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -1168,7 +1168,7 @@ var
|
|||
I: Integer;
|
||||
begin
|
||||
FProg := AProg;
|
||||
FlushPendingGenericInstances;
|
||||
FlushPendingGenericInstances();
|
||||
FCurrentUnitName := AProg.Name;
|
||||
BuildUsesChain(AProg.UsedUnits);
|
||||
FTable.UsesChainProvider := Self;
|
||||
|
|
@ -1202,7 +1202,7 @@ var
|
|||
begin
|
||||
FCurrentUnitName := AUnit.Name;
|
||||
FCurrentUnit := AUnit;
|
||||
FlushPendingGenericInstances;
|
||||
FlushPendingGenericInstances();
|
||||
FTable.PushScope();
|
||||
try
|
||||
{ The unit's own name and the names of directly used units are
|
||||
|
|
@ -1223,7 +1223,7 @@ begin
|
|||
*before* any FindTypeOrInstantiate call can clone an instance, or the
|
||||
instance is born with nil bodies and codegen emits no function for it. }
|
||||
LinkGenericClassMethodImpls(AUnit.ImplBlock);
|
||||
RepairEarlyGenericInstances;
|
||||
RepairEarlyGenericInstances();
|
||||
|
||||
{ Register interface-section global variables — visible to impl bodies. }
|
||||
for I := 0 to AUnit.IntfBlock.Decls.Count - 1 do
|
||||
|
|
@ -1534,7 +1534,7 @@ var
|
|||
begin
|
||||
FCurrentUnitName := AUnit.Name;
|
||||
FCurrentUnit := AUnit;
|
||||
FlushPendingGenericInstances;
|
||||
FlushPendingGenericInstances();
|
||||
BuildUsesChain(AUnit.UsedUnits);
|
||||
FTable.UsesChainProvider := Self;
|
||||
{ Auto-tag every global Define within this unit's analysis with the
|
||||
|
|
@ -1557,7 +1557,7 @@ begin
|
|||
the cloned instance method is born without a body and codegen emits
|
||||
no function — leaving call sites referencing an undefined symbol. }
|
||||
LinkGenericClassMethodImpls(AUnit.ImplBlock);
|
||||
RepairEarlyGenericInstances;
|
||||
RepairEarlyGenericInstances();
|
||||
|
||||
{ Register interface-section global variables. Marked IsGlobal so
|
||||
codegen emits them as data-segment slots rather than stack allocs;
|
||||
|
|
@ -3971,7 +3971,7 @@ begin
|
|||
declarations, transferring the body so AnalyseMethodBodies can process it. }
|
||||
LinkClassMethodImpls(ABlock);
|
||||
LinkGenericClassMethodImpls(ABlock);
|
||||
RepairEarlyGenericInstances;
|
||||
RepairEarlyGenericInstances();
|
||||
{ Register standalone proc/func signatures before class method bodies so that
|
||||
methods can call free functions declared in the same block. }
|
||||
AnalyseStandaloneDecls(ABlock);
|
||||
|
|
|
|||
|
|
@ -687,7 +687,7 @@ begin
|
|||
f := TFoo.Create()
|
||||
end;
|
||||
begin
|
||||
DoIt
|
||||
DoIt()
|
||||
end.
|
||||
''');
|
||||
FnPos := Pos('function $DoIt', IR);
|
||||
|
|
@ -725,7 +725,7 @@ begin
|
|||
f := TFoo.Create()
|
||||
end;
|
||||
begin
|
||||
DoIt
|
||||
DoIt()
|
||||
end.
|
||||
''');
|
||||
FnPos := Pos('function $DoIt', IR);
|
||||
|
|
@ -759,7 +759,7 @@ begin
|
|||
f := TFoo.Create()
|
||||
end;
|
||||
begin
|
||||
DoIt
|
||||
DoIt()
|
||||
end.
|
||||
''');
|
||||
FnPos := Pos('function $DoIt', IR);
|
||||
|
|
@ -800,7 +800,7 @@ begin
|
|||
F := TFoo(P)
|
||||
end;
|
||||
begin
|
||||
DoIt
|
||||
DoIt()
|
||||
end.
|
||||
''');
|
||||
FnPos := Pos('function $DoIt', IR);
|
||||
|
|
@ -832,7 +832,7 @@ const
|
|||
r := Make()
|
||||
end;
|
||||
begin
|
||||
Run
|
||||
Run()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -848,7 +848,7 @@ const
|
|||
b := a
|
||||
end;
|
||||
begin
|
||||
Run
|
||||
Run()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -868,7 +868,7 @@ const
|
|||
r := Make()
|
||||
end;
|
||||
begin
|
||||
Run
|
||||
Run()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -883,7 +883,7 @@ const
|
|||
b := a
|
||||
end;
|
||||
begin
|
||||
Run
|
||||
Run()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
|
|||
|
|
@ -964,7 +964,7 @@ begin
|
|||
'begin' + LineEnding +
|
||||
' for I := 1 to 5 do' + LineEnding +
|
||||
' Write(I);' + LineEnding +
|
||||
' WriteLn' + LineEnding +
|
||||
' WriteLn()' + LineEnding +
|
||||
'end.',
|
||||
Out_, EC) then
|
||||
begin
|
||||
|
|
@ -1037,7 +1037,7 @@ begin
|
|||
' Write(N);' + LineEnding +
|
||||
' N := N + 1' + LineEnding +
|
||||
' end;' + LineEnding +
|
||||
' WriteLn' + LineEnding +
|
||||
' WriteLn()' + LineEnding +
|
||||
'end.',
|
||||
Out_, EC) then
|
||||
begin
|
||||
|
|
@ -1163,7 +1163,7 @@ begin
|
|||
'begin' + LineEnding +
|
||||
' G := TGreeter.Create;' + LineEnding +
|
||||
' WriteLn(G.Greet());' + LineEnding +
|
||||
' G.Free' + LineEnding +
|
||||
' G.Free()' + LineEnding +
|
||||
'end.',
|
||||
Out_, EC) then
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ begin
|
|||
Proc.WaitOnExit();
|
||||
Code := Proc.ExitCode
|
||||
finally
|
||||
Proc.Free
|
||||
Proc.Free()
|
||||
end;
|
||||
|
||||
if Code <> 0 then
|
||||
|
|
|
|||
|
|
@ -480,7 +480,7 @@ begin
|
|||
x := Threshold
|
||||
end;
|
||||
begin
|
||||
DoWork
|
||||
DoWork()
|
||||
end.
|
||||
'''
|
||||
);
|
||||
|
|
|
|||
|
|
@ -517,7 +517,7 @@ const
|
|||
Freed := 0;
|
||||
L := TObjectList.Create(True);
|
||||
L.Add(TThing.Create());
|
||||
Borrow;
|
||||
Borrow();
|
||||
WriteLn(Freed);
|
||||
L.Free();
|
||||
WriteLn(Freed)
|
||||
|
|
|
|||
|
|
@ -389,8 +389,8 @@ const
|
|||
OL := TObjectList.Create(False);
|
||||
OL.Add(SL);
|
||||
WriteLn(TStringList(OL[0])[1]); // chained: cast(OL default) then default
|
||||
SL.Free;
|
||||
OL.Free
|
||||
SL.Free();
|
||||
OL.Free()
|
||||
end.
|
||||
''';
|
||||
var Output: string; RCode: Integer;
|
||||
|
|
|
|||
|
|
@ -192,11 +192,11 @@ const
|
|||
end;
|
||||
begin
|
||||
Counter := 0;
|
||||
Inner;
|
||||
Inner();
|
||||
WriteLn(Counter);
|
||||
end;
|
||||
begin
|
||||
Outer;
|
||||
Outer();
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -311,7 +311,7 @@ const
|
|||
end;
|
||||
Write(r); Write(' ');
|
||||
end;
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -326,7 +326,7 @@ const
|
|||
5, 7..9: Write('b');
|
||||
else Write('.');
|
||||
end;
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ const
|
|||
program Prg;
|
||||
var a: array of string; i: Integer;
|
||||
begin SetLength(a, 3); a[0] := 'x'; a[1] := 'y'; a[2] := 'z';
|
||||
for i := 0 to 2 do Write(a[i]); WriteLn end.
|
||||
for i := 0 to 2 do Write(a[i]); WriteLn() end.
|
||||
''';
|
||||
|
||||
SrcRefSem = '''
|
||||
|
|
@ -125,7 +125,7 @@ const
|
|||
SetLength(m, 2); SetLength(m[0], 2); SetLength(m[1], 2);
|
||||
m[0][0] := 1; m[0][1] := 2; m[1][0] := 3; m[1][1] := 4;
|
||||
for i := 0 to 1 do for j := 0 to 1 do Write(m[i][j]);
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -137,7 +137,7 @@ const
|
|||
m[0][0] := 'a'; m[0][1] := 'b'; m[1][0] := 'c'; m[1][1] := 'd';
|
||||
m[0][1] := 'B';
|
||||
for i := 0 to 1 do for j := 0 to 1 do Write(m[i][j]);
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -154,7 +154,7 @@ const
|
|||
c[i][j][k] := i*4 + j*2 + k;
|
||||
for i := 0 to 1 do for j := 0 to 1 do for k := 0 to 1 do
|
||||
Write(c[i][j][k]);
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -212,7 +212,7 @@ const
|
|||
SetLength(G, Length(H));
|
||||
for I := 0 to High(G) do G[I] := 9;
|
||||
for I := 0 to High(G) do Write(G[I]);
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -226,7 +226,7 @@ const
|
|||
SetLength(A, Length(B));
|
||||
for I := 0 to High(A) do A[I] := 8;
|
||||
for I := 0 to High(A) do Write(A[I]);
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -241,7 +241,7 @@ const
|
|||
SetLength(A, Pick());
|
||||
for I := 0 to High(A) do A[I] := 6;
|
||||
for I := 0 to High(A) do Write(A[I]);
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
|
|||
|
|
@ -372,9 +372,9 @@ const
|
|||
begin WriteLn(g.Greet(), ' / ', g.Who()) end;
|
||||
var per: TPerson; lou: TLoud; vl: TVeryLoud;
|
||||
begin
|
||||
per := TPerson.Create; Use(per); per.Free;
|
||||
lou := TLoud.Create; Use(lou); lou.Free;
|
||||
vl := TVeryLoud.Create; Use(vl); vl.Free
|
||||
per := TPerson.Create; Use(per); per.Free();
|
||||
lou := TLoud.Create; Use(lou); lou.Free();
|
||||
vl := TVeryLoud.Create; Use(vl); vl.Free()
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
|
|
@ -406,7 +406,7 @@ const
|
|||
i := d; // assignment to interface
|
||||
i.Hello();
|
||||
Use(d); // parameter passing
|
||||
d.Free
|
||||
d.Free()
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ begin
|
|||
' for e in s do n := n + 1;' + LE +
|
||||
' WriteLn(n);' + LE + { 4 }
|
||||
' for e in s do Write(Ord(e), '' '');' + LE +
|
||||
' WriteLn' + LE +
|
||||
' WriteLn()' + LE +
|
||||
'end.';
|
||||
AssertRunsOnAll(Src, '4' + LE + '5 40 70 79 ' + LE, 0);
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ const
|
|||
end
|
||||
end;
|
||||
begin
|
||||
DoIt;
|
||||
DoIt();
|
||||
WriteLn('done')
|
||||
end.
|
||||
''';
|
||||
|
|
|
|||
|
|
@ -347,7 +347,7 @@ const
|
|||
Obj := TFoo.Create();
|
||||
Obj.FVal := 55;
|
||||
M := @Obj.Print;
|
||||
M;
|
||||
M();
|
||||
Obj.Free()
|
||||
end.
|
||||
''';
|
||||
|
|
@ -991,11 +991,11 @@ const
|
|||
begin
|
||||
x := 5;
|
||||
WriteLn(IntToStr(x));
|
||||
Inner;
|
||||
Inner();
|
||||
WriteLn(IntToStr(x))
|
||||
end;
|
||||
begin
|
||||
Outer
|
||||
Outer()
|
||||
end.
|
||||
''';
|
||||
var Output: string; RCode: Integer;
|
||||
|
|
@ -1022,7 +1022,7 @@ const
|
|||
R.B := Sum * 2
|
||||
end;
|
||||
begin
|
||||
Inner
|
||||
Inner()
|
||||
end;
|
||||
var Rec: TRec;
|
||||
begin
|
||||
|
|
@ -1050,11 +1050,11 @@ const
|
|||
end;
|
||||
begin
|
||||
R.A := 1; R.B := 2;
|
||||
Inner;
|
||||
Inner();
|
||||
WriteLn(IntToStr(R.A), ' ', IntToStr(R.B))
|
||||
end;
|
||||
begin
|
||||
Outer
|
||||
Outer()
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
|
|
@ -1073,7 +1073,7 @@ const
|
|||
A[0] := 10; A[1] := 20; A[2] := A[0] + A[1]
|
||||
end;
|
||||
begin
|
||||
Inner
|
||||
Inner()
|
||||
end;
|
||||
var Ar: TArr;
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -1226,7 +1226,7 @@ const
|
|||
WriteLn(D)
|
||||
end;
|
||||
begin
|
||||
ShowDouble
|
||||
ShowDouble()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -1487,7 +1487,7 @@ const
|
|||
WriteLn(B.Data[4]);
|
||||
WriteLn(B.Data[I + 2]);
|
||||
WriteLn(B.At(1));
|
||||
B.Free;
|
||||
B.Free();
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -2843,13 +2843,13 @@ const
|
|||
end;
|
||||
procedure Tmi.use;
|
||||
begin
|
||||
im.print;
|
||||
im.print();
|
||||
end;
|
||||
var
|
||||
im: Tmi;
|
||||
begin
|
||||
im := Tmi.Create(Toutput.Create());
|
||||
im.use;
|
||||
im.use();
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
@ -4966,7 +4966,7 @@ begin
|
|||
begin
|
||||
B := TBox.Create(99);
|
||||
WriteLn(B.Val);
|
||||
B.Free
|
||||
B.Free()
|
||||
end.
|
||||
''',
|
||||
'99' + LE, 0);
|
||||
|
|
@ -4992,7 +4992,7 @@ begin
|
|||
A := TArr.Create;
|
||||
WriteLn(A.Items[0]);
|
||||
WriteLn(A.Items[1]);
|
||||
A.Free
|
||||
A.Free()
|
||||
end.
|
||||
''',
|
||||
'10' + LE + '30' + LE, 0);
|
||||
|
|
@ -5018,7 +5018,7 @@ begin
|
|||
B := TBox.Create;
|
||||
B.Val := 42;
|
||||
WriteLn(B.Val);
|
||||
B.Free
|
||||
B.Free()
|
||||
end.
|
||||
''',
|
||||
'42' + LE, 0);
|
||||
|
|
@ -5046,7 +5046,7 @@ begin
|
|||
A.Items[1] := 30;
|
||||
WriteLn(A.Items[0]);
|
||||
WriteLn(A.Items[1]);
|
||||
A.Free
|
||||
A.Free()
|
||||
end.
|
||||
''',
|
||||
'10' + LE + '30' + LE, 0);
|
||||
|
|
@ -5062,7 +5062,7 @@ begin
|
|||
begin
|
||||
F := TFoo.Create;
|
||||
WriteLn(F.ClassName);
|
||||
F.Free
|
||||
F.Free()
|
||||
end.
|
||||
''',
|
||||
'TFoo' + LE, 0);
|
||||
|
|
@ -5084,7 +5084,7 @@ begin
|
|||
begin
|
||||
Obj := TCalc.Create;
|
||||
WriteLn(Obj.Sum6(1, 2, 3, 4, 5, 6));
|
||||
Obj.Free
|
||||
Obj.Free()
|
||||
end.
|
||||
''',
|
||||
'21' + LE, 0);
|
||||
|
|
@ -5109,8 +5109,8 @@ begin
|
|||
O := TOuter.Create;
|
||||
O.FInner := I;
|
||||
WriteLn(O.GetVal());
|
||||
O.Free;
|
||||
I.Free
|
||||
O.Free();
|
||||
I.Free()
|
||||
end.
|
||||
''',
|
||||
'42' + LE, 0);
|
||||
|
|
@ -5137,8 +5137,8 @@ begin
|
|||
begin
|
||||
O := TMyObj.Create;
|
||||
O.FVal := 99;
|
||||
O.Show;
|
||||
O.Free
|
||||
O.Show();
|
||||
O.Free()
|
||||
end.
|
||||
''',
|
||||
'99' + LE, 0);
|
||||
|
|
@ -5176,7 +5176,7 @@ begin
|
|||
begin
|
||||
B := TChild.Create;
|
||||
WriteLn(B.GetVal());
|
||||
B.Free
|
||||
B.Free()
|
||||
end.
|
||||
''',
|
||||
'42' + LE, 0);
|
||||
|
|
@ -5322,7 +5322,7 @@ begin
|
|||
procedure TCalc.Run;
|
||||
begin
|
||||
FVal := 5;
|
||||
Step;
|
||||
Step();
|
||||
WriteLn(FVal);
|
||||
WriteLn(Double())
|
||||
end;
|
||||
|
|
@ -5360,8 +5360,8 @@ begin
|
|||
var P: TParser;
|
||||
begin
|
||||
P := TParser.Create();
|
||||
P.Setup;
|
||||
P.Advance;
|
||||
P.Setup();
|
||||
P.Advance();
|
||||
WriteLn(P.FA.Kind, ':', P.FA.Value, ':', P.FA.Line, ':', P.FA.Col);
|
||||
WriteLn(P.FB.Kind, ':', P.FB.Value, ':', P.FB.Line, ':', P.FB.Col);
|
||||
P.Free()
|
||||
|
|
|
|||
|
|
@ -671,7 +671,7 @@ const
|
|||
K := TMaker.Create;
|
||||
R := K.Make(0.25);
|
||||
WriteLn(R.X);
|
||||
K.Free
|
||||
K.Free()
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
|
|
|
|||
|
|
@ -967,7 +967,7 @@ const
|
|||
var c: TC;
|
||||
begin
|
||||
c := TC.Create();
|
||||
c.Fill;
|
||||
c.Fill();
|
||||
writeln(c.A[0]);
|
||||
writeln(c.A[1]);
|
||||
writeln(c.A[2]);
|
||||
|
|
|
|||
|
|
@ -198,7 +198,7 @@ const
|
|||
G[2] := L[2]
|
||||
end;
|
||||
begin
|
||||
Fill;
|
||||
Fill();
|
||||
WriteLn(G[0]);
|
||||
WriteLn(G[2])
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -254,7 +254,7 @@ const
|
|||
end
|
||||
end;
|
||||
begin
|
||||
Run
|
||||
Run()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ const
|
|||
Exit(5)
|
||||
end;
|
||||
begin
|
||||
DoIt
|
||||
DoIt()
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
|
|||
|
|
@ -793,7 +793,7 @@ const
|
|||
program Prg;
|
||||
uses UPair;
|
||||
begin
|
||||
InitPair;
|
||||
InitPair();
|
||||
end.
|
||||
''';
|
||||
|
||||
|
|
|
|||
|
|
@ -477,7 +477,7 @@ begin
|
|||
var C: TChild;
|
||||
begin
|
||||
C := TChild.Create();
|
||||
C.NoSuchMethod
|
||||
C.NoSuchMethod()
|
||||
end.
|
||||
''');
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -525,7 +525,7 @@ const
|
|||
end;
|
||||
procedure Tmi.use;
|
||||
begin
|
||||
im.print;
|
||||
im.print();
|
||||
end;
|
||||
var
|
||||
im: Tmi;
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ begin
|
|||
var F: TFoo;
|
||||
begin
|
||||
F := TFoo.Create();
|
||||
F.NoSuchMethod
|
||||
F.NoSuchMethod()
|
||||
end.
|
||||
''');
|
||||
end;
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ const
|
|||
procedure ext_abort; external name 'abort';
|
||||
procedure DoA;
|
||||
begin
|
||||
ext_abort
|
||||
ext_abort()
|
||||
end;
|
||||
end.
|
||||
''';
|
||||
|
|
@ -465,7 +465,7 @@ const
|
|||
procedure ext_abort; external name 'abort';
|
||||
procedure DoB;
|
||||
begin
|
||||
ext_abort
|
||||
ext_abort()
|
||||
end;
|
||||
end.
|
||||
''';
|
||||
|
|
@ -474,7 +474,7 @@ const
|
|||
program TestP;
|
||||
uses AltA, AltB;
|
||||
begin
|
||||
DoA; DoB
|
||||
DoA(); DoB()
|
||||
end.
|
||||
''';
|
||||
var
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ begin
|
|||
'''
|
||||
program P;
|
||||
begin
|
||||
WriteLn
|
||||
WriteLn()
|
||||
end.
|
||||
''');
|
||||
AssertTrue('empty WriteLn still emits newline',
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ const
|
|||
WriteLn(N)
|
||||
end;
|
||||
begin
|
||||
Greet;
|
||||
Greet();
|
||||
Greet(42)
|
||||
end.
|
||||
''';
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ type
|
|||
procedure TestAssignment_IntLit;
|
||||
procedure TestAssignment_StringLit;
|
||||
procedure TestProcCall_NoArgs;
|
||||
procedure TestProcCall_NoParens;
|
||||
procedure TestProcCall_BareNoParens_RequiresParens;
|
||||
procedure TestMethodCall_BareNoParens_RequiresParens;
|
||||
procedure TestProcCall_OneStringArg;
|
||||
procedure TestProcCall_OneIntArg;
|
||||
|
||||
|
|
@ -406,18 +407,37 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TParserTests.TestProcCall_NoParens;
|
||||
var
|
||||
Prog: TProgram;
|
||||
Call: TProcCall;
|
||||
procedure TParserTests.TestProcCall_BareNoParens_RequiresParens;
|
||||
begin
|
||||
Prog := ParseSource('program P; begin WriteLn end.');
|
||||
{ Issue #148: a parameterless call in statement position requires its
|
||||
mandatory () (see language-rationale.adoc, "Mandatory parentheses on
|
||||
zero-argument calls"). `WriteLn` without () is a parse error. }
|
||||
try
|
||||
Call := TProcCall(Prog.Block.Stmts.Items[0]);
|
||||
AssertEquals('Name', 'WriteLn', Call.Name);
|
||||
AssertEquals('0 args', 0, Call.Args.Count);
|
||||
finally
|
||||
Prog.Free();
|
||||
ParseSource('program P; begin WriteLn end.').Free();
|
||||
Fail('Expected EParseError for bare parameterless call');
|
||||
except
|
||||
on E: EParseError do
|
||||
AssertTrue('mentions mandatory parens',
|
||||
Pos('requires () for a call', E.Message) >= 0);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TParserTests.TestMethodCall_BareNoParens_RequiresParens;
|
||||
begin
|
||||
{ Issue #148: a parameterless METHOD call in statement position likewise
|
||||
requires (). `tester.print` (no parens) is a parse error. }
|
||||
try
|
||||
ParseSource(
|
||||
'program P;' +
|
||||
' type TC = class procedure P; end;' +
|
||||
' procedure TC.P; begin end;' +
|
||||
' var c: TC;' +
|
||||
' begin c := TC.Create(); c.P end.').Free();
|
||||
Fail('Expected EParseError for bare parameterless method call');
|
||||
except
|
||||
on E: EParseError do
|
||||
AssertTrue('mentions mandatory parens',
|
||||
Pos('requires () for a call', E.Message) >= 0);
|
||||
end;
|
||||
end;
|
||||
|
||||
|
|
|
|||
|
|
@ -588,10 +588,10 @@ const
|
|||
begin
|
||||
end;
|
||||
begin
|
||||
Inner;
|
||||
Inner();
|
||||
end;
|
||||
begin
|
||||
Outer;
|
||||
Outer();
|
||||
end.
|
||||
''';
|
||||
var IR: string;
|
||||
|
|
@ -619,10 +619,10 @@ const
|
|||
end;
|
||||
begin
|
||||
x := 0;
|
||||
Inner;
|
||||
Inner();
|
||||
end;
|
||||
begin
|
||||
Outer;
|
||||
Outer();
|
||||
end.
|
||||
''';
|
||||
var IR: string;
|
||||
|
|
@ -650,7 +650,7 @@ const
|
|||
WriteLn(1);
|
||||
end;
|
||||
begin
|
||||
Inner;
|
||||
Inner();
|
||||
end;
|
||||
procedure OuterB;
|
||||
procedure Inner;
|
||||
|
|
@ -658,11 +658,11 @@ const
|
|||
WriteLn(2);
|
||||
end;
|
||||
begin
|
||||
Inner;
|
||||
Inner();
|
||||
end;
|
||||
begin
|
||||
OuterA;
|
||||
OuterB;
|
||||
OuterA();
|
||||
OuterB();
|
||||
end.
|
||||
''';
|
||||
var
|
||||
|
|
|
|||
|
|
@ -494,7 +494,7 @@ begin
|
|||
begin
|
||||
end;
|
||||
begin
|
||||
UseFn;
|
||||
UseFn();
|
||||
end.
|
||||
'''
|
||||
);
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ const
|
|||
program P;
|
||||
type TM = procedure of object;
|
||||
var G: TM;
|
||||
begin G end.
|
||||
begin G() end.
|
||||
''';
|
||||
var IR: string;
|
||||
begin
|
||||
|
|
@ -448,7 +448,7 @@ const
|
|||
M.Code := MethodAddress(F, 'SayHi');
|
||||
M.Data := F;
|
||||
G := TGreet(M);
|
||||
G;
|
||||
G();
|
||||
F.Free()
|
||||
end.
|
||||
''';
|
||||
|
|
@ -509,7 +509,7 @@ const
|
|||
M.Code := MethodAddress(C, 'Print');
|
||||
M.Data := C;
|
||||
G := TPrintMethod(M);
|
||||
G;
|
||||
G();
|
||||
C.Free()
|
||||
end.
|
||||
''';
|
||||
|
|
|
|||
|
|
@ -473,7 +473,7 @@ end;
|
|||
|
||||
procedure TSemanticTests.TestProcCall_WriteLn_NoArgs_OK;
|
||||
begin
|
||||
Analyse('program P; begin WriteLn end.').Free();
|
||||
Analyse('program P; begin WriteLn() end.').Free();
|
||||
end;
|
||||
|
||||
procedure TSemanticTests.TestProcCall_WriteLn_StringArg_OK;
|
||||
|
|
@ -520,7 +520,7 @@ end;
|
|||
|
||||
procedure TSemanticTests.TestProcCall_UndeclaredProc_RaisesError;
|
||||
begin
|
||||
AnalyseExpectError('program P; begin NoSuchProc end.');
|
||||
AnalyseExpectError('program P; begin NoSuchProc() end.');
|
||||
end;
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
|
|
|
|||
|
|
@ -854,7 +854,7 @@ StaticSubscriptAssign
|
|||
* assignment (SubscriptFieldAssign) or a method call on the element
|
||||
* (SubscriptMethodCall). Examples:
|
||||
* a[0].Name := 'x'; a[i].Inner.N := 5;
|
||||
* a[0].Bump; a[i].Items.Add(x);
|
||||
* a[0].Bump(); a[i].Items.Add(x);
|
||||
* a[i].Arr[j] := v; — element write into an array-typed field
|
||||
* A terminating subscript directly over another subscript (a[i][j] := v, or
|
||||
* the comma form a[i, j] := v) is a multi-dimensional element write — see
|
||||
|
|
@ -866,7 +866,7 @@ SubscriptFieldAssign
|
|||
|
||||
SubscriptMethodCall
|
||||
= IDENT LBRACKET Expr RBRACKET { SubscriptSuffix }
|
||||
DOT IDENT [ LPAREN [ ArgList ] RPAREN ]
|
||||
DOT IDENT LPAREN [ ArgList ] RPAREN
|
||||
;
|
||||
|
||||
(* A subscript suffix indexes one or more dimensions. The comma form
|
||||
|
|
|
|||
|
|
@ -3954,6 +3954,14 @@ the lexical level: `Foo()` is always a call, `Foo` is always a read. The
|
|||
compiler AST becomes simpler — `TMethodCallExpr` for calls with explicit
|
||||
parentheses, `TFieldAccessExpr` only for field/property reads.
|
||||
|
||||
The rule applies in *statement* position as well as expression position: a
|
||||
bare `Foo;`, `Obj.Method;`, or `Obj.Free;` used as a statement is a parse
|
||||
error (issue #148). Earlier the statement parser silently accepted the
|
||||
bare form — expression position was already enforced, but statement-position
|
||||
parameterless calls slipped through and built a paren-less `TProcCall` /
|
||||
`TMethodCallStmt`. Both now raise the same "requires () for a call"
|
||||
diagnostic the `inherited` and expression-position paths use.
|
||||
|
||||
=== Alternatives considered
|
||||
|
||||
* *Deprecation warning period* — rejected. Blaise is in alpha; there is no
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@
|
|||
W.WriteBool('stable', True);
|
||||
|
||||
2. Array elements - value only, no name (the '...Value' forms):
|
||||
W.BeginArray;
|
||||
W.BeginArray();
|
||||
W.WriteStringValue('a');
|
||||
W.WriteIntValue(1);
|
||||
W.EndArray;
|
||||
W.EndArray();
|
||||
|
||||
3. Structure / escape hatch - when an object member's value is itself a
|
||||
nested object or array, write the key, then open the container:
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ begin
|
|||
Result := N.FValue;
|
||||
Self.FHead := N.FNext;
|
||||
Self.FCount := Self.FCount - 1;
|
||||
N.Free
|
||||
N.Free()
|
||||
end;
|
||||
|
||||
procedure TLinkedList.Walk;
|
||||
|
|
@ -120,7 +120,7 @@ begin
|
|||
while N <> nil do
|
||||
begin
|
||||
Next := N.FNext;
|
||||
N.Free;
|
||||
N.Free();
|
||||
N := Next
|
||||
end;
|
||||
Self.FHead := nil;
|
||||
|
|
@ -145,7 +145,7 @@ begin
|
|||
|
||||
WriteLn('count=', List.Count); { 4 }
|
||||
WriteLn('--- walk ---');
|
||||
List.Walk; { 40/tag0, 30/tag1/marked, 20/tag0, 10/tag0 }
|
||||
List.Walk(); { 40/tag0, 30/tag1/marked, 20/tag0, 10/tag0 }
|
||||
|
||||
{ Pop two values off the front }
|
||||
V := List.Pop();
|
||||
|
|
@ -155,7 +155,7 @@ begin
|
|||
|
||||
WriteLn('count_after_pops=', List.Count); { 2 }
|
||||
finally
|
||||
List.Clear;
|
||||
List.Free
|
||||
List.Clear();
|
||||
List.Free()
|
||||
end
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ var
|
|||
Dest: ^Integer;
|
||||
begin
|
||||
if Self.FCount = Self.FCapacity then
|
||||
Self.Grow;
|
||||
Self.Grow();
|
||||
Dest := Self.FData + Self.FCount * SizeOf(Integer);
|
||||
Dest^ := Value;
|
||||
Self.FCount := Self.FCount + 1
|
||||
|
|
@ -147,7 +147,7 @@ begin
|
|||
else
|
||||
begin
|
||||
if Self.FCount = Self.FCapacity then
|
||||
Self.Grow;
|
||||
Self.Grow();
|
||||
KPtr := Self.FKeys + Self.FCount * SizeOf(string);
|
||||
VPtr := Self.FValues + Self.FCount * SizeOf(Integer);
|
||||
KPtr^ := Key;
|
||||
|
|
@ -251,7 +251,7 @@ begin
|
|||
WriteLn('count_after_delete=', List.Count); { 4 }
|
||||
WriteLn('list[1]_after_delete=', List.Get(1)); { 30 }
|
||||
|
||||
List.Free;
|
||||
List.Free();
|
||||
|
||||
{ --- TDictionary<string,Integer> --- }
|
||||
Dict := TStrIntDict.Create;
|
||||
|
|
@ -277,5 +277,5 @@ begin
|
|||
Found := Dict.ContainsKey('alpha');
|
||||
WriteLn('has_alpha_after_remove=', Found); { 0 }
|
||||
|
||||
Dict.Free
|
||||
Dict.Free()
|
||||
end.
|
||||
|
|
|
|||
Loading…
Reference in a new issue