blob: 6bd7c9598764e0b357921d1878f6697d1fdb4402 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.fragment.lint
import com.android.tools.lint.detector.api.JavaContext
import com.intellij.psi.PsiType
import com.intellij.psi.util.PsiTypesUtil
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UQualifiedReferenceExpression
/**
* Checks if the [PsiType] is a subclass of class with canonical name [superName].
*
* @param context The context of the lint request.
* @param superName The canonical name to check that the [PsiType] is a subclass of.
* @param strict Whether [superName] is inclusive.
*/
internal fun PsiType?.extends(
context: JavaContext,
superName: String,
strict: Boolean = false
): Boolean = context.evaluator.extendsClass(PsiTypesUtil.getPsiClass(this), superName, strict)
/**
* Walks up the uastParent hierarchy from this element.
*/
internal fun UElement.walkUp(): Sequence<UElement> = generateSequence(uastParent) { it.uastParent }
/**
* This is useful if you're in a nested call expression and want to find the nearest parent while ignoring this call.
*
* For example, if you have the following two cases of a `foo()` expression:
* - `checkNotNull(fragment.foo())`
* - `checkNotNull(foo())` // if foo() is a local function
*
* Calling this from `foo()` in both cases will drop you at the outer `checkNotNull()` expression.
*/
val UElement.nearestNonQualifiedReferenceParent: UElement?
get() = walkUp().first {
it !is UQualifiedReferenceExpression
}
/**
* @see [fullyQualifiedNearestParentOrNull]
*/
internal fun UElement.fullyQualifiedNearestParent(includeSelf: Boolean = true): UElement {
return fullyQualifiedNearestParentOrNull(includeSelf)!!
}
/**
* Given an element, returns the nearest fully qualified parent.
*
* Examples where [this] is a `UCallExpression` representing `bar()`:
* - `Foo.bar()` -> `Foo.bar()`
* - `bar()` -> `bar()`
*
* @param includeSelf Whether or not to include [this] element in the checks.
*/
internal fun UElement.fullyQualifiedNearestParentOrNull(includeSelf: Boolean = true): UElement? {
val node = if (includeSelf) this else uastParent ?: return null
return if (node is UQualifiedReferenceExpression) {
node.uastParent
} else {
node
}
}