Other code review changes

This commit is contained in:
Yan Zhulanow
2016-03-31 22:19:13 +03:00
parent 991e783ccf
commit 6b30b8ce1f
194 changed files with 2538 additions and 3079 deletions
@@ -16,15 +16,28 @@
@file:JvmName("UastUtils")
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastClassKind
import org.jetbrains.uast.visitor.UastVisitor
internal val ERROR_NAME = "<error>"
/**
* Returns the containing class of an element.
*
* @return the containing [UClass] element,
* or null if the receiver is null, or it is a top-level declaration.
*/
tailrec fun UElement?.getContainingClass(): UClass? {
val parent = this?.parent ?: return null
if (parent is UClass) return parent
return parent.getContainingClass()
}
/**
* Returns the containing file of an element.
*
* @return the containing [UFile] element,
* or null if the receiver is null, or the element is not inside a [UFile] (it is abmormal).
*/
tailrec fun UElement?.getContainingFile(): UFile? {
val parent = this?.parent ?: return null
if (parent is UFile) return parent
@@ -33,20 +46,45 @@ tailrec fun UElement?.getContainingFile(): UFile? {
fun UElement?.getContainingClassOrEmpty() = getContainingClass() ?: UClassNotResolved
/**
* Returns the containing function of an element.
*
* @return the containing [UFunction] element,
* or null if the receiver is null, or the element is not inside a [UFunction].
*/
tailrec fun UElement?.getContainingFunction(): UFunction? {
val parent = this?.parent ?: return null
if (parent is UFunction) return parent
return parent.getContainingFunction()
}
/**
* Returns the containing declaration of an element.
*
* @return the containing [UDeclaration] element,
* or null if the receiver is null, or the element is a top-level declaration.
*/
tailrec fun UElement?.getContainingDeclaration(): UDeclaration? {
val parent = this?.parent ?: return null
if (parent is UDeclaration) return parent
return parent.getContainingDeclaration()
}
/**
* Checks if the element is a top-level declaration.
*
* @return true if the element is a top-level declaration, false otherwise.
*/
fun UDeclaration.isTopLevel() = parent is UFile
/**
* Builds the log message for the [UElement.logString] function.
*
* @param firstLine the message line (the interface name, some optional information).
* @param nested nested UElements. Could be `List<UElement>`, [UElement] or `null`.
* @throws IllegalStateException if the [nested] argument is invalid.
* @return the rendered log string.
*/
fun UElement.log(firstLine: String, vararg nested: Any?): String {
return (if (firstLine.isBlank()) "" else firstLine + "\n") + nested.joinToString("\n") {
when (it) {
@@ -74,12 +112,27 @@ fun UClass.findFunctions(name: String) = declarations.filter { it is UFunction &
fun UCallExpression.getReceiver(): UExpression? = (this.parent as? UQualifiedExpression)?.receiver
/**
* Resolves the receiver element if it implements [UResolvable].
*
* @return the resolved element, or null if the element was not resolved, or if the receiver element is not an [UResolvable].
*/
fun UElement.resolveIfCan(context: UastContext): UDeclaration? = (this as? UResolvable)?.resolve(context)
fun UElement.isThrow() = this is USpecialExpressionList && this.kind == UastSpecialExpressionKind.THROW
/**
* Find an annotation with the required qualified name.
*
* @param fqName the qualified name to search
* @return [UAnnotation] element if the annotation with the specified [fqName] was found, null otherwise.
*/
fun UAnnotated.findAnnotation(fqName: String) = annotations.firstOrNull { it.fqName == fqName }
/**
* Get all class declarations (including supertypes).
*
* @param context the Uast context
* @return the list of declarations for the receiver class
*/
fun UClass.getAllDeclarations(context: UastContext): List<UDeclaration> = mutableListOf<UDeclaration>().apply {
this += declarations
for (superType in superTypes) {
@@ -107,14 +160,6 @@ fun UCallExpression.getQualifiedCallElement(): UExpression {
return findParent(parent as? UExpression) ?: this
}
val UDeclaration.fqName: String
get() {
val containingFqName = this.getContainingDeclaration()?.fqName
?: this.getContainingFile()?.packageFqName
val containingFqNameWithDot = containingFqName?.let { it + "." } ?: ""
return containingFqNameWithDot + this.name
}
inline fun <reified T: UElement> UElement.getParentOfType(): T? = getParentOfType(T::class.java)
fun <T: UElement> UElement.getParentOfType(clazz: Class<T>): T? {
@@ -133,10 +178,9 @@ fun <T: UElement> UElement.getParentOfType(clazz: Class<T>): T? {
fun <T> UClass.findStaticMemberOfType(name: String, type: Class<out T>): T? {
for (companion in companions) {
val classKind = companion.kind as? UastClassKind.UastCompanionObject ?: continue
if (!classKind.default) continue
val member = companion.declarations.firstOrNull { it.name == name && type.isInstance(it) }
val member = companion.declarations.firstOrNull {
it.name == name && type.isInstance(it) && it is UModifierOwner && it.hasModifier(UastModifier.STATIC)
}
@Suppress("UNCHECKED_CAST")
if (member != null) return member as T
}
@@ -146,4 +190,52 @@ fun <T> UClass.findStaticMemberOfType(name: String, type: Class<out T>): T? {
it.name == name && it is UModifierOwner
&& it.hasModifier(UastModifier.STATIC) && type.isInstance(it)
} as T
}
fun UExpression.asQualifiedIdentifiers(): List<String>? {
var error = false
val list = mutableListOf<String>()
fun addIdentifiers(expr: UQualifiedExpression) {
val receiver = expr.receiver
val selector = expr.selector as? USimpleReferenceExpression ?: run { error = true; return }
when (receiver) {
is UQualifiedExpression -> addIdentifiers(receiver)
is USimpleReferenceExpression -> list += receiver.identifier
else -> {
error = true
return
}
}
list += selector.identifier
}
when (this) {
is UQualifiedExpression -> addIdentifiers(this)
is USimpleReferenceExpression -> listOf(identifier)
else -> return null
}
return if (error) null else list
}
fun UExpression.matchesQualified(fqName: String): Boolean {
val identifiers = this.asQualifiedIdentifiers() ?: return false
val passedIdentifiers = fqName.split('.')
return identifiers == passedIdentifiers
}
fun UExpression.startsWithQualified(fqName: String): Boolean {
val identifiers = this.asQualifiedIdentifiers() ?: return false
val passedIdentifiers = fqName.split('.')
identifiers.forEachIndexed { i, identifier ->
if (identifier != passedIdentifiers[i]) return false
}
return true
}
fun UExpression.endsWithQualified(fqName: String): Boolean {
val identifiers = this.asQualifiedIdentifiers() ?: return false
val passedIdentifiers = fqName.split('.')
identifiers.forEachIndexed { i, identifier ->
if (identifier != passedIdentifiers[i]) return false
}
return true
}
@@ -46,7 +46,6 @@ abstract class UastVisitor {
open fun visitPrefixExpression(node: UPrefixExpression) = visitElement(node)
open fun visitPostfixExpression(node: UPostfixExpression) = visitElement(node)
open fun visitSpecialExpressionList(node: USpecialExpressionList) = visitElement(node)
open fun visitExpressionList(node: UExpressionList) = visitElement(node)
open fun visitIfExpression(node: UIfExpression) = visitElement(node)
open fun visitSwitchExpression(node: USwitchExpression) = visitElement(node)
open fun visitSwitchClauseExpression(node: USwitchClauseExpression) = visitElement(node)
@@ -58,11 +57,66 @@ abstract class UastVisitor {
open fun visitLiteralExpression(node: ULiteralExpression) = visitElement(node)
open fun visitThisExpression(node: UThisExpression) = visitElement(node)
open fun visitSuperExpression(node: USuperExpression) = visitElement(node)
open fun visitReturnExpression(node: UReturnExpression) = visitElement(node)
open fun visitBreakExpression(node: UBreakExpression) = visitElement(node)
open fun visitContinueExpression(node: UContinueExpression) = visitElement(node)
open fun visitThrowExpression(node: UThrowExpression) = visitElement(node)
open fun visitArrayAccessExpression(node: UArrayAccessExpression) = visitElement(node)
open fun visitCallableReferenceExpression(node: UCallableReferenceExpression) = visitElement(node)
open fun visitClassLiteralExpression(node: UClassLiteralExpression) = visitElement(node)
open fun visitLambdaExpression(node: ULambdaExpression) = visitElement(node)
open fun visitObjectLiteralExpression(node: UObjectLiteralExpression) = visitElement(node)
// After
open fun afterVisitElement(node: UElement) {}
open fun afterVisitFile(node: UFile) {}
open fun afterVisitImportStatement(node: UImportStatement) {}
open fun afterVisitAnnotation(node: UAnnotation) {}
open fun afterVisitCatchClause(node: UCatchClause) {}
open fun afterVisitType(node: UType) {}
// Declarations
open fun afterVisitClass(node: UClass) {}
open fun afterVisitFunction(node: UFunction) {}
open fun afterVisitVariable(node: UVariable) {}
// Expressions
open fun afterVisitLabeledExpression(node: ULabeledExpression) {}
open fun afterVisitDeclarationsExpression(node: UDeclarationsExpression) {}
open fun afterVisitBlockExpression(node: UBlockExpression) {}
open fun afterVisitQualifiedExpression(node: UQualifiedExpression) {}
open fun afterVisitSimpleReferenceExpression(node: USimpleReferenceExpression) {}
open fun afterVisitCallExpression(node: UCallExpression) {}
open fun afterVisitBinaryExpression(node: UBinaryExpression) {}
open fun afterVisitBinaryExpressionWithType(node: UBinaryExpressionWithType) {}
open fun afterVisitParenthesizedExpression(node: UParenthesizedExpression) {}
open fun afterVisitUnaryExpression(node: UUnaryExpression) {}
open fun afterVisitPrefixExpression(node: UPrefixExpression) {}
open fun afterVisitPostfixExpression(node: UPostfixExpression) {}
open fun afterVisitSpecialExpressionList(node: USpecialExpressionList) {}
open fun afterVisitIfExpression(node: UIfExpression) {}
open fun afterVisitSwitchExpression(node: USwitchExpression) {}
open fun afterVisitSwitchClauseExpression(node: USwitchClauseExpression) {}
open fun afterVisitWhileExpression(node: UWhileExpression) {}
open fun afterVisitDoWhileExpression(node: UDoWhileExpression) {}
open fun afterVisitForExpression(node: UForExpression) {}
open fun afterVisitForEachExpression(node: UForEachExpression) {}
open fun afterVisitTryExpression(node: UTryExpression) {}
open fun afterVisitLiteralExpression(node: ULiteralExpression) {}
open fun afterVisitThisExpression(node: UThisExpression) {}
open fun afterVisitSuperExpression(node: USuperExpression) {}
open fun afterVisitReturnExpression(node: UReturnExpression) {}
open fun afterVisitBreakExpression(node: UBreakExpression) {}
open fun afterVisitContinueExpression(node: UContinueExpression) {}
open fun afterVisitThrowExpression(node: UThrowExpression) {}
open fun afterVisitArrayAccessExpression(node: UArrayAccessExpression) {}
open fun afterVisitCallableReferenceExpression(node: UCallableReferenceExpression) {}
open fun afterVisitClassLiteralExpression(node: UClassLiteralExpression) {}
open fun afterVisitLambdaExpression(node: ULambdaExpression) {}
open fun afterVisitObjectLiteralExpression(node: UObjectLiteralExpression) {}
}
object EmptyUastVisitor : UastVisitor()
@@ -17,24 +17,59 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
Represents an applied JVM annotation.
*/
interface UAnnotation : UElement, UNamed, UFqNamed {
/**
* Returns the list of named value arguments.
*/
val valueArguments: List<UNamedExpression>
/**
* Returns the class of this annotation.
*
* @return annotation class or null if the class was not resolved
*/
fun resolve(context: UastContext): UClass?
/**
* Returns the evaluated value of the argument with the specified name.
*
* @param name name of the annotation value parameter
* @return the argument value
*/
fun getValue(name: String) = valueArguments.firstOrNull { it.name == name }?.expression?.evaluate()
/**
* Returns the list of Pair(name, evaluatedValue) for all applied annotation arguments.
*
* @return the list of name-value pairs.
*/
fun getValues() = valueArguments.map { Pair(it.name, it.expression.evaluate()) }
override fun logString() = log("UAnnotation ($name)")
override fun renderString() = if (valueArguments.isEmpty()) "@$name" else "@$name(" +
valueArguments.joinToString { it.renderString() } + ")"
override fun renderString(): String {
return if (valueArguments.isEmpty())
"@$name"
else
"@$name(" + valueArguments.joinToString { it.renderString() } + ")"
}
override fun accept(visitor: UastVisitor) {
if (visitor.visitAnnotation(this)) return
valueArguments.acceptList(visitor)
visitor.afterVisitAnnotation(this)
}
}
/**
* Represents an annotated element.
*/
interface UAnnotated : UElement {
/**
* Returns the list of annotations applied to the current element.
*/
val annotations: List<UAnnotation>
}
@@ -17,39 +17,153 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* The common interface for all Uast elements.
*/
interface UElement {
/**
* Returns the element parent.
*/
val parent: UElement?
/**
* Returns true if this element is valid, false otherwise.
*/
open val isValid: Boolean
get() = true
/**
* Returns the log string.
*
* Output example (should be something like this):
* UWhileExpression
* UBinaryExpression (>)
* USimpleReferenceExpression (i)
* ULiteralExpression (5)
* UBlockExpression
* UCallExpression (println)
* ULiteralExpression (ABC)
* UPostfixExpression (--)
* USimpleReferenceExpression(i)
*
* @return the expression tree for this element.
* @see [UIfExpression] for example.
*/
fun logString(): String
/**
* Returns the string in pseudo-code.
*
* Output example (should be something like this):
* while (i > 5) {
* println("Hello, world")
* i--
* }
*
* @return the rendered text.
* @see [UIfExpression] for example.
*/
fun renderString(): String = logString()
/**
* Passes the element to the specified visitor.
*
* @param visitor the visitor to pass the element to.
*/
fun accept(visitor: UastVisitor) {
visitor.visitElement(this)
}
}
/**
* An interface for the [UElement] which has a name.
*/
interface UNamed {
/**
* Returns the name of this element.
*/
val name: String
/**
* Checks if the element name is equal to the passed name.
*
* @param name the name to check against
* @return true if the element name is equal to [name], false otherwise.
*/
fun matchesName(name: String) = this.name == name
}
/**
* An interface for the [UElement] which has a qualified name.
*/
interface UFqNamed : UNamed {
/**
* Returns the qualified name of this element, or null if the qualified name was not resolved.
*/
val fqName: String?
/**
* Checks if the element qualified name is equal to the passed name.
*
* @param fqName qualified name to check against
* @return true if the element name is not null and is equal to [name], false otherwise.
*/
fun matchesFqName(fqName: String) = this.fqName == fqName
}
/**
* An interface for the [UElement] which has Uast modifiers.
*/
interface UModifierOwner {
/**
* Checks if the element has the passed modifier.
*
* @param modifier modifier to check
* @return true if the element has [modifier], false otherwise.
*/
fun hasModifier(modifier: UastModifier): Boolean
}
/**
* An interface for the [UElement] which has a visibility (class, function, variable).
*/
interface UVisibilityOwner {
/**
* Returns the element visibility.
*/
val visibility: UastVisibility
}
/**
* An inteface for the [UElement] which can be resolved to the its declaration.
*/
interface UResolvable {
/**
* Returns the declaration element.
*
* @param context the Uast context
* @return the resolved declaration, or null if the declaration was not resolved.
*/
fun resolve(context: UastContext): UDeclaration?
/**
* Returns the declaration element or an empty Uast declaration element if the declaration was not resolved.
* An empty declaration element should be a singleton.
* [resolveOrEmpty] should not create new elements each time the declaration was not resolved.
*
* @param context the Uast context
* @return the resolved declaration, of an empty error Uast declaration element if the declaration was not resolved.
*
* @see [UDeclarationNotResolved]
*/
fun resolveOrEmpty(context: UastContext): UDeclaration = resolve(context) ?: UDeclarationNotResolved
/**
* Returns the declaration [UClass] element of null if the declaration was not resolved.
*
* @param context the Uast context
* @return the resolved [UClass] element, or null if the class was not resolved,
* or if the resolved [UElement] is not an instance of [UClass].
*/
fun resolveClass(context: UastContext): UClass? = resolve(context) as? UClass
}
@@ -24,6 +24,7 @@ interface UExpression : UElement {
override fun accept(visitor: UastVisitor) {
visitor.visitElement(this)
visitor.afterVisitElement(this)
}
}
@@ -36,6 +37,6 @@ interface NoModifiers : UModifierOwner {
override fun hasModifier(modifier: UastModifier) = false
}
class EmptyExpression(override val parent: UElement) : UExpression {
class EmptyUExpression(override val parent: UElement) : UExpression {
override fun logString() = "EmptyExpression"
}
@@ -17,11 +17,31 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a source file.
* File is the topmost element of the UElement hierarchy.
* Files should not be nested, thus the [parent] property should always return null.
*/
interface UFile: UElement {
/**
* Returns the qualified package name of this file.
* Could be an empty string if the package is "default", or null if the package directive is not present in this file.
*/
val packageFqName: String?
/**
* Returns the list of import statements.
*/
val importStatements: List<UImportStatement>
/**
* Returns the list of declarations in this file (classes, properties, functions, etc).
*/
val declarations: List<UDeclaration>
/**
* Returns list of classes containing in this file.
*/
val classes: List<UClass>
get() = declarations.filterIsInstance<UClass>()
@@ -32,12 +52,13 @@ interface UFile: UElement {
if (visitor.visitFile(this)) return
declarations.acceptList(visitor)
importStatements.acceptList(visitor)
visitor.afterVisitFile(this)
}
override fun logString() = "UFile (package = $packageFqName)\n" + declarations.joinToString("\n") { it.logString().withMargin }
override fun renderString() = buildString {
if (packageFqName != null) {
if (!packageFqName.isNullOrBlank()) {
appendln("package $packageFqName")
appendln()
}
@@ -17,13 +17,26 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represent a
*
* `do {
* // body
* } while (expr)`
*
* loop expression.
*/
interface UDoWhileExpression : ULoopExpression {
/**
* Returns the loop post-condition.
*/
val condition: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitDoWhileExpression(this)) return
condition.accept(visitor)
body.accept(visitor)
visitor.afterVisitDoWhileExpression(this)
}
override fun renderString() = buildString {
@@ -17,8 +17,24 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a
*
* `for (element : collectionOfElements) {
* // body
* }`
*
* loop expression.
*/
interface UForEachExpression : ULoopExpression {
/**
* Returns the loop variable.
*/
val variable: UVariable
/**
* Returns the iterated value (collection, sequence, iterable etc.)
*/
val iteratedValue: UExpression
override fun accept(visitor: UastVisitor) {
@@ -26,6 +42,7 @@ interface UForEachExpression : ULoopExpression {
variable.accept(visitor)
iteratedValue.accept(visitor)
body.accept(visitor)
visitor.afterVisitForEachExpression(this)
}
override fun renderString() = buildString {
@@ -17,9 +17,29 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a
*
* `for (initDeclarations; loopCondition; update) {
* // body
* }`
*
* loop expression.
*/
interface UForExpression : ULoopExpression {
/**
* Returns the [UExpression] containing variable declarations, or null if the are no variables declared.
*/
val declaration: UExpression?
/**
* Returns the loop condition, or null if the condition is empty.
*/
val condition: UExpression?
/**
* Returns the loop update expression(s).
*/
val update: UExpression?
override fun accept(visitor: UastVisitor) {
@@ -28,6 +48,7 @@ interface UForExpression : ULoopExpression {
condition?.accept(visitor)
update?.accept(visitor)
body.accept(visitor)
visitor.afterVisitForExpression(this)
}
override fun renderString() = buildString {
@@ -17,10 +17,40 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents
*
* `if (condition) {
* // do if true
* } else {
* // do if false
* }`
*
* and
*
* `condition : trueExpression ? falseExpression`
*
* condition expressions.
*/
interface UIfExpression : UExpression {
/**
* Returns the condition expression.
*/
val condition: UExpression
/**
* Returns the expression which is executed if the condition is true, or null if the expression is empty.
*/
val thenBranch: UExpression?
/**
* Returns the expression which is executed if the condition is false, or null if the expression is empty.
*/
val elseBranch: UExpression?
/**
* Returns true if the expression is ternary (condition ? trueExpression : falseExpression).
*/
val isTernary: Boolean
override fun accept(visitor: UastVisitor) {
@@ -28,9 +58,11 @@ interface UIfExpression : UExpression {
condition.accept(visitor)
thenBranch?.accept(visitor)
elseBranch?.accept(visitor)
visitor.afterVisitIfExpression(this)
}
override fun logString() = log("UIfExpression", condition, thenBranch, elseBranch)
override fun renderString() = buildString {
if (isTernary) {
append("(" + condition.renderString() + ")")
@@ -42,7 +74,7 @@ interface UIfExpression : UExpression {
append("if (${condition.renderString()}) ")
thenBranch?.let { append(it.renderString()) }
val elseBranch = elseBranch
if (elseBranch != null && elseBranch !is EmptyExpression) {
if (elseBranch != null && elseBranch !is EmptyUExpression) {
if (thenBranch !is UBlockExpression) append(" ")
append("else ")
append(elseBranch.renderString())
@@ -15,6 +15,12 @@
*/
package org.jetbrains.uast
/**
* Represents the loop expression.
*/
interface ULoopExpression : UExpression {
/**
* Returns the loop body [UExpression].
*/
val body: UExpression
}
@@ -17,14 +17,35 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a
*
* ` switch (expression) {
* case value1 -> expr1
* case value2 -> expr2
* ...
* else -> exprElse
* }
*
* conditional expression.
*/
interface USwitchExpression : UExpression {
/*
Returns the expression on which the `switch` expression is performed.
*/
val expression: UExpression?
/*
Returns the switch body.
Body should contain [USwitchClauseExpression] expressions.
*/
val body: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitSwitchExpression(this)) return
expression?.accept(visitor)
body.accept(visitor)
visitor.afterVisitSwitchExpression(this)
}
override fun logString() = log("USwitchExpression", expression, body)
@@ -35,23 +56,47 @@ interface USwitchExpression : UExpression {
}
}
interface USwitchClauseExpression : UExpression
interface UExpressionSwitchClauseExpression : USwitchClauseExpression {
val caseValue: UExpression
/**
* Represents a [USwitchExpression] clause.
* [USwitchClauseExpression] does not contain the clause body,
* and the actual body expression should be the next element in the parent expression list.
*/
interface USwitchClauseExpression : UExpression {
/**
* Returns the list of values for this clause, or null if the are no values for this close
* (for example, for the `else` clause).
*/
val caseValues: List<UExpression>?
override fun accept(visitor: UastVisitor) {
if (visitor.visitSwitchClauseExpression(this)) return
caseValue.accept(visitor)
caseValues?.acceptList(visitor)
visitor.afterVisitSwitchClauseExpression(this)
}
override fun renderString() = caseValue.renderString() + " -> "
override fun logString() = log("UExpressionSwitchClauseExpression", caseValue)
override fun renderString() = (caseValues?.joinToString { it.renderString() } ?: "else") + " -> "
override fun logString() = log("USwitchClauseExpression", caseValues)
}
interface UDefaultSwitchClauseExpression : USwitchClauseExpression {
override fun logString() = "UDefaultSwitchClause"
override fun renderString() = "else -> "
}
/**
* Represents a [USwitchExpression] clause with the body.
* [USwitchClauseExpressionWithBody], comparing with [USwitchClauseExpression], contains the body expression.
*
* Implementing this interface *is the right way* to support `switch` clauses in your language.
*/
interface USwitchClauseExpressionWithBody : USwitchClauseExpression {
/**
* Returns the body expression for this clause.
*/
val body: UExpression
class SimpleUDefaultSwitchClauseExpression(override val parent: UElement) : UDefaultSwitchClauseExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitSwitchClauseExpression(this)) return
caseValues?.acceptList(visitor)
body.accept(visitor)
visitor.afterVisitSwitchClauseExpression(this)
}
override fun renderString() = (caseValues?.joinToString { it.renderString() } ?: "else") + " -> " + body.renderString()
override fun logString() = log("USwitchClauseExpressionWithBody", caseValues, body)
}
@@ -17,10 +17,45 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents
*
* `try {
* // tryClause body
* } catch (e: Type1, Type2 ... TypeN) {
* // catchClause1 body
* } ... {
* finally {
* //finallyBody
* }`
*
* and
*
* `try (resource1, ..., resourceN) {
* // tryClause body
* }`
*
* expressions.
*/
interface UTryExpression : UExpression {
/**
* Returns the list of try resources, or null if this expression is not a `try-with-resources` expression.
*/
val resources: List<UElement>?
/**
* Returns the `try` clause expression.
*/
val tryClause: UExpression
/**
* Returns the `catch` clauses [UCatchClause] expression list.
*/
val catchClauses: List<UCatchClause>
/**
* Returns the `finally` clause expression, or null if the `finally` clause is absent.
*/
val finallyClause: UExpression?
override fun accept(visitor: UastVisitor) {
@@ -29,13 +64,14 @@ interface UTryExpression : UExpression {
tryClause.accept(visitor)
catchClauses.acceptList(visitor)
finallyClause?.accept(visitor)
visitor.afterVisitTryExpression(this)
}
override fun renderString() = buildString {
append("try ")
appendln(tryClause.renderString())
catchClauses.forEach { appendln(it.renderString()) }
finallyClause?.let { append("finally ").append(it.renderString()) }
appendln(tryClause.renderString().trim('\n'))
catchClauses.forEach { appendln(it.renderString().trim('\n')) }
finallyClause?.let { append("finally ").append(it.renderString().trim('\n')) }
}
override fun logString() = "UTryExpression\n" +
@@ -44,9 +80,23 @@ interface UTryExpression : UExpression {
(finallyClause?.let { it.logString().withMargin } ?: "<no finally clause>" )
}
/**
* Represents the `catch` clause in [UTryExpression].
*/
interface UCatchClause : UElement {
/**
* Returns the `catch` clause body expression.
*/
val body: UExpression
/**
* Returns the exception parameter variables for this `catch` clause.
*/
val parameters: List<UVariable>
/**
* Returns the exception types for this `catch` clause.
*/
val types: List<UType>
override fun accept(visitor: UastVisitor) {
@@ -54,6 +104,7 @@ interface UCatchClause : UElement {
body.accept(visitor)
parameters.acceptList(visitor)
types.acceptList(visitor)
visitor.afterVisitCatchClause(this)
}
override fun logString() = log("UCatchClause", body)
@@ -17,13 +17,26 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a
*
* `while (condition) {
* // body
* }`
*
* expression.
*/
interface UWhileExpression : ULoopExpression {
/**
* Returns the loop condition.
*/
val condition: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitWhileExpression(this)) return
condition.accept(visitor)
body.accept(visitor)
visitor.afterVisitWhileExpression(this)
}
override fun renderString() = buildString {
@@ -15,41 +15,94 @@
*/
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastClassKind
import org.jetbrains.uast.visitor.UastVisitor
interface UClass : UDeclaration, UFqNamed, UModifierOwner, UAnnotated {
/* The simple class name is only for the debug purposes. Do not check against it in the production code */
/**
* Represents a JVM class (ordinary class, interface, annotation, singleton, etc.).
*/
interface UClass : UDeclaration, UFqNamed, UModifierOwner, UVisibilityOwner, UAnnotated {
/**
* Returns the class simple (non-qualified) name.
* The simple class name is only for the debug purposes. Do not check against it in the production code.
*/
override val name: String
/**
* Returns true if the class is anonymous ([name] in this case should return a placeholder like "<anonymous>").
*/
val isAnonymous: Boolean
val visibility: UastVisibility
/**
* Returns the class kind (ordinary class, interface, annotation, etc.).
*/
val kind: UastClassKind
/**
* Returns the default type for this class.
*/
val defaultType: UType
/**
* Returns the class companions.
*/
val companions: List<UClass>
/**
* Returns the class JVM name, or null if the JVM name is unknown.
*/
open val internalName: String?
get() = null
/**
* Returns the all supertypes of this class.
*/
val superTypes: List<UType>
/**
* Returns class declarations (nested classes, constructors, functions, variables, etc.).
* An empty (default) constructor also should be present.
*/
val declarations: List<UDeclaration>
/**
* Returns nested classes declared in this class.
*/
val nestedClasses: List<UClass>
get() = declarations.filterIsInstance<UClass>()
/**
* Returns functions declared in this class.
*/
val functions: List<UFunction>
get() = declarations.filterIsInstance<UFunction>()
/**
* Returns properties declared in this class.
*/
val properties: List<UVariable>
get() = declarations.filterIsInstance<UVariable>()
/**
* Returns constructors declared in this class.
*/
@Suppress("UNCHECKED_CAST")
val constructors: List<UFunction>
get() = declarations.filter { it is UFunction && it.kind == UastFunctionKind.CONSTRUCTOR } as List<UFunction>
/**
* Checks if the class is subclass of another.
*
* @param fqName qualified name of the class to check against
* @return true if the class is the *subclass* of the class with the specified [fqName],
* false if the class qualified name is [fqName] or if the class is not a subclass of the class with the specified [fqName]
*/
fun isSubclassOf(fqName: String) : Boolean
/**
* Get the direct superclass of this class.
*
* @return null if the class has not a superclass (java.lang.Object).
*/
fun getSuperClass(context: UastContext): UClass?
override fun accept(visitor: UastVisitor) {
@@ -57,24 +110,24 @@ interface UClass : UDeclaration, UFqNamed, UModifierOwner, UAnnotated {
nameElement?.accept(visitor)
declarations.acceptList(visitor)
annotations.acceptList(visitor)
visitor.afterVisitClass(this)
}
override fun renderString(): String {
val modifiers = listOf(UastModifier.ABSTRACT, UastModifier.FINAL, UastModifier.STATIC)
.filter { hasModifier(it) }.joinToString(" ") { it.name }.let { if (it.isBlank()) it else "$it " }
val name = if (isAnonymous) "" else " $name"
override fun renderString() = buildString {
appendWithSpace(visibility.name)
appendWithSpace(renderModifiers())
appendWithSpace(kind.text)
appendWithSpace(name)
val declarations = if (declarations.isEmpty()) "" else buildString {
appendln("{")
append(declarations.joinToString("\n") { it.renderString() }.withMargin)
append(declarations.joinToString("\n\n") { it.renderString().trim('\n') }.withMargin)
append("\n}")
}
return "${visibility.name} " + modifiers + kind.text + name + " " + declarations
append(declarations)
}
override fun logString() = "UClass ($name, kind = ${kind.text})\n" + declarations.logString()
override fun logString() = log("UClass ($name, kind = ${kind.text})", declarations)
}
object UClassNotResolved : UClass {
@@ -88,7 +141,7 @@ object UClassNotResolved : UClass {
override val defaultType = UastErrorType
override val nameElement = null
override val parent = null
override val name = "<class not resolved>"
override val name = ERROR_NAME
override val fqName = null
override val internalName = null
@@ -17,16 +17,38 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a declaration.
*/
interface UDeclaration : UElement, UNamed {
/**
* Returns an element for the name node, or null if the node does not exist in the underlying AST (Psi).
*/
val nameElement: UElement?
/**
* Checks if the function name is [name], and the function containing class qualified name is [containingClassFqName].
*
* @param containingClassFqName the required containing class qualified name.
* @param name the function name to check against.
* @return true if the call is a function call, the function name is [name],
* and the qualified name of the function direct containing class is [containingClassFqName],
* false otherwise.
*/
open fun matchesNameWithContaining(containingClassFqName: String, name: String): Boolean {
if (!matchesName(name)) return false
val containingClass = parent as? UClass ?: return false
return containingClass.matchesFqName(containingClassFqName)
}
override fun accept(visitor: UastVisitor) {
visitor.visitElement(this)
visitor.afterVisitElement(this)
}
}
object UDeclarationNotResolved : UDeclaration {
override val name = "<declaration not resolved>"
override val name = ERROR_NAME
override val nameElement = null
override val parent = null
@@ -17,58 +17,109 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
interface UFunction : UDeclaration, UModifierOwner, UAnnotated {
/**
* Represents a function.
* Function could be a JVM method, a property accessor, a constructor, etc.
*/
interface UFunction : UDeclaration, UModifierOwner, UVisibilityOwner, UAnnotated {
/**
* Returns the function kind.
*/
val kind: UastFunctionKind
val valueParameters: List<UVariable>
val valueParameterCount: Int
val typeParameters: List<UTypeReference>
val typeParameterCount: Int
val returnType: UType?
val body: UExpression
val visibility: UastVisibility
/**
* Returns the function value parameters.
*/
val valueParameters: List<UVariable>
/**
* Returns the function value parameters count.
* Retrieving the parameter count could be faster than getting the [valueParameters.size],
* because there is no need to create actual [UVariable] instances.
*/
val valueParameterCount: Int
/**
* Returns the function type parameters.
*/
val typeParameters: List<UTypeReference>
/**
* Returns the function type parameter count.
*/
val typeParameterCount: Int
/**
* Returns the function return type, or null if the function does not have a return type
* (e.g. it is a constructor).
*/
val returnType: UType?
/**
* Returns the function body expression.
*/
val body: UExpression?
/**
* Returns the function JVM descriptor (for example, "(ILjava/lang/String;)[I"), or null if the descriptor is unknown.
*/
open val bytecodeDescriptor: String?
get() = null
/**
* Get the list of all super functions for this function.
*/
fun getSuperFunctions(context: UastContext): List<UFunction>
override fun accept(visitor: UastVisitor) {
if (visitor.visitFunction(this)) return
nameElement?.accept(visitor)
valueParameters.acceptList(visitor)
body.accept(visitor)
body?.accept(visitor)
annotations.acceptList(visitor)
typeParameters.acceptList(visitor)
returnType?.accept(visitor)
visitor.afterVisitFunction(this)
}
override fun renderString(): String {
val typeParameters = if (typeParameterCount == 0) "" else "<" + typeParameters.joinToString { it.renderString() } + "> "
val valueParameters = valueParameters.joinToString { it.renderString() }
val returnType = returnType?.let { ": " + it.renderString() } ?: ""
val body = when (body) {
override fun renderString(): String = buildString {
appendWithSpace(visibility.name)
appendWithSpace(renderModifiers())
append("fun ")
if (typeParameterCount > 0) {
append('<').append(typeParameters.joinToString { it.renderString() }).append("> ")
}
append(name)
append('(')
append(valueParameters.joinToString() { it.renderString() })
append(')')
returnType?.let { append(": " + it.renderString()) }
val body = body
val bodyRendered = when (body) {
null -> ""
is UBlockExpression -> " " + body.renderString()
else -> " = " + body.renderString()
}
return "${visibility.name} fun " + typeParameters + name + "(" + valueParameters + ")" + returnType + body
append(bodyRendered)
}
override fun logString() = "UFunction ($name, kind = ${kind.text}, " +
"paramCount = $valueParameterCount)\n" + body.logString().withMargin
override fun logString() = log("UFunction ($name, kind = ${kind.text}, paramCount = $valueParameterCount)", body)
}
object UFunctionNotResolved : UFunction {
override val kind = UastFunctionKind("<unknown>")
override val kind = UastFunctionKind(ERROR_NAME)
override val valueParameters = emptyList<UVariable>()
override val valueParameterCount = 0
override val typeParameters = emptyList<UTypeReference>()
override val typeParameterCount = 0
override val returnType = null
override val body = EmptyExpression(this)
override val body = null
override val visibility = UastVisibility.PRIVATE
override val nameElement = null
override val parent = null
override val name = "<function not resolved>"
override val name = ERROR_NAME
override fun hasModifier(modifier: UastModifier) = false
override fun getSuperFunctions(context: UastContext) = emptyList<UFunction>()
@@ -15,19 +15,33 @@
*/
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastImportKind
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents an import statement.
*/
interface UImportStatement : UElement {
/**
* Returns the qualified name to import.
*/
val fqNameToImport: String?
val isStarImport: Boolean
val kind: UastImportKind
/* Returns null if isStarImport = true */
/**
* Returns true is the import is a "star" import (on-demand, all-under).
*/
val isStarImport: Boolean
/**
* Resolve the import statement to the declaration.
*
* @param context the Uast context
* @return the declaration element, or null if the declaration was not resolved.
*/
fun resolve(context: UastContext): UDeclaration?
override fun accept(visitor: UastVisitor) {
visitor.visitImportStatement(this)
visitor.afterVisitImportStatement(this)
}
override fun logString() = "UImport ($fqNameToImport)"
@@ -17,50 +17,105 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents the type.
* The abstraction is quite simple. Intersection types, union types, platform types are yet to be supported.
*/
interface UType : UElement, UNamed, UFqNamed, UAnnotated, UResolvable {
override fun matchesName(name: String) = this.name == name || this.name.endsWith(".$name")
/* The simple type name is only for the debug purposes. Do not check against it in the production code */
/**
* Returns the simple (non-qualified) type name.
* The simple type name is only for the debug purposes. Do not check against it in the production code.
*/
override val name: String
/* Semantics: returns true if the type is either a boxed or an unboxed, false otherwise */
/**
* Returns true if the type is either a boxed or an unboxed [Integer], false otherwise.
*/
val isInt: Boolean
/**
* Returns true if the type is either a boxed or an unboxed [Short], false otherwise.
*/
val isShort: Boolean
/**
* Returns true if the type is either a boxed or an unboxed [Long], false otherwise.
*/
val isLong: Boolean
/**
* Returns true if the type is either a boxed or an unboxed [Float], false otherwise.
*/
val isFloat: Boolean
/**
* Returns true if the type is either a boxed or an unboxed [Double], false otherwise.
*/
val isDouble: Boolean
/**
* Returns true if the type is either a boxed or an unboxed [Character], false otherwise.
*/
val isChar: Boolean
/**
* Returns true if the type is either a boxed or an unboxed [Boolean], false otherwise.
*/
val isBoolean: Boolean
/**
* Returns true if the type is either a boxed or an unboxed [Byte], false otherwise.
*/
val isByte: Boolean
override fun logString() = "UType ($name)"
override fun renderString() = name
/**
* Returns the [UClass] declaration element for this type.
*
* @param context the Uast context
* @return the [UClass] declaration element, or null if the class was not resolved.
*/
override fun resolve(context: UastContext): UClass?
override fun resolveOrEmpty(context: UastContext) = resolve(context) ?: UClassNotResolved
override fun accept(visitor: UastVisitor) {
if (visitor.visitType(this)) return
annotations.acceptList(visitor)
visitor.afterVisitType(this)
}
}
/**
* Represents a type reference.
*/
interface UTypeReference : UDeclaration, UResolvable {
override fun renderString() = ""
override fun logString() = log("UTypeReference")
/**
* Returns the [UClass] declaration for this type reference.
*
* @param context the Uast context
* @return the [UClass] declaration element, or null if the class was not resolved.
*/
override fun resolve(context: UastContext): UClass?
override fun resolveOrEmpty(context: UastContext) = resolve(context) ?: UClassNotResolved
}
object UastErrorType : UType, NoAnnotations {
override val isInt = false
override val isLong = false
override val isShort = false
override val isFloat = false
override val isDouble = false
override val isChar = false
override val isByte = false
override val parent = null
override val name = "<error>"
override val name = ERROR_NAME
override val fqName = null
override val isBoolean = false
override fun resolve(context: UastContext) = null
@@ -17,16 +17,26 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
interface UVariable : UDeclaration, UModifierOwner, UAnnotated {
interface UVariable : UDeclaration, UModifierOwner, UVisibilityOwner, UAnnotated {
/**
* Return the variable initializer (or the default value for value parameter), or null if the variable is not initialized.
*/
val initializer: UExpression?
/**
* Return the variable kind.
*/
val kind: UastVariableKind
/**
* Return the variable type.
*/
val type: UType
val visibility: UastVisibility
open val getters: List<UFunction>?
get() = null
open val setters: List<UFunction>?
/**
* Return the list of accessors if the variable is a property, or null otherwise.
*/
open val accessors: List<UFunction>?
get() = null
override fun accept(visitor: UastVisitor) {
@@ -35,13 +45,28 @@ interface UVariable : UDeclaration, UModifierOwner, UAnnotated {
initializer?.accept(visitor)
annotations.acceptList(visitor)
type.accept(visitor)
visitor.afterVisitVariable(this)
}
override fun renderString(): String {
val initializer = if (initializer != null && initializer !is EmptyExpression) " = ${initializer!!.renderString()}" else ""
val prefix = if (kind == UastVariableKind.VALUE_PARAMETER) "" else "var "
val emptyLine = if (kind == UastVariableKind.MEMBER) "\n" else ""
return "$prefix$name: " + type.name + initializer + emptyLine
override fun renderString(): String = buildString {
if (kind != UastVariableKind.VALUE_PARAMETER) appendWithSpace(visibility.name)
appendWithSpace(renderModifiers())
if (kind != UastVariableKind.VALUE_PARAMETER) append("var ")
append(name)
append(": ")
append(type.name)
if (initializer != null && initializer !is EmptyUExpression) {
append(" = ")
append(initializer!!.renderString())
}
accessors?.let {
appendln()
it.forEachIndexed { i, accessor ->
this@buildString.append(accessor.renderString().withMargin)
if ((i + 1) < it.size) appendln()
}
}
}
override fun logString() = "UVariable ($name, kind = ${kind.name})\n" +
@@ -50,11 +75,11 @@ interface UVariable : UDeclaration, UModifierOwner, UAnnotated {
object UVariableNotResolved : UVariable {
override val initializer = null
override val kind = UastVariableKind.MEMBER
override val kind = UastVariableKind(ERROR_NAME)
override val type = UastErrorType
override val nameElement = null
override val parent = null
override val name = "<variable not resolved>"
override val name = ERROR_NAME
override val visibility = UastVisibility.LOCAL
override fun hasModifier(modifier: UastModifier) = false
@@ -17,14 +17,25 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents `receiver[index0, ..., indexN]` expression.
*/
interface UArrayAccessExpression : UExpression {
/**
* Returns the receiver expression.
*/
val receiver: UExpression
/**
* Returns the list of index expressions.
*/
val indices: List<UExpression>
override fun accept(visitor: UastVisitor) {
if (visitor.visitArrayAccessExpression(this)) return
receiver.accept(visitor)
indices.acceptList(visitor)
visitor.afterVisitArrayAccessExpression(this)
}
override fun logString() = log("UArrayAccessExpression", receiver, indices)
@@ -17,15 +17,30 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a binary expression (value1 op value2), eg. `2 + "A"`.
*/
interface UBinaryExpression : UExpression {
/**
* Returns the left operand.
*/
val leftOperand: UExpression
/**
* Returns the binary operator.
*/
val operator: UastBinaryOperator
/**
* Returns the right operand.
*/
val rightOperand: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitBinaryExpression(this)) return
leftOperand.accept(visitor)
rightOperand.accept(visitor)
visitor.afterVisitBinaryExpression(this)
}
override fun logString() =
@@ -17,17 +17,32 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a binary expression with type (value op type), e.g. ("A" instanceof String).
*/
interface UBinaryExpressionWithType : UExpression {
/**
* Returns the operand expression.
*/
val operand: UExpression
/**
* Returns the operation kind.
*/
val operationKind: UastBinaryExpressionWithTypeKind
/**
* Returns the type.
*/
val type: UType
override fun logString() = log("UBinaryExpressionWithType (${getExpressionType()?.name}, ${operationKind.name})", operand)
override fun renderString() = "(${operand.renderString()}) ${operationKind.name} ${getExpressionType()?.name}"
override fun renderString() = "${operand.renderString()} ${operationKind.name} ${type.name}"
override fun accept(visitor: UastVisitor) {
if (visitor.visitBinaryExpressionWithType(this)) return
operand.accept(visitor)
type.accept(visitor)
visitor.afterVisitBinaryExpressionWithType(this)
}
}
@@ -17,12 +17,19 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents the code block expression: `{ /* code */ }`.
*/
interface UBlockExpression : UExpression {
/**
* Returns the list of block expressions.
*/
val expressions: List<UExpression>
override fun accept(visitor: UastVisitor) {
if (visitor.visitBlockExpression(this)) return
expressions.acceptList(visitor)
visitor.afterVisitBlockExpression(this)
}
override fun logString() = log("UBlockExpression", expressions)
@@ -14,22 +14,24 @@
* limitations under the License.
*/
package org.jetbrains.uast.kinds
package org.jetbrains.uast
class UastImportKind(val text: String) {
import org.jetbrains.uast.visitor.UastVisitor
companion object {
@JvmField
val CLASS = UastImportKind("class")
/**
* Represents a `break` expression.
*/
interface UBreakExpression : UExpression {
/**
* Returns the expression label, or null if the label is not specified.
*/
val label: String?
@JvmField
val MEMBER = UastImportKind("member")
@JvmField
val UNKNOWN = UastImportKind("unknown")
override fun accept(visitor: UastVisitor) {
visitor.visitBreakExpression(this)
visitor.afterVisitBreakExpression(this)
}
override fun toString(): String{
return "UastImportKind(text='$text')"
}
override fun logString() = "UBreakExpression (" + (label ?: "<no label>") + ")"
override fun renderString() = label?.let { "break@$it" } ?: "break"
}
@@ -17,25 +17,99 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a call expression (function call, constructor call, array initializer).
*/
interface UCallExpression : UExpression, UResolvable {
val functionReference: USimpleReferenceExpression?
val classReference: USimpleReferenceExpression?
val functionName: String?
val functionNameElement: UElement?
fun functionNameMatches(name: String) = functionName == name
val valueArgumentCount: Int
val valueArguments: List<UExpression>
val typeArgumentCount: Int
val typeArguments: List<UType>
/**
* Returns the call kind.
*/
val kind: UastCallKind
/**
* Returns the function reference expression if the call is a function call, null otherwise.
*/
val functionReference: USimpleReferenceExpression?
/**
* Returns the class reference if the call is a constructor call, null otherwise.
*/
val classReference: USimpleReferenceExpression?
/**
* Returns the function name if the call is a function call, null otherwise.
*
* [functionName] should only be used in debug messages.
* Use [matchesFunctionName] to check against the name.
*/
val functionName: String?
/**
* Returns an element for the function name node, or null if the node does not exist in the underlying AST (Psi).
*/
val functionNameElement: UElement?
/**
* Checks if the function name is [name].
*
* @param name the name to check against.
* @return true if the call is a function call, and the function name is [name], false otherwise.
*/
open fun matchesFunctionName(name: String) = functionName == name
/**
* Checks if the function name is [name], and the function containing class qualified name is [containingClassFqName].
*
* @param containingClassFqName the required containing class qualified name.
* @param name the function name to check against.
* @return true if the call is a function call, the function name is [name],
* and the qualified name of the function direct containing class is [containingClassFqName],
* false otherwise.
*/
open fun matchesFunctionNameWithContaining(containingClassFqName: String, name: String): Boolean {
if (!matchesFunctionName(name)) return false
val containingClass = parent as? UClass ?: return false
return containingClass.matchesFqName(containingClassFqName)
}
/**
* Returns the value argument count.
*
* Retrieving the argument count could be faster than getting the [valueArguments.size],
* because there is no need to create actual [UExpression] instances.
*/
val valueArgumentCount: Int
/**
* Returns the list of value arguments.
*/
val valueArguments: List<UExpression>
/**
* Returns the type argument count.
*/
val typeArgumentCount: Int
/**
* Returns the function type arguments.
*/
val typeArguments: List<UType>
/**
* Resolve the call to the [UFunction] element.
*
* @param context the Uast context
* @return the [UFunction] element, or null if the reference was not resolved.
*/
override fun resolve(context: UastContext): UFunction?
/**
* Try to resolve the call to the [UFunction] element.
*
* @param context the Uast context
* @return the [UFunction] element, of [UFunctionNotResolved] if the reference was not resolved,
* or the call is not a function call.
*/
override fun resolveOrEmpty(context: UastContext): UFunction = resolve(context) ?: UFunctionNotResolved
override fun accept(visitor: UastVisitor) {
@@ -45,11 +119,12 @@ interface UCallExpression : UExpression, UResolvable {
functionNameElement?.accept(visitor)
valueArguments.acceptList(visitor)
typeArguments.acceptList(visitor)
visitor.afterVisitCallExpression(this)
}
override fun logString() = log("UFunctionCallExpression ($kind, argCount = $valueArgumentCount)", functionReference, valueArguments)
override fun renderString(): String {
val ref = functionName ?: functionReference?.renderString() ?: classReference?.renderString() ?: "<noref>"
val ref = functionName ?: classReference?.renderString() ?: functionReference?.renderString() ?: "<noref>"
return ref + "(" + valueArguments.joinToString { it.renderString() } + ")"
}
}
@@ -17,14 +17,42 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
interface UCallableReferenceExpression : UExpression {
val qualifierType: UType
/**
* Represents a callable reference expression, e.g. `Clazz::functionName`.
*/
interface UCallableReferenceExpression : UExpression, UResolvable {
/**
* Returns the qualifier expression.
* Can be null if the [qualifierType] is known.
*/
val qualifierExpression: UExpression?
/**
* Returns the qualifier type.
* Can be null if the qualifier is an expression.
*/
val qualifierType: UType?
/**
* Returns the callable name.
*/
val callableName: String
override fun accept(visitor: UastVisitor) {
if (visitor.visitCallableReferenceExpression(this)) return
qualifierType.accept(visitor)
qualifierExpression?.accept(visitor)
qualifierType?.accept(visitor)
visitor.afterVisitCallableReferenceExpression(this)
}
override fun logString() = "UCallableReferenceExpression"
override fun renderString() = "::" + qualifierType.name
override fun renderString() = buildString {
qualifierExpression?.let {
append(it.renderString())
} ?: qualifierType?.let {
append(it.name)
}
append("::")
append(callableName)
}
}
@@ -17,13 +17,20 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents the class literal expression, e.g. `Clazz.class`.
*/
interface UClassLiteralExpression : UExpression {
override fun logString() = "UClassLiteralExpression"
override fun renderString() = getExpressionType()?.name ?: "::class"
override fun renderString() = type.name + "::class"
/**
* Returns the type for this class literal expression.
*/
val type: UType
override fun accept(visitor: UastVisitor) {
visitor.visitClassLiteralExpression(this)
visitor.afterVisitClassLiteralExpression(this)
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* 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 org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `continue` expression.
*/
interface UContinueExpression : UExpression {
/**
* Returns the expression label, or null if the label is not specified.
*/
val label: String?
override fun accept(visitor: UastVisitor) {
visitor.visitContinueExpression(this)
visitor.afterVisitContinueExpression(this)
}
override fun logString() = "UContinueExpression (" + (label ?: "<no label>") + ")"
override fun renderString() = label?.let { "continue@$it" } ?: "continue"
}
@@ -17,15 +17,26 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a list of declarations.
* Example in Java: `int a = 4, b = 3`.
*/
interface UDeclarationsExpression : UExpression {
/**
* Returns the list of declarations inside this [UDeclarationsExpression].
*/
val declarations: List<UElement>
/**
* Returns the list of variables inside this [UDeclarationsExpression].
*/
val variables: List<UVariable>
get() = declarations.filterIsInstance<UVariable>()
override fun accept(visitor: UastVisitor) {
if (visitor.visitDeclarationsExpression(this)) return
declarations.acceptList(visitor)
visitor.afterVisitDeclarationsExpression(this)
}
override fun renderString() = declarations.joinToString("\n") { it.renderString() }
@@ -17,21 +17,26 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
interface UExpressionList : UExpression {
/**
* Represents a generic list of expressions.
*/
interface USpecialExpressionList : UExpression {
/**
* Returns the list of expressions.
*/
val expressions: List<UExpression>
override fun accept(visitor: UastVisitor) {
if (visitor.visitExpressionList(this)) return
expressions.acceptList(visitor)
}
override fun logString() = log("UExpressionList", expressions)
override fun renderString() = log("", expressions)
}
interface USpecialExpressionList : UExpressionList {
/**
* Returns the list kind.
*/
val kind: UastSpecialExpressionKind
override fun accept(visitor: UastVisitor) {
if (visitor.visitSpecialExpressionList(this)) return
expressions.acceptList(visitor)
visitor.afterVisitSpecialExpressionList(this)
}
fun firstOrNull(): UExpression? = expressions.firstOrNull()
override fun logString() = log("USpecialExpressionList (${kind.name})", expressions)
@@ -17,13 +17,24 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents an expression with the label specified.
*/
interface ULabeledExpression : UExpression {
/**
* Returns the expression label.
*/
val label: String
/**
* Returns the expression itself.
*/
val expression: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitLabeledExpression(this)) return
expression.accept(visitor)
visitor.afterVisitLabeledExpression(this)
}
override fun evaluate() = expression.evaluate()
@@ -17,14 +17,25 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents the lambda expression.
*/
interface ULambdaExpression : UExpression {
/**
* Returns the list of lambda value parameters.
*/
val valueParameters: List<UVariable>
/**
* Returns the lambda body expression.
*/
val body: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitLambdaExpression(this)) return
valueParameters.acceptList(visitor)
body.accept(visitor)
visitor.afterVisitLambdaExpression(this)
}
override fun logString() = log("ULambdaExpression", valueParameters, body)
@@ -17,23 +17,51 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a literal expression.
*/
interface ULiteralExpression : UExpression {
/**
* Returns the literal expression value.
* This is basically a String, Number or null if the literal is a `null` literal.
*/
val value: Any?
/**
* Returns true if the literal is a `null`-literal, false otherwise.
*/
val isNull: Boolean
/**
* Returns true if the literal is a [String] literal, false otherwise.
*/
val isString: Boolean
get() = evaluate() is String
/**
* Returns true if the literal is a [Boolean] literal, false otherwise.
*/
val isBoolean: Boolean
get() = evaluate() is Boolean
fun asString() = value?.toString() ?: ""
/**
* Returns the string representation of the literal expression.
*
* @return the string representation, or "null" if the literal is a "null"-literal.
*/
fun asString(): String {
val value = value
return if (value == null)
"null"
else
value.toString()
}
override fun accept(visitor: UastVisitor) {
visitor.visitLiteralExpression(this)
visitor.afterVisitLiteralExpression(this)
}
override fun logString() = "ULiteralExpression (${asString()})"
override fun renderString() = asString()
override fun renderString() = if (value is String) "\"$value\"" else asString()
}
@@ -26,6 +26,7 @@ class UNamedExpression(
override fun accept(visitor: UastVisitor) {
if (visitor.visitElement(this)) return
expression.accept(visitor)
visitor.afterVisitElement(this)
}
override fun logString() = log("UNamedExpression ($name)", expression)
@@ -17,12 +17,19 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents an object literal expression, e.g. `new Runnable() {}` in Java.
*/
interface UObjectLiteralExpression : UExpression {
/**
* Returns the class declaration.
*/
val declaration: UClass
override fun accept(visitor: UastVisitor) {
if (visitor.visitObjectLiteralExpression(this)) return
declaration.accept(visitor)
visitor.afterVisitObjectLiteralExpression(this)
}
override fun logString() = log("UObjectLiteralExpression", declaration)
@@ -17,12 +17,19 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a parenthesized expression, e.g. `(23 + 3)`.
*/
interface UParenthesizedExpression : UExpression {
/**
* Returns an expression inside the parenthesis.
*/
val expression: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitParenthesizedExpression(this)) return
expression.accept(visitor)
visitor.afterVisitParenthesizedExpression(this)
}
override fun evaluate() = expression.evaluate()
@@ -17,19 +17,48 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents the qualified expression (receiver.selector).
*/
interface UQualifiedExpression : UExpression, UResolvable {
/**
* Returns the expression receiver.
*/
val receiver: UExpression
/**
* Returns the expression selector.
*/
val selector: UExpression
/**
* Returns the access type (simple, safe access, etc.).
*/
val accessType: UastQualifiedExpressionAccessType
/**
* Checks if the selector is a simple reference expression, and if its identifier is [identifier].
*
* @param identifier the identifier to check agains
* @return true if the selector is a simple reference expression, and if its identifier is [identifier],
* false otherwise
*/
fun selectorMatches(identifier: String) = (selector as? USimpleReferenceExpression)?.identifier == identifier
/**
* Returns the selector identifier if the selector is a simple reference expression.
*
* @return the selector identifier if the selector is a simple reference expression, null otherwise.
*/
fun getSelectorAsIdentifier(): String? = (selector as? USimpleReferenceExpression)?.identifier
override fun renderString() = receiver.renderString() + accessType.name + selector.renderString()
override fun accept(visitor: UastVisitor) {
if (visitor.visitQualifiedExpression(this)) return
receiver.accept(visitor)
selector.accept(visitor)
visitor.afterVisitQualifiedExpression(this)
}
override fun logString() = log("UQualifiedExpression", receiver, selector)
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* 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 org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `return` expression.
*/
interface UReturnExpression : UExpression {
/**
* Returns the `return` value.
*/
val returnExpression: UExpression?
override fun accept(visitor: UastVisitor) {
if (visitor.visitReturnExpression(this)) return
returnExpression?.accept(visitor)
visitor.afterVisitReturnExpression(this)
}
override fun renderString() = returnExpression.let { if (it == null) "return" else "return " + it.renderString() }
override fun logString() = log("UReturnExpression", returnExpression)
}
@@ -17,11 +17,18 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a simple reference expression (a non-qualified identifier).
*/
interface USimpleReferenceExpression : UExpression, UResolvable {
/**
* Returns the identifier name.
*/
val identifier: String
override fun accept(visitor: UastVisitor) {
visitor.visitSimpleReferenceExpression(this)
visitor.afterVisitSimpleReferenceExpression(this)
}
override fun logString() = "USimpleReferenceExpression ($identifier)"
@@ -17,11 +17,16 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `super` expression.
* Qualified `super` is not supported at the moment.
*/
interface USuperExpression : UExpression {
override fun logString() = "USuperExpression"
override fun renderString() = "super"
override fun accept(visitor: UastVisitor) {
visitor.visitSuperExpression(this)
visitor.afterVisitSuperExpression(this)
}
}
@@ -17,11 +17,16 @@ package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `this` expression.
* Qualified `this` is not supported at the moment.
*/
interface UThisExpression : UExpression {
override fun logString() = "UThisExpression"
override fun renderString() = "this"
override fun accept(visitor: UastVisitor) {
visitor.visitThisExpression(this)
visitor.afterVisitThisExpression(this)
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* 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 org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `throw` expression.
*/
interface UThrowExpression : UExpression {
/**
* Returns ths thrown expression.
*/
val thrownExpression: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitThrowExpression(this)) return
thrownExpression.accept(visitor)
visitor.afterVisitThrowExpression(this)
}
override fun renderString() = "throw " + thrownExpression.renderString()
override fun logString() = log("UThrowExpression", thrownExpression)
}
@@ -15,7 +15,6 @@
*/
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastOperator
import org.jetbrains.uast.visitor.UastVisitor
interface UUnaryExpression : UExpression {
@@ -25,6 +24,7 @@ interface UUnaryExpression : UExpression {
override fun accept(visitor: UastVisitor) {
if (visitor.visitUnaryExpression(this)) return
operand.accept(visitor)
visitor.afterVisitUnaryExpression(this)
}
}
@@ -34,6 +34,7 @@ interface UPrefixExpression : UUnaryExpression {
override fun accept(visitor: UastVisitor) {
if (visitor.visitPrefixExpression(this)) return
operand.accept(visitor)
visitor.afterVisitPrefixExpression(this)
}
override fun logString() = log("UPrefixExpression (${operator.text})", operand)
@@ -46,6 +47,7 @@ interface UPostfixExpression : UUnaryExpression {
override fun accept(visitor: UastVisitor) {
if (visitor.visitPostfixExpression(this)) return
operand.accept(visitor)
visitor.afterVisitPostfixExpression(this)
}
override fun logString() = log("UPostfixExpression (${operator.text})", operand)
@@ -16,15 +16,40 @@
@file:JvmName("UastLiteralUtils")
package org.jetbrains.uast
/**
* Checks if the [UElement] is a null literal.
*
* @return true if the receiver is a null literal, false otherwise.
*/
fun UElement.isNullLiteral(): Boolean = this is ULiteralExpression && this.isNull
/**
* Checks if the [UElement] is a [String] literal.
*
* @return true if the receiver is a [String] literal, false otherwise.
*/
fun UElement.isStringLiteral(): Boolean = this is ULiteralExpression && this.isString
/**
* Returns the [String] literal value.
*
* @return literal text if the receiver is a valid [String] literal, null otherwise.
*/
fun UElement.getValueIfStringLiteral(): String? =
if (isStringLiteral()) (this as ULiteralExpression).value as String else null
/**
* Checks if the [UElement] is a [Number] literal (Integer, Long, Float, Double, etc.).
*
* @return true if the receiver is a [Number] literal, false otherwise.
*/
fun UElement.isNumberLiteral(): Boolean = this is ULiteralExpression && this.value is Number
/**
* Checks if the [UElement] is an integral literal (is an [Integer], [Long], [Short], [Char] or [Byte]).
*
* @return true if the receiver is an integral literal, false otherwise.
*/
fun UElement.isIntegralLiteral(): Boolean = this is ULiteralExpression && when (value) {
is Int -> true
is Long -> true
@@ -34,8 +59,12 @@ fun UElement.isIntegralLiteral(): Boolean = this is ULiteralExpression && when (
else -> false
}
fun UElement.isBooleanLiteral(): Boolean = this is ULiteralExpression && this.isBoolean
/**
* Returns the integral value of the literal.
*
* @return long representation of the literal expression value,
* 0 if the receiver literal expression is not a integral one.
*/
fun ULiteralExpression.getLongValue(): Long = value.let {
when (it) {
is Long -> it
@@ -15,4 +15,15 @@
*/
package org.jetbrains.uast
internal fun List<UElement>.logString() = joinToString("\n") { it.logString().withMargin }
internal fun List<UElement>.logString() = joinToString("\n") { it.logString().withMargin }
internal fun UModifierOwner.renderModifiers() = UastModifier.VALUES
.filter { hasModifier(it) }
.joinToString(" ") { it.name }
internal fun StringBuilder.appendWithSpace(s: String) {
if (s.isNotEmpty()) {
append(s)
append(' ')
}
}
@@ -16,6 +16,10 @@
@file:JvmName("UastBinaryExpressionWithTypeUtils")
package org.jetbrains.uast
/**
* Kinds of [UBinaryExpressionWithType].
* Examples: type casts, instance checks.
*/
open class UastBinaryExpressionWithTypeKind(val name: String) {
open class TypeCast(name: String) : UastBinaryExpressionWithTypeKind(name)
open class InstanceCheck(name: String) : UastBinaryExpressionWithTypeKind(name)
@@ -15,8 +15,9 @@
*/
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastOperator
/**
* Kinds of operators in [UBinaryExpression].
*/
open class UastBinaryOperator(override val text: String): UastOperator {
class LogicalOperator(text: String): UastBinaryOperator(text)
class ComparationOperator(text: String): UastBinaryOperator(text)
@@ -105,7 +106,7 @@ open class UastBinaryOperator(override val text: String): UastOperator {
@JvmField
val DIVIDE_ASSIGN = AssignOperator("/=")
@JvmField
val REMAINDER_ASSIGN = AssignOperator("%=")
@@ -15,13 +15,19 @@
*/
package org.jetbrains.uast
class UastCallKind(val name: String) {
/**
* Kinds of [UCallExpression].
*/
open class UastCallKind(val name: String) {
companion object {
@JvmField
val FUNCTION_CALL = UastCallKind("function_call")
@JvmField
val CONSTRUCTOR_CALL = UastCallKind("constructor_call")
@JvmField
val ARRAY_INITIALIZER = UastCallKind("array_initializer")
}
override fun toString(): String{
@@ -14,11 +14,12 @@
* limitations under the License.
*/
package org.jetbrains.uast.kinds
package org.jetbrains.uast
/**
* Kinds of [UClass].
*/
open class UastClassKind(val text: String) {
class UastCompanionObject(val default: Boolean) : UastClassKind("companion object")
companion object {
@JvmField
val CLASS = UastClassKind("class")
@@ -15,6 +15,9 @@
*/
package org.jetbrains.uast
/**
* Kinds of [UFunction].
*/
open class UastFunctionKind(val text: String) {
class UastInitializerKind(val name: String) : UastFunctionKind("INITIALIZER ($name)")
class UastVariableAccessor(val name: String) : UastFunctionKind(name)
@@ -15,7 +15,12 @@
*/
package org.jetbrains.uast
class UastModifier(val name: String) {
/**
* Uast declaration modifiers.
*
* @see UModifierOwner
*/
open class UastModifier(val name: String) {
companion object {
@JvmField
val ABSTRACT = UastModifier("abstract")
@@ -24,9 +29,13 @@ class UastModifier(val name: String) {
@JvmField
val FINAL = UastModifier("final")
@JvmField
val IMMUTABLE = UastModifier("immutable")
@JvmField
val VARARG = UastModifier("vararg")
@JvmField
val OVERRIDE = UastModifier("override")
val VALUES = listOf(ABSTRACT, STATIC, FINAL, IMMUTABLE, VARARG, OVERRIDE)
}
override fun toString(): String{
@@ -13,8 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.uast.kinds
package org.jetbrains.uast
/**
* Uast operator base inteface.
*
* @see [UastPrefixOperator], [UastPostfixOperator], [UastBinaryOperator]
*/
interface UastOperator {
/**
* Returns the operator text to render in [UElement.renderString].
*/
val text: String
}
@@ -15,9 +15,10 @@
*/
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastOperator
class UastPostfixOperator(override val text: String): UastOperator {
/**
* [UPostfixExpression] operators.
*/
open class UastPostfixOperator(override val text: String): UastOperator {
companion object {
@JvmField
val INC = UastPostfixOperator("++")
@@ -15,8 +15,9 @@
*/
package org.jetbrains.uast
import org.jetbrains.uast.kinds.UastOperator
/**
* [UPrefixExpression] operators.
*/
class UastPrefixOperator(override val text: String): UastOperator {
companion object {
@JvmField
@@ -15,7 +15,11 @@
*/
package org.jetbrains.uast
class UastQualifiedExpressionAccessType(val name: String) {
/**
* Access types of [UQualifiedExpression].
* Additional type examples: Kotlin safe call (?.).
*/
open class UastQualifiedExpressionAccessType(val name: String) {
companion object {
@JvmField
val SIMPLE = UastQualifiedExpressionAccessType(".")
@@ -15,21 +15,10 @@
*/
package org.jetbrains.uast
class UastSpecialExpressionKind(val name: String) {
companion object {
@JvmField
val RETURN = UastSpecialExpressionKind("return")
@JvmField
val THROW = UastSpecialExpressionKind("throw")
@JvmField
val BREAK = UastSpecialExpressionKind("break")
@JvmField
val CONTINUE = UastSpecialExpressionKind("continue")
}
/**
* Kinds of [USpecialExpressionList].
*/
open class UastSpecialExpressionKind(val name: String) {
override fun toString(): String{
return "UastSpecialExpressionKind(name='$name')"
}
@@ -15,7 +15,10 @@
*/
package org.jetbrains.uast
class UastVariableKind(val name: String) {
/**
* Kinds of [UVariable].
*/
open class UastVariableKind(val name: String) {
companion object {
@JvmField
val LOCAL_VARIABLE = UastVariableKind("local")
@@ -15,7 +15,10 @@
*/
package org.jetbrains.uast
class UastVisibility(val name: String) {
/**
* Uast visibility list.
*/
open class UastVisibility(val name: String) {
companion object {
@JvmField
val PUBLIC = UastVisibility("public")