JS frontend: added checkers for nativeInvoke, nativeGetter, nativeSetter.

This commit is contained in:
Zalim Bashorov
2014-11-25 16:33:48 +03:00
parent fdfbae05e4
commit c06d70d190
18 changed files with 228 additions and 24 deletions
+1
View File
@@ -367,6 +367,7 @@
<fileset dir="idea/src">
<include name="META-INF/extensions/common.xml"/>
<include name="META-INF/extensions/kotlin2jvm.xml"/>
<include name="META-INF/extensions/kotlin2js.xml"/>
</fileset>
@@ -1 +0,0 @@
org.jetbrains.jet.lang.resolve.java.diagnostics.DefaultErrorMessagesJvm
@@ -17,6 +17,8 @@
package org.jetbrains.jet.lang.diagnostics.rendering;
import com.google.common.collect.ImmutableList;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
@@ -35,7 +37,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import java.util.ServiceLoader;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.diagnostics.rendering.Renderers.*;
@@ -44,6 +45,8 @@ import static org.jetbrains.jet.renderer.DescriptorRenderer.*;
public class DefaultErrorMessages {
public interface Extension {
ExtensionPointName<Extension> EP_NAME = ExtensionPointName.create("org.jetbrains.kotlin.defaultErrorMessages");
@NotNull
DiagnosticFactoryToRendererMap getMap();
}
@@ -52,7 +55,7 @@ public class DefaultErrorMessages {
public static final List<DiagnosticFactoryToRendererMap> MAPS = ImmutableList.<DiagnosticFactoryToRendererMap>builder()
.addAll(
KotlinPackage.map(
ServiceLoader.load(Extension.class, DefaultErrorMessages.class.getClassLoader()),
Extensions.getExtensions(Extension.EP_NAME),
new Function1<Extension, DiagnosticFactoryToRendererMap>() {
@Override
public DiagnosticFactoryToRendererMap invoke(Extension extension) {
+2 -2
View File
@@ -24,6 +24,6 @@
<orderEntry type="library" scope="TEST" name="kotlin-runtime" level="project" />
<orderEntry type="library" scope="TEST" name="jps-test" level="project" />
<orderEntry type="module" module-name="js.dart-ast" />
<orderEntry type="module" module-name="js.frontend" />
</component>
</module>
</module>
@@ -48,6 +48,7 @@ import org.jetbrains.jet.context.LazyResolveToken
import org.jetbrains.jet.lang.resolve.java.JavaLazyAnalyzerPostConstruct
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolverPostConstruct
import org.jetbrains.jet.lang.resolve.lazy.ScopeProvider
import org.jetbrains.k2js.resolve.KotlinJsDeclarationCheckerProvider
// NOTE: After making changes, you need to re-generate the injectors.
// To do that, you can run main in this file.
@@ -132,7 +133,7 @@ private fun generatorForTopDownAnalyzerForJs() =
field(javaClass<MutablePackageFragmentProvider>())
field(javaClass<AdditionalCheckerProvider>(),
init = GivenExpression(javaClass<AdditionalCheckerProvider.Empty>().getCanonicalName() + ".INSTANCE$"))
init = GivenExpression(javaClass<KotlinJsDeclarationCheckerProvider>().getName() + ".INSTANCE$"))
}
private fun generatorForTopDownAnalyzerForJvm() =
@@ -18,8 +18,6 @@ package org.jetbrains.jet.plugin.caches.resolve
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession
import org.jetbrains.jet.analyzer.ResolverForModule
import org.jetbrains.jet.lang.psi.JetFile
import com.intellij.psi.search.GlobalSearchScope
import org.jetbrains.jet.analyzer.PlatformAnalysisParameters
import org.jetbrains.jet.analyzer.AnalyzerFacade
import com.intellij.openapi.project.Project
@@ -33,7 +31,7 @@ import org.jetbrains.jet.di.InjectorForLazyResolve
import org.jetbrains.jet.lang.descriptors.impl.ModuleDescriptorImpl
import org.jetbrains.jet.analyzer.ModuleInfo
import org.jetbrains.jet.analyzer.ModuleContent
import org.jetbrains.jet.lang.resolve.AdditionalCheckerProvider
import org.jetbrains.k2js.resolve.KotlinJsDeclarationCheckerProvider
public class JsResolverForModule(
override val lazyResolveSession: ResolveSession
@@ -55,7 +53,7 @@ public object JsAnalyzerFacade : AnalyzerFacade<JsResolverForModule, PlatformAna
project, globalContext.storageManager, syntheticFiles, moduleContentScope
)
val injector = InjectorForLazyResolve(project, globalContext, moduleDescriptor, declarationProviderFactory, BindingTraceContext(), AdditionalCheckerProvider.Empty)
val injector = InjectorForLazyResolve(project, globalContext, moduleDescriptor, declarationProviderFactory, BindingTraceContext(), KotlinJsDeclarationCheckerProvider)
val resolveSession = injector.getResolveSession()!!
moduleDescriptor.initialize(resolveSession.getPackageFragmentProvider())
return JsResolverForModule(resolveSession)
@@ -19,11 +19,12 @@ package org.jetbrains.jet.plugin.project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.resolve.AdditionalCheckerProvider;
import org.jetbrains.jet.lang.resolve.kotlin.JavaDeclarationCheckerProvider;
import org.jetbrains.k2js.resolve.KotlinJsDeclarationCheckerProvider;
public interface TargetPlatform {
@NotNull
AdditionalCheckerProvider getAdditionalCheckerProvider();
TargetPlatform JVM = new TargetPlatformImpl("JVM", JavaDeclarationCheckerProvider.INSTANCE$);
TargetPlatform JS = new TargetPlatformImpl("JS");
TargetPlatform JS = new TargetPlatformImpl("JS", KotlinJsDeclarationCheckerProvider.INSTANCE$);
}
+2
View File
@@ -2,6 +2,8 @@
<id>org.jetbrains.kotlin</id>
<extensionPoints>
<extensionPoint name="defaultErrorMessages"
interface="org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages$Extension"/>
<extensionPoint name="suppressStringProvider"
interface="org.jetbrains.jet.lang.resolve.DiagnosticsWithSuppression$SuppressStringProvider"/>
<extensionPoint name="diagnosticSuppressor"
@@ -2,6 +2,7 @@
<id>org.jetbrains.kotlin</id>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<defaultErrorMessages implementation="org.jetbrains.k2js.resolve.diagnostics.DefaultErrorMessagesJs"/>
<suppressStringProvider implementation="org.jetbrains.k2js.analyze.SuppressUnusedParameterForJsNative"/>
<diagnosticSuppressor implementation="org.jetbrains.k2js.analyze.SuppressWarningsFromExternalModules"/>
</extensions>
@@ -0,0 +1,7 @@
<idea-plugin>
<id>org.jetbrains.kotlin</id>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<defaultErrorMessages implementation="org.jetbrains.jet.lang.resolve.java.diagnostics.DefaultErrorMessagesJvm"/>
</extensions>
</idea-plugin>
+1
View File
@@ -827,6 +827,7 @@
</extensionPoints>
<extensions defaultExtensionNs="org.jetbrains.kotlin">
<xi:include href="extensions/kotlin2jvm.xml" xpointer="xpointer(/idea-plugin/extensions/*)"/>
<xi:include href="extensions/kotlin2js.xml" xpointer="xpointer(/idea-plugin/extensions/*)"/>
</extensions>
@@ -137,7 +137,7 @@ public class InjectorForTopDownAnalyzerForJs {
this.resolveSession = new ResolveSession(project, globalContext, module, declarationProviderFactory, bindingTrace);
this.lazyTopDownAnalyzer = new LazyTopDownAnalyzer();
this.mutablePackageFragmentProvider = new MutablePackageFragmentProvider(module);
this.additionalCheckerProvider = org.jetbrains.jet.lang.resolve.AdditionalCheckerProvider.Empty.INSTANCE$;
this.additionalCheckerProvider = org.jetbrains.k2js.resolve.KotlinJsDeclarationCheckerProvider.INSTANCE$;
this.annotationResolver = new AnnotationResolver();
this.callResolver = new CallResolver();
this.argumentTypeResolver = new ArgumentTypeResolver();
@@ -17,14 +17,17 @@
package org.jetbrains.k2js
import kotlin.properties.Delegates
import org.jetbrains.jet.lang.resolve.name.FqName
public enum class PredefinedAnnotation(public val fqName: String) {
public enum class PredefinedAnnotation(fqName: String) {
LIBRARY : PredefinedAnnotation("kotlin.js.library")
NATIVE : PredefinedAnnotation("kotlin.js.native")
NATIVE_INVOKE : PredefinedAnnotation("kotlin.js.nativeInvoke")
NATIVE_GETTER : PredefinedAnnotation("kotlin.js.nativeGetter")
NATIVE_SETTER : PredefinedAnnotation("kotlin.js.nativeSetter")
public val fqName: FqName = FqName(fqName)
class object {
// TODO: replace with straight assignment when KT-5761 will be fixed
val WITH_CUSTOM_NAME by Delegates.lazy { setOf(LIBRARY, NATIVE) }
@@ -19,7 +19,6 @@ package org.jetbrains.k2js.analyze
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.jet.lang.diagnostics.Errors
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe
import org.jetbrains.jet.lang.resolve.DiagnosticsWithSuppression
import org.jetbrains.k2js.PredefinedAnnotation
import org.jetbrains.jet.lang.diagnostics.Diagnostic
@@ -30,7 +29,7 @@ import org.jetbrains.k2js.config.LibrarySourcesConfig
class SuppressUnusedParameterForJsNative : DiagnosticsWithSuppression.SuppressStringProvider {
override fun get(annotationDescriptor: AnnotationDescriptor): List<String> {
val descriptor = DescriptorUtils.getClassDescriptorForType(annotationDescriptor.getType())
if (PredefinedAnnotation.NATIVE.fqName == DescriptorUtils.getFqName(descriptor).asString()) {
if (PredefinedAnnotation.NATIVE.fqName == DescriptorUtils.getFqNameSafe(descriptor)) {
return listOf(Errors.UNUSED_PARAMETER.getName().toLowerCase())
}
@@ -0,0 +1,101 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.resolve
import org.jetbrains.jet.lang.resolve.AdditionalCheckerProvider
import org.jetbrains.jet.lang.resolve.AnnotationChecker
import org.jetbrains.jet.lang.psi.JetDeclaration
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.diagnostics.DiagnosticSink
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
import org.jetbrains.jet.lang.psi.JetNamedFunction
import org.jetbrains.k2js.resolve.diagnostics.ErrorsJs
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.types.TypeUtils
import org.jetbrains.k2js.PredefinedAnnotation
import org.jetbrains.jet.lang.resolve.DescriptorUtils
import org.jetbrains.k2js.translate.utils.AnnotationsUtils
import org.jetbrains.jet.lang.descriptors.Visibilities
public object KotlinJsDeclarationCheckerProvider : AdditionalCheckerProvider {
override val annotationCheckers: List<AnnotationChecker> = listOf(NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker())
}
private abstract class AbstractNativeAnnotationsChecker(private val requiredAnnotation: PredefinedAnnotation) : AnnotationChecker {
open fun additionalCheck(declaration: JetNamedFunction, descriptor: FunctionDescriptor, diagnosticHolder: DiagnosticSink) {}
override fun check(declaration: JetDeclaration, descriptor: DeclarationDescriptor, diagnosticHolder: DiagnosticSink) {
val annotationDescriptor = descriptor.getAnnotations().findAnnotation(requiredAnnotation.fqName)
if (annotationDescriptor == null) return
if (declaration !is JetNamedFunction || descriptor !is FunctionDescriptor) {
diagnosticHolder.report(ErrorsJs.NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN.on(declaration, annotationDescriptor.getType()))
return
}
val isTopLevel = DescriptorUtils.isTopLevelDeclaration(descriptor)
val hasLocalVisibility = descriptor.getVisibility() == Visibilities.LOCAL
val isExtension = DescriptorUtils.isExtension(descriptor)
if (!isTopLevel && !hasLocalVisibility && isExtension ||
(isTopLevel || hasLocalVisibility) && !isExtension ||
!((isTopLevel || hasLocalVisibility) && isExtension) && !AnnotationsUtils.isNativeObject(descriptor)
) {
diagnosticHolder.report(ErrorsJs.NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN.on(declaration, annotationDescriptor.getType()))
}
additionalCheck(declaration, descriptor, diagnosticHolder)
}
}
public class NativeInvokeChecker : AbstractNativeAnnotationsChecker(PredefinedAnnotation.NATIVE_INVOKE)
private abstract class AbstractNativeIndexerChecker(
requiredAnnotation: PredefinedAnnotation,
private val indexerKind: String,
private val requiredParametersCount: Int
) : AbstractNativeAnnotationsChecker(requiredAnnotation) {
override fun additionalCheck(declaration: JetNamedFunction, descriptor: FunctionDescriptor, diagnosticHolder: DiagnosticSink) {
val parameters = descriptor.getValueParameters()
if (parameters.size() > 0) {
val firstParamClassDescriptor = DescriptorUtils.getClassDescriptorForType(parameters.get(0).getType())
if (firstParamClassDescriptor != KotlinBuiltIns.getInstance().getString() &&
!DescriptorUtils.isSubclass(firstParamClassDescriptor, KotlinBuiltIns.getInstance().getNumber())
) {
diagnosticHolder.report(ErrorsJs.NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER.on(declaration.getValueParameters().first, indexerKind))
}
}
if (parameters.size() != requiredParametersCount) {
diagnosticHolder.report(ErrorsJs.NATIVE_INDEXER_WRONG_PARAMETER_COUNT.on(declaration, requiredParametersCount, indexerKind))
}
}
}
public class NativeGetterChecker : AbstractNativeIndexerChecker(PredefinedAnnotation.NATIVE_GETTER, "getter", requiredParametersCount = 1) {
override fun additionalCheck(declaration: JetNamedFunction, descriptor: FunctionDescriptor, diagnosticHolder: DiagnosticSink) {
super.additionalCheck(declaration, descriptor, diagnosticHolder)
if (!TypeUtils.isNullableType(descriptor.getReturnType())) {
diagnosticHolder.report(ErrorsJs.NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE.on(declaration))
}
}
}
public class NativeSetterChecker : AbstractNativeIndexerChecker(PredefinedAnnotation.NATIVE_SETTER, "setter", requiredParametersCount = 2)
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.resolve.diagnostics
import org.jetbrains.jet.lang.diagnostics.rendering.DefaultErrorMessages
import org.jetbrains.jet.lang.diagnostics.rendering.DiagnosticFactoryToRendererMap
import org.jetbrains.jet.lang.diagnostics.rendering.Renderers
import kotlin.properties.Delegates
private val DIAGNOSTIC_FACTORY_TO_RENDERER by Delegates.lazy {
with(DiagnosticFactoryToRendererMap()) {
put(ErrorsJs.NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN,
"Annotation ''{0}'' is allowed only on member functions of declaration annotated as ''kotlin.js.native'' or on toplevel extension functions", Renderers.RENDER_TYPE)
put(ErrorsJs.NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER, "Native {0}''s first parameter type should be ''kotlin.String'' or subtype of ''kotlin.Number''", Renderers.STRING)
put(ErrorsJs.NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE, "Native getter''s return type should be nullable")
put(ErrorsJs.NATIVE_INDEXER_WRONG_PARAMETER_COUNT, "Expected {0} parameters for native {1}", Renderers.TO_STRING, Renderers.STRING)
this
}
}
public class DefaultErrorMessagesJs : DefaultErrorMessages.Extension {
override fun getMap(): DiagnosticFactoryToRendererMap = DIAGNOSTIC_FACTORY_TO_RENDERER
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.k2js.resolve.diagnostics;
import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory0;
import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory1;
import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory2;
import org.jetbrains.jet.lang.diagnostics.Errors;
import org.jetbrains.jet.lang.psi.JetDeclaration;
import org.jetbrains.jet.lang.psi.JetElement;
import org.jetbrains.jet.lang.types.JetType;
import static org.jetbrains.jet.lang.diagnostics.PositioningStrategies.DECLARATION_RETURN_TYPE;
import static org.jetbrains.jet.lang.diagnostics.PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT;
import static org.jetbrains.jet.lang.diagnostics.Severity.ERROR;
public interface ErrorsJs {
DiagnosticFactory1<JetElement, JetType> NATIVE_ANNOTATIONS_ALLOWED_ONLY_ON_MEMBER_OR_EXTENSION_FUN = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory1<JetElement, String> NATIVE_INDEXER_KEY_SHOULD_BE_STRING_OR_NUMBER = DiagnosticFactory1.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<JetDeclaration> NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE = DiagnosticFactory0.create(ERROR, DECLARATION_RETURN_TYPE);
DiagnosticFactory2<JetElement, Integer, String> NATIVE_INDEXER_WRONG_PARAMETER_COUNT = DiagnosticFactory2.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
@SuppressWarnings("UnusedDeclaration")
Object _initializer = new Object() {
{
Errors.Initializer.initializeFactoryNames(ErrorsJs.class);
}
};
}
@@ -106,14 +106,16 @@ public final class AnnotationsUtils {
}
@Nullable
private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor,
@NotNull PredefinedAnnotation annotation) {
private static AnnotationDescriptor getAnnotationByName(
@NotNull DeclarationDescriptor descriptor,
@NotNull PredefinedAnnotation annotation
) {
return getAnnotationByName(descriptor, annotation.getFqName());
}
@Nullable
private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) {
return descriptor.getAnnotations().findAnnotation(new FqName(fqn));
private static AnnotationDescriptor getAnnotationByName(@NotNull DeclarationDescriptor descriptor, @NotNull FqName fqName) {
return descriptor.getAnnotations().findAnnotation(fqName);
}
public static boolean isNativeObject(@NotNull DeclarationDescriptor descriptor) {
@@ -133,16 +135,18 @@ public final class AnnotationsUtils {
return false;
}
public static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor,
@NotNull PredefinedAnnotation annotation) {
public static boolean hasAnnotationOrInsideAnnotatedClass(
@NotNull DeclarationDescriptor descriptor,
@NotNull PredefinedAnnotation annotation
) {
return hasAnnotationOrInsideAnnotatedClass(descriptor, annotation.getFqName());
}
private static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor, @NotNull String fqn) {
if (getAnnotationByName(descriptor, fqn) != null) {
private static boolean hasAnnotationOrInsideAnnotatedClass(@NotNull DeclarationDescriptor descriptor, @NotNull FqName fqName) {
if (getAnnotationByName(descriptor, fqName) != null) {
return true;
}
ClassDescriptor containingClass = DescriptorUtils.getContainingClass(descriptor);
return containingClass != null && hasAnnotationOrInsideAnnotatedClass(containingClass, fqn);
return containingClass != null && hasAnnotationOrInsideAnnotatedClass(containingClass, fqName);
}
}