Skip to content

Commit 4b72bad

Browse files
committed
Fix name resolution on explicitly-accessed overloaded default properties
1 parent 0208bc9 commit 4b72bad

File tree

6 files changed

+172
-1
lines changed

6 files changed

+172
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

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

1517
## [1.11.0] - 2024-11-04
1618

delphi-checks/src/test/java/au/com/integradev/delphi/checks/ExplicitDefaultPropertyReferenceCheckTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,26 @@ void testExplicitDefaultPropertyAccessOnSelfShouldNotAddIssue() {
8383
.appendImpl("end;"))
8484
.verifyNoIssues();
8585
}
86+
87+
@Test
88+
void testExplicitDefaultPropertyAccessOnOverloadedParentPropertyShouldAddIssue() {
89+
CheckVerifier.newVerifier()
90+
.withCheck(new ExplicitDefaultPropertyReferenceCheck())
91+
.onFile(
92+
new DelphiTestUnitBuilder()
93+
.appendDecl("type")
94+
.appendDecl(" TFoo = class")
95+
.appendDecl(" property Baz[Index: Integer]: TObject; default;")
96+
.appendDecl(" end;")
97+
.appendDecl(" TBar = class(TFoo)")
98+
.appendDecl(" property Baz[Name: string]: TObject; default;")
99+
.appendDecl(" end;")
100+
.appendImpl("procedure Test(Bar: TBar);")
101+
.appendImpl("var")
102+
.appendImpl(" Obj: TObject;")
103+
.appendImpl("begin")
104+
.appendImpl(" Obj := Bar.Baz[0]; // Noncompliant")
105+
.appendImpl("end;"))
106+
.verifyIssues();
107+
}
86108
}

delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/resolve/NameResolver.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,24 @@ private void handleParenthesizedExpression(ParenthesizedExpressionNode parenthes
783783
}
784784

