KT-12877: add JsNonModule with support on front-end
This commit is contained in:
committed by
Alexey Andreev
parent
3f2ec6871d
commit
0238b182cc
@@ -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)
|
||||
+4
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user