UAST initial commit (copy of code from https://github.com/JetBrains/uast)

This commit is contained in:
Dmitry Jemerov
2017-02-17 17:08:36 +01:00
parent 8fd859094c
commit f29cb2a482
170 changed files with 11096 additions and 0 deletions

3
.idea/modules.xml generated
View File

@@ -270,6 +270,9 @@
<module fileurl="file://$PROJECT_DIR$/platform/testFramework/bootstrap/tests_bootstrap.iml" filepath="$PROJECT_DIR$/platform/testFramework/bootstrap/tests_bootstrap.iml" group="platform" />
<module fileurl="file://$PROJECT_DIR$/android/tools-base/testutils/testutils.iml" filepath="$PROJECT_DIR$/android/tools-base/testutils/testutils.iml" group="android/sdktools" />
<module fileurl="file://$PROJECT_DIR$/java/typeMigration/typeMigration.iml" filepath="$PROJECT_DIR$/java/typeMigration/typeMigration.iml" group="java" />
<module fileurl="file://$PROJECT_DIR$/uast/uast-common/uast-common.iml" filepath="$PROJECT_DIR$/uast/uast-common/uast-common.iml" group="platform/uast" />
<module fileurl="file://$PROJECT_DIR$/uast/uast-java/uast-java.iml" filepath="$PROJECT_DIR$/uast/uast-java/uast-java.iml" group="platform/uast" />
<module fileurl="file://$PROJECT_DIR$/uast/uast-tests/uast-tests.iml" filepath="$PROJECT_DIR$/uast/uast-tests/uast-tests.iml" group="platform/uast" />
<module fileurl="file://$PROJECT_DIR$/plugins/ui-designer/ui-designer.iml" filepath="$PROJECT_DIR$/plugins/ui-designer/ui-designer.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/plugins/ui-designer-core/ui-designer-core.iml" filepath="$PROJECT_DIR$/plugins/ui-designer-core/ui-designer-core.iml" group="plugins" />
<module fileurl="file://$PROJECT_DIR$/plugins/ui-designer/jps-plugin/ui-designer-jps-plugin.iml" filepath="$PROJECT_DIR$/plugins/ui-designer/jps-plugin/ui-designer-jps-plugin.iml" group="plugins" />

95
uast/build.gradle Normal file
View File

@@ -0,0 +1,95 @@
buildscript {
ext.kotlin_version = '1.0.5'
ext.intellij_core_version = '171.3019.7'
repositories {
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.3'
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7'
classpath 'de.undercouch:gradle-download-task:3.1.2'
}
}
String getUastVersion() {
return System.getenv("ARTIFACT_VERSION") ?: "1.0"
}
apply from: 'updateDependencies.gradle'
allprojects {
group = 'org.jetbrains.uast'
version = getUastVersion()
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'com.jfrog.bintray'
apply plugin: "maven-publish"
configurations {
provided
}
sourceSets {
main {
compileClasspath += configurations.provided
java.srcDirs = ['src']
kotlin.srcDirs = ['src']
}
test {
java.srcDirs = ['test']
kotlin.srcDirs = ['test']
}
}
compileJava {
sourceCompatibility = 1.6
targetCompatibility = 1.6
}
repositories {
jcenter()
}
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
bintray {
user = System.getenv("BINTRAY_USER") ?: ""
key = System.getenv("BINTRAY_API_KEY") ?: ""
publications = ['UastPublication']
pkg {
repo = 'uast'
name = 'uast'
userOrg = 'kotlin'
licenses = ['Apache-2.0']
vcsUrl = SCM_URL
version {
name = getUastVersion()
released = new Date()
}
}
}
publishing {
publications {
UastPublication(MavenPublication) {
from components.java
groupId 'org.jetbrains.uast'
artifactId project.name
version getUastVersion()
artifact sourcesJar
}
}
}
}

1
uast/gradle.properties Normal file
View File

@@ -0,0 +1 @@
SCM_URL=https://github.com/JetBrains/uast

164
uast/gradlew vendored Executable file
View File

@@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
uast/gradlew.bat vendored Normal file
View File

@@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

1
uast/settings.gradle Normal file
View File

@@ -0,0 +1 @@
include ':uast-common', ':uast-java', ':uast-tests'

View File

@@ -0,0 +1,130 @@
/*
* Copyright 2000-2017 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 com.intellij.lang.Language
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.project.Project
import com.intellij.psi.*
/**
* Manages the UAST to PSI conversion.
*/
class UastContext(val project: Project) : UastLanguagePlugin {
private companion object {
private val CONTEXT_LANGUAGE = object : Language("UastContextLanguage") {}
}
override val language: Language
get() = CONTEXT_LANGUAGE
override val priority: Int
get() = 0
val languagePlugins: Collection<UastLanguagePlugin>
get() = UastLanguagePlugin.getInstances()
fun findPlugin(element: PsiElement): UastLanguagePlugin? {
val language = element.language
return languagePlugins.firstOrNull { it.language == language }
}
override fun isFileSupported(fileName: String) = languagePlugins.any { it.isFileSupported(fileName) }
fun getMethod(method: PsiMethod): UMethod = convertWithParent<UMethod>(method)!!
fun getVariable(variable: PsiVariable): UVariable = convertWithParent<UVariable>(variable)!!
fun getClass(clazz: PsiClass): UClass = convertWithParent<UClass>(clazz)!!
override fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class<out UElement>?): UElement? {
return findPlugin(element)?.convertElement(element, parent, requiredType)
}
override fun convertElementWithParent(element: PsiElement, requiredType: Class<out UElement>?): UElement? {
return findPlugin(element)?.convertElementWithParent(element, requiredType)
}
override fun getMethodCallExpression(
element: PsiElement,
containingClassFqName: String?,
methodName: String
): UastLanguagePlugin.ResolvedMethod? {
return findPlugin(element)?.getMethodCallExpression(element, containingClassFqName, methodName)
}
override fun getConstructorCallExpression(
element: PsiElement,
fqName: String
): UastLanguagePlugin.ResolvedConstructor? {
return findPlugin(element)?.getConstructorCallExpression(element, fqName)
}
override fun isExpressionValueUsed(element: UExpression): Boolean {
val language = element.getLanguage()
return (languagePlugins.firstOrNull { it.language == language })?.isExpressionValueUsed(element) ?: false
}
private tailrec fun UElement.getLanguage(): Language {
psi?.language?.let { return it }
val containingElement = this.uastParent ?: throw IllegalStateException("At least UFile should have a language")
return containingElement.getLanguage()
}
}
/**
* Converts the element along with its parents to UAST.
*/
fun PsiElement?.toUElement() =
this?.let { ServiceManager.getService(project, UastContext::class.java).convertElementWithParent(this, null) }
/**
* Converts the element to an UAST element of the given type. Returns null if the PSI element type does not correspond
* to the given UAST element type.
*/
fun <T : UElement> PsiElement?.toUElement(cls: Class<out T>): T? =
this?.let { ServiceManager.getService(project, UastContext::class.java).convertElementWithParent(this, cls) as T? }
inline fun <reified T : UElement> PsiElement?.toUElementOfType(): T? =
this?.let { ServiceManager.getService(project, UastContext::class.java).convertElementWithParent(this, T::class.java) as T? }
/**
* Finds an UAST element of a given type at the given [offset] in the specified file. Returns null if there is no UAST
* element of the given type at the given offset.
*/
fun <T : UElement> PsiFile.findUElementAt(offset: Int, cls: Class<out T>): T? {
val element = findElementAt(offset) ?: return null
val uElement = element.toUElement() ?: return null
@Suppress("UNCHECKED_CAST")
return uElement.withContainingElements.firstOrNull { cls.isInstance(it) } as T?
}
/**
* Finds an UAST element of the given type among the parents of the given PSI element.
*/
@JvmOverloads
fun <T : UElement> PsiElement?.getUastParentOfType(cls: Class<out T>, strict: Boolean = false): T? {
val uElement = this.toUElement() ?: return null
val sequence = if (strict)
(uElement.uastParent?.withContainingElements ?: emptySequence())
else
uElement.withContainingElements
@Suppress("UNCHECKED_CAST")
return sequence.firstOrNull { cls.isInstance(it) } as T?
}
inline fun <reified T : UElement> PsiElement?.getUastParentOfType(strict: Boolean = false): T? = getUastParentOfType(T::class.java, strict)

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2000-2017 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 com.intellij.lang.Language
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.openapi.extensions.Extensions
import com.intellij.psi.*
interface UastLanguagePlugin {
companion object {
val extensionPointName: ExtensionPointName<UastLanguagePlugin> =
ExtensionPointName.create<UastLanguagePlugin>("org.jetbrains.uast.uastLanguagePlugin")
fun getInstances(): Collection<UastLanguagePlugin> {
val rootArea = Extensions.getRootArea()
if (!rootArea.hasExtensionPoint(extensionPointName.name)) return listOf()
return rootArea.getExtensionPoint(extensionPointName).extensions.toList()
}
}
data class ResolvedMethod(val call: UCallExpression, val method: PsiMethod)
data class ResolvedConstructor(val call: UCallExpression, val constructor: PsiMethod, val clazz: PsiClass)
val language: Language
/**
* Checks if the file with the given [fileName] is supported.
*
* @param fileName the source file name.
* @return true, if the file is supported by this converter, false otherwise.
*/
fun isFileSupported(fileName: String): Boolean
/**
* Returns the converter priority. Might be positive, negative or 0 (Java's is 0).
* UastConverter with the higher priority will be queried earlier.
*
* Priority is useful when a language N wraps its own elements (NElement) to, for example, Java's PsiElements,
* and Java resolves the reference to such wrapped PsiElements, not the original NElement.
* In this case N implementation can handle such wrappers in UastConverter earlier than Java's converter,
* so N language converter will have a higher priority.
*/
val priority: Int
/**
* Converts a PSI element, the parent of which already has an UAST representation, to UAST.
*
* @param element the element to convert
* @param parent the parent as an UAST element, or null if the element is a file
* @param requiredType the expected type of the result.
* @return the converted element, or null if the element isn't supported or doesn't match the required result type.
*/
fun convertElement(element: PsiElement, parent: UElement?, requiredType: Class<out UElement>? = null): UElement?
/**
* Converts a PSI element, along with its chain of parents, to UAST.
*
* @param element the element to convert
* @param requiredType the expected type of the result.
* @return the converted element, or null if the element isn't supported or doesn't match the required result type.
*/
fun convertElementWithParent(element: PsiElement, requiredType: Class<out UElement>?): UElement?
fun getMethodCallExpression(
element: PsiElement,
containingClassFqName: String?,
methodName: String
): ResolvedMethod?
fun getConstructorCallExpression(
element: PsiElement,
fqName: String
) : ResolvedConstructor?
fun getMethodBody(element: PsiMethod): UExpression? {
if (element is UMethod) return element.uastBody
return (convertElementWithParent(element, null) as? UMethod)?.uastBody
}
fun getInitializerBody(element: PsiClassInitializer): UExpression {
if (element is UClassInitializer) return element.uastBody
return (convertElementWithParent(element, null) as? UClassInitializer)?.uastBody ?: UastEmptyExpression
}
fun getInitializerBody(element: PsiVariable): UExpression? {
if (element is UVariable) return element.uastInitializer
return (convertElementWithParent(element, null) as? UVariable)?.uastInitializer
}
/**
* Returns true if the expression value is used.
* Do not rely on this property too much, its value can be approximate in some cases.
*/
fun isExpressionValueUsed(element: UExpression): Boolean
}
inline fun <reified T : UElement> UastLanguagePlugin.convertOpt(element: PsiElement?, parent: UElement?): T? {
if (element == null) return null
return convertElement(element, parent) as? T
}
inline fun <reified T : UElement> UastLanguagePlugin.convert(element: PsiElement, parent: UElement?): T {
return convertElement(element, parent, T::class.java) as T
}
inline fun <reified T : UElement> UastLanguagePlugin.convertWithParent(element: PsiElement?): T? {
if (element == null) return null
return convertElementWithParent(element, T::class.java) as? T
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2000-2017 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.
*/
@file:JvmMultifileClass
@file:JvmName("UastUtils")
package org.jetbrains.uast
import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiNamedElement
import com.intellij.psi.util.PsiTreeUtil
import java.io.File
inline fun <reified T : UElement> UElement.getParentOfType(strict: Boolean = true): T? = getParentOfType(T::class.java, strict)
@JvmOverloads
fun <T : UElement> UElement.getParentOfType(parentClass: Class<out UElement>, strict: Boolean = true): T? {
var element = (if (strict) uastParent else this) ?: return null
while (true) {
if (parentClass.isInstance(element)) {
@Suppress("UNCHECKED_CAST")
return element as T
}
element = element.uastParent ?: return null
}
}
fun <T : UElement> UElement.getParentOfType(
parentClass: Class<out UElement>,
strict: Boolean = true,
vararg terminators: Class<out UElement>
): T? {
var element = (if (strict) uastParent else this) ?: return null
while (true) {
if (parentClass.isInstance(element)) {
@Suppress("UNCHECKED_CAST")
return element as T
}
if (terminators.any { it.isInstance(element) }) {
return null
}
element = element.uastParent ?: return null
}
}
fun <T : UElement> UElement.getParentOfType(
strict: Boolean = true,
firstParentClass: Class<out T>,
vararg parentClasses: Class<out T>
): T? {
var element = (if (strict) uastParent else this) ?: return null
while (true) {
if (firstParentClass.isInstance(element)) {
@Suppress("UNCHECKED_CAST")
return element as T
}
if (parentClasses.any { it.isInstance(element) }) {
@Suppress("UNCHECKED_CAST")
return element as T
}
element = element.uastParent ?: return null
}
}
fun UElement.getContainingFile() = getParentOfType<UFile>(UFile::class.java)
fun UElement.getContainingUClass() = getParentOfType<UClass>(UClass::class.java)
fun UElement.getContainingUMethod() = getParentOfType<UMethod>(UMethod::class.java)
fun UElement.getContainingUVariable() = getParentOfType<UVariable>(UVariable::class.java)
fun UElement.getContainingMethod() = getContainingUMethod()?.psi
fun UElement.getContainingClass() = getContainingUClass()?.psi
fun UElement.getContainingVariable() = getContainingUVariable()?.psi
fun PsiElement?.getContainingClass() = this?.let { PsiTreeUtil.getParentOfType(it, PsiClass::class.java) }
fun UElement.isChildOf(probablyParent: UElement?, strict: Boolean = false): Boolean {
tailrec fun isChildOf(current: UElement?, probablyParent: UElement): Boolean {
return when (current) {
null -> false
probablyParent -> true
else -> isChildOf(current.uastParent, probablyParent)
}
}
if (probablyParent == null) return false
return isChildOf(if (strict) this else uastParent, probablyParent)
}
/**
* 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.tryResolve(): PsiElement? = (this as? UResolvable)?.resolve()
fun UElement.tryResolveNamed(): PsiNamedElement? = (this as? UResolvable)?.resolve() as? PsiNamedElement
fun UElement.tryResolveUDeclaration(context: UastContext): UDeclaration? {
return (this as? UResolvable)?.resolve()?.let { context.convertElementWithParent(it, null) as? UDeclaration }
}
fun UReferenceExpression?.getQualifiedName() = (this?.resolve() as? PsiClass)?.qualifiedName
/**
* Returns the String expression value, or null if the value can't be calculated or if the calculated value is not a String.
*/
fun UExpression.evaluateString(): String? = evaluate() as? String
/**
* Get a physical [File] for this file, or null if there is no such file on disk.
*/
fun UFile.getIoFile(): File? = psi.virtualFile?.let { VfsUtilCore.virtualToIoFile(it) }
tailrec fun UElement.getUastContext(): UastContext {
val psi = this.psi
if (psi != null) {
return ServiceManager.getService(psi.project, UastContext::class.java) ?: error("UastContext not found")
}
return (uastParent ?: error("PsiElement should exist at least for UFile")).getUastContext()
}
tailrec fun UElement.getLanguagePlugin(): UastLanguagePlugin {
val psi = this.psi
if (psi != null) {
val uastContext = ServiceManager.getService(psi.project, UastContext::class.java) ?: error("UastContext not found")
return uastContext.findPlugin(psi) ?: error("Language plugin was not found for $this (${this.javaClass.name})")
}
return (uastParent ?: error("PsiElement should exist at least for UFile")).getLanguagePlugin()
}
fun Collection<UElement>.toPsiElements() = mapNotNull { it.psi }

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiElement
import org.jetbrains.uast.internal.log
class UComment(override val psi: PsiElement, override val uastParent: UElement) : UElement {
val text: String
get() = asSourceString()
override fun asLogString() = log()
override fun asRenderString(): String = asSourceString()
override fun asSourceString(): String = psi.text
}

View File

@@ -0,0 +1,108 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiElement
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* The common interface for all Uast elements.
*/
interface UElement {
/**
* Returns the element parent.
*/
val uastParent: UElement?
/**
* Returns the PSI element underlying this element. Note that some UElements are synthetic and do not have
* an underlying PSI element; this doesn't mean that they are invalid.
*/
val psi: PsiElement?
/**
* Returns true if this element is valid, false otherwise.
*/
val isPsiValid: Boolean
get() = psi?.isValid ?: true
/**
* Returns the list of comments for this element.
*/
val comments: List<UComment>
get() = emptyList()
/**
* Returns the log string (usually one line containing the class name and some additional information).
*
* Examples:
* UWhileExpression
* UBinaryExpression (>)
* UCallExpression (println)
* USimpleReferenceExpression (i)
* ULiteralExpression (5)
*
* @return the expression tree for this element.
* @see [UIfExpression] for example.
*/
fun asLogString(): 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 asRenderString(): String = asLogString()
/**
* Returns the string as written in the source file.
* Use this String only for logging and diagnostic text messages.
*
* @return the original text.
*/
fun asSourceString(): String = asRenderString()
/**
* Passes the element to the specified visitor.
*
* @param visitor the visitor to pass the element to.
*/
fun accept(visitor: UastVisitor) {
visitor.visitElement(this)
visitor.afterVisitElement(this)
}
/**
* Passes the element to the specified typed visitor.
*
* @param visitor the visitor to pass the element to.
*/
fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D): R = visitor.visitElement(this, data)
}
/**
* Returns a sequence including this element and its containing elements.
*/
val UElement.withContainingElements: Sequence<UElement>
get() = generateSequence(this, UElement::uastParent)

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiElement
import com.intellij.psi.PsiType
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents an expression or statement (which is considered as an expression in Uast).
*/
interface UExpression : UElement, UAnnotated {
/**
* Returns the expression value or null if the value can't be calculated.
*/
fun evaluate(): Any? = null
/**
* Returns expression type, or null if type can not be inferred, or if this expression is a statement.
*/
fun getExpressionType(): PsiType? = null
override fun accept(visitor: UastVisitor) {
visitor.visitElement(this)
visitor.afterVisitElement(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) = visitor.visitExpression(this, data)
}
/**
* Represents an annotated element.
*/
interface UAnnotated : UElement {
/**
* Returns the list of annotations applied to the current element.
*/
val annotations: List<UAnnotation>
/**
* Looks up for annotation element using the annotation qualified name.
*
* @param fqName the qualified name to search
* @return the first annotation element with the specified qualified name, or null if there is no annotation with such name.
*/
fun findAnnotation(fqName: String): UAnnotation? = annotations.firstOrNull { it.qualifiedName == fqName }
}
/**
* Represents a labeled element.
*/
interface ULabeled : UElement {
/**
* Returns the label name, or null if the label is empty.
*/
val label: String?
/**
* Returns the label identifier, or null if the label is empty.
*/
val labelIdentifier: UIdentifier?
}
/**
* In some cases (user typing, syntax error) elements, which are supposed to exist, are missing.
* The obvious example — the lack of the condition expression in [UIfExpression], e.g. `if () return`.
* [UIfExpression.condition] is required to return not-null values,
* and Uast implementation should return something instead of `null` in this case.
*
* Use [UastEmptyExpression] in this case.
*/
object UastEmptyExpression : UExpression {
override val uastParent: UElement?
get() = null
override val annotations: List<UAnnotation>
get() = emptyList()
override val psi: PsiElement?
get() = null
override fun asLogString() = log()
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiElement
import org.jetbrains.uast.internal.log
class UIdentifier(
override val psi: PsiElement?,
override val uastParent: UElement?
) : UElement {
/**
* Returns the identifier name.
*/
val name: String
get() = psi?.text ?: "<error>"
override fun asLogString() = log("Identifier ($name)")
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiElement
interface UResolvable {
/**
* Resolve the reference.
* Note that the reference is *always* resolved to an unwrapped [PsiElement], never to a [UElement].
*
* @return the resolved element, or null if the reference couldn't be resolved.
*/
fun resolve(): PsiElement?
}
fun UResolvable.resolveToUElement(): UElement? = resolve().toUElement()

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiType
import com.intellij.psi.PsiTypeVisitor
object UastErrorType : PsiType(emptyArray()) {
override fun getInternalCanonicalText() = "<ErrorType>"
override fun equalsToText(text: String) = false
override fun getCanonicalText() = internalCanonicalText
override fun getPresentableText() = canonicalText
override fun isValid() = false
override fun getResolveScope() = null
override fun getSuperTypes() = emptyArray<PsiType>()
override fun <A : Any?> accept(visitor: PsiTypeVisitor<A>) = visitor.visitType(this)
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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
/**
* Returns an identifier for the 'do' keyword.
*/
val doIdentifier: UIdentifier
/**
* Returns an identifier for the 'while' keyword.
*/
val whileIdentifier: UIdentifier
override fun accept(visitor: UastVisitor) {
if (visitor.visitDoWhileExpression(this)) return
annotations.acceptList(visitor)
condition.accept(visitor)
body.accept(visitor)
visitor.afterVisitDoWhileExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitDoWhileExpression(this, data)
override fun asRenderString() = buildString {
append("do ")
append(body.asRenderString())
appendln("while (${condition.asRenderString()})")
}
override fun asLogString() = log()
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a
*
* `for (element : collectionOfElements) {
* // body
* }`
*
* loop expression.
*/
interface UForEachExpression : ULoopExpression {
/**
* Returns the loop variable.
*/
val variable: UParameter
/**
* Returns the iterated value (collection, sequence, iterable etc.)
*/
val iteratedValue: UExpression
/**
* Returns the identifier for the 'for' ('foreach') keyword.
*/
val forIdentifier: UIdentifier
override fun accept(visitor: UastVisitor) {
if (visitor.visitForEachExpression(this)) return
annotations.acceptList(visitor)
iteratedValue.accept(visitor)
body.accept(visitor)
visitor.afterVisitForEachExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitForEachExpression(this, data)
override fun asRenderString() = buildString {
append("for (")
append(variable.name)
append(" : ")
append(iteratedValue.asRenderString())
append(") ")
append(body.asRenderString())
}
override fun asLogString() = log()
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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?
/**
* Returns the identifier for the 'for' keyword.
*/
val forIdentifier: UIdentifier
override fun accept(visitor: UastVisitor) {
if (visitor.visitForExpression(this)) return
annotations.acceptList(visitor)
declaration?.accept(visitor)
condition?.accept(visitor)
update?.accept(visitor)
body.accept(visitor)
visitor.afterVisitForExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitForExpression(this, data)
override fun asRenderString() = buildString {
append("for (")
declaration?.let { append(it.asRenderString()) }
append("; ")
condition?.let { append(it.asRenderString()) }
append("; ")
update?.let { append(it.asRenderString()) }
append(") ")
append(body.asRenderString())
}
override fun asLogString() = log()
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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 thenExpression: UExpression?
/**
* Returns the expression which is executed if the condition is false, or null if the expression is empty.
*/
val elseExpression: UExpression?
/**
* Returns true if the expression is ternary (condition ? trueExpression : falseExpression).
*/
val isTernary: Boolean
/**
* Returns an identifier for the 'if' keyword.
*/
val ifIdentifier: UIdentifier
/**
* Returns an identifier for the 'else' keyword, or null if the conditional expression has not the 'else' part.
*/
val elseIdentifier: UIdentifier?
override fun accept(visitor: UastVisitor) {
if (visitor.visitIfExpression(this)) return
annotations.acceptList(visitor)
condition.accept(visitor)
thenExpression?.accept(visitor)
elseExpression?.accept(visitor)
visitor.afterVisitIfExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitIfExpression(this, data)
override fun asLogString() = log()
override fun asRenderString() = buildString {
if (isTernary) {
append("(" + condition.asRenderString() + ")")
append(" ? ")
append("(" + (thenExpression?.asRenderString() ?: "<noexpr>") + ")")
append(" : ")
append("(" + (elseExpression?.asRenderString() ?: "<noexpr>") + ")")
} else {
append("if (${condition.asRenderString()}) ")
thenExpression?.let { append(it.asRenderString()) }
val elseBranch = elseExpression
if (elseBranch != null && elseBranch !is UastEmptyExpression) {
if (thenExpression !is UBlockExpression) append(" ")
append("else ")
append(elseBranch.asRenderString())
}
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2000-2017 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.UastTypedVisitor
/**
* Represents a loop expression.
*/
interface ULoopExpression : UExpression {
/**
* Returns the loop body [UExpression].
*/
val body: UExpression
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) = visitor.visitLoopExpression(this, data)
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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.
The body should contain [USwitchClauseExpression] expressions.
*/
val body: UExpressionList
/**
* Returns an identifier for the 'switch' ('case', 'when', ...) keyword.
*/
val switchIdentifier: UIdentifier
override fun accept(visitor: UastVisitor) {
if (visitor.visitSwitchExpression(this)) return
annotations.acceptList(visitor)
expression?.accept(visitor)
body.accept(visitor)
visitor.afterVisitSwitchExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitSwitchExpression(this, data)
override fun asLogString() = log<USwitchExpression>()
override fun asRenderString() = buildString {
val expr = expression?.let { "(" + it.asRenderString() + ") " } ?: ""
appendln("switch $expr")
appendln(body.asRenderString())
}
}
/**
* 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
annotations.acceptList(visitor)
caseValues.acceptList(visitor)
visitor.afterVisitSwitchClauseExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitSwitchClauseExpression(this, data)
override fun asRenderString() = caseValues.joinToString { it.asRenderString() } + " -> "
override fun asLogString() = "USwitchClauseExpression"
}
/**
* 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: UExpressionList
override fun accept(visitor: UastVisitor) {
if (visitor.visitSwitchClauseExpression(this)) return
annotations.acceptList(visitor)
caseValues.acceptList(visitor)
body.accept(visitor)
visitor.afterVisitSwitchClauseExpression(this)
}
override fun asRenderString() = caseValues.joinToString { it.asRenderString() } + " -> " + body.asRenderString()
override fun asLogString() = log()
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiType
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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 `true` if the try expression is a try-with-resources expression.
*/
val hasResources: Boolean
/**
* Returns the list of resource variables declared in this expression, or an empty list if this expression is not a `try-with-resources` expression.
*/
val resourceVariables: List<UVariable>
/**
* 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?
/**
* Returns an identifier for the 'try' keyword.
*/
val tryIdentifier: UIdentifier
/**
* Returns an identifier for the 'finally' keyword, or null if the 'try' expression has not a 'finally' clause.
*/
val finallyIdentifier: UIdentifier?
override fun accept(visitor: UastVisitor) {
if (visitor.visitTryExpression(this)) return
annotations.acceptList(visitor)
resourceVariables.acceptList(visitor)
tryClause.accept(visitor)
catchClauses.acceptList(visitor)
finallyClause?.accept(visitor)
visitor.afterVisitTryExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitTryExpression(this, data)
override fun asRenderString() = buildString {
append("try ")
if (hasResources) {
append("(")
append(resourceVariables.joinToString("\n") { it.asRenderString() })
append(")")
}
appendln(tryClause.asRenderString().trim('\n', '\r'))
catchClauses.forEach { appendln(it.asRenderString().trim('\n', '\r')) }
finallyClause?.let { append("finally ").append(it.asRenderString().trim('\n', '\r')) }
}
override fun asLogString() = log(if (hasResources) "with resources" else "")
}
/**
* 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<UParameter>
/**
* Returns the exception type references for this `catch` clause.
*/
val typeReferences: List<UTypeReferenceExpression>
/**
* Returns the expression types for this `catch` clause.
*/
val types: List<PsiType>
get() = typeReferences.map { it.type }
override fun accept(visitor: UastVisitor) {
if (visitor.visitCatchClause(this)) return
body.accept(visitor)
visitor.afterVisitCatchClause(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitCatchClause(this, data)
override fun asLogString() = log(parameters.joinToString { it.name ?: "<error>" })
override fun asRenderString() = "catch (e) " + body.asRenderString()
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a
*
* `while (condition) {
* // body
* }`
*
* expression.
*/
interface UWhileExpression : ULoopExpression {
/**
* Returns the loop condition.
*/
val condition: UExpression
/**
* Returns an identifier for the 'while' keyword.
*/
val whileIdentifier: UIdentifier
override fun accept(visitor: UastVisitor) {
if (visitor.visitWhileExpression(this)) return
annotations.acceptList(visitor)
condition.accept(visitor)
body.accept(visitor)
visitor.afterVisitWhileExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitWhileExpression(this, data)
override fun asRenderString() = buildString {
append("while (${condition.asRenderString()}) ")
append(body.asRenderString())
}
override fun asLogString() = log()
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiClass
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* An annotation wrapper to be used in [UastVisitor].
*/
interface UAnnotation : UElement, UResolvable {
/**
* Returns the annotation qualified name.
*/
val qualifiedName: String?
/**
* Returns the annotation class, or null if the class reference was not resolved.
*/
override fun resolve(): PsiClass?
/**
* Returns the annotation values.
*/
val attributeValues: List<UNamedExpression>
fun findAttributeValue(name: String?): UExpression?
fun findDeclaredAttributeValue(name: String?): UExpression?
override fun asRenderString() = buildString {
append("@")
append(qualifiedName)
if(attributeValues.isNotEmpty()) {
attributeValues.joinTo(
buffer = this,
prefix = "(",
postfix = ")",
transform = UNamedExpression::asRenderString)
}
}
override fun asLogString() = log("fqName = $qualifiedName")
override fun accept(visitor: UastVisitor) {
if (visitor.visitAnnotation(this)) return
attributeValues.acceptList(visitor)
visitor.afterVisitAnnotation(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitAnnotation(this, data)
}

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiAnonymousClass
import com.intellij.psi.PsiClass
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* A class wrapper to be used in [UastVisitor].
*/
interface UClass : UDeclaration, PsiClass {
override val psi: PsiClass
/**
* Returns a [UClass] wrapper of the superclass of this class, or null if this class is [java.lang.Object].
*/
override fun getSuperClass(): UClass? {
val superClass = psi.superClass ?: return null
return getUastContext().convertWithParent(superClass)
}
val uastSuperTypes: List<UTypeReferenceExpression>
/**
* Returns [UDeclaration] wrappers for the class declarations.
*/
val uastDeclarations: List<UDeclaration>
override fun getFields(): Array<UField> =
psi.fields.map { getLanguagePlugin().convert<UField>(it, this) }.toTypedArray()
override fun getInitializers(): Array<UClassInitializer> =
psi.initializers.map { getLanguagePlugin().convert<UClassInitializer>(it, this) }.toTypedArray()
override fun getMethods(): Array<UMethod> =
psi.methods.map { getLanguagePlugin().convert<UMethod>(it, this) }.toTypedArray()
override fun getInnerClasses(): Array<UClass> =
psi.innerClasses.map { getLanguagePlugin().convert<UClass>(it, this) }.toTypedArray()
override fun asLogString() = log("name = $name")
override fun accept(visitor: UastVisitor) {
if (visitor.visitClass(this)) return
annotations.acceptList(visitor)
uastDeclarations.acceptList(visitor)
visitor.afterVisitClass(this)
}
override fun asRenderString() = buildString {
append(psi.renderModifiers())
val kind = when {
psi.isAnnotationType -> "annotation"
psi.isInterface -> "interface"
psi.isEnum -> "enum"
else -> "class"
}
append(kind).append(' ').append(psi.name)
val superTypes = uastSuperTypes
if (superTypes.isNotEmpty()) {
append(" : ")
append(superTypes.joinToString { it.asRenderString() })
}
appendln(" {")
uastDeclarations.forEachIndexed { index, declaration ->
appendln(declaration.asRenderString().withMargin)
}
append("}")
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitClass(this, data)
}
interface UAnonymousClass : UClass, PsiAnonymousClass {
override val psi: PsiAnonymousClass
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiClassInitializer
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* A class initializer wrapper to be used in [UastVisitor].
*/
interface UClassInitializer : UDeclaration, PsiClassInitializer {
override val psi: PsiClassInitializer
/**
* Returns the body of this class initializer.
*/
val uastBody: UExpression
@Deprecated("Use uastBody instead.", ReplaceWith("uastBody"))
override fun getBody() = psi.body
override fun accept(visitor: UastVisitor) {
if (visitor.visitInitializer(this)) return
annotations.acceptList(visitor)
uastBody.accept(visitor)
visitor.afterVisitInitializer(this)
}
override fun asRenderString() = buildString {
append(modifierList)
appendln(uastBody.asRenderString().withMargin)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitClassInitializer(this, data)
override fun asLogString() = log("isStatic = $isStatic")
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiElement
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiModifierListOwner
import org.jetbrains.uast.visitor.UastTypedVisitor
/**
* A [PsiElement] declaration wrapper.
*/
interface UDeclaration : UElement, PsiModifierListOwner, UAnnotated {
/**
* Returns the original declaration (which is *always* unwrapped, never a [UDeclaration]).
*/
override val psi: PsiModifierListOwner
override fun getOriginalElement(): PsiElement? = psi.originalElement
/**
* Returns the declaration name identifier, or null if the declaration is anonymous.
*/
val uastAnchor: UElement?
/**
* Returns `true` if this declaration has a [PsiModifier.STATIC] modifier.
*/
val isStatic: Boolean
get() = hasModifierProperty(PsiModifier.STATIC)
/**
* Returns `true` if this declaration has a [PsiModifier.FINAL] modifier.
*/
val isFinal: Boolean
get() = hasModifierProperty(PsiModifier.FINAL)
/**
* Returns a declaration visibility.
*/
val visibility: UastVisibility
get() = UastVisibility[this]
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) = visitor.visitDeclaration(this, data)
}
fun UElement.getContainingDeclaration() = withContainingElements.filterIsInstance<UDeclaration>().firstOrNull()

View File

@@ -0,0 +1,94 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiFile
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a Uast file.
*/
interface UFile : UElement, UAnnotated {
/**
* Returns the original [PsiFile].
*/
override val psi: PsiFile
/**
* Returns the Java package name of this file.
* Returns an empty [String] for the default package.
*/
val packageName: String
/**
* Returns the import statements for this file.
*/
val imports: List<UImportStatement>
/**
* Returns the list of top-level classes declared in this file.
*/
val classes: List<UClass>
/**
* Returns the plugin for a language used in this file.
*/
val languagePlugin: UastLanguagePlugin
/**
* Returns all comments in file.
*/
val allCommentsInFile: List<UComment>
override fun asLogString() = log("package = $packageName")
override fun asRenderString() = buildString {
val packageName = this@UFile.packageName
if (packageName.isNotEmpty()) appendln("package $packageName").appendln()
val imports = this@UFile.imports
if (imports.isNotEmpty()) {
imports.forEach { appendln(it.asRenderString()) }
appendln()
}
classes.forEachIndexed { index, clazz ->
if (index > 0) appendln()
appendln(clazz.asRenderString())
}
}
/**
* [UFile] is a top-level element of the Uast hierarchy, thus the [uastParent] always returns null for it.
*/
override val uastParent: UElement?
get() = null
override fun accept(visitor: UastVisitor) {
if (visitor.visitFile(this)) return
annotations.acceptList(visitor)
imports.acceptList(visitor)
classes.acceptList(visitor)
visitor.afterVisitFile(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitFile(this, data)
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2000-2017 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.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents an import statement.
*/
interface UImportStatement : UResolvable, UElement {
/**
* Returns true if the statement is an import-on-demand (star-import) statement.
*/
val isOnDemand: Boolean
/**
* Returns the reference to the imported element.
*/
val importReference: UElement?
override fun asLogString() = log("isOnDemand = $isOnDemand")
override fun asRenderString() = "import " + (importReference?.asRenderString() ?: "<error>")
override fun accept(visitor: UastVisitor) {
if (visitor.visitImportStatement(this)) return
visitor.afterVisitImportStatement(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitImportStatement(this, data)
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiAnnotationMethod
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* A method visitor to be used in [UastVisitor].
*/
interface UMethod : UDeclaration, PsiMethod {
override val psi: PsiMethod
/**
* Returns the body expression (which can be also a [UBlockExpression]).
*/
val uastBody: UExpression?
/**
* Returns the method parameters.
*/
val uastParameters: List<UParameter>
/**
* Returns true, if the method overrides a method of a super class.
*/
val isOverride: Boolean
@Deprecated("Use uastBody instead.", ReplaceWith("uastBody"))
override fun getBody() = psi.body
override fun accept(visitor: UastVisitor) {
if (visitor.visitMethod(this)) return
annotations.acceptList(visitor)
uastParameters.acceptList(visitor)
uastBody?.accept(visitor)
visitor.afterVisitMethod(this)
}
override fun asRenderString() = buildString {
if (annotations.isNotEmpty()) {
annotations.joinTo(buffer = this, separator = "\n", postfix = "\n", transform = UAnnotation::asRenderString)
}
append(psi.renderModifiers())
append("fun ").append(name)
uastParameters.joinTo(this, prefix = "(", postfix = ")") {
it.name + ": " + it.type.canonicalText
}
psi.returnType?.let { append(" : " + it.canonicalText) }
val body = uastBody
append(when (body) {
is UBlockExpression -> " " + body.asRenderString()
else -> " = " + ((body ?: UastEmptyExpression).asRenderString())
})
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitMethod(this, data)
override fun asLogString() = log("name = $name")
}
interface UAnnotationMethod : UMethod, PsiAnnotationMethod {
override val psi: PsiAnnotationMethod
/**
* Returns the default value of this annotation method.
*/
val uastDefaultValue: UExpression?
override fun getDefaultValue() = psi.defaultValue
override fun accept(visitor: UastVisitor) {
if (visitor.visitMethod(this)) return
annotations.acceptList(visitor)
uastParameters.acceptList(visitor)
uastBody?.accept(visitor)
uastDefaultValue?.accept(visitor)
visitor.afterVisitMethod(this)
}
override fun asLogString() = log("name = $name")
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.*
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* A variable wrapper to be used in [UastVisitor].
*/
interface UVariable : UDeclaration, PsiVariable {
override val psi: PsiVariable
/**
* Returns the variable initializer or the parameter default value, or null if the variable has not an initializer.
*/
val uastInitializer: UExpression?
/**
* Returns variable type reference.
*/
val typeReference: UTypeReferenceExpression?
override fun accept(visitor: UastVisitor) {
if (visitor.visitVariable(this)) return
visitContents(visitor)
visitor.afterVisitVariable(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitVariable(this, data)
@Deprecated("Use uastInitializer instead.", ReplaceWith("uastInitializer"))
override fun getInitializer() = psi.initializer
override fun asLogString() = log("name = $name")
override fun asRenderString() = buildString {
append(psi.renderModifiers())
append("var ").append(psi.name).append(": ").append(psi.type.getCanonicalText(false))
uastInitializer?.let { initializer -> append(" = " + initializer.asRenderString()) }
}
}
private fun UVariable.visitContents(visitor: UastVisitor) {
annotations.acceptList(visitor)
uastInitializer?.accept(visitor)
}
interface UParameter : UVariable, PsiParameter {
override val psi: PsiParameter
override fun asLogString() = log("name = $name")
override fun accept(visitor: UastVisitor) {
if (visitor.visitParameter(this)) return
visitContents(visitor)
visitor.afterVisitParameter(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) = visitor.visitParameter(this, data)
}
interface UField : UVariable, PsiField {
override val psi: PsiField
override fun asLogString() = log("name = $name")
override fun accept(visitor: UastVisitor) {
if (visitor.visitField(this)) return
visitContents(visitor)
visitor.afterVisitField(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) = visitor.visitField(this, data)
}
interface ULocalVariable : UVariable, PsiLocalVariable {
override val psi: PsiLocalVariable
override fun asLogString() = log("name = $name")
override fun accept(visitor: UastVisitor) {
if (visitor.visitLocalVariable(this)) return
visitContents(visitor)
visitor.afterVisitLocalVariable(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) = visitor.visitLocalVariable(this, data)
}
interface UEnumConstant : UField, UCallExpression, PsiEnumConstant {
override val psi: PsiEnumConstant
val initializingClass: UClass?
override fun asLogString() = log("name = $name")
override fun accept(visitor: UastVisitor) {
if (visitor.visitEnumConstant(this)) return
annotations.acceptList(visitor)
methodIdentifier?.accept(visitor)
classReference?.accept(visitor)
valueArguments.acceptList(visitor)
initializingClass?.accept(visitor)
visitor.afterVisitEnumConstant(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitEnumConstantExpression(this, data)
override fun asRenderString() = buildString {
append(name ?: "<ERROR>")
if (valueArguments.isNotEmpty()) {
valueArguments.joinTo(this, prefix = "(", postfix = ")", transform = UExpression::asRenderString)
}
initializingClass?.let {
appendln(" {")
it.uastDeclarations.forEachIndexed { index, declaration ->
appendln(declaration.asRenderString().withMargin)
}
append("}")
}
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2000-2017 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.evaluation
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UVariable
import org.jetbrains.uast.values.UValue
import org.jetbrains.uast.values.UVariableValue
abstract class AbstractEvaluationState(override val boundElement: UElement? = null) : UEvaluationState {
override fun assign(variable: UVariable, value: UValue, at: UElement): AbstractEvaluationState {
val variableValue = UVariableValue.create(variable, value)
val prevVariableValue = this[variable]
return if (prevVariableValue == variableValue) this
else DelegatingEvaluationState(
boundElement = at,
variableValue = variableValue,
baseState = this
)
}
override fun merge(otherState: UEvaluationState) =
if (this == otherState) this else MergingEvaluationState(this, otherState)
override fun equals(other: Any?) =
other is UEvaluationState && variables == other.variables && variables.all { this[it] == other[it] }
override fun hashCode(): Int {
var result = 31
result = result * 19 + variables.hashCode()
result = result * 19 + variables.map { this[it].hashCode() }.sum()
return result
}
override fun toString() = variables.joinToString(prefix = "[", postfix = "]", separator = ", ") {
"${it.psi.name} = ${this[it]}"
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright 2000-2017 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.evaluation
import com.intellij.lang.Language
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.*
import org.jetbrains.uast.values.UUndeterminedValue
import org.jetbrains.uast.values.UValue
abstract class AbstractEvaluatorExtension(override val language: Language) : UEvaluatorExtension {
override fun evaluatePostfix(
operator: UastPostfixOperator,
operandValue: UValue,
state: UEvaluationState
): UEvaluationInfo = UUndeterminedValue to state
override fun evaluatePrefix(
operator: UastPrefixOperator,
operandValue: UValue,
state: UEvaluationState
): UEvaluationInfo = UUndeterminedValue to state
override fun evaluateBinary(
binaryExpression: UBinaryExpression,
leftValue: UValue,
rightValue: UValue,
state: UEvaluationState
): UEvaluationInfo = UUndeterminedValue to state
override fun evaluateQualified(
accessType: UastQualifiedExpressionAccessType,
receiverInfo: UEvaluationInfo,
selectorInfo: UEvaluationInfo
): UEvaluationInfo = UUndeterminedValue to selectorInfo.state
override fun evaluateMethodCall(
target: PsiMethod,
argumentValues: List<UValue>,
state: UEvaluationState
): UEvaluationInfo = UUndeterminedValue to state
override fun evaluateVariable(
variable: UVariable,
state: UEvaluationState
): UEvaluationInfo = UUndeterminedValue to state
}
abstract class SimpleEvaluatorExtension : AbstractEvaluatorExtension(Language.ANY) {
override final fun evaluatePostfix(operator: UastPostfixOperator, operandValue: UValue, state: UEvaluationState): UEvaluationInfo {
val result = evaluatePostfix(operator, operandValue)
return if (result != UUndeterminedValue)
result.toConstant() to state
else
super.evaluatePostfix(operator, operandValue, state)
}
open fun evaluatePostfix(operator: UastPostfixOperator, operandValue: UValue): Any? = UUndeterminedValue
override final fun evaluatePrefix(operator: UastPrefixOperator, operandValue: UValue, state: UEvaluationState): UEvaluationInfo {
val result = evaluatePrefix(operator, operandValue)
return if (result != UUndeterminedValue)
result.toConstant() to state
else
super.evaluatePrefix(operator, operandValue, state)
}
open fun evaluatePrefix(operator: UastPrefixOperator, operandValue: UValue): Any? = UUndeterminedValue
override final fun evaluateBinary(binaryExpression: UBinaryExpression, leftValue: UValue, rightValue: UValue, state: UEvaluationState): UEvaluationInfo {
val result = evaluateBinary(binaryExpression, leftValue, rightValue)
return if (result != UUndeterminedValue)
result.toConstant() to state
else
super.evaluateBinary(binaryExpression, leftValue, rightValue, state)
}
open fun evaluateBinary(binaryExpression: UBinaryExpression, leftValue: UValue, rightValue: UValue): Any? = UUndeterminedValue
override final fun evaluateMethodCall(target: PsiMethod, argumentValues: List<UValue>, state: UEvaluationState): UEvaluationInfo {
val result = evaluateMethodCall(target, argumentValues)
return if (result != UUndeterminedValue)
result.toConstant() to state
else
super.evaluateMethodCall(target, argumentValues, state)
}
open fun evaluateMethodCall(target: PsiMethod, argumentValues: List<UValue>): Any? = UUndeterminedValue
override final fun evaluateVariable(variable: UVariable, state: UEvaluationState): UEvaluationInfo {
val result = evaluateVariable(variable)
return if (result != UUndeterminedValue)
result.toConstant() to state
else
super.evaluateVariable(variable, state)
}
open fun evaluateVariable(variable: UVariable): Any? = UUndeterminedValue
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2000-2017 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.evaluation
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UVariable
import org.jetbrains.uast.values.UVariableValue
class DelegatingEvaluationState(
boundElement: UElement,
private val variableValue: UVariableValue,
private val baseState: UEvaluationState
) : AbstractEvaluationState(boundElement) {
override val variables = baseState.variables + variableValue.variable
override fun get(variable: UVariable) =
if (variable == variableValue.variable) variableValue else baseState[variable]
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2000-2017 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.evaluation
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UVariable
import org.jetbrains.uast.values.UUndeterminedValue
class EmptyEvaluationState(boundElement: UElement) : AbstractEvaluationState(boundElement) {
override val variables: Set<UVariable>
get() = emptySet()
override fun get(variable: UVariable) = UUndeterminedValue
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2000-2017 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.evaluation
import org.jetbrains.uast.*
import org.jetbrains.uast.values.UUndeterminedValue
import org.jetbrains.uast.visitor.UastVisitor
import java.lang.ref.SoftReference
import java.util.*
class MapBasedEvaluationContext(
override val uastContext: UastContext,
override val extensions: List<UEvaluatorExtension>
) : UEvaluationContext {
private val evaluators = WeakHashMap<UDeclaration, SoftReference<UEvaluator>>()
override fun analyzeAll(file: UFile, state: UEvaluationState): UEvaluationContext {
file.accept(object: UastVisitor {
override fun visitElement(node: UElement) = false
override fun visitMethod(node: UMethod): Boolean {
analyze(node, state)
return true
}
override fun visitVariable(node: UVariable): Boolean {
if (node is UField) {
analyze(node, state)
return true
}
else return false
}
})
return this
}
private fun getOrCreateEvaluator(declaration: UDeclaration, state: UEvaluationState? = null) =
evaluators[declaration]?.get() ?: createEvaluator(uastContext, extensions).apply {
when (declaration) {
is UMethod -> this.analyze(declaration, state ?: declaration.createEmptyState())
is UField -> this.analyze(declaration, state ?: declaration.createEmptyState())
}
evaluators[declaration] = SoftReference(this)
}
override fun analyze(declaration: UDeclaration, state: UEvaluationState) = getOrCreateEvaluator(declaration, state)
override fun getEvaluator(declaration: UDeclaration) = getOrCreateEvaluator(declaration)
private fun getEvaluator(expression: UExpression): UEvaluator? {
var containingElement = expression.uastParent
while (containingElement != null) {
if (containingElement is UDeclaration) {
val evaluator = evaluators[containingElement]?.get()
if (evaluator != null) {
return evaluator
}
}
containingElement = containingElement.uastParent
}
return null
}
override fun valueOf(expression: UExpression) =
getEvaluator(expression)?.evaluate(expression) ?: UUndeterminedValue
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2000-2017 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.evaluation
import org.jetbrains.uast.UVariable
class MergingEvaluationState(
private val first: UEvaluationState,
private val second: UEvaluationState
) : AbstractEvaluationState() {
override val variables = first.variables + second.variables
override fun get(variable: UVariable) = first[variable].merge(second[variable])
}

View File

@@ -0,0 +1,652 @@
/*
* Copyright 2000-2017 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.evaluation
import com.intellij.psi.JavaPsiFacade
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiType
import com.intellij.psi.PsiVariable
import org.jetbrains.uast.*
import org.jetbrains.uast.values.*
import org.jetbrains.uast.values.UNothingValue.JumpKind.BREAK
import org.jetbrains.uast.values.UNothingValue.JumpKind.CONTINUE
import org.jetbrains.uast.visitor.UastTypedVisitor
class TreeBasedEvaluator(
override val context: UastContext,
val extensions: List<UEvaluatorExtension>
) : UastTypedVisitor<UEvaluationState, UEvaluationInfo>, UEvaluator {
override fun getDependents(dependency: UDependency): Set<UValue> {
return resultCache.values.map { it.value }.filter { dependency in it.dependencies }.toSet()
}
private val inputStateCache = mutableMapOf<UExpression, UEvaluationState>()
private val resultCache = mutableMapOf<UExpression, UEvaluationInfo>()
override fun visitElement(node: UElement, data: UEvaluationState): UEvaluationInfo {
return UEvaluationInfo(UUndeterminedValue, data).apply {
if (node is UExpression) {
this storeResultFor node
}
}
}
override fun analyze(method: UMethod, state: UEvaluationState) {
method.uastBody?.accept(this, state)
}
override fun analyze(field: UField, state: UEvaluationState) {
field.uastInitializer?.accept(this, state)
}
override fun evaluate(expression: UExpression, state: UEvaluationState?): UValue {
if (state == null) {
val result = resultCache[expression]
if (result != null) return result.value
}
val inputState = state ?: inputStateCache[expression] ?: expression.createEmptyState()
return expression.accept(this, inputState).value
}
// ----------------------- //
private infix fun UValue.to(state: UEvaluationState) = UEvaluationInfo(this, state)
private infix fun UEvaluationInfo.storeResultFor(expression: UExpression) = apply {
resultCache[expression] = this
}
override fun visitLiteralExpression(node: ULiteralExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val value = node.value
return value.toConstant(node) to data storeResultFor node
}
override fun visitClassLiteralExpression(node: UClassLiteralExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
return (node.type?.let { value -> UClassConstant(value, node) } ?: UUndeterminedValue) to data storeResultFor node
}
override fun visitReturnExpression(node: UReturnExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val argument = node.returnExpression
return UValue.UNREACHABLE to (argument?.accept(this, data)?.state ?: data) storeResultFor node
}
override fun visitBreakExpression(node: UBreakExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
return UNothingValue(node) to data storeResultFor node
}
override fun visitContinueExpression(node: UContinueExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
return UNothingValue(node) to data storeResultFor node
}
override fun visitThrowExpression(node: UThrowExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
return UValue.UNREACHABLE to data storeResultFor node
}
// ----------------------- //
override fun visitSimpleNameReferenceExpression(
node: USimpleNameReferenceExpression,
data: UEvaluationState
): UEvaluationInfo {
inputStateCache[node] = data
val resolvedElement = node.resolveToUElement()
return when (resolvedElement) {
is UEnumConstant -> UEnumEntryValueConstant(resolvedElement, node)
is UField -> if (resolvedElement.hasModifierProperty(PsiModifier.FINAL)) {
data[resolvedElement].ifUndetermined {
val helper = JavaPsiFacade.getInstance(resolvedElement.project).constantEvaluationHelper
val evaluated = helper.computeConstantExpression(resolvedElement.initializer)
evaluated?.toConstant() ?: UUndeterminedValue
}
}
else {
return super.visitSimpleNameReferenceExpression(node, data)
}
is UVariable -> data[resolvedElement].ifUndetermined {
node.evaluateViaExtensions { evaluateVariable(resolvedElement, data) }?.value ?: UUndeterminedValue
}
else -> return super.visitSimpleNameReferenceExpression(node, data)
} to data storeResultFor node
}
override fun visitReferenceExpression(
node: UReferenceExpression,
data: UEvaluationState
): UEvaluationInfo {
inputStateCache[node] = data
return UCallResultValue(node, emptyList()) to data storeResultFor node
}
// ----------------------- //
private fun UExpression.assign(
valueInfo: UEvaluationInfo,
operator: UastBinaryOperator.AssignOperator = UastBinaryOperator.ASSIGN
): UEvaluationInfo {
this.accept(this@TreeBasedEvaluator, valueInfo.state)
if (this is UResolvable) {
val resolvedElement = resolve()
if (resolvedElement is PsiVariable) {
val variable = context.getVariable(resolvedElement)
val currentValue = valueInfo.state[variable]
val result = when (operator) {
UastBinaryOperator.ASSIGN -> valueInfo.value
UastBinaryOperator.PLUS_ASSIGN -> currentValue + valueInfo.value
UastBinaryOperator.MINUS_ASSIGN -> currentValue - valueInfo.value
UastBinaryOperator.MULTIPLY_ASSIGN -> currentValue * valueInfo.value
UastBinaryOperator.DIVIDE_ASSIGN -> currentValue / valueInfo.value
UastBinaryOperator.REMAINDER_ASSIGN -> currentValue % valueInfo.value
UastBinaryOperator.AND_ASSIGN -> currentValue bitwiseAnd valueInfo.value
UastBinaryOperator.OR_ASSIGN -> currentValue bitwiseOr valueInfo.value
UastBinaryOperator.XOR_ASSIGN -> currentValue bitwiseXor valueInfo.value
UastBinaryOperator.SHIFT_LEFT_ASSIGN -> currentValue shl valueInfo.value
UastBinaryOperator.SHIFT_RIGHT_ASSIGN -> currentValue shr valueInfo.value
UastBinaryOperator.UNSIGNED_SHIFT_RIGHT_ASSIGN -> currentValue ushr valueInfo.value
else -> UUndeterminedValue
}
return result to valueInfo.state.assign(variable, result, this)
}
}
return UUndeterminedValue to valueInfo.state
}
private fun UExpression.assign(
operator: UastBinaryOperator.AssignOperator,
value: UExpression,
data: UEvaluationState
) = assign(value.accept(this@TreeBasedEvaluator, data), operator)
override fun visitPrefixExpression(node: UPrefixExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val operandInfo = node.operand.accept(this, data)
val operandValue = operandInfo.value
if (!operandValue.reachable) return operandInfo storeResultFor node
return when (node.operator) {
UastPrefixOperator.UNARY_PLUS -> operandValue
UastPrefixOperator.UNARY_MINUS -> -operandValue
UastPrefixOperator.LOGICAL_NOT -> !operandValue
UastPrefixOperator.INC -> {
val resultValue = operandValue.inc()
val newState = node.operand.assign(resultValue to operandInfo.state).state
return resultValue to newState storeResultFor node
}
UastPrefixOperator.DEC -> {
val resultValue = operandValue.dec()
val newState = node.operand.assign(resultValue to operandInfo.state).state
return resultValue to newState storeResultFor node
}
else -> {
return node.evaluateViaExtensions { evaluatePrefix(node.operator, operandValue, operandInfo.state) }
?: UUndeterminedValue to operandInfo.state storeResultFor node
}
} to operandInfo.state storeResultFor node
}
inline fun UElement.evaluateViaExtensions(block: UEvaluatorExtension.() -> UEvaluationInfo): UEvaluationInfo? {
for (ext in extensions) {
val extResult = ext.block()
if (extResult.value != UUndeterminedValue) return extResult
}
languageExtension()?.block()?.let { if (it.value != UUndeterminedValue) return it }
return null
}
override fun visitPostfixExpression(node: UPostfixExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val operandInfo = node.operand.accept(this, data)
val operandValue = operandInfo.value
if (!operandValue.reachable) return operandInfo storeResultFor node
return when (node.operator) {
UastPostfixOperator.INC -> {
operandValue to node.operand.assign(operandValue.inc() to operandInfo.state).state
}
UastPostfixOperator.DEC -> {
operandValue to node.operand.assign(operandValue.dec() to operandInfo.state).state
}
else -> {
return node.evaluateViaExtensions { evaluatePostfix(node.operator, operandValue, operandInfo.state) }
?: UUndeterminedValue to operandInfo.state storeResultFor node
}
} storeResultFor node
}
private fun UastBinaryOperator.evaluate(left: UValue, right: UValue): UValue? =
when (this) {
UastBinaryOperator.PLUS -> left + right
UastBinaryOperator.MINUS -> left - right
UastBinaryOperator.MULTIPLY -> left * right
UastBinaryOperator.DIV -> left / right
UastBinaryOperator.MOD -> left % right
UastBinaryOperator.EQUALS -> left valueEquals right
UastBinaryOperator.NOT_EQUALS -> left valueNotEquals right
UastBinaryOperator.IDENTITY_EQUALS -> left identityEquals right
UastBinaryOperator.IDENTITY_NOT_EQUALS -> left identityNotEquals right
UastBinaryOperator.GREATER -> left greater right
UastBinaryOperator.LESS -> left less right
UastBinaryOperator.GREATER_OR_EQUALS -> left greaterOrEquals right
UastBinaryOperator.LESS_OR_EQUALS -> left lessOrEquals right
UastBinaryOperator.LOGICAL_AND -> left and right
UastBinaryOperator.LOGICAL_OR -> left or right
UastBinaryOperator.BITWISE_AND -> left bitwiseAnd right
UastBinaryOperator.BITWISE_OR -> left bitwiseOr right
UastBinaryOperator.BITWISE_XOR -> left bitwiseXor right
UastBinaryOperator.SHIFT_LEFT -> left shl right
UastBinaryOperator.SHIFT_RIGHT -> left shr right
UastBinaryOperator.UNSIGNED_SHIFT_RIGHT -> left ushr right
else -> null
}
override fun visitBinaryExpression(node: UBinaryExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val operator = node.operator
if (operator is UastBinaryOperator.AssignOperator) {
return node.leftOperand.assign(operator, node.rightOperand, data) storeResultFor node
}
val leftInfo = node.leftOperand.accept(this, data)
if (!leftInfo.reachable) {
return leftInfo storeResultFor node
}
val rightInfo = node.rightOperand.accept(this, leftInfo.state)
operator.evaluate(leftInfo.value, rightInfo.value)?.let {
return it to rightInfo.state storeResultFor node
}
return node.evaluateViaExtensions { evaluateBinary(node, leftInfo.value, rightInfo.value, rightInfo.state) }
?: UUndeterminedValue to rightInfo.state storeResultFor node
}
override fun visitPolyadicExpression(node: UPolyadicExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val operator = node.operator
val infos = node.operands.map {
it.accept(this, data).apply {
if (!reachable) {
return this storeResultFor node
}
}
}
val lastInfo = infos.last()
val firstValue = infos.first().value
val restInfos = infos.drop(1)
return restInfos.fold(firstValue) { accumulator, info ->
operator.evaluate(accumulator, info.value) ?: return UUndeterminedValue to info.state storeResultFor node
} to lastInfo.state storeResultFor node
}
private fun evaluateTypeCast(operandInfo: UEvaluationInfo, type: PsiType): UEvaluationInfo {
val constant = operandInfo.value.toConstant() ?: return UUndeterminedValue to operandInfo.state
val resultConstant = when (type) {
PsiType.BOOLEAN -> {
constant as? UBooleanConstant
}
PsiType.CHAR -> when (constant) {
// Join the following three in UNumericConstant
// when https://youtrack.jetbrains.com/issue/KT-14868 is fixed
is UIntConstant -> UCharConstant(constant.value.toChar())
is ULongConstant -> UCharConstant(constant.value.toChar())
is UFloatConstant -> UCharConstant(constant.value.toChar())
is UCharConstant -> constant
else -> null
}
PsiType.LONG -> {
(constant as? UNumericConstant)?.value?.toLong()?.let { value -> ULongConstant(value) }
}
PsiType.BYTE, PsiType.SHORT, PsiType.INT -> {
(constant as? UNumericConstant)?.value?.toInt()?.let { UIntConstant(it, type) }
}
PsiType.FLOAT, PsiType.DOUBLE -> {
(constant as? UNumericConstant)?.value?.toDouble()?.let { UFloatConstant.create(it, type) }
}
else -> when (type.name) {
"java.lang.String" -> UStringConstant(constant.asString())
else -> null
}
} ?: return UUndeterminedValue to operandInfo.state
return when (operandInfo.value) {
resultConstant -> return operandInfo
is UConstant -> resultConstant
is UDependentValue -> UDependentValue.create(resultConstant, operandInfo.value.dependencies)
else -> UUndeterminedValue
} to operandInfo.state
}
private fun evaluateTypeCheck(operandInfo: UEvaluationInfo, type: PsiType): UEvaluationInfo {
val constant = operandInfo.value.toConstant() ?: return UUndeterminedValue to operandInfo.state
val valid = when (type) {
PsiType.BOOLEAN -> constant is UBooleanConstant
PsiType.LONG -> constant is ULongConstant
PsiType.BYTE, PsiType.SHORT, PsiType.INT, PsiType.CHAR -> constant is UIntConstant
PsiType.FLOAT, PsiType.DOUBLE -> constant is UFloatConstant
else -> when (type.name) {
"java.lang.String" -> constant is UStringConstant
else -> false
}
}
return UBooleanConstant.valueOf(valid) to operandInfo.state
}
override fun visitBinaryExpressionWithType(
node: UBinaryExpressionWithType, data: UEvaluationState
): UEvaluationInfo {
inputStateCache[node] = data
val operandInfo = node.operand.accept(this, data)
if (!operandInfo.reachable || operandInfo.value == UUndeterminedValue) {
return operandInfo storeResultFor node
}
return when (node.operationKind) {
UastBinaryExpressionWithTypeKind.TYPE_CAST -> evaluateTypeCast(operandInfo, node.type)
UastBinaryExpressionWithTypeKind.INSTANCE_CHECK -> evaluateTypeCheck(operandInfo, node.type)
else -> UUndeterminedValue to operandInfo.state
} storeResultFor node
}
override fun visitParenthesizedExpression(node: UParenthesizedExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
return node.expression.accept(this, data) storeResultFor node
}
override fun visitLabeledExpression(node: ULabeledExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
return node.expression.accept(this, data) storeResultFor node
}
override fun visitCallExpression(node: UCallExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
var currentInfo = UUndeterminedValue to data
currentInfo = node.receiver?.accept(this, currentInfo.state) ?: currentInfo
if (!currentInfo.reachable) return currentInfo storeResultFor node
val argumentValues = mutableListOf<UValue>()
for (valueArgument in node.valueArguments) {
currentInfo = valueArgument.accept(this, currentInfo.state)
if (!currentInfo.reachable) return currentInfo storeResultFor node
argumentValues.add(currentInfo.value)
}
return (node.evaluateViaExtensions {
node.resolve()?.let { method -> evaluateMethodCall(method, argumentValues, currentInfo.state) }
?: UUndeterminedValue to currentInfo.state
} ?: UCallResultValue(node, argumentValues) to currentInfo.state) storeResultFor node
}
override fun visitQualifiedReferenceExpression(
node: UQualifiedReferenceExpression,
data: UEvaluationState
): UEvaluationInfo {
inputStateCache[node] = data
var currentInfo = UUndeterminedValue to data
currentInfo = node.receiver.accept(this, currentInfo.state)
if (!currentInfo.reachable) return currentInfo storeResultFor node
val selectorInfo = node.selector.accept(this, currentInfo.state)
return when (node.accessType) {
UastQualifiedExpressionAccessType.SIMPLE -> {
selectorInfo
}
else -> {
return node.evaluateViaExtensions { evaluateQualified(node.accessType, currentInfo, selectorInfo) }
?: UUndeterminedValue to selectorInfo.state storeResultFor node
}
} storeResultFor node
}
override fun visitDeclarationsExpression(
node: UDeclarationsExpression,
data: UEvaluationState
): UEvaluationInfo {
inputStateCache[node] = data
var currentInfo = UUndeterminedValue to data
for (variable in node.declarations) {
currentInfo = variable.accept(this, currentInfo.state)
if (!currentInfo.reachable) return currentInfo storeResultFor node
}
return currentInfo storeResultFor node
}
override fun visitVariable(node: UVariable, data: UEvaluationState): UEvaluationInfo {
val initializer = node.uastInitializer
val initializerInfo = initializer?.accept(this, data) ?: UUndeterminedValue to data
if (!initializerInfo.reachable) return initializerInfo
return UUndeterminedValue to initializerInfo.state.assign(node, initializerInfo.value, node)
}
// ----------------------- //
override fun visitBlockExpression(node: UBlockExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
var currentInfo = UUndeterminedValue to data
for (expression in node.expressions) {
currentInfo = expression.accept(this, currentInfo.state)
if (!currentInfo.reachable) return currentInfo storeResultFor node
}
return currentInfo storeResultFor node
}
override fun visitIfExpression(node: UIfExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val conditionInfo = node.condition.accept(this, data)
if (!conditionInfo.reachable) return conditionInfo storeResultFor node
val thenInfo = node.thenExpression?.accept(this, conditionInfo.state)
val elseInfo = node.elseExpression?.accept(this, conditionInfo.state)
val conditionValue = conditionInfo.value
val defaultInfo = UUndeterminedValue to conditionInfo.state
val constantConditionValue = conditionValue.toConstant()
return when (constantConditionValue) {
is UBooleanConstant -> {
if (constantConditionValue.value) thenInfo ?: defaultInfo
else elseInfo ?: defaultInfo
}
else -> when {
thenInfo == null -> elseInfo?.merge(defaultInfo) ?: defaultInfo
elseInfo == null -> thenInfo.merge(defaultInfo)
else -> thenInfo.merge(elseInfo)
}
} storeResultFor node
}
override fun visitSwitchExpression(node: USwitchExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val subjectInfo = node.expression?.accept(this, data) ?: UUndeterminedValue to data
if (!subjectInfo.reachable) return subjectInfo storeResultFor node
var resultInfo: UEvaluationInfo? = null
var clauseInfo = subjectInfo
var fallThroughCondition: UValue = UBooleanConstant.False
fun List<UExpression>.evaluateAndFold(): UValue =
this.map {
clauseInfo = it.accept(this@TreeBasedEvaluator, clauseInfo.state)
(clauseInfo.value valueEquals subjectInfo.value).toConstant() as? UValueBase ?: UUndeterminedValue
}.fold(UBooleanConstant.False) { previous: UValue, next -> previous or next }
clausesLoop@ for (expression in node.body.expressions) {
val switchClauseWithBody = expression as USwitchClauseExpressionWithBody
val caseCondition = switchClauseWithBody.caseValues.evaluateAndFold().or(fallThroughCondition)
if (caseCondition != UBooleanConstant.False) {
for (bodyExpression in switchClauseWithBody.body.expressions) {
clauseInfo = bodyExpression.accept(this, clauseInfo.state)
if (!clauseInfo.reachable) break
}
val clauseValue = clauseInfo.value
if (clauseValue is UNothingValue && clauseValue.containingLoopOrSwitch == node) {
// break from switch
resultInfo = resultInfo?.merge(clauseInfo) ?: clauseInfo
if (caseCondition == UBooleanConstant.True) break@clausesLoop
clauseInfo = subjectInfo
fallThroughCondition = UBooleanConstant.False
}
// TODO: jump out
else {
fallThroughCondition = caseCondition
clauseInfo = clauseInfo.merge(subjectInfo)
}
}
}
resultInfo = resultInfo ?: subjectInfo
val resultValue = resultInfo.value
if (resultValue is UNothingValue && resultValue.containingLoopOrSwitch == node) {
resultInfo = resultInfo.copy(UUndeterminedValue)
}
return resultInfo storeResultFor node
}
private fun evaluateLoop(
loop: ULoopExpression,
inputState: UEvaluationState,
condition: UExpression? = null,
infinite: Boolean = false,
update: UExpression? = null
): UEvaluationInfo {
fun evaluateCondition(inputState: UEvaluationState): UEvaluationInfo =
condition?.accept(this, inputState)
?: (if (infinite) UBooleanConstant.True else UUndeterminedValue) to inputState
var resultInfo = UUndeterminedValue to inputState
do {
val previousInfo = resultInfo
resultInfo = evaluateCondition(resultInfo.state)
val conditionConstant = resultInfo.value.toConstant()
if (conditionConstant == UBooleanConstant.False) {
return resultInfo.copy(UUndeterminedValue) storeResultFor loop
}
val bodyInfo = loop.body.accept(this, resultInfo.state)
val bodyValue = bodyInfo.value
if (bodyValue is UNothingValue) {
if (bodyValue.kind == BREAK && bodyValue.containingLoopOrSwitch == loop) {
return if (conditionConstant == UBooleanConstant.True) {
bodyInfo.copy(UUndeterminedValue)
}
else {
bodyInfo.copy(UUndeterminedValue).merge(previousInfo)
} storeResultFor loop
}
else if (bodyValue.kind == CONTINUE && bodyValue.containingLoopOrSwitch == loop) {
val updateInfo = update?.accept(this, bodyInfo.state) ?: bodyInfo
resultInfo = updateInfo.copy(UUndeterminedValue).merge(previousInfo)
}
else {
return if (conditionConstant == UBooleanConstant.True) {
bodyInfo
}
else {
resultInfo.copy(UUndeterminedValue)
} storeResultFor loop
}
}
else {
val updateInfo = update?.accept(this, bodyInfo.state) ?: bodyInfo
resultInfo = updateInfo.merge(previousInfo)
}
} while (previousInfo != resultInfo)
return resultInfo.copy(UUndeterminedValue) storeResultFor loop
}
override fun visitForEachExpression(node: UForEachExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val iterableInfo = node.iteratedValue.accept(this, data)
return evaluateLoop(node, iterableInfo.state)
}
override fun visitForExpression(node: UForExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val initialState = node.declaration?.accept(this, data)?.state ?: data
return evaluateLoop(node, initialState, node.condition, node.condition == null, node.update)
}
override fun visitWhileExpression(node: UWhileExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
return evaluateLoop(node, data, node.condition)
}
override fun visitDoWhileExpression(node: UDoWhileExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val bodyInfo = node.body.accept(this, data)
return evaluateLoop(node, bodyInfo.state, node.condition)
}
override fun visitTryExpression(node: UTryExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val tryInfo = node.tryClause.accept(this, data)
val mergedTryInfo = tryInfo.merge(UUndeterminedValue to data)
val catchInfoList = node.catchClauses.map { it.accept(this, mergedTryInfo.state) }
val mergedTryCatchInfo = catchInfoList.fold(mergedTryInfo, UEvaluationInfo::merge)
val finallyInfo = node.finallyClause?.accept(this, mergedTryCatchInfo.state) ?: mergedTryCatchInfo
return finallyInfo storeResultFor node
}
// ----------------------- //
override fun visitObjectLiteralExpression(node: UObjectLiteralExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val objectInfo = node.declaration.accept(this, data)
val resultState = data.merge(objectInfo.state)
return UUndeterminedValue to resultState storeResultFor node
}
override fun visitLambdaExpression(node: ULambdaExpression, data: UEvaluationState): UEvaluationInfo {
inputStateCache[node] = data
val lambdaInfo = node.body.accept(this, data)
val resultState = data.merge(lambdaInfo.state)
return UUndeterminedValue to resultState storeResultFor node
}
override fun visitClass(node: UClass, data: UEvaluationState): UEvaluationInfo {
// fields / initializers / nested classes?
var resultState = data
for (method in node.methods) {
resultState = resultState.merge(method.accept(this, resultState).state)
}
return UUndeterminedValue to resultState
}
override fun visitMethod(node: UMethod, data: UEvaluationState): UEvaluationInfo {
return UUndeterminedValue to (node.uastBody?.accept(this, data)?.state ?: data)
}
}
fun Any?.toConstant(node: ULiteralExpression? = null) = when(this) {
null -> UNullConstant
is Float -> UFloatConstant.create(this.toDouble(), UNumericType.FLOAT, node)
is Double -> UFloatConstant.create(this, UNumericType.DOUBLE, node)
is Long -> ULongConstant(this, node)
is Int -> UIntConstant(this, UNumericType.INT, node)
is Short -> UIntConstant(this.toInt(), UNumericType.SHORT, node)
is Byte -> UIntConstant(this.toInt(), UNumericType.BYTE, node)
is Char -> UCharConstant(this, node)
is Boolean -> UBooleanConstant.valueOf(this)
is String -> UStringConstant(this, node)
else -> UUndeterminedValue
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2000-2017 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.evaluation
import com.intellij.openapi.util.Key
import org.jetbrains.uast.*
import org.jetbrains.uast.values.UValue
import java.lang.ref.SoftReference
interface UEvaluationContext {
val uastContext: UastContext
val extensions: List<UEvaluatorExtension>
fun analyzeAll(file: UFile, state: UEvaluationState = file.createEmptyState()): UEvaluationContext
fun analyze(declaration: UDeclaration, state: UEvaluationState = declaration.createEmptyState()): UEvaluator
fun valueOf(expression: UExpression): UValue
fun getEvaluator(declaration: UDeclaration): UEvaluator
}
fun UFile.analyzeAll(context: UastContext = getUastContext(), extensions: List<UEvaluatorExtension> = emptyList()): UEvaluationContext =
MapBasedEvaluationContext(context, extensions).analyzeAll(this)
@JvmOverloads
fun UExpression.uValueOf(extensions: List<UEvaluatorExtension> = emptyList()): UValue? {
val declaration = getContainingDeclaration() ?: return null
val context = declaration.getEvaluationContextWithCaching(extensions)
context.analyze(declaration)
return context.valueOf(this)
}
fun UExpression.uValueOf(vararg extensions: UEvaluatorExtension): UValue? = uValueOf(extensions.asList())
fun UDeclaration.getEvaluationContextWithCaching(extensions: List<UEvaluatorExtension> = emptyList()): UEvaluationContext {
return containingFile?.let { file ->
val cachedContext = file.getUserData(EVALUATION_CONTEXT_KEY)?.get()
if (cachedContext != null && cachedContext.extensions == extensions)
cachedContext
else
MapBasedEvaluationContext(getUastContext(), extensions).apply {
file.putUserData(EVALUATION_CONTEXT_KEY, SoftReference(this))
}
} ?: MapBasedEvaluationContext(getUastContext(), extensions)
}
val EVALUATION_CONTEXT_KEY = Key<SoftReference<out UEvaluationContext>>("uast.EvaluationContext")

View File

@@ -0,0 +1,35 @@
/*
* Copyright 2000-2017 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.evaluation
import org.jetbrains.uast.values.UValue
data class UEvaluationInfo(val value: UValue, val state: UEvaluationState) {
fun merge(otherInfo: UEvaluationInfo): UEvaluationInfo {
// info with 'UNothingValue' is just ignored, if other is not UNothingValue
if (!reachable && otherInfo.reachable) return otherInfo
if (!otherInfo.reachable && reachable) return this
// Regular merge
val mergedValue = value.merge(otherInfo.value)
val mergedState = state.merge(otherInfo.state)
return UEvaluationInfo(mergedValue, mergedState)
}
fun copy(value: UValue) = if (value != this.value) UEvaluationInfo(value, state) else this
val reachable: Boolean
get() = value.reachable
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2000-2017 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.evaluation
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UVariable
import org.jetbrains.uast.values.UValue
// Role: stores current values for all variables (and may be something else)
// Immutable
interface UEvaluationState {
val boundElement: UElement?
val variables: Set<UVariable>
operator fun get(variable: UVariable): UValue
// Creates new evaluation state with state[variable] = value and boundElement = at
fun assign(variable: UVariable, value: UValue, at: UElement): UEvaluationState
// Merged two states
fun merge(otherState: UEvaluationState): UEvaluationState
}
fun UElement.createEmptyState(): UEvaluationState = EmptyEvaluationState(this)

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2000-2017 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.evaluation
import com.intellij.openapi.extensions.Extensions
import com.intellij.psi.PsiElement
import org.jetbrains.uast.*
import org.jetbrains.uast.values.UDependency
import org.jetbrains.uast.values.UValue
// Role: at the current state, evaluate expression(s)
interface UEvaluator {
val context: UastContext
val languageExtensions: List<UEvaluatorExtension>
get() {
val rootArea = Extensions.getRootArea()
if (!rootArea.hasExtensionPoint(UEvaluatorExtension.EXTENSION_POINT_NAME.name)) return listOf()
return rootArea.getExtensionPoint(UEvaluatorExtension.EXTENSION_POINT_NAME).extensions.toList()
}
fun PsiElement.languageExtension() = languageExtensions.firstOrNull { it.language == language }
fun UElement.languageExtension() = psi?.languageExtension()
fun analyze(method: UMethod, state: UEvaluationState = method.createEmptyState())
fun analyze(field: UField, state: UEvaluationState = field.createEmptyState())
fun evaluate(expression: UExpression, state: UEvaluationState? = null): UValue
fun getDependents(dependency: UDependency): Set<UValue>
}
fun createEvaluator(context: UastContext, extensions: List<UEvaluatorExtension>): UEvaluator =
TreeBasedEvaluator(context, extensions)

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2000-2017 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.evaluation
import com.intellij.lang.Language
import com.intellij.openapi.extensions.ExtensionPointName
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.*
import org.jetbrains.uast.values.UValue
interface UEvaluatorExtension {
companion object {
val EXTENSION_POINT_NAME: ExtensionPointName<UEvaluatorExtension> =
ExtensionPointName.create<UEvaluatorExtension>("org.jetbrains.uast.evaluation.UEvaluatorExtension")
}
infix fun UValue.to(state: UEvaluationState) = UEvaluationInfo(this, state)
val language: Language
fun evaluatePostfix(
operator: UastPostfixOperator,
operandValue: UValue,
state: UEvaluationState
): UEvaluationInfo
fun evaluatePrefix(
operator: UastPrefixOperator,
operandValue: UValue,
state: UEvaluationState
): UEvaluationInfo
fun evaluateBinary(
binaryExpression: UBinaryExpression,
leftValue: UValue,
rightValue: UValue,
state: UEvaluationState
): UEvaluationInfo
fun evaluateQualified(
accessType: UastQualifiedExpressionAccessType,
receiverInfo: UEvaluationInfo,
selectorInfo: UEvaluationInfo
): UEvaluationInfo
fun evaluateMethodCall(
target: PsiMethod,
argumentValues: List<UValue>,
state: UEvaluationState
): UEvaluationInfo
fun evaluateVariable(
variable: UVariable,
state: UEvaluationState
): UEvaluationInfo
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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
annotations.acceptList(visitor)
receiver.accept(visitor)
indices.acceptList(visitor)
visitor.afterVisitArrayAccessExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitArrayAccessExpression(this, data)
override fun asLogString() = log()
override fun asRenderString() = receiver.asRenderString() +
indices.joinToString(prefix = "[", postfix = "]") { it.asRenderString() }
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiMethod
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a binary expression (value1 op value2), eg. `2 + "A"`.
*/
interface UBinaryExpression : UPolyadicExpression {
/**
* Returns the left operand.
*/
val leftOperand: UExpression
/**
* Returns the right operand.
*/
val rightOperand: UExpression
/**
* Returns the operator identifier.
*/
val operatorIdentifier: UIdentifier?
/**
* Resolve the operator method.
*
* @return the resolved method, or null if the method can't be resolved, or if the expression is not a method call.
*/
fun resolveOperator(): PsiMethod?
override val operands: List<UExpression>
get() = listOf(leftOperand, rightOperand)
override fun accept(visitor: UastVisitor) {
if (visitor.visitBinaryExpression(this)) return
annotations.acceptList(visitor)
leftOperand.accept(visitor)
rightOperand.accept(visitor)
visitor.afterVisitBinaryExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitBinaryExpression(this, data)
override fun asLogString() = log("operator = $operator")
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiType
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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 reference of this expression.
*/
val typeReference: UTypeReferenceExpression?
/**
* Returns the type.
*/
val type: PsiType
override fun asLogString() = log()
override fun asRenderString() = "${operand.asRenderString()} ${operationKind.name} ${type.name}"
override fun accept(visitor: UastVisitor) {
if (visitor.visitBinaryExpressionWithType(this)) return
annotations.acceptList(visitor)
operand.accept(visitor)
typeReference?.accept(visitor)
visitor.afterVisitBinaryExpressionWithType(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitBinaryExpressionWithType(this, data)
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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
annotations.acceptList(visitor)
expressions.acceptList(visitor)
visitor.afterVisitBlockExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitBlockExpression(this, data)
override fun asLogString() = log()
override fun asRenderString() = buildString {
appendln("{")
expressions.forEach { appendln(it.asRenderString().withMargin) }
append("}")
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2000-2017 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.UastTypedVisitor
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `break` expression.
*/
interface UBreakExpression : UJumpExpression {
override fun accept(visitor: UastVisitor) {
if (visitor.visitBreakExpression(this)) return
annotations.acceptList(visitor)
visitor.afterVisitBreakExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitBreakExpression(this, data)
override fun asLogString() = log("label = $label")
override fun asRenderString() = label?.let { "break@$it" } ?: "break"
}

View File

@@ -0,0 +1,116 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiMethod
import com.intellij.psi.PsiType
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a call expression (method/constructor call, array initializer).
*/
interface UCallExpression : UExpression, UResolvable {
/**
* Returns the call kind.
*/
val kind: UastCallKind
/**
* Returns the called method name, or null if the call is not a method call.
* This property should return the actual resolved function name.
*/
val methodName: String?
/**
* Returns the expression receiver.
* For example, for call `a.b.[c()]` the receiver is `a.b`.
*/
val receiver: UExpression?
/**
* Returns the receiver type, or null if the call has not a receiver.
*/
val receiverType: PsiType?
/**
* Returns the function reference expression if the call is a non-constructor method call, null otherwise.
*/
val methodIdentifier: UIdentifier?
/**
* Returns the class reference if the call is a constructor call, null otherwise.
*/
val classReference: UReferenceExpression?
/**
* 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 type arguments for the call.
*/
val typeArguments: List<PsiType>
/**
* Returns the return type of the called function, or null if the call is not a function call.
*/
val returnType: PsiType?
/**
* Resolve the called method.
*
* @return the [PsiMethod], or null if the method was not resolved.
* Note that the [PsiMethod] is an unwrapped [PsiMethod], not a [UMethod].
*/
override fun resolve(): PsiMethod?
override fun accept(visitor: UastVisitor) {
if (visitor.visitCallExpression(this)) return
annotations.acceptList(visitor)
methodIdentifier?.accept(visitor)
classReference?.accept(visitor)
valueArguments.acceptList(visitor)
visitor.afterVisitCallExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitCallExpression(this, data)
override fun asLogString() = log("kind = $kind, argCount = $valueArgumentCount)")
override fun asRenderString(): String {
val ref = classReference?.asRenderString() ?: methodName ?: methodIdentifier?.asRenderString() ?: "<noref>"
return ref + "(" + valueArguments.joinToString { it.asRenderString() } + ")"
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiType
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a callable reference expression, e.g. `Clazz::methodName`.
*/
interface UCallableReferenceExpression : UReferenceExpression {
/**
* 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: PsiType?
/**
* Returns the callable name.
*/
val callableName: String
override fun accept(visitor: UastVisitor) {
if (visitor.visitCallableReferenceExpression(this)) return
annotations.acceptList(visitor)
qualifierExpression?.accept(visitor)
visitor.afterVisitCallableReferenceExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitCallableReferenceExpression(this, data)
override fun asLogString() = log("name = $callableName")
override fun asRenderString() = buildString {
qualifierExpression?.let {
append(it.asRenderString())
} ?: qualifierType?.let {
append(it.name)
}
append("::")
append(callableName)
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiType
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents the class literal expression, e.g. `Clazz.class`.
*/
interface UClassLiteralExpression : UExpression {
override fun asLogString() = log()
override fun asRenderString() = (type?.name) ?: "(${expression?.asRenderString() ?: "<no expression>"})" + "::class"
/**
* Returns the type referenced by this class literal, or null if the type can't be determined in a compile-time.
*/
val type: PsiType?
/**
* Returns an expression for this class literal expression.
* Might be null if the [type] is specified.
*/
val expression: UExpression?
override fun accept(visitor: UastVisitor) {
if (visitor.visitClassLiteralExpression(this)) return
annotations.acceptList(visitor)
expression?.accept(visitor)
visitor.afterVisitClassLiteralExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitClassLiteralExpression(this, data)
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2000-2017 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.UastTypedVisitor
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `continue` expression.
*/
interface UContinueExpression : UJumpExpression {
override fun accept(visitor: UastVisitor) {
if (visitor.visitContinueExpression(this)) return
annotations.acceptList(visitor)
visitor.afterVisitContinueExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitContinueExpression(this, data)
override fun asLogString() = log("label = $label")
override fun asRenderString() = label?.let { "continue@$it" } ?: "continue"
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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<UDeclaration>
override fun accept(visitor: UastVisitor) {
if (visitor.visitDeclarationsExpression(this)) return
annotations.acceptList(visitor)
declarations.acceptList(visitor)
visitor.afterVisitDeclarationsExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitDeclarationsExpression(this, data)
override fun asRenderString() = declarations.joinToString(LINE_SEPARATOR) { it.asRenderString() }
override fun asLogString() = log()
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a generic list of expressions.
*/
interface UExpressionList : UExpression {
/**
* Returns the list of expressions.
*/
val expressions: List<UExpression>
/**
* Returns the list kind.
*/
val kind: UastSpecialExpressionKind
override fun accept(visitor: UastVisitor) {
if (visitor.visitExpressionList(this)) return
annotations.acceptList(visitor)
expressions.acceptList(visitor)
visitor.afterVisitExpressionList(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitExpressionList(this, data)
fun firstOrNull(): UExpression? = expressions.firstOrNull()
override fun asLogString() = log(kind.name)
override fun asRenderString() = kind.name + " " + expressions.joinToString(" : ") { it.asRenderString() }
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright 2000-2017 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
/**
* A common parent for "this" and "super" expressions.
*/
interface UInstanceExpression : UExpression, ULabeled, UResolvable

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2000-2017 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
/**
* Represents jump expression (break / continue) with label
*/
interface UJumpExpression : UExpression {
/**
* Returns the expression label, or null if the label is not specified.
*/
val label: String?
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents an expression with the label specified.
*/
interface ULabeledExpression : UExpression, ULabeled {
/**
* Returns the expression label.
*/
override val label: String
/**
* Returns the expression itself.
*/
val expression: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitLabeledExpression(this)) return
annotations.acceptList(visitor)
expression.accept(visitor)
visitor.afterVisitLabeledExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitLabeledExpression(this, data)
override fun evaluate() = expression.evaluate()
override fun asLogString() = log("label = $label")
override fun asRenderString() = "$label@ ${expression.asRenderString()}"
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents the lambda expression.
*/
interface ULambdaExpression : UExpression {
/**
* Returns the list of lambda value parameters.
*/
val valueParameters: List<UParameter>
/**
* Returns the lambda body expression.
*/
val body: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitLambdaExpression(this)) return
annotations.acceptList(visitor)
valueParameters.acceptList(visitor)
body.accept(visitor)
visitor.afterVisitLambdaExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitLambdaExpression(this, data)
override fun asLogString() = log()
override fun asRenderString(): String {
val renderedValueParameters = if (valueParameters.isEmpty())
""
else
valueParameters.joinToString { it.asRenderString() } + " ->" + LINE_SEPARATOR
return "{ " + renderedValueParameters + body.asRenderString().withMargin + LINE_SEPARATOR + "}"
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2000-2017 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.UastTypedVisitor
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
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
get() = value == null
/**
* 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
override fun accept(visitor: UastVisitor) {
if (visitor.visitLiteralExpression(this)) return
annotations.acceptList(visitor)
visitor.afterVisitLiteralExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitLiteralExpression(this, data)
override fun asRenderString(): String {
val value = value
return when (value) {
null -> "null"
is Char -> "'$value'"
is String -> '"' + value.replace("\\", "\\\\")
.replace("\r", "\\r").replace("\n", "\\n")
.replace("\t", "\\t").replace("\b", "\\b")
.replace("\"", "\\\"") + '"'
else -> value.toString()
}
}
override fun asLogString() = log("value = ${asRenderString()}")
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastVisitor
interface UNamedExpression: UExpression {
val name: String?
val expression: UExpression
override fun accept(visitor: UastVisitor) {
if (visitor.visitElement(this)) return
annotations.acceptList(visitor)
expression.accept(visitor)
visitor.afterVisitElement(this)
}
override fun asLogString() = log("name = $name")
override fun asRenderString() = name + " = " + expression.asRenderString()
override fun evaluate() = expression.evaluate()
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiType
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents an object literal expression, e.g. `new Runnable() {}` in Java.
*/
interface UObjectLiteralExpression : UCallExpression {
/**
* Returns the class declaration.
*/
val declaration: UClass
override val methodIdentifier: UIdentifier?
get() = null
override val kind: UastCallKind
get() = UastCallKind.CONSTRUCTOR_CALL
override val methodName: String?
get() = null
override val receiver: UExpression?
get() = null
override val receiverType: PsiType?
get() = null
override val returnType: PsiType?
get() = null
override fun accept(visitor: UastVisitor) {
if (visitor.visitObjectLiteralExpression(this)) return
annotations.acceptList(visitor)
declaration.accept(visitor)
visitor.afterVisitObjectLiteralExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitObjectLiteralExpression(this, data)
override fun asLogString() = log()
override fun asRenderString() = "anonymous " + declaration.text
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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
annotations.acceptList(visitor)
expression.accept(visitor)
visitor.afterVisitParenthesizedExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitParenthesizedExpression(this, data)
override fun evaluate() = expression.evaluate()
override fun asLogString() = log()
override fun asRenderString() = '(' + expression.asRenderString() + ')'
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a polyadic expression (value1 op value2 op value3 op ...), eg. `2 + "A" + c + d`.
*/
interface UPolyadicExpression : UExpression {
/**
* Returns a list of expression operands.
*/
val operands: List<UExpression>
/**
* Returns the operator.
*/
val operator: UastBinaryOperator
override fun accept(visitor: UastVisitor) {
if (visitor.visitPolyadicExpression(this)) return
annotations.acceptList(visitor)
operands.acceptList(visitor)
visitor.afterVisitPolyadicExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitPolyadicExpression(this, data)
override fun asLogString() = log("operator = $operator")
override fun asRenderString() =
operands.joinToString(separator = " ${operator.text} ", transform = UExpression::asRenderString)
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents the qualified expression (receiver.selector).
*/
interface UQualifiedReferenceExpression : UReferenceExpression {
/**
* 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
override fun asRenderString() = receiver.asRenderString() + accessType.name + selector.asRenderString()
override fun accept(visitor: UastVisitor) {
if (visitor.visitQualifiedReferenceExpression(this)) return
annotations.acceptList(visitor)
receiver.accept(visitor)
selector.accept(visitor)
visitor.afterVisitQualifiedReferenceExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitQualifiedReferenceExpression(this, data)
override fun asLogString() = log()
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2000-2017 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.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
interface UReferenceExpression : UExpression, UResolvable {
/**
* Returns the resolved name for this reference, or null if the reference can't be resolved.
*/
val resolvedName: String?
override fun asLogString() = log()
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) = visitor.visitReferenceExpression(this, data)
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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
annotations.acceptList(visitor)
returnExpression?.accept(visitor)
visitor.afterVisitReturnExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitReturnExpression(this, data)
override fun asRenderString() = returnExpression.let { if (it == null) "return" else "return " + it.asRenderString() }
override fun asLogString() = log()
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2000-2017 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.UastTypedVisitor
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a simple reference expression (a non-qualified identifier).
*/
interface USimpleNameReferenceExpression : UReferenceExpression {
/**
* Returns the identifier name.
*/
val identifier: String
override fun accept(visitor: UastVisitor) {
if (visitor.visitSimpleNameReferenceExpression(this)) return
annotations.acceptList(visitor)
visitor.afterVisitSimpleNameReferenceExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitSimpleNameReferenceExpression(this, data)
override fun asLogString() = log("identifier = $identifier")
override fun asRenderString() = identifier
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2000-2017 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.UastTypedVisitor
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `super` expression.
* Qualified `super` is not supported at the moment.
*/
interface USuperExpression : UInstanceExpression {
override fun asLogString() = log("label = $label")
override fun asRenderString() = "super"
override fun accept(visitor: UastVisitor) {
if (visitor.visitSuperExpression(this)) return
annotations.acceptList(visitor)
visitor.afterVisitSuperExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitSuperExpression(this, data)
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2000-2017 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.UastTypedVisitor
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastVisitor
/**
* Represents a `this` expression.
* Qualified `this` is not supported at the moment.
*/
interface UThisExpression : UInstanceExpression {
override fun asLogString() = log("label = $label")
override fun asRenderString() = "this"
override fun accept(visitor: UastVisitor) {
if (visitor.visitThisExpression(this)) return
annotations.acceptList(visitor)
visitor.afterVisitThisExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitThisExpression(this, data)
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2000-2017 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.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
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
annotations.acceptList(visitor)
thrownExpression.accept(visitor)
visitor.afterVisitThrowExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitThrowExpression(this, data)
override fun asRenderString() = "throw " + thrownExpression.asRenderString()
override fun asLogString() = log()
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiType
import com.intellij.psi.util.PsiTypesUtil
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
interface UTypeReferenceExpression : UExpression {
/**
* Returns the resolved type for this reference.
*/
val type: PsiType
/**
* Returns the qualified name of the class type, or null if the [type] is not a class type.
*/
fun getQualifiedName() = PsiTypesUtil.getPsiClass(type)?.qualifiedName
override fun accept(visitor: UastVisitor) {
if (visitor.visitTypeReferenceExpression(this)) return
annotations.acceptList(visitor)
visitor.afterVisitTypeReferenceExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitTypeReferenceExpression(this, data)
override fun asLogString() = log("name = ${type.name}")
override fun asRenderString(): String = type.name
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiMethod
import org.jetbrains.uast.internal.acceptList
import org.jetbrains.uast.internal.log
import org.jetbrains.uast.visitor.UastTypedVisitor
import org.jetbrains.uast.visitor.UastVisitor
interface UUnaryExpression : UExpression {
/**
* Returns the expression operand.
*/
val operand: UExpression
/**
* Returns the expression operator.
*/
val operator: UastOperator
/**
* Returns the operator identifier.
*/
val operatorIdentifier: UIdentifier?
/**
* Resolve the operator method.
*
* @return the resolved method, or null if the method can't be resolved, or if the expression is not a method call.
*/
fun resolveOperator(): PsiMethod?
override fun accept(visitor: UastVisitor) {
if (visitor.visitUnaryExpression(this)) return
annotations.acceptList(visitor)
operand.accept(visitor)
visitor.afterVisitUnaryExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitUnaryExpression(this, data)
}
interface UPrefixExpression : UUnaryExpression {
override val operator: UastPrefixOperator
override fun accept(visitor: UastVisitor) {
if (visitor.visitPrefixExpression(this)) return
annotations.acceptList(visitor)
operand.accept(visitor)
visitor.afterVisitPrefixExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitPrefixExpression(this, data)
override fun asLogString() = log("operator = $operator")
override fun asRenderString() = operator.text + operand.asRenderString()
}
interface UPostfixExpression : UUnaryExpression {
override val operator: UastPostfixOperator
override fun accept(visitor: UastVisitor) {
if (visitor.visitPostfixExpression(this)) return
annotations.acceptList(visitor)
operand.accept(visitor)
visitor.afterVisitPostfixExpression(this)
}
override fun <D, R> accept(visitor: UastTypedVisitor<D, R>, data: D) =
visitor.visitPostfixExpression(this, data)
override fun asLogString() = log("operator = $operator")
override fun asRenderString() = operand.asRenderString() + operator.text
}

View File

@@ -0,0 +1,98 @@
/*
* Copyright 2000-2017 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.
*/
@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 boolean literal.
*
* @return true if the receiver is a boolean literal, false otherwise.
*/
fun UElement.isBooleanLiteral(): Boolean = this is ULiteralExpression && this.isBoolean
/**
* Checks if the [UElement] is a `true` boolean literal.
*
* @return true if the receiver is a `true` boolean literal, false otherwise.
*/
fun UElement.isTrueLiteral(): Boolean = this is ULiteralExpression && this.isBoolean && this.value == true
/**
* Checks if the [UElement] is a `false` boolean literal.
*
* @return true if the receiver is a `false` boolean literal, false otherwise.
*/
fun UElement.isFalseLiteral(): Boolean = this is ULiteralExpression && this.isBoolean && this.value == false
/**
* 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
is Short -> true
is Char -> true
is Byte -> true
else -> false
}
/**
* 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
is Int -> it.toLong()
is Short -> it.toLong()
is Char -> it.toLong()
is Byte -> it.toLong()
else -> 0
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2000-2017 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.internal
import org.jetbrains.uast.UElement
import org.jetbrains.uast.visitor.UastVisitor
fun List<UElement>.acceptList(visitor: UastVisitor) {
for (element in this) {
element.accept(visitor)
}
}
@Suppress("unused")
inline fun <reified T : UElement> T.log(text: String = ""): String {
val className = T::class.java.simpleName
return if (text.isEmpty()) className else "$className ($text)"
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiModifier
import com.intellij.psi.PsiModifierListOwner
import com.intellij.psi.PsiType
internal val LINE_SEPARATOR = System.getProperty("line.separator") ?: "\n"
val String.withMargin: String
get() = lines().joinToString(LINE_SEPARATOR) { " " + it }
internal operator fun String.times(n: Int) = this.repeat(n)
internal fun List<UElement>.asLogString() = joinToString(LINE_SEPARATOR) { it.asLogString().withMargin }
internal tailrec fun UExpression.unwrapParenthesis(): UExpression = when (this) {
is UParenthesizedExpression -> expression.unwrapParenthesis()
else -> this
}
internal fun <T> lz(f: () -> T) = lazy(LazyThreadSafetyMode.NONE, f)
internal val PsiType.name: String
get() = getCanonicalText(false)
internal fun PsiModifierListOwner.renderModifiers(): String {
val modifiers = PsiModifier.MODIFIERS.filter { hasModifierProperty(it) }.joinToString(" ")
return if (modifiers.isEmpty()) "" else modifiers + " "
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2000-2017 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.
*/
@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)
companion object {
@JvmField
val TYPE_CAST = TypeCast("as")
@JvmField
val INSTANCE_CHECK = InstanceCheck("is")
@JvmField
val UNKNOWN = UastBinaryExpressionWithTypeKind("<unknown>")
}
}

View File

@@ -0,0 +1,133 @@
/*
* Copyright 2000-2017 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
/**
* Kinds of operators in [UBinaryExpression].
*/
open class UastBinaryOperator(override val text: String): UastOperator {
class LogicalOperator(text: String): UastBinaryOperator(text)
class ComparisonOperator(text: String): UastBinaryOperator(text)
class ArithmeticOperator(text: String): UastBinaryOperator(text)
class BitwiseOperator(text: String): UastBinaryOperator(text)
class AssignOperator(text: String): UastBinaryOperator(text)
companion object {
@JvmField
val ASSIGN = AssignOperator("=")
@JvmField
val PLUS = ArithmeticOperator("+")
@JvmField
val MINUS = ArithmeticOperator("-")
@JvmField
val MULTIPLY = ArithmeticOperator("*")
@JvmField
val DIV = ArithmeticOperator("/")
@JvmField
val MOD = ArithmeticOperator("%")
@JvmField
val LOGICAL_OR = LogicalOperator("||")
@JvmField
val LOGICAL_AND = LogicalOperator("&&")
@JvmField
val BITWISE_OR = BitwiseOperator("|")
@JvmField
val BITWISE_AND = BitwiseOperator("&")
@JvmField
val BITWISE_XOR = BitwiseOperator("^")
@JvmField
val EQUALS = ComparisonOperator("==")
@JvmField
val NOT_EQUALS = ComparisonOperator("!=")
@JvmField
val IDENTITY_EQUALS = ComparisonOperator("===")
@JvmField
val IDENTITY_NOT_EQUALS = ComparisonOperator("!==")
@JvmField
val GREATER = ComparisonOperator(">")
@JvmField
val GREATER_OR_EQUALS = ComparisonOperator(">=")
@JvmField
val LESS = ComparisonOperator("<")
@JvmField
val LESS_OR_EQUALS = ComparisonOperator("<=")
@JvmField
val SHIFT_LEFT = BitwiseOperator("<<")
@JvmField
val SHIFT_RIGHT = BitwiseOperator(">>")
@JvmField
val UNSIGNED_SHIFT_RIGHT = BitwiseOperator(">>>")
@JvmField
val OTHER = UastBinaryOperator("<other>")
@JvmField
val PLUS_ASSIGN = AssignOperator("+=")
@JvmField
val MINUS_ASSIGN = AssignOperator("-=")
@JvmField
val MULTIPLY_ASSIGN = AssignOperator("*=")
@JvmField
val DIVIDE_ASSIGN = AssignOperator("/=")
@JvmField
val REMAINDER_ASSIGN = AssignOperator("%=")
@JvmField
val AND_ASSIGN = AssignOperator("&=")
@JvmField
val XOR_ASSIGN = AssignOperator("^=")
@JvmField
val OR_ASSIGN = AssignOperator("|=")
@JvmField
val SHIFT_LEFT_ASSIGN = AssignOperator("<<=")
@JvmField
val SHIFT_RIGHT_ASSIGN = AssignOperator(">>=")
@JvmField
val UNSIGNED_SHIFT_RIGHT_ASSIGN = AssignOperator(">>>=")
}
override fun toString() = text
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2000-2017 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
/**
* Kinds of [UCallExpression].
*/
open class UastCallKind(val name: String) {
companion object {
@JvmField
val METHOD_CALL = UastCallKind("method_call")
@JvmField
val CONSTRUCTOR_CALL = UastCallKind("constructor_call")
@JvmField
val NEW_ARRAY_WITH_DIMENSIONS = UastCallKind("new_array_with_dimensions")
/**
* Initializer parts are available in call expression as value arguments.
* [NEW_ARRAY_WITH_INITIALIZER] is a top-level initializer. In case of multi-dimensional arrays, inner initializers
* have type of [NESTED_ARRAY_INITIALIZER].
*/
@JvmField
val NEW_ARRAY_WITH_INITIALIZER = UastCallKind("new_array_with_initializer")
@JvmField
val NESTED_ARRAY_INITIALIZER = UastCallKind("array_initializer")
}
override fun toString(): String{
return "UastCallKind(name='$name')"
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2000-2017 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
/**
* Kinds of [UClass].
*/
open class UastClassKind(val text: String) {
companion object {
@JvmField
val CLASS = UastClassKind("class")
@JvmField
val INTERFACE = UastClassKind("interface")
@JvmField
val ANNOTATION = UastClassKind("annotation")
@JvmField
val ENUM = UastClassKind("enum")
@JvmField
val OBJECT = UastClassKind("object")
}
override fun toString(): String{
return "UastClassKind(text='$text')"
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2000-2017 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
/**
* Uast operator base interface.
*
* @see [UastPrefixOperator], [UastPostfixOperator], [UastBinaryOperator]
*/
interface UastOperator {
/**
* Returns the operator text to render in [UElement.asRenderString].
*/
val text: String
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2000-2017 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
/**
* [UPostfixExpression] operators.
*/
open class UastPostfixOperator(override val text: String): UastOperator {
companion object {
@JvmField
val INC = UastPostfixOperator("++")
@JvmField
val DEC = UastPostfixOperator("--")
@JvmField
val UNKNOWN = UastPostfixOperator("<unknown>")
}
override fun toString() = text
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2000-2017 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
/**
* [UPrefixExpression] operators.
*/
class UastPrefixOperator(override val text: String): UastOperator {
companion object {
@JvmField
val INC = UastPrefixOperator("++")
@JvmField
val DEC = UastPrefixOperator("--")
@JvmField
val UNARY_MINUS = UastPrefixOperator("-")
@JvmField
val UNARY_PLUS = UastPrefixOperator("+")
@JvmField
val LOGICAL_NOT = UastPrefixOperator("!")
@JvmField
val BITWISE_NOT = UastPrefixOperator("~")
@JvmField
val UNKNOWN = UastPrefixOperator("<unknown>")
}
override fun toString() = text
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2000-2017 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
/**
* Access types of [UQualifiedReferenceExpression].
* Additional type examples: Kotlin safe call (?.).
*/
open class UastQualifiedExpressionAccessType(val name: String) {
companion object {
@JvmField
val SIMPLE = UastQualifiedExpressionAccessType(".")
}
override fun toString(): String{
return "UastQualifiedExpressionAccessType(name='$name')"
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2000-2017 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
/**
* Kinds of [UExpressionList].
*/
open class UastSpecialExpressionKind(val name: String) {
override fun toString(): String{
return "UastSpecialExpressionKind(name='$name')"
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2000-2017 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 com.intellij.psi.PsiLocalVariable
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiModifierListOwner
enum class UastVisibility(val text: String) {
PUBLIC("public"),
PRIVATE("private"),
PROTECTED("protected"),
PACKAGE_LOCAL("packageLocal"),
LOCAL("local");
override fun toString() = text
companion object {
operator fun get(declaration: PsiModifierListOwner): UastVisibility {
if (declaration.hasModifierProperty(PsiModifier.PUBLIC)) return UastVisibility.PUBLIC
if (declaration.hasModifierProperty(PsiModifier.PROTECTED)) return UastVisibility.PROTECTED
if (declaration.hasModifierProperty(PsiModifier.PRIVATE)) return UastVisibility.PRIVATE
if (declaration is PsiLocalVariable) return UastVisibility.LOCAL
return UastVisibility.PACKAGE_LOCAL
}
}
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright 2000-2017 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.psi
import com.intellij.openapi.util.Segment
import org.jetbrains.uast.UElement
interface UElementWithLocation : UElement, Segment

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2000-2017 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.psi
import com.intellij.lang.Language
import com.intellij.psi.PsiElement
import com.intellij.psi.impl.light.LightParameter
import org.jetbrains.uast.UastErrorType
class UastPsiParameterNotResolved(
declarationScope: PsiElement,
language: Language
) : LightParameter("error", UastErrorType, declarationScope, language)

View File

@@ -0,0 +1,199 @@
/*
* Copyright 2000-2017 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.
*/
@file:JvmMultifileClass
@file:JvmName("UastUtils")
package org.jetbrains.uast
import org.jetbrains.uast.visitor.UastVisitor
/**
* Get the topmost parent qualified expression for the call expression.
*
* Example 1:
* Code: variable.call(args)
* Call element: E = call(args)
* Qualified parent (return value): Q = [getQualifiedCallElement](E) = variable.call(args)
*
* Example 2:
* Code: call(args)
* Call element: E = call(args)
* Qualified parent (return value): Q = [getQualifiedCallElement](E) = call(args) (no qualifier)
*
* @return containing qualified expression if the call is a child of the qualified expression, call element otherwise.
*/
fun UExpression.getQualifiedParentOrThis(): UExpression {
fun findParent(current: UExpression?, previous: UExpression): UExpression? = when (current) {
is UQualifiedReferenceExpression -> {
if (current.selector == previous)
findParent(current.uastParent as? UExpression, current) ?: current
else
previous
}
is UParenthesizedExpression -> findParent(current.expression, previous) ?: previous
else -> null
}
return findParent(uastParent as? UExpression, this) ?: this
}
fun UExpression.asQualifiedPath(): List<String>? {
if (this is USimpleNameReferenceExpression) {
return listOf(this.identifier)
} else if (this !is UQualifiedReferenceExpression) {
return null
}
var error = false
val list = mutableListOf<String>()
fun addIdentifiers(expr: UQualifiedReferenceExpression) {
val receiver = expr.receiver.unwrapParenthesis()
val selector = expr.selector as? USimpleNameReferenceExpression ?: run { error = true; return }
when (receiver) {
is UQualifiedReferenceExpression -> addIdentifiers(receiver)
is USimpleNameReferenceExpression -> list += receiver.identifier
else -> {
error = true
return
}
}
list += selector.identifier
}
addIdentifiers(this)
return if (error) null else list
}
/**
* Return the list of qualified expressions.
*
* Example:
* Code: obj.call(param).anotherCall(param2).getter
* Qualified chain: [obj, call(param), anotherCall(param2), getter]
*
* @return list of qualified expressions, or the empty list if the received expression is not a qualified expression.
*/
fun UExpression?.getQualifiedChain(): List<UExpression> {
fun collect(expr: UQualifiedReferenceExpression, chains: MutableList<UExpression>) {
val receiver = expr.receiver.unwrapParenthesis()
if (receiver is UQualifiedReferenceExpression) {
collect(receiver, chains)
} else {
chains += receiver
}
val selector = expr.selector.unwrapParenthesis()
if (selector is UQualifiedReferenceExpression) {
collect(selector, chains)
} else {
chains += selector
}
}
if (this == null) return emptyList()
val qualifiedExpression = this as? UQualifiedReferenceExpression ?: return listOf(this)
val chains = mutableListOf<UExpression>()
collect(qualifiedExpression, chains)
return chains
}
/**
* Return the outermost qualified expression.
*
* @return the outermost qualified expression,
* this element if the parent expression is not a qualified expression,
* or null if the element is not a qualified expression.
*
* Example:
* Code: a.b.c(asd).g
* Call element: c(asd)
* Outermost qualified (return value): a.b.c(asd).g
*/
fun UExpression.getOutermostQualified(): UQualifiedReferenceExpression? {
tailrec fun getOutermostQualified(current: UElement?, previous: UExpression): UQualifiedReferenceExpression? = when (current) {
is UQualifiedReferenceExpression -> getOutermostQualified(current.uastParent, current)
is UParenthesizedExpression -> getOutermostQualified(current.uastParent, previous)
else -> if (previous is UQualifiedReferenceExpression) previous else null
}
return getOutermostQualified(this.uastParent, this)
}
/**
* Checks if the received expression is a qualified chain of identifiers, and the trailing part of such chain is [fqName].
*
* @param fqName the chain part to check against. Sequence of identifiers, separated by dot ('.'). Example: "com.example".
* @return true, if the received expression is a qualified chain of identifiers, and the trailing part of such chain is [fqName].
*/
fun UExpression.matchesQualified(fqName: String): Boolean {
val identifiers = this.asQualifiedPath() ?: return false
val passedIdentifiers = fqName.trim('.').split('.')
return identifiers == passedIdentifiers
}
/**
* Checks if the received expression is a qualified chain of identifiers, and the leading part of such chain is [fqName].
*
* @param fqName the chain part to check against. Sequence of identifiers, separated by dot ('.'). Example: "com.example".
* @return true, if the received expression is a qualified chain of identifiers, and the leading part of such chain is [fqName].
*/
fun UExpression.startsWithQualified(fqName: String): Boolean {
val identifiers = this.asQualifiedPath() ?: return false
val passedIdentifiers = fqName.trim('.').split('.')
if (identifiers.size < passedIdentifiers.size) return false
passedIdentifiers.forEachIndexed { i, passedIdentifier ->
if (passedIdentifier != identifiers[i]) return false
}
return true
}
/**
* Checks if the received expression is a qualified chain of identifiers, and the trailing part of such chain is [fqName].
*
* @param fqName the chain part to check against. Sequence of identifiers, separated by dot ('.'). Example: "com.example".
* @return true, if the received expression is a qualified chain of identifiers, and the trailing part of such chain is [fqName].
*/
fun UExpression.endsWithQualified(fqName: String): Boolean {
val identifiers = this.asQualifiedPath()?.asReversed() ?: return false
val passedIdentifiers = fqName.trim('.').split('.').asReversed()
if (identifiers.size < passedIdentifiers.size) return false
passedIdentifiers.forEachIndexed { i, passedIdentifier ->
if (passedIdentifier != identifiers[i]) return false
}
return true
}
fun UElement.asRecursiveLogString(): String {
val stringBuilder = StringBuilder()
val indent = " "
accept(object : UastVisitor {
private var level = 0
override fun visitElement(node: UElement): Boolean {
stringBuilder.append(indent.repeat(level))
stringBuilder.appendln(node.asLogString())
level++
return false
}
override fun afterVisitElement(node: UElement) {
super.afterVisitElement(node)
level--
}
})
return stringBuilder.toString()
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2000-2017 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.
*/
@file:JvmName("UastExpressionUtils")
package org.jetbrains.uast.util
import org.jetbrains.uast.*
fun UElement.isConstructorCall() = (this as? UCallExpression)?.kind == UastCallKind.CONSTRUCTOR_CALL
fun UElement.isMethodCall() = (this as? UCallExpression)?.kind == UastCallKind.METHOD_CALL
fun UElement.isNewArray() = isNewArrayWithDimensions() || isNewArrayWithInitializer()
fun UElement.isNewArrayWithDimensions() = (this as? UCallExpression)?.kind == UastCallKind.NEW_ARRAY_WITH_DIMENSIONS
fun UElement.isNewArrayWithInitializer() = (this as? UCallExpression)?.kind == UastCallKind.NEW_ARRAY_WITH_INITIALIZER
@Deprecated("Use isArrayInitializer()", ReplaceWith("isArrayInitializer()"))
fun UElement.isNestedArrayInitializer() = isArrayInitializer()
fun UElement.isArrayInitializer() = (this as? UCallExpression)?.kind == UastCallKind.NESTED_ARRAY_INITIALIZER
fun UElement.isTypeCast() = (this as? UBinaryExpressionWithType)?.operationKind is UastBinaryExpressionWithTypeKind.TypeCast
fun UElement.isInstanceCheck() = (this as? UBinaryExpressionWithType)?.operationKind is UastBinaryExpressionWithTypeKind.InstanceCheck
fun UElement.isAssignment() = (this as? UBinaryExpression)?.operator is UastBinaryOperator.AssignOperator
fun UVariable.isResourceVariable() = uastParent is UTryExpression

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2000-2017 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.values
import org.jetbrains.uast.UElement
import org.jetbrains.uast.UResolvable
// Value of something resolvable (e.g. call or property access)
// that we cannot or do not want to evaluate
class UCallResultValue(val resolvable: UResolvable, val arguments: List<UValue>) : UValueBase(), UDependency {
override fun equals(other: Any?) = other is UCallResultValue && resolvable == other.resolvable && arguments == other.arguments
override fun hashCode() = resolvable.hashCode()
override fun toString(): String {
return "external ${(resolvable as? UElement)?.asRenderString() ?: "???"}(${arguments.joinToString()})"
}
}

View File

@@ -0,0 +1,426 @@
/*
* Copyright 2000-2017 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.values
import com.intellij.psi.PsiEnumConstant
import com.intellij.psi.PsiType
import org.jetbrains.uast.*
interface UConstant : UValue {
val value: Any?
val source: UExpression?
// Used for string concatenation
fun asString(): String
// Used for logging / debugging purposes
override fun toString(): String
}
abstract class UAbstractConstant : UValueBase(), UConstant {
override fun valueEquals(other: UValue) = when (other) {
this -> UBooleanConstant.True
is UConstant -> UBooleanConstant.False
else -> super.valueEquals(other)
}
override fun equals(other: Any?) = other is UAbstractConstant && value == other.value
override fun hashCode() = value?.hashCode() ?: 0
override fun toString() = "$value"
override fun asString() = toString()
}
enum class UNumericType(val prefix: String = "") {
BYTE("(byte)"),
SHORT("(short)"),
INT(),
LONG("(long)"),
FLOAT("(float)"),
DOUBLE();
fun merge(other: UNumericType): UNumericType {
if (this == DOUBLE || other == DOUBLE) return DOUBLE
if (this == FLOAT || other == FLOAT) return FLOAT
if (this == LONG || other == LONG) return LONG
return INT
}
}
abstract class UNumericConstant(val type: UNumericType, override val source: ULiteralExpression?) : UAbstractConstant() {
override abstract val value: Number
override fun toString() = "${type.prefix}$value"
override fun asString() = "$value"
}
private fun PsiType.toNumeric(): UNumericType = when (this) {
PsiType.LONG -> UNumericType.LONG
PsiType.INT -> UNumericType.INT
PsiType.SHORT -> UNumericType.SHORT
PsiType.BYTE -> UNumericType.BYTE
PsiType.DOUBLE -> UNumericType.DOUBLE
PsiType.FLOAT -> UNumericType.FLOAT
else -> throw AssertionError("Conversion is impossible for type $canonicalText")
}
private fun Int.asType(type: UNumericType): Int = when (type) {
UNumericType.BYTE -> toByte().toInt()
UNumericType.SHORT -> toShort().toInt()
else -> this
}
class UIntConstant(
rawValue: Int, type: UNumericType = UNumericType.INT, override val source: ULiteralExpression? = null
) : UNumericConstant(type, source) {
init {
when (type) {
UNumericType.INT, UNumericType.SHORT, UNumericType.BYTE -> {}
else -> throw AssertionError("Incorrect UIntConstant type: $type")
}
}
override val value: Int = rawValue.asType(type)
constructor(value: Int, type: PsiType): this(value, type.toNumeric())
override fun plus(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value + other.value, type.merge(other.type))
is ULongConstant -> other + this
is UFloatConstant -> other + this
else -> super.plus(other)
}
override fun times(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value * other.value, type.merge(other.type))
is ULongConstant -> other * this
is UFloatConstant -> other * this
else -> super.times(other)
}
override fun div(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value / other.value, type.merge(other.type))
is ULongConstant -> ULongConstant(value / other.value)
is UFloatConstant -> UFloatConstant.create(value / other.value, type.merge(other.type))
else -> super.div(other)
}
override fun mod(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value % other.value, type.merge(other.type))
is ULongConstant -> ULongConstant(value % other.value)
is UFloatConstant -> UFloatConstant.create(value % other.value, type.merge(other.type))
else -> super.mod(other)
}
override fun unaryMinus() = UIntConstant(-value, type)
override fun greater(other: UValue) = when (other) {
is UIntConstant -> UBooleanConstant.valueOf(value > other.value)
is ULongConstant -> UBooleanConstant.valueOf(value > other.value)
is UFloatConstant -> UBooleanConstant.valueOf(value > other.value)
else -> super.greater(other)
}
override fun inc() = UIntConstant(value + 1, type)
override fun dec() = UIntConstant(value - 1, type)
override fun bitwiseAnd(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value and other.value, type.merge(other.type))
else -> super.bitwiseAnd(other)
}
override fun bitwiseOr(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value or other.value, type.merge(other.type))
else -> super.bitwiseOr(other)
}
override fun bitwiseXor(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value xor other.value, type.merge(other.type))
else -> super.bitwiseXor(other)
}
override fun shl(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value shl other.value, type.merge(other.type))
else -> super.shl(other)
}
override fun shr(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value shr other.value, type.merge(other.type))
else -> super.shr(other)
}
override fun ushr(other: UValue) = when (other) {
is UIntConstant -> UIntConstant(value ushr other.value, type.merge(other.type))
else -> super.ushr(other)
}
}
class ULongConstant(override val value: Long, source: ULiteralExpression? = null) : UNumericConstant(UNumericType.LONG, source) {
override fun plus(other: UValue) = when (other) {
is ULongConstant -> ULongConstant(value + other.value)
is UIntConstant -> ULongConstant(value + other.value)
is UFloatConstant -> other + this
else -> super.plus(other)
}
override fun times(other: UValue) = when (other) {
is ULongConstant -> ULongConstant(value * other.value)
is UIntConstant -> ULongConstant(value * other.value)
is UFloatConstant -> other * this
else -> super.times(other)
}
override fun div(other: UValue) = when (other) {
is ULongConstant -> ULongConstant(value / other.value)
is UIntConstant -> ULongConstant(value / other.value)
is UFloatConstant -> UFloatConstant.create(value / other.value, type.merge(other.type))
else -> super.div(other)
}
override fun mod(other: UValue) = when (other) {
is ULongConstant -> ULongConstant(value % other.value)
is UIntConstant -> ULongConstant(value % other.value)
is UFloatConstant -> UFloatConstant.create(value % other.value, type.merge(other.type))
else -> super.mod(other)
}
override fun unaryMinus() = ULongConstant(-value)
override fun greater(other: UValue) = when (other) {
is ULongConstant -> UBooleanConstant.valueOf(value > other.value)
is UIntConstant -> UBooleanConstant.valueOf(value > other.value)
is UFloatConstant -> UBooleanConstant.valueOf(value > other.value)
else -> super.greater(other)
}
override fun inc() = ULongConstant(value + 1)
override fun dec() = ULongConstant(value - 1)
override fun bitwiseAnd(other: UValue) = when (other) {
is ULongConstant -> ULongConstant(value and other.value)
else -> super.bitwiseAnd(other)
}
override fun bitwiseOr(other: UValue) = when (other) {
is ULongConstant -> ULongConstant(value or other.value)
else -> super.bitwiseOr(other)
}
override fun bitwiseXor(other: UValue) = when (other) {
is ULongConstant -> ULongConstant(value xor other.value)
else -> super.bitwiseXor(other)
}
override fun shl(other: UValue) = when (other) {
is UIntConstant -> ULongConstant(value shl other.value)
else -> super.shl(other)
}
override fun shr(other: UValue) = when (other) {
is UIntConstant -> ULongConstant(value shr other.value)
else -> super.shr(other)
}
override fun ushr(other: UValue) = when (other) {
is UIntConstant -> ULongConstant(value ushr other.value)
else -> super.ushr(other)
}
}
open class UFloatConstant protected constructor(
override val value: Double, type: UNumericType = UNumericType.DOUBLE, source: ULiteralExpression? = null
) : UNumericConstant(type, source) {
override fun plus(other: UValue) = when (other) {
is ULongConstant -> create(value + other.value, type.merge(other.type))
is UIntConstant -> create(value + other.value, type.merge(other.type))
is UFloatConstant -> create(value + other.value, type.merge(other.type))
else -> super.plus(other)
}
override fun times(other: UValue) = when (other) {
is ULongConstant -> create(value * other.value, type.merge(other.type))
is UIntConstant -> create(value * other.value, type.merge(other.type))
is UFloatConstant -> create(value * other.value, type.merge(other.type))
else -> super.times(other)
}
override fun div(other: UValue) = when (other) {
is ULongConstant -> create(value / other.value, type.merge(other.type))
is UIntConstant -> create(value / other.value, type.merge(other.type))
is UFloatConstant -> create(value / other.value, type.merge(other.type))
else -> super.div(other)
}
override fun mod(other: UValue) = when (other) {
is ULongConstant -> create(value % other.value, type.merge(other.type))
is UIntConstant -> create(value % other.value, type.merge(other.type))
is UFloatConstant -> create(value % other.value, type.merge(other.type))
else -> super.mod(other)
}
override fun greater(other: UValue) = when (other) {
is ULongConstant -> UBooleanConstant.valueOf(value > other.value)
is UIntConstant -> UBooleanConstant.valueOf(value > other.value)
is UFloatConstant -> UBooleanConstant.valueOf(value > other.value)
else -> super.greater(other)
}
override fun unaryMinus() = create(-value, type)
override fun inc() = create(value + 1, type)
override fun dec() = create(value - 1, type)
companion object {
fun create(value: Double, type: UNumericType = UNumericType.DOUBLE, source: ULiteralExpression? = null) =
when (type) {
UNumericType.DOUBLE, UNumericType.FLOAT -> {
if (value.isNaN()) UNaNConstant.valueOf(type)
else UFloatConstant(value, type, source)
}
else -> throw AssertionError("Incorrect UFloatConstant type: $type")
}
fun create(value: Double, type: PsiType) = create(value, type.toNumeric())
}
}
sealed class UNaNConstant(type: UNumericType = UNumericType.DOUBLE) : UFloatConstant(kotlin.Double.NaN, type) {
object Float : UNaNConstant(UNumericType.FLOAT)
object Double : UNaNConstant(UNumericType.DOUBLE)
override fun greater(other: UValue) = UBooleanConstant.False
override fun less(other: UValue) = UBooleanConstant.False
override fun greaterOrEquals(other: UValue) = UBooleanConstant.False
override fun lessOrEquals(other: UValue) = UBooleanConstant.False
override fun valueEquals(other: UValue) = UBooleanConstant.False
companion object {
fun valueOf(type: UNumericType) = when (type) {
UNumericType.DOUBLE -> Double
UNumericType.FLOAT -> Float
else -> throw AssertionError("NaN exists only for Float / Double, but not for $type")
}
}
}
class UCharConstant(override val value: Char, override val source: ULiteralExpression? = null) : UAbstractConstant() {
override fun plus(other: UValue) = when (other) {
is UIntConstant -> UCharConstant(value + other.value)
is UCharConstant -> UCharConstant(value + other.value.toInt())
else -> super.plus(other)
}
override fun minus(other: UValue) = when (other) {
is UIntConstant -> UCharConstant(value - other.value)
is UCharConstant -> UIntConstant(value - other.value)
else -> super.plus(other)
}
override fun greater(other: UValue) = when (other) {
is UCharConstant -> UBooleanConstant.valueOf(value > other.value)
else -> super.greater(other)
}
override fun inc() = this + UIntConstant(1)
override fun dec() = this - UIntConstant(1)
override fun toString() = "\'$value\'"
override fun asString() = "$value"
}
sealed class UBooleanConstant(override val value: Boolean) : UAbstractConstant() {
override val source = null
object True : UBooleanConstant(true) {
override fun not() = False
override fun and(other: UValue) = other as? UBooleanConstant ?: super.and(other)
override fun or(other: UValue) = True
}
object False : UBooleanConstant(false) {
override fun not() = True
override fun and(other: UValue) = False
override fun or(other: UValue) = other as? UBooleanConstant ?: super.or(other)
}
companion object {
fun valueOf(value: Boolean) = if (value) True else False
}
}
class UStringConstant(override val value: String, override val source: ULiteralExpression? = null) : UAbstractConstant() {
override fun plus(other: UValue) = when (other) {
is UConstant -> UStringConstant(value + other.asString())
else -> super.plus(other)
}
override fun greater(other: UValue) = when (other) {
is UStringConstant -> UBooleanConstant.valueOf(value > other.value)
else -> super.greater(other)
}
override fun asString() = value
override fun toString() = "\"$value\""
}
class UEnumEntryValueConstant(override val value: PsiEnumConstant, override val source: USimpleNameReferenceExpression? = null) : UAbstractConstant() {
override fun equals(other: Any?) =
other is UEnumEntryValueConstant &&
value.nameIdentifier.text == other.value.nameIdentifier.text &&
value.containingClass?.qualifiedName == other.value.containingClass?.qualifiedName
override fun hashCode(): Int {
var result = 19
result = result * 13 + value.nameIdentifier.text.hashCode()
result = result * 13 + (value.containingClass?.qualifiedName?.hashCode() ?: 0)
return result
}
override fun toString() = value.name?.let { "$it (enum entry)" }?: "<unnamed enum entry>"
override fun asString() = value.name ?: ""
}
class UClassConstant(override val value: PsiType, override val source: UClassLiteralExpression? = null) : UAbstractConstant() {
override fun toString() = value.name
}
object UNullConstant : UAbstractConstant() {
override val value = null
override val source = null
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright 2000-2017 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.values
interface UDependency {
}

View File

@@ -0,0 +1,137 @@
/*
* Copyright 2000-2017 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.values
open class UDependentValue protected constructor(
val value: UValue,
override val dependencies: Set<UDependency> = emptySet()
) : UValueBase() {
private fun UValue.unwrap() = (this as? UDependentValue)?.unwrap() ?: this
private fun unwrap(): UValue = value.unwrap()
private val dependenciesWithThis: Set<UDependency>
get() = (this as? UDependency)?.let { dependencies + it } ?: dependencies
private fun wrapBinary(result: UValue, arg: UValue): UValue {
val wrappedDependencies = (arg as? UDependentValue)?.dependenciesWithThis ?: emptySet()
val resultDependencies = dependenciesWithThis + wrappedDependencies
return create(result, resultDependencies)
}
private fun wrapUnary(result: UValue) = create(result, dependenciesWithThis)
override fun plus(other: UValue) = wrapBinary(unwrap() + other.unwrap(), other)
override fun minus(other: UValue) = wrapBinary(unwrap() - other.unwrap(), other)
override fun times(other: UValue) = wrapBinary(unwrap() * other.unwrap(), other)
override fun div(other: UValue) = wrapBinary(unwrap() / other.unwrap(), other)
internal fun inverseDiv(other: UValue) = wrapBinary(other.unwrap() / unwrap(), other)
override fun mod(other: UValue) = wrapBinary(unwrap() % other.unwrap(), other)
internal fun inverseMod(other: UValue) = wrapBinary(other.unwrap() % unwrap(), other)
override fun unaryMinus() = wrapUnary(-unwrap())
override fun valueEquals(other: UValue) = wrapBinary(unwrap() valueEquals other.unwrap(), other)
override fun valueNotEquals(other: UValue) = wrapBinary(unwrap() valueNotEquals other.unwrap(), other)
override fun not() = wrapUnary(!unwrap())
override fun greater(other: UValue) = wrapBinary(unwrap() greater other.unwrap(), other)
override fun less(other: UValue) = wrapBinary(other.unwrap() greater unwrap(), other)
override fun inc() = wrapUnary(unwrap().inc())
override fun dec() = wrapUnary(unwrap().dec())
override fun and(other: UValue) = wrapBinary(unwrap() and other.unwrap(), other)
override fun or(other: UValue) = wrapBinary(unwrap() or other.unwrap(), other)
override fun bitwiseAnd(other: UValue) = wrapBinary(unwrap() bitwiseAnd other.unwrap(), other)
override fun bitwiseOr(other: UValue) = wrapBinary(unwrap() bitwiseOr other.unwrap(), other)
override fun bitwiseXor(other: UValue) = wrapBinary(unwrap() bitwiseXor other.unwrap(), other)
override fun shl(other: UValue) = wrapBinary(unwrap() shl other.unwrap(), other)
internal fun inverseShiftLeft(other: UValue) = wrapBinary(other.unwrap() shl unwrap(), other)
override fun shr(other: UValue) = wrapBinary(unwrap() shr other.unwrap(), other)
internal fun inverseShiftRight(other: UValue) = wrapBinary(other.unwrap() shr unwrap(), other)
override fun ushr(other: UValue) = wrapBinary(unwrap() ushr other.unwrap(), other)
internal fun inverseShiftRightUnsigned(other: UValue) =
wrapBinary(other.unwrap() ushr unwrap(), other)
override fun merge(other: UValue) = when (other) {
this -> this
value -> this
is UDependentValue -> {
if (value != other.value) UPhiValue.create(this, other)
else UDependentValue(value, dependencies + other.dependencies)
}
else -> UPhiValue.create(this, other)
}
override fun toConstant() = value.toConstant()
open internal fun copy(dependencies: Set<UDependency>) =
if (dependencies == this.dependencies) this else create(value, dependencies)
override fun coerceConstant(constant: UConstant): UValue =
if (toConstant() == constant) this
else create(value.coerceConstant(constant), dependencies)
override fun equals(other: Any?) =
other is UDependentValue
&& javaClass == other.javaClass
&& value == other.value
&& dependencies == other.dependencies
override fun hashCode(): Int {
var result = 31
result = result * 19 + value.hashCode()
result = result * 19 + dependencies.hashCode()
return result
}
override fun toString() =
if (dependencies.isNotEmpty())
"$value" + dependencies.joinToString(prefix = " (depending on: ", postfix = ")", separator = ", ")
else
"$value"
companion object {
fun create(value: UValue, dependencies: Set<UDependency>): UValue =
if (dependencies.isNotEmpty()) UDependentValue(value, dependencies)
else value
internal fun UValue.coerceConstant(constant: UConstant): UValue =
(this as? UValueBase)?.coerceConstant(constant) ?: constant
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2000-2017 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.values
import org.jetbrains.uast.*
// Something that never can be reached / created
internal class UNothingValue private constructor(
val containingLoopOrSwitch: UExpression?,
val kind: JumpKind
) : UValueBase() {
constructor(jump: UJumpExpression) : this(jump.containingLoopOrSwitch(), jump.kind())
constructor() : this(null, JumpKind.OTHER)
enum class JumpKind {
BREAK,
CONTINUE,
OTHER;
}
override val reachable = false
override fun merge(other: UValue) = when (other) {
is UNothingValue -> {
val mergedLoopOrSwitch =
if (containingLoopOrSwitch == other.containingLoopOrSwitch) containingLoopOrSwitch
else null
val mergedKind = if (mergedLoopOrSwitch == null || kind != other.kind) JumpKind.OTHER else kind
UNothingValue(mergedLoopOrSwitch, mergedKind)
}
else -> super.merge(other)
}
override fun toString() = "Nothing" + when (kind) {
JumpKind.BREAK -> "(break)"
JumpKind.CONTINUE -> "(continue)"
else -> ""
}
companion object {
private fun UJumpExpression.containingLoopOrSwitch(): UExpression? {
var containingElement = uastParent
while (containingElement != null) {
if (this is UBreakExpression && label == null && containingElement is USwitchExpression) {
return containingElement
}
if (containingElement is ULoopExpression) {
val containingLabeled = containingElement.uastParent as? ULabeledExpression
if (label == null || label == containingLabeled?.label) {
return containingElement
}
}
containingElement = containingElement.uastParent
}
return null
}
private fun UExpression.kind(): JumpKind = when (this) {
is UBreakExpression -> JumpKind.BREAK
is UContinueExpression -> JumpKind.CONTINUE
else -> JumpKind.OTHER
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2000-2017 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.values
interface UOperand {
operator fun plus(other: UValue): UValue
operator fun minus(other: UValue): UValue
operator fun times(other: UValue): UValue
operator fun div(other: UValue): UValue
operator fun mod(other: UValue): UValue
operator fun unaryMinus(): UValue
operator fun not(): UValue
infix fun valueEquals(other: UValue): UValue
infix fun valueNotEquals(other: UValue): UValue
infix fun identityEquals(other: UValue): UValue
infix fun identityNotEquals(other: UValue): UValue
infix fun greater(other: UValue): UValue
infix fun less(other: UValue): UValue
infix fun greaterOrEquals(other: UValue): UValue
infix fun lessOrEquals(other: UValue): UValue
fun inc(): UValue
fun dec(): UValue
infix fun and(other: UValue): UValue
infix fun or(other: UValue): UValue
infix fun bitwiseAnd(other: UValue): UValue
infix fun bitwiseOr(other: UValue): UValue
infix fun bitwiseXor(other: UValue): UValue
infix fun shl(other: UValue): UValue
infix fun shr(other: UValue): UValue
infix fun ushr(other: UValue): UValue
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2000-2017 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.values
class UPhiValue private constructor(val values: Set<UValue>): UValueBase() {
override val dependencies: Set<UDependency> = values.flatMapTo(linkedSetOf()) { it.dependencies }
override fun equals(other: Any?) = other is UPhiValue && values == other.values
override fun hashCode() = values.hashCode()
override fun toString() = values.joinToString(prefix = "Phi(", postfix = ")", separator = ", ")
companion object {
fun create(values: Iterable<UValue>): UPhiValue {
val flattenedValues = values.flatMapTo(linkedSetOf<UValue>()) { (it as? UPhiValue)?.values ?: listOf(it) }
if (flattenedValues.size <= 1) {
throw AssertionError("UPhiValue should contain two or more values: $flattenedValues")
}
return UPhiValue(flattenedValues)
}
fun create(vararg values: UValue) = create(values.asIterable())
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2000-2017 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.values
// Something with value that cannot be evaluated
object UUndeterminedValue : UValueBase() {
override fun toString() = "Undetermined"
}
fun UValue.ifUndetermined(block: () -> UValue) = if (this == UUndeterminedValue) block() else this

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2000-2017 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.values
interface UValue : UOperand {
fun merge(other: UValue): UValue
val dependencies: Set<UDependency>
get() = emptySet()
fun toConstant(): UConstant?
val reachable: Boolean
companion object {
val UNREACHABLE: UValue = UNothingValue()
}
}
fun UValue.toPossibleConstants(): Set<UConstant> {
val results = mutableSetOf<UConstant>()
toPossibleConstants(results)
return results
}
private fun UValue.toPossibleConstants(result: MutableSet<UConstant>) {
when (this) {
is UDependentValue -> value.toPossibleConstants(result)
is UPhiValue -> values.forEach { it.toPossibleConstants(result) }
else -> toConstant()?.let { result.add(it) }
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2000-2017 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.values
abstract class UValueBase : UValue {
override operator fun plus(other: UValue): UValue =
if (other is UDependentValue) other + this else UUndeterminedValue
override operator fun minus(other: UValue): UValue = this + (-other)
override operator fun times(other: UValue): UValue =
if (other is UDependentValue) other * this else UUndeterminedValue
override operator fun div(other: UValue): UValue =
(other as? UDependentValue)?.inverseDiv(this) ?: UUndeterminedValue
override operator fun mod(other: UValue): UValue =
(other as? UDependentValue)?.inverseMod(this) ?: UUndeterminedValue
override fun unaryMinus(): UValue = UUndeterminedValue
override fun valueEquals(other: UValue): UValue =
if (other is UDependentValue || other is UNaNConstant) other.valueEquals(this) else UUndeterminedValue
override fun valueNotEquals(other: UValue): UValue = !this.valueEquals(other)
override fun identityEquals(other: UValue): UValue = valueEquals(other)
override fun identityNotEquals(other: UValue): UValue = !this.identityEquals(other)
override fun not(): UValue = UUndeterminedValue
override fun greater(other: UValue): UValue =
if (other is UDependentValue || other is UNaNConstant) other.less(this) else UUndeterminedValue
override fun less(other: UValue): UValue = other.greater(this)
override fun greaterOrEquals(other: UValue) = this.greater(other) or this.valueEquals(other)
override fun lessOrEquals(other: UValue) = this.less(other) or this.valueEquals(other)
override fun inc(): UValue = UUndeterminedValue
override fun dec(): UValue = UUndeterminedValue
override fun and(other: UValue): UValue =
if (other is UDependentValue || other == UBooleanConstant.False) other and this else UUndeterminedValue
override fun or(other: UValue): UValue =
if (other is UDependentValue || other == UBooleanConstant.True) other or this else UUndeterminedValue
override fun bitwiseAnd(other: UValue): UValue =
if (other is UDependentValue) other bitwiseAnd this else UUndeterminedValue
override fun bitwiseOr(other: UValue): UValue =
if (other is UDependentValue) other bitwiseOr this else UUndeterminedValue
override fun bitwiseXor(other: UValue): UValue =
if (other is UDependentValue) other bitwiseXor this else UUndeterminedValue
override fun shl(other: UValue): UValue =
(other as? UDependentValue)?.inverseShiftLeft(this) ?: UUndeterminedValue
override fun shr(other: UValue): UValue =
(other as? UDependentValue)?.inverseShiftRight(this) ?: UUndeterminedValue
override fun ushr(other: UValue): UValue =
(other as? UDependentValue)?.inverseShiftRightUnsigned(this) ?: UUndeterminedValue
override fun merge(other: UValue): UValue = when (other) {
this -> this
is UVariableValue -> other.merge(this)
else -> UPhiValue.create(this, other)
}
override val dependencies: Set<UDependency>
get() = emptySet()
override fun toConstant(): UConstant? = this as? UConstant
internal open fun coerceConstant(constant: UConstant): UValue = constant
override val reachable = true
override abstract fun toString(): String
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright 2000-2017 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.values
import com.intellij.psi.PsiType
import org.jetbrains.uast.UVariable
class UVariableValue private constructor(
val variable: UVariable,
value: UValue,
dependencies: Set<UDependency>
) : UDependentValue(value, dependencies), UDependency {
override fun identityEquals(other: UValue): UValue =
if (this == other) super.valueEquals(other)
else when (variable.psi.type) {
PsiType.BYTE, PsiType.FLOAT, PsiType.DOUBLE, PsiType.LONG,
PsiType.SHORT, PsiType.INT, PsiType.CHAR, PsiType.BOOLEAN -> super.valueEquals(other)
else -> UUndeterminedValue
}
override fun merge(other: UValue) = when (other) {
this -> this
value -> this
is UVariableValue -> {
if (variable != other.variable || value != other.value) UPhiValue.create(this, other)
else create(variable, value, dependencies + other.dependencies)
}
is UDependentValue -> {
if (value != other.value) UPhiValue.create(this, other)
else create(variable, value, dependencies + other.dependencies)
}
else -> UPhiValue.create(this, other)
}
override fun copy(dependencies: Set<UDependency>) =
if (dependencies == this.dependencies) this else create(variable, value, dependencies)
override fun coerceConstant(constant: UConstant): UValue =
if (constant == toConstant()) this
else create(variable, value.coerceConstant(constant), dependencies)
override fun equals(other: Any?) =
other is UVariableValue
&& variable == other.variable
&& value == other.value
&& dependencies == other.dependencies
override fun hashCode(): Int {
var result = 31
result = result * 19 + variable.hashCode()
result = result * 19 + value.hashCode()
result = result * 19 + dependencies.hashCode()
return result
}
override fun toString() = "(var ${variable.name ?: "<unnamed>"} = ${super.toString()})"
companion object {
private fun Set<UDependency>.filterNot(variable: UVariable) =
filterTo(linkedSetOf()) { it !is UVariableValue || variable != it.variable }
fun create(variable: UVariable, value: UValue, dependencies: Set<UDependency> = emptySet()): UVariableValue {
when (variable.psi.type) {
PsiType.BYTE, PsiType.SHORT -> {
val constant = value.toConstant()
if (constant is UIntConstant && constant.type == UNumericType.INT) {
val castConstant = UIntConstant(constant.value, variable.psi.type)
return create(variable, value.coerceConstant(castConstant), dependencies)
}
}
}
val dependenciesWithoutSelf = dependencies.filterNot(variable)
return when {
value is UVariableValue
&& variable == value.variable
&& dependenciesWithoutSelf == value.dependencies -> value
value is UDependentValue -> {
val valueDependencies = value.dependencies.filterNot(variable)
val modifiedValue = value.copy(valueDependencies)
UVariableValue(variable, modifiedValue, dependenciesWithoutSelf)
}
else -> UVariableValue(variable, value, dependenciesWithoutSelf)
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More