Skip to content

Commit a38d11d

Browse files
committed
Fix parsing errors on anonymous methods with routine directives
1 parent 0595ded commit a38d11d

File tree

8 files changed

+183
-9
lines changed

8 files changed

+183
-9
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- **API:** `AnonymousMethodNode::getAnonymousMethodHeading` method.
13+
- **API:** `AnonymousMethodNode::getDirectives` method.
14+
- **API:** `AnonymousMethodNode::hasDirective` method.
15+
- **API:** `AnonymousMethodHeadingNode` node type.
16+
- **API:** `DelphiTokenType.ANONYMOUS_METHOD` token type.
17+
- **API:** `DelphiTokenType.ANONYMOUS_METHOD_HEADING` token type.
18+
1019
### Fixed
1120

1221
- Parsing errors on `implementation` keywords nested in conditional branches.
22+
- Parsing errors on anonymous methods with routine directives (like calling conventions).
1323

1424
## [1.10.0] - 2024-10-01
1525

delphi-frontend/src/main/antlr3/au/com/integradev/delphi/antlr/Delphi.g

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ tokens {
6969
TkArrayConstructor;
7070
TkArrayIndices;
7171
TkArgument;
72+
TkAnonymousMethod;
73+
TkAnonymousMethodHeading;
7274
}
7375

7476
@header
@@ -907,8 +909,12 @@ argumentExpression : expression writeArguments?
907909
;
908910
writeArguments : ':'! expression (':'! expression)? // See: https://docwiki.embarcadero.com/Libraries/en/System.Write
909911
;
910-
anonymousMethod : PROCEDURE<AnonymousMethodNodeImpl>^ routineParameters? block
911-
| FUNCTION<AnonymousMethodNodeImpl>^ routineParameters? routineReturnType block
912+
anonymousMethod : anonymousMethodHeading block -> ^(TkAnonymousMethod<AnonymousMethodNodeImpl> anonymousMethodHeading block)
913+
;
914+
anonymousMethodHeading : PROCEDURE routineParameters? ((';')? interfaceDirective)*
915+
-> ^(TkAnonymousMethodHeading<AnonymousMethodHeadingNodeImpl> PROCEDURE routineParameters? ((';')? interfaceDirective)*)
916+
| FUNCTION routineParameters? routineReturnType ((';')? interfaceDirective)*
917+
-> ^(TkAnonymousMethodHeading<AnonymousMethodHeadingNodeImpl> FUNCTION routineParameters? routineReturnType ((';')? interfaceDirective)*)
912918
;
913919
expressionOrRange : expression ('..'<RangeExpressionNodeImpl>^ expression)?
914920
;
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Sonar Delphi Plugin
3+
* Copyright (C) 2024 Integrated Application Development
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
18+
*/
19+
package au.com.integradev.delphi.antlr.ast.node;
20+
21+
import au.com.integradev.delphi.antlr.ast.visitors.DelphiParserVisitor;
22+
import com.google.common.collect.ImmutableSet;
23+
import java.util.Set;
24+
import org.antlr.runtime.Token;
25+
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodHeadingNode;
26+
import org.sonar.plugins.communitydelphi.api.ast.DelphiNode;
27+
import org.sonar.plugins.communitydelphi.api.ast.RoutineParametersNode;
28+
import org.sonar.plugins.communitydelphi.api.ast.RoutineReturnTypeNode;
29+
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
30+
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineKind;
31+
import org.sonar.plugins.communitydelphi.api.token.DelphiToken;
32+
33+
public final class AnonymousMethodHeadingNodeImpl extends DelphiNodeImpl
34+
implements AnonymousMethodHeadingNode {
35+
private RoutineKind routineKind;
36+
private Set<RoutineDirective> directives;
37+
38+
public AnonymousMethodHeadingNodeImpl(Token token) {
39+
super(token);
40+
}
41+
42+
public AnonymousMethodHeadingNodeImpl(int tokenType) {
43+
super(tokenType);
44+
}
45+
46+
@Override
47+
public <T> T accept(DelphiParserVisitor<T> visitor, T data) {
48+
return visitor.visit(this, data);
49+
}
50+
51+
@Override
52+
public RoutineParametersNode getRoutineParametersNode() {
53+
return getFirstChildOfType(RoutineParametersNode.class);
54+
}
55+
56+
@Override
57+
public RoutineReturnTypeNode getReturnTypeNode() {
58+
return getFirstChildOfType(RoutineReturnTypeNode.class);
59+
}
60+
61+
@Override
62+
public RoutineKind getRoutineKind() {
63+
if (routineKind == null) {
64+
routineKind = RoutineKind.fromTokenType(getChild(0).getTokenType());
65+
}
66+
return routineKind;
67+
}
68+
69+
@Override
70+
public Set<RoutineDirective> getDirectives() {
71+
if (directives == null) {
72+
var builder = new ImmutableSet.Builder<RoutineDirective>();
73+
for (DelphiNode child : getChildren()) {
74+
DelphiToken token = child.getToken();
75+
RoutineDirective directive = RoutineDirective.fromToken(token);
76+
if (directive != null) {
77+
builder.add(directive);
78+
}
79+
}
80+
directives = builder.build();
81+
}
82+
return directives;
83+
}
84+
}

