Skip to content

Commit

Permalink
more work on objectLiteral assign check (+ bugfix for #1182)
Browse files Browse the repository at this point in the history
  • Loading branch information
m0rkeulv committed Aug 11, 2024
1 parent 0a4fa6f commit 6924f66
Show file tree
Hide file tree
Showing 17 changed files with 120 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,11 @@
import com.intellij.lang.annotation.AnnotationBuilder;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.util.text.Strings;
import com.intellij.plugins.haxe.HaxeBundle;
import com.intellij.plugins.haxe.model.type.HaxeAssignContext;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
* A library of annotations that can be re-used. Place annotations that are used more than
* once (or should be) in this class.
Expand All @@ -47,21 +43,17 @@ private HaxeStandardAnnotation() {

public static @NotNull AnnotationBuilder typeMismatchMissingMembers(@NotNull AnnotationHolder holder,
@NotNull PsiElement incompatibleElement,
List<String> missingFields) {
HaxeAssignContext context) {

String message = HaxeBundle.message("haxe.semantic.incompatible.type.missing.members.0",
Strings.join(missingFields, ","));
context.getMissingMembersString());
return holder.newAnnotation(HighlightSeverity.ERROR, message).range(incompatibleElement);
}
public static @NotNull AnnotationBuilder typeMismatchWrongTypeMembers(@NotNull AnnotationHolder holder,
@NotNull PsiElement incompatibleElement,
Map<String, String> wrongTypes) {

String wrongTypeText = wrongTypes.entrySet().stream()
.map(entry -> "have '" + entry.getKey() + "' wants '" + entry.getValue() + "'")
.collect(Collectors.joining(", "));
@NotNull PsiElement incompatibleElement,
HaxeAssignContext context) {

String message = HaxeBundle.message("haxe.semantic.incompatible.type.wrong.member.types.0", wrongTypeText);
String message = HaxeBundle.message("haxe.semantic.incompatible.type.wrong.member.types.0", context.geWrongTypeMembersString());

return holder.newAnnotation(HighlightSeverity.ERROR, message).range(incompatibleElement);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ public static void check(HaxeAssignExpression psi, AnnotationHolder holder) {

if(context.hasMissingMembers() || context.hasWrongTypeMembers()) {
if(context.hasMissingMembers()) {
HaxeStandardAnnotation.typeMismatchMissingMembers(holder, rhs, context.getMissingMembers())
HaxeStandardAnnotation.typeMismatchMissingMembers(holder, rhs, context)
.create();
}
if(context.hasWrongTypeMembers()) {
HaxeStandardAnnotation.typeMismatchWrongTypeMembers(holder, rhs, context.getWrongTypeMembers())
HaxeStandardAnnotation.typeMismatchWrongTypeMembers(holder, rhs, context)
.create();
}
}else {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.intellij.plugins.haxe.ide.annotator.semantics;

import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.Strings;
import com.intellij.plugins.haxe.HaxeBundle;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.lang.psi.impl.HaxeMethodDeclarationImpl;
Expand All @@ -10,11 +11,11 @@
import com.intellij.plugins.haxe.model.type.resolver.ResolverEntry;
import com.intellij.plugins.haxe.util.UsefulPsiTreeUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import lombok.Data;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.stream.Collectors;

import static com.intellij.plugins.haxe.ide.annotator.semantics.TypeParameterUtil.*;
import static com.intellij.plugins.haxe.model.type.HaxeTypeCompatible.canAssignToFrom;
Expand Down Expand Up @@ -159,7 +160,7 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi
else {
// out of parameters and last is not var arg, must mean that ve have skipped optionals and still had arguments left
if (parameterType != null && argumentType != null) {
validation.errors.add(annotateTypeMismatch(parameterType, argumentType, argument));
validation.errors.add(annotateTypeMismatch(parameterType, argumentType, argument, null));
}
break;
}
Expand Down Expand Up @@ -214,8 +215,9 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi

// check if argument matches Type Parameter constraint
if (optionalTypeParameterConstraint.isPresent()) {
HaxeAssignContext assignContext = new HaxeAssignContext(parameter.getBasePsi(), argument);
ResultHolder constraint = optionalTypeParameterConstraint.get();
if (canAssignToFrom(constraint, argumentType)) {
if (canAssignToFrom(constraint, argumentType, assignContext)) {
addToIndexMap(validation, argumentCounter, parameterCounter);
addArgumentTypeToIndex(validation, argumentCounter, argumentType);
addParameterTypeToIndex(validation, parameterCounter, parameterType);
Expand All @@ -231,7 +233,7 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi
}else if (argumentType.isClassType() && argumentType.isMissingClassModel()){
validation.warnings.add(annotateUnableToCompare( argumentType, argument));
}else {
validation.errors.add(annotateTypeMismatch(constraint, argumentType, argument));
validation.errors.add(annotateTypeMismatch(constraint, argumentType, argument, assignContext));
}
addToIndexMap(validation, argumentCounter, parameterCounter);
}
Expand All @@ -240,8 +242,8 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi
else {
ResultHolder resolvedParameterType = HaxeTypeResolver.resolveParameterizedType(parameterType, resolver.withoutUnknowns());


if (canAssignToFrom(resolvedParameterType, argumentType)) {
HaxeAssignContext assignContext = new HaxeAssignContext(parameter.getBasePsi(), argument);
if (canAssignToFrom(resolvedParameterType, argumentType, assignContext)) {
addToIndexMap(validation, argumentCounter, parameterCounter);
addArgumentTypeToIndex(validation, argumentCounter, argumentType);
addParameterTypeToIndex(validation, parameterCounter, parameterType);
Expand All @@ -256,7 +258,7 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi
}else if (argumentType.isClassType() && argumentType.isMissingClassModel()){
validation.warnings.add(annotateUnableToCompare( argumentType, argument));
}else {
validation.errors.add(annotateTypeMismatch(resolvedParameterType, argumentType, argument));
validation.errors.add(annotateTypeMismatch(resolvedParameterType, argumentType, argument, assignContext));
}
addToIndexMap(validation, argumentCounter, parameterCounter);
}
Expand Down Expand Up @@ -390,7 +392,7 @@ public static CallExpressionValidation checkFunctionCall(HaxeCallExpression call
else {
// out of parameters and last is not var arg, must mean that ve have skipped optionals and still had arguments left
if (parameterType != null && argumentType != null) {
validation.errors.add(annotateTypeMismatch(parameterType, argumentType, argument));
validation.errors.add(annotateTypeMismatch(parameterType, argumentType, argument, null));
}
break;
}
Expand All @@ -411,8 +413,8 @@ public static CallExpressionValidation checkFunctionCall(HaxeCallExpression call
// check if argument matches Type Parameter constraint
ResultHolder resolvedParameterType = HaxeTypeResolver.resolveParameterizedType(parameterType, resolver);


if (canAssignToFrom(resolvedParameterType, argumentType)) {
HaxeAssignContext assignContext = new HaxeAssignContext(parameter.getType().getElementContext(), argument);
if (canAssignToFrom(resolvedParameterType, argumentType, assignContext)) {
addToIndexMap(validation, argumentCounter, parameterCounter);
addArgumentTypeToIndex(validation, argumentCounter, argumentType);
addParameterTypeToIndex(validation, parameterCounter, parameterType);
Expand All @@ -421,7 +423,7 @@ public static CallExpressionValidation checkFunctionCall(HaxeCallExpression call
if (parameter.isOptional()) {
argumentCounter--; //retry argument with next parameter
} else {
validation.errors.add(annotateTypeMismatch(parameterType, argumentType, argument));
validation.errors.add(annotateTypeMismatch(parameterType, argumentType, argument, assignContext));
addToIndexMap(validation, argumentCounter, parameterCounter);
}
}
Expand Down Expand Up @@ -576,7 +578,8 @@ public static CallExpressionValidation checkConstructor(HaxeNewExpression newExp
// check if argument matches Type Parameter constraint
if (optionalTypeParameterConstraint.isPresent()) {
ResultHolder constraint = optionalTypeParameterConstraint.get();
if (canAssignToFrom(constraint, argumentType)) {
HaxeAssignContext assignContext = new HaxeAssignContext(parameter.getType().getElementContext(), argument);
if (canAssignToFrom(constraint, argumentType, assignContext)) {
addToIndexMap(validation, argumentCounter, parameterCounter);
addArgumentTypeToIndex(validation, argumentCounter, argumentType);
addParameterTypeToIndex(validation, parameterCounter, parameterType);
Expand All @@ -586,16 +589,16 @@ public static CallExpressionValidation checkConstructor(HaxeNewExpression newExp
argumentCounter--; //retry argument with next parameter
}
else {
validation.errors.add(annotateTypeMismatch(constraint, argumentType, argument));
validation.errors.add(annotateTypeMismatch(constraint, argumentType, argument, assignContext));
addToIndexMap(validation, argumentCounter, parameterCounter);
}
}
}
else {
ResultHolder resolvedParameterType = HaxeTypeResolver.resolveParameterizedType(parameterType, resolver.withoutUnknowns());


if (canAssignToFrom(resolvedParameterType, argumentType)) {
HaxeAssignContext assignContext = new HaxeAssignContext(parameter.getType().getElementContext(), argument);
if (canAssignToFrom(resolvedParameterType, argumentType, assignContext)) {
addToIndexMap(validation, argumentCounter, parameterCounter);
addArgumentTypeToIndex(validation, argumentCounter, argumentType);
addParameterTypeToIndex(validation, parameterCounter, parameterType);
Expand All @@ -605,7 +608,7 @@ public static CallExpressionValidation checkConstructor(HaxeNewExpression newExp
if (parameter.isOptional()) {
argumentCounter--; //retry argument with next parameter
}else {
validation.errors.add(annotateTypeMismatch(parameterType, argumentType, argument));
validation.errors.add(annotateTypeMismatch(parameterType, argumentType, argument, assignContext));
addToIndexMap(validation, argumentCounter, parameterCounter);
}
}
Expand Down Expand Up @@ -821,12 +824,27 @@ private static HaxeGenericResolver findTypeParametersToInherit(SpecificTypeRefer
}


private static ErrorRecord annotateTypeMismatch( ResultHolder expected, ResultHolder got, HaxeExpression expression) {
private static ErrorRecord annotateTypeMismatch(ResultHolder expected, ResultHolder got, HaxeExpression expression,
HaxeAssignContext context) {
if (context != null) {
if (context.hasMissingMembers()) {
String message = HaxeBundle.message("haxe.semantic.method.parameter.mismatch.missing.members",
context.getMissingMembersString());
return new ErrorRecord(expression.getTextRange(), message);

} else if (context.hasWrongTypeMembers()) {
String message = HaxeBundle.message("haxe.semantic.method.parameter.mismatch.wrong.type.members",
context.geWrongTypeMembersString());
return new ErrorRecord(expression.getTextRange(), message);
}
}

String message = HaxeBundle.message("haxe.semantic.method.parameter.mismatch",
expected.toPresentationString(),
got.toPresentationString());
return new ErrorRecord(expression.getTextRange(), message);
}

private static WarningRecord annotateUnableToCompare( ResultHolder problemType, HaxeExpression expression) {
String message = HaxeBundle.message("haxe.semantic.method.parameter.unable.to.compare", problemType.toPresentationString());
return new WarningRecord(expression.getTextRange(), message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public static Collection<HaxeClass> getItemsByQNameFirstLevelChildrenOnly(final
.allowParallelProcessing()
.filtering(element -> element instanceof HaxeClass)
.mapping( element -> (HaxeClass) element)
.filtering(aClass -> !aClass.isAnonymousType())// ignore anonymous as they do not inherit
.filtering(aClass -> !aClass.isObjectLiteralType())// ignore object literals as they do not inherit
.filtering(element ->
Arrays.stream(element.getSuperTypes()).map(PsiClassType::resolve).anyMatch(psiClass -> psiClass == haxeClass)
).findAll();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ public String getName() {

boolean isAbstractType();

boolean isObjectLiteralType();

boolean isExtern();

boolean isInterface();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1492,8 +1492,8 @@ private PsiElement resolveQualifiedReference(HaxeReference reference) {
List<HaxeModel> result = HaxeProjectModel.fromElement(reference).resolve(qualifiedInfo, reference.getResolveScope());
if (result != null && !result.isEmpty()) {
HaxeModel item = result.get(0);
if (item instanceof HaxeFileModel) {
HaxeClassModel mainClass = ((HaxeFileModel)item).getMainClassModel();
if (item instanceof HaxeFileModel fileModel) {
HaxeClassModel mainClass = fileModel.getMainClassModel();
if (mainClass != null) {
return mainClass.haxeClass.getComponentName();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ public boolean isExtern() {
public boolean isAbstractType() {
return (this instanceof HaxeAbstractTypeDeclaration);
}
public boolean isObjectLiteralType() {
return (this instanceof HaxeObjectLiteral);
}


@Override
public boolean isInterface() {
Expand All @@ -174,6 +178,9 @@ public boolean isAnonymousType() {
return false;
}




@NotNull
@Override
public List<HaxeType> getHaxeExtendsList() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* @author: Fedor.Korotkov
*/
public abstract class HaxeObjectLiteralTypeImpl extends AbstractHaxePsiClass {

public HaxeObjectLiteralTypeImpl(@NotNull HaxeObjectLiteral objectLiteral) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,7 @@ public boolean isPureClassReferenceOf(@NotNull String className) {
return !(parent instanceof HaxeType)
&& !(parent instanceof HaxeReference)
&& resolve instanceof HaxeClass haxeClass
&& getLastChild().textMatches(haxeClass.getName())
&& className.equalsIgnoreCase(haxeClass.getName());// identical classname and elementText
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.intellij.plugins.haxe.model.type;

import com.intellij.openapi.util.text.Strings;
import com.intellij.psi.PsiElement;
import lombok.Data;
import org.jetbrains.annotations.Nullable;
Expand All @@ -8,6 +9,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Data
public class HaxeAssignContext {
Expand Down Expand Up @@ -36,4 +38,13 @@ public boolean hasMissingMembers() {
public boolean hasWrongTypeMembers() {
return !wrongTypeMembers.isEmpty();
}

public String getMissingMembersString() {
return Strings.join(getMissingMembers(), ", ");
}
public String geWrongTypeMembersString() {
return getWrongTypeMembers().entrySet().stream()
.map(entry -> "have '" + entry.getKey() + "' wants '" + entry.getValue() + "'")
.collect(Collectors.joining(", "));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static ResultHolder handleWithRecursionGuard(PsiElement element,
HaxeGenericResolver resolver) {
RecursionManager.markStack();
if (element == null ) return null;
return evaluatorHandlersRecursionGuard.doPreventingRecursion(element, true, () -> handle(element, context, resolver));
return evaluatorHandlersRecursionGuard.doPreventingRecursion(element, false, () -> handle(element, context, resolver));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
public class HaxeTypeCompatible {

// disabling for now, too many mismatches (seems to struggle with resolving correct enum types)
static boolean checkAnonymousMemberTypes = false;
static boolean checkAnonymousMemberTypes = true;

static public boolean canAssignToFrom(@Nullable SpecificTypeReference to, @Nullable ResultHolder from) {
if (null == to || null == from) return false;
Expand Down Expand Up @@ -581,9 +581,15 @@ private static boolean containsAllMembers(SpecificHaxeClassReference to, Specifi
HaxeBaseMemberModel fromMember = first.get();
if (context != null && context.getFromOrigin() != null) {
Boolean canAssign = containsMembersRecursionGuard.computePreventingRecursion(context.getFromOrigin(), false, () -> {
ResultHolder fromType = fromMember.getResultType();
ResultHolder toType = toMember.getResultType();
return fromType.canAssign(toType);
ResultHolder fromType = fromMember.getResultType();

//TODO, temp hack ignoring type of enum until better resolve logic is added
SpecificTypeReference type = fromType.getType();
boolean fromIsEnum = type.isEnumValue() || type.isEnumType() || type.isEnumClass() || type.isEnumValueClass();
if (toType.isEnum() && fromIsEnum) return true;

return toType.canAssign(fromType);
});

// in case recursion guard is triggered
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,15 +355,6 @@ public boolean isAnonymousType() {
return false;
}

final public boolean isPureClassReference() {
if (context instanceof HaxeReferenceImpl reference && this instanceof SpecificHaxeClassReference classReference) {
@NotNull ResultHolder[] specifics = classReference.getSpecifics();
if (specifics.length != 1) return false;
return reference.isPureClassReferenceOf(specifics[0].getClassType().getClassName());
}
return false;
}

final public boolean isEnumValue() {
return (this instanceof SpecificEnumValueReference)
|| (this.getConstant() instanceof HaxeEnumValueDeclaration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1238,7 +1238,12 @@ private static HaxeClass tryResolveClassByQNameWhenGetQNameFail(@NotNull PsiElem
result = findClassByQName(className, type.getContext());
}

result = result != null ? result : findClassByQName(className, type.getContext());
if (result == null && className != null) {
//make sure we wont try to files with invalid characters
if (className.matches("[^:<>{}()/]+")) {
result = findClassByQName(className, type.getContext());
}
}

return result instanceof HaxeClass haxeClass ? haxeClass : null;
}
Expand Down
Loading

0 comments on commit 6924f66

Please sign in to comment.