Skip to content

Commit

Permalink
using callExpression evaluation for typeParameter resolve when not de…
Browse files Browse the repository at this point in the history
…fined (ex. `var map = map();`)
  • Loading branch information
m0rkeulv committed Apr 8, 2024
1 parent d812bad commit 2563a0d
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 76 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
# Changelog
## 1.5.0.beta-1
* Improvement: Better type inferences
* Improvement: Better resolve of TypeParameters (generics)
* Fixed: Incorrect parsing of functionTypes where old and new styles where mixed.
* Added: Support for enum extractors in switch-case with array expressions.
* Misc: Lots of minor improvements (mostly for resolver and annotators).


* Known issues:
- Resolver might not select correct method when extension methods and member methods got the same name.
- Non-idempotent computation errors, happens due to mix between old and new resolver (just ignore for now)
- Parser fails to parse more complex cases of Reification
- heavy use of type inference can lead to slower annotations

## 1.4.47
* Fixed: Incorrect sorting of Project roots
* Fixed: Resolve of super types would fail when type was import alias
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class HaxeCallExpressionUtil {

//TODO find a way to combine check method, function, constructor and Enum constructor
// (amongst the problem is mixed parameter classes and method needing reference for type resolve)

@NotNull
public static CallExpressionValidation checkMethodCall(@NotNull HaxeCallExpression callExpression, @NotNull HaxeMethod method) {
CallExpressionValidation validation = new CallExpressionValidation();
validation.isMethod = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.intellij.plugins.haxe.model;

import com.intellij.openapi.util.ModificationTracker;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.util.HaxeAddImportHelper;
Expand Down Expand Up @@ -135,7 +136,7 @@ private static List<PsiElement> getChildren(HaxeFile file) {
private static CachedValueProvider.Result<List<PsiElement>> _getChildren(HaxeFile file) {
PsiElement[] children = file.getChildren();
List<PsiElement> list = Arrays.asList(children);
return new CachedValueProvider.Result<>(list, file);
return new CachedValueProvider.Result<>(list, ModificationTracker.EVER_CHANGED );
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -486,9 +486,11 @@ public static ResultHolder searchReferencesForType(final HaxeComponentName compo
public static CachedValueProvider.Result<List<PsiReference>> cachedSearch(final HaxeComponentName componentName, @Nullable final PsiElement searchScope) {
PsiSearchHelper searchHelper = PsiSearchHelper.getInstance(componentName.getProject());
SearchScope scope = searchScope != null ? new LocalSearchScope(searchScope) : searchHelper.getCodeUsageScope(componentName);

return cachedSearch(componentName, scope);
}
public static CachedValueProvider.Result<List<PsiReference>> cachedSearch(final HaxeComponentName componentName, @Nullable final SearchScope searchScope) {
int offset = componentName.getIdentifier().getTextRange().getEndOffset();
List<PsiReference> references = new ArrayList<>(ReferencesSearch.search(componentName, scope).findAll()).stream()
List<PsiReference> references = new ArrayList<>(ReferencesSearch.search(componentName, searchScope).findAll()).stream()
.sorted((r1, r2) -> {
int i1 = getDistance(r1, offset);
int i2 = getDistance(r2, offset);
Expand Down Expand Up @@ -587,18 +589,12 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxePsiField

HaxeGenericResolver classResolver = classType.getGenericResolver();
PsiSearchHelper searchHelper = PsiSearchHelper.getInstance(componentName.getProject());
SearchScope useScope = searchHelper.getCodeUsageScope(componentName);
final SearchScope useScope = searchHelper.getCodeUsageScope(componentName);

List<PsiReference> references =
CachedValuesManager.getProjectPsiDependentCache(componentName, (c) -> cachedSearch(c, useScope))
.getValue();

int offset = componentName.getIdentifier().getTextRange().getEndOffset();
List<PsiReference> references = ProgressManager.getInstance().computeInNonCancelableSection(() -> {
return new ArrayList<>(ReferencesSearch.search(componentName, useScope).findAll()).stream()
.sorted((r1, r2) -> {
int i1 = getDistance(r1, offset);
int i2 = getDistance(r2, offset);
return i1 -i2;
} ).toList();
});
for (PsiReference reference : references) {
if (reference instanceof HaxeExpression expression) {
if (expression.getParent() instanceof HaxeAssignExpression assignExpression) {
Expand All @@ -618,67 +614,19 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxePsiField
if (resolved instanceof HaxeMethodDeclaration methodDeclaration
&& referenceExpression.getParent() instanceof HaxeCallExpression callExpression) {

@NotNull String[] specificNames = classResolver.names();
HaxeMethodModel methodModel = methodDeclaration.getModel();
HaxeGenericResolver methodResolver = methodModel.getGenericResolver(null);
@NotNull String[] methodSpecificNames = methodResolver.names();

List<String> specificsForClass = Arrays.asList(specificNames);
specificsForClass.removeAll(Arrays.asList(methodSpecificNames));
// make sure we are using class level typeParameters (and not method level)
if (methodModel.getGenericParams().isEmpty()) {
HaxeCallExpressionList list = callExpression.getExpressionList();
if (list != null) {
List<HaxeExpression> arguments = list.getExpressionList();
List<HaxeParameterModel> parameters = methodDeclaration.getModel().getParameters();
//TODO This really need to be cleaned up and is one big messy hack at the moment

// correct way to solve it.
//- find method parameters that are typeParameters from class (not from method)
// - try to match parameters and arguments and create map with typeParameters to resolved argument type
// update type specifics with map



HaxeClassModel classModel = classType.getHaxeClassModel();
if (classModel == null) continue;
List<HaxeGenericParamModel> params = classModel.getGenericParams();
@NotNull ResultHolder[] specifics = type.getSpecifics();
Map<String, Integer>specificsMap = new HashMap<>();
for (int i = 0; i < specifics.length; i++) {
HaxeGenericParamModel model = params.get(i);
ResultHolder specific = specifics[i];
if (specific.getType() instanceof SpecificHaxeClassReference classReference) {
if (classReference.isUnknown() || classReference.isTypeParameter()) {
specificsMap.put(model.getName(), i);
}
}
}

Set<String> genericNames = specificsMap.keySet();

int inputCount = Math.min(parameters.size(), arguments.size());
for (int i = 0; i<inputCount; i++) {
SpecificTypeReference paramType = parameters.get(i).getType().getType();
if (paramType instanceof SpecificHaxeClassReference classReference && classReference.isTypeParameter()) {
String name = classReference.getClassName();

if (genericNames.contains(name)) {
Integer index = specificsMap.get(name);
if (specifics[index].isUnknown()) {
ResultHolder handle = handle(arguments.get(i), context, resolver);
if (specifics[index].isUnknown()) {
specifics[index] = handle;
}else {
ResultHolder unified = HaxeTypeUnifier.unify(handle, specifics[index]);
specifics[index] = unified;
}
}
}
}
}
HaxeCallExpressionUtil.CallExpressionValidation validation =
evaluateCallExpressionWithRecursionGuard(callExpression, methodModel);

HaxeGenericResolver resolverFromCallExpression = validation.getResolver();
if (resolverFromCallExpression != null) {
ResultHolder resolve = resolverFromCallExpression.resolve(resultHolder);
if (resolve != null && !resolve.isUnknown()) {
return resolve;
}
}

}
}
if (expression.getParent() instanceof HaxeArrayAccessExpression arrayAccessExpression) {
Expand Down Expand Up @@ -763,7 +711,10 @@ public static ResultHolder searchReferencesForTypeParameters(final HaxePsiField
return resultHolder;
}




private static HaxeCallExpressionUtil.@NotNull CallExpressionValidation evaluateCallExpressionWithRecursionGuard(HaxeCallExpression callExpression,
HaxeMethodModel methodModel) {
HaxeCallExpressionUtil.CallExpressionValidation validation =
HaxeCallExpressionUtil.checkMethodCall(callExpression, methodModel.getMethod());
return validation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,8 @@ static ResultHolder handleVarInit(
if (expression == null) {
return SpecificTypeReference.getInvalid(varInit).createHolder();
}
return handle(expression, context, resolver);
ResultHolder holder = handleWithRecursionGuard(expression, context, resolver);
return holder != null ? holder : createUnknown(varInit);
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.plugins.haxe.ide.projectStructure.detection.HaxeProjectStructureDetector;
import com.intellij.plugins.haxe.util.HaxeTestUtils;
import com.intellij.testFramework.LightPlatformTestCase;
import com.intellij.testFramework.PlatformTestCase;
import org.junit.Test;

Expand All @@ -35,7 +36,7 @@
/**
* @author: Fedor.Korotkov
*/
public class HaxeSourceRootDetectionTest extends PlatformTestCase {
public class HaxeSourceRootDetectionTest extends LightPlatformTestCase {

@Override
protected void tearDown() throws Exception {
Expand Down

0 comments on commit 2563a0d

Please sign in to comment.