Search in sources :

Example 1 with MemberAccessModifier

use of org.eclipse.n4js.ts.types.MemberAccessModifier in project n4js by eclipse.

the class N4JSMemberRedefinitionValidator method constraints_65_overrideCompatible.

/**
 * Constraints 65 (Override Compatible) and relation overrideCompatible.
 *
 * @param m
 *            the overriding member
 * @param s
 *            the overridden or implemented member
 * @param consumptionConflict
 *            check override or implementation override, the latter usually stems from a consumption conflict (so
 *            that s did not get consumed in the first place)
 * @param mm
 *            member matrix, only to improve error message
 * @return true if m is override compatible to s. Note that false does not necessarily means that an error occurred,
 *         since e.g., a getter does not effect a setter
 */
private OverrideCompatibilityResult constraints_65_overrideCompatible(RedefinitionType redefinitionType, TMember m, TMember s, boolean consumptionConflict, MemberMatrix mm) {
    // 1. name and static modifier are always equal here, so we do not have to check that again
    // 2. meta type
    boolean metaTypeCompatible = MemberRedefinitionUtils.isMetaTypeCompatible(m, s);
    if (!metaTypeCompatible) {
        if (!consumptionConflict) {
            // avoid consequential errors
            messageOverrideMetaTypeIncompatible(redefinitionType, m, s, mm);
        }
        return OverrideCompatibilityResult.ERROR;
    }
    // 3. s not final
    if (s.isFinal()) {
        if (!consumptionConflict) {
            // avoid consequential errors
            messageOverrideFinal(redefinitionType, m, s);
        }
        return OverrideCompatibilityResult.ERROR;
    }
    final boolean sIsField = s instanceof TField;
    final boolean sIsSetter = s instanceof TSetter;
    final boolean mIsField = m instanceof TField;
    // 4. s not const
    if (sIsField) {
        // const only defined on TField & TStructuralField
        TField sF = (TField) s;
        if (sF.isConst()) {
            // By GHOLD-186 const redefinition is allowed for const fields
            if (!((mIsField) && ((TField) m).isConst())) {
                if (!consumptionConflict) {
                    // avoid consequential errors
                    messageOverrideConst(redefinitionType, m, sF);
                }
                return OverrideCompatibilityResult.ERROR;
            }
        }
    }
    // 5. must not override non-final/non-const field or setter with a @Final/const field
    if (sIsField || sIsSetter) {
        if (!s.isFinal() && !s.isConst()) {
            if (mIsField && (m.isFinal() || m.isConst())) {
                if (!consumptionConflict) {
                    // avoid consequential errors
                    messageOverrideWithFinalOrConstField(redefinitionType, m, s);
                }
                return OverrideCompatibilityResult.ERROR;
            }
        }
    }
    // 6. abstract
    if (m.isAbstract() && !s.isAbstract()) {
        if (!consumptionConflict) {
            // avoid consequential errors
            messageOverrideAbstract(redefinitionType, m, s);
        }
        return OverrideCompatibilityResult.ERROR;
    }
    // 7. type compatible
    if (!m.isSetter() && !s.isSetter()) {
        // in Method (including constructor), Getter, Field
        Result<Boolean> result = isSubTypeResult(m, s);
        if (result.failed()) {
            if (!consumptionConflict) {
                // avoid consequential errors
                messageOverrideMemberTypeConflict(redefinitionType, m, s, result, mm);
            }
            return OverrideCompatibilityResult.ERROR;
        }
    }
    boolean sIsConst = false;
    if (sIsField) {
        sIsConst = ((TField) s).isConst();
    }
    if ((m.isSetter() || m.isField()) && !s.isGetter() && !sIsConst) {
        Result<Boolean> result = isSubTypeResult(s, m);
        if (result.failed()) {
            if (!consumptionConflict) {
                // avoid consequential errors
                messageOverrideMemberTypeConflict(redefinitionType, m, s, result, mm);
            }
            return OverrideCompatibilityResult.ERROR;
        }
    }
    // 8.1 accessibility must not be reduced
    if (AccessModifiers.checkedLess(m, s)) {
        // fix modifiers in order to avoid strange behavior
        if (!consumptionConflict) {
            // avoid consequential errors
            messageOverrideAccessibilityReduced(redefinitionType, m, s);
        }
        return OverrideCompatibilityResult.ERROR;
    }
    // 8.2 special accessibility handling of public@Internal and protected as they reduce each other
    MemberAccessModifier fixedLeft = AccessModifiers.fixed(m);
    MemberAccessModifier fixedRight = AccessModifiers.fixed(s);
    if ((fixedLeft == MemberAccessModifier.PROTECTED && fixedRight == MemberAccessModifier.PUBLIC_INTERNAL) || (fixedLeft == MemberAccessModifier.PUBLIC_INTERNAL && fixedRight == MemberAccessModifier.PROTECTED)) {
        messageOverrideAccessibilityReduced(redefinitionType, m, s);
        return OverrideCompatibilityResult.ERROR;
    }
    return OverrideCompatibilityResult.COMPATIBLE;
}
Also used : TSetter(org.eclipse.n4js.ts.types.TSetter) TField(org.eclipse.n4js.ts.types.TField) MemberAccessModifier(org.eclipse.n4js.ts.types.MemberAccessModifier)

Example 2 with MemberAccessModifier

use of org.eclipse.n4js.ts.types.MemberAccessModifier in project n4js by eclipse.

the class ProjectCompareHelper method internalCompareApiImpl.

private ProjectCompareResult internalCompareApiImpl(ProjectComparisonEntry entry, int implIdx) {
    if (!entry.isElementEntry())
        // not an entry for an EObject element -> never report differences
        return ProjectCompareResult.equal();
    final int implCount = entry.getImplCount();
    if (implIdx < 0 || implIdx >= implCount)
        // implementation index out of range -> never report differences
        return ProjectCompareResult.equal();
    // compare implementation to API
    final EObject api = entry.getElementAPI();
    final EObject impl = entry.getElementImpl()[implIdx];
    // special case: no API
    if (api == null) {
        if (impl != null)
            return ProjectCompareResult.compliant();
        else
            return ProjectCompareResult.equal();
    }
    // special case: no impl
    if (impl == null) {
        // note: we know api!=null because of above check
        return ProjectCompareResult.error("missing implementation");
    }
    // accessibility-based compare:
    if (api instanceof TMember && impl instanceof TMember) {
        // order important: check for member before type!
        if (AccessModifiers.less((TMember) impl, (TMember) api))
            return ProjectCompareResult.error("reduced visibility");
    } else if (api instanceof Type && impl instanceof Type) {
        final MemberAccessModifier apiAcc = AccessModifiers.toMemberModifier((Type) api);
        final MemberAccessModifier implAcc = AccessModifiers.toMemberModifier((Type) impl);
        if (AccessModifiers.less(implAcc, apiAcc))
            return ProjectCompareResult.error("reduced visibility");
    }
    ImplToApiReplacementProvider typeReplacementProvider = new ImplToApiReplacementProvider(entry.getRoot());
    // subtype-based compare:
    if (api instanceof TMember && impl instanceof TMember) {
        final TMember apiMember = (TMember) api;
        final TMember implMember = (TMember) impl;
        if (apiMember instanceof TField) {
            boolean bAPIProvidesInitializer = PROVIDES_INITIALZER.hasAnnotation(apiMember);
            if (bAPIProvidesInitializer && !hasInitializer(impl)) {
                if (bAPIProvidesInitializer) {
                    return ProjectCompareResult.error("no initializer in implementation but @" + PROVIDES_INITIALZER.name + " in API");
                } else {
                    return ProjectCompareResult.error("initializer in implementation but no @" + PROVIDES_INITIALZER.name + " in API)");
                }
            }
        } else {
            // Method or accessor
            boolean bAPIProvidesDefImpl = PROVIDES_DEFAULT_IMPLEMENTATION.hasAnnotation(apiMember);
            if ((bAPIProvidesDefImpl != hasBody(impl)) && apiMember.eContainer() instanceof TInterface) {
                if (bAPIProvidesDefImpl) {
                    return ProjectCompareResult.error("no body in implementation but @" + PROVIDES_DEFAULT_IMPLEMENTATION.name + " in API");
                } else {
                    return ProjectCompareResult.error("body in implementation but no @" + PROVIDES_DEFAULT_IMPLEMENTATION.name + " in API");
                }
            }
        }
        final TypeRef context = TypeUtils.createTypeRef((Type) api.eContainer());
        final TypeRef typeApi = typeSystem.tau(apiMember, context);
        final TypeRef typeImpl = typeSystem.tau(implMember, context);
        final RuleEnvironment G = RuleEnvironmentExtensions.newRuleEnvironment(api);
        RuleEnvironmentExtensions.setTypeReplacement(G, typeReplacementProvider);
        final Result<Boolean> implSubtypeApi = typeSystem.subtype(G, typeImpl, typeApi);
        final Result<Boolean> apiSubtypeImpl = typeSystem.subtype(G, typeApi, typeImpl);
        final boolean isImplSubtypeApi = !implSubtypeApi.failed();
        final boolean isApiSubtypeImpl = !apiSubtypeImpl.failed();
        final boolean isEqualType = isImplSubtypeApi && isApiSubtypeImpl;
        if (!isEqualType) {
            if (isImplSubtypeApi)
                // not equal but at least compliant
                return ProjectCompareResult.compliant();
            else {
                final String msg = implSubtypeApi.getRuleFailedException().getLocalizedMessage();
                // not even compliant
                return ProjectCompareResult.error(msg);
            }
        }
        if (isSpecialCaseOfHiddenMethodDiff(api, impl)) {
            // not equal but at least compliant
            return ProjectCompareResult.compliant();
        }
        // all fine
        return ProjectCompareResult.equal();
    }
    // classifier compare
    if (api instanceof TClassifier && impl instanceof TClassifier) {
        TClassifier apiClassifier = (TClassifier) api;
        TClassifier implClassifier = (TClassifier) impl;
        EList<TypeVariable> apiTypeVars = apiClassifier.getTypeVars();
        EList<TypeVariable> implTypeVars = implClassifier.getTypeVars();
        // check for number of type variables
        if (apiTypeVars.size() != implTypeVars.size()) {
            return ProjectCompareResult.error("the number of type variables doesn't match");
        }
        final RuleEnvironment ruleEnvironment = RuleEnvironmentExtensions.newRuleEnvironment(api);
        RuleEnvironmentExtensions.setTypeReplacement(ruleEnvironment, typeReplacementProvider);
        // check for upper bound compatibility
        for (int i = 0; i < apiTypeVars.size(); i++) {
            TypeVariable apiTypeVar = apiTypeVars.get(i);
            TypeVariable implTypeVar = implTypeVars.get(i);
            TypeRef apiDeclaredUpperBound = apiTypeVar.getDeclaredUpperBound();
            TypeRef implDeclaredUpperBound = implTypeVar.getDeclaredUpperBound();
            if ((apiDeclaredUpperBound != null) != (implDeclaredUpperBound != null) || (apiDeclaredUpperBound != null && implDeclaredUpperBound != null && !typeSystem.equaltypeSucceeded(ruleEnvironment, apiDeclaredUpperBound, implDeclaredUpperBound))) {
                return ProjectCompareResult.error(String.format("the upper bound of type variable %s isn't compatible with the API", implTypeVar.getName()));
            }
        }
        return ProjectCompareResult.equal();
    }
    // text-based compare:
    // always compare with API
    final String textApi = entry.getTextAPI();
    final String textImpl = entry.getTextImpl(implIdx);
    final boolean isEqual = textApi != null ? textApi.equals(textImpl) : textImpl == null;
    if (!isEqual)
        return ProjectCompareResult.error(textImpl + " is not equal to " + textApi);
    return ProjectCompareResult.equal();
}
Also used : TClassifier(org.eclipse.n4js.ts.types.TClassifier) TField(org.eclipse.n4js.ts.types.TField) TInterface(org.eclipse.n4js.ts.types.TInterface) TypeRef(org.eclipse.n4js.ts.typeRefs.TypeRef) MemberAccessModifier(org.eclipse.n4js.ts.types.MemberAccessModifier) ContainerType(org.eclipse.n4js.ts.types.ContainerType) Type(org.eclipse.n4js.ts.types.Type) TypeVariable(org.eclipse.n4js.ts.types.TypeVariable) EObject(org.eclipse.emf.ecore.EObject) RuleEnvironment(org.eclipse.xsemantics.runtime.RuleEnvironment) TMember(org.eclipse.n4js.ts.types.TMember)

