Support platform/impl modifiers for classes

Do not report "unused parameter" for parameters of platform declarations. Do
not allow platform class constructors to have val/var parameters or have an
explicit delegation call to another constructor. Do not allow platform classes
to have 'init' blocks.

Also suppress the "supertype not initialized" error for platform classes: the
supertype should be initialized in the impl class
This commit is contained in:
Alexander Udalov
2016-10-28 11:22:35 +03:00
parent ce9691cd2b
commit 751949db69
21 changed files with 528 additions and 7 deletions
@@ -636,9 +636,8 @@ class ControlFlowInformationProvider private constructor(
when (owner) {
is KtPrimaryConstructor -> if (!element.hasValOrVar()) {
val containingClass = owner.getContainingClassOrObject()
val containingClassDescriptor = trace.get(
DECLARATION_TO_DESCRIPTOR, containingClass)
if (!DescriptorUtils.isAnnotationClass(containingClassDescriptor)) {
val containingClassDescriptor = trace.get(DECLARATION_TO_DESCRIPTOR, containingClass) as? ClassDescriptor
if (!DescriptorUtils.isAnnotationClass(containingClassDescriptor) && containingClassDescriptor?.isPlatform == false) {
report(UNUSED_PARAMETER.on(element, variableDescriptor), ctxt)
}
}
@@ -652,6 +651,7 @@ class ControlFlowInformationProvider private constructor(
if (isMain
|| functionDescriptor.isOverridableOrOverrides
|| owner.hasModifier(KtTokens.OVERRIDE_KEYWORD)
|| functionDescriptor.isPlatform || functionDescriptor.isImpl
|| OperatorNameConventions.GET_VALUE == functionName
|| OperatorNameConventions.SET_VALUE == functionName
|| OperatorNameConventions.PROPERTY_DELEGATED == functionName) {
@@ -493,7 +493,8 @@ public interface Errors {
DiagnosticFactory0<KtDeclaration> PLATFORM_DECLARATION_WITH_BODY = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE);
DiagnosticFactory0<KtParameter> PLATFORM_DECLARATION_WITH_DEFAULT_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtConstructorDelegationCall> PLATFORM_CLASS_CONSTRUCTOR_DELEGATION_CALL = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtParameter> PLATFORM_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtExpression> PLATFORM_PROPERTY_INITIALIZER = DiagnosticFactory0.create(ERROR);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -259,7 +259,8 @@ public class DefaultErrorMessages {
MAP.put(PLATFORM_DECLARATION_WITH_BODY, "Platform declaration must not have a body");
MAP.put(PLATFORM_DECLARATION_WITH_DEFAULT_PARAMETER, "Platform declaration cannot have parameters with default values");
MAP.put(PLATFORM_CLASS_CONSTRUCTOR_DELEGATION_CALL, "Explicit delegation call for constructor of a platform class is not allowed");
MAP.put(PLATFORM_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER, "Platform class constructor cannot have a property parameter");
MAP.put(PLATFORM_PROPERTY_INITIALIZER, "Platform property cannot have an initializer");
MAP.put(PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT, "Projections are not allowed on type arguments of functions and properties");
@@ -20,6 +20,7 @@ import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.parsing.KotlinParsing;
import java.util.Collections;
import java.util.List;
@@ -65,6 +66,14 @@ public class KtConstructorDelegationCall extends KtElementImpl implements KtCall
return findChildByClass(KtConstructorDelegationReferenceExpression.class);
}
/**
* @return true if this delegation call is not present in the source code. Note that we always parse delegation calls
* for secondary constructors, even if there's no explicit call in the source (see {@link KotlinParsing#parseSecondaryConstructor}).
*
* class Foo {
* constructor(name: String) // <--- implicit constructor delegation call (empty element after RPAR)
* }
*/
public boolean isImplicit() {
KtConstructorDelegationReferenceExpression callee = getCalleeExpression();
return callee != null && callee.getFirstChild() == null;
@@ -169,6 +169,11 @@ public class BodyResolver {
@NotNull KtSecondaryConstructor constructor,
@NotNull ClassConstructorDescriptor descriptor
) {
if (descriptor.isPlatform()) {
// For platform classes, we do not resolve constructor delegation calls because they are prohibited
return DataFlowInfo.Companion.getEMPTY();
}
OverloadResolutionResults<?> results = callResolver.resolveConstructorDelegationCall(
trace, scope, outerDataFlowInfo,
descriptor, constructor.getDelegationCall());
@@ -362,6 +367,7 @@ public class BodyResolver {
descriptor.getUnsubstitutedPrimaryConstructor() != null &&
superClass.getKind() != ClassKind.INTERFACE &&
!superClass.getConstructors().isEmpty() &&
!descriptor.isPlatform() &&
!ErrorUtils.isError(superClass)
) {
trace.report(SUPERTYPE_NOT_INITIALIZED.on(specifier));
@@ -556,6 +562,9 @@ public class BodyResolver {
if (classDescriptor.getConstructors().isEmpty()) {
trace.report(ANONYMOUS_INITIALIZER_IN_INTERFACE.on(anonymousInitializer));
}
if (classDescriptor.isPlatform()) {
trace.report(PLATFORM_DECLARATION_WITH_BODY.on(anonymousInitializer));
}
}
private void processModifiersOnInitializer(@NotNull KtModifierListOwner owner, @NotNull LexicalScope scope) {
@@ -228,12 +228,36 @@ class DeclarationsChecker(
TypeAliasExpander(reportStrategy).expandWithoutAbbreviation(typeAliasExpansion, Annotations.EMPTY)
}
private fun checkConstructorDeclaration(constructorDescriptor: ClassConstructorDescriptor, declaration: KtDeclaration) {
private fun checkConstructorDeclaration(constructorDescriptor: ClassConstructorDescriptor, declaration: KtConstructor<*>) {
declaration.checkTypeReferences()
modifiersChecker.checkModifiersForDeclaration(declaration, constructorDescriptor)
identifierChecker.checkDeclaration(declaration, trace)
checkVarargParameters(trace, constructorDescriptor)
checkConstructorVisibility(constructorDescriptor, declaration)
checkPlatformClassConstructor(constructorDescriptor, declaration)
}
private fun checkPlatformClassConstructor(constructorDescriptor: ClassConstructorDescriptor, declaration: KtConstructor<*>) {
if (!constructorDescriptor.isPlatform) return
if (declaration.hasBody()) {
trace.report(PLATFORM_DECLARATION_WITH_BODY.on(declaration))
}
if (declaration is KtPrimaryConstructor && !DescriptorUtils.isAnnotationClass(constructorDescriptor.constructedClass)) {
for (parameter in declaration.valueParameters) {
if (parameter.hasValOrVar()) {
trace.report(PLATFORM_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER.on(parameter))
}
}
}
if (declaration is KtSecondaryConstructor) {
val delegationCall = declaration.getDelegationCall()
if (!delegationCall.isImplicit) {
trace.report(PLATFORM_CLASS_CONSTRUCTOR_DELEGATION_CALL.on(delegationCall))
}
}
}
private fun checkConstructorVisibility(constructorDescriptor: ClassConstructorDescriptor, declaration: KtDeclaration) {
@@ -711,6 +735,7 @@ class DeclarationsChecker(
if (containingDescriptor is ClassDescriptor) {
val inInterface = containingDescriptor.kind == ClassKind.INTERFACE
val isPlatformClass = containingDescriptor.isPlatform
if (hasAbstractModifier && !classCanHaveAbstractMembers(containingDescriptor)) {
trace.report(ABSTRACT_FUNCTION_IN_NON_ABSTRACT_CLASS.on(function, functionDescriptor.name.asString(), containingDescriptor))
}
@@ -726,7 +751,7 @@ class DeclarationsChecker(
trace.report(REDUNDANT_OPEN_IN_INTERFACE.on(function))
}
}
if (!hasBody && !hasAbstractModifier && !hasExternalModifier && !inInterface) {
if (!hasBody && !hasAbstractModifier && !hasExternalModifier && !inInterface && !isPlatformClass) {
trace.report(NON_ABSTRACT_FUNCTION_WITH_NO_BODY.on(function, functionDescriptor))
}
}