Skip to content

Commit

Permalink
Ignore external interfaces when triggering `EXTENSION_FUNCTION_WITH…
Browse files Browse the repository at this point in the history
…_CLASS` (#1630)

### What's done:

 - Fixes #1586
  • Loading branch information
0x6675636b796f75676974687562 authored Mar 17, 2023
1 parent 82ee915 commit fceabd5
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@ import com.pinterest.ktlint.core.ast.ElementType.IDENTIFIER
import com.pinterest.ktlint.core.ast.ElementType.TYPE_REFERENCE
import com.pinterest.ktlint.core.ast.prevSibling
import org.jetbrains.kotlin.com.intellij.lang.ASTNode
import org.jetbrains.kotlin.com.intellij.psi.PsiElement
import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
import org.jetbrains.kotlin.lexer.KtTokens.EXTERNAL_KEYWORD
import org.jetbrains.kotlin.psi.KtClass
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.psiUtil.allChildren

/**
* This rule checks if there are any extension functions for the class in the same file, where it is defined
Expand All @@ -35,11 +40,29 @@ class ExtensionFunctionsInFileRule(configRules: List<RulesConfig>) : DiktatRule(
}
}

@Suppress("UnsafeCallOnNullableType")
private fun collectAllClassNames(node: ASTNode): List<String> {
val classes = node.findAllDescendantsWithSpecificType(CLASS)
/**
* Collects all class names in the [file], except those with modifiers from
* the [ignore list][ignoredModifierTypes].
*
* @throws IllegalArgumentException if [file] is not a
* [FILE][ElementType.FILE] node.
*/
private fun collectAllClassNames(file: ASTNode): List<String> {
require(file.elementType == ElementType.FILE)

val classes = file.findAllDescendantsWithSpecificType(CLASS)

return classes.map { (it.psi as KtClass).name!! }
return classes.asSequence()
.map(ASTNode::getPsi)
.filterIsInstance(KtClass::class.java)
.filter { clazz ->
clazz.modifierTypes().none { modifierType ->
modifierType in ignoredModifierTypes
}
}
.map(KtClass::getName)
.filterNotNull()
.toList()
}

private fun fireWarning(node: ASTNode) {
Expand All @@ -55,5 +78,30 @@ class ExtensionFunctionsInFileRule(configRules: List<RulesConfig>) : DiktatRule(

companion object {
const val NAME_ID = "extension-functions-class-file"

/**
* Types of class/interface modifiers which, if present, don't trigger
* the warning.
*
* @since 1.2.5
*/
private val ignoredModifierTypes: Array<out KtModifierKeywordToken> = arrayOf(
EXTERNAL_KEYWORD,
)

/**
* @since 1.2.5
*/
private fun KtClass.modifiers(): Sequence<PsiElement> =
modifierList?.allChildren ?: emptySequence()

/**
* @since 1.2.5
*/
private fun KtClass.modifierTypes(): Sequence<KtModifierKeywordToken> =
modifiers()
.filterIsInstance<LeafPsiElement>()
.map(LeafPsiElement::getElementType)
.filterIsInstance<KtModifierKeywordToken>()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,23 @@ class ExtensionFunctionsInFileWarnTest : LintTestBase(::ExtensionFunctionsInFile
""".trimMargin()
)
}

/**
* See [#1586](https://github.com/saveourtool/diktat/issues/1586).
*
* @since 1.2.5
*/
@Test
@Tag(EXTENSION_FUNCTION_WITH_CLASS)
fun `should raise no warnings for external interfaces`() {
lintMethod(
"""
|internal external interface External {
| val a: Int
|}
|
|fun External.extension() = println(a)
""".trimMargin()
)
}
}

0 comments on commit fceabd5

Please sign in to comment.