Extract Function: Improve rendering of error messages

This commit is contained in:
Alexey Sedunov
2014-07-04 19:31:38 +04:00
parent 4734be2065
commit 1618d7448d
20 changed files with 58 additions and 45 deletions
@@ -47,8 +47,7 @@ fun getFunctionForExtractedFragment(
): JetNamedFunction? {
fun getErrorMessageForExtractFunctionResult(analysisResult: AnalysisResult): String {
return analysisResult.messages.map {
errorMessage ->
return analysisResult.messages.map { errorMessage ->
val message = when(errorMessage) {
ErrorMessage.NO_EXPRESSION -> "Cannot perform an action without an expression"
ErrorMessage.NO_CONTAINER -> "Cannot perform an action at this breakpoint ${breakpointFile.getName()}:${breakpointLine}"
@@ -60,8 +59,8 @@ fun getFunctionForExtractedFragment(
ErrorMessage.MULTIPLE_EXIT_POINTS,
ErrorMessage.DECLARATIONS_ARE_USED_OUTSIDE -> "Cannot perform an action for this expression"
}
if (errorMessage.additionalInfo == null) message else "$message: ${errorMessage.additionalInfo?.makeString(", ")}"
}.makeString(", ")
errorMessage.additionalInfo?.let { "$message: ${it.joinToString(", ")}" } ?: message
}.joinToString(", ")
}
return ApplicationManager.getApplication()?.runReadAction(object: Computable<JetNamedFunction> {
@@ -37,6 +37,7 @@ import org.jetbrains.jet.plugin.refactoring.extractFunction.AnalysisResult.Error
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor
import org.jetbrains.jet.lang.psi.JetProperty
import org.jetbrains.jet.lang.psi.JetDeclaration
import com.intellij.openapi.util.text.StringUtil
trait Parameter {
val argumentText: String
@@ -193,21 +194,21 @@ class AnalysisResult (
}
fun renderMessage(): String {
val message = JetRefactoringBundle.message(when(this) {
NO_EXPRESSION -> "cannot.refactor.no.expression"
NO_CONTAINER -> "cannot.refactor.no.container"
SUPER_CALL -> "cannot.extract.super.call"
DENOTABLE_TYPES -> "parameter.types.are.not.denotable"
MULTIPLE_OUTPUT -> "selected.code.fragment.has.multiple.output.values"
OUTPUT_AND_EXIT_POINT -> "selected.code.fragment.has.output.values.and.exit.points"
MULTIPLE_EXIT_POINTS -> "selected.code.fragment.has.multiple.exit.points"
DECLARATIONS_ARE_USED_OUTSIDE -> "declarations.are.used.outside.of.selected.code.fragment"
DECLARATIONS_OUT_OF_SCOPE -> "declarations.will.move.out.of.scope"
})!!
if (additionalInfo != null) {
return "$message\n${additionalInfo?.makeString("\n")}"
}
return message
val message = JetRefactoringBundle.message(
when (this) {
NO_EXPRESSION -> "cannot.refactor.no.expression"
NO_CONTAINER -> "cannot.refactor.no.container"
SUPER_CALL -> "cannot.extract.super.call"
DENOTABLE_TYPES -> "parameter.types.are.not.denotable"
MULTIPLE_OUTPUT -> "selected.code.fragment.has.multiple.output.values"
OUTPUT_AND_EXIT_POINT -> "selected.code.fragment.has.output.values.and.exit.points"
MULTIPLE_EXIT_POINTS -> "selected.code.fragment.has.multiple.exit.points"
DECLARATIONS_ARE_USED_OUTSIDE -> "declarations.are.used.outside.of.selected.code.fragment"
DECLARATIONS_OUT_OF_SCOPE -> "declarations.will.move.out.of.scope"
}
)
return additionalInfo?.let { "$message\n\n${it.map { StringUtil.htmlEmphasize(it) }.joinToString("\n")}" } ?: message
}
}
}
@@ -68,11 +68,21 @@ import org.jetbrains.jet.lang.resolve.OverridingUtil
import org.jetbrains.jet.lang.psi.psiUtil.isAncestor
import org.jetbrains.jet.plugin.intentions.declarations.DeclarationUtils
import org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils
import com.intellij.openapi.util.text.StringUtil
private val DEFAULT_FUNCTION_NAME = "myFun"
private val DEFAULT_RETURN_TYPE = KotlinBuiltIns.getInstance().getUnitType()
private val DEFAULT_PARAMETER_TYPE = KotlinBuiltIns.getInstance().getNullableAnyType()
private fun DeclarationDescriptor.renderForMessage(): String =
DescriptorRenderer.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(this)
private fun JetType.renderForMessage(): String =
DescriptorRenderer.SOURCE_CODE.renderType(this)
private fun JetDeclaration.renderForMessage(bindingContext: BindingContext): String? =
bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, this]?.renderForMessage()
private fun JetType.isDefault(): Boolean = KotlinBuiltIns.getInstance().isUnit(this)
private fun List<Instruction>.getModifiedVarDescriptors(bindingContext: BindingContext): Set<VariableDescriptor> {
@@ -203,7 +213,7 @@ private fun ExtractionData.analyzeControlFlow(
val defaultControlFlow = DefaultControlFlow(if (returnValueType.isMeaningful()) returnValueType else typeOfDefaultFlow, declarationsToCopy)
if (declarationsToReport.isNotEmpty()) {
val localVarStr = declarationsToReport.map { it.getName()!! }.sort()
val localVarStr = declarationsToReport.map { it.renderForMessage(bindingContext)!! }.distinct().sort()
return Pair(defaultControlFlow, ErrorMessage.DECLARATIONS_ARE_USED_OUTSIDE.addAdditionalInfo(localVarStr))
}
@@ -214,7 +224,9 @@ private fun ExtractionData.analyzeControlFlow(
val outValuesCount = outDeclarations.size + outParameters.size
when {
outValuesCount > 1 -> {
val outValuesStr = (outParameters.map { it.argumentText } + outDeclarations.map { it.getName()!! }).sort()
val outValuesStr =
(outParameters.map { it.originalDescriptor.renderForMessage() }
+ outDeclarations.map { it.renderForMessage(bindingContext)!! }).sort()
return Pair(defaultControlFlow, ErrorMessage.MULTIPLE_OUTPUT.addAdditionalInfo(outValuesStr))
}
@@ -532,7 +544,8 @@ private fun ExtractionData.inferParametersInfo(
private fun ExtractionData.checkDeclarationsMovingOutOfScope(
enclosingDeclaration: JetDeclaration,
controlFlow: ControlFlow
controlFlow: ControlFlow,
bindingContext: BindingContext
): ErrorMessage? {
val declarationsOutOfScope = HashSet<JetNamedDeclaration>()
if (controlFlow is JumpBasedControlFlow) {
@@ -551,7 +564,7 @@ private fun ExtractionData.checkDeclarationsMovingOutOfScope(
}
if (declarationsOutOfScope.isNotEmpty()) {
val declStr = declarationsOutOfScope.map { it.getName()!! }.sort()
val declStr = declarationsOutOfScope.map { it.renderForMessage(bindingContext)!! }.sort()
return ErrorMessage.DECLARATIONS_OUT_OF_SCOPE.addAdditionalInfo(declStr)
}
@@ -614,7 +627,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
controlFlow.returnType.processTypeIfExtractable(paramsInfo.typeParameters, paramsInfo.nonDenotableTypes)
if (paramsInfo.nonDenotableTypes.isNotEmpty()) {
val typeStr = paramsInfo.nonDenotableTypes.map {DescriptorRenderer.HTML.renderType(it)}.sort()
val typeStr = paramsInfo.nonDenotableTypes.map {it.renderForMessage()}.sort()
return AnalysisResult(
null,
Status.CRITICAL_ERROR,
@@ -622,7 +635,7 @@ fun ExtractionData.performAnalysis(): AnalysisResult {
)
}
checkDeclarationsMovingOutOfScope(enclosingDeclaration!!, controlFlow)?.let { messages.add(it) }
checkDeclarationsMovingOutOfScope(enclosingDeclaration!!, controlFlow, bindingContext)?.let { messages.add(it) }
val functionNameValidator =
JetNameValidatorImpl(
@@ -689,9 +702,9 @@ fun ExtractionDescriptor.validate(): ExtractionDescriptorWithConflicts {
&& parameters.any { it.mirrorVarName == currentDescriptor.getName().asString() }) continue
if (diagnostics.any { it.getFactory() == Errors.UNRESOLVED_REFERENCE }
|| (currentDescriptor != null
&& !ErrorUtils.isError(currentDescriptor)
&& !comparePossiblyOverridingDescriptors(currentDescriptor, resolveResult.descriptor))) {
|| (currentDescriptor != null
&& !ErrorUtils.isError(currentDescriptor)
&& !comparePossiblyOverridingDescriptors(currentDescriptor, resolveResult.descriptor))) {
conflicts.putValue(
resolveResult.originalRefExpr,
JetRefactoringBundle.message(
@@ -2,4 +2,4 @@ prop += 1
prop2 +=2
prop + prop2
// RESULT: Cannot perform an action because this code fragment changes more than one variable: prop, prop2
// RESULT: Cannot perform an action because this code fragment changes more than one variable: var prop2: Int, var prop: Int
@@ -1,3 +1,3 @@
object {}
// RESULT: Cannot perform an action because following types are unavailable from debugger scope: errors.MyClass.baseFun.&lt;no name provided&gt;
// RESULT: Cannot perform an action because following types are unavailable from debugger scope: errors.MyClass.baseFun.<no name provided>
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: foo.A
Cannot extract method since following types are not denotable in the target scope: foo.A
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: foo.A
Cannot extract method since following types are not denotable in the target scope: foo.A
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: foo.A
Cannot extract method since following types are not denotable in the target scope: foo.A
@@ -1 +1 @@
Selected code fragment has multiple output values: b c
Selected code fragment has multiple output values: var b: Int var c: Int
@@ -1 +1 @@
Selected code fragment has multiple output values: b c
Selected code fragment has multiple output values: var b: Int var c: Int
@@ -1 +1 @@
Selected code fragment has multiple output values: b c
Selected code fragment has multiple output values: var b: Int var c: Int
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: foo.&lt;no name provided&gt;
Cannot extract method since following types are not denotable in the target scope: foo.&lt;no name provided&gt;
@@ -1 +1 @@
Selected code fragment has multiple output values: a b
Selected code fragment has multiple output values: val a: Int val b: Int
@@ -1 +1 @@
Following declarations won't be available outside of extracted function body: x
Following declarations won't be available outside of extracted function body: val x: Int
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: [ERROR : ]
Cannot extract method since following types are not denotable in the target scope: [ERROR : &lt;ERROR FUNCTION RETURN TYPE&gt;]
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: [ERROR : ]
Cannot extract method since following types are not denotable in the target scope: [ERROR : &lt;ERROR FUNCTION RETURN TYPE&gt;]
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: foo.&lt;no name provided&gt;
Cannot extract method since following types are not denotable in the target scope: foo.&lt;no name provided&gt;
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: foo.T
Cannot extract method since following types are not denotable in the target scope: foo.T
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: foo.X
Cannot extract method since following types are not denotable in the target scope: foo.X
@@ -1 +1 @@
Cannot extract method since following types are not denotable in the target scope: foo.X
Cannot extract method since following types are not denotable in the target scope: foo.X