Skip to content

Commit

Permalink
handle ObjectLiterals as classes, so we can resolve members and use t…
Browse files Browse the repository at this point in the history
…hem without a typeTag describing it
  • Loading branch information
m0rkeulv committed Jul 22, 2024
1 parent 683e6a0 commit fa2b200
Show file tree
Hide file tree
Showing 48 changed files with 458 additions and 162 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ platformPlugins = com.intellij.java, com.intellij.flex:241.15989.21, JavaScript
javaVersion = 17

# Gradle Releases -> https://github.com/gradle/gradle/releases
gradleVersion = 7.6
gradleVersion = 7.6.4

# Opt-out flag for bundling Kotlin standard library.
# See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details.
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,23 @@ public void run() {
static private void checkDuplicatedFields(final HaxeClassModel clazz, final AnnotationHolder holder) {
if (!DUPLICATE_FIELDS.isEnabled(clazz.getBasePsi())) return;

Map<String, HaxeMemberModel> map = new HashMap<>();
Set<HaxeMemberModel> repeatedMembers = new HashSet<>();
for (HaxeMemberModel member : clazz.getMembersSelf()) {
Map<String, HaxeBaseMemberModel> map = new HashMap<>();
Set<HaxeBaseMemberModel> repeatedMembers = new HashSet<>();
for (HaxeBaseMemberModel member : clazz.getMembersSelf()) {
final String memberName = member.getName();
HaxeMemberModel repeatedMember = map.get(memberName);
if (repeatedMember != null && !repeatedMember.isOverload()) {
repeatedMembers.add(member);
repeatedMembers.add(repeatedMember);
HaxeBaseMemberModel repeatedMember = map.get(memberName);
if (repeatedMember instanceof HaxeMemberModel memberModel) {
if (!memberModel.isOverload()) {
repeatedMembers.add(member);
repeatedMembers.add(repeatedMember);
}
}
else {
map.put(memberName, member);
}
}

for (HaxeMemberModel member : repeatedMembers) {
for (HaxeBaseMemberModel member : repeatedMembers) {
holder.newAnnotation(HighlightSeverity.ERROR, "Duplicate class field declaration : " + member.getName())
.range(member.getNameOrBasePsi())
.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.intellij.plugins.haxe.HaxeLanguage;
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypeSets;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.model.HaxeBaseMemberModel;
import com.intellij.plugins.haxe.model.HaxeEnumValueModel;
import com.intellij.plugins.haxe.model.HaxeMemberModel;
import com.intellij.plugins.haxe.model.type.HaxeExpressionEvaluator;
Expand Down Expand Up @@ -235,8 +236,8 @@ private static void addEnumValuesIfSourceIsEnum(PsiElement completionElementAsCo
List<String> alreadyInUse =
list.stream().map(HaxeSwitchCase::getSwitchCaseExprList).filter(not(List::isEmpty)).map(exprs -> exprs.get(0).getText())
.toList();
List<HaxeMemberModel> members = classReference.getHaxeClassModel().getMembers(null);
for (HaxeMemberModel member : members) {
List<HaxeBaseMemberModel> members = classReference.getHaxeClassModel().getMembers(null);
for (HaxeBaseMemberModel member : members) {

if (member instanceof HaxeEnumValueModel model) {
String name = member.getName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.intellij.plugins.haxe.lang.psi.HaxeClass;
import com.intellij.plugins.haxe.lang.psi.HaxeReference;
import com.intellij.plugins.haxe.lang.psi.HaxeResolver;
import com.intellij.plugins.haxe.model.HaxeBaseMemberModel;
import com.intellij.plugins.haxe.model.HaxeMemberModel;
import com.intellij.plugins.haxe.util.HaxeAddImportHelper;
import com.intellij.plugins.haxe.util.HaxeElementGenerator;
Expand Down Expand Up @@ -88,7 +89,7 @@ public void handleInsert(InsertionContext context) {
public @Nullable PsiElement getPsiElement() {
HaxeClass haxeClass = HaxeResolveUtil.findClassByQName(qname, helperPsi);
if (haxeClass == null) return null;
HaxeMemberModel member = haxeClass.getModel().getMember(memberName, null);
HaxeBaseMemberModel member = haxeClass.getModel().getMember(memberName, null);
if (member == null) return null;
return member.getNameOrBasePsi();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ public static boolean ternary(PsiBuilder builder_, int level_) {
}


public static boolean isValidStringIdentifier(PsiBuilder builder_, int level_) {
IElementType elementType = builder_.rawLookup(0);
if (elementType == REGULAR_STRING_PART) {
String text = builder_.getTokenText();
if(text != null && text.matches("[a-zA-Z_][a-zA-Z_0-9]*")) {
return true;
}
}
return false;
}

/**
* Make a semi-colon optional in the case that it's preceded by a block statement.
*
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/com/intellij/plugins/haxe/lang/parser/haxe.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -948,10 +948,26 @@ anonymousTypeField ::= "?"? componentName typeTag
private emptyObjectLiteral ::= '{' '}' {name="Object Literal"}
private nonEmptyObjectLiteral ::= '{' objectLiteralElementList '}' {pin=2}
objectLiteral ::= nonEmptyObjectLiteral | emptyObjectLiteral
{mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxeObjectLiteralTypeImpl" implements="com.intellij.plugins.haxe.lang.psi.HaxeClass"}

private objectLiteralElementList ::= objectLiteralElement (',' objectLiteralElement)* ','? {pin=1 recoverWhile="object_literal_list_recover"}
private object_literal_list_recover ::= !('}')
// extractor match expression can contain ObjectLiterals and we need to allow extractors inside these expressions
//TODO extractor match expression can contain ObjectLiterals and we need to allow extractors inside these expressions

objectLiteralIdentifier ::= (<<isValidStringIdentifier>> REGULAR_STRING_PART) | macroIdentifier | normalIdentifier {extends=identifier}
objectLiteralComponentName ::= (OPEN_QUOTE objectLiteralIdentifier CLOSING_QUOTE) | objectLiteralIdentifier
{ extends=componentName methods=[identifier="objectLiteralIdentifier"]}

//lazy way to consume strings that are not valid as identifers
objectLiteralNonComponentName ::= OPEN_QUOTE (REGULAR_STRING_PART | shortTemplateEntry | longTemplateEntry)* CLOSING_QUOTE

objectLiteralElement ::= (objectLiteralComponentName | objectLiteralNonComponentName) ':' expression
{
pin=2
recoverWhile="object_literal_part_recover"
methods=[componentName="objectLiteralComponentName" other="stringLiteralExpression"]
mixin="com.intellij.plugins.haxe.lang.psi.impl.HaxePsiFieldImpl"
implements="com.intellij.plugins.haxe.lang.psi.HaxePsiField"
}

objectLiteralElement ::= (identifier | stringLiteralExpression) ':' expression {pin=2 recoverWhile="object_literal_part_recover"}
private object_literal_part_recover ::= !(',' | '}')
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ private List<? extends PsiElement> checkSwitchOnEnum(HaxeReference reference) {
SpecificHaxeClassReference type = possibleType.getClassType();
HaxeClassModel classModel = type.getHaxeClassModel();
if(classModel!= null) {
HaxeMemberModel member = classModel.getMember(reference.getText(), null);
HaxeBaseMemberModel member = classModel.getMember(reference.getText(), null);
if (member != null) return List.of(member.getNameOrBasePsi());
}
}
Expand Down Expand Up @@ -947,7 +947,7 @@ private List<PsiElement> checkIfDefaultValueInMatchExpression(HaxeReference refe
if (enumClass != null) {
HaxeClassModel model = enumClass.getHaxeClassModel();
if (model != null) {
HaxeMemberModel enumValue = model.getMember(argumentExtractor.getEnumValueReference().getText(), null);
HaxeBaseMemberModel enumValue = model.getMember(argumentExtractor.getEnumValueReference().getText(), null);
if (enumValue instanceof HaxeEnumValueModel enumValueModel) {
int argumentIndex = findExtractorIndex(switchCaseExpr.getChildren(), argumentExtractor);
if (argumentIndex > -1) {
Expand All @@ -968,7 +968,7 @@ private List<PsiElement> checkIfDefaultValueInMatchExpression(HaxeReference refe
HaxeClass aClass = result.getHaxeClass();
if (aClass != null) {
HaxeClassModel model = aClass.getModel();
HaxeMemberModel member = model.getMember(enumType.getText(), null);
HaxeBaseMemberModel member = model.getMember(enumType.getText(), null);
if (member instanceof HaxeEnumValueModel enumValueModel) {
HaxeParameterList parameters = enumValueModel.getConstructorParameters();
if (parameters != null) {
Expand Down Expand Up @@ -1305,7 +1305,7 @@ private List<? extends PsiElement> resolveChain(HaxeReference lefthandExpression
SpecificHaxeClassReference classType = result.getClassType();
HaxeClass haxeClass = classType != null ? classType.getHaxeClass() : null;
if (haxeClass != null) {
HaxeMemberModel member = haxeClass.getModel().getMember(identifier, classType.getGenericResolver());
HaxeBaseMemberModel member = haxeClass.getModel().getMember(identifier, classType.getGenericResolver());
if (member != null) {
//return Collections.singletonList(member.getBasePsi());
//debugList.add(Collections.singletonList(member.getNameOrBasePsi()));
Expand Down Expand Up @@ -1446,7 +1446,7 @@ private HaxeComponentName tryResolveHelperClass(HaxeReference leftReference, Str
}
}

HaxeMemberModel member = model.getMember(helperName, resolveResult.getGenericResolver());
HaxeBaseMemberModel member = model.getMember(helperName, resolveResult.getGenericResolver());
if (member != null) return member.getNamePsi();

if (model.isAbstractType() && ((HaxeAbstractClassModel)model).hasForwards()) {
Expand Down Expand Up @@ -1613,7 +1613,7 @@ private static List<? extends PsiElement> resolveByClassAndSymbol(@Nullable Haxe

if (leftClass != null) {
final HaxeClassModel leftClassModel = leftClass.getModel();
HaxeMemberModel member = leftClassModel.getMember(reference.getReferenceName(), resolver);
HaxeBaseMemberModel member = leftClassModel.getMember(reference.getReferenceName(), resolver);
if (member != null) return asList(member.getNamePsi());

// if class is abstract try find in forwards
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import com.intellij.plugins.haxe.lang.lexer.HaxeTokenTypes;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.model.*;
import com.intellij.plugins.haxe.model.type.ResultHolder;
import com.intellij.plugins.haxe.model.type.SpecificTypeReference;
import com.intellij.plugins.haxe.util.HaxeDebugUtil;
import com.intellij.plugins.haxe.util.HaxePresentableUtil;
import com.intellij.plugins.haxe.util.HaxeResolveUtil;
Expand Down Expand Up @@ -139,6 +141,12 @@ public String getPresentableText() {
result.append(':');
result.append(typeName);
}
} else if (model instanceof HaxeObjectLiteralMemberModel objectLiteralMemberModel) {
ResultHolder type = objectLiteralMemberModel.getResultType(null);
if(type != null && !type.isUnknown()) {
result.append(':');
result.append(type.getType().withoutConstantValue().toPresentationString());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ public HaxeClassModel getModel() {
_model = new HaxeEnumModelImpl(enumDeclaration);
} else if (this instanceof HaxeExternClassDeclaration externClassDeclaration) {
_model = new HaxeExternClassModel(externClassDeclaration);
} else if (this instanceof HaxeObjectLiteralImpl objectLiteral) {
_model = new HaxeObjectLiteralClassModel(objectLiteral);
} else if (this instanceof HaxeAbstractTypeDeclaration abstractDeclaration) {
if (abstractDeclaration.isEnum()) {
_model = new HaxeAbstractEnumModel(abstractDeclaration);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.intellij.plugins.haxe.lang.psi.impl;

import com.intellij.lang.ASTNode;
import com.intellij.plugins.haxe.lang.psi.HaxeComponentName;
import com.intellij.plugins.haxe.lang.psi.HaxeGenericParam;
import com.intellij.plugins.haxe.lang.psi.HaxeObjectLiteral;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

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

public HaxeObjectLiteralTypeImpl(@NotNull HaxeObjectLiteral objectLiteral) {
super(objectLiteral.getNode());
}

public HaxeObjectLiteralTypeImpl(@NotNull ASTNode node) {
super(node);
}

@Override
public @Nullable HaxeComponentName getComponentName() {
return null;
}

@Override
public @Nullable HaxeGenericParam getGenericParam() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@ public HaxePsiFieldImpl(ASTNode node) {
super(node);
}

private HaxeMemberModel _model = null;
private HaxeBaseMemberModel _model = null;
@Override
public HaxeMemberModel getModel() {
public HaxeBaseMemberModel getModel() {
if (_model == null) {
if (this instanceof HaxeEnumValueDeclaration enumValueDeclaration) {
if (this instanceof HaxeObjectLiteralElementImpl objectLiteralElement) {
_model = new HaxeObjectLiteralMemberModel(objectLiteralElement);
} else if (this instanceof HaxeEnumValueDeclaration enumValueDeclaration) {
_model = new HaxeEnumValueModel(enumValueDeclaration);
}else if (HaxeAbstractEnumUtil.isAbstractEnum(getContainingClass()) && HaxeAbstractEnumUtil.couldBeAbstractEnumField(this)) {
_model = new HaxeEnumValueModel((HaxeFieldDeclaration)this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1279,8 +1279,9 @@ else if (result != null && !result.isFunctionType()) {
// TODO: fix haxeClass by type inference. Use compiler code assist?!
}
if (haxeClass != null) {
boolean isObjectLiteral = haxeClass instanceof HaxeObjectLiteral;
boolean isSuper = leftReference instanceof HaxeSuperExpression;
addClassNonStaticMembersVariants(suggestedVariants, haxeClass, leftReference, resolver, !(isThis || isSuper));
addClassNonStaticMembersVariants(suggestedVariants, haxeClass, leftReference, resolver, !(isThis || isSuper || isObjectLiteral));
addUsingVariants(suggestedVariants, suggestedVariantsExtensions, haxeClass, this);
}
}
Expand Down Expand Up @@ -1436,11 +1437,17 @@ private static void addClassStaticMembersVariants(@NotNull final Set<HaxeCompone

final boolean isEnum = haxeClass.isEnum();

List<HaxeComponentName> staticMembers = haxeClass.getModel().getMembersSelf().stream()
.filter(member -> (isEnum && member instanceof HaxeEnumValueModel) || member.isStatic())
.filter(member -> !filterByAccess || member.isPublic())
.map(HaxeMemberModel::getNamePsi)
.collect(Collectors.toList());
List<HaxeComponentName> staticMembers = new ArrayList<>();
for (HaxeBaseMemberModel member : haxeClass.getModel().getMembersSelf()) {
if (member instanceof HaxeMemberModel memberModel) {
if (isEnum && member instanceof HaxeEnumValueModel || memberModel.isStatic()) {
if (!filterByAccess || memberModel.isPublic()) {
HaxeComponentName psi = member.getNamePsi();
staticMembers.add(psi);
}
}
}
}

suggestedVariants.addAll(staticMembers);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,19 +126,19 @@ private List<HaxeMethodModel> getInheritedMethods(@Nullable HaxeGenericResolver
}

@Override
public @Nullable HaxeMemberModel getMember(String name, @Nullable HaxeGenericResolver resolver) {
public @Nullable HaxeBaseMemberModel getMember(String name, @Nullable HaxeGenericResolver resolver) {
return getAllMembers(resolver).stream().filter(model -> model.getName().equals(name)).findFirst().orElse(null);
}

public List<HaxeMemberModel> getAllMembers(@Nullable HaxeGenericResolver resolver) {
public List<HaxeBaseMemberModel> getAllMembers(@Nullable HaxeGenericResolver resolver) {
return getMembers(resolver);
}
@Override
public List<HaxeMemberModel> getMembers(@Nullable HaxeGenericResolver resolver) {
public List<HaxeBaseMemberModel> getMembers(@Nullable HaxeGenericResolver resolver) {
List<HaxeFieldModel> fields = getFields();
List<HaxeMethodModel> methods = getMethods(resolver);

ArrayList<HaxeMemberModel> memberModels = new ArrayList<>();
ArrayList<HaxeBaseMemberModel> memberModels = new ArrayList<>();
memberModels.addAll(fields);
memberModels.addAll(methods);
return memberModels;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@
*/
package com.intellij.plugins.haxe.model;

import com.intellij.openapi.util.Key;
import com.intellij.plugins.haxe.lang.psi.*;
import com.intellij.plugins.haxe.lang.psi.impl.AbstractHaxeNamedComponent;
import com.intellij.plugins.haxe.model.type.HaxeGenericResolver;
import com.intellij.plugins.haxe.model.type.HaxeTypeResolver;
import com.intellij.plugins.haxe.model.type.ResultHolder;
import com.intellij.plugins.haxe.util.HaxeAbstractEnumUtil;
import com.intellij.plugins.haxe.util.UsefulPsiTreeUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -53,6 +50,7 @@ public static HaxeBaseMemberModel fromPsi(PsiElement element) {
if (element instanceof HaxeEnumValueDeclaration enumValueDeclaration) return (HaxeBaseMemberModel) enumValueDeclaration.getModel();
if (element instanceof HaxeLocalVarDeclaration varDeclaration) return (HaxeBaseMemberModel) varDeclaration.getModel();
if (element instanceof HaxeAnonymousTypeField anonymousTypeField) return (HaxeBaseMemberModel) anonymousTypeField.getModel();
if (element instanceof HaxeObjectLiteralElement objectLiteralElement) return (HaxeBaseMemberModel) objectLiteralElement.getModel();

if (element instanceof HaxeParameter) return new HaxeParameterModel((HaxeParameter)element);
if (element instanceof HaxeForStatement) return null;
Expand All @@ -65,6 +63,14 @@ public PsiElement getBasePsi() {
return basePsi;
}

@NotNull
public PsiElement getNameOrBasePsi() {
PsiElement element = getNamePsi();
if (element == null) element = getBasePsi();
return element;
}


@NotNull
public HaxeDocumentModel getDocument() {
if (document == null) document = new HaxeDocumentModel(this.getBasePsi());
Expand Down
Loading

0 comments on commit fa2b200

Please sign in to comment.