All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.fir.analysis.checkers.extended.RedundantVisibilityModifierChecker.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
 * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
 */

package org.jetbrains.kotlin.fir.analysis.checkers.extended

import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirFakeSourceElement
import org.jetbrains.kotlin.fir.FirFakeSourceElementKind
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.fir.analysis.checkers.*
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirBasicDeclarationChecker
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
import org.jetbrains.kotlin.fir.analysis.getChild
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens

object RedundantVisibilityModifierChecker : FirBasicDeclarationChecker() {
    override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) {
        if (declaration is FirConstructor && declaration.source?.kind is FirFakeSourceElementKind) return
        if (declaration.source is FirFakeSourceElement<*>) return
        if (
            declaration !is FirMemberDeclaration
            && !(declaration is FirPropertyAccessor && declaration.visibility == context.containingPropertyVisibility)
        ) return

        val modifiers = declaration.source.getModifierList()
        val visibilityModifier = when (modifiers) {
            is FirPsiModifierList -> modifiers.modifierList.getVisibility()
            is FirLightModifierList -> modifiers.modifiers.visibilityOrNull()
            else -> null
        } ?: return
        val implicitVisibility = declaration.implicitVisibility(context)
        val containingMemberDeclaration = context.findClosest()

        val redundantVisibility = when {
            visibilityModifier == implicitVisibility -> implicitVisibility
            modifiers?.modifiers.hasModifier(KtTokens.INTERNAL_KEYWORD) &&
                    containingMemberDeclaration.let { decl ->
                        decl != null && (decl.isLocalMember || modifiers?.modifiers.hasModifier(KtTokens.PRIVATE_KEYWORD))
                    } -> Visibilities.Internal
            else -> return
        }

        if (
            redundantVisibility == Visibilities.Public
            && declaration is FirProperty
            && modifiers?.modifiers.hasModifier(KtTokens.OVERRIDE_KEYWORD)
            && declaration.isVar
            && declaration.setter?.visibility == Visibilities.Public
        ) return

        reporter.report(declaration.source?.getChild(KtTokens.VISIBILITY_MODIFIERS), FirErrors.REDUNDANT_VISIBILITY_MODIFIER)
    }

    private fun FirDeclaration.implicitVisibility(context: CheckerContext): Visibility {
        return when {
            this is FirPropertyAccessor && isSetter && status.isOverride -> this.visibility

            this is FirPropertyAccessor -> {
                context.findClosest()?.visibility ?: Visibilities.DEFAULT_VISIBILITY
            }

            this is FirConstructor -> {
                val clazz = this.getContainingClass(context)
                if (
                    clazz is FirClass
                    && (clazz.classKind == ClassKind.ENUM_CLASS || clazz.modality() == Modality.SEALED)
                ) {
                    Visibilities.Private
                } else {
                    Visibilities.DEFAULT_VISIBILITY
                }
            }

            this is FirSimpleFunction
                    && context.containingDeclarations.last() is FirClass<*>
                    && this.isOverride -> findFunctionVisibility(this, context)

            else -> Visibilities.DEFAULT_VISIBILITY
        }
    }

    private fun findFunctionVisibility(function: FirSimpleFunction, context: CheckerContext): Visibility {
        val currentClass = context.findClosestClassOrObject() ?: return Visibilities.Unknown
        val overriddenFunctions = function.overriddenFunctions(currentClass, context)
        var visibility: Visibility = Visibilities.Private
        for (func in overriddenFunctions) {
            val currentVisibility = func.fir.visibility()
            if (currentVisibility != null) {
                val compareResult = Visibilities.compare(currentVisibility, visibility)
                if (compareResult != null && compareResult > 0) {
                    visibility = currentVisibility
                }
            }
        }

        return visibility
    }

    private fun FirFunction<*>.visibility(): Visibility? {
        (symbol.fir as? FirMemberDeclaration)?.visibility?.let {
            return it
        }

        (symbol.fir as? FirPropertyAccessor)?.visibility?.let {
            return it
        }

        return null
    }

    private val FirMemberDeclaration.isLocalMember: Boolean
        get() = when (this) {
            is FirProperty -> this.isLocal
            is FirRegularClass -> this.isLocal
            is FirSimpleFunction -> this.isLocal
            else -> false
        }

    private val CheckerContext.containingPropertyVisibility
        get() = (this.containingDeclarations.last() as? FirProperty)?.visibility

    private fun List.visibilityOrNull() =
        firstOrNull { it.token.toVisibilityOrNull() != null }?.token?.toVisibilityOrNull()

    private fun List>?.hasModifier(token: KtModifierKeywordToken) = this != null && any { it.token == token }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy