PSI: Make JetPostfixExpression.getBaseExpression() nullable
#KT-7761 Fixed
This commit is contained in:
+1
-1
@@ -223,7 +223,7 @@ public class JavaNullabilityWarningsChecker : AdditionalTypeChecker {
|
|||||||
when (expression) {
|
when (expression) {
|
||||||
is JetPostfixExpression ->
|
is JetPostfixExpression ->
|
||||||
if (expression.getOperationToken() == JetTokens.EXCLEXCL) {
|
if (expression.getOperationToken() == JetTokens.EXCLEXCL) {
|
||||||
val baseExpression = expression.getBaseExpression()
|
val baseExpression = expression.getBaseExpression() ?: return
|
||||||
val baseExpressionType = c.trace.getType(baseExpression) ?: return
|
val baseExpressionType = c.trace.getType(baseExpression) ?: return
|
||||||
doIfNotNull(
|
doIfNotNull(
|
||||||
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c),
|
DataFlowValueFactory.createDataFlowValue(baseExpression, baseExpressionType, c),
|
||||||
|
|||||||
@@ -17,7 +17,9 @@
|
|||||||
package org.jetbrains.kotlin.psi;
|
package org.jetbrains.kotlin.psi;
|
||||||
|
|
||||||
import com.intellij.lang.ASTNode;
|
import com.intellij.lang.ASTNode;
|
||||||
|
import com.intellij.psi.util.PsiTreeUtil;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class JetPostfixExpression extends JetUnaryExpression {
|
public class JetPostfixExpression extends JetUnaryExpression {
|
||||||
public JetPostfixExpression(@NotNull ASTNode node) {
|
public JetPostfixExpression(@NotNull ASTNode node) {
|
||||||
@@ -25,11 +27,9 @@ public class JetPostfixExpression extends JetUnaryExpression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@Nullable @IfNotParsed
|
||||||
public JetExpression getBaseExpression() {
|
public JetExpression getBaseExpression() {
|
||||||
JetExpression answer = findChildByClass(JetExpression.class);
|
return PsiTreeUtil.getPrevSiblingOfType(getOperationReference(), JetExpression.class);
|
||||||
assert answer != null;
|
|
||||||
return answer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
== UnitIncDec ==
|
||||||
|
class UnitIncDec() {
|
||||||
|
fun inc() : Unit {}
|
||||||
|
fun dec() : Unit {}
|
||||||
|
}
|
||||||
|
---------------------
|
||||||
|
L0:
|
||||||
|
1 <START>
|
||||||
|
L1:
|
||||||
|
<END> NEXT:[<SINK>]
|
||||||
|
error:
|
||||||
|
<ERROR> PREV:[]
|
||||||
|
sink:
|
||||||
|
<SINK> PREV:[<ERROR>, <END>]
|
||||||
|
=====================
|
||||||
|
== inc ==
|
||||||
|
fun inc() : Unit {}
|
||||||
|
---------------------
|
||||||
|
L0:
|
||||||
|
1 <START>
|
||||||
|
2 mark({})
|
||||||
|
read (Unit)
|
||||||
|
L1:
|
||||||
|
1 <END> NEXT:[<SINK>]
|
||||||
|
error:
|
||||||
|
<ERROR> PREV:[]
|
||||||
|
sink:
|
||||||
|
<SINK> PREV:[<ERROR>, <END>]
|
||||||
|
=====================
|
||||||
|
== dec ==
|
||||||
|
fun dec() : Unit {}
|
||||||
|
---------------------
|
||||||
|
L0:
|
||||||
|
1 <START>
|
||||||
|
2 mark({})
|
||||||
|
read (Unit)
|
||||||
|
L1:
|
||||||
|
1 <END> NEXT:[<SINK>]
|
||||||
|
error:
|
||||||
|
<ERROR> PREV:[]
|
||||||
|
sink:
|
||||||
|
<SINK> PREV:[<ERROR>, <END>]
|
||||||
|
=====================
|
||||||
|
== testUnitIncDec ==
|
||||||
|
fun testUnitIncDec() {
|
||||||
|
var x = UnitIncDec()
|
||||||
|
x = <warning>x<error>++</error></warning>
|
||||||
|
}
|
||||||
|
---------------------
|
||||||
|
L0:
|
||||||
|
1 <START>
|
||||||
|
2 mark({ var x = UnitIncDec() x = <warning>x<error>++</error></warning> })
|
||||||
|
v(var x = UnitIncDec())
|
||||||
|
mark(UnitIncDec())
|
||||||
|
call(UnitIncDec(), <init>) -> <v0>
|
||||||
|
w(x|<v0>)
|
||||||
|
mark(<warning>)
|
||||||
|
magic[UNRESOLVED_CALL](<warning>) -> <v1>
|
||||||
|
mark(<warning>x<)
|
||||||
|
call(<warning>x<, <ERROR FUNCTION>|<v1>) -> <v2>
|
||||||
|
magic[VALUE_CONSUMER](>++|!<v3>) -> <v4>
|
||||||
|
mark(<warning>x<error>++)
|
||||||
|
call(<warning>x<error>++, <ERROR FUNCTION>|<v2>) -> <v5>
|
||||||
|
mark(/error>)
|
||||||
|
magic[UNRESOLVED_CALL](/error>) -> <v6>
|
||||||
|
magic[VALUE_CONSUMER](/error>|<v6>) -> <v7>
|
||||||
|
mark(<warning>x<error>++</error>)
|
||||||
|
call(<warning>x<error>++</error>, <ERROR FUNCTION>|<v5>) -> <v8>
|
||||||
|
mark(/warning>)
|
||||||
|
magic[UNRESOLVED_CALL](/warning>) -> <v9>
|
||||||
|
magic[VALUE_CONSUMER](/warning>|<v9>) -> <v10>
|
||||||
|
mark(<warning>x<error>++</error></warning>)
|
||||||
|
call(<warning>x<error>++</error></warning>, <ERROR FUNCTION>|<v8>) -> <v11>
|
||||||
|
w(x|<v11>)
|
||||||
|
L1:
|
||||||
|
1 <END> NEXT:[<SINK>]
|
||||||
|
error:
|
||||||
|
<ERROR> PREV:[]
|
||||||
|
sink:
|
||||||
|
<SINK> PREV:[<ERROR>, <END>]
|
||||||
|
=====================
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
class UnitIncDec() {
|
||||||
|
fun inc() : Unit {}
|
||||||
|
fun dec() : Unit {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun testUnitIncDec() {
|
||||||
|
var x = UnitIncDec()
|
||||||
|
x = <warning>x<error>++</error></warning>
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
== UnitIncDec ==
|
||||||
|
class UnitIncDec() {
|
||||||
|
fun inc() : Unit {}
|
||||||
|
fun dec() : Unit {}
|
||||||
|
}
|
||||||
|
---------------------
|
||||||
|
=====================
|
||||||
|
== inc ==
|
||||||
|
fun inc() : Unit {}
|
||||||
|
---------------------
|
||||||
|
=====================
|
||||||
|
== dec ==
|
||||||
|
fun dec() : Unit {}
|
||||||
|
---------------------
|
||||||
|
=====================
|
||||||
|
== testUnitIncDec ==
|
||||||
|
fun testUnitIncDec() {
|
||||||
|
var x = UnitIncDec()
|
||||||
|
x = <warning>x<error>++</error></warning>
|
||||||
|
}
|
||||||
|
---------------------
|
||||||
|
<v10>: * NEW: magic[VALUE_CONSUMER](/warning>|<v9>) -> <v10>
|
||||||
|
<v4>: * NEW: magic[VALUE_CONSUMER](>++|!<v3>) -> <v4>
|
||||||
|
<v7>: * NEW: magic[VALUE_CONSUMER](/error>|<v6>) -> <v7>
|
||||||
|
UnitIncDec() <v0>: UnitIncDec NEW: call(UnitIncDec(), <init>) -> <v0>
|
||||||
|
<warning> <v1>: * NEW: magic[UNRESOLVED_CALL](<warning>) -> <v1>
|
||||||
|
<warning>x< <v2>: * NEW: call(<warning>x<, <ERROR FUNCTION>|<v1>) -> <v2>
|
||||||
|
>++ !<v3>: *
|
||||||
|
<warning>x<error>++ <v5>: * NEW: call(<warning>x<error>++, <ERROR FUNCTION>|<v2>) -> <v5>
|
||||||
|
/error> <v6>: * NEW: magic[UNRESOLVED_CALL](/error>) -> <v6>
|
||||||
|
<warning>x<error>++</error> <v8>: * NEW: call(<warning>x<error>++</error>, <ERROR FUNCTION>|<v5>) -> <v8>
|
||||||
|
/warning> <v9>: * NEW: magic[UNRESOLVED_CALL](/warning>) -> <v9>
|
||||||
|
<warning>x<error>++</error></warning> <v11>: UnitIncDec NEW: call(<warning>x<error>++</error></warning>, <ERROR FUNCTION>|<v8>) -> <v11>
|
||||||
|
x = <warning>x<error>++</error></warning> !<v12>: *
|
||||||
|
{ var x = UnitIncDec() x = <warning>x<error>++</error></warning> } !<v12>: * COPY
|
||||||
|
=====================
|
||||||
@@ -144,6 +144,12 @@ public class ControlFlowTestGenerated extends AbstractControlFlowTest {
|
|||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/bugs/jumpToOuterScope.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/bugs/jumpToOuterScope.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt7761.kt")
|
||||||
|
public void testKt7761() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/bugs/kt7761.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/cfg/controlStructures")
|
@TestMetadata("compiler/testData/cfg/controlStructures")
|
||||||
|
|||||||
@@ -146,6 +146,12 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest {
|
|||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/bugs/jumpToOuterScope.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/bugs/jumpToOuterScope.kt");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kt7761.kt")
|
||||||
|
public void testKt7761() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/cfg/bugs/kt7761.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("compiler/testData/cfg/controlStructures")
|
@TestMetadata("compiler/testData/cfg/controlStructures")
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ class PartialBodyResolveFilter(
|
|||||||
expression.acceptChildren(this)
|
expression.acceptChildren(this)
|
||||||
|
|
||||||
if (expression.getOperationToken() == JetTokens.EXCLEXCL) {
|
if (expression.getOperationToken() == JetTokens.EXCLEXCL) {
|
||||||
addIfCanBeSmartCast(expression.getBaseExpression())
|
addIfCanBeSmartCast(expression.getBaseExpression() ?: return)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -40,6 +40,7 @@ public class OperatorToFunctionIntention : JetSelfTargetingIntention<JetExpressi
|
|||||||
private fun isApplicablePostfix(element: JetPostfixExpression, caretOffset: Int): Boolean {
|
private fun isApplicablePostfix(element: JetPostfixExpression, caretOffset: Int): Boolean {
|
||||||
val opRef = element.getOperationReference()
|
val opRef = element.getOperationReference()
|
||||||
if (!opRef.getTextRange().containsOffset(caretOffset)) return false
|
if (!opRef.getTextRange().containsOffset(caretOffset)) return false
|
||||||
|
if (element.getBaseExpression() == null) return false
|
||||||
return when (opRef.getReferencedNameElementType()) {
|
return when (opRef.getReferencedNameElementType()) {
|
||||||
JetTokens.PLUSPLUS, JetTokens.MINUSMINUS -> true
|
JetTokens.PLUSPLUS, JetTokens.MINUSMINUS -> true
|
||||||
else -> false
|
else -> false
|
||||||
@@ -98,7 +99,7 @@ public class OperatorToFunctionIntention : JetSelfTargetingIntention<JetExpressi
|
|||||||
|
|
||||||
private fun convertPostFix(element: JetPostfixExpression): JetExpression {
|
private fun convertPostFix(element: JetPostfixExpression): JetExpression {
|
||||||
val op = element.getOperationReference().getReferencedNameElementType()
|
val op = element.getOperationReference().getReferencedNameElementType()
|
||||||
val base = element.getBaseExpression().getText()
|
val base = element.getBaseExpression()!!.getText()
|
||||||
|
|
||||||
val call = when (op) {
|
val call = when (op) {
|
||||||
JetTokens.PLUSPLUS -> "inc()"
|
JetTokens.PLUSPLUS -> "inc()"
|
||||||
|
|||||||
@@ -224,7 +224,12 @@ public class JetNameSuggester {
|
|||||||
return matcher.replaceAll("");
|
return matcher.replaceAll("");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addNamesForExpression(final ArrayList<String> result, JetExpression expression, final JetNameValidator validator) {
|
private static void addNamesForExpression(
|
||||||
|
final ArrayList<String> result,
|
||||||
|
@Nullable JetExpression expression,
|
||||||
|
final JetNameValidator validator) {
|
||||||
|
if (expression == null) return;
|
||||||
|
|
||||||
expression.accept(new JetVisitorVoid() {
|
expression.accept(new JetVisitorVoid() {
|
||||||
@Override
|
@Override
|
||||||
public void visitQualifiedExpression(@NotNull JetQualifiedExpression expression) {
|
public void visitQualifiedExpression(@NotNull JetQualifiedExpression expression) {
|
||||||
|
|||||||
+5
-2
@@ -37,11 +37,14 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement
|
|||||||
|
|
||||||
public class DoubleBangToIfThenIntention : JetSelfTargetingRangeIntention<JetPostfixExpression>(javaClass(), "Replace '!!' expression with 'if' expression") {
|
public class DoubleBangToIfThenIntention : JetSelfTargetingRangeIntention<JetPostfixExpression>(javaClass(), "Replace '!!' expression with 'if' expression") {
|
||||||
override fun applicabilityRange(element: JetPostfixExpression): TextRange? {
|
override fun applicabilityRange(element: JetPostfixExpression): TextRange? {
|
||||||
return if (element.getOperationToken() == JetTokens.EXCLEXCL) element.getOperationReference().getTextRange() else null
|
return if (element.getOperationToken() == JetTokens.EXCLEXCL && element.getBaseExpression() != null)
|
||||||
|
element.getOperationReference().getTextRange()
|
||||||
|
else
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun applyTo(element: JetPostfixExpression, editor: Editor) {
|
override fun applyTo(element: JetPostfixExpression, editor: Editor) {
|
||||||
val base = JetPsiUtil.safeDeparenthesize(element.getBaseExpression())
|
val base = JetPsiUtil.safeDeparenthesize(element.getBaseExpression()!!)
|
||||||
val expressionText = formatForUseInExceptionArgument(base.getText()!!)
|
val expressionText = formatForUseInExceptionArgument(base.getText()!!)
|
||||||
|
|
||||||
val defaultException = JetPsiFactory(element).createExpression("throw NullPointerException()")
|
val defaultException = JetPsiFactory(element).createExpression("throw NullPointerException()")
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ public class ExclExclCallFix implements IntentionAction {
|
|||||||
PsiElement parent = exclExclElement.getParent();
|
PsiElement parent = exclExclElement.getParent();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
PsiElement operationParent = parent.getParent();
|
PsiElement operationParent = parent.getParent();
|
||||||
if (operationParent instanceof JetPostfixExpression) {
|
if (operationParent instanceof JetPostfixExpression && ((JetPostfixExpression) operationParent).getBaseExpression() != null) {
|
||||||
return (JetPostfixExpression) operationParent;
|
return (JetPostfixExpression) operationParent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user