From d434c7b16d32c08ef50dc5e2a97fbb0b3ec4d1da Mon Sep 17 00:00:00 2001 From: m0rkeulv Date: Sat, 24 Aug 2024 18:54:45 +0200 Subject: [PATCH] Treat optional fields/parameters types as null<> wrapped types. --- .../plugins/haxe/lang/psi/HaxePsiField.java | 2 ++ .../haxe/lang/psi/impl/HaxePsiFieldImpl.java | 11 +++++- .../type/HaxeExpressionEvaluatorHandlers.java | 10 +++++- .../haxe/model/type/HaxeTypeResolver.java | 3 ++ .../plugins/haxe/model/type/ResultHolder.java | 10 ++++++ .../haxe/ide/inlay/HaxeLocalVarInlayTest.java | 4 +++ .../OptionalFieldsHints.hx | 35 +++++++++++++++++++ 7 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/testData/inlay/haxe.local.variable/OptionalFieldsHints.hx diff --git a/src/main/java/com/intellij/plugins/haxe/lang/psi/HaxePsiField.java b/src/main/java/com/intellij/plugins/haxe/lang/psi/HaxePsiField.java index fc137f793..6f878bfb1 100644 --- a/src/main/java/com/intellij/plugins/haxe/lang/psi/HaxePsiField.java +++ b/src/main/java/com/intellij/plugins/haxe/lang/psi/HaxePsiField.java @@ -40,4 +40,6 @@ public interface HaxePsiField extends HaxeComponent, PsiField, HaxeModelTarget { @Nullable HaxeVarInit getVarInit(); + + boolean isOptional(); } diff --git a/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxePsiFieldImpl.java b/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxePsiFieldImpl.java index 2599a7622..42afd3531 100644 --- a/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxePsiFieldImpl.java +++ b/src/main/java/com/intellij/plugins/haxe/lang/psi/impl/HaxePsiFieldImpl.java @@ -39,6 +39,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import static com.intellij.plugins.haxe.metadata.psi.HaxeMeta.OPTIONAL; + /** * Created by srikanthg on 10/9/14. */ @@ -111,7 +113,14 @@ public PsiIdentifier getNameIdentifier() { } public boolean isOptional() { - return this instanceof HaxeOptionalFieldDeclaration; + if(this instanceof HaxeOptionalFieldDeclaration) return true; + if(this instanceof HaxeAnonymousTypeField field) { + return field.getOptionalMark() != null; + } + if(this instanceof HaxeFieldDeclaration fieldDeclaration) { + return fieldDeclaration.hasCompileTimeMetadata(OPTIONAL); + } + return false; } @Nullable diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorHandlers.java b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorHandlers.java index a4b5d9afa..e94e6adec 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorHandlers.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluatorHandlers.java @@ -499,7 +499,11 @@ static ResultHolder handleParameter( if (typeTag != null) { ResultHolder typeFromTypeTag = HaxeTypeResolver.getTypeFromTypeTag(typeTag, parameter); ResultHolder resolve = resolver.resolve(typeFromTypeTag); - if (!resolve.isUnknown()) return resolve; + if (resolve != null && !resolve.isUnknown()) typeFromTypeTag = resolve; + // if parameter is optional then its nullable and should be Null + if (parameter.getOptionalMark() != null && !typeFromTypeTag.isNullWrappedType()) { + return typeFromTypeTag.wrapInNullType(); + } return typeFromTypeTag; } @@ -507,6 +511,10 @@ static ResultHolder handleParameter( if (init != null) { ResultHolder holder = handle(init, context, resolver); if (!holder.isUnknown()) { + if (parameter.getOptionalMark() != null && !holder.isNullWrappedType()) { + // if parameter is optional then its nullable and should be Null + return holder.wrapInNullType(); + } return holder; } }else { diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeTypeResolver.java b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeTypeResolver.java index e699f413c..480cbdbdf 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeTypeResolver.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeTypeResolver.java @@ -155,6 +155,9 @@ static private ResultHolder getFieldType(HaxeNamedComponent comp, HaxeGenericRes if (resolver != null) { ResultHolder resolved = resolver.resolve(typeFromTag); if (resolved != null) typeFromTag = resolved; + if (psiField.isOptional() && !typeFromTag.isNullWrappedType()) { + typeFromTag = typeFromTag.wrapInNullType(); + } } final Object initConstant = result != null ? result.getType().getConstant() : null; if (typeFromTag != null) { diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/ResultHolder.java b/src/main/java/com/intellij/plugins/haxe/model/type/ResultHolder.java index 8d2b705dd..d08757196 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/ResultHolder.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/ResultHolder.java @@ -227,4 +227,14 @@ public ResultHolder tryUnwrapNullType() { } return this; } + + public boolean isNullWrappedType() { + SpecificHaxeClassReference classType = getClassType(); + return classType != null && classType.isNullType(); + } + + public ResultHolder wrapInNullType() { + SpecificTypeReference typeToWrap = getType(); + return SpecificHaxeClassReference.getNull(typeToWrap.getElementContext(), typeToWrap.createHolder()).createHolder(); + } } diff --git a/src/test/java/com/intellij/plugins/haxe/ide/inlay/HaxeLocalVarInlayTest.java b/src/test/java/com/intellij/plugins/haxe/ide/inlay/HaxeLocalVarInlayTest.java index 9c4e773c7..10229222d 100644 --- a/src/test/java/com/intellij/plugins/haxe/ide/inlay/HaxeLocalVarInlayTest.java +++ b/src/test/java/com/intellij/plugins/haxe/ide/inlay/HaxeLocalVarInlayTest.java @@ -33,5 +33,9 @@ public void testSimpleVarHints() throws Exception { public void testLocalVarMacros() throws Exception { doTest(hintsProvider); } + @Test + public void testOptionalFieldsHints() throws Exception { + doTest(hintsProvider); + } } diff --git a/src/test/resources/testData/inlay/haxe.local.variable/OptionalFieldsHints.hx b/src/test/resources/testData/inlay/haxe.local.variable/OptionalFieldsHints.hx new file mode 100644 index 000000000..342ed5e27 --- /dev/null +++ b/src/test/resources/testData/inlay/haxe.local.variable/OptionalFieldsHints.hx @@ -0,0 +1,35 @@ +package testData.inlay.haxe.local.variable; +typedef SomeTypeDef = { + var normalA:Int; + final normalB:String; + @:optional var optionalC:String; +} +typedef SomeStruct = { + ?optinal:Int, +} +class NullWrapping { + public function optionalStruct(s:SomeStruct) { + var x/*<# :Null #>*/ = s.optinal; + } + public function optionalTypeDef(s:SomeTypeDef) { + var a/*<# :Int #>*/ = s.normalA; + var b/*<# :String #>*/ = s.normalB; + var c/*<# :Null #>*/ = s.optionalC; + } + + public function optionalTypeA(?i:String) { + var x/*<# :Null #>*/ = i; + } + + public function optionalTypeB(?i = "Str") { + var x/*<# :Null #>*/ = i; + } + + public function optionalTypeParameter(?i:T) { + var x/*<# :Null #>*/ = i; + } + + public function defaultType(i = "String") { + var x/*<# :String #>*/ = i; + } +} \ No newline at end of file