diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/LazyOverrideAwareMemberCollector.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/LazyOverrideAwareMemberCollector.java new file mode 100644 index 0000000000..2ad81b6aad --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/LazyOverrideAwareMemberCollector.java @@ -0,0 +1,226 @@ +/** + * Copyright (c) 2016 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ +package org.eclipse.n4js.validation.validators; + +import static org.eclipse.xtext.xbase.lib.IterableExtensions.exists; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.eclipse.emf.common.util.EList; +import org.eclipse.n4js.smith.Measurement; +import org.eclipse.n4js.smith.N4JSDataCollectors; +import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef; +import org.eclipse.n4js.ts.typeRefs.TypeRef; +import org.eclipse.n4js.ts.types.ContainerType; +import org.eclipse.n4js.ts.types.PrimitiveType; +import org.eclipse.n4js.ts.types.TClass; +import org.eclipse.n4js.ts.types.TClassifier; +import org.eclipse.n4js.ts.types.TInterface; +import org.eclipse.n4js.ts.types.TMember; +import org.eclipse.n4js.ts.types.Type; +import org.eclipse.n4js.types.utils.TypeUtils; +import org.eclipse.n4js.typesystem.utils.AbstractCompleteHierarchyTraverser; +import org.eclipse.n4js.typesystem.utils.RuleEnvironment; +import org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions; +import org.eclipse.n4js.utils.DeclMergingHelper; + +import com.google.common.collect.Lists; +import com.google.inject.Inject; + +/** + * Collects all members, including inherited members, of a type and omits some members overridden in the type hierarchy. + * That is, members overridden in the type hierarchy are removed and replaced by the overridden members. However, this + * is done lazily, that is not all cases are considered, as the result is further validated anyway. That is, + * + * Thus, this collector should only be used for validation purposes. + */ + +public class LazyOverrideAwareMemberCollector { + @Inject + private DeclMergingHelper declMergingHelper; + + /** + * Collects all members, including owned members and members defined in implicit super types. + */ + public List collectAllMembers(ContainerType type) { + return new LazyOverrideAwareMemberCollectorX(type, declMergingHelper, true, false).getResult(); + } + + /** + * Collects all inherited members, including members defined in implicit super types; owned members are omitted. + */ + public List collectAllInheritedMembers(ContainerType type) { + return new LazyOverrideAwareMemberCollectorX(type, declMergingHelper, true, true).getResult(); + } + + /** + * Collects all declared members, including owned members; members defined in implicit super types are omitted. + */ + public List collectAllDeclaredMembers(ContainerType type) { + return new LazyOverrideAwareMemberCollectorX(type, declMergingHelper, false, false).getResult(); + } + + /** + * Collects all declared inherited members; owned members and members defined in implicit super types are omitted. + */ + public List collectAllDeclaredInheritedMembers(ContainerType type) { + return new LazyOverrideAwareMemberCollectorX(type, declMergingHelper, false, true).getResult(); + } + + static class LazyOverrideAwareMemberCollectorX extends AbstractCompleteHierarchyTraverser> { + + boolean includeImplicitSuperTypes; + + private List result; + private final RuleEnvironment G; + + boolean onlyInheritedMembers; + + /** + * Creates a new collector with optional support for implicit super types, better use static helper methods. + * + * @param type + * the base type. Must be contained in a resource set if includeImplicitSuperTypes is + * set to true. + * @param includeImplicitSuperTypes + * if true also members of implicit super types will be collected; otherwise only members of declared + * super types are included. + * @param onlyInheritedMembers + * if true, owned members of type are ignore, that is only inherited members are collected + * @throws IllegalArgumentException + * if includeImplicitSuperTypes is set to true and type is + * not contained in a properly initialized N4JS resource set. + */ + private LazyOverrideAwareMemberCollectorX(ContainerType type, DeclMergingHelper declMergingHelper, + boolean includeImplicitSuperTypes, boolean onlyInheritedMembers) { + super(type, declMergingHelper); + this.onlyInheritedMembers = onlyInheritedMembers; + this.includeImplicitSuperTypes = includeImplicitSuperTypes; + this.result = createResultInstance(); + this.G = (includeImplicitSuperTypes) ? RuleEnvironmentExtensions.newRuleEnvironment(type) : null; + } + + @Override + public Measurement getMeasurement() { + return N4JSDataCollectors.dcTHT_LazyOverrideAwareMemberCollectorX + .getMeasurementIfInactive("HierarchyTraverser"); + } + + public List createResultInstance() { + return Lists.newLinkedList(); + } + + @Override + public List doGetResult() { + return result; + } + + @Override + public void doProcess(ContainerType type) { + if (type instanceof TClassifier) { + EList ownedMembers = ((TClassifier) type).getOwnedMembers(); + result.addAll(ownedMembers); + } + } + + @Override + public void doProcess(PrimitiveType currentType) { + // nothing to do in this case + } + + protected void processAndReplace(TClassifier type) { + EList ownedMembers = type.getOwnedMembers(); + Iterator iterInherited = result.iterator(); + while (iterInherited.hasNext()) { + TMember inherited = iterInherited.next(); + if (exists(ownedMembers, m -> Objects.equals(m.getName(), inherited.getName()) + && m.getMemberType() == inherited.getMemberType() && m.isStatic() == inherited.isStatic())) { + iterInherited.remove(); + } + } + result.addAll(ownedMembers); + } + + /** + * Does not add owned members and add inherited members overridden aware. + */ + @Override + public boolean visitTClass(ParameterizedTypeRef typeRef, TClass object) { + List parentResult = result; + if (object != bottomType) { + result = createResultInstance(); + } + + // add and merge + doSwitchTypeRefs(getSuperTypes(object)); + doSwitchTypeRefs(object.getImplementedInterfaceRefs()); + + // add and replace + if (!onlyInheritedMembers || object != bottomType) { // do not add + processAndReplace(object); // ownedMembers + } + + if (parentResult != result) { + parentResult.addAll(result); // merge + result = parentResult; + } + + return Boolean.FALSE; + } + + /** + * Does not add owned members and add inherited members overridden aware. + */ + @Override + public boolean visitTInterface(ParameterizedTypeRef typeRef, TInterface object) { + List parentResult = result; + if (object != bottomType) { + result = createResultInstance(); + } + + doSwitchTypeRefs(object.getSuperInterfaceRefs()); + doSwitchTypeRefs(object.getSuperInterfaceRefs()); + if (!onlyInheritedMembers || object != bottomType) { // do not add + processAndReplace(object); + } + + if (parentResult != result) { + parentResult.addAll(result); // merge + result = parentResult; + } + + return Boolean.FALSE; + } + + @Override + public List getImplicitSuperTypes(Type t) { + if (includeImplicitSuperTypes) { + List implSuperTypeRefs = new ArrayList<>(); + for (TypeRef currTypeRef : RuleEnvironmentExtensions.collectAllImplicitSuperTypes(G, + TypeUtils.createTypeRef(t))) { + implSuperTypeRefs.add((ParameterizedTypeRef) currTypeRef); // they should all be + // ParameterizedTypeRefs + } + return implSuperTypeRefs; + } else { + return Collections.emptyList(); + } + } + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/LazyOverrideAwareMemberCollector.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/LazyOverrideAwareMemberCollector.xtend deleted file mode 100644 index 7168551693..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/LazyOverrideAwareMemberCollector.xtend +++ /dev/null @@ -1,216 +0,0 @@ -/** - * Copyright (c) 2016 NumberFour AG. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * NumberFour AG - Initial API and implementation - */ -package org.eclipse.n4js.validation.validators - -import com.google.common.collect.Lists -import java.util.ArrayList -import java.util.Collections -import java.util.List -import org.eclipse.n4js.ts.typeRefs.ParameterizedTypeRef -import org.eclipse.n4js.ts.typeRefs.TypeRef -import org.eclipse.n4js.ts.types.ContainerType -import org.eclipse.n4js.ts.types.PrimitiveType -import org.eclipse.n4js.ts.types.TClass -import org.eclipse.n4js.ts.types.TClassifier -import org.eclipse.n4js.ts.types.TInterface -import org.eclipse.n4js.ts.types.TMember -import org.eclipse.n4js.ts.types.Type -import org.eclipse.n4js.types.utils.TypeUtils -import org.eclipse.n4js.typesystem.utils.AbstractCompleteHierarchyTraverser -import org.eclipse.n4js.typesystem.utils.RuleEnvironment -import org.eclipse.n4js.typesystem.utils.RuleEnvironmentExtensions -import org.eclipse.n4js.utils.DeclMergingHelper -import com.google.inject.Inject -import org.eclipse.n4js.smith.N4JSDataCollectors -import org.eclipse.n4js.smith.Measurement - -/** - * Collects all members, including inherited members, of a type and omits some members overridden in the type hierarchy. - * That is, members overridden in the type hierarchy are removed and replaced by the overridden members. However, - * this is done lazily, that is not all cases are considered, as the result is further validated anyway. - * That is, - *
    - *
  • data fields and corresponding accessors are not recognized to override each other - *
  • methods (and fields) inherited from interfaces are all added and not filtered. - *
  • private members are added to result (which is required for validation: cannot override private member etc.) - *
- * Thus, this collector should only be used for validation purposes. - */ - -public class LazyOverrideAwareMemberCollector { - @Inject - private DeclMergingHelper declMergingHelper; - - - /** - * Collects all members, including owned members and members defined in implicit super types. - */ - def List collectAllMembers(ContainerType type) { - return new LazyOverrideAwareMemberCollectorX(type, declMergingHelper, true, false).getResult(); - } - - /** - * Collects all inherited members, including members defined in implicit super types; owned members are omitted. - */ - def List collectAllInheritedMembers(ContainerType type) { - return new LazyOverrideAwareMemberCollectorX(type, declMergingHelper, true, true).getResult(); - } - - /** - * Collects all declared members, including owned members; members defined in implicit super types are omitted. - */ - def List collectAllDeclaredMembers(ContainerType type) { - return new LazyOverrideAwareMemberCollectorX(type, declMergingHelper, false, false).getResult(); - } - - /** - * Collects all declared inherited members; owned members and members defined in implicit super types are omitted. - */ - def List collectAllDeclaredInheritedMembers(ContainerType type) { - return new LazyOverrideAwareMemberCollectorX(type, declMergingHelper, false, true).getResult(); - } - - static class LazyOverrideAwareMemberCollectorX extends AbstractCompleteHierarchyTraverser> { - - val boolean includeImplicitSuperTypes; - - private var List result; - private val RuleEnvironment G; - - val boolean onlyInheritedMembers - - - /** - * Creates a new collector with optional support for implicit super types, better use static helper methods. - * - * @param type - * the base type. Must be contained in a resource set if includeImplicitSuperTypes is set to - * true. - * @param includeImplicitSuperTypes - * if true also members of implicit super types will be collected; otherwise only members of declared - * super types are included. - * @param onlyInheritedMembers - * if true, owned members of type are ignore, that is only inherited members are collected - * @throws IllegalArgumentException - * if includeImplicitSuperTypes is set to true and type is not - * contained in a properly initialized N4JS resource set. - */ - private new(ContainerType type, DeclMergingHelper declMergingHelper, boolean includeImplicitSuperTypes, boolean onlyInheritedMembers) { - super(type, declMergingHelper); - this.onlyInheritedMembers = onlyInheritedMembers; - this.includeImplicitSuperTypes = includeImplicitSuperTypes; - result = createResultInstance(); - G = if ( includeImplicitSuperTypes) RuleEnvironmentExtensions.newRuleEnvironment(type) else null; - } - - override Measurement getMeasurement() { - return N4JSDataCollectors.dcTHT_LazyOverrideAwareMemberCollectorX.getMeasurementIfInactive("HierarchyTraverser"); - } - - def List createResultInstance() { - return Lists.newLinkedList() - } - - override protected List doGetResult() { - return result; - } - - override protected void doProcess(ContainerType type) { - if (type instanceof TClassifier) { - val ownedMembers = type.getOwnedMembers() - result.addAll(ownedMembers); - } - } - - override protected doProcess(PrimitiveType currentType) { - // nothing to do in this case - } - - def protected processAndReplace(TClassifier type) { - val ownedMembers = type.getOwnedMembers() - val iterInherited = result.iterator - while (iterInherited.hasNext()) { - val inherited = iterInherited.next; - if (ownedMembers.exists[ - name==inherited.name && memberType===inherited.memberType && static===inherited.static - ]) { - iterInherited.remove - } - } - result.addAll(ownedMembers) - } - - - /** - * Does not add owned members and add inherited members overridden aware. - */ - override boolean visitTClass(ParameterizedTypeRef typeRef, TClass object) { - val parentResult = result - if (object!==bottomType) { - result = createResultInstance(); - } - - // add and merge - doSwitchTypeRefs(getSuperTypes(object)); - doSwitchTypeRefs(object.getImplementedInterfaceRefs()); - - // add and replace - if (!onlyInheritedMembers || object!=bottomType) { // do not add - processAndReplace(object); // ownedMembers - } - - if (parentResult!==result) { - parentResult.addAll(result); // merge - result = parentResult; - } - - return Boolean.FALSE; - } - - - - /** - * Does not add owned members and add inherited members overridden aware. - */ - override public boolean visitTInterface(ParameterizedTypeRef typeRef, TInterface object) { - val parentResult = result - if (object!==bottomType) { - result = createResultInstance(); - } - - doSwitchTypeRefs(object.getSuperInterfaceRefs()); - doSwitchTypeRefs(object.getSuperInterfaceRefs()); - if (!onlyInheritedMembers || object!=bottomType) { // do not add - processAndReplace(object); - } - - if (parentResult!==result) { - parentResult.addAll(result); // merge - result = parentResult; - } - - return Boolean.FALSE; - } - - - override protected List getImplicitSuperTypes(Type t) { - if (includeImplicitSuperTypes) { - val List implSuperTypeRefs = new ArrayList(); - for (TypeRef currTypeRef : RuleEnvironmentExtensions.collectAllImplicitSuperTypes(G, - TypeUtils.createTypeRef(t))) - implSuperTypeRefs.add(currTypeRef as ParameterizedTypeRef); // they should all be ParameterizedTypeRefs - return implSuperTypeRefs; - } - else - return Collections.emptyList(); - } - } -} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/ThirdPartyValidator.java b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/ThirdPartyValidator.java new file mode 100644 index 0000000000..8259209e90 --- /dev/null +++ b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/ThirdPartyValidator.java @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2016 NumberFour AG. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * NumberFour AG - Initial API and implementation + */ +package org.eclipse.n4js.validation.validators; + +import static org.eclipse.n4js.validation.IssueCodes.THIRD_PARTY_BABEL_LET_CONST_IN_FUN_EXPR; + +import java.util.Objects; + +import org.eclipse.n4js.n4JS.FunctionExpression; +import org.eclipse.n4js.n4JS.N4JSPackage; +import org.eclipse.n4js.n4JS.Statement; +import org.eclipse.n4js.n4JS.VariableDeclaration; +import org.eclipse.n4js.n4JS.VariableStatement; +import org.eclipse.n4js.n4JS.VariableStatementKeyword; +import org.eclipse.n4js.validation.AbstractN4JSDeclarativeValidator; +import org.eclipse.xtext.validation.Check; +import org.eclipse.xtext.validation.EValidatorRegistrar; + +/** + * Validations that implement warnings unrelated to the N4JS language. They hint towards limitations and problems in + * third-party tools that are commonly used together with N4JS (e.g. Babel). + */ +public class ThirdPartyValidator extends AbstractN4JSDeclarativeValidator { + + /** + * NEEEDED + * + * when removed check methods will be called twice once by N4JSValidator, and once by + * AbstractDeclarativeN4JSValidator + */ + @Override + public void register(EValidatorRegistrar registrar) { + // nop + } + + /** + * Adds warning for Babel issue #6302. + *

+ * This can be removed once the corresponding test file compiles without errors in Babel:
+ * {@code /org.eclipse.n4js.spec.tests/xpect-tests/Others/ThirdParty_Babel_LetConstInFunctionExpression.n4js.xt}. + */ + @Check + public void checkLetConstInFunctionExpression(FunctionExpression funExpr) { + String funName = funExpr.getName(); + if (funName != null) { + // only interested in top-level statements of the body + for (Statement stmnt : funExpr.getBody().getStatements()) { + if (stmnt instanceof VariableStatement) { + VariableStatement vs = (VariableStatement) stmnt; + if (vs.getVarStmtKeyword() == VariableStatementKeyword.LET // only interested in let/const + || vs.getVarStmtKeyword() == VariableStatementKeyword.CONST) { + + for (VariableDeclaration varDecl : vs.getVarDecl()) { + if (Objects.equals(varDecl.getName(), funName)) { + addIssue(varDecl, N4JSPackage.eINSTANCE.getAbstractVariable_Name(), + THIRD_PARTY_BABEL_LET_CONST_IN_FUN_EXPR.toIssueItem()); + } + } + } + } + } + } + } +} diff --git a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/ThirdPartyValidator.xtend b/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/ThirdPartyValidator.xtend deleted file mode 100644 index 447b7ae056..0000000000 --- a/plugins/org.eclipse.n4js/src/org/eclipse/n4js/validation/validators/ThirdPartyValidator.xtend +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright (c) 2016 NumberFour AG. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * NumberFour AG - Initial API and implementation - */ -package org.eclipse.n4js.validation.validators - -import org.eclipse.n4js.n4JS.FunctionExpression -import org.eclipse.n4js.n4JS.N4JSPackage -import org.eclipse.n4js.n4JS.VariableStatement -import org.eclipse.n4js.n4JS.VariableStatementKeyword -import org.eclipse.n4js.validation.AbstractN4JSDeclarativeValidator -import org.eclipse.xtext.validation.Check -import org.eclipse.xtext.validation.EValidatorRegistrar - -import static org.eclipse.n4js.validation.IssueCodes.* - -/** - * Validations that implement warnings unrelated to the N4JS language. They hint towards limitations and problems in - * third-party tools that are commonly used together with N4JS (e.g. Babel). - */ -class ThirdPartyValidator extends AbstractN4JSDeclarativeValidator { - - /** - * NEEEDED - * - * when removed check methods will be called twice once by N4JSValidator, and once by - * AbstractDeclarativeN4JSValidator - */ - override register(EValidatorRegistrar registrar) { - // nop - } - - /** - * Adds warning for Babel issue #6302. - *

- * This can be removed once the corresponding test file compiles without errors in Babel:
- * {@code /org.eclipse.n4js.spec.tests/xpect-tests/Others/ThirdParty_Babel_LetConstInFunctionExpression.n4js.xt}. - */ - @Check - def void checkLetConstInFunctionExpression(FunctionExpression funExpr) { - val funName = funExpr.name; - if (funName !== null) { - for (stmnt : funExpr.body.statements) { // only interested in top-level statements of the body - if (stmnt instanceof VariableStatement) { - if (stmnt.varStmtKeyword === VariableStatementKeyword.LET // only interested in let/const - || stmnt.varStmtKeyword === VariableStatementKeyword.CONST) { - for (varDecl : stmnt.varDecl) { - if (varDecl.name == funName) { - addIssue(varDecl, N4JSPackage.eINSTANCE.abstractVariable_Name, THIRD_PARTY_BABEL_LET_CONST_IN_FUN_EXPR.toIssueItem()); - } - } - } - } - } - } - } -}