From d340f01ebb5d41119632aef49cbcf09b35c31ee9 Mon Sep 17 00:00:00 2001 From: m0rkeulv Date: Tue, 19 Mar 2024 21:07:34 +0100 Subject: [PATCH 1/2] Attempt att better typeParameter resolve for cunstructors (+ fix for #1159) --- .../semantics/HaxeCallExpressionUtil.java | 12 +++- .../model/type/HaxeExpressionEvaluator.java | 58 +++++++------------ .../haxe/model/type/HaxeGenericResolver.java | 7 +++ 3 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeCallExpressionUtil.java b/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeCallExpressionUtil.java index f16a6baa2..c8eb6084c 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeCallExpressionUtil.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/annotator/semantics/HaxeCallExpressionUtil.java @@ -74,7 +74,11 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi //max arg check if (argumentList.size() > maxArgAllowed) { String message = HaxeBundle.message("haxe.semantic.method.parameter.too.many", maxArgAllowed, argumentList.size()); - validation.errors.add(new ErrorRecord(callExpressionList.getTextRange(), message)); + if (callExpressionList != null) { + validation.errors.add(new ErrorRecord(callExpressionList.getTextRange(), message)); + }else { + validation.errors.add(new ErrorRecord(callExpression.getTextRange(), message)); + } return validation; } @@ -245,6 +249,7 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi } } validation.completed = true; + validation.resolver = resolver; return validation; } @@ -555,6 +560,7 @@ public static CallExpressionValidation checkConstructor(HaxeNewExpression newExp } } validation.completed = true; + validation.resolver = resolver; return validation; } @@ -723,6 +729,7 @@ public static CallExpressionValidation checkEnumConstructor(HaxeCallExpression e } } validation.completed = true; + validation.resolver = resolver; return validation; } @@ -957,6 +964,9 @@ public static class CallExpressionValidation { List errors = new ArrayList<>(); List warnings = new ArrayList<>(); + + HaxeGenericResolver resolver = null; + boolean completed = false; boolean memberMacroFunction = false; boolean isStaticExtension = false; diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluator.java b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluator.java index d44a1a1fc..a6344b8df 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluator.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeExpressionEvaluator.java @@ -362,52 +362,34 @@ static private ResultHolder _handle(final PsiElement element, if (isMacroVariable(type.getReferenceExpression().getIdentifier())){ return SpecificTypeReference.getDynamic(element).createHolder(); } + ResultHolder hint = resolver.getAssignHint(); ResultHolder typeHolder = HaxeTypeResolver.getTypeFromType(type, resolver); + if (hint != null && hint.isClassType()) { + HaxeGenericResolver localResolver = new HaxeGenericResolver(); + HaxeGenericResolver hintsResolver = hint.getClassType().getGenericResolver(); + localResolver.addAll(hintsResolver); + ResultHolder resolvedWithHint = localResolver.resolve(typeHolder); + if (resolvedWithHint != null && !resolvedWithHint.isUnknown()) typeHolder = resolvedWithHint; + } + if (!typeHolder.isUnknown() && typeHolder.getClassType() != null) { SpecificHaxeClassReference classReference = typeHolder.getClassType(); HaxeClassModel classModel = classReference.getHaxeClassModel(); HaxeGenericResolver classResolver = classReference.getGenericResolver(); if (classModel != null) { - HaxeMethodModel constructor = classModel.getConstructor(classResolver); - if (constructor != null) { - @NotNull String[] specificNames = classResolver.names(); - HaxeMethod method = constructor.getMethod(); - HaxeMethodModel methodModel = method.getModel(); - if (methodModel.getGenericParams().isEmpty()) { - boolean changedTypeParameters = false; - HaxeCallExpressionUtil.CallExpressionValidation validation = HaxeCallExpressionUtil.checkConstructor(expression); - Map toParameterIndex = validation.getArgumentToParameterIndex(); - - - List arguments = expression.getExpressionList(); - List parameters = methodModel.getParameters(); - - if (!parameters.isEmpty()) { - - for (Map.Entry entry : toParameterIndex.entrySet()) { - Integer argumentIndex = entry.getKey(); - Integer parameterIndex = entry.getValue(); - - HaxeParameterModel parameter = parameters.get(parameterIndex); - if (parameter.getType().isTypeParameter()) { - for (int i = 0; i < Math.min(specificNames.length, arguments.size()); i++) { - if (specificNames[i].equals(parameter.getTypeTagPsi().getTypeOrAnonymous().getText())) { - // we could try to map parameters and args, but in most cases this probably won't be necessary and it would make this part very complex - @NotNull ResultHolder[] specifics = classReference.getSpecifics(); - if (specifics[i].isUnknown()) { - ResultHolder handle = handle(arguments.get(argumentIndex), context, resolver); - if (!handle.isUnknown()) { - changedTypeParameters = true; - specifics[i] = handle; - } - } - } - } - } + HaxeMethodModel constructor = classModel.getConstructor(classResolver); + if (constructor != null) { + HaxeMethod method = constructor.getMethod(); + HaxeMethodModel methodModel = method.getModel(); + if (methodModel.getGenericParams().isEmpty()) { + HaxeCallExpressionUtil.CallExpressionValidation validation = HaxeCallExpressionUtil.checkConstructor(expression); + HaxeGenericResolver resolverFromCallExpression = validation.getResolver(); + + if (resolverFromCallExpression != null) { + ResultHolder resolve = resolverFromCallExpression.resolve(typeHolder); + if (!resolve.isUnknown()) typeHolder = resolve; } } - if (changedTypeParameters) return typeHolder.duplicate(); - } } } } diff --git a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeGenericResolver.java b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeGenericResolver.java index 14acaf2a9..a9b5b0f90 100644 --- a/src/main/java/com/intellij/plugins/haxe/model/type/HaxeGenericResolver.java +++ b/src/main/java/com/intellij/plugins/haxe/model/type/HaxeGenericResolver.java @@ -250,6 +250,13 @@ private Optional findAssignToType() { .filter(entry -> entry.resolveSource() == ResolveSource.ASSIGN_TYPE) .findFirst(); } + public ResultHolder getAssignHint() { + return resolvers.stream() + .filter(entry -> entry.resolveSource() == ResolveSource.ASSIGN_TYPE) + .findFirst() + .map(ResolverEntry::type) + .orElse(null); + } @Nullable public ResultHolder resolveReturnType(ResultHolder resultHolder) { From d90b604073cdc8259546ae3df65a24d6b8313d2e Mon Sep 17 00:00:00 2001 From: m0rkeulv Date: Tue, 19 Mar 2024 23:34:52 +0100 Subject: [PATCH 2/2] removing all state from Intent implementations in an attempt to solve unexpected nullPointerExceptions (#1156) --- .../intention/AddReturnTypeTagIntention.java | 48 +++++++++++-------- .../intention/AddTypeTagToFieldIntention.java | 31 +++++++----- .../ConvertPropertyToVariableIntention.java | 20 ++++---- ...onvertVariableToPropertyIntentionBase.java | 43 ++++++++++------- .../intention/IteratorForLoopIntention.java | 38 +++++++-------- .../KeyValueIteratorForLoopIntention.java | 43 +++++++++-------- .../RemoveReturnTypeTagIntention.java | 20 ++++---- .../RemoveTypeTagFromFieldIntention.java | 15 +++--- 8 files changed, 140 insertions(+), 118 deletions(-) diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/AddReturnTypeTagIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/AddReturnTypeTagIntention.java index 015dd3fa6..f858ee8b7 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/AddReturnTypeTagIntention.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/AddReturnTypeTagIntention.java @@ -17,14 +17,13 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static com.intellij.plugins.haxe.util.HaxeElementGenerator.createTypeTag; import static com.intellij.plugins.haxe.util.UsefulPsiTreeUtil.findParentOfTypeButStopIfTypeIs; public class AddReturnTypeTagIntention extends BaseIntentionAction { - private HaxeMethod myMethod; - private ResultHolder returnType; public AddReturnTypeTagIntention() { } @@ -46,19 +45,15 @@ public String getText() { public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; - attemptToFindMethod(editor, file); + HaxeMethod method = attemptToFindMethod(editor, file); - boolean isMissingTypeTag = myMethod != null - && myMethod.getModel() != null - && !myMethod.isConstructor() - && myMethod instanceof HaxeMethodDeclaration declaration + boolean isMissingTypeTag = method != null + && method.getModel() != null + && !method.isConstructor() + && method instanceof HaxeMethodDeclaration declaration && declaration.getTypeTag() == null; if (isMissingTypeTag) { - HaxeMethodModel model = myMethod.getModel(); - HaxeGenericResolver resolver = model.getGenericResolver(null); - resolver = resolver.withTypeParametersAsType(model.getGenericParams()); - returnType = model.getReturnType(resolver); - return !(returnType == null); + return !( getReturnType(method) == null); } return false; } @@ -66,23 +61,34 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + HaxeMethod method = attemptToFindMethod(editor, file); + if (method != null) { + ResultHolder returnType = getReturnType(method); + + String typeText = returnType.isUnknown() ? SpecificTypeReference.VOID : returnType.getType().toPresentationString(); + HaxeTypeTag tag = createTypeTag(project, typeText); + PsiParameterList list = method.getParameterList(); + PsiElement element = PsiTreeUtil.nextVisibleLeaf(list); + method.addAfter(tag, element); + } + } - String typeText = returnType.isUnknown() ? SpecificTypeReference.VOID : returnType.getType().toPresentationString(); - HaxeTypeTag tag = createTypeTag(project, typeText); - PsiParameterList list = myMethod.getParameterList(); - PsiElement element = PsiTreeUtil.nextVisibleLeaf(list); - myMethod.addAfter(tag, element); - + private static ResultHolder getReturnType(HaxeMethod method) { + HaxeMethodModel model = method.getModel(); + HaxeGenericResolver resolver = model.getGenericResolver(null); + resolver = resolver.withTypeParametersAsType(model.getGenericParams()); + return model.getReturnType(resolver); } - private void attemptToFindMethod(Editor editor, PsiFile file) { + private @Nullable HaxeMethod attemptToFindMethod(Editor editor, PsiFile file) { PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); if (place instanceof HaxeMethod method) { - myMethod = method; + return method; }else if (place != null){ - myMethod = findParentOfTypeButStopIfTypeIs(place, HaxeMethod.class, HaxeBlockStatement.class); + return findParentOfTypeButStopIfTypeIs(place, HaxeMethod.class, HaxeBlockStatement.class); } + return null; } diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/AddTypeTagToFieldIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/AddTypeTagToFieldIntention.java index 686143242..e7f8ae2d1 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/AddTypeTagToFieldIntention.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/AddTypeTagToFieldIntention.java @@ -25,9 +25,6 @@ public class AddTypeTagToFieldIntention extends BaseIntentionAction { - private HaxePsiField myField; - private ResultHolder type; - public AddTypeTagToFieldIntention() { } @@ -48,12 +45,12 @@ public String getText() { public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; - attemptToFindField(editor, file); - HaxePsiField field = myField; + + HaxePsiField field = attemptToFindField(editor, file);; boolean isMissingTypeTag = field != null && field.getTypeTag() == null; if (isMissingTypeTag) { - type = HaxeExpressionEvaluator.evaluate(field, new HaxeExpressionEvaluatorContext(field), null).result; + ResultHolder type = HaxeExpressionEvaluator.evaluate(field, new HaxeExpressionEvaluatorContext(field), null).result; return !(type == null || type.isUnknown()); } return false; @@ -63,23 +60,33 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - HaxeTypeTag tag = createTypeTag(project, type.getType().toString()); - myField.addAfter(tag, myField.getComponentName()); + HaxePsiField field = attemptToFindField(editor, file);; + boolean isMissingTypeTag = field != null && field.getTypeTag() == null; + if (isMissingTypeTag) { + ResultHolder type = HaxeExpressionEvaluator.evaluate(field, new HaxeExpressionEvaluatorContext(field), null).result; + if (!(type == null || type.isUnknown())) { + HaxeTypeTag tag = createTypeTag(project, type.getType().toString()); + field.addAfter(tag, field.getComponentName()); + } + } } - private void attemptToFindField(Editor editor, PsiFile file) { + private HaxePsiField attemptToFindField(Editor editor, PsiFile file) { PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); HaxeLocalVarDeclarationList varDeclarationList = PsiTreeUtil.getParentOfType(place, HaxeLocalVarDeclarationList.class); + + HaxePsiField field = null; if (varDeclarationList != null) { List list = varDeclarationList.getLocalVarDeclarationList(); - if (!list.isEmpty())myField = list.get(list.size() - 1); + if (!list.isEmpty())field = list.get(list.size() - 1); } else if (place instanceof HaxePsiField psiField) { - myField = psiField; + field = psiField; }else { - myField = PsiTreeUtil.getParentOfType(place, HaxePsiField.class); + field = PsiTreeUtil.getParentOfType(place, HaxePsiField.class); } + return field; } diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/ConvertPropertyToVariableIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/ConvertPropertyToVariableIntention.java index 29bbfd907..26eb9a46b 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/ConvertPropertyToVariableIntention.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/ConvertPropertyToVariableIntention.java @@ -16,7 +16,6 @@ public class ConvertPropertyToVariableIntention extends BaseIntentionAction { - private HaxeFieldDeclaration myField; @Nls @NotNull @@ -35,11 +34,11 @@ public String getText() { @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; - attemptToFindField(editor, file); + HaxeFieldDeclaration field = attemptToFindField(editor, file); - if (myField == null) return false; - if (myField.getTypeTag() == null) return false; - if (myField.getPropertyDeclaration() == null) return false; + if (field == null) return false; + if (field.getTypeTag() == null) return false; + if (field.getPropertyDeclaration() == null) return false; return true; } @@ -47,19 +46,22 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - myField.getPropertyDeclaration().delete(); + HaxeFieldDeclaration field = attemptToFindField(editor, file); + if (field != null && field.getPropertyDeclaration() != null) { + field.getPropertyDeclaration().delete(); + } } - private void attemptToFindField(Editor editor, PsiFile file) { + private HaxeFieldDeclaration attemptToFindField(Editor editor, PsiFile file) { PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); if (place instanceof HaxeFieldDeclaration psiField) { - myField = psiField; + return psiField; } else { - myField = PsiTreeUtil.getParentOfType(place, HaxeFieldDeclaration.class); + return PsiTreeUtil.getParentOfType(place, HaxeFieldDeclaration.class); } } } \ No newline at end of file diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/ConvertVariableToPropertyIntentionBase.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/ConvertVariableToPropertyIntentionBase.java index b1add63c4..daf20e02d 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/ConvertVariableToPropertyIntentionBase.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/ConvertVariableToPropertyIntentionBase.java @@ -17,19 +17,16 @@ public abstract class ConvertVariableToPropertyIntentionBase extends BaseIntentionAction { - private HaxePsiField myField; - - @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; - attemptToFindField(editor, file); + HaxePsiField field = attemptToFindFieldDeclaration(editor, file); - if (myField == null) return false; + if (field == null) return false; - if (myField.getTypeTag() == null) return false; - if (myField.getVarInit() != null) return false; - if (myField.getModel() instanceof HaxeFieldModel model) { + if (field.getTypeTag() == null) return false; + if (field.getVarInit() != null) return false; + if (field.getModel() instanceof HaxeFieldModel model) { if (model.hasModifier(HaxePsiModifier.INLINE)) return false; if (model.isProperty()) return false; }else { @@ -42,15 +39,18 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - HaxePsiField element = getTempProperty(project); - PsiElement copy = HaxeElementGenerator.createVarDeclaration(project, element.getText()).copy(); - myField.replace(copy); + HaxePsiField field = attemptToFindFieldDeclaration(editor, file); + if (field!= null) { + HaxePsiField element = getTempProperty(project, field); + PsiElement copy = HaxeElementGenerator.createVarDeclaration(project, element.getText()).copy(); + field.replace(copy); + } } @NotNull - private HaxePsiField getTempProperty(@NotNull Project project) { - HaxePsiField element = (HaxePsiField)myField.copy(); + private HaxePsiField getTempProperty(@NotNull Project project, HaxePsiField field) { + HaxePsiField element = (HaxePsiField)field.copy(); HaxePropertyDeclaration declaration = generateTmpDeclaration(project); HaxeComponentName name = element.getComponentName(); HaxeIdentifier identifier = name.getIdentifier(); @@ -68,18 +68,25 @@ private HaxePropertyDeclaration generateTmpDeclaration(@NotNull Project project) protected abstract String getPropertyElementString(); - private void attemptToFindField(Editor editor, PsiFile file) { + private HaxeFieldDeclaration attemptToFindFieldDeclaration(Editor editor, PsiFile file) { PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); HaxeLocalVarDeclarationList varDeclarationList = PsiTreeUtil.getParentOfType(place, HaxeLocalVarDeclarationList.class); + + HaxeFieldDeclaration declaration = null; if (varDeclarationList != null) { List list = varDeclarationList.getLocalVarDeclarationList(); - if (!list.isEmpty()) myField = list.get(list.size() - 1); + if (!list.isEmpty()){ + if(list.get(list.size() - 1) instanceof HaxeFieldDeclaration fieldDeclaration){ + declaration = fieldDeclaration; + } + } } - else if (place instanceof HaxePsiField psiField) { - myField = psiField; + else if (place instanceof HaxeFieldDeclaration fieldDeclaration) { + declaration = fieldDeclaration; } else { - myField = PsiTreeUtil.getParentOfType(place, HaxePsiField.class); + declaration = PsiTreeUtil.getParentOfType(place, HaxeFieldDeclaration.class); } + return declaration; } } \ No newline at end of file diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/IteratorForLoopIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/IteratorForLoopIntention.java index 2ab9e95a4..cf36dc38e 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/IteratorForLoopIntention.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/IteratorForLoopIntention.java @@ -2,7 +2,6 @@ import com.intellij.codeInsight.CodeInsightUtilCore; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; -import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; @@ -13,8 +12,6 @@ import com.intellij.plugins.haxe.lang.psi.HaxeForStatement; import com.intellij.plugins.haxe.lang.psi.HaxePsiCompositeElement; import com.intellij.plugins.haxe.lang.psi.HaxeReference; -import com.intellij.plugins.haxe.model.HaxeMemberModel; -import com.intellij.plugins.haxe.model.HaxeMethodModel; import com.intellij.plugins.haxe.model.type.HaxeExpressionEvaluator; import com.intellij.plugins.haxe.model.type.ResultHolder; import com.intellij.plugins.haxe.model.type.SpecificHaxeClassReference; @@ -24,19 +21,15 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.PsiWhiteSpace; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.refactoring.introduce.inplace.InplaceVariableIntroducer; import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import java.util.LinkedHashSet; import java.util.List; -import java.util.Optional; public class IteratorForLoopIntention extends BaseIntentionAction { - private SpecificHaxeClassReference resolvedType; - private HaxeReference haxeReference; public IteratorForLoopIntention() { } @@ -57,17 +50,19 @@ public String getText() { @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; - - return attemptToFindIterableExpression(editor, file); + HaxeReference reference = attemptToFindIterableExpression(editor, file); + return hasIterator(reference); } @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - HaxeForStatement itr = HaxeElementGenerator.createForInLoop(project, "itr", haxeReference.getText()); - itr = (HaxeForStatement)haxeReference.replace(itr.copy()); + HaxeReference reference = attemptToFindIterableExpression(editor, file); + if (reference != null) { + HaxeForStatement itr = HaxeElementGenerator.createForInLoop(project, "itr", reference.getText()); + itr = (HaxeForStatement)reference.replace(itr.copy()); - if (!editor.isViewer()) { + if (!editor.isViewer()) { CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(itr); HaxeComponentName name = itr.getComponentName(); final var introducer = new HaxeIntroduceHandler.HaxeInplaceVariableIntroducer(name, editor, List.of()); @@ -77,24 +72,25 @@ public void invoke(@NotNull final Project project, Editor editor, PsiFile file) editor.getCaretModel().moveToOffset(range.getEndOffset()); introducer.performInplaceRefactoring(new LinkedHashSet<>(List.of())); } + } } - private boolean attemptToFindIterableExpression(Editor editor, PsiFile file) { + private HaxeReference attemptToFindIterableExpression(Editor editor, PsiFile file) { PsiElement psiElement = file.findElementAt(editor.getCaretModel().getOffset()); if (psiElement instanceof PsiWhiteSpace) { - psiElement = - UsefulPsiTreeUtil.getPrevSiblingSkippingCondition(psiElement, element -> !(element instanceof HaxePsiCompositeElement), true); + psiElement = UsefulPsiTreeUtil.getPrevSiblingSkippingCondition(psiElement, element -> !(element instanceof HaxePsiCompositeElement), true); } - if (psiElement == null) return false; + if (psiElement == null) return null; - haxeReference = - psiElement instanceof HaxeReference reference ? reference : PsiTreeUtil.getParentOfType(psiElement, HaxeReference.class); + return psiElement instanceof HaxeReference reference ? reference : PsiTreeUtil.getParentOfType(psiElement, HaxeReference.class); + } - if (haxeReference == null) return false; + private boolean hasIterator(HaxeReference reference) { + if (reference == null) return false; - ResultHolder holder = HaxeExpressionEvaluator.evaluate(haxeReference, null).result; - resolvedType = holder.getClassType(); + ResultHolder holder = HaxeExpressionEvaluator.evaluate(reference, null).result; + SpecificHaxeClassReference resolvedType = holder.getClassType(); if (resolvedType == null) return false; return resolvedType.isLiteralArray() || hasIterator(resolvedType); diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/KeyValueIteratorForLoopIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/KeyValueIteratorForLoopIntention.java index d94bb3ac5..1bb274c3b 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/KeyValueIteratorForLoopIntention.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/KeyValueIteratorForLoopIntention.java @@ -28,8 +28,6 @@ public class KeyValueIteratorForLoopIntention extends BaseIntentionAction { - private SpecificHaxeClassReference resolvedType; - private HaxeReference haxeReference; public KeyValueIteratorForLoopIntention() { @@ -52,20 +50,24 @@ public String getText() { public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; - return attemptToFindIterableExpression(editor, file); + HaxeReference reference = attemptToFindIterableExpression(editor, file); + return hasIterator(reference); } @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - HaxeForStatement forLoop = HaxeElementGenerator.createForInLoop(project, "key", "value", haxeReference.getText()); - forLoop = (HaxeForStatement)haxeReference.replace(forLoop.copy()); + HaxeReference reference = attemptToFindIterableExpression(editor, file); + if (reference != null) { + HaxeForStatement forLoop = HaxeElementGenerator.createForInLoop(project, "key", "value", reference.getText()); + forLoop = (HaxeForStatement)reference.replace(forLoop.copy()); - if (!editor.isViewer()) { - CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(forLoop); - HaxeKeyValueIterator keyValueIterator = forLoop.getKeyValueIterator(); + if (!editor.isViewer()) { + CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(forLoop); + HaxeKeyValueIterator keyValueIterator = forLoop.getKeyValueIterator(); - introduceKeyValueIterators(editor, keyValueIterator); + introduceKeyValueIterators(editor, keyValueIterator); + } } } @@ -87,28 +89,27 @@ private static void introduceKeyValueIterators(Editor editor, HaxeKeyValueIterat introducer.performInplaceRefactoring(new LinkedHashSet<>(List.of())); } - - private boolean attemptToFindIterableExpression(Editor editor, PsiFile file) { + private HaxeReference attemptToFindIterableExpression(Editor editor, PsiFile file) { PsiElement psiElement = file.findElementAt(editor.getCaretModel().getOffset()); if (psiElement instanceof PsiWhiteSpace) { - psiElement = - UsefulPsiTreeUtil.getPrevSiblingSkippingCondition(psiElement, element -> !(element instanceof HaxePsiCompositeElement), true); + psiElement = UsefulPsiTreeUtil.getPrevSiblingSkippingCondition(psiElement, element -> !(element instanceof HaxePsiCompositeElement), true); } - if (psiElement == null) return false; + if (psiElement == null) return null; - haxeReference = - psiElement instanceof HaxeReference reference ? reference - : PsiTreeUtil.getParentOfType(psiElement, HaxeReference.class); + return psiElement instanceof HaxeReference reference ? reference : PsiTreeUtil.getParentOfType(psiElement, HaxeReference.class); + } - if (haxeReference == null) return false; + private boolean hasIterator(HaxeReference reference) { + if (reference == null) return false; - ResultHolder holder = HaxeExpressionEvaluator.evaluate(haxeReference, null).result; - resolvedType = holder.getClassType(); + ResultHolder holder = HaxeExpressionEvaluator.evaluate(reference, null).result; + SpecificHaxeClassReference resolvedType = holder.getClassType(); if (resolvedType == null) return false; - return resolvedType.isLiteralMap() || hasKeyValueIterator(resolvedType); + return resolvedType.isLiteralArray() || hasKeyValueIterator(resolvedType); } + private boolean hasKeyValueIterator(SpecificHaxeClassReference type) { if (type.getHaxeClassModel() == null) return false; return type.getHaxeClassModel().getMember("keyValueIterator", null) != null; diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveReturnTypeTagIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveReturnTypeTagIntention.java index 2e02aba3f..3245f99c9 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveReturnTypeTagIntention.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveReturnTypeTagIntention.java @@ -14,12 +14,12 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import static com.intellij.plugins.haxe.util.UsefulPsiTreeUtil.findParentOfTypeButStopIfTypeIs; public class RemoveReturnTypeTagIntention extends BaseIntentionAction { - private HaxeMethod myMethod; public RemoveReturnTypeTagIntention() { } @@ -41,7 +41,7 @@ public String getText() { public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; - attemptToFindMethod(editor, file); + HaxeMethod myMethod = attemptToFindMethod(editor, file); return myMethod instanceof HaxeMethodDeclaration declaration && declaration.getTypeTag() != null; } @@ -49,21 +49,23 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - - HaxeMethodDeclaration declaration = (HaxeMethodDeclaration)myMethod; - HaxeTypeTag tag = declaration.getTypeTag(); - if (tag != null) tag.delete(); + HaxeMethod myMethod = attemptToFindMethod(editor, file); + if (myMethod instanceof HaxeMethodDeclaration declaration) { + HaxeTypeTag tag = declaration.getTypeTag(); + if (tag != null) tag.delete(); + } } - private void attemptToFindMethod(Editor editor, PsiFile file) { + private @Nullable HaxeMethod attemptToFindMethod(Editor editor, PsiFile file) { PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); if (place instanceof HaxeMethod method) { - myMethod = method; + return method; } else if (place != null) { - myMethod = findParentOfTypeButStopIfTypeIs(place, HaxeMethod.class, HaxeBlockStatement.class); + return findParentOfTypeButStopIfTypeIs(place, HaxeMethod.class, HaxeBlockStatement.class); } + return null; } } \ No newline at end of file diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveTypeTagFromFieldIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveTypeTagFromFieldIntention.java index ec272c198..3bc60cf29 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveTypeTagFromFieldIntention.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveTypeTagFromFieldIntention.java @@ -19,8 +19,6 @@ public class RemoveTypeTagFromFieldIntention extends BaseIntentionAction { - private HaxePsiField myField; - @Nls @NotNull @@ -37,22 +35,24 @@ public String getText() { @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; - - attemptToFindField(editor, file); - + HaxePsiField myField = attemptToFindField(editor, file); return myField != null && myField.getTypeTag() != null; } @Override public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - myField.getTypeTag().delete(); + HaxePsiField myField = attemptToFindField(editor, file); + if (myField != null && myField.getTypeTag() != null) { + myField.getTypeTag().delete(); + } } - private void attemptToFindField(Editor editor, PsiFile file) { + private HaxePsiField attemptToFindField(Editor editor, PsiFile file) { PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); HaxeLocalVarDeclarationList varDeclarationList = PsiTreeUtil.getParentOfType(place, HaxeLocalVarDeclarationList.class); + HaxePsiField myField = null; if (varDeclarationList != null) { List list = varDeclarationList.getLocalVarDeclarationList(); if (!list.isEmpty())myField = list.get(list.size() - 1); @@ -61,6 +61,7 @@ private void attemptToFindField(Editor editor, PsiFile file) { }else { myField = PsiTreeUtil.getParentOfType(place, HaxePsiField.class); } + return myField; }