Skip to content

Commit b55709c

Browse files
committed
Fix implicit generic specialization of open array types
Previously, our implicit specialization required **exact** mappings of types to top-level type parameters, but open arrays can go one level deep with the type parameter as the element type.
1 parent 7edb90b commit b55709c

File tree

4 files changed

+37
-6
lines changed

4 files changed

+37
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3737
- False positives on enumerator property `Current` in `UnusedProperty`.
3838
- False positives on enums that are never referenced by name (but have used values) in `UnusedType`.
3939
- Name resolution failures in legacy initialization sections referencing the implementation section.
40-
- Name resolution failures whena accessing ancestors of enclosing types from nested type methods.
40+
- Name resolution failures when accessing ancestors of enclosing types from nested type methods.
41+
- Name resolution failures on invocations of methods with generic open array parameters.
4142
- Incorrect file position calculation for multiline string tokens.
4243
- Analysis errors around `type of` type declarations.
4344

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.sonar.plugins.communitydelphi.api.type.IntrinsicType;
3333
import org.sonar.plugins.communitydelphi.api.type.Parameter;
3434
import org.sonar.plugins.communitydelphi.api.type.Type;
35+
import org.sonar.plugins.communitydelphi.api.type.Type.CollectionType;
3536

3637
/**
3738
* Stores information about an invocation candidate, used for overload resolution. Based directly
@@ -197,11 +198,17 @@ public static InvocationCandidate implicitSpecialization(
197198
Map<Type, Type> argumentsByTypeParameters = new HashMap<>();
198199
for (int i = 0; i < argumentTypes.size(); ++i) {
199200
Type parameterType = routineDeclaration.getParameter(i).getType();
201+
Type argumentType = argumentTypes.get(i);
202+
203+
if (parameterType.isOpenArray() && argumentType.isArray()) {
204+
parameterType = ((CollectionType) parameterType).elementType();
205+
argumentType = ((CollectionType) argumentType).elementType();
206+
}
207+
200208
if (!parameterType.isTypeParameter()) {
201209
continue;
202210
}
203211

204-
Type argumentType = argumentTypes.get(i);
205212
Type existingMapping = argumentsByTypeParameters.get(parameterType);
206213

207214
if (existingMapping != null && !existingMapping.is(argumentType)) {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1053,7 +1053,8 @@ void testGenericParameterizedMethods() {
10531053
@Test
10541054
void testGenericImplicitSpecializations() {
10551055
execute("generics/ImplicitSpecialization.pas");
1056-
verifyUsages(14, 14, reference(19, 20), reference(26, 2), reference(27, 2), reference(28, 2));
1056+
verifyUsages(15, 14, reference(21, 20), reference(37, 2), reference(38, 2), reference(39, 2));
1057+
verifyUsages(16, 14, reference(26, 20), reference(48, 2), reference(49, 2), reference(50, 2));
10571058
}
10581059

10591060
@Test

delphi-frontend/src/test/resources/au/com/integradev/delphi/symbol/generics/ImplicitSpecialization.pas

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ interface
44

55
type
66
TConsumable = class
7-
constructor Create; override;
7+
constructor Create;
88
procedure GetEaten;
99
class function Pizza: TConsumable;
1010
end;
1111

1212
TConsumer = class
13-
procedure Test(Consumable: TConsumable);
14-
procedure Consume<T: TConsumable>(Tasty: T);
13+
procedure Test(Consumable: TConsumable); overload;
14+
procedure Test(Consumables: array of TConsumable); overload;
15+
procedure Consume<T: TConsumable>(Consumable: T); overload;
16+
procedure Consume<T: TConsumable>(Consumables: array of T); overload;
1517
end;
1618

1719
implementation
@@ -21,11 +23,31 @@ procedure TConsumer.Consume<T>(Consumable: T);
2123
Consumable.GetEaten;
2224
end;
2325

26+
procedure TConsumer.Consume<T>(Consumables: array of T);
27+
var
28+
Consumable: T;
29+
begin
30+
for Consumable in Consumables do begin
31+
Consume(Consumable);
32+
end;
33+
end;
34+
2435
procedure TConsumer.Test(Consumable: TConsumable);
2536
begin
2637
Consume(Consumable);
2738
Consume(TConsumable.Pizza);
2839
Consume(TConsumable.Create);
2940
end;
3041

42+
procedure TConsumer.Test(Consumables: array of TConsumable);
43+
var
44+
DynamicConsumables: TArray<TConsumable>;
45+
begin
46+
DynamicConsumables := [TConsumable.Create];
47+
48+
Consume(Consumables);
49+
Consume(DynamicConsumables);
50+
Consume([TConsumable.Pizza]);
51+
end;
52+
3153
end.

0 commit comments

Comments
 (0)