diff --git a/idea/resources-en/inspectionDescriptions/IncompleteDestructuring.html b/idea/resources-en/inspectionDescriptions/IncompleteDestructuring.html
new file mode 100644
index 00000000000..d6b9ed10a96
--- /dev/null
+++ b/idea/resources-en/inspectionDescriptions/IncompleteDestructuring.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports incomplete destructuring declaration.
+
+
diff --git a/idea/resources-en/messages/KotlinBundle.properties b/idea/resources-en/messages/KotlinBundle.properties
index 001a1f51f14..0a605c9ac29 100644
--- a/idea/resources-en/messages/KotlinBundle.properties
+++ b/idea/resources-en/messages/KotlinBundle.properties
@@ -1926,6 +1926,8 @@ open.moved.members.in.editor=Open moved members in editor
to.fully.qualified.name=To (fully qualified name):
toggle.library.to.source.dependency.support=Toggle library to source dependency support
enable.components.for.library.to.source.analysis.in.kotlin=Enable components for analyzing libraries depending on project source files in Kotlin
+incomplete.destructuring.declaration.text=Incomplete destructuring declaration
+incomplete.destructuring.fix.family.name=Add missing variables to destructuring declaration
action.Kotlin.StopScratch.text=Stop Scratch Execution
action.Kotlin.StopScratch.description=Stop scratch execution
@@ -1984,6 +1986,7 @@ action.KotlinConsoleREPL.text=Kotlin REPL
action.LibraryToSourceDependencySupportToggleAction.text=Toggle library to source dependency support
inspection.unused.unary.operator.display.name=Unused unary operator
+inspection.incomplete.destructuring.declaration.display.name=Incomplete destructuring declaration
inspection.replace.guard.clause.with.function.call.display.name=Replace guard clause with kotlin's function call
inspection.lateinit.var.overrides.lateinit.var.display.name=lateinit var property overrides lateinit var property
inspection.kotlin.equals.between.inconvertible.types.display.name='equals()' between objects of inconvertible types
diff --git a/idea/resources/META-INF/inspections.xml b/idea/resources/META-INF/inspections.xml
index 1cb6775ab65..4f009feab37 100644
--- a/idea/resources/META-INF/inspections.xml
+++ b/idea/resources/META-INF/inspections.xml
@@ -2493,6 +2493,14 @@
language="kotlin"
key="inspection.unused.unary.operator.display.name" bundle="messages.KotlinBundle"/>
+
+
) : LocalQuickFix {
+ override fun getFamilyName() = KotlinBundle.message("incomplete.destructuring.fix.family.name")
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val element = descriptor.psiElement
+ val destructuringDeclaration = element.parent as? KtDestructuringDeclaration ?: return
+ if (destructuringDeclaration.entries.size >= primaryParameterNames.size) return
+
+ val namesToAdd =
+ primaryParameterNames.subList(destructuringDeclaration.entries.size, primaryParameterNames.size)
+ val names = destructuringDeclaration.entries.mapNotNull { it.name }.toMutableList() + namesToAdd
+ val joinedNames = names.joinToString()
+
+ val initializer = destructuringDeclaration.initializer ?: return
+ val factory = KtPsiFactory(destructuringDeclaration)
+
+ val newDestructuringDeclaration = factory.createDestructuringDeclarationByPattern(
+ if (destructuringDeclaration.isVar) "var ($0) = $1" else "val ($0) = $1",
+ joinedNames, initializer
+ )
+
+ element.parent.replace(newDestructuringDeclaration)
+ }
+}
diff --git a/idea/testData/inspectionsLocal/incompleteDestructuringInspection/.inspection b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/.inspection
new file mode 100644
index 00000000000..e35e7492a1b
--- /dev/null
+++ b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.IncompleteDestructuringInspection
diff --git a/idea/testData/inspectionsLocal/incompleteDestructuringInspection/basic.kt b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/basic.kt
new file mode 100644
index 00000000000..367e7da55c6
--- /dev/null
+++ b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/basic.kt
@@ -0,0 +1,6 @@
+data class Person(val name: String, val age: Int)
+
+fun test() {
+ val person = Person("", 0)
+ val (name) = person
+}
diff --git a/idea/testData/inspectionsLocal/incompleteDestructuringInspection/basic.kt.after b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/basic.kt.after
new file mode 100644
index 00000000000..bc563dddb9d
--- /dev/null
+++ b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/basic.kt.after
@@ -0,0 +1,6 @@
+data class Person(val name: String, val age: Int)
+
+fun test() {
+ val person = Person("", 0)
+ val (name, age) = person
+}
diff --git a/idea/testData/inspectionsLocal/incompleteDestructuringInspection/negative.kt b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/negative.kt
new file mode 100644
index 00000000000..25867255137
--- /dev/null
+++ b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/negative.kt
@@ -0,0 +1,7 @@
+// PROBLEM: none
+data class Person(val name: String, val age: Int)
+
+fun test() {
+ val person = Person("", 0)
+ val (name, age) = person
+}
diff --git a/idea/testData/inspectionsLocal/incompleteDestructuringInspection/underscore.kt b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/underscore.kt
new file mode 100644
index 00000000000..13aa45f100e
--- /dev/null
+++ b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/underscore.kt
@@ -0,0 +1,5 @@
+data class Person(val name: String, val age: Int, val location: String)
+
+fun test() {
+ val (_, age) = Person("", 0, "")
+}
diff --git a/idea/testData/inspectionsLocal/incompleteDestructuringInspection/underscore.kt.after b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/underscore.kt.after
new file mode 100644
index 00000000000..25e71d1da4a
--- /dev/null
+++ b/idea/testData/inspectionsLocal/incompleteDestructuringInspection/underscore.kt.after
@@ -0,0 +1,5 @@
+data class Person(val name: String, val age: Int, val location: String)
+
+fun test() {
+ val (_, age, location) = Person("", 0, "")
+}
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
index cb704c58fc8..7daec00a368 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -5139,6 +5139,34 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
}
}
+ @TestMetadata("idea/testData/inspectionsLocal/incompleteDestructuringInspection")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class IncompleteDestructuringInspection extends AbstractLocalInspectionTest {
+ private void runTest(String testDataFilePath) throws Exception {
+ KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
+ }
+
+ public void testAllFilesPresentInIncompleteDestructuringInspection() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("idea/testData/inspectionsLocal/incompleteDestructuringInspection"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), null, true);
+ }
+
+ @TestMetadata("basic.kt")
+ public void testBasic() throws Exception {
+ runTest("idea/testData/inspectionsLocal/incompleteDestructuringInspection/basic.kt");
+ }
+
+ @TestMetadata("negative.kt")
+ public void testNegative() throws Exception {
+ runTest("idea/testData/inspectionsLocal/incompleteDestructuringInspection/negative.kt");
+ }
+
+ @TestMetadata("underscore.kt")
+ public void testUnderscore() throws Exception {
+ runTest("idea/testData/inspectionsLocal/incompleteDestructuringInspection/underscore.kt");
+ }
+ }
+
@TestMetadata("idea/testData/inspectionsLocal/javaCollectionsStaticMethod")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)