Handle erroneous code (initial drop, TBD).

This commit is contained in:
Dmitry Petrov
2016-09-02 17:11:15 +03:00
committed by Dmitry Petrov
parent bf03be97a1
commit fe397eddbe
14 changed files with 233 additions and 62 deletions
@@ -38,7 +38,7 @@ class DeclarationGenerator(override val context: GeneratorContext) : Generator {
is KtTypeAlias ->
generateTypeAliasDeclaration(ktDeclaration)
else ->
IrDummyDeclaration(
IrErrorDeclarationImpl(
ktDeclaration.startOffset, ktDeclaration.endOffset,
getOrFail(BindingContext.DECLARATION_TO_DESCRIPTOR, ktDeclaration)
)
@@ -0,0 +1,70 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.ir.expressions.IrErrorCallExpressionImpl
import org.jetbrains.kotlin.ir.expressions.IrErrorExpressionImpl
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.types.ErrorUtils
class ErrorExpressionGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) {
fun generateErrorExpression(ktElement: KtElement, message: String?): IrExpression =
IrErrorExpressionImpl(ktElement.startOffset, ktElement.endOffset,
if (ktElement is KtExpression) getErrorExpressionType(ktElement) else ErrorUtils.createErrorType(""),
message ?: "")
fun generateErrorCall(ktCall: KtCallExpression): IrExpression {
val type = getErrorExpressionType(ktCall)
val irErrorCall = IrErrorCallExpressionImpl(ktCall.startOffset, ktCall.endOffset, type, "") // TODO problem description?
irErrorCall.explicitReceiver = (ktCall.parent as? KtDotQualifiedExpression)?.let {
statementGenerator.generateExpression(it.receiverExpression)
}
ktCall.valueArguments.forEach {
val ktArgument = it.getArgumentExpression()
if (ktArgument != null) {
irErrorCall.addArgument(statementGenerator.generateExpression(ktArgument))
}
}
ktCall.lambdaArguments.forEach {
irErrorCall.addArgument(statementGenerator.generateExpression(it.getArgumentExpression()))
}
return irErrorCall
}
private fun getErrorExpressionType(ktExpression: KtExpression) =
getInferredTypeWithImplicitCasts(ktExpression) ?: ErrorUtils.createErrorType("")
fun generateErrorExpression(ktName: KtSimpleNameExpression): IrExpression {
val type = getErrorExpressionType(ktName)
val irErrorCall = IrErrorCallExpressionImpl(ktName.startOffset, ktName.endOffset, type, "") // TODO problem description?
irErrorCall.explicitReceiver = (ktName.parent as? KtDotQualifiedExpression)?.let {
statementGenerator.generateExpression(it.receiverExpression)
}
return irErrorCall
}
}
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.psi2ir.generators
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.ir.expressions.IrDummyExpression
import org.jetbrains.kotlin.ir.expressions.IrErrorExpressionImpl
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.psiUtil.endOffset
@@ -57,5 +57,5 @@ fun Generator.getInferredTypeWithImplicitCastsOrFail(key: KtExpression): KotlinT
fun Generator.getResolvedCall(key: KtElement): ResolvedCall<out CallableDescriptor>? =
key.getResolvedCall(context.bindingContext)
fun Generator.createDummyExpression(ktExpression: KtExpression, description: String): IrDummyExpression =
IrDummyExpression(ktExpression.startOffset, ktExpression.endOffset, getInferredTypeWithImplicitCastsOrFail(ktExpression), description)
fun Generator.createDummyExpression(ktExpression: KtExpression, description: String): IrErrorExpressionImpl =
IrErrorExpressionImpl(ktExpression.startOffset, ktExpression.endOffset, getInferredTypeWithImplicitCastsOrFail(ktExpression), description)
@@ -56,7 +56,12 @@ class StatementGenerator(
ktExpression.genExpr()
private fun KtElement.genStmt(): IrStatement =
deparenthesize().accept(this@StatementGenerator, null)
try {
deparenthesize().accept(this@StatementGenerator, null)
}
catch (e: Exception) {
ErrorExpressionGenerator(this@StatementGenerator).generateErrorExpression(this, e.message)
}
private fun KtElement.genExpr(): IrExpression =
genStmt().assertCast()
@@ -220,9 +225,7 @@ class StatementGenerator(
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression, data: Nothing?): IrExpression {
val resolvedCall = getResolvedCall(expression) ?:
return IrDummyExpression(expression.startOffset, expression.endOffset,
context.builtIns.nothingType,
"No resolved call for ${expression.text}")
return ErrorExpressionGenerator(this).generateErrorExpression(expression)
if (resolvedCall is VariableAsFunctionResolvedCall) {
val variableCall = pregenerateCall(resolvedCall.variableCall)
@@ -257,14 +260,15 @@ class StatementGenerator(
is VariableDescriptor ->
CallGenerator(this).generateGetVariable(expression.startOffset, expression.endOffset, descriptor)
else ->
IrDummyExpression(
IrErrorExpressionImpl(
expression.startOffset, expression.endOffset, getInferredTypeWithImplicitCastsOrFail(expression),
expression.text + ": ${descriptor.name} ${descriptor.javaClass.simpleName}"
)
}
override fun visitCallExpression(expression: KtCallExpression, data: Nothing?): IrStatement {
val resolvedCall = getResolvedCall(expression) ?: TODO("No resolved call for call expression")
val resolvedCall = getResolvedCall(expression) ?:
return ErrorExpressionGenerator(this).generateErrorCall(expression)
if (resolvedCall is VariableAsFunctionResolvedCall) {
val functionCall = pregenerateCall(resolvedCall.functionCall)
@@ -33,12 +33,14 @@ abstract class AbstractIrGeneratorTestCase : CodegenTestCase() {
protected abstract fun doTest(wholeFile: File, testFiles: List<TestFile>)
protected fun generateIrFilesAsSingleModule(testFiles: List<TestFile>): Map<TestFile, IrFile> {
protected fun generateIrFilesAsSingleModule(testFiles: List<TestFile>, ignoreErrors: Boolean = false): Map<TestFile, IrFile> {
assert(myFiles != null) { "myFiles not initialized" }
assert(myEnvironment != null) { "myEnvironment not initialized" }
val analysisResult = JvmResolveUtil.analyzeAndCheckForErrors(myFiles.psiFiles, myEnvironment)
analysisResult.throwIfError()
AnalyzingUtils.throwExceptionOnErrors(analysisResult.bindingContext)
val analysisResult = JvmResolveUtil.analyze(myFiles.psiFiles, myEnvironment)
if (!ignoreErrors) {
analysisResult.throwIfError()
AnalyzingUtils.throwExceptionOnErrors(analysisResult.bindingContext)
}
val psi2ir = Psi2IrTranslator()
val irModule = psi2ir.generateModule(analysisResult.moduleDescriptor, myFiles.psiFiles, analysisResult.bindingContext)
val ktFiles = testFiles.filter { it.name.endsWith(".kt") }
@@ -32,7 +32,8 @@ import java.util.regex.Pattern
abstract class AbstractIrTextTestCase : AbstractIrGeneratorTestCase() {
override fun doTest(wholeFile: File, testFiles: List<TestFile>) {
val dir = wholeFile.parentFile
for ((testFile, irFile) in generateIrFilesAsSingleModule(testFiles)) {
val ignoreErrors = shouldIgnoreErrors(wholeFile)
for ((testFile, irFile) in generateIrFilesAsSingleModule(testFiles, ignoreErrors)) {
doTestIrFileAgainstExpectations(dir, testFile, irFile)
}
}
@@ -74,6 +75,10 @@ abstract class AbstractIrTextTestCase : AbstractIrGeneratorTestCase() {
companion object {
private val EXPECTED_OCCURRENCES_PATTERN = Regex("""^\s*//\s*(\d+)\s*(.*)$""")
private val IR_TREES_TXT_PATTERN = Regex("""// \s*<<<\s+(.*)$""")
private val IGNORE_ERRORS_PATTERN = Regex("""// !IGNORE_ERRORS""")
internal fun shouldIgnoreErrors(wholeFile: File): Boolean =
IGNORE_ERRORS_PATTERN.containsMatchIn(wholeFile.readText())
internal fun parseExpectations(dir: File, testFile: TestFile): Expectations {
val regexps = ArrayList<RegexpInText>()
@@ -21,15 +21,17 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.throwNoSuchSlot
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
class IrDummyDeclaration(
interface IrErrorDeclaration : IrDeclaration
class IrErrorDeclarationImpl(
startOffset: Int,
endOffset: Int,
override val descriptor: DeclarationDescriptor
) : IrDeclarationBase(startOffset, endOffset, IrDeclarationOrigin.DEFINED) {
) : IrDeclarationBase(startOffset, endOffset, IrDeclarationOrigin.DEFINED), IrErrorDeclaration {
override val declarationKind: IrDeclarationKind get() = IrDeclarationKind.DUMMY
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitDummyDeclaration(this, data)
return visitor.visitErrorDeclaration(this, data)
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
@@ -1,37 +0,0 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
class IrDummyExpression(
startOffset: Int,
endOffset: Int,
type: KotlinType,
val description: String
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrExpressionWithCopy {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitDummyExpression(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// No children
}
override fun copy(): IrDummyExpression =
IrDummyExpression(startOffset, endOffset, type, description)
}
@@ -0,0 +1,90 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.ir.expressions
import org.jetbrains.kotlin.ir.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.SmartList
interface IrErrorExpression : IrExpression {
val description: String
}
interface IrErrorCallExpression : IrErrorExpression {
var explicitReceiver: IrExpression?
val arguments: List<IrExpression>
}
class IrErrorExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override val description: String
) : IrTerminalExpressionBase(startOffset, endOffset, type), IrExpressionWithCopy, IrErrorExpression {
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R =
visitor.visitErrorExpression(this, data)
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
// No children
}
override fun copy(): IrErrorExpressionImpl =
IrErrorExpressionImpl(startOffset, endOffset, type, description)
}
class IrErrorCallExpressionImpl(
startOffset: Int,
endOffset: Int,
type: KotlinType,
override val description: String
) : IrExpressionBase(startOffset, endOffset, type), IrErrorCallExpression {
override var explicitReceiver: IrExpression? = null
set(value) {
value?.assertDetached()
field?.detach()
field = value
value?.setTreeLocation(this, DISPATCH_RECEIVER_SLOT)
}
override val arguments: MutableList<IrExpression> = SmartList()
fun addArgument(argument: IrExpression) {
argument.assertDetached()
argument.setTreeLocation(this, arguments.size)
arguments.add(argument)
}
override fun <R, D> accept(visitor: IrElementVisitor<R, D>, data: D): R {
return visitor.visitErrorCallExpression(this, data)
}
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
explicitReceiver?.accept(visitor, data)
arguments.forEach { it.accept(visitor, data) }
}
override fun getChild(slot: Int): IrElement? =
arguments.getOrNull(slot)
override fun replaceChild(slot: Int, newChild: IrElement) {
newChild.assertDetached()
arguments[slot].detach()
arguments[slot] = newChild.assertCast()
newChild.setTreeLocation(this, slot)
}
}
@@ -59,6 +59,13 @@ class DumpIrTreeVisitor(out: Appendable): IrElementVisitor<Unit, String> {
}
}
override fun visitErrorCallExpression(expression: IrErrorCallExpression, data: String) {
expression.dumpLabeledElementWith(data) {
expression.explicitReceiver?.accept(this, "receiver")
expression.arguments.forEach { it.accept(this, "") }
}
}
private fun visitFunctionWithParameters(declaration: IrFunction, data: String) {
declaration.dumpLabeledElementWith(data) {
declaration.descriptor.valueParameters.forEach { valueParameter ->
@@ -182,11 +182,14 @@ class RenderIrElementVisitor : IrElementVisitor<String, Nothing?> {
override fun visitTryCatch(tryCatch: IrTryCatch, data: Nothing?): String =
"TRY_CATCH type=${tryCatch.type.render()}"
override fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: Nothing?): String =
"DUMMY ${declaration.descriptor.javaClass.simpleName} ${declaration.descriptor.name}"
override fun visitErrorDeclaration(declaration: IrErrorDeclaration, data: Nothing?): String =
"ERROR_DECL ${declaration.descriptor.javaClass.simpleName} ${declaration.descriptor.name}"
override fun visitDummyExpression(expression: IrDummyExpression, data: Nothing?): String =
"DUMMY ${expression.description} type=${expression.type.render()}"
override fun visitErrorExpression(expression: IrErrorExpression, data: Nothing?): String =
"ERROR_EXPR '${expression.description}' type=${expression.type.render()}"
override fun visitErrorCallExpression(expression: IrErrorCallExpression, data: Nothing?): String =
"ERROR_CALL '${expression.description}' type=${expression.type.render()}"
companion object {
private val DESCRIPTOR_RENDERER = DescriptorRenderer.withOptions {
@@ -91,7 +91,7 @@ interface IrElementVisitor<out R, in D> {
fun visitReturn(expression: IrReturn, data: D) = visitExpression(expression, data)
fun visitThrow(expression: IrThrow, data: D) = visitExpression(expression, data)
// NB Use it only for testing purposes; will be removed as soon as all Kotlin expression types are covered
fun visitDummyDeclaration(declaration: IrDummyDeclaration, data: D) = visitDeclaration(declaration, data)
fun visitDummyExpression(expression: IrDummyExpression, data: D) = visitExpression(expression, data)
fun visitErrorDeclaration(declaration: IrErrorDeclaration, data: D) = visitDeclaration(declaration, data)
fun visitErrorExpression(expression: IrErrorExpression, data: D) = visitExpression(expression, data)
fun visitErrorCallExpression(expression: IrErrorCallExpression, data: D) = visitErrorExpression(expression, data)
}
@@ -0,0 +1,10 @@
// !IGNORE_ERRORS
val test1 = unresolved
val test2: Unresolved =
unresolved()
val test3 = 42.unresolved(56)
val test4 = 42 *
@@ -209,6 +209,21 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
}
}
@TestMetadata("compiler/testData/ir/irText/errors")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class Errors extends AbstractIrTextTestCase {
public void testAllFilesPresentInErrors() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/ir/irText/errors"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("unresolvedReference.kt")
public void testUnresolvedReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/errors/unresolvedReference.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/ir/irText/expressions")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)