diff --git a/CHANGELOG.md b/CHANGELOG.md index c76214a07..4046da3dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Name resolution failures when accessing ancestors of enclosing types from nested type methods. - Name resolution failures on invocations of methods with generic open array parameters. - Name resolution failures around `Create` calls on types with `constructor` constraints. +- Name resolution failures on `read` and `write` specifiers of indexed properties. - Incorrect file position calculation for multiline string tokens. - Analysis errors around `type of` type declarations. diff --git a/delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/resolve/NameResolutionHelper.java b/delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/resolve/NameResolutionHelper.java index 56af410f9..be5f4186c 100644 --- a/delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/resolve/NameResolutionHelper.java +++ b/delphi-frontend/src/main/java/au/com/integradev/delphi/symbol/resolve/NameResolutionHelper.java @@ -81,6 +81,7 @@ import org.sonar.plugins.communitydelphi.api.symbol.scope.DelphiScope; import org.sonar.plugins.communitydelphi.api.symbol.scope.FileScope; import org.sonar.plugins.communitydelphi.api.symbol.scope.RoutineScope; +import org.sonar.plugins.communitydelphi.api.type.IntrinsicType; import org.sonar.plugins.communitydelphi.api.type.Type; import org.sonar.plugins.communitydelphi.api.type.Type.AliasType; import org.sonar.plugins.communitydelphi.api.type.Type.ProceduralType; @@ -252,17 +253,15 @@ public void resolve(PropertyNode property) { if (read != null) { NameResolver readResolver = createNameResolver(); readResolver.readPrimaryExpression(read.getExpression()); - readResolver.disambiguateParameters(property.getParameterTypes()); + readResolver.disambiguateParameters(getGetterParameterTypes(property)); readResolver.addToSymbolTable(); } PropertyWriteSpecifierNode write = property.getWriteSpecifier(); if (write != null) { - List parameterTypes = new ArrayList<>(property.getParameterTypes()); - parameterTypes.add(property.getType()); NameResolver writeResolver = createNameResolver(); writeResolver.readPrimaryExpression(write.getExpression()); - writeResolver.disambiguateParameters(parameterTypes); + writeResolver.disambiguateParameters(getSetterParameterTypes(property)); writeResolver.addToSymbolTable(); } @@ -272,6 +271,25 @@ public void resolve(PropertyNode property) { } } + private List getGetterParameterTypes(PropertyNode property) { + if (property.getIndexSpecifier() == null) { + return property.getParameterTypes(); + } + + var parameterTypes = new ArrayList<>(property.getParameterTypes()); + parameterTypes.add(typeFactory.getIntrinsic(IntrinsicType.INTEGER)); + return parameterTypes; + } + + private List getSetterParameterTypes(PropertyNode property) { + var parameterTypes = new ArrayList<>(property.getParameterTypes()); + if (property.getIndexSpecifier() != null) { + parameterTypes.add(typeFactory.getIntrinsic(IntrinsicType.INTEGER)); + } + parameterTypes.add(property.getType()); + return parameterTypes; + } + public void resolve(RoutineDeclarationNode routine) { resolveRoutine(routine); } diff --git a/delphi-frontend/src/test/java/au/com/integradev/delphi/executor/DelphiSymbolTableExecutorTest.java b/delphi-frontend/src/test/java/au/com/integradev/delphi/executor/DelphiSymbolTableExecutorTest.java index 193706998..342cd64f3 100644 --- a/delphi-frontend/src/test/java/au/com/integradev/delphi/executor/DelphiSymbolTableExecutorTest.java +++ b/delphi-frontend/src/test/java/au/com/integradev/delphi/executor/DelphiSymbolTableExecutorTest.java @@ -103,6 +103,27 @@ void testSimilarParameterDeclarations() { verifyUsages(9, 19); } + @Test + void testProperties() { + execute("Properties.pas"); + // Field + verifyUsages(10, 4, reference(19, 33), reference(19, 46)); + // Normal getter + verifyUsages(16, 13, reference(20, 33), reference(21, 33)); + // Normal setter + verifyUsages(14, 14, reference(20, 50)); + // Const setter + verifyUsages(12, 14, reference(21, 50)); + // Alias setter + verifyUsages(13, 14, reference(22, 55)); + // Alias getter + verifyUsages(17, 13, reference(22, 33)); + // Index getter + verifyUsages(29, 13, reference(32, 41)); + // Index setter + verifyUsages(30, 14, reference(32, 58)); + } + @Test void testRecords() { execute("Records.pas"); diff --git a/delphi-frontend/src/test/resources/au/com/integradev/delphi/symbol/Properties.pas b/delphi-frontend/src/test/resources/au/com/integradev/delphi/symbol/Properties.pas new file mode 100644 index 000000000..72a188141 --- /dev/null +++ b/delphi-frontend/src/test/resources/au/com/integradev/delphi/symbol/Properties.pas @@ -0,0 +1,37 @@ +unit Properties; + +interface + +type + TIntAlias = Integer; + + TMyObj = class(TObject) + private + FField: Integer; + + procedure SetMyFieldConst(const Val: Integer); + procedure SetMyFieldAlias(Val: TIntAlias); + procedure SetMyField(Val: Integer); + + function GetMyField: Integer; + function GetMyFieldAlias: TIntAlias; + public + property Prop1: Integer read FField write FField; + property Prop2: Integer read GetMyField write SetMyField; + property Prop3: Integer read GetMyField write SetMyFieldConst; + property Prop4: Integer read GetMyFieldAlias write SetMyFieldAlias; + end; + + TIndexObj = class(TObject) + private + FField: TObject; + + function GetMyField(Index: Integer): TObject; + procedure SetMyField(Index: Integer; Value: TObject); + public + property Prop1: TObject index 0 read GetMyField write SetMyField; + end; + +implementation + +end. \ No newline at end of file