diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 07cb7bda75a..c142b096082 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -208,7 +208,9 @@ public interface Errors { DiagnosticFactory2 DEPRECATED_MODIFIER = DiagnosticFactory2.create(WARNING); DiagnosticFactory2 REDUNDANT_MODIFIER_FOR_TARGET = DiagnosticFactory2.create(WARNING); DiagnosticFactory0 NO_EXPLICIT_VISIBILITY_IN_API_MODE = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 NO_EXPLICIT_RETURN_TYPE_IN_API_MODE = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION = DiagnosticFactory0.create(WARNING); + DiagnosticFactory0 NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_MIGRATION = DiagnosticFactory0.create(WARNING); DiagnosticFactory2 WRONG_MODIFIER_CONTAINING_DECLARATION = DiagnosticFactory2.create(ERROR); DiagnosticFactory2 DEPRECATED_MODIFIER_CONTAINING_DECLARATION = DiagnosticFactory2.create(WARNING); DiagnosticFactory1 ILLEGAL_INLINE_PARAMETER_MODIFIER = DiagnosticFactory1.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index e7e0ee4ca03..0bc7626bfe6 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -120,7 +120,9 @@ public class DefaultErrorMessages { MAP.put(DEPRECATED_MODIFIER, "Modifier ''{0}'' is deprecated, use ''{1}'' instead", TO_STRING, TO_STRING); MAP.put(REDUNDANT_MODIFIER_FOR_TARGET, "Modifier ''{0}'' is redundant for ''{1}''", TO_STRING, TO_STRING); MAP.put(NO_EXPLICIT_VISIBILITY_IN_API_MODE, "This declaration is effectively public API and API mode is on, but no visibility is specified"); + MAP.put(NO_EXPLICIT_RETURN_TYPE_IN_API_MODE, "This declaration is effectively public API and API mode is on, but no explicit return type is specified"); MAP.put(NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION, "This declaration is effectively public API and API mode is on, but no visibility is specified"); + MAP.put(NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_MIGRATION, "This declaration is effectively public API and API mode is on, but no explicit return type is specified"); MAP.put(WRONG_MODIFIER_CONTAINING_DECLARATION, "Modifier ''{0}'' is not applicable inside ''{1}''", TO_STRING, TO_STRING); MAP.put(DEPRECATED_MODIFIER_CONTAINING_DECLARATION, "Modifier ''{0}'' is deprecated inside ''{1}''", TO_STRING, TO_STRING); MAP.put(ILLEGAL_INLINE_PARAMETER_MODIFIER, "Modifier ''{0}'' is allowed only for function parameters of an inline function", TO_STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ApiModeDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ApiModeDeclarationChecker.kt index d4879630a33..5a1fa3db44d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ApiModeDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ApiModeDeclarationChecker.kt @@ -5,14 +5,17 @@ package org.jetbrains.kotlin.resolve.checkers +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.config.AnalysisFlags import org.jetbrains.kotlin.config.ApiMode import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0 import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.reportDiagnosticOnce import org.jetbrains.kotlin.lexer.KtModifierKeywordToken -import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.visibilityModifier import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyPublicApi @@ -24,16 +27,64 @@ class ApiModeDeclarationChecker : DeclarationChecker { val isApi = (descriptor as? DeclarationDescriptorWithVisibility)?.isEffectivelyPublicApi ?: return if (!isApi) return + + checkVisibilityModifier(state, declaration, descriptor, context) + checkExplicitReturnType(state, declaration, descriptor, context) + } + + private fun checkVisibilityModifier( + state: ApiMode, + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + context: DeclarationCheckerContext + ) { val modifier = declaration.visibilityModifier()?.node?.elementType as? KtModifierKeywordToken if (modifier != null) return if (excludeForDiagnostic(descriptor)) return + context.selectDiagnosticAndReport( + declaration, + state, + Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE, + Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION + ) + } + + private fun checkExplicitReturnType( + state: ApiMode, + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + context: DeclarationCheckerContext + ) { + if (declaration !is KtCallableDeclaration) return + if (!returnTypeCheckIsApplicable(declaration)) return + + val shouldReport = returnTypeRequired( + declaration, descriptor, + checkForPublicApi = true, + checkForInternal = false, + checkForPrivate = false + ) + if (shouldReport) context.selectDiagnosticAndReport( + declaration, + state, + Errors.NO_EXPLICIT_RETURN_TYPE_IN_API_MODE, + Errors.NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_MIGRATION + ) + } + + private fun DeclarationCheckerContext.selectDiagnosticAndReport( + on: KtDeclaration, + state: ApiMode, + ifError: DiagnosticFactory0, + ifWarning: DiagnosticFactory0 + ) { val diagnostic = if (state == ApiMode.ENABLED) - Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE.on(declaration) + ifError.on(on) else - Errors.NO_EXPLICIT_VISIBILITY_IN_API_MODE_MIGRATION.on(declaration) - context.trace.reportDiagnosticOnce(diagnostic) + ifWarning.on(on) + trace.reportDiagnosticOnce(diagnostic) } /** @@ -57,5 +108,36 @@ class ApiModeDeclarationChecker : DeclarationChecker { fun isEnabled(settings: LanguageVersionSettings): ApiMode { return settings.getFlag(AnalysisFlags.apiMode) } + + fun returnTypeRequired( + element: KtCallableDeclaration, + descriptor: DeclarationDescriptor?, + checkForPublicApi: Boolean, + checkForInternal: Boolean, + checkForPrivate: Boolean + ): Boolean = + element.containingClassOrObject?.isLocal != true && + when (element) { + is KtFunction -> !element.isLocal + is KtProperty -> !element.isLocal + else -> false + } && run { + val callableMemberDescriptor = descriptor as? CallableMemberDescriptor + + val visibility = callableMemberDescriptor?.effectiveVisibility()?.toVisibility() + (checkForPublicApi && visibility?.isPublicAPI == true) || (checkForInternal && visibility == Visibilities.INTERNAL) || + (checkForPrivate && visibility == Visibilities.PRIVATE) + } + + fun returnTypeCheckIsApplicable(element: KtCallableDeclaration): Boolean { + if (element.containingFile is KtCodeFragment) return false + if (element is KtFunctionLiteral) return false // TODO(Mikhail Glukhikh): should KtFunctionLiteral be KtCallableDeclaration at all? + if (element is KtConstructor<*>) return false + if (element.typeReference != null) return false + + if (element is KtNamedFunction && element.hasBlockBody()) return false + + return true + } } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/PublicApiImplicitTypeInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/PublicApiImplicitTypeInspection.kt index cebcb23d54c..c7412d2f1e4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/PublicApiImplicitTypeInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/PublicApiImplicitTypeInspection.kt @@ -6,13 +6,12 @@ package org.jetbrains.kotlin.idea.inspections import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel +import org.jetbrains.kotlin.config.AnalysisFlags +import org.jetbrains.kotlin.config.ApiMode import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.Visibilities -import org.jetbrains.kotlin.descriptors.effectiveVisibility import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.psi.KtFunction -import org.jetbrains.kotlin.psi.KtProperty -import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject +import org.jetbrains.kotlin.idea.project.languageVersionSettings +import org.jetbrains.kotlin.resolve.checkers.ApiModeDeclarationChecker import javax.swing.JComponent class PublicApiImplicitTypeInspection( @@ -20,20 +19,11 @@ class PublicApiImplicitTypeInspection( @JvmField var reportPrivate: Boolean = false ) : AbstractImplicitTypeInspection( { element, inspection -> - element.containingClassOrObject?.isLocal != true && - when (element) { - is KtFunction -> !element.isLocal - is KtProperty -> !element.isLocal - else -> false - } && run { - val callableMemberDescriptor = element.resolveToDescriptorIfAny() as? CallableMemberDescriptor - val forInternal = (inspection as PublicApiImplicitTypeInspection).reportInternal - val forPrivate = inspection.reportPrivate - - val visibility = callableMemberDescriptor?.effectiveVisibility()?.toVisibility() - visibility?.isPublicAPI == true || (forInternal && visibility == Visibilities.INTERNAL) || - (forPrivate && visibility == Visibilities.PRIVATE) - } + val shouldCheckForPublic = element.languageVersionSettings.getFlag(AnalysisFlags.apiMode) == ApiMode.DISABLED + val callableMemberDescriptor = element.resolveToDescriptorIfAny() as? CallableMemberDescriptor + val forInternal = (inspection as PublicApiImplicitTypeInspection).reportInternal + val forPrivate = inspection.reportPrivate + ApiModeDeclarationChecker.returnTypeRequired(element, callableMemberDescriptor, shouldCheckForPublic, forInternal, forPrivate) } ) { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantVisibilityModifierInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantVisibilityModifierInspection.kt index 864f78d2764..ca9f4a9d4b9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantVisibilityModifierInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantVisibilityModifierInspection.kt @@ -7,11 +7,14 @@ package org.jetbrains.kotlin.idea.inspections import com.intellij.codeInspection.* import com.intellij.psi.PsiElementVisitor +import org.jetbrains.kotlin.config.AnalysisFlags +import org.jetbrains.kotlin.config.ApiMode import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.idea.core.implicitVisibility +import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.lexer.KtTokens @@ -26,6 +29,9 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult class RedundantVisibilityModifierInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { return declarationVisitor(fun(declaration: KtDeclaration) { + val isInApiMode = declaration.languageVersionSettings.getFlag(AnalysisFlags.apiMode) != ApiMode.DISABLED + if (isInApiMode) return@declarationVisitor + if (declaration is KtPropertyAccessor && declaration.isGetter) return // There is a quick fix for REDUNDANT_MODIFIER_IN_GETTER val visibilityModifier = declaration.visibilityModifier() ?: return val implicitVisibility = declaration.implicitVisibility() diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt index 47bb7e49ac0..17ef002fe89 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SpecifyTypeExplicitlyIntention.kt @@ -39,6 +39,7 @@ import org.jetbrains.kotlin.idea.util.getResolvableApproximations import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.checkers.ApiModeDeclarationChecker import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.typeUtil.makeNotNullable @@ -50,12 +51,7 @@ class SpecifyTypeExplicitlyIntention : SelfTargetingRangeIntention) return null - if (element.typeReference != null) return null - - if (element is KtNamedFunction && element.hasBlockBody()) return null + if (!ApiModeDeclarationChecker.returnTypeCheckIsApplicable(element)) return null text = if (element is KtFunction) "Specify return type explicitly" else "Specify type explicitly" diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt index bcf3eaf6875..2680476a83f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/QuickFixRegistrar.kt @@ -25,10 +25,7 @@ import org.jetbrains.kotlin.idea.inspections.AddModifierFixFactory import org.jetbrains.kotlin.idea.inspections.InfixCallFixActionFactory import org.jetbrains.kotlin.idea.inspections.PlatformUnresolvedProvider import org.jetbrains.kotlin.idea.inspections.RemoveAnnotationFix -import org.jetbrains.kotlin.idea.intentions.AbstractAddAccessorsIntention -import org.jetbrains.kotlin.idea.intentions.AddValVarToConstructorParameterAction -import org.jetbrains.kotlin.idea.intentions.ConvertPropertyInitializerToGetterIntention -import org.jetbrains.kotlin.idea.intentions.MoveMemberToCompanionObjectIntention +import org.jetbrains.kotlin.idea.intentions.* import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.* import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromCallWithConstructorCalleeActionFactory import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createClass.CreateClassFromConstructorCallActionFactory @@ -154,6 +151,9 @@ class QuickFixRegistrar : QuickFixContributor { FORBIDDEN_BINARY_MOD.registerFactory(removeModifierFactory) FORBIDDEN_BINARY_MOD.registerFactory(RenameModToRemFix.Factory) + NO_EXPLICIT_RETURN_TYPE_IN_API_MODE.registerActions(SpecifyTypeExplicitlyFix()) + NO_EXPLICIT_RETURN_TYPE_IN_API_MODE_MIGRATION.registerActions(SpecifyTypeExplicitlyFix()) + UNRESOLVED_REFERENCE.registerFactory(ImportFix) UNRESOLVED_REFERENCE.registerFactory(ImportConstructorReferenceFix)