Example 3 with MemberAccessModifier

use of org.eclipse.n4js.ts.types.MemberAccessModifier in project n4js by eclipse.

the class AccessModifiers method fixed.

/**
 * Returns either the declared access modifier, or a correct fixed version. E.g., members of interfaces must never
 * be declared private, thus such a modifier is corrected to project. Note that
 * {@link TMemberWithAccessModifier#getMemberAccessModifier()} may calculate the modifier if it is undefined, but
 * does not "fix" a wrongly declared modifier.
 */
public static MemberAccessModifier fixed(TMember m) {
    MemberAccessModifier modifier = m.getMemberAccessModifier();
    Type containingType = m.getContainingType();
    if (containingType instanceof TInterface) {
        // correspongingMAM will be one of {PRIVATE PROJECT PUBLIC_INTERNAL PUBLIC} - with PROJECT as default.
        MemberAccessModifier correspondingTypesMAM = toMemberModifier(containingType);
        if (correspondingTypesMAM == MemberAccessModifier.PRIVATE && containingType instanceof TInterface) {
            correspondingTypesMAM = MemberAccessModifier.PROJECT;
        }
    }
    return modifier;
}
Also used : Type(org.eclipse.n4js.ts.types.Type) TInterface(org.eclipse.n4js.ts.types.TInterface) MemberAccessModifier(org.eclipse.n4js.ts.types.MemberAccessModifier)

