Skip to content

Commit

Permalink
Merge branch 'upstream-develop' into fix/completion-hxml
Browse files Browse the repository at this point in the history
  • Loading branch information
m0rkeulv committed Feb 15, 2021
2 parents 5d92c9c + f302eda commit d4855ba
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 75 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ on:
- 'master'
- 'develop'
- 'feature/*'
pull_request:
branches:
- 'master'
- 'develop'
- 'feature/*'

jobs:
Build:
Expand Down
27 changes: 15 additions & 12 deletions grammar/haxe.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ private prefixOperator ::= '-' | '!' | '~' { extends=operator }
private questionOperator ::= '?' { extends=operator }
private suffixOperator ::= '!' { extends=operator }

// KFROM and KTO are only keywords for abstracts and can be used as identifiers elsewhere in the code
identifier ::= ID | KFROM | KTO
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeIdentifierPsiMixinImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeIdentifierPsiMixin" name="identifier"}

thisExpression ::= 'this'
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeReference"}

superExpression ::= 'super'
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeReference"}

packageStatement ::= 'package' simpleQualifiedReferenceExpression? ';'
{pin=1 implements="com.intellij.plugins.haxe.lang.psi.HaxePackageStatementPsiMixin" mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxePackageStatementPsiMixinImpl"}
Expand Down Expand Up @@ -305,11 +314,13 @@ externInterfaceDeclaration ::= externAndMaybePrivate? 'interface' componentName
classDeclaration ::= classModifierList? 'class' componentName genericParam? inheritList? classBody
{pin=3 mixin="com.intellij.plugins.haxe.lang.psi.impl.AbstractHaxePsiClass" implements="com.intellij.plugins.haxe.lang.psi.HaxeClass"}

//'from' | 'to'
underlyingType ::= '(' typeWrapper ')'
abstractClassDeclaration ::= privateKeyWord? abstractClassType componentName genericParam? underlyingType? ((identifier) type)* abstractBody

abstractClassDeclaration ::= privateKeyWord? abstractClassType componentName genericParam? underlyingType? (abstractToType | abstractFromType)* abstractBody
{pin=3 mixin="com.intellij.plugins.haxe.lang.psi.impl.AbstractHaxePsiClass" implements="com.intellij.plugins.haxe.lang.psi.HaxeClass"}
abstractClassType ::= 'enum'? 'abstract'
underlyingType ::= '(' typeWrapper ')'
abstractToType ::= 'to' ('(' typeWrapper ')' | typeWrapper){pin=1}
abstractFromType ::= 'from' ('(' typeWrapper ')' | typeWrapper){pin=1}
abstractBody ::= '{' abstractBodyPart* '}' {extends="classBody"}
private abstractBodyPart ::= fieldDeclaration | methodDeclaration | constructorDeclaration {recoverWhile="class_body_part_recover"}

Expand Down Expand Up @@ -436,7 +447,7 @@ private namedTypeOrAnonymous ::= [optionalMark] [componentName ':'] typeOrAnonym
private namedFunctionType ::= [optionalMark] [componentName ':'] functionType
private optionalFunctionTypeWithoutName ::= optionalMark '(' functionType ')' {pin=3}

private oldFunctionType ::= oldFunctionTypeArgumentsList functionReturnType {pin=2}
private oldFunctionType ::= oldFunctionTypeArgumentsList ('(' functionReturnType ')' | functionReturnType) {pin=2}
private oldFunctionTypeArgumentsList ::= ( oldFunctionTypeArgument '->')+
oldFunctionTypeArgument ::= optionalMark? ('(' functionType ')' | typeOrAnonymous) {elementType="functionArgument" pin(".*")=3}

Expand Down Expand Up @@ -713,14 +724,6 @@ private simpleQualifiedReferenceExpression ::= referenceExpression qualifiedRefe

componentName ::= identifier
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeNamedElementImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeNamedElement"}
identifier ::= ID
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeIdentifierPsiMixinImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeIdentifierPsiMixin" name="identifier"}

thisExpression ::= 'this'
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeReference"}

superExpression ::= 'super'
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeReferenceImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeReference"}

// XXX - This could be callExpression, and it makes sense that newExpression would extend/implement callExpression,
// but that affects a lot of code and is best not attempted just before a release...
Expand Down
4 changes: 2 additions & 2 deletions grammar/haxe.flex
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ CONDITIONAL_ERROR="#error"[^\r\n]*
"cast" { return emitToken( KCAST); }

"abstract" { return emitToken( KABSTRACT); }
//"from" { return emitToken( KFROM); }
//"to" { return emitToken( KTO ); }
"from" { return emitToken( KFROM); }
"to" { return emitToken( KTO ); }

"class" { return emitToken( KCLASS); }
"enum" { return emitToken( KENUM); }
Expand Down
2 changes: 2 additions & 0 deletions src/common/com/intellij/plugins/haxe/haxelib/HaxeLibrary.java
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ public static HaxeLibrary load(HaxelibLibraryCache owner, String libName, Sdk sd
return new HaxeLibrary(libName, libraryRoot, owner);
} catch (InvalidParameterException e) {
; // libName must not have been an url
} catch (Exception e) {
LOG.error("Unable to read Haxelib '" + libName +"' reason:" + e.getMessage(), e);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,11 @@ public HaxeClasspath getClasspathForHaxelib(String libraryName) {

// It's not in the cache, so go get it and cache the results.
HaxeLibrary newlib = HaxeLibrary.load(this, libraryName, mySdk);
myCache.add(newlib);

timeLog.stamp("Finished loading library: " + libraryName);
return newlib.getClasspathEntries();
if(null != newlib) {
myCache.add(newlib);
timeLog.stamp("Finished loading library: " + libraryName);
return newlib.getClasspathEntries();
}
}

timeLog.stamp("Unknown library !!! " + libraryName + " !!! ");
Expand Down
29 changes: 13 additions & 16 deletions src/common/com/intellij/plugins/haxe/model/HaxeClassModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -195,16 +195,15 @@ public SpecificHaxeClassReference getUnderlyingClassReference(HaxeGenericResolve
return null;
}

// @TODO: this should be properly parsed in haxe.bnf so searching for to is not required
public List<HaxeType> getAbstractToList() {
if (!isAbstract()) return Collections.emptyList();
if (!isAbstract() ) return Collections.emptyList();

List<HaxeType> types = new LinkedList<HaxeType>();
for (HaxeIdentifier id : UsefulPsiTreeUtil.getChildren(haxeClass, HaxeIdentifier.class)) {
if (id.getText().equals("to")) {
PsiElement sibling = UsefulPsiTreeUtil.getNextSiblingSkipWhiteSpacesAndComments(id);
if (sibling instanceof HaxeType) {
types.add((HaxeType)sibling);
}
HaxeAbstractClassDeclaration abstractClass = (HaxeAbstractClassDeclaration)haxeClass;
List<HaxeAbstractToType> list = abstractClass.getAbstractToTypeList();
for(HaxeAbstractToType toType : list) {
if(toType.getTypeOrAnonymous() != null ) {
types.add(toType.getTypeOrAnonymous().getType());
}
}
return types;
Expand Down Expand Up @@ -304,16 +303,14 @@ private SpecificHaxeClassReference getTypeOfFirstParameter(@NotNull HaxeMethodMo



// @TODO: this should be properly parsed in haxe.bnf so searching for from is not required
public List<HaxeType> getAbstractFromList() {
if (!isAbstract()) return Collections.emptyList();
if (!isAbstract() ) return Collections.emptyList();
List<HaxeType> types = new LinkedList<HaxeType>();
for (HaxeIdentifier id : UsefulPsiTreeUtil.getChildren(haxeClass, HaxeIdentifier.class)) {
if (id.getText().equals("from")) {
PsiElement sibling = UsefulPsiTreeUtil.getNextSiblingSkipWhiteSpacesAndComments(id);
if (sibling instanceof HaxeType) {
types.add((HaxeType)sibling);
}
HaxeAbstractClassDeclaration abstractClass = (HaxeAbstractClassDeclaration)haxeClass;
List<HaxeAbstractFromType> list = abstractClass.getAbstractFromTypeList();
for(HaxeAbstractFromType fromType : list) {
if(fromType.getTypeOrAnonymous() != null ) {
types.add(fromType.getTypeOrAnonymous().getType());
}
}
return types;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@
*/
package com.intellij.plugins.haxe.model.type;

import com.intellij.plugins.haxe.lang.psi.HaxeClass;
import com.intellij.plugins.haxe.lang.psi.HaxeSpecificFunction;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;
import java.util.Set;
import java.util.*;

import static com.intellij.plugins.haxe.model.type.SpecificTypeReference.getStdClass;

Expand Down Expand Up @@ -63,6 +61,7 @@ static private boolean isTypeDefFunction(SpecificTypeReference ref ) {
return false;
}

@NotNull
static private SpecificFunctionReference asFunctionReference(SpecificTypeReference ref) {
if (ref instanceof SpecificFunctionReference)
return (SpecificFunctionReference)ref;
Expand Down Expand Up @@ -97,6 +96,22 @@ static public boolean canAssignToFrom(
if (to == null || from == null) return false;
if (to.isDynamic() || from.isDynamic()) return true;

// if abstract of function is being compared to a function we map the abstract to its underlying function
if (hasAbstractFunctionTypeCast(to, true) && isFunctionTypeOrReference(from)) {
List<SpecificFunctionReference> functionTypes = getAbstractFunctionTypes((SpecificHaxeClassReference)to, true);
SpecificFunctionReference fromFunctionType = asFunctionReference(from);
for(SpecificFunctionReference functionType : functionTypes) {
if(canAssignToFromFunction(functionType, fromFunctionType)) return true;
}
}
if (isFunctionTypeOrReference(to) && hasAbstractFunctionTypeCast(from, false)) {
List<SpecificFunctionReference> functionTypes = getAbstractFunctionTypes((SpecificHaxeClassReference)from, false);
SpecificFunctionReference toFunctionType = asFunctionReference(to);
for(SpecificFunctionReference functionType : functionTypes) {
if(canAssignToFromFunction(toFunctionType, functionType)) return true;
}
}

if (isFunctionTypeOrReference(to) && isFunctionTypeOrReference(from)) {
SpecificFunctionReference toRef = asFunctionReference(to);
SpecificFunctionReference fromRef = asFunctionReference(from);
Expand Down Expand Up @@ -144,6 +159,46 @@ static private boolean canAssignToFromFunction(
return to.returnValue != null && (to.returnValue.isVoid() || to.returnValue.canAssign(from.returnValue));
}

private static boolean hasAbstractFunctionTypeCast(SpecificTypeReference reference, boolean castFrom) {
if (reference instanceof SpecificHaxeClassReference) {
HaxeClass haxeClass = ((SpecificHaxeClassReference)reference).getHaxeClass();
if (haxeClass instanceof HaxeAbstractClassDeclaration) {
HaxeAbstractClassDeclaration abstractClass = (HaxeAbstractClassDeclaration)haxeClass;
if (castFrom && !abstractClass.getAbstractFromTypeList().isEmpty()) return true;
if (!castFrom && !abstractClass.getAbstractToTypeList().isEmpty()) return true;
}
}
return false;
}

@NotNull
private static List<SpecificFunctionReference> getAbstractFunctionTypes(SpecificHaxeClassReference classReference, boolean getCastFrom) {
if (!(classReference.getHaxeClass() instanceof HaxeAbstractClassDeclaration)) return Collections.emptyList();
HaxeAbstractClassDeclaration abstractClass = (HaxeAbstractClassDeclaration)classReference.getHaxeClass();
List<SpecificFunctionReference> list = new ArrayList<>();
if (abstractClass != null) {
if (getCastFrom && !abstractClass.getAbstractFromTypeList().isEmpty()) {
for(HaxeAbstractFromType type : abstractClass.getAbstractFromTypeList()) {
if(type.getFunctionType() != null) {
HaxeSpecificFunction specificFunction =
new HaxeSpecificFunction(type.getFunctionType(), classReference.getGenericResolver().getSpecialization(null));
list.add(SpecificFunctionReference.create(specificFunction));
}
}
}
if (!getCastFrom && !abstractClass.getAbstractToTypeList().isEmpty()){
for(HaxeAbstractToType type : abstractClass.getAbstractToTypeList()) {
if (type.getFunctionType() != null) {
HaxeSpecificFunction specificFunction =
new HaxeSpecificFunction(type.getFunctionType(), classReference.getGenericResolver().getSpecialization(null));
list.add(SpecificFunctionReference.create(specificFunction));
}
}
}
}
return list;
}



static public SpecificHaxeClassReference getUnderlyingClassIfAbstractNull(SpecificHaxeClassReference ref) {
Expand Down
35 changes: 35 additions & 0 deletions testData/annotation.semantic/AbstractAssignmentFromToFunctions.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package;

class AbstractAssignmentFromTo1 {
public static function variableTests():Void {
var val:FunctionFromTo = function(i:Int){return;};
var val:FunctionFrom = function(i:Int){return;};

var val:FunctionFromTo = testMethodIV;
var val:FunctionFrom = testMethodIV;

// should fail due to wrong parameter types
var <error descr="Incompatible type: String->Int should be FunctionFromTo">val:FunctionFromTo = function(i:String){return 1;}</error>;
var <error descr="Incompatible type: String->Int should be FunctionFrom">val:FunctionFrom = function(i:String){return 1;}</error>;
var <error descr="Incompatible type: String->Void should be FunctionFromTo">val:FunctionFromTo = testMethodSV</error>;

//should fail (has no from type or implisit casts)
var <error descr="Incompatible type: Int->Void should be FunctionTo">val:FunctionTo = function(i:Int){return;}</error>;
var <error descr="Incompatible type: Int->Void should be FunctionTo">val:FunctionTo = testMethodIV</error>;
var <error descr="Incompatible type: Int->Void should be FunctionNon">val:FunctionNon = testMethodIV</error>;

}

public static function testMethodIV(i:Int) {return;}
public static function testMethodSV(i:String) {return;}
}



abstract FunctionFromTo(Int->Void) from Int->Void to Int->Void {}

abstract FunctionFrom(Int->Void) from Int->Void {}

abstract FunctionTo(Int->Void) to Int->Void {}

abstract FunctionNon(Int->Void) {}
2 changes: 1 addition & 1 deletion testData/parsing/haxe/declarations/import/Empty182.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
Haxe File
IMPORT_STATEMENT
HaxePsiToken:import('import')
PsiErrorElement:ID expected
PsiErrorElement:<expression> expected
<empty list>
78 changes: 42 additions & 36 deletions testData/parsing/haxe/expressions/Haxe3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1141,12 +1141,13 @@ Haxe File
COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('Kilometer')
IDENTIFIER
HaxePsiToken:ID('from')
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
ABSTRACT_FROM_TYPE
HaxePsiToken:KFROM('from')
TYPE_OR_ANONYMOUS
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
ABSTRACT_BODY
HaxePsiToken:{('{')
METHOD_DECLARATION
Expand Down Expand Up @@ -1240,12 +1241,13 @@ Haxe File
COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('Int')
IDENTIFIER
HaxePsiToken:ID('to')
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Float')
ABSTRACT_TO_TYPE
HaxePsiToken:KTO('to')
TYPE_OR_ANONYMOUS
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Float')
ABSTRACT_BODY
HaxePsiToken:{('{')
HaxePsiToken:}('}')
Expand All @@ -1256,18 +1258,20 @@ Haxe File
COMPONENT_NAME
IDENTIFIER
HaxePsiToken:ID('UInt')
IDENTIFIER
HaxePsiToken:ID('to')
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
IDENTIFIER
HaxePsiToken:ID('from')
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
ABSTRACT_TO_TYPE
HaxePsiToken:KTO('to')
TYPE_OR_ANONYMOUS
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
ABSTRACT_FROM_TYPE
HaxePsiToken:KFROM('from')
TYPE_OR_ANONYMOUS
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
ABSTRACT_BODY
HaxePsiToken:{('{')
HaxePsiToken:}('}')
Expand Down Expand Up @@ -1478,18 +1482,20 @@ Haxe File
IDENTIFIER
HaxePsiToken:ID('Int')
HaxePsiToken:)(')')
IDENTIFIER
HaxePsiToken:ID('from')
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
IDENTIFIER
HaxePsiToken:ID('to')
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
ABSTRACT_FROM_TYPE
HaxePsiToken:KFROM('from')
TYPE_OR_ANONYMOUS
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
ABSTRACT_TO_TYPE
HaxePsiToken:KTO('to')
TYPE_OR_ANONYMOUS
TYPE
REFERENCE_EXPRESSION
IDENTIFIER
HaxePsiToken:ID('Int')
ABSTRACT_BODY
HaxePsiToken:{('{')
PsiComment(MSL_COMMENT)('// MyInt + MyInt can be used as is, and returns a MyInt')
Expand Down
Loading

0 comments on commit d4855ba

Please sign in to comment.