Skip to content

Commit

Permalink
(WIP) replace analyze by withKaSession
Browse files Browse the repository at this point in the history
  • Loading branch information
Godin committed Nov 29, 2024
1 parent d41920b commit 1cfbd22
Show file tree
Hide file tree
Showing 56 changed files with 168 additions and 171 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ import org.sonar.api.utils.Version
import org.sonarsource.kotlin.api.frontend.KotlinFileContext
import org.sonarsource.kotlin.api.reporting.KotlinTextRanges.merge
import org.sonarsource.kotlin.api.reporting.KotlinTextRanges.textRange
import org.sonarsource.kotlin.api.visiting.analyze
import org.sonarsource.kotlin.api.visiting.withKaSession

annotation class Rewritten

Expand All @@ -96,15 +96,15 @@ fun KtExpression.predictRuntimeStringValue() =
predictRuntimeValueExpression().stringValue()

@Rewritten
fun KtExpression.predictRuntimeIntValue(): Int? = analyze {
fun KtExpression.predictRuntimeIntValue(): Int? = withKaSession {
val valueExpression = predictRuntimeValueExpression()
if (valueExpression.expressionType?.isIntType == true) {
valueExpression?.evaluate()?.value as? Int
} else null
}

@Rewritten
fun KtExpression.predictRuntimeBooleanValue() = analyze {
fun KtExpression.predictRuntimeBooleanValue() = withKaSession {

val valueExpression = predictRuntimeValueExpression()
if (valueExpression.expressionType?.isBooleanType == true) {
Expand Down Expand Up @@ -168,15 +168,15 @@ fun KtExpression.predictRuntimeValueExpression(
declarations
)

is KtThisExpression -> analyze {
is KtThisExpression -> withKaSession {
var symbol = deparenthesized.instanceReference.mainReference.resolveToSymbol()
// In K1 the symbol is anonymous function symbol, in K2 it is a parameter
if (symbol !is KaAnonymousFunctionSymbol) symbol = symbol?.containingSymbol
symbol?.findFunctionLiteral(deparenthesized)
?.findLetAlsoRunWithTargetExpression()
}

else -> analyze {
else -> withKaSession {
deparenthesized.resolveToCall()?.successfulFunctionCallOrNull()?.predictValueExpression()
}
} ?: deparenthesized as? KtExpression
Expand All @@ -201,7 +201,7 @@ fun KtCallExpression.predictReceiverExpression(
@Rewritten
fun KtExpression.predictReceiverExpression(
): KtExpression? =
analyze {
withKaSession {
val call = this@predictReceiverExpression.resolveToCall()?.successfulFunctionCallOrNull()
val symbol = call?.partiallyAppliedSymbol
val receiver = symbol?.extensionReceiver ?: symbol?.dispatchReceiver
Expand Down Expand Up @@ -277,7 +277,7 @@ private fun KtExpression.stringValue(
@Rewritten
fun KtExpression.stringValue(
declarations: MutableList<PsiElement> = mutableListOf(),
): String? = analyze {
): String? = withKaSession {
when (this@stringValue) {
is KtStringTemplateExpression -> {
val entries = entries.map {
Expand Down Expand Up @@ -332,7 +332,7 @@ private fun KtReferenceExpression.extractFromInitializer(
@Rewritten
private fun KtReferenceExpression.extractFromInitializer(
declarations: MutableList<PsiElement> = mutableListOf(),
) = analyze {
) = withKaSession {
(this@extractFromInitializer.mainReference.resolveToSymbol() as? KaVariableSymbol)
?.let {
if (it.isVal) {
Expand Down Expand Up @@ -379,7 +379,7 @@ private fun KaImplicitReceiverValue.findReceiverScopeFunctionLiteral(

@Rewritten
private fun KtReferenceExpression.findReceiverScopeFunctionLiteral(): KtFunctionLiteral? =
analyze {
withKaSession {
when (val resolvedSymbol = this@findReceiverScopeFunctionLiteral.mainReference.resolveToSymbol()) {
is KaValueParameterSymbol -> resolvedSymbol.containingSymbol?.findFunctionLiteral(
this@findReceiverScopeFunctionLiteral
Expand Down Expand Up @@ -410,9 +410,9 @@ private fun KtFunctionLiteral.findLetAlsoRunWithTargetExpression(bindingContext:

@Rewritten
private fun KtFunctionLiteral.findLetAlsoRunWithTargetExpression(): KtExpression? =
analyze {
withKaSession {
(getParentCall() as? KaFunctionCall<*>)?.let { larwCallCandidate ->
analyze {
withKaSession {
when (larwCallCandidate.partiallyAppliedSymbol.symbol.name?.asString()) {
in KOTLIN_CHAIN_CALL_CONSTRUCTS -> {
(larwCallCandidate.partiallyAppliedSymbol.extensionReceiver as? KaExplicitReceiverValue)?.expression?.predictRuntimeValueExpression()
Expand All @@ -439,7 +439,7 @@ fun KtElement.getParentCall(): KaCall? {
KtUnaryExpression::class.java, KtArrayAccessExpression::class.java
)
val parentOfType = PsiTreeUtil.getParentOfType(this, *callExpressionTypes)
return analyze { parentOfType?.resolveToCall()?.successfulCallOrNull() }
return withKaSession { parentOfType?.resolveToCall()?.successfulCallOrNull() }
}


Expand Down Expand Up @@ -470,7 +470,7 @@ private fun DeclarationDescriptor.findFunctionLiteral(
@Rewritten
private fun KaSymbol.findFunctionLiteral(
startNode: PsiElement,
): KtFunctionLiteral? = analyze {
): KtFunctionLiteral? = withKaSession {
var curNode: PsiElement? = startNode
for (i in 0 until MAX_AST_PARENT_TRAVERSALS) {
curNode = curNode?.parent ?: break
Expand Down Expand Up @@ -534,7 +534,7 @@ fun KtParameter.determineTypeAsString(bindingContext: BindingContext, printTypeA
/**
* TODO see also [FunMatcherImpl.checkReturnType]
*/
fun KtDeclaration.determineTypeAsString() = analyze {
fun KtDeclaration.determineTypeAsString() = withKaSession {
(this@determineTypeAsString.returnType as? KaClassType)?.classId?.asFqNameString()
}

Expand All @@ -545,13 +545,13 @@ private fun KtTypeReference.determineType(bindingContext: BindingContext) =
fun KtTypeReference.determineTypeAsString(bindingContext: BindingContext, printTypeArguments: Boolean = false) =
determineType(bindingContext)?.getKotlinTypeFqName(printTypeArguments)

fun KtTypeReference.determineTypeAsString() = analyze {
fun KtTypeReference.determineTypeAsString() = withKaSession {
this@determineTypeAsString.type.symbol?.classId?.asFqNameString()
}

fun KtNamedFunction.returnTypeAsString(): String? {
val namedFunction = this
analyze {
withKaSession {
when (val returnType = namedFunction.returnType) {
is KaClassType -> return returnType.classId.asFqNameString()
else -> return null
Expand All @@ -572,7 +572,7 @@ fun KtNamedFunction.isInfix() = hasModifier(KtTokens.INFIX_KEYWORD)
* Checks whether the expression is a call, matches the FunMatchers in [STRING_TO_BYTE_FUNS] and is called on a constant string value.
*/
@Rewritten
fun KtExpression.isBytesInitializedFromString() = analyze {
fun KtExpression.isBytesInitializedFromString() = withKaSession {
val resolveToCall = this@isBytesInitializedFromString.resolveToCall()

val functionCall = resolveToCall?.successfulFunctionCallOrNull()
Expand Down Expand Up @@ -657,7 +657,7 @@ fun KtExpression.isInitializedPredictably(searchStartNode: KtExpression, binding
@Rewritten
fun KtExpression.isInitializedPredictably(searchStartNode: KtExpression): Boolean {
return this !is KtNameReferenceExpression || this.findUsages(searchStartNode) {
analyze {
withKaSession {
it.getParentOfType<KtCallExpression>(false)?.resolveToCall()
?.successfulFunctionCallOrNull() matches SECURE_RANDOM_FUNS
}
Expand All @@ -676,7 +676,7 @@ fun KtExpression?.isLocalVariable(bindingContext: BindingContext) =
fun KtExpression?.isLocalVariable(): Boolean {
if (this !is KtNameReferenceExpression) return false
val expression = this
analyze {
withKaSession {
return expression.mainReference.resolveToSymbol() is KaLocalVariableSymbol
}
}
Expand All @@ -697,7 +697,7 @@ fun KtExpression?.setterMatches(
fun KtExpression?.setterMatches(
propertyName: String,
matcher: FunMatcherImpl
): Boolean = analyze {
): Boolean = withKaSession {
when (this@setterMatches) {
is KtNameReferenceExpression -> (getReferencedName() == propertyName) &&
(matcher.matches(this@setterMatches.resolveToCall()?.successfulVariableAccessCall() ?: return false))
Expand Down Expand Up @@ -781,7 +781,7 @@ fun PsiElement?.determineType(bindingContext: BindingContext): KotlinType? =

}

fun PsiElement?.determineType(): KaType? = analyze {
fun PsiElement?.determineType(): KaType? = withKaSession {
this?.let {
when (this@determineType) {
is KtCallExpression -> determineTypeFromCall()
Expand All @@ -803,7 +803,7 @@ fun PsiElement?.determineType(): KaType? = analyze {
}
}

private fun KtElement.determineTypeFromCall(): KaType? = analyze {
private fun KtElement.determineTypeFromCall(): KaType? = withKaSession {
this@determineTypeFromCall.resolveToCall()?.let {
(it.successfulFunctionCallOrNull() ?: it.singleVariableAccessCall())
?.partiallyAppliedSymbol?.symbol?.returnType
Expand Down Expand Up @@ -838,7 +838,7 @@ fun SensorContext.hasCacheEnabled(): Boolean {
}

@OptIn(KaIdeApi::class)
fun KtWhenExpression.isExhaustive(context: KotlinFileContext): Boolean = analyze {
fun KtWhenExpression.isExhaustive(context: KotlinFileContext): Boolean = withKaSession {
return entries.any { it.isElse } || this@isExhaustive.computeMissingCases().isEmpty()
// return entries.any { it.isElse } || context.bindingContext[BindingContext.EXHAUSTIVE_WHEN, this] == true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.sonarsource.kotlin.api.frontend.KotlinFileContext
import org.sonarsource.kotlin.api.visiting.analyze
import org.sonarsource.kotlin.api.visiting.withKaSession

abstract class CallAbstractCheck : AbstractCheck() {
abstract val functionsToVisit: Iterable<FunMatcherImpl>
Expand All @@ -48,7 +48,7 @@ abstract class CallAbstractCheck : AbstractCheck() {
open fun visitFunctionCall(callExpression: KtCallExpression, resolvedCall: KaFunctionCall<*>, kotlinFileContext: KotlinFileContext) = Unit

final override fun visitCallExpression(callExpression: KtCallExpression, kotlinFileContext: KotlinFileContext) {
analyze {
withKaSession {
val resolvedCall = callExpression.resolveToCall()?.singleFunctionCallOrNull() ?: return
functionsToVisit.firstOrNull { it.matches(resolvedCall) }
?.let { visitFunctionCall(callExpression, resolvedCall, it, kotlinFileContext) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
import org.jetbrains.kotlin.resolve.descriptorUtil.overriddenTreeUniqueAsSequence
import org.sonarsource.kotlin.api.visiting.analyze
import org.sonarsource.kotlin.api.visiting.withKaSession

private const val VARARG_PREFIX = "vararg ";

Expand Down Expand Up @@ -73,7 +73,7 @@ class FunMatcherImpl(
matches(bindingContext[RESOLVED_CALL, call]?.resultingDescriptor)
}

fun matches(node: KtCallExpression): Boolean = analyze {
fun matches(node: KtCallExpression): Boolean = withKaSession {
val call = node.resolveToCall()?.successfulFunctionCallOrNull()
return call != null && matches(call)
}
Expand All @@ -85,7 +85,7 @@ class FunMatcherImpl(
}

@OptIn(KaExperimentalApi::class)
fun matches(node: KtNamedFunction): Boolean = analyze {
fun matches(node: KtNamedFunction): Boolean = withKaSession {
// TODO try node.resolveToCall
return matches(null, node.symbol.asSignature())
}
Expand Down Expand Up @@ -211,7 +211,7 @@ class FunMatcherImpl(
}
}

private fun checkSubType(functionDescriptor: KaCallableSignature<*>): Boolean = analyze {
private fun checkSubType(functionDescriptor: KaCallableSignature<*>): Boolean = withKaSession {
if (functionDescriptor.symbol is KaConstructorSymbol) return false
return functionDescriptor.symbol.allOverriddenSymbols.any {
val className: String? = it.callableId?.asSingleFqName()?.parent()?.asString()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import org.sonarsource.kotlin.api.frontend.TextRangeTracker
import org.sonarsource.kotlin.api.checks.isPlus as isConcat
import org.sonarsource.kotlin.api.reporting.SecondaryLocation
import org.sonarsource.kotlin.api.reporting.KotlinTextRanges.textRange
import org.sonarsource.kotlin.api.visiting.analyze
import org.sonarsource.kotlin.api.visiting.withKaSession
import java.util.regex.Pattern

val PATTERN_COMPILE_MATCHER = FunMatcher(qualifier = "java.util.regex.Pattern", name = "compile")
Expand Down Expand Up @@ -203,7 +203,7 @@ private fun KtExpression?.extractRegexFlags(): FlagSet =
}
?.flatMap { it.collectDescendantsOfType<KtNameReferenceExpression>() }
?.mapNotNull {
analyze { it.mainReference.resolveToSymbol()?.name?.asString() }
withKaSession { it.mainReference.resolveToSymbol()?.name?.asString() }
}
?.mapNotNull { FLAGS[it] }
?.fold(0, Int::or)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ import org.sonarsource.kotlin.api.checks.InputFileContext
import org.sonarsource.kotlin.api.frontend.KotlinFileContext
import org.sonarsource.kotlin.api.frontend.KotlinTree

@Deprecated("use withKaSession instead", ReplaceWith("withKaSession(action)"))
inline fun <R> analyze(action: KaSession.() -> R) = withKaSession(action)

/**
* Executes the given [action] in a [KaSession] context
* providing access to [Kotlin Analysis API](https://kotl.in/analysis-api).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import org.sonarsource.kotlin.api.checks.FunMatcher
import org.sonarsource.kotlin.api.checks.FunMatcherImpl
import org.sonarsource.kotlin.api.checks.predictReceiverExpression
import org.sonarsource.kotlin.api.frontend.KotlinFileContext
import org.sonarsource.kotlin.api.visiting.analyze
import org.sonarsource.kotlin.api.visiting.withKaSession

private val OBJECT_ARRAY_MATCHER = FunMatcher(qualifier = "kotlin.Array") {
withNames("hashCode", "toString")
Expand Down Expand Up @@ -78,7 +78,7 @@ class ArrayHashCodeAndToStringCheck : CallAbstractCheck() {
}
}

private fun receiverIsArrayOfArray(callExpression: KtCallExpression): Boolean = analyze {
private fun receiverIsArrayOfArray(callExpression: KtCallExpression): Boolean = withKaSession {
val argument = callExpression.predictReceiverExpression()?.expressionType?.arrayElementType
return ARRAY_QUALIFIERS.contains(argument?.symbol?.classId?.asFqNameString())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import org.sonarsource.kotlin.api.checks.*
import org.sonarsource.kotlin.api.reporting.SecondaryLocation
import org.sonarsource.kotlin.api.reporting.KotlinTextRanges.textRange
import org.sonarsource.kotlin.api.frontend.KotlinFileContext
import org.sonarsource.kotlin.api.visiting.analyze
import org.sonarsource.kotlin.api.visiting.withKaSession

private const val BUILDER = "android.security.keystore.KeyGenParameterSpec.Builder"

Expand All @@ -52,14 +52,14 @@ class AuthorisingNonAuthenticatedUsersCheck : CallAbstractCheck() {
matchedFun: FunMatcherImpl,
kotlinFileContext: KotlinFileContext
) {
var receiver = analyze { callExpression.resolveToCall()?.successfulFunctionCallOrNull() } ?: return
var receiver = withKaSession { callExpression.resolveToCall()?.successfulFunctionCallOrNull() } ?: return
var callElement = callExpression
val secondaryLocations = mutableListOf<SecondaryLocation>()

while (!KEY_GEN_BUILDER_MATCHER.matches(receiver)) {

if (KEY_GEN_BUILDER_SET_AUTH_MATCHER.matches(receiver)) {
analyze {
withKaSession {
if (receiver.argumentMapping.keys.toList().first().predictRuntimeBooleanValue() != false)
return
secondaryLocations.add(SecondaryLocation(kotlinFileContext.textRange(callElement.calleeExpression!!)))
Expand All @@ -72,7 +72,7 @@ class AuthorisingNonAuthenticatedUsersCheck : CallAbstractCheck() {
else -> null
} ?: return

receiver = analyze { receiverExpression?.resolveToCall()?.singleFunctionCallOrNull() } ?: return
receiver = withKaSession { receiverExpression?.resolveToCall()?.singleFunctionCallOrNull() } ?: return
}
kotlinFileContext.reportIssue(callElement.calleeExpression!!,
"Make sure authorizing non-authenticated users to use this key is safe here.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import org.sonarsource.kotlin.api.checks.predictRuntimeStringValue
import org.sonarsource.kotlin.api.checks.predictRuntimeValueExpression

import org.sonarsource.kotlin.api.frontend.KotlinFileContext
import org.sonarsource.kotlin.api.visiting.analyze
import org.sonarsource.kotlin.api.visiting.withKaSession

private val CIPHER_INIT_MATCHER = FunMatcher(qualifier = "javax.crypto.Cipher", name = "init") {
withArguments(INT_TYPE, "java.security.Key", "java.security.spec.AlgorithmParameterSpec")
Expand Down Expand Up @@ -69,7 +69,7 @@ class CipherBlockChainingCheck : CallAbstractCheck() {
}
}

private fun KtExpression.isInitializedWithToByteArray() = analyze {
private fun KtExpression.isInitializedWithToByteArray() = withKaSession {
firstArgumentOfInitializer(IV_PARAMETER_SPEC_MATCHER)
?.predictRuntimeValueExpression()
?.resolveToCall()
Expand All @@ -85,7 +85,7 @@ private fun KtExpression.isCBC() =
?.contains("CBC", ignoreCase = true)
?: false

private fun KtExpression.firstArgumentOfInitializer(matcher: FunMatcherImpl) = analyze {
private fun KtExpression.firstArgumentOfInitializer(matcher: FunMatcherImpl) = withKaSession {
predictRuntimeValueExpression()
.resolveToCall()
?.successfulFunctionCallOrNull()?.let {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import org.sonarsource.kotlin.api.checks.*
import org.sonarsource.kotlin.api.reporting.SecondaryLocation
import org.sonarsource.kotlin.api.reporting.KotlinTextRanges.textRange
import org.sonarsource.kotlin.api.frontend.KotlinFileContext
import org.sonarsource.kotlin.api.visiting.analyze
import org.sonarsource.kotlin.api.visiting.withKaSession

private val CIPHER_INIT_MATCHER = FunMatcher(qualifier = "javax.crypto.Cipher", name = "init") {
withArguments(INT_TYPE, "java.security.Key", "java.security.spec.AlgorithmParameterSpec")
Expand Down Expand Up @@ -82,7 +82,7 @@ private fun generateSecondaryLocations(secondaries: List<PsiElement>, kotlinFile

private fun KtExpression.getByteExpression(secondaries: MutableList<PsiElement>) =
with(predictRuntimeValueExpression(secondaries)) {
analyze {
withKaSession {
this@with.resolveToCall()
?.successfulFunctionCallOrNull()
?.let {
Expand All @@ -92,7 +92,7 @@ private fun KtExpression.getByteExpression(secondaries: MutableList<PsiElement>)
}
}

private fun KtExpression.getGCMExpression(secondaries: MutableList<PsiElement>) = analyze {
private fun KtExpression.getGCMExpression(secondaries: MutableList<PsiElement>) = withKaSession {
predictRuntimeValueExpression()
.resolveToCall()
?.successfulFunctionCallOrNull()
Expand Down
Loading

0 comments on commit 1cfbd22

Please sign in to comment.