delphi-frontend/src/main/java/au/com/integradev/delphi/antlr/ast/node/AnonymousMethodNodeImpl.java

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,38 +22,50 @@
2222
import au.com.integradev.delphi.type.factory.TypeFactoryImpl;
2323
import au.com.integradev.delphi.type.parameter.FormalParameter;
2424
import java.util.Collections;
25+
import java.util.Set;
2526
import java.util.stream.Collectors;
2627
import javax.annotation.Nonnull;
2728
import org.antlr.runtime.Token;
29+
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodHeadingNode;
2830
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodNode;
2931
import org.sonar.plugins.communitydelphi.api.ast.RoutineParametersNode;
3032
import org.sonar.plugins.communitydelphi.api.ast.RoutineReturnTypeNode;
33+
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
3134
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineKind;
3235
import org.sonar.plugins.communitydelphi.api.type.Type;
36+
import org.sonar.plugins.communitydelphi.api.type.Type.ProceduralType.ProceduralKind;
3337
import org.sonar.plugins.communitydelphi.api.type.TypeFactory;
3438

3539
public final class AnonymousMethodNodeImpl extends ExpressionNodeImpl
3640
implements AnonymousMethodNode {
3741
private String image;
38-
private RoutineKind routineKind;
3942

4043
public AnonymousMethodNodeImpl(Token token) {
4144
super(token);
4245
}
4346

47+
public AnonymousMethodNodeImpl(int tokenType) {
48+
super(tokenType);
49+
}
50+
4451
@Override
4552
public <T> T accept(DelphiParserVisitor<T> visitor, T data) {
4653
return visitor.visit(this, data);
4754
}
4855

56+
@Override
57+
public AnonymousMethodHeadingNode getAnonymousMethodHeading() {
58+
return (AnonymousMethodHeadingNode) getChild(0);
59+
}
60+
4961
@Override
5062
public RoutineParametersNode getRoutineParametersNode() {
51-
return getFirstChildOfType(RoutineParametersNode.class);
63+
return getAnonymousMethodHeading().getRoutineParametersNode();
5264
}
5365

5466
@Override
5567
public RoutineReturnTypeNode getReturnTypeNode() {
56-
return getFirstChildOfType(RoutineReturnTypeNode.class);
68+
return getAnonymousMethodHeading().getReturnTypeNode();
5769
}
5870

5971
@Override
@@ -63,10 +75,17 @@ public Type getReturnType() {
6375

6476
@Override
6577
public RoutineKind getRoutineKind() {
66-
if (routineKind == null) {
67-
routineKind = RoutineKind.fromTokenType(getTokenType());
68-
}
69-
return routineKind;
78+
return getAnonymousMethodHeading().getRoutineKind();
79+
}
80+
81+
@Override
82+
public Set<RoutineDirective> getDirectives() {
83+
return getAnonymousMethodHeading().getDirectives();
84+
}
85+
86+
@Override
87+
public boolean hasDirective(RoutineDirective directive) {
88+
return getDirectives().contains(directive);
7089
}
7190

7291
@Override

delphi-frontend/src/main/java/au/com/integradev/delphi/antlr/ast/visitors/DelphiParserVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
package au.com.integradev.delphi.antlr.ast.visitors;
2424

2525
import org.sonar.plugins.communitydelphi.api.ast.AncestorListNode;
26+
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodHeadingNode;
2627
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodNode;
2728
import org.sonar.plugins.communitydelphi.api.ast.ArgumentListNode;
2829
import org.sonar.plugins.communitydelphi.api.ast.ArgumentNode;
@@ -185,6 +186,10 @@ default void visitToken(DelphiToken token, T data) {
185186
// Do nothing
186187
}
187188

189+
default T visit(AnonymousMethodHeadingNode node, T data) {
190+
return visit((DelphiNode) node, data);
191+
}
192+
188193
default T visit(ArgumentListNode node, T data) {
189194
return visit((DelphiNode) node, data);
190195
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Sonar Delphi Plugin
3+
* Copyright (C) 2024 Integrated Application Development
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU Lesser General Public
7+
* License as published by the Free Software Foundation; either
8+
* version 3 of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
* Lesser General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU Lesser General Public
16+
* License along with this program; if not, write to the Free Software
17+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
18+
*/
19+
package org.sonar.plugins.communitydelphi.api.ast;
20+
21+
import java.util.Set;
22+
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
23+
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineKind;
24+
25+
public interface AnonymousMethodHeadingNode extends DelphiNode {
26+
RoutineParametersNode getRoutineParametersNode();
27+
28+
RoutineReturnTypeNode getReturnTypeNode();
29+
30+
RoutineKind getRoutineKind();
31+
32+
Set<RoutineDirective> getDirectives();
33+
}

delphi-frontend/src/main/java/org/sonar/plugins/communitydelphi/api/ast/AnonymousMethodNode.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@
1818
*/
1919
package org.sonar.plugins.communitydelphi.api.ast;
2020

21+
import java.util.Set;
22+
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
2123
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineKind;
2224
import org.sonar.plugins.communitydelphi.api.type.Type;
2325

2426
public interface AnonymousMethodNode extends ExpressionNode {
27+
AnonymousMethodHeadingNode getAnonymousMethodHeading();
28+
2529
RoutineParametersNode getRoutineParametersNode();
2630

2731
RoutineReturnTypeNode getReturnTypeNode();
@@ -30,6 +34,10 @@ public interface AnonymousMethodNode extends ExpressionNode {
3034

3135
RoutineKind getRoutineKind();
3236

37+
Set<RoutineDirective> getDirectives();
38+
39+
boolean hasDirective(RoutineDirective directive);
40+
3341
boolean isFunction();
3442

3543
boolean isProcedure();

delphi-frontend/src/test/resources/au/com/integradev/delphi/grammar/AnonymousMethods.pas

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,13 @@ function IsStringEmptyImmediateInvocation(const aValue : string) : Boolean;
3232
end)(aValue);
3333
end;
3434

35+
function IsStringEmptyWithCallingConvention(const aValue : string) : Boolean;
36+
begin
37+
Result := StringMatches(aValue,
38+
function(const aValue : string) : Boolean register
39+
begin
40+
Result := aValue = '';
41+
end);
42+
end;
43+
3544
end.

0 commit comments

Comments
 (0)