Example 4 with MemberAccessModifier

use of org.eclipse.n4js.ts.types.MemberAccessModifier in project n4js by eclipse.

the class MemberVisibilityChecker method isVisible.

/**
 * Returns the MemberVisibility of the <i>member</i> of the <i>receiverType</> in the given <i>context</i>
 */
private MemberVisibility isVisible(TModule contextModule, Type contextType, Type declaredReceiverType, TMember member, boolean supercall) {
    int startIndex = member.getMemberAccessModifier().getValue();
    boolean visibility = false;
    String firstVisible = "PUBLIC";
    for (int i = startIndex; i < MemberAccessModifier.values().length; i++) {
        boolean visibilityForModifier = false;
        MemberAccessModifier modifier = MemberAccessModifier.get(i);
        switch(modifier) {
            case PRIVATE:
                visibilityForModifier = isModuleVisible(contextModule, member);
                break;
            case PROJECT:
                visibilityForModifier = isProjectVisible(contextModule, member);
                break;
            case PROTECTED_INTERNAL:
                visibilityForModifier = isProtectedInternalVisible(contextType, contextModule, declaredReceiverType, member, supercall);
                break;
            case PROTECTED:
                visibilityForModifier = isProtectedVisible(contextType, contextModule, declaredReceiverType, member, supercall);
                break;
            case PUBLIC_INTERNAL:
                visibilityForModifier = isPublicInternalVisible(contextType, contextModule, declaredReceiverType, member);
                break;
            case PUBLIC:
                visibilityForModifier = true;
                break;
            default:
                break;
        }
        // First modifier = element modifier
        if (i - startIndex < 1) {
            visibility = visibilityForModifier;
        }
        // First visible modifier = suggested element modifier
        if (visibilityForModifier) {
            firstVisible = modifier.getName();
            break;
        }
    }
    return new MemberVisibility(visibility, firstVisible);
}
Also used : MemberAccessModifier(org.eclipse.n4js.ts.types.MemberAccessModifier)

Example 5 with MemberAccessModifier

use of org.eclipse.n4js.ts.types.MemberAccessModifier in project n4js by eclipse.

the class TMethodImpl method getMemberAccessModifier.

/**
 * <!-- begin-user-doc -->
 * <!-- end-user-doc -->
 * @generated
 */
public MemberAccessModifier getMemberAccessModifier() {
    MemberAccessModifier _declaredMemberAccessModifier = this.getDeclaredMemberAccessModifier();
    boolean _tripleEquals = (_declaredMemberAccessModifier == MemberAccessModifier.UNDEFINED);
    if (_tripleEquals) {
        final EObject parent = this.eContainer();
        if ((parent instanceof TInterface)) {
            final MemberAccessModifier modifierDerivedFromType = AccessModifiers.toMemberModifier(((Type) parent).getTypeAccessModifier());
            if ((modifierDerivedFromType != MemberAccessModifier.PRIVATE)) {
                return modifierDerivedFromType;
            }
        }
        return MemberAccessModifier.PROJECT;
    }
    return this.getDeclaredMemberAccessModifier();
}
Also used : TInterface(org.eclipse.n4js.ts.types.TInterface) EObject(org.eclipse.emf.ecore.EObject) MemberAccessModifier(org.eclipse.n4js.ts.types.MemberAccessModifier)

Aggregations

MemberAccessModifier (org.eclipse.n4js.ts.types.MemberAccessModifier)9 TInterface (org.eclipse.n4js.ts.types.TInterface)4 EObject (org.eclipse.emf.ecore.EObject)3 ENotificationImpl (org.eclipse.emf.ecore.impl.ENotificationImpl)2 TField (org.eclipse.n4js.ts.types.TField)2 Type (org.eclipse.n4js.ts.types.Type)2 TypeRef (org.eclipse.n4js.ts.typeRefs.TypeRef)1 ContainerType (org.eclipse.n4js.ts.types.ContainerType)1 TClassifier (org.eclipse.n4js.ts.types.TClassifier)1 TMember (org.eclipse.n4js.ts.types.TMember)1 TSetter (org.eclipse.n4js.ts.types.TSetter)1 TypeVariable (org.eclipse.n4js.ts.types.TypeVariable)1 RuleEnvironment (org.eclipse.xsemantics.runtime.RuleEnvironment)1