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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Incorrect return types for `Length`, `High`, and `Low` on open/dynamic arrays depending on the
compiler version and toolchain.
- Name resolution failures on explicit references to default array properties with overloads on
ancestor types.

## [1.11.0] - 2024-11-04

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,26 @@ void testExplicitDefaultPropertyAccessOnSelfShouldNotAddIssue() {
.appendImpl("end;"))
.verifyNoIssues();
}

@Test
void testExplicitDefaultPropertyAccessOnOverloadedParentPropertyShouldAddIssue() {
CheckVerifier.newVerifier()
.withCheck(new ExplicitDefaultPropertyReferenceCheck())
.onFile(
new DelphiTestUnitBuilder()
.appendDecl("type")
.appendDecl(" TFoo = class")
.appendDecl(" property Baz[Index: Integer]: TObject; default;")
.appendDecl(" end;")
.appendDecl(" TBar = class(TFoo)")
.appendDecl(" property Baz[Name: string]: TObject; default;")
.appendDecl(" end;")
.appendImpl("procedure Test(Bar: TBar);")
.appendImpl("var")
.appendImpl(" Obj: TObject;")
.appendImpl("begin")
.appendImpl(" Obj := Bar.Baz[0]; // Noncompliant")
.appendImpl("end;"))
.verifyIssues();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,24 @@ private void handleParenthesizedExpression(ParenthesizedExpressionNode parenthes
}

private boolean handleDefaultArrayProperties(ArrayAccessorNode accessor) {
if (!declarations.isEmpty() && isArrayProperty(Iterables.getLast(declarations))) {
if (!declarations.isEmpty()
&& declarations.stream().allMatch(NameResolver::isDefaultArrayProperty)) {
Type type = currentType;
if (type.isClassReference()) {
type = ((ClassReferenceType) type).classType();
} else if (type.isProcedural()) {
type = ((ProceduralType) type).returnType();
}

if (type.isStruct()) {
StructType structType = (StructType) TypeUtils.findBaseType(type);
NameDeclaration declaration = Iterables.getLast(declarations);
((StructTypeImpl) structType)
.findDefaultArrayProperties().stream()
.filter(property -> property.getName().equalsIgnoreCase(declaration.getName()))
.forEach(declarations::add);
}

// An explicit array property access can be handled by argument disambiguation.
return false;
}
Expand Down Expand Up @@ -830,6 +847,11 @@ private static boolean isArrayProperty(NameDeclaration declaration) {
&& ((PropertyNameDeclaration) declaration).isArrayProperty();
}

private static boolean isDefaultArrayProperty(NameDeclaration declaration) {
return isArrayProperty(declaration)
&& ((PropertyNameDeclaration) declaration).isDefaultProperty();
}

void disambiguateImplicitEmptyArgumentList() {
if (declarations.stream().noneMatch(RoutineNameDeclaration.class::isInstance)) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,22 @@ void testHiddenDefaultProperties() {
verifyUsages(11, 14, reference(27, 25));
}

@Test
void testDefaultArrayProperties() {
execute("properties/DefaultArrayProperties.pas");
verifyUsages(12, 14, reference(44, 15), reference(48, 19));
verifyUsages(18, 14, reference(45, 15), reference(49, 19));
verifyUsages(19, 14, reference(46, 15), reference(50, 19));
}

@Test
void testDefaultClassArrayProperties() {
execute("properties/DefaultClassArrayProperties.pas");
verifyUsages(12, 20, reference(46, 16), reference(50, 24));
verifyUsages(18, 20, reference(47, 16), reference(51, 24));
verifyUsages(19, 20, reference(48, 16), reference(52, 24));
}

@Test
void testSimpleOverloads() {
execute("overloads/Simple.pas");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
unit DefaultArrayProperties;

interface

implementation

type
TEnum = (Flarp);

TFoo = class
function GetBaz(I: Integer): Integer;
property Baz[I: Integer]: Integer read GetBaz; default;
end;

TBar = class(TFoo)
function GetBaz(S: string): string; overload;
function GetBaz(E: TEnum): TEnum; overload;
property Baz[S: string]: string read GetBaz; default;
property Baz[E: TEnum]: TEnum read GetBaz; default;
end;

function TFoo.GetBaz(I: Integer): Integer;
begin
Result := I;
end;

function TBar.GetBaz(S: string): string;
begin
Result := S;
end;

function TBar.GetBaz(E: TEnum): TEnum;
begin
Result := E;
end;

function BarFunc: TBar;
begin
Result := TBar.Create;
end;

procedure Test(Bar: TBar);
begin
var A := Bar.Baz[123];
var B := Bar.Baz['123'];
var C := Bar.Baz[Flarp];

var D := BarFunc.Baz[123];
var E := BarFunc.Baz['123'];
var F := BarFunc.Baz[Flarp];
end;

end.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
unit DefaultClassArrayProperties;

interface

implementation

type
TEnum = (Flarp);

TFoo = class
class function GetBaz(I: Integer): Integer; static;
class property Baz[I: Integer]: Integer read GetBaz; default;
end;

TBar = class(TFoo)
class function GetBaz(S: string): string; overload; static;
class function GetBaz(E: TEnum): TEnum; overload; static;
class property Baz[S: string]: string read GetBaz; default;
class property Baz[E: TEnum]: TEnum read GetBaz; default;
end;

TBarClass = class of TBar;

class function TFoo.GetBaz(I: Integer): Integer;
begin
Result := I;
end;

class function TBar.GetBaz(S: string): string;
begin
Result := S;
end;

class function TBar.GetBaz(E: TEnum): TEnum;
begin
Result := E;
end;

function BarClassFunc: TBarClass;
begin
Result := TBar;
end;

procedure Test;
begin
var A := TBar.Baz[123];
var B := TBar.Baz['123'];
var C := TBar.Baz[Flarp];

var D := BarClassFunc.Baz[123];
var E := BarClassFunc.Baz['123'];
var F := BarClassFunc.Baz[Flarp];
end;


end.
Loading