KT-12877: add JsNonModule with support on front-end

This commit is contained in:
Alexey Andreev
2016-07-01 16:47:40 +03:00
committed by Alexey Andreev
parent 3f2ec6871d
commit 0238b182cc
20 changed files with 1581 additions and 57 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.js.analyze
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.LanguageVersionSettingsImpl
import org.jetbrains.kotlin.context.ContextForNewModule
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.frontend.js.di.createTopDownAnalyzerForJs
import org.jetbrains.kotlin.js.analyzer.JsAnalysisResult
import org.jetbrains.kotlin.js.config.JsConfig
import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.js.resolve.MODULE_KIND
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.*
@@ -39,7 +41,9 @@ object TopDownAnalyzerFacadeForJS {
config.moduleDescriptors.map { it.data } +
listOf(JsPlatform.builtIns.builtInsModule)
)
return analyzeFilesWithGivenTrace(files, BindingTraceContext(), context, config)
val trace = BindingTraceContext()
trace.record(MODULE_KIND, context.module, config.moduleKind)
return analyzeFilesWithGivenTrace(files, trace, context, config)
}
@JvmStatic
@@ -37,7 +37,7 @@ object JsPlatformConfigurator : PlatformConfigurator(
JsNameChecker, JsModuleChecker,
PlatformImplDeclarationChecker()
),
additionalCallCheckers = listOf(ReifiedTypeParameterSubstitutionChecker()),
additionalCallCheckers = listOf(ReifiedTypeParameterSubstitutionChecker(), JsModuleCallChecker),
additionalTypeCheckers = listOf(),
additionalClassifierUsageCheckers = listOf(),
additionalAnnotationCheckers = listOf(),
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2016 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.kotlin.js.resolve
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.serialization.js.ModuleKind
import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice
import org.jetbrains.kotlin.util.slicedMap.RewritePolicy
@JvmField val MODULE_KIND = BasicWritableSlice<ModuleDescriptor, ModuleKind>(RewritePolicy.DO_NOTHING)
@@ -50,6 +50,10 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy {
put(ErrorsJs.JS_NAME_PROHIBITED_FOR_NAMED_NATIVE, "@JsName is prohibited for @native declaration with explicit name")
put(ErrorsJs.JS_MODULE_PROHIBITED_ON_VAR, "@JsModule annotation prohibited for 'var' declarations. Use 'val' instead.")
put(ErrorsJs.JS_MODULE_PROHIBITED_ON_NON_NATIVE, "@JsModule annotation prohibited for non-@native declarations.")
put(ErrorsJs.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM, "Can't access declaration marked with @JsModule annotation " +
"from non-modular project")
put(ErrorsJs.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM, "Can't access declaration marked with @JsNonModule annotation " +
"from modular project")
put(ErrorsJs.CANNOT_CHECK_FOR_NATIVE_INTERFACE, "Cannot check for native interface: {0}", RENDER_TYPE)
put(ErrorsJs.UNCHECKED_CAST_TO_NATIVE_INTERFACE, "Unchecked cast to native interface: {0} to {1}", RENDER_TYPE, RENDER_TYPE)
put(ErrorsJs.NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT, "Cannot pass native interface {0} for reified type parameter", RENDER_TYPE)
@@ -54,6 +54,8 @@ public interface ErrorsJs {
DiagnosticFactory0<PsiElement> JS_NAME_PROHIBITED_FOR_NAMED_NATIVE = DiagnosticFactory0.create(ERROR);
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_VAR = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<KtElement> JS_MODULE_PROHIBITED_ON_NON_NATIVE = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE_OR_DEFAULT);
DiagnosticFactory0<PsiElement> CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM = DiagnosticFactory0.create(ERROR, DEFAULT);
DiagnosticFactory0<PsiElement> CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM = DiagnosticFactory0.create(ERROR, DEFAULT);
DiagnosticFactory1<PsiElement, KotlinType> CANNOT_CHECK_FOR_NATIVE_INTERFACE = DiagnosticFactory1.create(ERROR);
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> UNCHECKED_CAST_TO_NATIVE_INTERFACE = DiagnosticFactory2.create(WARNING);
DiagnosticFactory1<PsiElement, KotlinType> NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT = DiagnosticFactory1.create(ERROR);
@@ -0,0 +1,71 @@
/*
* Copyright 2010-2016 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.kotlin.js.resolve.diagnostics
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor
import org.jetbrains.kotlin.js.resolve.MODULE_KIND
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.scopes.receivers.ClassValueReceiver
import org.jetbrains.kotlin.serialization.js.ModuleKind
object JsModuleCallChecker : CallChecker {
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
val bindingContext = context.trace.bindingContext
val containingDescriptor = context.scope.ownerDescriptor
val module = DescriptorUtils.getContainingModule(containingDescriptor)
val moduleKind = bindingContext[MODULE_KIND, module] ?: return
val callee = findRoot(extractModuleCallee(resolvedCall) ?: return)
if (!AnnotationsUtils.isNativeObject(callee)) return
val callToModule = AnnotationsUtils.getModuleName(callee) != null ||
AnnotationsUtils.getFileModuleName(bindingContext, callee) != null
val callToNonModule = AnnotationsUtils.isNonModule(callee) || AnnotationsUtils.isFromNonModuleFile(bindingContext, callee)
if (moduleKind == ModuleKind.PLAIN || moduleKind == ModuleKind.UMD) {
if (!callToNonModule && callToModule) {
context.trace.report(ErrorsJs.CALL_TO_JS_MODULE_WITHOUT_MODULE_SYSTEM.on(reportOn))
}
}
if (moduleKind != ModuleKind.PLAIN) {
if (!callToModule && callToNonModule) {
context.trace.report(ErrorsJs.CALL_TO_JS_NON_MODULE_WITH_MODULE_SYSTEM.on(reportOn))
}
}
}
private fun extractModuleCallee(call: ResolvedCall<*>): DeclarationDescriptor? {
val callee = call.resultingDescriptor
if (DescriptorUtils.isTopLevelDeclaration(callee)) return callee
val receiver = call.dispatchReceiver ?: return callee
if (receiver is ClassValueReceiver) return receiver.classQualifier.descriptor
return null
}
private fun findRoot(callee: DeclarationDescriptor) =
generateSequence(callee) { it.containingDeclaration }
.takeWhile { it !is PackageFragmentDescriptor }
.last()
}
@@ -41,6 +41,7 @@ import java.util.List;
public final class AnnotationsUtils {
private static final String JS_NAME = "kotlin.js.JsName";
private static final FqName JS_MODULE_ANNOTATION = new FqName("kotlin.js.JsModule");
private static final FqName JS_NON_MODULE_ANNOTATION = new FqName("kotlin.js.JsNonModule");
private AnnotationsUtils() {
}
@@ -203,6 +204,24 @@ public final class AnnotationsUtils {
return null;
}
public static boolean isNonModule(@NotNull DeclarationDescriptor declaration) {
return declaration.getAnnotations().findAnnotation(JS_NON_MODULE_ANNOTATION) != null;
}
public static boolean isFromNonModuleFile(@NotNull BindingContext bindingContext, @NotNull DeclarationDescriptor declaration) {
for (AnnotationDescriptor annotation : getContainingFileAnnotations(bindingContext, declaration)) {
DeclarationDescriptor annotationType = annotation.getType().getConstructor().getDeclarationDescriptor();
if (annotationType == null) continue;
FqNameUnsafe fqName = DescriptorUtils.getFqName(annotation.getType().getConstructor().getDeclarationDescriptor());
if (fqName.equals(JS_NON_MODULE_ANNOTATION.toUnsafe())) {
return true;
}
}
return false;
}
@NotNull
private static String extractJsModuleName(@NotNull AnnotationDescriptor annotation) {
ConstantValue<?> importValue = annotation.getAllValueArguments().values().iterator().next();
@@ -225,7 +244,9 @@ public final class AnnotationsUtils {
List<AnnotationDescriptor> annotations = new ArrayList<AnnotationDescriptor>();
for (KtAnnotationEntry psiAnnotation : kotlinFile.getAnnotationEntries()) {
AnnotationDescriptor annotation = bindingContext.get(BindingContext.ANNOTATION, psiAnnotation);
annotations.add(annotation);
if (annotation != null) {
annotations.add(annotation);
}
}
return annotations;
}