use of org.eclipse.n4js.ts.types.TInterface 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();
}
use of org.eclipse.n4js.ts.types.TInterface 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;
}
use of org.eclipse.n4js.ts.types.TInterface in project n4js by eclipse.
the class AllSuperTypeRefsCollector method doProcess.
@Override
protected void doProcess(ContainerType<?> containerType) {
if (containerType instanceof TClass) {
TClass casted = (TClass) containerType;
ParameterizedTypeRef superType = casted.getSuperClassRef();
result.addAll(casted.getImplementedInterfaceRefs());
if (superType != null) {
result.add(superType);
}
} else if (containerType instanceof TInterface) {
TInterface casted = (TInterface) containerType;
result.addAll(casted.getSuperInterfaceRefs());
} else if (containerType instanceof TObjectPrototype) {
TObjectPrototype casted = (TObjectPrototype) containerType;
ParameterizedTypeRef superType = casted.getSuperType();
if (superType != null) {
result.add(superType);
}
} else if (containerType instanceof PrimitiveType) {
PrimitiveType assignmentCompatible = ((PrimitiveType) containerType).getAssignmentCompatible();
if (assignmentCompatible != null) {
ParameterizedTypeRef typeRef = TypeRefsFactory.eINSTANCE.createParameterizedTypeRef();
typeRef.setDeclaredType(assignmentCompatible);
result.add(typeRef);
}
}
}
use of org.eclipse.n4js.ts.types.TInterface 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();
}
use of org.eclipse.n4js.ts.types.TInterface in project n4js by eclipse.
the class MissingApiMembersForTranspiler method create.
/**
* Returns a tuple of collections used by transpiler to generate interface or class members.
*/
public static MissingApiMembersForTranspiler create(ContainerTypesHelper containerTypesHelper, ScriptApiTracker apiTracker, TClassifier type, ConcreteMembersOrderedForTranspiler cmoft, Script context) {
MemberCollector collector = containerTypesHelper.fromContext(context);
// IDE-1510 create missing API-methods here.
MemberList<TMethod> missingApiMethods = new MemberList<>();
if (type instanceof TClass) {
List<TMethod> c = apiTracker.computeMissingApiMethods((TClass) type, context);
missingApiMethods.addAll(c);
List<VirtualApiTMethod> missingAPIfromInheritance = apiTracker.computeMethodDiff((TClass) type, collector, cmoft.ownedAndMixedInConcreteMembers, missingApiMethods);
missingApiMethods.addAll(missingAPIfromInheritance);
}
if (type instanceof TInterface) {
List<TMethod> c = apiTracker.computeMissingApiMethods((TInterface) type, context);
missingApiMethods.addAll(c);
}
// IDE-1510 create missing API-fields here.
List<AccessorTuple> missingApiAccessorTuples = new ArrayList<>();
{
List<AccessorTuple> computedMissingApiGetterSetter = apiTracker.computeMissingApiGetterSetter((TN4Classifier) type, cmoft.concreteAccessorTuples);
List<AccessorTuple> computedMissingApiFields = apiTracker.computeMissingApiFields((TN4Classifier) type);
// In case of field vs. getter the implementation should be free to choose the form.
// So filter out all missing set/get pairs which are backed by a field (either concrete or inherited)
List<AccessorTuple> filteredMissingApiGetterSetter = filterOutTuplesImplementedByField(computedMissingApiGetterSetter, cmoft.ownedAndMixedInConcreteMembers, cmoft.concreteInheritedMembers);
// Some logic applies to missing fields which are backed by a getter/setter. The situation here is a
// little bit more complex as the set/get must be processed as a pair and one could be missing or they stem
// from different (super-)types.
// *Beware* not side-effect free since we have to mix in virtual getter/setter into an existing but
// incomplete accessor-pairs
List<AccessorTuple> filteredMissingApiFields0 = filterMissingApiFieldsAndEnrichExistingTuples(computedMissingApiFields, cmoft.concreteAccessorTuples);
List<AccessorTuple> filteredMissingApiFields = filterMissingApiFieldsImplementedBySuperGetSet(filteredMissingApiFields0, cmoft.concreteInheritedMembers);
missingApiAccessorTuples.addAll(filteredMissingApiGetterSetter);
missingApiAccessorTuples.addAll(filteredMissingApiFields);
}
MemberList<TField> fieldsOverridingAccessors = getFieldsOverridingAccessor(cmoft.ownedAndMixedInConcreteMembers, cmoft.concreteInheritedMembers);
// compute the list of mixed in fields, which do not override any Accessor (handled separately)
MemberList<TField> fieldsPurelyMixedInNotOverridingAccessor = new MemberList<>();
fieldsPurelyMixedInNotOverridingAccessor.addAll(cmoft.ownedAndMixedInConcreteMembers.stream().filter(it -> it instanceof TField && // must stem from different type
it.getContainingType() != type).map(it -> (TField) it).filter(// remove the ones overriding get/set
it -> !fieldsOverridingAccessors.contains(it)).collect(Collectors.toList()));
return new MissingApiMembersForTranspiler(missingApiMethods, missingApiAccessorTuples);
}
Aggregations