|
17 | 17 | package org.sonarsource.kotlin.checks
|
18 | 18 |
|
19 | 19 | import com.intellij.psi.PsiElement
|
20 |
| -import org.jetbrains.kotlin.analysis.api.symbols.* |
| 20 | +import org.jetbrains.kotlin.analysis.api.symbols.KaClassKind |
| 21 | +import org.jetbrains.kotlin.analysis.api.symbols.KaClassSymbol |
| 22 | +import org.jetbrains.kotlin.analysis.api.symbols.KaFunctionSymbol |
| 23 | +import org.jetbrains.kotlin.analysis.api.symbols.name |
21 | 24 | import org.jetbrains.kotlin.analysis.api.types.KaType
|
22 | 25 | import org.jetbrains.kotlin.analysis.api.types.KaTypeParameterType
|
23 | 26 | import org.jetbrains.kotlin.analysis.api.types.symbol
|
24 |
| -import org.jetbrains.kotlin.psi.* |
| 27 | +import org.jetbrains.kotlin.psi.KtBlockExpression |
| 28 | +import org.jetbrains.kotlin.psi.KtCallExpression |
| 29 | +import org.jetbrains.kotlin.psi.KtClass |
| 30 | +import org.jetbrains.kotlin.psi.KtClassOrObject |
| 31 | +import org.jetbrains.kotlin.psi.KtDotQualifiedExpression |
| 32 | +import org.jetbrains.kotlin.psi.KtNameReferenceExpression |
| 33 | +import org.jetbrains.kotlin.psi.KtNamedFunction |
| 34 | +import org.jetbrains.kotlin.psi.KtParameter |
| 35 | +import org.jetbrains.kotlin.psi.KtReturnExpression |
| 36 | +import org.jetbrains.kotlin.psi.KtValueArgument |
25 | 37 | import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
26 | 38 | import org.jetbrains.kotlin.psi.psiUtil.isPublic
|
27 | 39 | import org.sonar.check.Rule
|
28 |
| -import org.sonarsource.kotlin.api.checks.* |
| 40 | +import org.sonarsource.kotlin.api.checks.AbstractCheck |
| 41 | +import org.sonarsource.kotlin.api.checks.allPaired |
| 42 | +import org.sonarsource.kotlin.api.checks.overrides |
29 | 43 | import org.sonarsource.kotlin.api.frontend.KotlinFileContext
|
30 | 44 | import org.sonarsource.kotlin.api.visiting.withKaSession
|
31 | 45 |
|
32 | 46 | @Rule(key = "S6514")
|
33 | 47 | class DelegationPatternCheck : AbstractCheck() {
|
34 | 48 |
|
35 |
| - override fun visitClassOrObject(classOrObject: KtClassOrObject, context: KotlinFileContext) { |
36 |
| - withKaSession { |
37 |
| - val classSymbol = classOrObject.classSymbol ?: return |
38 |
| - if (classSymbol.classKind == KaClassKind.INTERFACE) return |
39 |
| - val superInterfaces: Set<KaClassSymbol> = classSymbol.getSuperInterfaces() |
| 49 | + override fun visitClassOrObject(classOrObject: KtClassOrObject, context: KotlinFileContext) = withKaSession { |
| 50 | + val classSymbol = classOrObject.classSymbol ?: return |
| 51 | + if (classSymbol.classKind == KaClassKind.INTERFACE) return |
| 52 | + val superInterfaces: Set<KaClassSymbol> = classSymbol.getSuperInterfaces() |
| 53 | + if (superInterfaces.isEmpty()) return |
40 | 54 |
|
41 |
| - classOrObject.declarations.forEach { |
42 |
| - if (it is KtNamedFunction) { |
43 |
| - checkNamedFunction(it, context, superInterfaces) |
44 |
| - } |
| 55 | + classOrObject.declarations.forEach { |
| 56 | + if (it is KtNamedFunction) { |
| 57 | + checkNamedFunction(it, superInterfaces, context) |
45 | 58 | }
|
46 | 59 | }
|
47 | 60 | }
|
48 | 61 |
|
49 |
| - private fun checkNamedFunction( |
50 |
| - function: KtNamedFunction, |
51 |
| - context: KotlinFileContext, |
52 |
| - superInterfaces: Set<KaClassSymbol> |
53 |
| - ) { |
| 62 | + private fun checkNamedFunction(function: KtNamedFunction, superInterfaces: Set<KaClassSymbol>, context: KotlinFileContext) = withKaSession { |
54 | 63 | if (!(function.isPublic && function.overrides())) return
|
55 |
| - val delegeeType1 = getDelegeeOrNull(function)?.determineType() ?: return |
| 64 | + val delegeeType = getDelegeeOrNull(function)?.expressionType ?: return |
56 | 65 |
|
57 |
| - val commonSuperInterfaces = getCommonSuperInterfaces(superInterfaces, delegeeType1) |
58 |
| - |
59 |
| - if (commonSuperInterfaces.any { |
60 |
| - isFunctionInInterface(function, it) |
61 |
| - }) { |
| 66 | + if (getCommonSuperInterfaces(superInterfaces, delegeeType).any { |
| 67 | + isFunctionInInterface(function, it) |
| 68 | + }) { |
62 | 69 | context.reportIssue(function.nameIdentifier!!, """Replace with interface delegation using "by" in the class header.""")
|
63 | 70 | }
|
64 | 71 | }
|
65 | 72 | }
|
66 | 73 |
|
67 |
| -private fun isFunctionInInterface( |
68 |
| - function: KtNamedFunction, |
69 |
| - superInterface1: KaClassSymbol |
70 |
| -): Boolean = withKaSession { |
71 |
| - val classDeclaration = superInterface1.psi as? KtClass ?: return false |
72 |
| - return classDeclaration.declarations.any { |
73 |
| - it is KtNamedFunction && haveCompatibleFunctionSignature(it.symbol, function.symbol) |
| 74 | +private fun isFunctionInInterface(function: KtNamedFunction, superInterface: KaClassSymbol): Boolean = withKaSession { |
| 75 | + superInterface.declaredMemberScope.declarations.any { |
| 76 | + it is KaFunctionSymbol && haveCompatibleFunctionSignature(it, function.symbol) |
74 | 77 | }
|
75 | 78 | }
|
76 | 79 |
|
@@ -123,7 +126,7 @@ private fun getDelegeeOrNull(function: KtNamedFunction): KtNameReferenceExpressi
|
123 | 126 | private fun isDelegatedParameter(parameter: KtParameter, arguments: KtValueArgument): Boolean = withKaSession {
|
124 | 127 | val argumentExpression = arguments.getArgumentExpression() as? KtNameReferenceExpression ?: return false
|
125 | 128 | if (parameter.name != argumentExpression.getReferencedName()) return false
|
126 |
| - val argumentType = argumentExpression.determineType() ?: return false |
| 129 | + val argumentType = argumentExpression.expressionType ?: return false |
127 | 130 | return parameter.symbol.returnType.semanticallyEquals(argumentType)
|
128 | 131 | }
|
129 | 132 |
|
|
0 commit comments