Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- **API:** `ProceduralType::directives` method.
- **API:** `ProceduralTypeNode::getDirectives` method.
- **API:** `ProceduralTypeNode::hasDirective` method.
- **API:** `ProceduralTypeHeadingNode::getDirectives` method.
- **API:** `AnonymousMethodNode::getAnonymousMethodHeading` method.
- **API:** `AnonymousMethodNode::getDirectives` method.
- **API:** `AnonymousMethodNode::hasDirective` method.
- **API:** `AnonymousMethodHeadingNode` node type.
- **API:** `DelphiTokenType.ANONYMOUS_METHOD` token type.
- **API:** `DelphiTokenType.ANONYMOUS_METHOD_HEADING` token type.

### Fixed

- Parsing errors on `implementation` keywords nested in conditional branches.
- Parsing errors on anonymous methods with routine directives (like calling conventions).
- `IndexOutOfBoundsException` errors around procedural types declared with `varargs` in
`VariableInitialization`.

## [1.10.0] - 2024-10-01

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1237,6 +1237,25 @@ void testOutParameterInitializedInNestedRoutineShouldNotAddIssue() {
.verifyNoIssues();
}

// See: https://github.com/integrated-application-development/sonar-delphi/issues/297
@Test
void testIssue297ShouldAddIssue() {
CheckVerifier.newVerifier()
.withCheck(new VariableInitializationCheck())
.onFile(
new DelphiTestUnitBuilder()
.appendImpl("var")
.appendImpl(" SetValues: procedure(Values: Integer); cdecl varargs;")
.appendImpl("")
.appendImpl("procedure MyProcedure;")
.appendImpl("var")
.appendImpl(" Value: Integer;")
.appendImpl("begin")
.appendImpl(" SetValues(Value, Value); // Noncompliant")
.appendImpl("end;"))
.verifyIssues();
}

private static DelphiTestUnitBuilder createSysUtils() {
return new DelphiTestUnitBuilder()
.unitName("System.SysUtils")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ tokens {
TkArrayConstructor;
TkArrayIndices;
TkArgument;
TkAnonymousMethod;
TkAnonymousMethodHeading;
}

@header
Expand Down Expand Up @@ -907,8 +909,12 @@ argumentExpression : expression writeArguments?
;
writeArguments : ':'! expression (':'! expression)? // See: https://docwiki.embarcadero.com/Libraries/en/System.Write
;
anonymousMethod : PROCEDURE<AnonymousMethodNodeImpl>^ routineParameters? block
| FUNCTION<AnonymousMethodNodeImpl>^ routineParameters? routineReturnType block
anonymousMethod : anonymousMethodHeading block -> ^(TkAnonymousMethod<AnonymousMethodNodeImpl> anonymousMethodHeading block)
;
anonymousMethodHeading : PROCEDURE routineParameters? ((';')? interfaceDirective)*
-> ^(TkAnonymousMethodHeading<AnonymousMethodHeadingNodeImpl> PROCEDURE routineParameters? ((';')? interfaceDirective)*)
| FUNCTION routineParameters? routineReturnType ((';')? interfaceDirective)*
-> ^(TkAnonymousMethodHeading<AnonymousMethodHeadingNodeImpl> FUNCTION routineParameters? routineReturnType ((';')? interfaceDirective)*)
;
expressionOrRange : expression ('..'<RangeExpressionNodeImpl>^ expression)?
;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Sonar Delphi Plugin
* Copyright (C) 2024 Integrated Application Development
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package au.com.integradev.delphi.antlr.ast.node;

import au.com.integradev.delphi.antlr.ast.visitors.DelphiParserVisitor;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
import org.antlr.runtime.Token;
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodHeadingNode;
import org.sonar.plugins.communitydelphi.api.ast.DelphiNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineParametersNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineReturnTypeNode;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineKind;
import org.sonar.plugins.communitydelphi.api.token.DelphiToken;

public final class AnonymousMethodHeadingNodeImpl extends DelphiNodeImpl
implements AnonymousMethodHeadingNode {
private RoutineKind routineKind;
private Set<RoutineDirective> directives;

public AnonymousMethodHeadingNodeImpl(Token token) {
super(token);
}

public AnonymousMethodHeadingNodeImpl(int tokenType) {
super(tokenType);
}

@Override
public <T> T accept(DelphiParserVisitor<T> visitor, T data) {
return visitor.visit(this, data);
}

@Override
public RoutineParametersNode getRoutineParametersNode() {
return getFirstChildOfType(RoutineParametersNode.class);
}

@Override
public RoutineReturnTypeNode getReturnTypeNode() {
return getFirstChildOfType(RoutineReturnTypeNode.class);
}

@Override
public RoutineKind getRoutineKind() {
if (routineKind == null) {
routineKind = RoutineKind.fromTokenType(getChild(0).getTokenType());
}
return routineKind;
}

@Override
public Set<RoutineDirective> getDirectives() {
if (directives == null) {
var builder = new ImmutableSet.Builder<RoutineDirective>();
for (DelphiNode child : getChildren()) {
DelphiToken token = child.getToken();
RoutineDirective directive = RoutineDirective.fromToken(token);
if (directive != null) {
builder.add(directive);
}
}
directives = builder.build();
}
return directives;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,50 @@
import au.com.integradev.delphi.type.factory.TypeFactoryImpl;
import au.com.integradev.delphi.type.parameter.FormalParameter;
import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.antlr.runtime.Token;
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodHeadingNode;
import org.sonar.plugins.communitydelphi.api.ast.AnonymousMethodNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineParametersNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineReturnTypeNode;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineKind;
import org.sonar.plugins.communitydelphi.api.type.Type;
import org.sonar.plugins.communitydelphi.api.type.Type.ProceduralType.ProceduralKind;
import org.sonar.plugins.communitydelphi.api.type.TypeFactory;

public final class AnonymousMethodNodeImpl extends ExpressionNodeImpl
implements AnonymousMethodNode {
private String image;
private RoutineKind routineKind;

public AnonymousMethodNodeImpl(Token token) {
super(token);
}

public AnonymousMethodNodeImpl(int tokenType) {
super(tokenType);
}

@Override
public <T> T accept(DelphiParserVisitor<T> visitor, T data) {
return visitor.visit(this, data);
}

@Override
public AnonymousMethodHeadingNode getAnonymousMethodHeading() {
return (AnonymousMethodHeadingNode) getChild(0);
}

@Override
public RoutineParametersNode getRoutineParametersNode() {
return getFirstChildOfType(RoutineParametersNode.class);
return getAnonymousMethodHeading().getRoutineParametersNode();
}

@Override
public RoutineReturnTypeNode getReturnTypeNode() {
return getFirstChildOfType(RoutineReturnTypeNode.class);
return getAnonymousMethodHeading().getReturnTypeNode();
}

@Override
Expand All @@ -63,10 +75,17 @@ public Type getReturnType() {

@Override
public RoutineKind getRoutineKind() {
if (routineKind == null) {
routineKind = RoutineKind.fromTokenType(getTokenType());
}
return routineKind;
return getAnonymousMethodHeading().getRoutineKind();
}

@Override
public Set<RoutineDirective> getDirectives() {
return getAnonymousMethodHeading().getDirectives();
}

@Override
public boolean hasDirective(RoutineDirective directive) {
return getDirectives().contains(directive);
}

@Override
Expand Down Expand Up @@ -104,14 +123,16 @@ protected Type createType() {
RoutineReturnTypeNode returnTypeNode = getReturnTypeNode();

return ((TypeFactoryImpl) getTypeFactory())
.anonymous(
.createProcedural(
ProceduralKind.ANONYMOUS,
parameters == null
? Collections.emptyList()
: parameters.getParameters().stream()
.map(FormalParameter::create)
.collect(Collectors.toUnmodifiableList()),
returnTypeNode == null
? TypeFactory.voidType()
: returnTypeNode.getTypeNode().getType());
: returnTypeNode.getTypeNode().getType(),
getDirectives());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@

import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.antlr.runtime.Token;
import org.sonar.plugins.communitydelphi.api.ast.FormalParameterNode.FormalParameterData;
import org.sonar.plugins.communitydelphi.api.ast.ProceduralTypeNode;
import org.sonar.plugins.communitydelphi.api.ast.ProcedureTypeHeadingNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineParametersNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineReturnTypeNode;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
import org.sonar.plugins.communitydelphi.api.type.Type;
import org.sonar.plugins.communitydelphi.api.type.TypeFactory;

Expand Down Expand Up @@ -53,4 +55,14 @@ public List<FormalParameterData> getParameters() {
RoutineParametersNode parametersNode = getHeading().getRoutineParametersNode();
return parametersNode == null ? Collections.emptyList() : parametersNode.getParameters();
}

@Override
public Set<RoutineDirective> getDirectives() {
return getHeading().getDirectives();
}

@Override
public boolean hasDirective(RoutineDirective directive) {
return getDirectives().contains(directive);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.antlr.runtime.Token;
import org.sonar.plugins.communitydelphi.api.ast.ProcedureOfObjectTypeNode;
import org.sonar.plugins.communitydelphi.api.type.Type;
import org.sonar.plugins.communitydelphi.api.type.Type.ProceduralType.ProceduralKind;

public final class ProcedureOfObjectTypeNodeImpl extends ProceduralTypeNodeImpl
implements ProcedureOfObjectTypeNode {
Expand All @@ -42,10 +43,12 @@ public <T> T accept(DelphiParserVisitor<T> visitor, T data) {
@Nonnull
protected Type createType() {
return ((TypeFactoryImpl) getTypeFactory())
.ofObject(
.createProcedural(
ProceduralKind.PROCEDURE_OF_OBJECT,
getParameters().stream()
.map(FormalParameter::create)
.collect(Collectors.toUnmodifiableList()),
getReturnType());
getReturnType(),
getDirectives());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.antlr.runtime.Token;
import org.sonar.plugins.communitydelphi.api.ast.ProcedureReferenceTypeNode;
import org.sonar.plugins.communitydelphi.api.type.Type;
import org.sonar.plugins.communitydelphi.api.type.Type.ProceduralType.ProceduralKind;

public final class ProcedureReferenceTypeNodeImpl extends ProceduralTypeNodeImpl
implements ProcedureReferenceTypeNode {
Expand All @@ -42,10 +43,12 @@ public <T> T accept(DelphiParserVisitor<T> visitor, T data) {
@Nonnull
protected Type createType() {
return ((TypeFactoryImpl) getTypeFactory())
.reference(
.createProcedural(
ProceduralKind.REFERENCE,
getParameters().stream()
.map(FormalParameter::create)
.collect(Collectors.toUnmodifiableList()),
getReturnType());
getReturnType(),
getDirectives());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,21 @@
package au.com.integradev.delphi.antlr.ast.node;

import au.com.integradev.delphi.antlr.ast.visitors.DelphiParserVisitor;
import com.google.common.collect.ImmutableSet;
import java.util.Set;
import javax.annotation.Nullable;
import org.antlr.runtime.Token;
import org.sonar.plugins.communitydelphi.api.ast.DelphiNode;
import org.sonar.plugins.communitydelphi.api.ast.ProcedureTypeHeadingNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineParametersNode;
import org.sonar.plugins.communitydelphi.api.ast.RoutineReturnTypeNode;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
import org.sonar.plugins.communitydelphi.api.token.DelphiToken;

public final class ProcedureTypeHeadingNodeImpl extends DelphiNodeImpl
implements ProcedureTypeHeadingNode {
private Set<RoutineDirective> directives;

public ProcedureTypeHeadingNodeImpl(Token token) {
super(token);
}
Expand Down Expand Up @@ -61,6 +67,22 @@ public RoutineReturnTypeNode getRoutineReturnTypeNode() {
return (node instanceof RoutineReturnTypeNode) ? (RoutineReturnTypeNode) node : null;
}

@Override
public Set<RoutineDirective> getDirectives() {
if (directives == null) {
var builder = new ImmutableSet.Builder<RoutineDirective>();
for (DelphiNode child : getChildren()) {
DelphiToken token = child.getToken();
RoutineDirective directive = RoutineDirective.fromToken(token);
if (directive != null) {
builder.add(directive);
}
}
directives = builder.build();
}
return directives;
}

private boolean hasRoutineParametersNode() {
return getRoutineParametersNode() != null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.antlr.runtime.Token;
import org.sonar.plugins.communitydelphi.api.ast.ProcedureTypeNode;
import org.sonar.plugins.communitydelphi.api.type.Type;
import org.sonar.plugins.communitydelphi.api.type.Type.ProceduralType.ProceduralKind;

public final class ProcedureTypeNodeImpl extends ProceduralTypeNodeImpl
implements ProcedureTypeNode {
Expand All @@ -46,10 +47,12 @@ public <T> T accept(DelphiParserVisitor<T> visitor, T data) {
@Nonnull
protected Type createType() {
return ((TypeFactoryImpl) getTypeFactory())
.procedure(
.createProcedural(
ProceduralKind.PROCEDURE,
getParameters().stream()
.map(FormalParameter::create)
.collect(Collectors.toUnmodifiableList()),
getReturnType());
getReturnType(),
getDirectives());
}
}
Loading
Loading