├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── .gitattributes ├── settings.gradle.kts ├── gradle.properties ├── README.md ├── core ├── src │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── facebook │ │ │ ├── aelements │ │ │ ├── APropertyAccessor.kt │ │ │ ├── ATypeConstraint.kt │ │ │ ├── AIfExpression.kt │ │ │ ├── ADeclarationOrLambdaWithBody.kt │ │ │ ├── ANamedElement.kt │ │ │ ├── ACallableDeclarationOrLambda.kt │ │ │ ├── AConstructor.kt │ │ │ ├── AFile.kt │ │ │ ├── ATypeReference.kt │ │ │ ├── APackageDirective.kt │ │ │ ├── mods │ │ │ │ └── AFileMods.kt │ │ │ ├── AParameter.kt │ │ │ ├── AExpression.kt │ │ │ ├── ALocalProperty.kt │ │ │ ├── AMemberProperty.kt │ │ │ ├── ATypeParameter.kt │ │ │ ├── AWhileStatement.kt │ │ │ ├── ACodeBlockExpressionOrStatement.kt │ │ │ ├── ASwitchStatement.kt │ │ │ ├── AAnnotated.kt │ │ │ ├── ADoWhileStatement.kt │ │ │ ├── AWhileExpression.kt │ │ │ ├── ADoWhileExpression.kt │ │ │ ├── AClassLiteralExpression.kt │ │ │ ├── AWhenExpression.kt │ │ │ ├── ATypeParameterListOwner.kt │ │ │ ├── AAssignmentExpression.kt │ │ │ ├── ALambdaExpression.kt │ │ │ ├── ACallableDeclaration.kt │ │ │ ├── ABinaryExpression.kt │ │ │ ├── ADeclarationWithBody.kt │ │ │ ├── AVariableDeclaration.kt │ │ │ ├── AForeachStatement.kt │ │ │ ├── AImportDirective.kt │ │ │ ├── AForExpression.kt │ │ │ ├── AExpressionOrStatement.kt │ │ │ ├── AReturnExpression.kt │ │ │ ├── AProperty.kt │ │ │ ├── ACodeBlock.kt │ │ │ ├── AIfExpressionOrStatement.kt │ │ │ ├── AThrowExpression.kt │ │ │ ├── ATypeArgumentList.kt │ │ │ ├── ACallExpression.kt │ │ │ ├── ABreakExpression.kt │ │ │ ├── AForStatement.kt │ │ │ ├── AContinueExpression.kt │ │ │ ├── AParameterList.kt │ │ │ ├── AAnnotation.kt │ │ │ ├── AValueArgumentList.kt │ │ │ ├── ANamedFunction.kt │ │ │ ├── ATryExpression.kt │ │ │ ├── AModifierListOwner.kt │ │ │ ├── AArrayAccessExpression.kt │ │ │ ├── AQualifiedExpression.kt │ │ │ ├── AQualifiedCallExpression.kt │ │ │ ├── AClassOrObject.kt │ │ │ └── AMethodOrNewCallExpression.kt │ │ │ ├── matching │ │ │ ├── OrPsiAstMatcher.kt │ │ │ ├── Resolver.kt │ │ │ ├── PsiAstMatcher.kt │ │ │ └── Variable.kt │ │ │ └── asttools │ │ │ ├── ProjectHelper.kt │ │ │ ├── PsiActions.kt │ │ │ └── PsiTestUtils.kt │ └── test │ │ └── java │ │ └── com │ │ └── facebook │ │ ├── asttools │ │ └── PsiActionsTest.kt │ │ ├── aelements │ │ ├── AFileTest.kt │ │ ├── ATypeConstraintTest.kt │ │ ├── AVariableDeclarationTest.kt │ │ ├── APackageDirectiveTest.kt │ │ ├── ABreakExpressionTest.kt │ │ ├── AConstructorTest.kt │ │ ├── AReturnExpressionTest.kt │ │ ├── AContinueExpressionTest.kt │ │ ├── AThrowExpressionTest.kt │ │ ├── ATypeArgumentListTest.kt │ │ ├── ANamedElementTest.kt │ │ ├── AAssignmentExpressionTest.kt │ │ ├── ATypeParameterListOwnerTest.kt │ │ ├── ALambdaExpressionTest.kt │ │ ├── AParameterListTest.kt │ │ ├── AValueArgumentListTest.kt │ │ ├── ACallExpressionTest.kt │ │ ├── ADeclarationWithBodyTest.kt │ │ ├── AClassLiteralExpressionTest.kt │ │ ├── ADeclarationOrLambdaWithBodyTest.kt │ │ ├── AIfExpressionOrStatementTest.kt │ │ ├── AAnnotationTest.kt │ │ ├── ATryExpressionTest.kt │ │ ├── AElementUtilTest.kt │ │ ├── AArrayAccessExpressionTest.kt │ │ ├── ABinaryExpressionTest.kt │ │ └── mods │ │ │ └── AFileModsTest.kt │ │ └── matching │ │ ├── PsiAstMatcherImplTest.kt │ │ └── PsiAstTemplateParserTest.kt └── build.gradle.kts ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fbsamples/kotlin_ast_tools/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .DS_Store 3 | .idea 4 | build/ 5 | local.properties 6 | localhost/ 7 | obj/ 8 | *.iml 9 | Gemfile.lock 10 | _site/ 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | # 4 | # Linux start script should use lf 5 | /gradlew text eol=lf 6 | 7 | # These are Windows script files and should use crlf 8 | *.bat text eol=crlf 9 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 19 14:44:16 EDT 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | rootProject.name = "ast_tools" 18 | 19 | include("core") 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Copyright (c) Meta Platforms, Inc. and affiliates. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Project-wide Gradle settings. 16 | # Specifies the JVM arguments used for the daemon process. 17 | # The setting is particularly useful for tweaking memory settings. 18 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 19 | # Kotlin code style for this project: "official" or "obsolete": 20 | kotlin.code.style=official 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlin AST Tools by Meta 2 | 3 | These are various examples, tools and utilities extracted from Meta's Java to Kotlin migration effort. Some of these may be directly used, and some are supplied as examples and inspirations to encourage more automated editing of Kotlin code. 4 | 5 | We are releasing this to provide examples of AST manipulation in Kotlin using Kotlin APIs in hope that this will encourage more people to use them. 6 | 7 | Currently, there are three packages here: 8 | 1. `com.facebook.asttools` containing some utilities we use to simplify work with the Kotlin compiler API 9 | 2. `com.facebook.matching` containing a helper class to allow simple and readable refactors to a Kotlin file 10 | 3. `com.facebook.kotlin.postconversion` containing a few examples of common cleanups of newly converted Kotlin files, and a simple command line tool to run them. 11 | 12 | ## Running the tool 13 | 14 | The example tool can be built and run with gradle: 15 | 16 | ```shell 17 | ./gradlew run 18 | ``` 19 | 20 | ## License 21 | 22 | Apache License 2.0 23 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/APropertyAccessor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.psi.KtPropertyAccessor 21 | 22 | class APropertyAccessor internal constructor(psiElement: PsiElement) : 23 | ADeclarationWithBody, AElementImpl(psiElement) { 24 | 25 | constructor(ktAPropertyAccessor: KtPropertyAccessor) : this(ktAPropertyAccessor as PsiElement) 26 | 27 | override val kotlinElement: KtPropertyAccessor? 28 | get() = castKotlinElement() 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ATypeConstraint.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.psi.KtTypeConstraint 21 | 22 | /** 23 | * Represents a type constraint for a type parameter in a Kotlin function, class or object. 24 | * 25 | * i.e. `A : String` in `fun f() where A : String {}` 26 | */ 27 | open class ATypeConstraint internal constructor(psiElement: PsiElement) : AElementImpl(psiElement) { 28 | constructor(ktTypeConstraint: KtTypeConstraint) : this(ktTypeConstraint as PsiElement) 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AIfExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.psi.KtIfExpression 20 | 21 | /** Represents an if construct, which is an expression in Kotlin, but a statement in Java */ 22 | open class AIfExpression(ktIfExpression: KtIfExpression) : 23 | AExpression, AIfExpressionOrStatement(ktIfExpression) { 24 | 25 | override val javaElement: Nothing? 26 | get() = null 27 | 28 | override val kotlinElement: KtIfExpression? 29 | get() = castKotlinElement() 30 | 31 | override val ifLanguage: Cases 32 | get() = castIfLanguage() 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ADeclarationOrLambdaWithBody.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.psi.KtElement 21 | 22 | /** An element with a body expression, a function or a constructor */ 23 | interface ADeclarationOrLambdaWithBody : AElement { 24 | 25 | override val javaElement: PsiElement? 26 | get() = castJavaElement() 27 | 28 | override val kotlinElement: KtElement? 29 | get() = castKotlinElement() 30 | 31 | override val ifLanguage: Cases 32 | get() = castIfLanguage() 33 | 34 | val bodyExpression: AElement? 35 | } 36 | -------------------------------------------------------------------------------- /core/build.gradle.kts: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | plugins { 18 | id("org.jetbrains.kotlin.jvm") 19 | id("application") 20 | } 21 | 22 | repositories { mavenCentral() } 23 | 24 | dependencies { 25 | implementation(platform("org.jetbrains.kotlin:kotlin-bom")) 26 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") 27 | implementation("com.google.guava:guava:31.0.1-jre") 28 | implementation("org.jetbrains.kotlin:kotlin-compiler") 29 | testImplementation("org.assertj:assertj-core:2.9.0") 30 | testImplementation("org.jetbrains.kotlin:kotlin-test") 31 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit") 32 | } 33 | 34 | application { mainClass.set("com.facebook.kotlin.postconversion.PostConversionExamples") } 35 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Kotlin AST Tools 2 | This project is mostly made of sample code, but if you see mistakes or issues we would happily accept a pull request. 3 | 4 | **If you plan a significant change, please first start a discussion as a GitHub Issue.** 5 | 6 | ## Pull Requests 7 | We actively welcome your pull requests. 8 | 9 | 1. Fork the repo and create your branch from `main`. 10 | 2. If you've added code that should be tested, add tests. 11 | 3. If you've changed APIs, update the documentation. 12 | 4. Ensure the test suite passes. 13 | 5. Make sure your code lints. 14 | 6. If you haven't already, complete the Contributor License Agreement ("CLA"). 15 | 16 | ## Contributor License Agreement ("CLA") 17 | In order to accept your pull request, we need you to submit a CLA. You only need 18 | to do this once to work on any of Facebook's open source projects. 19 | 20 | Complete your CLA here: 21 | 22 | ## Issues 23 | We use GitHub issues to track public bugs. Please ensure your description is 24 | clear and has sufficient instructions to be able to reproduce the issue. 25 | 26 | Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe 27 | disclosure of security bugs. In those cases, please go through the process 28 | outlined on that page and do not file a public issue. 29 | 30 | ## License 31 | By contributing to ktfmt, you agree that your contributions will be licensed 32 | under the LICENSE file in the root directory of this source tree. 33 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ANamedElement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiNameIdentifierOwner 20 | import org.jetbrains.kotlin.psi.KtNamedDeclaration 21 | 22 | /** Represents an element with a name */ 23 | interface ANamedElement : AElement { 24 | 25 | override val javaElement: PsiNameIdentifierOwner? 26 | get() = castJavaElement() 27 | 28 | override val kotlinElement: KtNamedDeclaration? 29 | get() = castKotlinElement() 30 | 31 | override val ifLanguage: Cases 32 | get() = castIfLanguage() 33 | 34 | val name: String? 35 | get() = javaElement?.name ?: kotlinElement?.name 36 | 37 | val nameIdentifier: AElement? 38 | get() = javaElement?.nameIdentifier?.toAElement() ?: kotlinElement?.nameIdentifier?.toAElement() 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ACallableDeclarationOrLambda.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethod 21 | import org.jetbrains.kotlin.psi.KtCallableDeclaration 22 | 23 | /** Represents a method in Java or a function in Kotlin */ 24 | abstract class ACallableDeclarationOrLambda internal constructor(psiElement: PsiElement) : 25 | AAnnotated(psiElement) { 26 | 27 | override val javaElement: PsiMethod? 28 | get() = castJavaElement() 29 | 30 | override val kotlinElement: KtCallableDeclaration? 31 | get() = castKotlinElement() 32 | 33 | override val ifLanguage: Cases 34 | get() = castIfLanguage() 35 | 36 | abstract val parameterList: AParameterList 37 | 38 | abstract val valueParameters: List 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AConstructor.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethod 21 | import org.jetbrains.kotlin.psi.KtConstructor 22 | 23 | /** Represents a method in Java or a function in Kotlin */ 24 | open class AConstructor internal constructor(psiElement: PsiElement) : 25 | ACallableDeclaration(psiElement), ADeclarationWithBody { 26 | constructor(psiMethod: PsiMethod) : this(psiMethod as PsiElement) 27 | 28 | constructor(ktConstructor: KtConstructor<*>) : this(ktConstructor as PsiElement) 29 | 30 | override val javaElement: PsiMethod? 31 | get() = castJavaElement() 32 | 33 | override val kotlinElement: KtConstructor<*>? 34 | get() = castKotlinElement() 35 | 36 | override val ifLanguage: Cases> 37 | get() = castIfLanguage() 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AFile.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiFile 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiJavaFile 21 | import org.jetbrains.kotlin.psi.KtFile 22 | 23 | /** Represents an entire file (which is the root of the AST) */ 24 | open class AFile internal constructor(psiFile: PsiFile) : AElementImpl(psiFile) { 25 | constructor(psiJavaFile: PsiJavaFile) : this(psiJavaFile as PsiFile) 26 | 27 | constructor(ktFile: KtFile) : this(ktFile as PsiFile) 28 | 29 | override val psiElement: PsiFile 30 | get() = super.psiElement as PsiFile 31 | 32 | override val javaElement: PsiJavaFile? 33 | get() = castJavaElement() 34 | 35 | override val kotlinElement: KtFile? 36 | get() = castKotlinElement() 37 | 38 | val packageName: String? 39 | get() = javaElement?.packageName ?: kotlinElement?.packageFqName?.asString() 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/matching/OrPsiAstMatcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.matching 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | 21 | /** 22 | * Takes two matchers and merges them as one, this is needed to support one specific case for now so 23 | * we keep it as internal 24 | */ 25 | internal class OrPsiAstMatcher< 26 | CommonAncestorElement : PsiElement, 27 | E1 : CommonAncestorElement, 28 | E2 : CommonAncestorElement, 29 | >( 30 | private val matcher1: PsiAstMatcher, 31 | private val matcher2: PsiAstMatcher, 32 | ) : PsiAstMatcher { 33 | 34 | override val shouldMatchToNull: Boolean 35 | get() = matcher1.shouldMatchToNull || matcher2.shouldMatchToNull 36 | 37 | override fun matches(obj: PsiElement?): MatchResult? { 38 | return (matcher1.matches(obj) ?: matcher2.matches(obj)) as MatchResult? 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ATypeReference.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiTypeElement 21 | import org.jetbrains.kotlin.psi.KtTypeReference 22 | 23 | /** Represents the name of a type, e.g. `String` in `String s` or `Foo in fun f(foo: Foo)` */ 24 | open class ATypeReference internal constructor(psiElement: PsiElement) : AElementImpl(psiElement) { 25 | constructor(psiTypeElement: PsiTypeElement) : this(psiTypeElement as PsiElement) 26 | 27 | constructor(ktTypeReference: KtTypeReference) : this(ktTypeReference as PsiElement) 28 | 29 | override val javaElement: PsiTypeElement? 30 | get() = castJavaElement() 31 | 32 | override val kotlinElement: KtTypeReference? 33 | get() = castKotlinElement() 34 | 35 | override val ifLanguage: Cases 36 | get() = castIfLanguage() 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/APackageDirective.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiPackageStatement 21 | import org.jetbrains.kotlin.psi.KtPackageDirective 22 | 23 | /** Represents the package statement at top of a file, i.e. `package foo.bar` */ 24 | open class APackageDirective internal constructor(psiElement: PsiElement) : 25 | AElementImpl(psiElement) { 26 | constructor(psiPackageStatement: PsiPackageStatement) : this(psiPackageStatement as PsiElement) 27 | 28 | constructor(ktPackageDirective: KtPackageDirective) : this(ktPackageDirective as PsiElement) 29 | 30 | override val javaElement: PsiPackageStatement? 31 | get() = castJavaElement() 32 | 33 | override val kotlinElement: KtPackageDirective? 34 | get() = castKotlinElement() 35 | 36 | val packageName: String? 37 | get() = javaElement?.packageName ?: kotlinElement?.fqName?.asString() 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/mods/AFileMods.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements.mods 18 | 19 | import com.facebook.aelements.AElement 20 | import com.facebook.aelements.AFile 21 | import com.facebook.aelements.collectDescendantsOfType 22 | import com.facebook.tools.codemods.writer.psi.PsiWriter 23 | 24 | /** 25 | * Replaces all elements that match the given matcher with the given replacement in place. Add 26 | * necessary imports and buck dependencies 27 | */ 28 | inline fun AFile.replaceInPlace( 29 | crossinline matcher: (T) -> Boolean, 30 | replacement: (T) -> String, 31 | imports: List = emptyList(), 32 | deps: List = emptyList(), 33 | psiWriter: PsiWriter, 34 | ) { 35 | val selections = collectDescendantsOfType { matcher(it) } 36 | selections.forEach { selection -> psiWriter.changeTo(selection, replacement(selection)) } 37 | imports.forEach { psiWriter.addImport(it) } 38 | deps.forEach { psiWriter.buck.addDep(it) } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AParameter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiParameter 21 | import org.jetbrains.kotlin.psi.KtParameter 22 | 23 | /* Represents a value parameter (not to be confused with a type parameter) in a function declaration. */ 24 | open class AParameter internal constructor(psiElement: PsiElement) : 25 | AVariableDeclaration(psiElement) { 26 | constructor(psiTypeElement: PsiParameter) : this(psiTypeElement as PsiElement) 27 | 28 | constructor(ktTypeReference: KtParameter) : this(ktTypeReference as PsiElement) 29 | 30 | override val javaElement: PsiParameter? 31 | get() = super.javaElement as PsiParameter? 32 | 33 | override val kotlinElement: KtParameter? 34 | get() = super.kotlinElement as KtParameter? 35 | 36 | override val ifLanguage: Cases 37 | get() = super.ifLanguage as Cases 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiExpression 21 | import org.jetbrains.kotlin.psi.KtExpression 22 | 23 | /** 24 | * Represents any expression. Note that Java has the concept of statements as well, but not Kotlin 25 | */ 26 | interface AExpression : AExpressionOrStatement { 27 | override val javaElement: PsiExpression? 28 | get() = castJavaElement() 29 | 30 | override val kotlinElement: KtExpression? 31 | get() = castKotlinElement() 32 | 33 | override val ifLanguage: Cases 34 | get() = castIfLanguage() 35 | } 36 | 37 | open class AExpressionImpl internal constructor(psiElement: PsiElement) : 38 | AExpression, AElementImpl(psiElement) { 39 | constructor(psiExpression: PsiExpression) : this(psiExpression as PsiElement) 40 | 41 | constructor(ktExpression: KtExpression) : this(ktExpression as PsiElement) 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ALocalProperty.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiLocalVariable 21 | import org.jetbrains.kotlin.psi.KtProperty 22 | 23 | /** 24 | * Represents a local variable declaration. Note that in Kotlin Psi this is the same element as 25 | * class field, but we make them into two different AElements to be consistent with Java 26 | */ 27 | open class ALocalProperty internal constructor(psiElement: PsiElement) : 28 | AProperty(psiElement), AExpressionOrStatement { 29 | constructor(psiLocalVariable: PsiLocalVariable) : this(psiLocalVariable as PsiElement) 30 | 31 | constructor(ktProperty: KtProperty) : this(ktProperty as PsiElement) 32 | 33 | override val javaElement: PsiLocalVariable? 34 | get() = castJavaElement() 35 | 36 | override val kotlinElement: KtProperty? 37 | get() = castKotlinElement() 38 | 39 | override val ifLanguage: Cases 40 | get() = castIfLanguage() 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AMemberProperty.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiField 21 | import org.jetbrains.kotlin.psi.KtProperty 22 | 23 | /** 24 | * Represents a member variable declaration. Note that in Kotlin Psi this is the same element as a 25 | * local declaration, but we make them into two different AElements to be consistent with Java 26 | */ 27 | open class AMemberProperty internal constructor(psiElement: PsiElement) : AProperty(psiElement) { 28 | constructor(psiField: PsiField) : this(psiField as PsiElement) 29 | 30 | constructor(ktProperty: KtProperty) : this(ktProperty as PsiElement) 31 | 32 | override val javaElement: PsiField? 33 | get() = super.javaElement as PsiField? 34 | 35 | override val kotlinElement: KtProperty? 36 | get() = super.kotlinElement as KtProperty? 37 | 38 | override val ifLanguage: Cases 39 | get() = super.ifLanguage as Cases 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ATypeParameter.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiTypeParameter 21 | import org.jetbrains.kotlin.psi.KtTypeParameter 22 | 23 | /** 24 | * Represents a type parameter (not to be confused with a type argument) in function, class or 25 | * object definition i.e. `A` and `B` in `fun f() {}` or `A` and `B` in `class C {}` 26 | */ 27 | open class ATypeParameter internal constructor(psiElement: PsiElement) : AElementImpl(psiElement) { 28 | constructor(psiTypeParameter: PsiTypeParameter) : this(psiTypeParameter as PsiElement) 29 | 30 | constructor(ktTypeParameter: KtTypeParameter) : this(ktTypeParameter as PsiElement) 31 | 32 | override val javaElement: PsiTypeParameter? 33 | get() = castJavaElement() 34 | 35 | override val kotlinElement: KtTypeParameter? 36 | get() = castKotlinElement() 37 | 38 | override val ifLanguage: Cases 39 | get() = castIfLanguage() 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AWhileStatement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiWhileStatement 20 | 21 | /** Represents a while loop in Java. For example: `while (i < 10) { ... }` */ 22 | interface AWhileStatement : AExpressionOrStatement { 23 | /** For example: `i < 10` in `while (i < 10)` */ 24 | val condition: AExpression? 25 | 26 | /** The body of the while loop. */ 27 | val body: AExpressionOrStatement? 28 | } 29 | 30 | class AWhileStatementImpl(override val psiElement: PsiWhileStatement) : 31 | AWhileStatement, AElementImpl(psiElement) { 32 | 33 | override val javaElement: PsiWhileStatement 34 | get() = psiElement 35 | 36 | override val kotlinElement: Nothing? 37 | get() = null 38 | 39 | override val condition: AExpression? 40 | get() = psiElement.condition?.toAElement() 41 | 42 | override val body: AExpressionOrStatement? 43 | get() = psiElement.body?.toAElement() 44 | 45 | override val ifLanguage: Cases 46 | get() = castIfLanguage() 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ACodeBlockExpressionOrStatement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiBlockStatement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiCodeBlock 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 22 | import org.jetbrains.kotlin.psi.KtBlockExpression 23 | 24 | /** Represents a code block which is an expression in Kotlin and a statement in Java */ 25 | open class ACodeBlockExpressionOrStatement private constructor(psiElement: PsiElement) : 26 | AExpressionOrStatementImpl(psiElement), ACodeBlock { 27 | constructor(psiCodeBlock: PsiBlockStatement) : this(psiCodeBlock.codeBlock as PsiElement) 28 | 29 | constructor(ktBlockExpression: KtBlockExpression) : this(ktBlockExpression as PsiElement) 30 | 31 | override val javaElement: PsiCodeBlock? 32 | get() = castJavaElement() 33 | 34 | override val kotlinElement: KtBlockExpression? 35 | get() = castKotlinElement() 36 | 37 | override val ifLanguage: Cases 38 | get() = castIfLanguage() 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ASwitchStatement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiSwitchStatement 20 | 21 | /** Represents a switch statement in Java. For example: `switch (value) { case 1: ... }` */ 22 | interface ASwitchStatement : AExpressionOrStatement { 23 | /** For example: `value` in `switch (value)` */ 24 | val expression: AExpression? 25 | 26 | /** The body containing case statements. */ 27 | val body: ACodeBlock? 28 | } 29 | 30 | class ASwitchStatementImpl(override val psiElement: PsiSwitchStatement) : 31 | ASwitchStatement, AElementImpl(psiElement) { 32 | 33 | override val javaElement: PsiSwitchStatement 34 | get() = psiElement 35 | 36 | override val kotlinElement: Nothing? 37 | get() = null 38 | 39 | override val expression: AExpression? 40 | get() = psiElement.expression?.toAElement() 41 | 42 | override val body: ACodeBlock? 43 | get() = psiElement.body?.toAElement() 44 | 45 | override val ifLanguage: Cases 46 | get() = castIfLanguage() 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AAnnotated.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiModifierListOwner 21 | import org.jetbrains.kotlin.psi.KtAnnotated 22 | 23 | /** Represents any AElement that can be annotated. */ 24 | open class AAnnotated internal constructor(psiElement: PsiElement) : AElementImpl(psiElement) { 25 | constructor(psiJvmModifierOwner: PsiModifierListOwner) : this(psiJvmModifierOwner as PsiElement) 26 | 27 | constructor(ktAnnotated: KtAnnotated) : this(ktAnnotated as PsiElement) 28 | 29 | override val javaElement: PsiModifierListOwner? 30 | get() = castJavaElement() 31 | 32 | override val kotlinElement: KtAnnotated? 33 | get() = castKotlinElement() 34 | 35 | override val ifLanguage: Cases 36 | get() = castIfLanguage() 37 | 38 | val annotations: List 39 | get() = 40 | javaElement?.annotations?.map { AAnnotation(it) } 41 | ?: kotlinElement!!.annotationEntries.map { AAnnotation(it) } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ADoWhileStatement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiDoWhileStatement 20 | 21 | /** Represents a do-while loop in Java. For example: `do { ... } while (i < 10);` */ 22 | interface ADoWhileStatement : AExpressionOrStatement { 23 | /** For example: `i < 10` in `do { ... } while (i < 10)` */ 24 | val condition: AExpression? 25 | 26 | /** The body of the do-while loop. */ 27 | val body: AExpressionOrStatement? 28 | } 29 | 30 | class ADoWhileStatementImpl(override val psiElement: PsiDoWhileStatement) : 31 | ADoWhileStatement, AElementImpl(psiElement) { 32 | 33 | override val javaElement: PsiDoWhileStatement 34 | get() = psiElement 35 | 36 | override val kotlinElement: Nothing? 37 | get() = null 38 | 39 | override val condition: AExpression? 40 | get() = psiElement.condition?.toAElement() 41 | 42 | override val body: AExpressionOrStatement? 43 | get() = psiElement.body?.toAElement() 44 | 45 | override val ifLanguage: Cases 46 | get() = castIfLanguage() 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AWhileExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.psi.KtExpression 20 | import org.jetbrains.kotlin.psi.KtWhileExpression 21 | 22 | /** Represents a while loop in Kotlin. For example: `while (i < 10) { ... }` */ 23 | interface AWhileExpression : AExpression { 24 | /** For example: `i < 10` in `while (i < 10)` */ 25 | val condition: AExpression? 26 | 27 | /** The body of the while loop. */ 28 | val body: AExpressionOrStatement? 29 | } 30 | 31 | class AWhileExpressionImpl(override val psiElement: KtWhileExpression) : 32 | AWhileExpression, AElementImpl(psiElement) { 33 | 34 | override val javaElement: Nothing? 35 | get() = null 36 | 37 | override val kotlinElement: KtExpression 38 | get() = psiElement 39 | 40 | override val condition: AExpression? 41 | get() = psiElement.condition?.toAElement() as? AExpression 42 | 43 | override val body: AExpressionOrStatement? 44 | get() = psiElement.body?.toAElement() 45 | 46 | override val ifLanguage: Cases 47 | get() = castIfLanguage() 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ADoWhileExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.psi.KtDoWhileExpression 20 | import org.jetbrains.kotlin.psi.KtExpression 21 | 22 | /** Represents a do-while loop in Kotlin. For example: `do { ... } while (i < 10)` */ 23 | interface ADoWhileExpression : AExpression { 24 | /** For example: `i < 10` in `do { ... } while (i < 10)` */ 25 | val condition: AExpression? 26 | 27 | /** The body of the do-while loop. */ 28 | val body: AExpressionOrStatement? 29 | } 30 | 31 | class ADoWhileExpressionImpl(override val psiElement: KtDoWhileExpression) : 32 | ADoWhileExpression, AElementImpl(psiElement) { 33 | 34 | override val javaElement: Nothing? 35 | get() = null 36 | 37 | override val kotlinElement: KtExpression 38 | get() = psiElement 39 | 40 | override val condition: AExpression? 41 | get() = psiElement.condition?.toAElement() as? AExpression 42 | 43 | override val body: AExpressionOrStatement? 44 | get() = psiElement.body?.toAElement() 45 | 46 | override val ifLanguage: Cases 47 | get() = castIfLanguage() 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AClassLiteralExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiClassObjectAccessExpression 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtClassLiteralExpression 22 | 23 | open class AClassLiteralExpression constructor(psiElement: PsiElement) : 24 | AExpressionImpl(psiElement) { 25 | 26 | constructor( 27 | ktClassLiteralExpression: KtClassLiteralExpression 28 | ) : this(ktClassLiteralExpression as PsiElement) 29 | 30 | constructor( 31 | psiClassObjectAccessExpression: PsiClassObjectAccessExpression 32 | ) : this(psiClassObjectAccessExpression as PsiElement) 33 | 34 | override val javaElement: PsiClassObjectAccessExpression? 35 | get() = castJavaElement() 36 | 37 | override val kotlinElement: KtClassLiteralExpression? 38 | get() = castKotlinElement() 39 | 40 | override val ifLanguage: Cases 41 | get() = castIfLanguage() 42 | 43 | val receiverExpression: AElement 44 | get() = (javaElement?.operand ?: kotlinElement?.receiverExpression)!!.toAElement() 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AWhenExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.psi.KtExpression 20 | import org.jetbrains.kotlin.psi.KtWhenExpression 21 | 22 | /** Represents a when expression in Kotlin. For example: `when (x) { 1 -> ... else -> ... }` */ 23 | interface AWhenExpression : AExpression { 24 | /** For example: `x` in `when (x)`. Can be null for `when { ... }` */ 25 | val subjectExpression: AExpression? 26 | 27 | /** The list of when entries (branches). For example: `1 -> ...`, `else -> ...` */ 28 | val entries: List 29 | } 30 | 31 | class AWhenExpressionImpl(override val psiElement: KtWhenExpression) : 32 | AWhenExpression, AElementImpl(psiElement) { 33 | 34 | override val javaElement: Nothing? 35 | get() = null 36 | 37 | override val kotlinElement: KtExpression 38 | get() = psiElement 39 | 40 | override val subjectExpression: AExpression? 41 | get() = psiElement.subjectExpression?.toAElement() as? AExpression 42 | 43 | override val entries: List 44 | get() = psiElement.entries.map { it.toAElement() } 45 | 46 | override val ifLanguage: Cases 47 | get() = castIfLanguage() 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ATypeParameterListOwner.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.facebook.aelements 17 | 18 | import org.jetbrains.kotlin.com.intellij.psi.PsiTypeParameterListOwner 19 | import org.jetbrains.kotlin.psi.KtTypeParameterListOwner 20 | 21 | /** 22 | * Represents a list of type parameter (not to be confused with the list of type arguments) in a 23 | * function, a class or an object definition i.e. `` in `fun f() {}` or `` in 24 | * `class C {}` 25 | */ 26 | interface ATypeParameterListOwner : AElement { 27 | 28 | override val javaElement: PsiTypeParameterListOwner? 29 | get() = castJavaElement() 30 | 31 | override val kotlinElement: KtTypeParameterListOwner? 32 | get() = castKotlinElement() 33 | 34 | override val ifLanguage: Cases 35 | get() = castIfLanguage() 36 | 37 | val typeParameters: List 38 | get() = 39 | javaElement?.typeParameters?.toList()?.map { it.toAElement() } 40 | ?: kotlinElement!!.typeParameters.map { it.toAElement() } 41 | 42 | val typeConstraints: List 43 | get() = kotlinElement?.typeConstraints?.map { it.toAElement() } ?: emptyList() 44 | } 45 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/asttools/PsiActionsTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.asttools 18 | 19 | import org.assertj.core.api.Assertions.assertThat 20 | import org.jetbrains.kotlin.psi.KtExpression 21 | import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType 22 | import org.junit.Test 23 | 24 | /** Tests [PsiActions] */ 25 | class PsiActionsTest { 26 | 27 | @Test 28 | fun `replace an integer constant with +1 using a list`() { 29 | val ktFile = 30 | KotlinParserUtil.parseAsFile( 31 | """ 32 | |fun f() { 33 | | doIt(1) 34 | | doIt(2) 35 | | val a = 1 36 | |} 37 | """ 38 | .trimMargin() 39 | ) 40 | 41 | val nodes = ktFile.collectDescendantsOfType { it.text?.toIntOrNull() != null } 42 | val replacements = nodes.map { (it.text.toInt() + 1).toString() } 43 | val newCode = replaceElements(ktFile.text, nodes, replacements) 44 | 45 | assertThat(newCode) 46 | .isEqualTo( 47 | """ 48 | |fun f() { 49 | | doIt(2) 50 | | doIt(3) 51 | | val a = 2 52 | |} 53 | """ 54 | .trimMargin() 55 | ) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AAssignmentExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiAssignmentExpression 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtBinaryExpression 22 | 23 | /** Represents an assignment expression, i.e. `a = 1` but not a declaration such as `val a = 1` */ 24 | open class AAssignmentExpression private constructor(psiElement: PsiElement) : 25 | AExpressionImpl(psiElement) { 26 | constructor(psiExpression: PsiAssignmentExpression) : this(psiExpression as PsiElement) 27 | 28 | constructor(ktExpression: KtBinaryExpression) : this(ktExpression as PsiElement) 29 | 30 | override val javaElement: PsiAssignmentExpression? 31 | get() = castJavaElement() 32 | 33 | override val kotlinElement: KtBinaryExpression? 34 | get() = castKotlinElement() 35 | 36 | override val ifLanguage: Cases 37 | get() = castIfLanguage() 38 | 39 | val left: AExpression 40 | get() = (javaElement?.lExpression ?: kotlinElement?.left)?.toAElement() as AExpression 41 | 42 | val right: AExpression 43 | get() = (javaElement?.rExpression ?: kotlinElement?.right)?.toAElement() as AExpression 44 | } 45 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AFileTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiJavaFile 21 | import org.jetbrains.kotlin.psi.KtFile 22 | import org.junit.Test 23 | 24 | /** Tests [AFile] */ 25 | class AFileTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = AElementTestingUtil() 30 | 31 | val (javaElement, kotlinElement) = 32 | aElementsTestUtil.loadTestAElements( 33 | javaCode = 34 | """ 35 | |package com.facebook.foo; 36 | | 37 | |public class TestClass {} 38 | """ 39 | .trimMargin(), 40 | kotlinCode = 41 | """ 42 | |package com.facebook.foo 43 | | 44 | |class TestClass 45 | """ 46 | .trimMargin(), 47 | ) 48 | 49 | for (aElement in listOf(javaElement, kotlinElement)) { 50 | aElementsTestUtil.assertSameString( 51 | aElement, 52 | { it.packageName }, 53 | { "com.facebook.foo" }, 54 | { "com.facebook.foo" }, 55 | ) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ALambdaExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiLambdaExpression 21 | import org.jetbrains.kotlin.psi.KtLambdaExpression 22 | 23 | class ALambdaExpression internal constructor(psiElement: PsiElement) : 24 | ADeclarationOrLambdaWithBody, AExpressionImpl(psiElement) { 25 | constructor(psiLambdaExpression: PsiLambdaExpression) : this(psiLambdaExpression as PsiElement) 26 | 27 | constructor(ktLambdaExpression: KtLambdaExpression) : this(ktLambdaExpression as PsiElement) 28 | 29 | override val javaElement: PsiLambdaExpression? 30 | get() = castJavaElement() 31 | 32 | override val kotlinElement: KtLambdaExpression? 33 | get() = castKotlinElement() 34 | 35 | override val ifLanguage: Cases 36 | get() = castIfLanguage() 37 | 38 | override val bodyExpression: AElement? 39 | get() = javaElement?.body?.toAElement() ?: kotlinElement?.bodyExpression?.toAElement() 40 | 41 | val valueParameters: List 42 | get() = 43 | javaElement?.parameterList?.parameters?.map { it.toAElement() } 44 | ?: kotlinElement?.valueParameters?.map { it.toAElement() } 45 | ?: emptyList() 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/matching/Resolver.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.matching 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | 21 | interface Resolver { 22 | /** Tries to return the fully qualified name of the type of this PsiElement */ 23 | fun resolveToFullyQualifiedType(psiElement: PsiElement): String? 24 | 25 | /** 26 | * Tries to return the fully qualified name of the type of this PsiElement, along with every type 27 | * it extends. e.g. class A extends BClass implements CInterface. class BClass extends 28 | * DAbstractClass. Calling this on an instance of A would return 29 | * [com.fqn.A, com.fqn.BClass, com.fqn.CInterface, com.fqn.DAbstractClass] 30 | */ 31 | fun resolveToFullyQualifiedTypeAndSupertypes(psiElement: PsiElement): List? 32 | 33 | companion object { 34 | val DEFAULT = 35 | object : Resolver { 36 | override fun resolveToFullyQualifiedType(psiElement: PsiElement): String? { 37 | error("Template was not built with a resolver to allow resolving of symbols") 38 | } 39 | 40 | override fun resolveToFullyQualifiedTypeAndSupertypes( 41 | psiElement: PsiElement 42 | ): List? { 43 | error("Template was not built with a resolver to allow resolving of symbols") 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ACallableDeclaration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethod 21 | import org.jetbrains.kotlin.psi.KtCallableDeclaration 22 | 23 | /** Represents a method in Java or a function in Kotlin */ 24 | open class ACallableDeclaration internal constructor(psiElement: PsiElement) : 25 | ACallableDeclarationOrLambda(psiElement) { 26 | constructor(psiMethod: PsiMethod) : this(psiMethod as PsiElement) 27 | 28 | constructor( 29 | ktCallableDeclaration: KtCallableDeclaration 30 | ) : this(ktCallableDeclaration as PsiElement) 31 | 32 | override val javaElement: PsiMethod? 33 | get() = castJavaElement() 34 | 35 | override val kotlinElement: KtCallableDeclaration? 36 | get() = castKotlinElement() 37 | 38 | override val ifLanguage: Cases 39 | get() = castIfLanguage() 40 | 41 | override val parameterList: AParameterList 42 | get() = 43 | javaElement?.parameterList?.toAElement() ?: kotlinElement?.valueParameterList!!.toAElement() 44 | 45 | override val valueParameters 46 | get() = 47 | javaElement?.parameterList?.parameters?.map { AParameter(it) } 48 | ?: kotlinElement!!.valueParameters.map { AParameter(it) } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ABinaryExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiBinaryExpression 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtBinaryExpression 22 | 23 | /** Represents a binary expression, i.e. `a + 1` or `b && true` */ 24 | open class ABinaryExpression private constructor(psiElement: PsiElement) : 25 | AExpressionImpl(psiElement) { 26 | constructor(psiExpression: PsiBinaryExpression) : this(psiExpression as PsiElement) 27 | 28 | constructor(ktExpression: KtBinaryExpression) : this(ktExpression as PsiElement) 29 | 30 | override val javaElement: PsiBinaryExpression? 31 | get() = castJavaElement() 32 | 33 | override val kotlinElement: KtBinaryExpression? 34 | get() = castKotlinElement() 35 | 36 | override val ifLanguage: Cases 37 | get() = castIfLanguage() 38 | 39 | val left: AExpression 40 | get() = (javaElement?.lOperand ?: kotlinElement?.left)?.toAElement() as AExpression 41 | 42 | val right: AExpressionOrStatement 43 | get() = (javaElement?.rOperand ?: kotlinElement?.right)?.toAElement() as AExpressionOrStatement 44 | 45 | val operator: String 46 | get() = (javaElement?.operationSign?.text ?: kotlinElement?.operationReference?.text)!! 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ADeclarationWithBody.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethod 21 | import org.jetbrains.kotlin.psi.KtDeclarationWithBody 22 | 23 | /** An element with a body expression, a function or a constructor */ 24 | interface ADeclarationWithBody : ADeclarationOrLambdaWithBody { 25 | 26 | override val javaElement: PsiMethod? 27 | get() = castJavaElement() 28 | 29 | override val kotlinElement: KtDeclarationWithBody? 30 | get() = castKotlinElement() 31 | 32 | override val ifLanguage: Cases 33 | get() = castIfLanguage() 34 | 35 | override val bodyExpression: AElement? 36 | get() = javaElement?.body?.toAElement() ?: kotlinElement?.bodyExpression?.toAElement() 37 | 38 | val bodyBlockExpression: ACodeBlock? 39 | get() = javaElement?.body?.toAElement() ?: kotlinElement?.bodyBlockExpression?.toAElement() 40 | } 41 | 42 | class ADeclarationWithBodyImpl internal constructor(psiElement: PsiElement) : 43 | ADeclarationWithBody, AElementImpl(psiElement) { 44 | constructor(psiMethod: PsiMethod) : this(psiMethod as PsiElement) 45 | 46 | constructor( 47 | ktDeclarationWithBody: KtDeclarationWithBody 48 | ) : this(ktDeclarationWithBody as PsiElement) 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AVariableDeclaration.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiVariable 21 | import org.jetbrains.kotlin.psi.KtCallableDeclaration 22 | 23 | /** 24 | * Represents a decalration of a field, variable, or parameter, examples: 25 | * ``` 26 | * // in Java 27 | * int x; 28 | * void f(int x) {} 29 | * 30 | * // in Kotlin 31 | * val x: Int 32 | * fun f(x: Int) {} 33 | * ``` 34 | */ 35 | open class AVariableDeclaration internal constructor(psiElement: PsiElement) : 36 | AAnnotated(psiElement), ANamedElement { 37 | constructor(psiVariable: PsiVariable) : this(psiVariable as PsiElement) 38 | 39 | constructor(ktProperty: KtCallableDeclaration) : this(ktProperty as PsiElement) 40 | 41 | override val javaElement: PsiVariable? 42 | get() = castJavaElement() 43 | 44 | override val kotlinElement: KtCallableDeclaration? 45 | get() = castKotlinElement() 46 | 47 | override val ifLanguage: Cases 48 | get() = castIfLanguage() 49 | 50 | /** The type definition of the variable, i.e. `int` in `int x` or `Int` in `x: Int` */ 51 | val typeReference: ATypeReference? 52 | get() = (javaElement?.typeElement?.toAElement() ?: kotlinElement?.typeReference?.toAElement()) 53 | } 54 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AForeachStatement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiForeachStatement 20 | 21 | /** 22 | * Represents a foreach loop in Java (enhanced for loop). For example: `for (String s : list) { ... 23 | * }` 24 | */ 25 | interface AForeachStatement : AExpressionOrStatement { 26 | /** For example: `String s` in `for (String s : list)` */ 27 | val iterationParameter: AElement? 28 | 29 | /** For example: `list` in `for (String s : list)` */ 30 | val iteratedValue: AExpression? 31 | 32 | /** The body of the foreach loop. */ 33 | val body: AExpressionOrStatement? 34 | } 35 | 36 | class AForeachStatementImpl(override val psiElement: PsiForeachStatement) : 37 | AForeachStatement, AElementImpl(psiElement) { 38 | 39 | override val javaElement: PsiForeachStatement 40 | get() = psiElement 41 | 42 | override val kotlinElement: Nothing? 43 | get() = null 44 | 45 | override val iterationParameter: AElement? 46 | get() = psiElement.iterationParameter.toAElement() 47 | 48 | override val iteratedValue: AExpression? 49 | get() = psiElement.iteratedValue?.toAElement() 50 | 51 | override val body: AExpressionOrStatement? 52 | get() = psiElement.body?.toAElement() 53 | 54 | override val ifLanguage: Cases 55 | get() = castIfLanguage() 56 | } 57 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/matching/PsiAstMatcherImplTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.matching 18 | 19 | import com.facebook.asttools.KotlinParserUtil 20 | import org.assertj.core.api.Assertions.assertThat 21 | import org.jetbrains.kotlin.psi.KtCallExpression 22 | import org.junit.Test 23 | 24 | /** Tests [PsiAstMatcherImpl] */ 25 | class PsiAstMatcherImplTest { 26 | 27 | @Test 28 | fun `test matcher reorders matching functions`() { 29 | val ktFile = 30 | KotlinParserUtil.parseAsFile( 31 | """ 32 | |package com.test 33 | | 34 | |fun doIt() { 35 | | doIt(5) 36 | | doNotDoIt(6) 37 | |} 38 | """ 39 | .trimMargin() 40 | ) 41 | var counter1 = 0 42 | var counter2 = 0 43 | val results = 44 | match() 45 | .apply { 46 | addChildMatcher { 47 | counter1++ 48 | Thread.sleep(1) 49 | "doIt" in it.text 50 | } 51 | addChildMatcher { 52 | counter2++ 53 | "doIt" in it.text 54 | } 55 | } 56 | .findAll(ktFile) 57 | 58 | assertThat(results.map { it.text }).containsExactly("doIt(5)") 59 | assertThat(counter1).isEqualTo(1) 60 | assertThat(counter2).isEqualTo(2) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AImportDirective.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiImportStatementBase 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiImportStaticStatement 22 | import org.jetbrains.kotlin.psi.KtImportDirective 23 | 24 | /** Represents an import, i.e. `import com.foo.Foo` or `import static com.foo.Foo.ba` */ 25 | class AImportDirective private constructor(psiElement: PsiElement) : AElementImpl(psiElement) { 26 | constructor( 27 | psiImportStatementBase: PsiImportStatementBase 28 | ) : this(psiImportStatementBase as PsiElement) 29 | 30 | constructor(ktImportDirective: KtImportDirective) : this(ktImportDirective as PsiElement) 31 | 32 | override val javaElement: PsiImportStatementBase? 33 | get() = castJavaElement() 34 | 35 | override val kotlinElement: KtImportDirective? 36 | get() = castKotlinElement() 37 | 38 | override val ifLanguage: Cases 39 | get() = castIfLanguage() 40 | 41 | val fullyQualifiedName: String? 42 | get() = (javaElement?.importReference?.qualifiedName ?: kotlinElement?.importPath?.pathStr) 43 | 44 | val isStatic: Boolean 45 | get() = javaElement is PsiImportStaticStatement 46 | 47 | val alias: String? 48 | get() = kotlinElement?.alias?.name 49 | } 50 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ATypeConstraintTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.asttools.KotlinParserUtil 20 | import org.assertj.core.api.Assertions.assertThat 21 | import org.junit.Test 22 | 23 | /** Tests [ATypeConstraint] */ 24 | class ATypeConstraintTest { 25 | @Test 26 | fun `basic functionality`() { 27 | val aFile = 28 | KotlinParserUtil.parseAsFile( 29 | """ 30 | |open class TestClass where ClassTypeParam : String { 31 | | fun foo(): FunctionParam where FunctionParam : String { return null } 32 | |} 33 | """ 34 | .trimMargin() 35 | ) 36 | .toAElement() 37 | 38 | val testClass = 39 | aFile.collectDescendantsOfType().firstOrNull { it.name == "TestClass" } 40 | assertThat(testClass?.typeConstraints).size().isEqualTo(1) 41 | assertThat(testClass?.typeConstraints?.map { it.text }) 42 | .containsExactly("ClassTypeParam : String") 43 | 44 | val fooFunction = 45 | testClass?.collectDescendantsOfType()?.firstOrNull { it.name == "foo" } 46 | assertThat(fooFunction?.typeConstraints).size().isEqualTo(1) 47 | assertThat(fooFunction?.typeConstraints?.map { it.text }) 48 | .containsExactly("FunctionParam : String") 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AForExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.psi.KtExpression 20 | import org.jetbrains.kotlin.psi.KtForExpression 21 | 22 | /** 23 | * Represents a for loop in Kotlin. For example: `for (item in collection) { ... }` or `for (i in 24 | * 0..9) { ... }` 25 | */ 26 | interface AForExpression : AExpression { 27 | /** For example: `item` in `for (item in collection)` */ 28 | val loopParameter: AElement? 29 | 30 | /** For example: `collection` in `for (item in collection)` or `0..9` in `for (i in 0..9)` */ 31 | val loopRange: AExpression? 32 | 33 | /** The body of the for loop. */ 34 | val body: AExpressionOrStatement? 35 | } 36 | 37 | class AForExpressionImpl(override val psiElement: KtForExpression) : 38 | AForExpression, AElementImpl(psiElement) { 39 | 40 | override val javaElement: Nothing? 41 | get() = null 42 | 43 | override val kotlinElement: KtExpression 44 | get() = psiElement 45 | 46 | override val loopParameter: AElement? 47 | get() = psiElement.loopParameter?.toAElement() 48 | 49 | override val loopRange: AExpression? 50 | get() = psiElement.loopRange?.toAElement() as? AExpression 51 | 52 | override val body: AExpressionOrStatement? 53 | get() = psiElement.body?.toAElement() 54 | 55 | override val ifLanguage: Cases 56 | get() = castIfLanguage() 57 | } 58 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AVariableDeclarationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiVariable 21 | import org.jetbrains.kotlin.psi.KtCallableDeclaration 22 | import org.junit.Test 23 | 24 | /** Tests [AVariableDeclaration] */ 25 | class AVariableDeclarationTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaElement, kotlinElement) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | int a = 5; 38 | |} 39 | """ 40 | .trimMargin(), 41 | kotlinCode = 42 | """ 43 | |class TestClass { 44 | | val a = 5 45 | |} 46 | """ 47 | .trimMargin(), 48 | ) 49 | 50 | for (aElement in listOf(javaElement, kotlinElement)) { 51 | aElementsTestUtil.assertSamePsiElement( 52 | aElement, 53 | { it.typeReference }, 54 | { it.typeElement }, 55 | { it.typeReference }, 56 | ) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/APackageDirectiveTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiPackageStatement 21 | import org.jetbrains.kotlin.psi.KtPackageDirective 22 | import org.junit.Test 23 | 24 | /** Tests [APackageDirective] */ 25 | class APackageDirectiveTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaElement, kotlinElement) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |package com.facebook.foo; 37 | | 38 | |public class TestClass {} 39 | """ 40 | .trimMargin(), 41 | kotlinCode = 42 | """ 43 | |package com.facebook.foo 44 | | 45 | |class TestClass 46 | """ 47 | .trimMargin(), 48 | ) 49 | 50 | for (aElement in listOf(javaElement, kotlinElement)) { 51 | aElementsTestUtil.assertSameString( 52 | aElement, 53 | { it.packageName }, 54 | { "com.facebook.foo" }, 55 | { "com.facebook.foo" }, 56 | ) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AExpressionOrStatement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiExpression 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiStatement 22 | import org.jetbrains.kotlin.psi.KtExpression 23 | 24 | /** 25 | * Represents any expression or statement. Note that Java has the concept of statements as well, but 26 | * not Kotlin. For Kotlin this is equivalent to [AExpression] 27 | */ 28 | interface AExpressionOrStatement : AElement { 29 | override val javaElement: PsiElement? 30 | 31 | override val kotlinElement: KtExpression? 32 | 33 | override val ifLanguage: Cases 34 | } 35 | 36 | open class AExpressionOrStatementImpl internal constructor(psiElement: PsiElement) : 37 | AExpressionOrStatement, AElementImpl(psiElement) { 38 | constructor(psiExpression: PsiExpression) : this(psiExpression as PsiElement) 39 | 40 | constructor(psiStatment: PsiStatement) : this(psiStatment as PsiElement) 41 | 42 | constructor(ktExpression: KtExpression) : this(ktExpression as PsiElement) 43 | 44 | override val javaElement: PsiElement? 45 | get() = castJavaElement() 46 | 47 | override val kotlinElement: KtExpression? 48 | get() = castKotlinElement() 49 | 50 | override val ifLanguage: Cases 51 | get() = castIfLanguage() 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AReturnExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiReturnStatement 21 | import org.jetbrains.kotlin.psi.KtReturnExpression 22 | 23 | /** 24 | * Represents a return statement/expression, which is an expression in Kotlin, but a statement in 25 | * Java 26 | * 27 | * Example: 28 | * ``` 29 | * return 42 30 | * ``` 31 | */ 32 | open class AReturnExpression private constructor(psiElement: PsiElement) : 33 | AExpressionOrStatementImpl(psiElement) { 34 | constructor(psiReturnStatement: PsiReturnStatement) : this(psiReturnStatement as PsiElement) 35 | 36 | constructor(ktReturnExpression: KtReturnExpression) : this(ktReturnExpression as PsiElement) 37 | 38 | override val javaElement: PsiReturnStatement? 39 | get() = castJavaElement() 40 | 41 | override val kotlinElement: KtReturnExpression? 42 | get() = castKotlinElement() 43 | 44 | override val ifLanguage: Cases 45 | get() = castIfLanguage() 46 | 47 | /** 48 | * The value being returned, i.e. `42` in `return 42`, or null for a bare `return` with no value 49 | */ 50 | val returnValue: AExpressionOrStatement? 51 | get() = 52 | (javaElement?.returnValue ?: kotlinElement?.returnedExpression)?.toAElement() 53 | as AExpressionOrStatement? 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AProperty.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiVariable 21 | import org.jetbrains.kotlin.psi.KtProperty 22 | 23 | /** Represents a decalration of a field or variable, either member or local */ 24 | open class AProperty internal constructor(psiElement: PsiElement) : 25 | AVariableDeclaration(psiElement), AModifierListOwner { 26 | constructor(psiVariable: PsiVariable) : this(psiVariable as PsiElement) 27 | 28 | constructor(ktProperty: KtProperty) : this(ktProperty as PsiElement) 29 | 30 | override val javaElement: PsiVariable? 31 | get() = castJavaElement() 32 | 33 | override val kotlinElement: KtProperty? 34 | get() = castKotlinElement() 35 | 36 | override val ifLanguage: Cases 37 | get() = castIfLanguage() 38 | 39 | val initializer: AExpressionOrStatement? 40 | get() = (javaElement?.initializer?.toAElement() ?: kotlinElement?.initializer?.toAElement()) 41 | 42 | /** Only available in Kotlin, i.e. `foo` in `val bar by foo` */ 43 | val delegateExpression: AExpressionOrStatement? 44 | get() = kotlinElement?.delegateExpression?.toAElement() 45 | 46 | val initializerOrDelegateExpression: AExpressionOrStatement? 47 | get() = initializer ?: delegateExpression 48 | 49 | val isLocal: Boolean 50 | get() = this is ALocalProperty 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ACodeBlock.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiCodeBlock 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtBlockExpression 22 | 23 | /** 24 | * Represents a code block that is not a statement or expression, this can include a Java class body 25 | * for example 26 | */ 27 | interface ACodeBlock : AElement { 28 | 29 | override val javaElement: PsiCodeBlock? 30 | get() = castJavaElement() 31 | 32 | override val kotlinElement: KtBlockExpression? 33 | get() = castKotlinElement() 34 | 35 | override val ifLanguage: Cases 36 | get() = castIfLanguage() 37 | 38 | val statements: List 39 | get() = 40 | (javaElement?.statements?.map { it.toAElement() } 41 | ?: kotlinElement!!.statements.map { it.toAElement() }) 42 | 43 | val lBrace: PsiElement? 44 | get() = javaElement?.lBrace ?: kotlinElement!!.lBrace 45 | 46 | val rBrace: PsiElement? 47 | get() = javaElement?.rBrace ?: kotlinElement!!.rBrace 48 | } 49 | 50 | class ACodeBlockImpl private constructor(psiElement: PsiElement) : 51 | AElementImpl(psiElement), ACodeBlock { 52 | constructor(psiCodeBlock: PsiCodeBlock) : this(psiCodeBlock as PsiElement) 53 | 54 | constructor(ktBlockExpression: KtBlockExpression) : this(ktBlockExpression as PsiElement) 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AIfExpressionOrStatement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiIfStatement 21 | import org.jetbrains.kotlin.psi.KtIfExpression 22 | 23 | /** Represents an if construct, which is an expression in Kotlin, but a statement in Java */ 24 | open class AIfExpressionOrStatement private constructor(psiElement: PsiElement) : 25 | AExpressionOrStatementImpl(psiElement) { 26 | constructor(psiIfStatement: PsiIfStatement) : this(psiIfStatement as PsiElement) 27 | 28 | constructor(ktIfExpression: KtIfExpression) : this(ktIfExpression as PsiElement) 29 | 30 | override val javaElement: PsiIfStatement? 31 | get() = castJavaElement() 32 | 33 | override val kotlinElement: KtIfExpression? 34 | get() = castKotlinElement() 35 | 36 | override val ifLanguage: Cases 37 | get() = castIfLanguage() 38 | 39 | val condition: AExpressionOrStatement 40 | get() = 41 | (javaElement?.condition ?: kotlinElement?.condition)?.toAElement() as AExpressionOrStatement 42 | 43 | val then: AExpressionOrStatement 44 | get() = (javaElement?.thenBranch ?: kotlinElement?.then)?.toAElement() as AExpressionOrStatement 45 | 46 | val `else`: AExpressionOrStatement? 47 | get() = 48 | (javaElement?.elseBranch ?: kotlinElement?.`else`)?.toAElement() as AExpressionOrStatement? 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AThrowExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiThrowStatement 21 | import org.jetbrains.kotlin.psi.KtThrowExpression 22 | 23 | /** 24 | * Represents a throw statement/expression, which is an expression in Kotlin, but a statement in 25 | * Java 26 | * 27 | * Example: 28 | * ``` 29 | * throw IllegalArgumentException("Invalid value") 30 | * ``` 31 | */ 32 | open class AThrowExpression private constructor(psiElement: PsiElement) : 33 | AExpressionOrStatementImpl(psiElement) { 34 | constructor(psiThrowStatement: PsiThrowStatement) : this(psiThrowStatement as PsiElement) 35 | 36 | constructor(ktThrowExpression: KtThrowExpression) : this(ktThrowExpression as PsiElement) 37 | 38 | override val javaElement: PsiThrowStatement? 39 | get() = castJavaElement() 40 | 41 | override val kotlinElement: KtThrowExpression? 42 | get() = castKotlinElement() 43 | 44 | override val ifLanguage: Cases 45 | get() = castIfLanguage() 46 | 47 | /** 48 | * The exception being thrown, i.e. `IllegalArgumentException("Invalid value")` in `throw 49 | * IllegalArgumentException("Invalid value")` 50 | */ 51 | val exception: AExpressionOrStatement? 52 | get() = 53 | (javaElement?.exception ?: kotlinElement?.thrownExpression)?.toAElement() 54 | as AExpressionOrStatement? 55 | } 56 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ABreakExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiBreakStatement 21 | import org.jetbrains.kotlin.psi.KtBreakExpression 22 | import org.junit.Test 23 | 24 | /** Tests [ABreakExpression] */ 25 | class ABreakExpressionTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaABreakExpression, kotlinABreakExpression) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | public void doIt() { 38 | | while (true) { 39 | | break; 40 | | } 41 | | } 42 | |} 43 | """ 44 | .trimMargin(), 45 | kotlinCode = 46 | """ 47 | |class TestClass { 48 | | fun doIt() { 49 | | while (true) { 50 | | break 51 | | } 52 | | } 53 | |} 54 | """ 55 | .trimMargin(), 56 | ) 57 | 58 | // Both break statements exist 59 | assert(javaABreakExpression != null) 60 | assert(kotlinABreakExpression != null) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ATypeArgumentList.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiReferenceParameterList 21 | import org.jetbrains.kotlin.psi.KtTypeArgumentList 22 | 23 | /** 24 | * Represents a list of type arguments (not to be confused with value arguements) in a call to a 25 | * function, i.e. `` in `f(a, b)` 26 | * 27 | * Why does this exist and it's not just a list of the types? This is because this list is a node by 28 | * itself, for example getting the braces marking it is both useful and makes sense to represent in 29 | * an AST. 30 | */ 31 | open class ATypeArgumentList internal constructor(psiElement: PsiElement) : 32 | AElementImpl(psiElement) { 33 | constructor(psiExpressionList: PsiReferenceParameterList) : this(psiExpressionList as PsiElement) 34 | 35 | constructor(ktAnnotated: KtTypeArgumentList) : this(ktAnnotated as PsiElement) 36 | 37 | override val javaElement: PsiReferenceParameterList? 38 | get() = castJavaElement() 39 | 40 | override val kotlinElement: KtTypeArgumentList? 41 | get() = castKotlinElement() 42 | 43 | override val ifLanguage: Cases 44 | get() = castIfLanguage() 45 | 46 | val typeArguments: List 47 | get() = 48 | javaElement?.typeParameterElements?.toList()?.map { it.toAElement() } 49 | ?: kotlinElement!!.arguments.map { it.typeReference!!.toAElement() } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AConstructorTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethod 21 | import org.jetbrains.kotlin.psi.KtConstructor 22 | import org.junit.Test 23 | 24 | /** Tests [AConstructor] */ 25 | class AConstructorTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = AElementTestingUtil>() 30 | 31 | val (javaElement, kotlinElement) = 32 | aElementsTestUtil.loadTestAElements( 33 | javaCode = 34 | """ 35 | |public class TestClass { 36 | | 37 | | private int sum; 38 | | 39 | | public TestClass(int a, int b) { 40 | | sum = a + b; 41 | | } 42 | |} 43 | """ 44 | .trimMargin(), 45 | kotlinCode = 46 | """ 47 | |class TestClass(a: Int, b: Int) { 48 | | private val sum = a + b 49 | |} 50 | """ 51 | .trimMargin(), 52 | ) 53 | 54 | for (aElement in listOf(javaElement, kotlinElement)) { 55 | aElementsTestUtil.assertSamePsiElementList( 56 | aElement, 57 | { it.valueParameters }, 58 | { it.parameterList.parameters.toList() }, 59 | { it.valueParameters }, 60 | ) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AReturnExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiReturnStatement 21 | import org.jetbrains.kotlin.psi.KtReturnExpression 22 | import org.junit.Test 23 | 24 | /** Tests [AReturnExpression] */ 25 | class AReturnExpressionTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaAReturnExpression, kotlinAReturnExpression) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | public int doIt() { 38 | | return 42; 39 | | } 40 | |} 41 | """ 42 | .trimMargin(), 43 | kotlinCode = 44 | """ 45 | |class TestClass { 46 | | fun doIt(): Int { 47 | | return 42 48 | | } 49 | |} 50 | """ 51 | .trimMargin(), 52 | ) 53 | 54 | for (aElement in listOf(javaAReturnExpression, kotlinAReturnExpression)) { 55 | aElementsTestUtil.assertSamePsiElement( 56 | aElement, 57 | { it.returnValue }, 58 | { it.returnValue }, 59 | { it.returnedExpression }, 60 | ) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ACallExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethodCallExpression 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiReferenceExpression 22 | import org.jetbrains.kotlin.psi.KtCallExpression 23 | import org.jetbrains.kotlin.psi.KtExpression 24 | 25 | /** 26 | * Represents a call expression, i.e. `invoke(a)` 27 | * 28 | * See [AQualifiedCallExpression] for explanations of the mess involved in bridgeing this for Java 29 | * and Kotlin 30 | */ 31 | interface ACallExpression : AMethodOrNewCallExpression { 32 | 33 | override val javaElement: PsiMethodCallExpression? 34 | get() = castJavaElement() 35 | 36 | override val kotlinElement: KtExpression? 37 | get() = castKotlinElement() 38 | 39 | override val ifLanguage: Cases 40 | get() = castIfLanguage() 41 | 42 | /** The short name of the method being called, i.e. `foo` in `a.b.c.foo(d)` */ 43 | val unqualifiedCalleeName: String? 44 | get() = 45 | javaElement?.methodExpression?.referenceName 46 | ?: callExpressionKotlinElement?.calleeExpression?.text 47 | } 48 | 49 | class ACallExpressionImpl 50 | internal constructor( 51 | psiElement: PsiElement, 52 | ) : AMethodOrNewCallExpressionImpl(psiElement), ACallExpression { 53 | constructor(psiExpression: PsiReferenceExpression) : this(psiExpression as PsiElement) 54 | 55 | constructor(ktExpression: KtCallExpression) : this(ktExpression as PsiElement) 56 | } 57 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AContinueExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiContinueStatement 21 | import org.jetbrains.kotlin.psi.KtContinueExpression 22 | import org.junit.Test 23 | 24 | /** Tests [AContinueExpression] */ 25 | class AContinueExpressionTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaAContinueExpression, kotlinAContinueExpression) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | public void doIt() { 38 | | for (int i = 0; i < 10; i++) { 39 | | continue; 40 | | } 41 | | } 42 | |} 43 | """ 44 | .trimMargin(), 45 | kotlinCode = 46 | """ 47 | |class TestClass { 48 | | fun doIt() { 49 | | for (i in 0..9) { 50 | | continue 51 | | } 52 | | } 53 | |} 54 | """ 55 | .trimMargin(), 56 | ) 57 | 58 | // Both continue statements exist 59 | assert(javaAContinueExpression != null) 60 | assert(kotlinAContinueExpression != null) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AThrowExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiThrowStatement 21 | import org.jetbrains.kotlin.psi.KtThrowExpression 22 | import org.junit.Test 23 | 24 | /** Tests [AThrowExpression] */ 25 | class AThrowExpressionTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaAThrowExpression, kotlinAThrowExpression) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | public void doIt() { 38 | | throw new RuntimeException("error"); 39 | | } 40 | |} 41 | """ 42 | .trimMargin(), 43 | kotlinCode = 44 | """ 45 | |class TestClass { 46 | | fun doIt() { 47 | | throw RuntimeException("error") 48 | | } 49 | |} 50 | """ 51 | .trimMargin(), 52 | ) 53 | 54 | for (aElement in listOf(javaAThrowExpression, kotlinAThrowExpression)) { 55 | aElementsTestUtil.assertSamePsiElement( 56 | aElement, 57 | { it.exception }, 58 | { it.exception }, 59 | { it.thrownExpression }, 60 | ) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ABreakExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiBreakStatement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtBreakExpression 22 | 23 | /** 24 | * Represents a break statement/expression, which is an expression in Kotlin, but a statement in 25 | * Java 26 | * 27 | * Example: 28 | * ``` 29 | * while (condition) { 30 | * if (shouldExit) break 31 | * process() 32 | * } 33 | * ``` 34 | * 35 | * Example with label: 36 | * ``` 37 | * outer@ while (i < 10) { 38 | * while (j < 10) { 39 | * if (shouldExitOuter) break@outer 40 | * } 41 | * } 42 | * ``` 43 | */ 44 | open class ABreakExpression private constructor(psiElement: PsiElement) : 45 | AExpressionOrStatementImpl(psiElement) { 46 | constructor(psiBreakStatement: PsiBreakStatement) : this(psiBreakStatement as PsiElement) 47 | 48 | constructor(ktBreakExpression: KtBreakExpression) : this(ktBreakExpression as PsiElement) 49 | 50 | override val javaElement: PsiBreakStatement? 51 | get() = castJavaElement() 52 | 53 | override val kotlinElement: KtBreakExpression? 54 | get() = castKotlinElement() 55 | 56 | override val ifLanguage: Cases 57 | get() = castIfLanguage() 58 | 59 | /** 60 | * The label identifier for labeled break statements, i.e. `outer` in `break@outer`, or null for 61 | * unlabeled breaks 62 | */ 63 | val labelName: String? 64 | get() = javaElement?.labelIdentifier?.text ?: kotlinElement?.getLabelName() 65 | } 66 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AForStatement.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiForStatement 20 | 21 | /** 22 | * Represents a traditional for loop in Java. For example: `for (int i = 0; i < 10; i++) { ... }` 23 | */ 24 | interface AForStatement : AExpressionOrStatement { 25 | /** For example: `int i = 0` in `for (int i = 0; i < 10; i++)` */ 26 | val initialization: AExpressionOrStatement? 27 | 28 | /** For example: `i < 10` in `for (int i = 0; i < 10; i++)` */ 29 | val condition: AExpression? 30 | 31 | /** For example: `i++` in `for (int i = 0; i < 10; i++)` */ 32 | val update: AExpressionOrStatement? 33 | 34 | /** The body of the for loop. */ 35 | val body: AExpressionOrStatement? 36 | } 37 | 38 | class AForStatementImpl(override val psiElement: PsiForStatement) : 39 | AForStatement, AElementImpl(psiElement) { 40 | 41 | override val javaElement: PsiForStatement 42 | get() = psiElement 43 | 44 | override val kotlinElement: Nothing? 45 | get() = null 46 | 47 | override val initialization: AExpressionOrStatement? 48 | get() = psiElement.initialization?.takeUnless { it.text == ";" }?.toAElement() 49 | 50 | override val condition: AExpression? 51 | get() = psiElement.condition?.toAElement() 52 | 53 | override val update: AExpressionOrStatement? 54 | get() = psiElement.update?.takeUnless { it.text == ";" }?.toAElement() 55 | 56 | override val body: AExpressionOrStatement? 57 | get() = psiElement.body?.toAElement() 58 | 59 | override val ifLanguage: Cases 60 | get() = castIfLanguage() 61 | } 62 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ATypeArgumentListTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiReferenceParameterList 21 | import org.jetbrains.kotlin.psi.KtTypeArgumentList 22 | import org.junit.Test 23 | 24 | /** Tests [ATypeArgumentList] */ 25 | class ATypeArgumentListTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaElement, kotlinElement) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | public void doIt(int a, int b) { 38 | | return Foo.f(a, b); 39 | | } 40 | |} 41 | """ 42 | .trimMargin(), 43 | kotlinCode = 44 | """ 45 | |class TestClass { 46 | | fun doIt(a: Int, b: Int) { 47 | | return f(a, b) 48 | | } 49 | |} 50 | """ 51 | .trimMargin(), 52 | ) 53 | 54 | for (aElement in listOf(javaElement, kotlinElement)) { 55 | aElementsTestUtil.assertSamePsiElementList( 56 | aElement, 57 | { it.typeArguments }, 58 | { it.typeParameterElements.toList() }, 59 | { it.arguments.map { it.typeReference } }, 60 | ) 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ANamedElementTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiNameIdentifierOwner 21 | import org.jetbrains.kotlin.psi.KtNamedDeclaration 22 | import org.junit.Test 23 | 24 | /** Tests [ANamedElement] */ 25 | class ANamedElementTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaElement, kotlinElement) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | public void doIt(int a, int b) { 38 | | return a + b; 39 | | } 40 | |} 41 | """ 42 | .trimMargin(), 43 | kotlinCode = 44 | """ 45 | |class TestClass { 46 | | fun doIt(a: Int, b: Int) { 47 | | return a + b 48 | | } 49 | |} 50 | """ 51 | .trimMargin(), 52 | ) 53 | 54 | for (aElement in listOf(javaElement, kotlinElement)) { 55 | aElementsTestUtil.assertSameString(aElement, { it.name }, { it.name }, { it.name }) 56 | aElementsTestUtil.assertSameString( 57 | aElement, 58 | { it.nameIdentifier?.text }, 59 | { it.nameIdentifier?.text }, 60 | { it.nameIdentifier?.text }, 61 | ) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AContinueExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiContinueStatement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtContinueExpression 22 | 23 | /** 24 | * Represents a continue statement/expression, which is an expression in Kotlin, but a statement in 25 | * Java 26 | * 27 | * Example: 28 | * ``` 29 | * while (condition) { 30 | * if (shouldSkip) continue 31 | * process() 32 | * } 33 | * ``` 34 | * 35 | * Example with label: 36 | * ``` 37 | * outer@ while (i < 10) { 38 | * while (j < 10) { 39 | * if (shouldSkipOuter) continue@outer 40 | * } 41 | * } 42 | * ``` 43 | */ 44 | open class AContinueExpression private constructor(psiElement: PsiElement) : 45 | AExpressionOrStatementImpl(psiElement) { 46 | constructor(psiContinueStatement: PsiContinueStatement) : this(psiContinueStatement as PsiElement) 47 | 48 | constructor(ktContinueExpression: KtContinueExpression) : this(ktContinueExpression as PsiElement) 49 | 50 | override val javaElement: PsiContinueStatement? 51 | get() = castJavaElement() 52 | 53 | override val kotlinElement: KtContinueExpression? 54 | get() = castKotlinElement() 55 | 56 | override val ifLanguage: Cases 57 | get() = castIfLanguage() 58 | 59 | /** 60 | * The label identifier for labeled continue statements, i.e. `outer` in `continue@outer`, or null 61 | * for unlabeled continues 62 | */ 63 | val labelName: String? 64 | get() = javaElement?.labelIdentifier?.text ?: kotlinElement?.getLabelName() 65 | } 66 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AParameterList.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiParameterList 21 | import org.jetbrains.kotlin.psi.KtParameterList 22 | 23 | /** 24 | * Represents a list of parameters in a method declaration, i.e. `int a, int b` in `void f(int a, 25 | * int b)` 26 | * 27 | * Why does this exist and it's not just a list of the parameters? This since this list is a node by 28 | * itself, for example getting the parenthesis marking it is both useful and makes sense to 29 | * represent in an AST. 30 | */ 31 | open class AParameterList internal constructor(psiElement: PsiElement) : AElementImpl(psiElement) { 32 | constructor(psiExpressionList: PsiParameterList) : this(psiExpressionList as PsiElement) 33 | 34 | constructor(ktAnnotated: KtParameterList) : this(ktAnnotated as PsiElement) 35 | 36 | override val javaElement: PsiParameterList? 37 | get() = castJavaElement() 38 | 39 | override val kotlinElement: KtParameterList? 40 | get() = castKotlinElement() 41 | 42 | override val ifLanguage: Cases 43 | get() = castIfLanguage() 44 | 45 | val parameters: List 46 | get() = 47 | javaElement?.parameters?.toList()?.map { it.toAElement() } 48 | ?: kotlinElement!!.parameters.map { it.toAElement() } 49 | 50 | val leftParenthesis: AElement 51 | get() = ifLanguage(isJava = { it.firstChild }, isKotlin = { it.leftParenthesis })!!.toAElement() 52 | 53 | val rightParenthesis: AElement 54 | get() = ifLanguage(isJava = { it.lastChild }, isKotlin = { it.rightParenthesis })!!.toAElement() 55 | } 56 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AAnnotation.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiAnnotation 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtAnnotationEntry 22 | 23 | /** Represents an annotation entry, i.e. `@Foo` or `@Foo(1)` */ 24 | open class AAnnotation internal constructor(psiElement: PsiElement) : AElementImpl(psiElement) { 25 | constructor(psiMethod: PsiAnnotation) : this(psiMethod as PsiElement) 26 | 27 | constructor(ktAnnotationEntry: KtAnnotationEntry) : this(ktAnnotationEntry as PsiElement) 28 | 29 | override val javaElement: PsiAnnotation? 30 | get() = castJavaElement() 31 | 32 | override val kotlinElement: KtAnnotationEntry? 33 | get() = castKotlinElement() 34 | 35 | override val ifLanguage: Cases 36 | get() = castIfLanguage() 37 | 38 | val shortName = 39 | javaElement?.nameReferenceElement?.referenceName ?: kotlinElement?.shortName?.identifier ?: "" 40 | 41 | val valueArguments: List 42 | get() = 43 | (javaElement?.parameterList?.attributes?.mapNotNull { 44 | it.value?.toAElement() as? AExpressionOrStatement 45 | } 46 | ?: kotlinElement?.valueArgumentList?.arguments?.map { 47 | it.getArgumentExpression()!!.toAElement() as AExpressionOrStatement 48 | }) ?: emptyList() 49 | 50 | val valueArgumentNames: List 51 | get() = 52 | (javaElement?.parameterList?.attributes?.map { it.name } 53 | ?: kotlinElement?.valueArgumentList?.arguments?.map { 54 | it.getArgumentName()?.asName?.identifier 55 | }) ?: emptyList() 56 | } 57 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AAssignmentExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiAssignmentExpression 21 | import org.jetbrains.kotlin.psi.KtBinaryExpression 22 | import org.junit.Test 23 | 24 | /** Tests [AAssignmentExpression] */ 25 | class AAssignmentExpressionTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaElement, kotlinElement) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | 38 | | int a = 0; 39 | | 40 | | public void doIt() { 41 | | a = 5; 42 | | } 43 | |} 44 | """ 45 | .trimMargin(), 46 | kotlinCode = 47 | """ 48 | |class TestClass { 49 | | 50 | | var a: Int = 0 51 | | 52 | | fun doIt() { 53 | | a = 5 54 | | } 55 | |} 56 | """ 57 | .trimMargin(), 58 | ) 59 | 60 | for (aElement in listOf(javaElement, kotlinElement)) { 61 | aElementsTestUtil.assertSamePsiElement(aElement, { it.left }, { it.lExpression }, { it.left }) 62 | aElementsTestUtil.assertSamePsiElement( 63 | aElement, 64 | { it.right }, 65 | { it.rExpression }, 66 | { it.right }, 67 | ) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AValueArgumentList.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiExpressionList 21 | import org.jetbrains.kotlin.psi.KtValueArgumentList 22 | 23 | /** 24 | * Represents a list of value arguments (not to be confused with type arguements) in a call to a 25 | * function, i.e. `(a, b)` in `f(a, b)` 26 | * 27 | * Why does this exist and it's not just a list of the parameters? This since this list is a node by 28 | * itself, for example getting the parenthesis marking it is both useful and makes sense to 29 | * represent in an AST. 30 | */ 31 | open class AValueArgumentList internal constructor(psiElement: PsiElement) : 32 | AElementImpl(psiElement) { 33 | constructor(psiExpressionList: PsiExpressionList) : this(psiExpressionList as PsiElement) 34 | 35 | constructor(ktAnnotated: KtValueArgumentList) : this(ktAnnotated as PsiElement) 36 | 37 | override val javaElement: PsiExpressionList? 38 | get() = castJavaElement() 39 | 40 | override val kotlinElement: KtValueArgumentList? 41 | get() = castKotlinElement() 42 | 43 | override val ifLanguage: Cases 44 | get() = castIfLanguage() 45 | 46 | val valueArguments: List 47 | get() = 48 | javaElement?.expressions?.toList()?.map { it.toAElement() } 49 | ?: kotlinElement!!.arguments.map { it.getArgumentExpression()!!.toAElement() } 50 | 51 | val leftParenthesis: AElement 52 | get() = ifLanguage(isJava = { it.firstChild }, isKotlin = { it.leftParenthesis })!!.toAElement() 53 | 54 | val rightParenthesis: AElement 55 | get() = ifLanguage(isJava = { it.lastChild }, isKotlin = { it.rightParenthesis })!!.toAElement() 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/asttools/ProjectHelper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.asttools 18 | 19 | import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys 20 | import org.jetbrains.kotlin.cli.common.messages.MessageRenderer 21 | import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector 22 | import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles 23 | import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment 24 | import org.jetbrains.kotlin.com.intellij.openapi.project.Project 25 | import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer 26 | import org.jetbrains.kotlin.config.CompilerConfiguration 27 | 28 | /** 29 | * Helper that creates or uses a provided Project for psi operations. For example, finding the 30 | * PsiManager. 31 | */ 32 | object ProjectHelper { 33 | 34 | private var overriddenProject: Project? = null 35 | private val kotlinCoreEnvironment: KotlinCoreEnvironment by lazy { createCoreEnvironment() } 36 | 37 | fun getProject(): Project { 38 | return overriddenProject ?: kotlinCoreEnvironment.project 39 | } 40 | 41 | fun setProjectOverride(project: Project) { 42 | this.overriddenProject = project 43 | } 44 | 45 | private fun createCoreEnvironment(): KotlinCoreEnvironment { 46 | val disposable = Disposer.newDisposable() 47 | return KotlinCoreEnvironment.createForProduction( 48 | disposable, 49 | getConfiguration(), 50 | EnvironmentConfigFiles.JVM_CONFIG_FILES, 51 | ) 52 | } 53 | 54 | private fun getConfiguration(): CompilerConfiguration { 55 | val configuration = CompilerConfiguration() 56 | configuration.put( 57 | CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, 58 | PrintingMessageCollector(System.err, MessageRenderer.PLAIN_RELATIVE_PATHS, false), 59 | ) 60 | return configuration 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ATypeParameterListOwnerTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.asttools.JavaPsiParserUtil 20 | import com.facebook.asttools.KotlinParserUtil 21 | import org.assertj.core.api.Assertions.assertThat 22 | import org.junit.Test 23 | 24 | /** Tests [ATypeParameterListOwner] */ 25 | class ATypeParameterListOwnerTest { 26 | @Test 27 | fun `basic functionality`() { 28 | val (javaElement, kotlinElement) = 29 | JavaPsiParserUtil.parseAsFile( 30 | """ 31 | |public class TestClass { 32 | | public FunctionParam foo() { return null; } 33 | |} 34 | """ 35 | .trimMargin() 36 | ) 37 | .toAElement() to 38 | KotlinParserUtil.parseAsFile( 39 | """ 40 | |open class TestClass { 41 | | fun foo(): FunctionParam { return null } 42 | |} 43 | """ 44 | .trimMargin() 45 | ) 46 | .toAElement() 47 | 48 | for (aFile in listOf(javaElement, kotlinElement)) { 49 | val testClass = 50 | aFile.collectDescendantsOfType().firstOrNull { it.name == "TestClass" } 51 | assertThat(testClass?.typeParameters).size().isEqualTo(1) 52 | assertThat(testClass?.typeParameters?.map { it.text }).containsExactly("ClassTypeParam") 53 | 54 | val fooFunction = 55 | testClass?.collectDescendantsOfType()?.firstOrNull { it.name == "foo" } 56 | assertThat(fooFunction?.typeParameters).size().isEqualTo(1) 57 | assertThat(fooFunction?.typeParameters?.map { it.text }).containsExactly("FunctionParam") 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ANamedFunction.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethod 21 | import org.jetbrains.kotlin.lexer.KtTokens 22 | import org.jetbrains.kotlin.psi.KtNamedFunction 23 | import org.jetbrains.kotlin.psi.KtObjectDeclaration 24 | import org.jetbrains.kotlin.psi.psiUtil.getParentOfType 25 | 26 | /** Represents a method in Java or a function in Kotlin */ 27 | open class ANamedFunction internal constructor(psiElement: PsiElement) : 28 | ACallableDeclaration(psiElement), 29 | ANamedElement, 30 | ADeclarationWithBody, 31 | AModifierListOwner, 32 | ATypeParameterListOwner { 33 | constructor(psiMethod: PsiMethod) : this(psiMethod as PsiElement) 34 | 35 | constructor(ktANamedFunction: KtNamedFunction) : this(ktANamedFunction as PsiElement) 36 | 37 | override val javaElement: PsiMethod? 38 | get() = castJavaElement() 39 | 40 | override val kotlinElement: KtNamedFunction? 41 | get() = castKotlinElement() 42 | 43 | override val ifLanguage: Cases 44 | get() = castIfLanguage() 45 | 46 | val typeReference 47 | get() = 48 | javaElement?.returnTypeElement?.toAElement() ?: kotlinElement?.typeReference?.toAElement() 49 | 50 | val isOverride 51 | get() = 52 | kotlinElement?.modifierList?.hasModifier(KtTokens.OVERRIDE_KEYWORD) == true || 53 | annotations.any { it.shortName == "Override" } 54 | 55 | val isOverrideStatic 56 | get() = annotations.any { it.shortName == "OverrideStatic" } 57 | 58 | override val isStatic 59 | get() = 60 | super.isStatic || 61 | annotations.any { it.shortName == "JvmStatic" } || 62 | kotlinElement?.getParentOfType(strict = true)?.isCompanion() == 63 | true 64 | } 65 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ALambdaExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.assertj.core.api.Assertions.assertThat 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiLambdaExpression 22 | import org.jetbrains.kotlin.psi.KtLambdaExpression 23 | import org.junit.Test 24 | 25 | /** Tests [ALambdaExpression] */ 26 | class ALambdaExpressionTest { 27 | 28 | @Test 29 | fun `basic functionality`() { 30 | val aElementsTestUtil = 31 | AElementTestingUtil() 32 | 33 | val (javaElement, kotlinElement) = 34 | aElementsTestUtil.loadTestAElements( 35 | javaCode = 36 | """ 37 | |public class TestClass { 38 | | public void doIt(int a, int b) { 39 | | log((a) -> a.toString()); 40 | | } 41 | |} 42 | """ 43 | .trimMargin(), 44 | kotlinCode = 45 | """ 46 | |class TestClass { 47 | | fun doIt(a: Int, b: Int) { 48 | | log { a -> a.toString() } 49 | | } 50 | |} 51 | """ 52 | .trimMargin(), 53 | ) 54 | 55 | for (aElement in listOf(javaElement, kotlinElement)) { 56 | assertThat(aElement.psiElement.toAElement()).isInstanceOf(ALambdaExpression::class.java) 57 | assertThat(aElement.valueParameters.size).isEqualTo(1) 58 | assertThat(aElement.valueParameters.first().name).isEqualTo("a") 59 | aElementsTestUtil.assertSameString( 60 | aElement = aElement, 61 | onAElement = { it.bodyExpression?.text }, 62 | onJava = { "a.toString()" }, 63 | onKotlin = { "a.toString()" }, 64 | ) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AParameterListTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiParameterList 21 | import org.jetbrains.kotlin.psi.KtParameterList 22 | import org.junit.Test 23 | 24 | /** Tests [AParameterList] */ 25 | class AParameterListTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = AElementTestingUtil() 30 | 31 | val (javaElement, kotlinElement) = 32 | aElementsTestUtil.loadTestAElements( 33 | javaCode = 34 | """ 35 | |public class TestClass { 36 | | public void doIt(int a, int b) { 37 | | return a + b; 38 | | } 39 | |} 40 | """ 41 | .trimMargin(), 42 | kotlinCode = 43 | """ 44 | |class TestClass { 45 | | fun doIt(a: Int, b: Int) { 46 | | return a + b 47 | | } 48 | |} 49 | """ 50 | .trimMargin(), 51 | ) 52 | 53 | for (aElement in listOf(javaElement, kotlinElement)) { 54 | aElementsTestUtil.assertSamePsiElement( 55 | aElement, 56 | { it.rightParenthesis }, 57 | { it.lastChild }, 58 | { it.rightParenthesis }, 59 | ) 60 | aElementsTestUtil.assertSamePsiElement( 61 | aElement, 62 | { it.leftParenthesis }, 63 | { it.firstChild }, 64 | { it.leftParenthesis }, 65 | ) 66 | aElementsTestUtil.assertSamePsiElementList( 67 | aElement, 68 | { it.parameters }, 69 | { it.parameters.toList() }, 70 | { it.parameters }, 71 | ) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AValueArgumentListTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiExpressionList 21 | import org.jetbrains.kotlin.psi.KtValueArgumentList 22 | import org.junit.Test 23 | 24 | /** Tests [AValueArgumentList] */ 25 | class AValueArgumentListTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaElement, kotlinElement) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | public void doIt(int a, int b) { 38 | | return f(a, b); 39 | | } 40 | |} 41 | """ 42 | .trimMargin(), 43 | kotlinCode = 44 | """ 45 | |class TestClass { 46 | | fun doIt(a: Int, b: Int) { 47 | | return f(name = a, b) 48 | | } 49 | |} 50 | """ 51 | .trimMargin(), 52 | ) 53 | 54 | for (aElement in listOf(javaElement, kotlinElement)) { 55 | aElementsTestUtil.assertSamePsiElement( 56 | aElement, 57 | { it.rightParenthesis }, 58 | { it.lastChild }, 59 | { it.rightParenthesis }, 60 | ) 61 | aElementsTestUtil.assertSamePsiElement( 62 | aElement, 63 | { it.leftParenthesis }, 64 | { it.firstChild }, 65 | { it.leftParenthesis }, 66 | ) 67 | aElementsTestUtil.assertSamePsiElementList( 68 | aElement, 69 | { it.valueArguments }, 70 | { it.expressions.toList() }, 71 | { it.arguments.map { it.getArgumentExpression() } }, 72 | ) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/asttools/PsiActions.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.asttools 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.psi.psiUtil.endOffset 21 | import org.jetbrains.kotlin.psi.psiUtil.startOffset 22 | 23 | /** 24 | * Replaces multiple given nodes in a any code (kotlin or java) with the given replacements 25 | * 26 | * @param code the Kotlin/Java code to edit 27 | * @param elements a list of AST node from the file 28 | * @param replacements the strings to replace the text of the node, with index matching [elements] 29 | */ 30 | fun replaceElements(code: String, elements: List, replacements: List): String { 31 | if (elements.size != replacements.size) { 32 | throw IllegalArgumentException( 33 | "'elements' (${elements.size}) and replacements (${replacements.size}) have different lengths" 34 | ) 35 | } 36 | val sortedPatches: List> = 37 | elements.zip(replacements).sortedBy { it.first.endOffset }.sortedBy { it.first.startOffset } 38 | 39 | var previousPatchEndOffset = -1 40 | for (patch in sortedPatches) { 41 | if (patch.first.startOffset < previousPatchEndOffset) { 42 | val patchesDescription = 43 | sortedPatches.joinToString(separator = "\n", prefix = "\n") { 44 | (if (it == patch) "**** " else "") + 45 | "Patch: ${it.first.javaClass}" + 46 | " from: ${it.first.startOffset}" + 47 | " to: ${it.first.endOffset}" + 48 | " original text: ${it.first.text}" + 49 | " replacement: ${it.second}" 50 | } 51 | throw IllegalArgumentException("Cannot apply patches, patches intersect:$patchesDescription") 52 | } 53 | previousPatchEndOffset = patch.first.endOffset 54 | } 55 | 56 | var text = code 57 | for ((element, replacement) in sortedPatches.reversed()) { 58 | text = text.substring(0, element.startOffset) + replacement + text.substring(element.endOffset) 59 | } 60 | return text 61 | } 62 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ACallExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.assertj.core.api.Assertions.assertThat 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethodCallExpression 22 | import org.jetbrains.kotlin.psi.KtCallExpression 23 | import org.jetbrains.kotlin.psi.KtExpression 24 | import org.junit.Test 25 | 26 | /** Tests [ACallExpression] */ 27 | class ACallExpressionTest { 28 | 29 | @Test 30 | fun `basic functionality`() { 31 | val aElementsTestUtil = 32 | AElementTestingUtil() 33 | 34 | val (javaElement, kotlinElement) = 35 | aElementsTestUtil.loadTestAElements( 36 | javaCode = 37 | """ 38 | |public class TestClass { 39 | | public void doIt(int a, int b) { 40 | | invoke(a, b); 41 | | } 42 | |} 43 | """ 44 | .trimMargin(), 45 | kotlinCode = 46 | """ 47 | |class TestClass { 48 | | fun doIt(a: Int, b: Int) { 49 | | invoke(a, b) 50 | | } 51 | |} 52 | """ 53 | .trimMargin(), 54 | ) 55 | for (aElement in listOf(javaElement, kotlinElement)) { 56 | assertThat(aElement.psiElement.toAElement()).isInstanceOf(ACallExpression::class.java) 57 | aElementsTestUtil.assertSamePsiElementList( 58 | aElement = aElement, 59 | onAElement = { it.valueArguments }, 60 | onJava = { it.argumentList.expressions.toList() }, 61 | onKotlin = { (it as KtCallExpression).valueArguments.map { it.getArgumentExpression() } }, 62 | ) 63 | } 64 | assertThat(javaElement.callExpressionKotlinElement?.text).isNull() 65 | assertThat(kotlinElement.callExpressionKotlinElement?.text).isEqualTo("invoke(a, b)") 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ADeclarationWithBodyTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.asttools.JavaPsiParserUtil 20 | import com.facebook.asttools.KotlinParserUtil 21 | import org.assertj.core.api.Assertions.assertThat 22 | import org.junit.Test 23 | 24 | /** Tests [ADeclarationWithBody] */ 25 | class ADeclarationWithBodyTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val psiJavaFile = 30 | JavaPsiParserUtil.parseAsFile( 31 | """ 32 | |public class TestClass { 33 | | public int a = 5; 34 | | 35 | | public TestClass() {} 36 | | 37 | | public void doIt() {} 38 | | 39 | | Function1 f = (a) -> a.toString(); 40 | |} 41 | """ 42 | .trimMargin() 43 | ) 44 | assertThat( 45 | psiJavaFile.toAElement().collectDescendantsOfType().map { 46 | it.text 47 | } 48 | ) 49 | .containsExactly( 50 | "public TestClass() {}", 51 | "public void doIt() {}", 52 | ) 53 | 54 | val ktFile = 55 | KotlinParserUtil.parseAsFile( 56 | """ 57 | |class TestClass() { 58 | | 59 | | val a = 5 60 | | 61 | | fun doIt() {} 62 | | 63 | | val b: Int 64 | | get() { return 7 } 65 | | 66 | | val f = { a: Int -> a.toString() } 67 | | val f2 = fun (a: Int) = a + a 68 | | val f3 = Runnable { a } 69 | |} 70 | """ 71 | .trimMargin() 72 | ) 73 | 74 | assertThat(ktFile.toAElement().collectDescendantsOfType().map { it.text }) 75 | .containsExactly( 76 | "()", 77 | "fun doIt() {}", 78 | "get() { return 7 }", 79 | "fun (a: Int) = a + a", 80 | ) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AClassLiteralExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.assertj.core.api.Assertions.assertThat 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiClassObjectAccessExpression 22 | import org.jetbrains.kotlin.psi.KtClassLiteralExpression 23 | import org.junit.Test 24 | 25 | /** Tests [AClassLiteralExpression] */ 26 | class AClassLiteralExpressionTest { 27 | 28 | @Test 29 | fun `basic functionality`() { 30 | val aElementsTestUtil = 31 | AElementTestingUtil< 32 | AClassLiteralExpression, 33 | PsiClassObjectAccessExpression, 34 | KtClassLiteralExpression, 35 | >() 36 | 37 | val (javaElement, kotlinElement) = 38 | aElementsTestUtil.loadTestAElements( 39 | javaCode = 40 | """ 41 | |public class TestClass { 42 | | public void doIt() { 43 | | Class clazz = String.class; 44 | | } 45 | |} 46 | """ 47 | .trimMargin(), 48 | kotlinCode = 49 | """ 50 | |class TestClass { 51 | | fun doIt() { 52 | | val clazz = String::class 53 | | } 54 | |} 55 | """ 56 | .trimMargin(), 57 | ) 58 | 59 | for (aElement in listOf(javaElement, kotlinElement)) { 60 | assertThat(aElement.psiElement.toAElement()).isInstanceOf(AClassLiteralExpression::class.java) 61 | aElementsTestUtil.assertSamePsiElement( 62 | aElement = aElement, 63 | onAElement = { it.receiverExpression }, 64 | onJava = { it.operand }, 65 | onKotlin = { it.receiverExpression }, 66 | ) 67 | } 68 | assertThat(javaElement.receiverExpression.text).isEqualTo("String") 69 | assertThat(kotlinElement.receiverExpression.text).isEqualTo("String") 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/ATryExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiTryStatement 21 | import org.jetbrains.kotlin.psi.KtTryExpression 22 | 23 | /** 24 | * Represents a try-catch-finally construct, which is an expression in Kotlin, but a statement in 25 | * Java 26 | * 27 | * Example: 28 | * ``` 29 | * try { 30 | * riskyOperation() 31 | * } catch (e: Exception) { 32 | * handleError(e) 33 | * } finally { 34 | * cleanup() 35 | * } 36 | * ``` 37 | */ 38 | open class ATryExpression private constructor(psiElement: PsiElement) : 39 | AExpressionOrStatementImpl(psiElement) { 40 | constructor(psiTryStatement: PsiTryStatement) : this(psiTryStatement as PsiElement) 41 | 42 | constructor(ktTryExpression: KtTryExpression) : this(ktTryExpression as PsiElement) 43 | 44 | override val javaElement: PsiTryStatement? 45 | get() = castJavaElement() 46 | 47 | override val kotlinElement: KtTryExpression? 48 | get() = castKotlinElement() 49 | 50 | override val ifLanguage: Cases 51 | get() = castIfLanguage() 52 | 53 | /** The try block, i.e. `{ riskyOperation() }` in `try { riskyOperation() } catch (e) { ... }` */ 54 | val tryBlock: AElement? 55 | get() = (javaElement?.tryBlock ?: kotlinElement?.tryBlock)?.toAElement() 56 | 57 | /** 58 | * The catch clauses, i.e. `catch (e: Exception) { handleError(e) }` in a try-catch block. Returns 59 | * a list of catch blocks/clauses. 60 | */ 61 | val catchClauses: List 62 | get() = 63 | javaElement?.catchBlocks?.map { it.toAElement() } 64 | ?: kotlinElement?.catchClauses?.map { it.toAElement() } 65 | ?: emptyList() 66 | 67 | /** The finally block, i.e. `{ cleanup() }` in `try { ... } finally { cleanup() }`, if present */ 68 | val finallyBlock: AElement? 69 | get() = 70 | (javaElement?.finallyBlock ?: kotlinElement?.finallyBlock?.finalExpression)?.toAElement() 71 | } 72 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AModifierListOwner.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.lang.jvm.JvmModifier 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiClass 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiModifierListOwner 22 | import org.jetbrains.kotlin.lexer.KtTokens 23 | import org.jetbrains.kotlin.psi.KtModifierListOwner 24 | import org.jetbrains.kotlin.psi.psiUtil.getParentOfType 25 | import org.jetbrains.kotlin.psi.psiUtil.isPrivate 26 | import org.jetbrains.kotlin.psi.psiUtil.isProtected 27 | import org.jetbrains.kotlin.psi.psiUtil.isPublic 28 | 29 | /** Represents a decalration that can have modifiers such as `private`, `public` etc */ 30 | interface AModifierListOwner : AElement { 31 | 32 | override val javaElement: PsiModifierListOwner? 33 | get() = castJavaElement() 34 | 35 | override val kotlinElement: KtModifierListOwner? 36 | get() = castKotlinElement() 37 | 38 | override val ifLanguage: Cases 39 | get() = castIfLanguage() 40 | 41 | val isPrivate: Boolean 42 | get() = 43 | ifLanguage(isJava = { it.hasModifier(JvmModifier.PRIVATE) }, isKotlin = { it.isPrivate() }) 44 | 45 | val isProtected: Boolean 46 | get() = 47 | ifLanguage( 48 | isJava = { it.hasModifier(JvmModifier.PROTECTED) }, 49 | isKotlin = { it.isProtected() }, 50 | ) 51 | 52 | val isPackage: Boolean 53 | get() = ifLanguage(isJava = { !isPublic && !isPrivate && !isProtected }, isKotlin = { false }) 54 | 55 | val isInternal: Boolean 56 | get() = ifLanguage(isJava = { false }, isKotlin = { it.hasModifier(KtTokens.INTERNAL_KEYWORD) }) 57 | 58 | val isPublic: Boolean 59 | get() = 60 | ifLanguage( 61 | isJava = { 62 | it.hasModifier(JvmModifier.PUBLIC) || 63 | it.getParentOfType(strict = true)?.isInterface == true 64 | }, 65 | isKotlin = { it.isPublic }, 66 | ) 67 | 68 | val isStatic: Boolean 69 | get() = ifLanguage(isJava = { it.hasModifier(JvmModifier.STATIC) }, isKotlin = { false }) 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AArrayAccessExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiArrayAccessExpression 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtArrayAccessExpression 22 | 23 | /** 24 | * Represents an expression accessing a Java array or using the Kotlin get operator, i.e. `map["a"]` 25 | */ 26 | class AArrayAccessExpression 27 | internal constructor( 28 | psiElement: PsiElement, 29 | ) : AExpressionImpl(psiElement) { 30 | constructor( 31 | psiArrayAccessExpression: PsiArrayAccessExpression 32 | ) : this(psiArrayAccessExpression as PsiElement) 33 | 34 | constructor( 35 | ktArrayAccessExpression: KtArrayAccessExpression 36 | ) : this(ktArrayAccessExpression as PsiElement) 37 | 38 | override val javaElement: PsiArrayAccessExpression? 39 | get() = castJavaElement() 40 | 41 | override val kotlinElement: KtArrayAccessExpression? 42 | get() = castKotlinElement() 43 | 44 | override val ifLanguage: Cases 45 | get() = castIfLanguage() 46 | 47 | /** 48 | * An element representing all the value arguments and the parenthesis they're 49 | * 50 | * in i.e. `(a, b)` in `foo(a, b)` 51 | * 52 | * This can be null if only a lambda argument exists, for example in `foo { ... }` 53 | */ 54 | val arrayExpression: AExpressionOrStatement? 55 | get() = 56 | javaElement?.arrayExpression?.toAElement() ?: kotlinElement!!.arrayExpression?.toAElement() 57 | 58 | /** All arguments passed to the call, i.e. `a` and the lambda in `foo(a) { ... }` */ 59 | val indexExpressions: List 60 | get() = 61 | (javaElement?.indexExpression?.let { listOf(it.toAElement()) } 62 | ?: kotlinElement!!.indexExpressions.map { it.toAElement() }) 63 | 64 | val leftBracket: PsiElement 65 | get() = javaElement?.children?.first { it.text == "[" } ?: kotlinElement?.leftBracket!! 66 | 67 | val rightBracket: PsiElement 68 | get() = javaElement?.children?.first { it.text == "]" } ?: kotlinElement?.rightBracket!! 69 | } 70 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/asttools/PsiTestUtils.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.asttools 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiNamedElement 21 | import org.jetbrains.kotlin.psi.KtNamedDeclaration 22 | import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType 23 | 24 | /** 25 | * Loads a single Psi element that matches text or name or both, this is meant to be used to keep 26 | * tests shorter and simpler 27 | */ 28 | inline fun PsiElement.requireSingleOfType( 29 | text: String? = null, 30 | name: String? = null, 31 | ): T { 32 | val results = 33 | collectDescendantsOfType { 34 | (text == null || it.text == text) && 35 | (name == null || 36 | (it as? KtNamedDeclaration)?.name == name || 37 | (it as? PsiNamedElement)?.name == name) 38 | } 39 | return results.singleOrNull() 40 | ?: run { 41 | val matchString = 42 | listOfNotNull( 43 | text?.let { "text=\"$text\"" }, 44 | "type=${T::class.java.simpleName}" 45 | .takeIf { T::class.java != PsiElement::class.java }, 46 | name?.let { "name=\"$name\"" }, 47 | ) 48 | .joinToString(separator = ", and ") 49 | val resultsString = 50 | if (results.isEmpty()) "no elements" 51 | else 52 | "${results.size} elements:\n" + 53 | results.withIndex().joinToString(separator = "\n") { (index, value) -> 54 | "${index + 1}) " + value.text 55 | } 56 | error( 57 | "Expected exactly one element to match $matchString under element, but found $resultsString" 58 | ) 59 | } 60 | } 61 | 62 | /** 63 | * Loads a single Psi element that matches text or name or both, this is meant to be used to keep 64 | * tests shorter and simpler 65 | */ 66 | fun PsiElement.requireSingle( 67 | text: String? = null, 68 | name: String? = null, 69 | ): PsiElement { 70 | return requireSingleOfType(text, name) 71 | } 72 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AQualifiedExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiExpression 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiReferenceExpression 22 | import org.jetbrains.kotlin.psi.KtQualifiedExpression 23 | 24 | /** 25 | * Represents a qualified expression, a member or field access usually, i.e. `a.b` 26 | * 27 | * If the selector (i.e. b in the example above) is a function call, it will be represented as a 28 | * [AQualifiedCallExpression] 29 | * 30 | * See [AQualifiedCallExpression] for more explanations of the mess involved in bridgeing this for 31 | * Java and Kotlin 32 | */ 33 | interface AQualifiedExpression : AExpression { 34 | override val javaElement: PsiExpression? 35 | get() = castJavaElement() 36 | 37 | override val kotlinElement: KtQualifiedExpression? 38 | get() = castKotlinElement() 39 | 40 | override val ifLanguage: Cases 41 | get() = castIfLanguage() 42 | 43 | val javaQualifiedExpression: PsiReferenceExpression? 44 | 45 | val receiverExpressionOrStatement: AExpressionOrStatement 46 | get() = 47 | (javaQualifiedExpression?.qualifierExpression ?: kotlinElement?.receiverExpression)!! 48 | .toAElement() as AExpressionOrStatement 49 | 50 | val selectorExpression: AExpression 51 | get() = 52 | (javaQualifiedExpression?.element ?: kotlinElement?.selectorExpression)!!.toAElement() 53 | as AExpression 54 | 55 | val operator: String 56 | get() = if (language == Language.JAVA) "." else kotlinElement?.operationSign?.value!! 57 | } 58 | 59 | class AQualifiedExpressionImpl 60 | internal constructor( 61 | psiElement: PsiElement, 62 | ) : AExpressionImpl(psiElement), AQualifiedExpression { 63 | constructor(psiExpression: PsiReferenceExpression) : this(psiExpression as PsiElement) 64 | 65 | constructor(ktExpression: KtQualifiedExpression) : this(ktExpression as PsiElement) 66 | 67 | override val javaQualifiedExpression: PsiReferenceExpression? 68 | get() = javaElement as PsiReferenceExpression? 69 | } 70 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ADeclarationOrLambdaWithBodyTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.asttools.JavaPsiParserUtil 20 | import com.facebook.asttools.KotlinParserUtil 21 | import org.assertj.core.api.Assertions.assertThat 22 | import org.junit.Test 23 | 24 | /** Tests [ADeclarationOrLambdaWithBody] */ 25 | class ADeclarationOrLambdaWithBodyTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val psiJavaFile = 30 | JavaPsiParserUtil.parseAsFile( 31 | """ 32 | |public class TestClass { 33 | | public int a = 5; 34 | | 35 | | public TestClass() {} 36 | | 37 | | public void doIt() {} 38 | | 39 | | Function1 f = (a) -> a.toString(); 40 | |} 41 | """ 42 | .trimMargin() 43 | ) 44 | assertThat( 45 | psiJavaFile.toAElement().collectDescendantsOfType().map { 46 | it.text 47 | } 48 | ) 49 | .containsExactly("public TestClass() {}", "public void doIt() {}", "(a) -> a.toString()") 50 | 51 | val ktFile = 52 | KotlinParserUtil.parseAsFile( 53 | """ 54 | |class TestClass() { 55 | | 56 | | val a = 5 57 | | 58 | | fun doIt() {} 59 | | 60 | | val b: Int 61 | | get() { return 7 } 62 | | 63 | | val f = { a: Int -> a.toString() } 64 | | val f2 = fun (a: Int) = a + a 65 | | val f3 = Runnable { a } 66 | |} 67 | """ 68 | .trimMargin() 69 | ) 70 | 71 | assertThat( 72 | ktFile.toAElement().collectDescendantsOfType().map { 73 | it.text 74 | } 75 | ) 76 | .containsExactly( 77 | "()", 78 | "fun doIt() {}", 79 | "get() { return 7 }", 80 | "{ a: Int -> a.toString() }", 81 | "fun (a: Int) = a + a", 82 | "{ a }", 83 | ) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AIfExpressionOrStatementTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiIfStatement 21 | import org.jetbrains.kotlin.psi.KtIfExpression 22 | import org.junit.Test 23 | 24 | /** Tests [AIfExpressionOrStatement] */ 25 | class AIfExpressionOrStatementTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = 30 | AElementTestingUtil() 31 | 32 | val (javaAIfExpressionOrStatement, kotlinAIfExpressionOrStatement) = 33 | aElementsTestUtil.loadTestAElements( 34 | javaCode = 35 | """ 36 | |public class TestClass { 37 | | public void doIt(int a, int b) { 38 | | if (a + b > 0) { 39 | | return; 40 | | } else { 41 | | System.out.println("a + b <= 0"); 42 | | } 43 | | } 44 | |} 45 | """ 46 | .trimMargin(), 47 | kotlinCode = 48 | """ 49 | |class TestClass { 50 | | fun doIt(a: Int, b: Int) { 51 | | if (a + b > 0) { 52 | | return 53 | | } else { 54 | | println("a + b <= 0") 55 | | } 56 | | } 57 | |} 58 | """ 59 | .trimMargin(), 60 | ) 61 | 62 | for (aElement in listOf(javaAIfExpressionOrStatement, kotlinAIfExpressionOrStatement)) { 63 | aElementsTestUtil.assertSamePsiElement( 64 | aElement, 65 | { it.condition }, 66 | { it.condition }, 67 | { it.condition }, 68 | ) 69 | aElementsTestUtil.assertSamePsiElement(aElement, { it.then }, { it.thenBranch }, { it.then }) 70 | aElementsTestUtil.assertSamePsiElement( 71 | aElement, 72 | { it.`else` }, 73 | { it.elseBranch }, 74 | { it.`else` }, 75 | ) 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/matching/PsiAstMatcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.matching 18 | 19 | import com.google.errorprone.annotations.CheckReturnValue 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtTreeVisitorVoid 22 | 23 | class MatchResult( 24 | val psiElement: Element, 25 | internal val matchedVariables: Map, 26 | ) { 27 | 28 | fun getVariableResult(variableName: String): String? = matchedVariables[variableName]?.text 29 | 30 | fun hasValue(variableName: String): Boolean = matchedVariables.containsKey(variableName) 31 | 32 | operator fun get(variableName: String): String? = getVariableResult(variableName) 33 | } 34 | 35 | /** 36 | * Kotlin nodes matcher 37 | * 38 | * In order to avoid the need to create this class per every type it is built in a generic way 39 | * storing a list of children matchers. 40 | * 41 | * Create one of these matchers using [match] 42 | */ 43 | interface PsiAstMatcher { 44 | 45 | /** 46 | * Whether this matcher will match a null ot not, this is useful for building optional matchers 47 | */ 48 | val shouldMatchToNull: Boolean 49 | 50 | @CheckReturnValue 51 | fun findAll(element: PsiElement): List { 52 | return findAllWithVariables(element).map { it.psiElement as Element } 53 | } 54 | 55 | @CheckReturnValue 56 | fun findAllWithVariables(element: PsiElement): List> { 57 | val results = mutableListOf>() 58 | element.accept( 59 | object : KtTreeVisitorVoid() { 60 | override fun visitElement(element: PsiElement) { 61 | val result = matches(element) 62 | if (result != null) { 63 | results.add(result as MatchResult) 64 | } 65 | super.visitElement(element) 66 | } 67 | } 68 | ) 69 | return results 70 | } 71 | 72 | /** 73 | * Checks if the given element satisfies the conditions of this matcher 74 | * - If it is, a [MatchResult] is returned, which points to `obj` and contains all references to 75 | * satisfy variables that are part of this matcher. 76 | * - If the element does not match a `null` is returned 77 | */ 78 | fun matches(obj: PsiElement?): MatchResult? 79 | } 80 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AAnnotationTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.assertj.core.api.Assertions.assertThat 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiAnnotation 22 | import org.jetbrains.kotlin.psi.KtAnnotationEntry 23 | import org.junit.Test 24 | 25 | class AAnnotationTest { 26 | 27 | @Test 28 | fun `annotation with arguments`() { 29 | val aElementsTestUtil = AElementTestingUtil() 30 | 31 | val (javaElement, kotlinElement) = 32 | aElementsTestUtil.loadTestAElements( 33 | javaCode = 34 | """ 35 | |@Magic(value = 5, name = "test") 36 | |public class TestClass { 37 | |} 38 | """ 39 | .trimMargin(), 40 | kotlinCode = 41 | """ 42 | |@Magic(value = 5, name = "test") 43 | |class TestClass { 44 | |} 45 | """ 46 | .trimMargin(), 47 | ) 48 | 49 | assertThat(javaElement.shortName).isEqualTo("Magic") 50 | assertThat(javaElement.valueArguments).hasSize(2) 51 | 52 | assertThat(kotlinElement.shortName).isEqualTo("Magic") 53 | assertThat(kotlinElement.valueArguments).hasSize(2) 54 | } 55 | 56 | @Test 57 | fun `annotation with argument names`() { 58 | val aElementsTestUtil = AElementTestingUtil() 59 | 60 | val (javaElement, kotlinElement) = 61 | aElementsTestUtil.loadTestAElements( 62 | javaCode = 63 | """ 64 | |@Magic(value = 5, name = "test") 65 | |public class TestClass { 66 | |} 67 | """ 68 | .trimMargin(), 69 | kotlinCode = 70 | """ 71 | |@Magic(value = 5, name = "test") 72 | |class TestClass { 73 | |} 74 | """ 75 | .trimMargin(), 76 | ) 77 | 78 | assertThat(javaElement.valueArgumentNames).containsExactly("value", "name") 79 | assertThat(kotlinElement.valueArgumentNames).containsExactly("value", "name") 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ATryExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiTryStatement 21 | import org.jetbrains.kotlin.psi.KtTryExpression 22 | import org.junit.Test 23 | 24 | /** Tests [ATryExpression] */ 25 | class ATryExpressionTest { 26 | 27 | @Test 28 | fun `basic functionality`() { 29 | val aElementsTestUtil = AElementTestingUtil() 30 | 31 | val (javaATryExpression, kotlinATryExpression) = 32 | aElementsTestUtil.loadTestAElements( 33 | javaCode = 34 | """ 35 | |public class TestClass { 36 | | public void doIt() { 37 | | try { 38 | | System.out.println("trying"); 39 | | } catch (Exception e) { 40 | | System.out.println("caught"); 41 | | } finally { 42 | | System.out.println("finally"); 43 | | } 44 | | } 45 | |} 46 | """ 47 | .trimMargin(), 48 | kotlinCode = 49 | """ 50 | |class TestClass { 51 | | fun doIt() { 52 | | try { 53 | | println("trying") 54 | | } catch (e: Exception) { 55 | | println("caught") 56 | | } finally { 57 | | println("finally") 58 | | } 59 | | } 60 | |} 61 | """ 62 | .trimMargin(), 63 | ) 64 | 65 | for (aElement in listOf(javaATryExpression, kotlinATryExpression)) { 66 | aElementsTestUtil.assertSamePsiElement( 67 | aElement, 68 | { it.tryBlock }, 69 | { it.tryBlock }, 70 | { it.tryBlock }, 71 | ) 72 | assert(aElement.catchClauses.isNotEmpty()) { "Expected catch clauses to be non-empty" } 73 | aElementsTestUtil.assertSamePsiElement( 74 | aElement, 75 | { it.finallyBlock }, 76 | { it.finallyBlock }, 77 | { it.finallyBlock?.finalExpression }, 78 | ) 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/matching/PsiAstTemplateParserTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.matching 18 | 19 | import com.facebook.asttools.KotlinParserUtil 20 | import org.assertj.core.api.Assertions.assertThat 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 22 | import org.jetbrains.kotlin.psi.KtExpression 23 | import org.junit.Test 24 | 25 | /** Tests [PsiAstTemplateParser] */ 26 | class PsiAstTemplateParserTest { 27 | 28 | @Test 29 | fun `create matcher with resolver and resolve on type`() { 30 | val ktFile = 31 | KotlinParserUtil.parseAsFile( 32 | """ 33 | |class Foo { 34 | | val bar = foo.get().get() 35 | | var bar2 = foo.getMore().get() 36 | | var bar3 = foo.getEvenMore().get() 37 | |} 38 | """ 39 | .trimMargin() 40 | ) 41 | 42 | val parser = 43 | PsiAstTemplateParser( 44 | object : Resolver { 45 | override fun resolveToFullyQualifiedType(psiElement: PsiElement): String? = 46 | when (psiElement.text) { 47 | "foo.get()" -> "com.facebook.bar.Bar" 48 | "foo.getMore()" -> "com.facebook.bar.NotBar" 49 | else -> null 50 | } 51 | 52 | override fun resolveToFullyQualifiedTypeAndSupertypes( 53 | psiElement: PsiElement 54 | ): List? = 55 | when (psiElement.text) { 56 | "foo.get()" -> listOf("com.facebook.bar.Bar", "com.facebook.bar.Baz") 57 | "foo.getMore()" -> listOf("com.facebook.bar.NotBar", "com.facebook.bar.NotBaz") 58 | else -> null 59 | } 60 | } 61 | ) 62 | 63 | assertThat( 64 | parser 65 | .parseTemplateWithVariables("#a{type=com.facebook.bar.Bar}#.get()") 66 | .findAll(ktFile) 67 | .map { it.text } 68 | ) 69 | .containsExactly("foo.get().get()") 70 | 71 | assertThat( 72 | parser 73 | .parseTemplateWithVariables("#a{type=com.facebook.bar.Baz}#.get()") 74 | .findAll(ktFile) 75 | .map { it.text } 76 | ) 77 | .containsExactly("foo.get().get()") 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AElementUtilTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.asttools.JavaPsiParserUtil 20 | import com.facebook.asttools.KotlinParserUtil 21 | import org.assertj.core.api.Assertions.assertThat 22 | import org.jetbrains.kotlin.com.intellij.psi.PsiJvmModifiersOwner 23 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethodCallExpression 24 | import org.jetbrains.kotlin.psi.KtAnnotated 25 | import org.junit.Test 26 | 27 | /** Tests [AElementUtil] */ 28 | class AElementUtilTest { 29 | @Test 30 | fun `test AElement conversion for fields`() { 31 | val javaField = JavaPsiParserUtil.parseAsField("int n = 5;") 32 | assertThat(javaField.toAElement()).isInstanceOf(AProperty::class.java) 33 | assertThat((javaField as PsiJvmModifiersOwner).toAElement()).isInstanceOf(AProperty::class.java) 34 | 35 | val kotlinProperty = KotlinParserUtil.parseAsProperty("val n: Int = 5") 36 | assertThat(kotlinProperty.toAElement()).isInstanceOf(AProperty::class.java) 37 | assertThat((kotlinProperty as KtAnnotated).toAElement()).isInstanceOf(AProperty::class.java) 38 | } 39 | 40 | @Test 41 | fun `test AElement conversion for expressions`() { 42 | assertThat((JavaPsiParserUtil.parseAsExpression("1 + 1")).toAElement()) 43 | .isInstanceOf(ABinaryExpression::class.java) 44 | assertThat((JavaPsiParserUtil.parseAsExpression("a.b()")).toAElement()) 45 | .isInstanceOf(AQualifiedCallExpression::class.java) 46 | assertThat( 47 | (JavaPsiParserUtil.parseAsExpression("a.b()") as PsiMethodCallExpression).toAElement() 48 | ) 49 | .isInstanceOf(AQualifiedCallExpression::class.java) 50 | assertThat((JavaPsiParserUtil.parseAsExpression("b()")).toAElement()) 51 | .isInstanceOf(ACallExpression::class.java) 52 | 53 | assertThat((KotlinParserUtil.parseAsExpression("1 + 1")).toAElement()) 54 | .isInstanceOf(ABinaryExpression::class.java) 55 | assertThat((KotlinParserUtil.parseAsExpression("a.b()")).toAElement()) 56 | .isInstanceOf(AQualifiedCallExpression::class.java) 57 | assertThat((KotlinParserUtil.parseAsExpression("b()")).toAElement()) 58 | .isInstanceOf(ACallExpression::class.java) 59 | assertThat((KotlinParserUtil.parseAsExpression("if (true) 1 else 2")).toAElement()) 60 | .isInstanceOf(AIfExpressionOrStatement::class.java) 61 | assertThat((JavaPsiParserUtil.parseAsExpression("new Foo()")).toAElement()) 62 | .isInstanceOf(AMethodOrNewCallExpression::class.java) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/AArrayAccessExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import org.assertj.core.api.Assertions.assertThat 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiArrayAccessExpression 22 | import org.jetbrains.kotlin.psi.KtArrayAccessExpression 23 | import org.junit.Test 24 | 25 | /** Tests [AArrayAccessExpression] */ 26 | class AArrayAccessExpressionTest { 27 | 28 | @Test 29 | fun `basic functionality`() { 30 | val aElementsTestUtil = 31 | AElementTestingUtil< 32 | AArrayAccessExpression, 33 | PsiArrayAccessExpression, 34 | KtArrayAccessExpression, 35 | >() 36 | 37 | val (javaElement, kotlinElement) = 38 | aElementsTestUtil.loadTestAElements( 39 | javaCode = 40 | """ 41 | |public class TestClass { 42 | | public void doIt(int a, int b) { 43 | | m[a]; 44 | | } 45 | |} 46 | """ 47 | .trimMargin(), 48 | kotlinCode = 49 | """ 50 | |class TestClass { 51 | | fun doIt(a: Int, b: Int) { 52 | | m[a, b] 53 | | } 54 | |} 55 | """ 56 | .trimMargin(), 57 | ) 58 | for (aElement in listOf(javaElement, kotlinElement)) { 59 | assertThat(aElement.psiElement.toAElement()).isInstanceOf(AArrayAccessExpression::class.java) 60 | aElementsTestUtil.assertSamePsiElement( 61 | aElement = aElement, 62 | onAElement = { it.arrayExpression }, 63 | onJava = { it.arrayExpression }, 64 | onKotlin = { it.arrayExpression }, 65 | ) 66 | aElementsTestUtil.assertSamePsiElementList( 67 | aElement = aElement, 68 | onAElement = { it.indexExpressions }, 69 | onJava = { listOf(it.indexExpression) }, 70 | onKotlin = { it.indexExpressions }, 71 | ) 72 | aElementsTestUtil.assertSameString( 73 | aElement = aElement, 74 | onAElement = { it.leftBracket.text }, 75 | onJava = { "[" }, 76 | onKotlin = { "[" }, 77 | ) 78 | aElementsTestUtil.assertSameString( 79 | aElement = aElement, 80 | onAElement = { it.rightBracket.text }, 81 | onJava = { "]" }, 82 | onKotlin = { "]" }, 83 | ) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/ABinaryExpressionTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import com.facebook.aelements.util.AElementTestingUtil 20 | import com.facebook.asttools.KotlinParserUtil 21 | import org.assertj.core.api.Assertions.assertThat 22 | import org.jetbrains.kotlin.com.intellij.psi.PsiBinaryExpression 23 | import org.jetbrains.kotlin.psi.KtBinaryExpression 24 | import org.junit.Test 25 | 26 | /** Tests [ABinaryExpression] */ 27 | class ABinaryExpressionTest { 28 | 29 | @Test 30 | fun `basic functionality`() { 31 | val aElementsTestUtil = 32 | AElementTestingUtil() 33 | 34 | val (javaABinaryExpression, kotlinABinaryExpression) = 35 | aElementsTestUtil.loadTestAElements( 36 | javaCode = 37 | """ 38 | |public class TestClass { 39 | | public void doIt(int a, int b) { 40 | | return a + b; 41 | | } 42 | |} 43 | """ 44 | .trimMargin(), 45 | kotlinCode = 46 | """ 47 | |class TestClass { 48 | | fun doIt(a: Int, b: Int) { 49 | | return a + b 50 | | } 51 | |} 52 | """ 53 | .trimMargin(), 54 | ) 55 | 56 | for (aElement in listOf(javaABinaryExpression, kotlinABinaryExpression)) { 57 | aElementsTestUtil.assertSamePsiElement(aElement, { it.left }, { it.lOperand }, { it.left }) 58 | aElementsTestUtil.assertSamePsiElement(aElement, { it.right }, { it.rOperand }, { it.right }) 59 | aElementsTestUtil.assertSameString( 60 | aElement, 61 | { it.operator }, 62 | { it.operationSign.text }, 63 | { it.operationReference.text }, 64 | ) 65 | } 66 | } 67 | 68 | @Test 69 | fun `return statement in a binary expression`() { 70 | val ktFile = 71 | KotlinParserUtil.parseAsFile( 72 | """ 73 | |class TestClass { 74 | | fun doIt(a: String?) { 75 | | val b = a ?: return 76 | | } 77 | |} 78 | """ 79 | .trimMargin() 80 | ) 81 | val aElement = ktFile.toAElement().collectDescendantsOfType().first() 82 | assertThat(aElement.left?.text).isEqualTo("a") 83 | assertThat(aElement.operator).isEqualTo("?:") 84 | assertThat(aElement.right?.text).isEqualTo("return") 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /core/src/test/java/com/facebook/aelements/mods/AFileModsTest.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements.mods 18 | 19 | import com.facebook.aelements.ACallExpression 20 | import com.facebook.aelements.AFile 21 | import com.facebook.aelements.collectDescendantsOfType 22 | import com.facebook.aelements.util.AElementTestingUtil 23 | import com.facebook.tools.codemods.writer.psi.PsiWriter 24 | import org.jetbrains.kotlin.com.intellij.psi.PsiJavaFile 25 | import org.jetbrains.kotlin.psi.KtFile 26 | import org.junit.Test 27 | import org.mockito.kotlin.any 28 | import org.mockito.kotlin.eq 29 | import org.mockito.kotlin.mock 30 | import org.mockito.kotlin.never 31 | import org.mockito.kotlin.verify 32 | 33 | class AFileModsTest { 34 | @Test 35 | fun `test replaceInPlace`() { 36 | val aElementsTestUtil = AElementTestingUtil() 37 | 38 | val (javaElement, kotlinElement) = 39 | aElementsTestUtil.loadTestAElements( 40 | javaCode = 41 | """ 42 | |public class TestClass { 43 | | public void foo(int a) { 44 | | print(a); 45 | | } 46 | | 47 | | public void doIt(int a, int b) { 48 | | foo(a + b); 49 | | } 50 | |} 51 | """ 52 | .trimMargin(), 53 | kotlinCode = 54 | """ 55 | |class TestClass { 56 | | fun foo(a: Int) { 57 | | print(a) 58 | | } 59 | | 60 | | fun doIt(a: Int, b: Int) { 61 | | foo(a + b) 62 | | } 63 | |} 64 | """ 65 | .trimMargin(), 66 | ) 67 | listOf(javaElement, kotlinElement).forEach { aElement -> 68 | val psiWriter: PsiWriter = mock() 69 | aElement.replaceInPlace( 70 | matcher = { it.unqualifiedCalleeName == "foo" }, 71 | replacement = { "bar${it.valueArgumentList?.text}" }, 72 | imports = listOf("com.meta.TestClass"), 73 | psiWriter = psiWriter, 74 | ) 75 | verify(psiWriter).addImport("com.meta.TestClass") 76 | aElement.collectDescendantsOfType().forEach { call -> 77 | if (call.unqualifiedCalleeName == "foo") { 78 | verify(psiWriter).changeTo(call, "bar(a + b)") 79 | } else { 80 | verify(psiWriter, never()).changeTo(eq(call), any()) 81 | } 82 | } 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to make participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies within all project spaces, and it also applies when 49 | an individual is representing the project or its community in public spaces. 50 | Examples of representing a project or community include using an official 51 | project e-mail address, posting via an official social media account, or acting 52 | as an appointed representative at an online or offline event. Representation of 53 | a project may be further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AQualifiedCallExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiMethodCallExpression 21 | import org.jetbrains.kotlin.com.intellij.psi.PsiReferenceExpression 22 | import org.jetbrains.kotlin.psi.KtCallExpression 23 | import org.jetbrains.kotlin.psi.KtQualifiedExpression 24 | 25 | /** 26 | * Represents a function call which is qualified , i.e. `a.foo(1)` 27 | * 28 | * The hierarchy for qualified expressions (fields or method calls or references) and call 29 | * expressions are materially different between Java and Kotlin in their Psi repesentation, and this 30 | * creates a bunch of chaos which we try to bridge here. 31 | * 32 | * The expression `a.b.c(d)` in Java is represented as: 33 | * ``` 34 | * PsiMethodCallExpression `a.b.c(d)` 35 | * / \ 36 | * / \ 37 | * PsiReferenceExpression `a.b.c` PsiExpressionList `(d)` 38 | * ``` 39 | * 40 | * That is, the top node splits into the full qualification and the parameters 41 | * 42 | * In Kotlin however, it is represented as: 43 | * ``` 44 | * KtQualifiedExpression `a.b.c(d)` 45 | * / \ 46 | * / \ 47 | * KtQualifiedExpression `a.b` KtCallExpression `c(d)` 48 | * ``` 49 | * 50 | * We want to allow handling both call expressions, and qualified expressions and make them easy to 51 | * find. So we break this to multiple types: 52 | * 1. ACallExpression - Represents a call expression, which is a method call or a constructor call 53 | * 2. AQualifiedExpression - Represents a qualified expression, which is a field access or a method 54 | * call or a reference 55 | * 3. AQualifiedCallExpression - Represents a qualified expression which is a method call or a 56 | * constructor call 57 | */ 58 | open class AQualifiedCallExpression private constructor(psiElement: PsiElement) : 59 | AExpressionImpl(psiElement), ACallExpression, AQualifiedExpression { 60 | constructor( 61 | psiMethodCallExpression: PsiMethodCallExpression 62 | ) : this(psiMethodCallExpression as PsiElement) 63 | 64 | constructor( 65 | ktQualifiedExpression: KtQualifiedExpression 66 | ) : this(ktQualifiedExpression as PsiElement) 67 | 68 | override val javaElement: PsiMethodCallExpression? 69 | get() = castJavaElement() 70 | 71 | override val kotlinElement: KtQualifiedExpression? 72 | get() = castKotlinElement() 73 | 74 | override val ifLanguage: Cases 75 | get() = castIfLanguage() 76 | 77 | override val callExpressionKotlinElement: KtCallExpression? 78 | get() = kotlinElement?.selectorExpression as? KtCallExpression 79 | 80 | override val javaQualifiedExpression: PsiReferenceExpression? 81 | get() = javaElement?.methodExpression 82 | } 83 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AClassOrObject.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiClass 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtClass 22 | import org.jetbrains.kotlin.psi.KtClassOrObject 23 | import org.jetbrains.kotlin.psi.KtNamedFunction 24 | import org.jetbrains.kotlin.psi.KtProperty 25 | import org.jetbrains.kotlin.psi.allConstructors 26 | 27 | /** Represents a class or object, i.e. `public class Foo {}` or `object : Foo {}` */ 28 | open class AClassOrObject internal constructor(psiAElement: PsiElement) : 29 | AAnnotated(psiAElement), AModifierListOwner, ATypeParameterListOwner { 30 | constructor(psiClass: PsiClass) : this(psiClass as PsiElement) 31 | 32 | constructor(ktClassOrObject: KtClassOrObject) : this(ktClassOrObject as PsiElement) 33 | 34 | override val javaElement: PsiClass? 35 | get() = castJavaElement() 36 | 37 | override val kotlinElement: KtClassOrObject? 38 | get() = castKotlinElement() 39 | 40 | override val ifLanguage: Cases 41 | get() = castIfLanguage() 42 | 43 | val allConstructors: List 44 | get() = 45 | javaElement?.constructors?.map { AConstructor(it) } 46 | ?: kotlinElement?.allConstructors?.map { AConstructor(it) } 47 | ?: emptyList() 48 | 49 | val methods: List 50 | get() = 51 | javaElement 52 | ?.methods 53 | ?.filterNot { psiMethod -> psiMethod.isConstructor } 54 | ?.mapNotNull { it.toAElement() as ANamedFunction } 55 | ?: kotlinElement!!.declarations.filterIsInstance().map { 56 | it.toAElement() 57 | } 58 | 59 | val properties: List 60 | get() = 61 | javaElement?.fields?.map { it.toAElement() }?.toList() 62 | ?: kotlinElement!!.declarations.filterIsInstance().map { 63 | it.toAElement() as AMemberProperty 64 | } 65 | 66 | val name: String? 67 | get() = javaElement?.name ?: kotlinElement?.name 68 | 69 | val superTypes: List 70 | get() = 71 | ifLanguage( 72 | isKotlin = { kotlinElement -> 73 | kotlinElement.superTypeListEntries.mapNotNull { it.typeReference?.toAElement() } 74 | }, 75 | isJava = { javaElement -> 76 | if (javaElement.isEnum || javaElement.isAnnotationType) { 77 | emptyList() 78 | } else { 79 | (javaElement.implementsListTypes + javaElement.extendsListTypes).mapNotNull { 80 | it?.toAElement() 81 | } 82 | } 83 | }, 84 | ) 85 | 86 | val isInterface: Boolean 87 | get() = 88 | ifLanguage( 89 | isJava = { it.isInterface }, 90 | isKotlin = { (it as? KtClass)?.isInterface() == true }, 91 | ) 92 | } 93 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/matching/Variable.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.matching 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 20 | 21 | /** 22 | * Represents a variagle in a template 23 | * 24 | * A variable is declared in a template with the syntax "#name{arg1=value1, arg2=value2}(?)# 25 | * - is the name to be used later in templates 26 | * - allows defining common predicates on the variable, such as 'text=.*MyRegex.*' 27 | * - (?) an optional quetion mark means this variable will be optional, if the node is missing it 28 | * will match, and if it is available it needs to satisfy the other requirements 29 | */ 30 | class Variable( 31 | val name: String, 32 | val matcher: PsiAstMatcherImpl<*>, 33 | val isOptional: Boolean, 34 | val isKotlin: Boolean, 35 | val resolver: Resolver, 36 | arguments: String?, 37 | ) { 38 | 39 | private var textMatchArgument: Regex? = null 40 | private var typeMatchArgument: String? = null 41 | 42 | init { 43 | matcher.variableName = name 44 | matcher.shouldMatchToNull = isOptional 45 | if (arguments != null) { 46 | val argumentsClean = arguments.removeSurrounding("{", "}") 47 | val regex = " *(?[a-zA-Z0-9]+) *= *(?[^,]+)".toRegex() 48 | var i = 0 49 | while (i < argumentsClean.length) { 50 | val match = 51 | regex.matchAt(argumentsClean, i) 52 | ?: error("Syntax error in matcher argument string $argumentsClean, $i") 53 | i = match.range.last + 1 54 | val argumentName = checkNotNull(match.groups["name"]).value 55 | val argumentValue = checkNotNull(match.groups["value"]).value 56 | when (argumentName) { 57 | "text" -> textMatchArgument = argumentValue.toRegex() 58 | "type" -> typeMatchArgument = argumentValue 59 | else -> error("Unknown template argument to variable $name: $argumentName") 60 | } 61 | } 62 | } 63 | } 64 | 65 | fun addConditionsFromVariable(matcher: PsiAstMatcherImpl<*>) { 66 | textMatchArgument?.let { regex -> 67 | matcher.addChildMatcher { it is PsiElement && it.text.matches(regex) } 68 | } 69 | typeMatchArgument?.let { 70 | check(resolver != Resolver.DEFAULT) { "Missing resolver, cannot deduce types" } 71 | matcher.addChildMatcher { 72 | it is PsiElement && 73 | resolver.resolveToFullyQualifiedTypeAndSupertypes(it)?.any { typeName -> 74 | typeName == typeMatchArgument 75 | } == true 76 | } 77 | } 78 | } 79 | 80 | val parsableCodeString: String = if (isKotlin) "`$$name$`" else "$$name$" 81 | val templateString: String = "#$name${if (isOptional) "?" else ""}#" 82 | 83 | companion object { 84 | val ANY_SENTINEL: PsiAstMatcherImpl = PsiAstMatcherImpl(PsiElement::class.java) 85 | val TEMPLATE_VARIABLE_REGEX: Regex = 86 | "#(?[A-Za-z0-9_]+)(?\\{[^}]*\\})?(?[?]?)#".toRegex() 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /core/src/main/java/com/facebook/aelements/AMethodOrNewCallExpression.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) Meta Platforms, Inc. and affiliates. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.facebook.aelements 18 | 19 | import org.jetbrains.kotlin.com.intellij.psi.PsiCallExpression 20 | import org.jetbrains.kotlin.com.intellij.psi.PsiElement 21 | import org.jetbrains.kotlin.psi.KtCallExpression 22 | import org.jetbrains.kotlin.psi.KtExpression 23 | 24 | /** 25 | * Represents a call expression or a Java constructor call, i.e. `invoke(a)` ot `new Foo()` 26 | * 27 | * See [AQualifiedCallExpression] for explanations of the mess involved in bridgeing this for Java 28 | * and Kotlin 29 | */ 30 | interface AMethodOrNewCallExpression : AExpression { 31 | 32 | override val javaElement: PsiCallExpression? 33 | get() = castJavaElement() 34 | 35 | override val kotlinElement: KtExpression? 36 | get() = castKotlinElement() 37 | 38 | override val ifLanguage: Cases 39 | get() = castIfLanguage() 40 | 41 | val callExpressionKotlinElement: KtCallExpression? 42 | 43 | /** 44 | * An element representing all the value arguments and the parenthesis they're 45 | * 46 | * in i.e. `(a, b)` in `foo(a, b)` 47 | * 48 | * This can be null if only a lambda argument exists, for example in `foo { ... }` 49 | */ 50 | val valueArgumentList: AValueArgumentList? 51 | get() = 52 | javaElement?.argumentList?.toAElement() 53 | ?: callExpressionKotlinElement?.valueArgumentList?.toAElement() 54 | 55 | /** All arguments passed to the call, i.e. `a` and the lambda in `foo(a) { ... }` */ 56 | val valueArguments: List 57 | get() = 58 | (javaElement?.argumentList?.expressions?.toList() 59 | ?: callExpressionKotlinElement?.valueArguments?.map { 60 | it.getArgumentExpression()!! 61 | })!! 62 | .map { it.toAElement() as AExpressionOrStatement } 63 | 64 | /** The node of the list of all type arguments i.e. `` in foo() */ 65 | val typeArgumentList: ATypeArgumentList? 66 | get() = 67 | javaElement?.typeArgumentList?.toAElement() 68 | ?: callExpressionKotlinElement?.typeArgumentList?.toAElement() 69 | 70 | /** All type arguments passed to the call, i.e. `A` in foo() */ 71 | val typeArguments: List 72 | get() = 73 | (javaElement?.typeArgumentList?.typeParameterElements?.toList()?.map { it.toAElement() } 74 | ?: callExpressionKotlinElement?.typeArguments?.map { 75 | it.typeReference!!.toAElement() 76 | })!! 77 | } 78 | 79 | open class AMethodOrNewCallExpressionImpl 80 | internal constructor( 81 | psiElement: PsiElement, 82 | ) : AExpressionImpl(psiElement), AMethodOrNewCallExpression { 83 | constructor(psiExpression: PsiCallExpression) : this(psiExpression as PsiElement) 84 | 85 | constructor(ktExpression: KtCallExpression) : this(ktExpression as PsiElement) 86 | 87 | override val callExpressionKotlinElement: KtCallExpression? 88 | get() = kotlinElement as? KtCallExpression 89 | } 90 | --------------------------------------------------------------------------------