Smart completion: auto-cast to non-null type is not recognized for field from constructor

#KT-4906 Fixed
This commit is contained in:
Valentin Kipyatkov
2014-04-29 20:52:33 +04:00
parent 3c75e9ec28
commit 7aa11a4207
10 changed files with 75 additions and 44 deletions
@@ -34,6 +34,7 @@ import com.google.common.collect.SetMultimap
import org.jetbrains.jet.lang.resolve.calls.autocasts.Nullability
import java.util.HashSet
import org.jetbrains.jet.lang.resolve.BindingContext
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver
class TypesWithAutoCasts(val bindingContext: BindingContext) {
public fun calculate(expression: JetExpression, receiver: JetExpression?): (DeclarationDescriptor) -> Iterable<JetType> {
@@ -77,43 +78,43 @@ class TypesWithAutoCasts(val bindingContext: BindingContext) {
)
private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo?, receiver: JetExpression?): ProcessDataFlowInfoResult {
if (dataFlowInfo != null) {
val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor?
if (receiver != null) {
val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver]
if (receiverType != null) {
val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext).getId()
dataFlowValueToVariable = {(value) ->
val id = value.getId()
if (id is com.intellij.openapi.util.Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null
}
}
else {
return ProcessDataFlowInfoResult()
if (dataFlowInfo == null) return ProcessDataFlowInfoResult()
val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor?
if (receiver != null) {
val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver] ?: return ProcessDataFlowInfoResult()
val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext).getId()
dataFlowValueToVariable = {(value) ->
val id = value.getId()
if (id is com.intellij.openapi.util.Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null
}
}
else {
dataFlowValueToVariable = {(value) ->
val id = value.getId()
when {
id is VariableDescriptor -> id
id is com.intellij.openapi.util.Pair<*, *> && id.first is ThisReceiver -> id.second as? VariableDescriptor
else -> null
}
}
else {
dataFlowValueToVariable = {(value) -> value.getId() as? VariableDescriptor }
}
val variableToType = HashMap<VariableDescriptor, Collection<JetType>>()
val typeInfo: SetMultimap<DataFlowValue, JetType> = dataFlowInfo.getCompleteTypeInfo()
for ((dataFlowValue, types) in typeInfo.asMap().entrySet()) {
val variable = dataFlowValueToVariable.invoke(dataFlowValue)
if (variable != null) {
variableToType[variable] = types
}
}
val nullabilityInfo: Map<DataFlowValue, Nullability> = dataFlowInfo.getCompleteNullabilityInfo()
val notNullVariables = nullabilityInfo
.filter { it.getValue() == Nullability.NOT_NULL }
.map { dataFlowValueToVariable(it.getKey()) }
.filterNotNullTo(HashSet<VariableDescriptor>())
return ProcessDataFlowInfoResult(variableToType, notNullVariables)
}
return ProcessDataFlowInfoResult()
val variableToType = HashMap<VariableDescriptor, Collection<JetType>>()
val typeInfo: SetMultimap<DataFlowValue, JetType> = dataFlowInfo.getCompleteTypeInfo()
for ((dataFlowValue, types) in typeInfo.asMap().entrySet()) {
val variable = dataFlowValueToVariable.invoke(dataFlowValue)
if (variable != null) {
variableToType[variable] = types
}
}
val nullabilityInfo: Map<DataFlowValue, Nullability> = dataFlowInfo.getCompleteNullabilityInfo()
val notNullVariables = nullabilityInfo
.filter { it.getValue() == Nullability.NOT_NULL }
.map { dataFlowValueToVariable(it.getKey()) }
.filterNotNullTo(HashSet<VariableDescriptor>())
return ProcessDataFlowInfoResult(variableToType, notNullVariables)
}
}
@@ -9,4 +9,4 @@ open class Foo{
class Bar : Foo
// EXIST: this
// EXIST: { itemText:"this" }
@@ -4,4 +4,4 @@ fun f(p: Any) {
}
}
// EXIST: p
// EXIST: { itemText:"p" }
@@ -6,5 +6,5 @@ class Foo(val prop1 : Any, val prop2 : Any){
}
}
// EXIST: prop1
// ABSENT: prop2
// EXIST: { itemText:"prop1" }
// ABSENT: { itemText:"prop2" }
@@ -5,4 +5,4 @@ fun String?.foo(){
}
// EXIST: this
// EXIST: { itemText:"this" }
@@ -4,4 +4,4 @@ fun f(p: String?) {
}
}
// EXIST: p
// EXIST: { itemText:"p" }
@@ -0,0 +1,9 @@
class Foo(val prop : String?){
fun f(foo: Foo) {
if (foo.prop != null){
var a: String = <caret>
}
}
}
// ABSENT: { itemText:"prop" }
@@ -0,0 +1,11 @@
class Foo(val s: String?) {
fun bar(){
if (s != null) {
foo(<caret>)
}
}
}
fun foo(s: String){}
// EXIST: { itemText:"s" }
@@ -6,7 +6,7 @@ class Foo(val prop1 : String?, val prop2 : String?){
}
}
// EXIST: prop1
// ABSENT: { lookupString:"prop2", itemText:"prop2" }
// EXIST: { lookupString:"prop2", itemText:"!! prop2" }
// EXIST: { lookupString:"prop2", itemText:"?: prop2" }
// EXIST: { itemText:"prop1" }
// ABSENT: { itemText:"prop2" }
// EXIST: { itemText:"!! prop2" }
// EXIST: { itemText:"?: prop2" }
@@ -71,6 +71,16 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT
doTest("idea/testData/completion/smart/AutoNotNullType.kt");
}
@TestMetadata("AutoNotNullType2.kt")
public void testAutoNotNullType2() throws Exception {
doTest("idea/testData/completion/smart/AutoNotNullType2.kt");
}
@TestMetadata("AutoNotNullTypeForConstructorParameter.kt")
public void testAutoNotNullTypeForConstructorParameter() throws Exception {
doTest("idea/testData/completion/smart/AutoNotNullTypeForConstructorParameter.kt");
}
@TestMetadata("AutoNotNullTypeWithQualifier.kt")
public void testAutoNotNullTypeWithQualifier() throws Exception {
doTest("idea/testData/completion/smart/AutoNotNullTypeWithQualifier.kt");