Create From Usage: Generate Unit-typed declaration if result is unused

#KT-5903 Fixed
This commit is contained in:
Alexey Sedunov
2014-10-03 16:42:44 +04:00
parent a3a0edca58
commit 497e4fab79
9 changed files with 66 additions and 16 deletions
@@ -59,6 +59,8 @@ import org.jetbrains.jet.plugin.util.application.runWriteAction
import org.jetbrains.jet.plugin.refactoring.isMultiLine
import org.jetbrains.kotlin.util.printAndReturn
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
import org.jetbrains.jet.plugin.intentions.declarations.DeclarationUtils
import org.jetbrains.jet.plugin.intentions.SpecifyTypeExplicitlyAction
private val TYPE_PARAMETER_LIST_VARIABLE_NAME = "typeParameterList"
private val TEMPLATE_FROM_USAGE_FUNCTION_BODY = "New Kotlin Function Body.kt"
@@ -171,7 +173,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
}
private inner class Context {
val isUnit: Boolean
val skipReturnType: Boolean
val isExtension: Boolean
val containingFile: JetFile
val containingFileEditor: Editor
@@ -209,8 +211,6 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
containingFileEditor = config.currentEditor
}
isUnit = config.callableInfo.returnTypeInfo.let { it is TypeInfo.ByType && it.theType.isUnit() }
val scope = if (isExtension || receiverClassDescriptor == null) {
currentFileModule.getPackage(config.currentFile.getPackageFqName())!!.getMemberScope()
}
@@ -226,21 +226,22 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
val substitutions = ownerTypeArguments.zip(classTypeParameters).map {
JetTypeSubstitution(it.first.getType(), it.second.getType())
}.copyToArray()
config.callableInfo.parameterInfos.forEach {
parameter ->
computeTypeCandidates(parameter.typeInfo, substitutions, scope)
}
if (!isUnit) {
computeTypeCandidates(config.callableInfo.returnTypeInfo, substitutions, scope)
config.callableInfo.parameterInfos.forEach {
computeTypeCandidates(it.typeInfo, substitutions, scope)
}
val returnTypeCandidates = computeTypeCandidates(config.callableInfo.returnTypeInfo, substitutions, scope)
skipReturnType = config.callableInfo is FunctionInfo
&& returnTypeCandidates.size == 1
&& returnTypeCandidates.first().theType.isUnit()
// now that we have done substitutions, we can throw it away
receiverTypeCandidate = receiverType?.let { TypeCandidate(it, scope) }
// figure out type parameter renames to avoid conflicts
typeParameterNameMap = getTypeParameterRenames(scope)
config.callableInfo.parameterInfos.forEach { renderTypeCandidates(it.typeInfo, typeParameterNameMap) }
if (!isUnit) {
if (!skipReturnType) {
renderTypeCandidates(config.callableInfo.returnTypeInfo, typeParameterNameMap)
}
receiverTypeCandidate?.render(typeParameterNameMap)
@@ -268,7 +269,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
CallableKind.PROPERTY ->
""
}
val returnTypeString = if (isUnit || assignmentToReplace != null) "" else ": Any"
val returnTypeString = if (skipReturnType || assignmentToReplace != null) "" else ": Any"
val header = "$ownerTypeString${callableInfo.name}$paramList$returnTypeString"
val psiFactory = JetPsiFactory(currentFile)
@@ -366,7 +367,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
.flatMap { it.typeParameters.stream() }
.toCollection(allTypeParametersNotInScope)
if (!isUnit) {
if (!skipReturnType) {
computeTypeCandidates(config.callableInfo.returnTypeInfo).stream().flatMapTo(allTypeParametersNotInScope) { it.typeParameters.stream() }
}
@@ -432,7 +433,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
private fun setupFunctionBody(func: JetNamedFunction) {
val fileTemplate = FileTemplateManager.getInstance()!!.getCodeTemplate(TEMPLATE_FROM_USAGE_FUNCTION_BODY)
val properties = Properties()
properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, if (isUnit) "Unit" else func.getTypeReference()!!.getText())
properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, if (skipReturnType) "Unit" else func.getTypeReference()!!.getText())
receiverClassDescriptor?.let {
properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, DescriptorUtils.getFqName(it).asString())
properties.setProperty(FileTemplate.ATTRIBUTE_SIMPLE_CLASS_NAME, it.getName().asString())
@@ -554,7 +555,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
if (declaration is JetProperty) {
setupValVarTemplate(builder, declaration)
}
val returnTypeExpression = if (isUnit) null else setupReturnTypeTemplate(builder, declaration)
val returnTypeExpression = if (skipReturnType) null else setupReturnTypeTemplate(builder, declaration)
val parameterTypeExpressions =
setupParameterTypeTemplates(builder, declaration.getValueParameterList()?.getParameters() ?: Collections.emptyList())
@@ -581,6 +582,8 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
// run the template
TemplateManager.getInstance(project).startTemplate(containingFileEditor, templateImpl, object : TemplateEditingAdapter() {
override fun templateFinished(template: Template?, brokenOff: Boolean) {
PsiDocumentManager.getInstance(project).commitDocument(containingFileEditor.getDocument())
// file templates
val offset = templateImpl.getSegmentOffset(0)
val newDeclaration = PsiTreeUtil.findElementOfClassAtOffset(
@@ -23,6 +23,9 @@ import org.jetbrains.jet.lang.types.Variance
import org.jetbrains.jet.lang.types.TypeProjectionImpl
import org.jetbrains.jet.lang.types.JetTypeImpl
import org.jetbrains.jet.lang.psi.psiUtil.getAssignmentByLHS
import org.jetbrains.jet.lang.resolve.bindingContextUtil.isUsedAsStatement
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns
import org.jetbrains.jet.lang.psi.JetDeclaration
private fun JetType.contains(inner: JetType): Boolean {
return JetTypeChecker.DEFAULT.equalTypes(this, inner) || getArguments().any { inner in it.getType() }
@@ -72,6 +75,8 @@ fun JetType.getTypeParameters(): Set<TypeParameterDescriptor> {
}
fun JetExpression.guessTypes(context: BindingContext): Array<JetType> {
if (this !is JetDeclaration && isUsedAsStatement(context)) return array(KotlinBuiltIns.getInstance().getUnitType())
// if we know the actual type of the expression
val theType1 = context[BindingContext.EXPRESSION_TYPE, this]
if (theType1 != null) {
@@ -1,7 +1,7 @@
// "Create function 'plusAssign' from usage" "true"
class A<T>(val n: T) {
fun plusAssign(arg: T): Unit {
fun plusAssign(arg: T) {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
@@ -0,0 +1,9 @@
// "Create function 'foo' from usage" "true"
fun test() {
foo(2, "2")
}
fun foo(i: Int, s: String) {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -2,7 +2,7 @@
// ERROR: Unresolved reference: B
class A: B {
fun foo(): Any {
fun foo() {
throw UnsupportedOperationException("not implemented") //To change body of created functions use File | Settings | File Templates.
}
@@ -0,0 +1,5 @@
// "Create function 'foo' from usage" "true"
fun test() {
<caret>foo(2, "2")
}
@@ -0,0 +1,9 @@
// "Create local variable 'foo'" "true"
// ACTION: Create parameter 'foo'
// ERROR: Variable 'foo' must be initialized
fun test() {
val foo: Unit
val u: Unit = foo
}
@@ -0,0 +1,7 @@
// "Create local variable 'foo'" "true"
// ACTION: Create parameter 'foo'
// ERROR: Variable 'foo' must be initialized
fun test() {
val u: Unit = <caret>foo
}
@@ -880,6 +880,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName);
}
@TestMetadata("beforeUnitFun.kt")
public void testUnitFun() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeUnitFun.kt");
doTest(fileName);
}
@TestMetadata("beforeUnknownType.kt")
public void testUnknownType() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/call/beforeUnknownType.kt");
@@ -1269,6 +1275,12 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName);
}
@TestMetadata("beforeUnitTypedInFun.kt")
public void testUnitTypedInFun() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/localVariable/beforeUnitTypedInFun.kt");
doTest(fileName);
}
}
@TestMetadata("idea/testData/quickfix/createFromUsage/createVariable/parameter")