Skip to content

Commit

Permalink
resolve members from typeParameter constrains if present
Browse files Browse the repository at this point in the history
  • Loading branch information
m0rkeulv committed Sep 10, 2024
1 parent 1caebad commit e18a65c
Show file tree
Hide file tree
Showing 16 changed files with 206 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpressi
// map type Parameters to method declaring class resolver if necessary
if (methodClass != null && methodClass.haxeClass != null) {
HaxeClass callieClass = callieType.getClassType().getHaxeClass();
classTypeResolver = HaxeGenericResolverUtil.createInheritedClassResolver(methodClass.haxeClass, callieClass, classTypeResolver);
if(callieClass != null) {
classTypeResolver = HaxeGenericResolverUtil.createInheritedClassResolver(methodClass.haxeClass, callieClass, classTypeResolver);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public String getName() {
@NonNls
String getQualifiedName();

@NotNull
HaxeClassModel getModel();

@NotNull
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/intellij/plugins/haxe/lang/psi/HaxeResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.intellij.openapi.util.RecursionManager;
import com.intellij.plugins.haxe.ide.annotator.semantics.HaxeCallExpressionUtil;
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes;
import com.intellij.plugins.haxe.lang.psi.impl.HaxeClassWrapperForTypeParameter;
import com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceExpressionImpl;
import com.intellij.plugins.haxe.metadata.psi.HaxeMeta;
import com.intellij.plugins.haxe.metadata.psi.HaxeMetadataCompileTimeMeta;
Expand Down Expand Up @@ -1481,6 +1482,8 @@ private List<? extends PsiElement> resolveChain(HaxeReference lefthandExpression
// this is important as recursion guards might prevent us from getting the type and returning a different result depending on
// whether or not we got the type is bad and causes issues.
if (haxeClass != null && !result.isUnknown()) {
// if type parameter, try to find constraints and use those ?
haxeClass = useConstraintsIfTypeParameter(reference, haxeClass);
HaxeClassModel classModel = haxeClass.getModel();
HaxeBaseMemberModel member = classModel.getMember(identifier, classType.getGenericResolver());
if (member != null) {
Expand Down Expand Up @@ -1567,6 +1570,21 @@ private List<? extends PsiElement> resolveChain(HaxeReference lefthandExpression

}

private static HaxeClass useConstraintsIfTypeParameter(HaxeReference reference, HaxeClass haxeClass) {
if(haxeClass instanceof HaxeClassWrapperForTypeParameter typeParameter) {
HaxeGenericResolver referenceResolver = HaxeGenericResolverUtil.generateResolverFromScopeParents(reference);
ResultHolder resolved = referenceResolver.resolve(typeParameter);
if (resolved != null && resolved.isClassType()) {
SpecificHaxeClassReference classReference = resolved.getClassType();
if (classReference != null) {
HaxeClass newClassValue = classReference.getHaxeClass();
if(newClassValue != null) haxeClass = newClassValue;
}
}
}
return haxeClass;
}

private PsiElement resolveQualifiedReference(HaxeReference reference) {
String qualifiedName = reference.getText();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ public String getQualifiedName() {

private HaxeClassModel _model = null;

@NotNull
public HaxeClassModel getModel() {
if (_model == null) {
if (this instanceof HaxeTypeParameterMultiType multiType) {
Expand Down Expand Up @@ -261,7 +262,7 @@ public HaxeNamedComponent findHaxeFieldByName(@NotNull final String name, @Nulla
}

private static CachedValueProvider.Result<List<HaxeNamedComponent>> getHaxeFieldAllCached(@NotNull AbstractHaxePsiClass haxePsiClass) {
List<HaxeNamedComponent> all = haxePsiClass.getHaxeFieldAll(HaxeComponentType.CLASS, HaxeComponentType.ENUM, HaxeComponentType.ABSTRACT);
List<HaxeNamedComponent> all = haxePsiClass.getHaxeFieldAll(HaxeComponentType.CLASS, HaxeComponentType.ENUM, HaxeComponentType.ABSTRACT, HaxeComponentType.TYPEDEF);

List<PsiElement> dependencies = collectCacheDependencies(haxePsiClass);
return CachedValueProvider.Result.create(all, dependencies);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@
import com.intellij.openapi.project.Project;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.model.HaxeAnonymousTypeModel;
import com.intellij.plugins.haxe.model.type.ResultHolder;
import com.intellij.plugins.haxe.model.type.SpecificHaxeClassReference;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.impl.PsiClassImplUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
* @author: Fedor.Korotkov
Expand All @@ -44,6 +49,18 @@ public List<HaxeType> getHaxeExtendsList() {
return model.getExtensionTypesPsi();
}

@Override
@NotNull
public PsiClass[] getSupers() {
HaxeAnonymousTypeModel model = (HaxeAnonymousTypeModel) getModel();
return model.getExtendsTypes().stream()
.map(ResultHolder::getClassType)
.filter(Objects::nonNull)
.map(SpecificHaxeClassReference::getHaxeClass)
.filter(Objects::nonNull)
.toArray(PsiClass[]::new);
}

@Override
public HaxeComponentName getComponentName() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1236,9 +1236,10 @@ public Object[] getVariants() {
// if not first in chain
// foo.bar.baz
HaxeResolveResult result = null;
HaxeGenericResolver resolver = null;
final HaxeReference leftReference = HaxeResolveUtil.getLeftReference(this);
if (leftReference != null) {
HaxeGenericResolver resolver = HaxeGenericResolverUtil.generateResolverFromScopeParents(leftReference);
resolver = HaxeGenericResolverUtil.generateResolverFromScopeParents(leftReference);
ResultHolder leftResult = HaxeTypeResolver.getPsiElementType(leftReference, resolver);
if (leftResult.getClassType() != null) {
SpecificTypeReference reference = leftResult.getClassType().fullyResolveTypeDefAndUnwrapNullTypeReference();
Expand All @@ -1250,14 +1251,14 @@ public Object[] getVariants() {

HaxeClass haxeClass = null;
String name = null;
HaxeGenericResolver resolver = null;

if (result != null) {
if (result != HaxeResolveResult.EMPTY) {
haxeClass = result.getHaxeClass();
if (haxeClass != null) {
name = haxeClass.getName();
}
resolver = result.getSpecialization().toGenericResolver(haxeClass);
resolver.addAll(result.getSpecialization().toGenericResolver(haxeClass));
}
if (leftReference.resolve() instanceof HaxeImportAlias alias) {
name = alias.getIdentifier().getText();
Expand Down Expand Up @@ -1493,7 +1494,13 @@ private void addClassNonStaticMembersVariants(Set<HaxeComponentName> suggestedVa
}
}
}

// if type parameter, try to find constraints and use those ?
if(haxeClass instanceof HaxeClassWrapperForTypeParameter typeParameter) {
ResultHolder resolved = resolver.resolve(typeParameter);
if (resolved != null && resolved.isClassType()) {
haxeClass = resolved.getClassType().getHaxeClass();
}
}
for (HaxeNamedComponent namedComponent : HaxeResolveUtil.findNamedSubComponents(resolver, haxeClass)) {
final boolean needFilter = filterByAccess && !namedComponent.isPublic();
if (isAbstractEnum && HaxeAbstractEnumUtil.couldBeAbstractEnumField(namedComponent)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,28 @@ public HaxeAnonymousTypeModel(@NotNull HaxeAnonymousType anonymousType) {
this.anonymousType = anonymousType;
}

//TODO consolidate composite types (`{> type, ...}`) and extension types (`typeX & typeY`)
public List<ResultHolder> getCompositeTypes() {
List<HaxeType> typeList = getCompositeTypesPsi();
return typeList.stream().map(HaxeTypeResolver::getTypeFromType).toList();
}
public List<ResultHolder> getExtendsTypes() {
List<HaxeType> typeList = getExtendsList();
return typeList.stream().map(HaxeTypeResolver::getTypeFromType).toList();
}

public List<HaxeType> getCompositeTypesPsi() {
return CachedValuesManager.getProjectPsiDependentCache(anonymousType, HaxeAnonymousTypeModel::_getCompositeTypes);
}
public List<HaxeType> getExtensionTypesPsi() {
return CachedValuesManager.getProjectPsiDependentCache(anonymousType, HaxeAnonymousTypeModel::_getExtendsTypes);
}
@NotNull
public List<HaxeType> getExtendsList() {
List<HaxeType> extendList = new ArrayList<>(getCompositeTypesPsi());
extendList.addAll(getExtensionTypesPsi());
return extendList;
}

private static List<HaxeType> _getCompositeTypes(HaxeAnonymousType anonymousType) {
val items = new ArrayList<HaxeType>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,8 @@ static ResultHolder handleReferenceExpression( HaxeExpressionEvaluatorContext co

if (typeReference.isUnknown()) continue;

// unwrap Null
//TODO make util for unwrap/get underlying type of Null<T>? (or fix resolver ?)
if (typeReference.isNullType()) {
typeHolder = typeHolder.getClassType().getSpecifics()[0];
typeHolder = typeHolder.tryUnwrapNullType();
}

// TODO: Yo! Eric!! This needs to get fixed. The resolver is coming back as Dynamic, when it should be String
Expand All @@ -201,6 +199,7 @@ static ResultHolder handleReferenceExpression( HaxeExpressionEvaluatorContext co

SpecificHaxeClassReference classType = typeHolder.getClassType();
if (null != classType) {
localResolver = localResolver.withoutClassTypeParameters();
localResolver.addAll(classType.getGenericResolver());
}
String accessName = child.getText();
Expand Down Expand Up @@ -281,6 +280,11 @@ else if (subelement instanceof HaxeFieldDeclaration fieldDeclaration) {
HaxeGenericResolver resolverForContainingClass = inheritedClassResolver.getSpecialization(null).toGenericResolver(containingClass);
ResultHolder resolve = resolverForContainingClass.resolve(typeHolder);
if (resolve != null && !resolve.isUnknown()) typeHolder = resolve;
}else if (typeHolder.isTypeParameter()) {
ResultHolder resolve = resolver.resolve(typeHolder);
if(resolve != null && !resolve.isUnknown()) {
typeHolder = resolve;
}
}

}
Expand Down Expand Up @@ -907,8 +911,13 @@ static ResultHolder handleArrayAccessExpression(
HaxeArrayAccessExpression arrayAccessExpression) {
final List<HaxeExpression> list = arrayAccessExpression.getExpressionList();
if (list.size() >= 2) {
final SpecificTypeReference left = handle(list.get(0), context, resolver).getType();
final SpecificTypeReference right = handle(list.get(1), context, resolver).getType();
SpecificTypeReference left = handle(list.get(0), context, resolver).getType();
SpecificTypeReference right = handle(list.get(1), context, resolver).getType();
// if left is typeParameter try to use typeParameter constraints and see if it have array accessor
if(left.isTypeParameter()) {
ResultHolder resolve = resolver.resolve(left.createHolder());
if(resolve != null && !resolve.isUnknown())left = resolve.getType();
}
if (left.isArray()) {
Object constant = null;
if (left.isConstant()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -676,15 +676,19 @@ public HaxeGenericResolver removeClassScopeIfMethodIsPresent() {
}

public HaxeGenericResolver withoutMethodTypeParameters() {
HaxeGenericResolver copy = copy();
copy.resolvers.removeIf(entry -> entry.resolveSource() == METHOD_TYPE_PARAMETER);
copy.constaints.removeIf(entry -> entry.resolveSource() == METHOD_TYPE_PARAMETER);
return copy;
return without(METHOD_TYPE_PARAMETER);
}
public HaxeGenericResolver withoutArgumentType() {
return without(ARGUMENT_TYPE);
}

public HaxeGenericResolver withoutClassTypeParameters() {
return without(CLASS_TYPE_PARAMETER);
}
public HaxeGenericResolver without(ResolveSource source) {
HaxeGenericResolver copy = copy();
copy.resolvers.removeIf(entry -> entry.resolveSource() == ARGUMENT_TYPE);
copy.constaints.removeIf(entry -> entry.resolveSource() == ARGUMENT_TYPE);
copy.resolvers.removeIf(entry -> entry.resolveSource() == source);
copy.constaints.removeIf(entry -> entry.resolveSource() == source);
return copy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,17 +257,21 @@ private static void mapTypeParametersFunction(Map<String, ResultHolder> map, Spe
}
}

public static HaxeGenericResolver createInheritedClassResolver(HaxeClass targetClass, HaxeClass currentClass,
HaxeGenericResolver localResolver) {
public static HaxeGenericResolver createInheritedClassResolver(@NotNull HaxeClass targetClass, @NotNull HaxeClass currentClass,
@Nullable HaxeGenericResolver localResolver) {

List<SpecificHaxeClassReference> path = new ArrayList<>();
findClassHierarchy(currentClass, targetClass, path);

Collections.reverse(path);

HaxeGenericResolver resolver = currentClass.getMemberResolver(localResolver);
if(resolver == null) resolver = new HaxeGenericResolver();
for (SpecificHaxeClassReference reference : path) {
ResultHolder resolved = resolver.resolve(reference.createHolder());
resolver = resolved.getClassType().getGenericResolver();
if(resolved.isClassType()) {
resolver = resolved.getClassType().getGenericResolver();
}
}
return resolver;
}
Expand All @@ -280,12 +284,23 @@ private static boolean findClassHierarchy(HaxeClass from, HaxeClass to, List<Spe
if (fromModel.isTypedef()) {
SpecificHaxeClassReference reference = fromModel.getUnderlyingClassReference(fromModel.getGenericResolver(null));
if (reference!= null) {
path.add(reference);
// NOTE: anonymous types can extend/ combine multiple definitions
// we therefor only add to path when we have found and reached the target (when the recursive calls returns true)
SpecificHaxeClassReference resolvedTypeDef = reference.resolveTypeDefClass();
if (resolvedTypeDef != null) {
HaxeClass childClass = resolvedTypeDef.getHaxeClass();
if (childClass == to) return true;
if (childClass != null) return findClassHierarchy(childClass, to, path);
if (childClass == to){
path.add(reference);
return true;
}
if (childClass != null){
return findClassHierarchy(childClass, to, path);
}
}else {
if (reference.getHaxeClass() == to) {
path.add(reference);
return true;
}
}
}
}
Expand All @@ -304,10 +319,6 @@ private static boolean findClassHierarchy(HaxeClass from, HaxeClass to, List<Spe
}
}
}

return false;
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -298,9 +298,9 @@ public ResultHolder access(String name, HaxeExpressionEvaluatorContext context,
}

HaxeNamedComponent field = aClass.findHaxeFieldByName(name, localResolver);
if (field != null) {
if (field instanceof HaxePsiField haxePsiField) {
if (context.root == field) return null;
HaxeClass containingClass = (HaxeClass)((HaxePsiField)field).getContainingClass();
HaxeClass containingClass = (HaxeClass)haxePsiField.getContainingClass();
if (containingClass != aClass) {
localResolver.addAll(createInheritedClassResolver(containingClass, aClass, localResolver));
}
Expand Down Expand Up @@ -634,7 +634,7 @@ public SpecificTypeReference fullyResolveTypeDefReference() {
return resolver.resolve(reference1);
}
reference = typeDef.getTargetClass(resolver);
if (reference.isTypeDefOfClass()) {
if (reference!= null && reference.isTypeDefOfClass()) {
haxeClass = reference.getHaxeClass();
resolver = reference.getGenericResolver();
}else{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -866,10 +866,16 @@ public void testResolveTypeFromUsage() throws Throwable {
doTestNoFixWithWarnings();
}

@Test
public void testTypeFromConstraints() throws Throwable {
doTestNoFixWithWarnings();
}

@Test
public void testTypeParameterAnonymousStructure() throws Throwable {
doTestNoFixWithWarnings();
}

@Test
public void testSwitchPatternMatching() throws Throwable {
myFixture.enableInspections(HaxeUnresolvedSymbolInspection.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ public void testTypedefOptionalField() throws Throwable {
myFixture.configureByFiles("TypedefOptionalField.hx");
doTestVariantsInner("TypedefOptionalField.txt");
}
@Test
public void testTypeParameterConstraints() throws Throwable {
myFixture.configureByFiles("std/String.hx", "std/StdTypes.hx", "std/Array.hx");
doTestInclude();
}

//https://github.com/TiVo/intellij-haxe/issues/262
@Test
Expand Down
Loading

0 comments on commit e18a65c

Please sign in to comment.