Create From Usage: Generate Unit-typed declaration if result is unused
#KT-5903 Fixed
This commit is contained in:
+17
-14
@@ -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(
|
||||
|
||||
+5
@@ -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
-1
@@ -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.
|
||||
}
|
||||
+1
-1
@@ -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")
|
||||
}
|
||||
+9
@@ -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
|
||||
}
|
||||
+7
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user