785785
private boolean handleDefaultArrayProperties(ArrayAccessorNode accessor) {
786-
if (!declarations.isEmpty() && isArrayProperty(Iterables.getLast(declarations))) {
786+
if (!declarations.isEmpty()
787+
&& declarations.stream().allMatch(NameResolver::isDefaultArrayProperty)) {
788+
Type type = currentType;
789+
if (type.isClassReference()) {
790+
type = ((ClassReferenceType) type).classType();
791+
} else if (type.isProcedural()) {
792+
type = ((ProceduralType) type).returnType();
793+
}
794+
795+
if (type.isStruct()) {
796+
StructType structType = (StructType) TypeUtils.findBaseType(type);
797+
NameDeclaration declaration = Iterables.getLast(declarations);
798+
((StructTypeImpl) structType)
799+
.findDefaultArrayProperties().stream()
800+
.filter(property -> property.getName().equalsIgnoreCase(declaration.getName()))
801+
.forEach(declarations::add);
802+
}
803+
787804
// An explicit array property access can be handled by argument disambiguation.
788805
return false;
789806
}
@@ -830,6 +847,11 @@ private static boolean isArrayProperty(NameDeclaration declaration) {
830847
&& ((PropertyNameDeclaration) declaration).isArrayProperty();
831848
}
832849

850+
private static boolean isDefaultArrayProperty(NameDeclaration declaration) {
851+
return isArrayProperty(declaration)
852+
&& ((PropertyNameDeclaration) declaration).isDefaultProperty();
853+
}
854+
833855
void disambiguateImplicitEmptyArgumentList() {
834856
if (declarations.stream().noneMatch(RoutineNameDeclaration.class::isInstance)) {
835857
return;

delphi-frontend/src/test/java/au/com/integradev/delphi/executor/DelphiSymbolTableExecutorTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -870,6 +870,22 @@ void testHiddenDefaultProperties() {
870870
verifyUsages(11, 14, reference(27, 25));
871871
}
872872

873+
@Test
874+
void testDefaultArrayProperties() {
875+
execute("properties/DefaultArrayProperties.pas");
876+
verifyUsages(12, 14, reference(44, 15), reference(48, 19));
877+
verifyUsages(18, 14, reference(45, 15), reference(49, 19));
878+
verifyUsages(19, 14, reference(46, 15), reference(50, 19));
879+
}
880+
881+
@Test
882+
void testDefaultClassArrayProperties() {
883+
execute("properties/DefaultClassArrayProperties.pas");
884+
verifyUsages(12, 20, reference(46, 16), reference(50, 24));
885+
verifyUsages(18, 20, reference(47, 16), reference(51, 24));
886+
verifyUsages(19, 20, reference(48, 16), reference(52, 24));
887+
}
888+
873889
@Test
874890
void testSimpleOverloads() {
875891
execute("overloads/Simple.pas");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
unit DefaultArrayProperties;
2+
3+
interface
4+
5+
implementation
6+
7+
type
8+
TEnum = (Flarp);
9+
10+
TFoo = class
11+
function GetBaz(I: Integer): Integer;
12+
property Baz[I: Integer]: Integer read GetBaz; default;
13+
end;
14+
15+
TBar = class(TFoo)
16+
function GetBaz(S: string): string; overload;
17+
function GetBaz(E: TEnum): TEnum; overload;
18+
property Baz[S: string]: string read GetBaz; default;
19+
property Baz[E: TEnum]: TEnum read GetBaz; default;
20+
end;
21+
22+
function TFoo.GetBaz(I: Integer): Integer;
23+
begin
24+
Result := I;
25+
end;
26+
27+
function TBar.GetBaz(S: string): string;
28+
begin
29+
Result := S;
30+
end;
31+
32+
function TBar.GetBaz(E: TEnum): TEnum;
33+
begin
34+
Result := E;
35+
end;
36+
37+
function BarFunc: TBar;
38+
begin
39+
Result := TBar.Create;
40+
end;
41+
42+
procedure Test(Bar: TBar);
43+
begin
44+
var A := Bar.Baz[123];
45+
var B := Bar.Baz['123'];
46+
var C := Bar.Baz[Flarp];
47+
48+
var D := BarFunc.Baz[123];
49+
var E := BarFunc.Baz['123'];
50+
var F := BarFunc.Baz[Flarp];
51+
end;
52+
53+
end.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
unit DefaultClassArrayProperties;
2+
3+
interface
4+
5+
implementation
6+
7+
type
8+
TEnum = (Flarp);
9+
10+
TFoo = class
11+
class function GetBaz(I: Integer): Integer; static;
12+
class property Baz[I: Integer]: Integer read GetBaz; default;
13+
end;
14+
15+
TBar = class(TFoo)
16+
class function GetBaz(S: string): string; overload; static;
17+
class function GetBaz(E: TEnum): TEnum; overload; static;
18+
class property Baz[S: string]: string read GetBaz; default;
19+
class property Baz[E: TEnum]: TEnum read GetBaz; default;
20+
end;
21+
22+
TBarClass = class of TBar;
23+
24+
class function TFoo.GetBaz(I: Integer): Integer;
25+
begin
26+
Result := I;
27+
end;
28+
29+
class function TBar.GetBaz(S: string): string;
30+
begin
31+
Result := S;
32+
end;
33+
34+
class function TBar.GetBaz(E: TEnum): TEnum;
35+
begin
36+
Result := E;
37+
end;
38+
39+
function BarClassFunc: TBarClass;
40+
begin
41+
Result := TBar;
42+
end;
43+
44+
procedure Test;
45+
begin
46+
var A := TBar.Baz[123];
47+
var B := TBar.Baz['123'];
48+
var C := TBar.Baz[Flarp];
49+
50+
var D := BarClassFunc.Baz[123];
51+
var E := BarClassFunc.Baz['123'];
52+
var F := BarClassFunc.Baz[Flarp];
53+
end;
54+
55+
56+
end.

0 commit comments

Comments
 (0)