@@ -717,11 +717,16 @@ class Resolver(
717
717
* Constructs a type for the addr.
718
718
*
719
719
* There are three options here:
720
+ *
720
721
* * it successfully constructs a type suitable with defaultType and returns it;
722
+ *
721
723
* * it constructs a type that cannot be assigned in a variable with the [defaultType] and we `touched`
722
- * the [addr] during the analysis. In such case the method returns [defaultType] as a result
723
- * if the constructed type is not an array, and in case of array it returns the [defaultType] if it has the same
724
- * dimensions as the constructed type and its ancestors includes the constructed type, or null otherwise;
724
+ * the [addr] during the analysis. In such case we have only two options: either this object was aliased with
725
+ * an object of another type (by solver's decision) and we didn't touch it in fact, or it happened because
726
+ * of missed connection between an array type and types of its elements. For example, we might case
727
+ * array to a succ type after we `touched` it's element. In the first scenario null will be returns, in the
728
+ * second one -- a default type (that will be a subtype of actualType).
729
+ *
725
730
* * it constructs a type that cannot be assigned in a variable with the [defaultType] and we did **not** `touched`
726
731
* the [addr] during the analysis. It means we can create [UtNullModel] to represent such element. In such case
727
732
* the method returns null as the result.
@@ -816,19 +821,35 @@ class Resolver(
816
821
// as const or store model.
817
822
if (defaultBaseType is PrimType ) return null
818
823
824
+ // There is no way you have java.lang.Object as a defaultType here, since it'd mean that
825
+ // some actualType is not an inheritor of it
819
826
require(! defaultType.isJavaLangObject()) {
820
827
" Object type $defaultType is unexpected in fallback to default type"
821
828
}
822
829
823
- if (defaultType.numDimensions == 0 ) {
824
- return defaultType
830
+ // It might happen only if we have a wrong aliasing here: some object has not been touched
831
+ // during the execution, and solver returned for him an address of already existed object
832
+ // with another type. In such case `UtNullModel` should be constructed.
833
+ if (defaultBaseType.isJavaLangObject() && actualType.numDimensions < defaultType.numDimensions) {
834
+ return null
835
+ }
836
+
837
+ // All cases with `java.lang.Object` as default base type should have been already processed
838
+ require(! defaultBaseType.isJavaLangObject()) {
839
+ " Unexpected `java.lang.Object` as a default base type"
825
840
}
826
841
827
842
val actualBaseType = actualType.baseType
828
843
829
844
require(actualBaseType is RefType ) { " Expected RefType, but $actualBaseType found" }
830
845
require(defaultBaseType is RefType ) { " Expected RefType, but $defaultBaseType found" }
831
846
847
+
848
+ // The same idea about fake aliasing. It might happen only if there have been an aliasing
849
+ // because of the solver's decision. In fact an object for which we construct type has not been
850
+ // touched during analysis.
851
+ if (actualType.numDimensions != defaultType.numDimensions) return null
852
+
832
853
val ancestors = typeResolver.findOrConstructAncestorsIncludingTypes(defaultBaseType)
833
854
834
855
// This is intended to fix a specific problem. We have code:
@@ -840,7 +861,27 @@ class Resolver(
840
861
// when the array is ColoredPoint[], but the first element of it got type Point from the solver.
841
862
// In such case here we'll have ColoredPoint as defaultType and Point as actualType. It is obvious from the example
842
863
// that we can construct ColoredPoint instance instead of it with randomly filled colored-specific fields.
843
- return defaultType.takeIf { actualBaseType in ancestors && actualType.numDimensions == defaultType.numDimensions }
864
+ // Note that it won't solve a problem when this `array[0]` has already been constructed somewhere above,
865
+ // since a model for it is already presented in cache and will be taken by addr from it.
866
+ // TODO corresponding issue https://github.com/UnitTestBot/UTBotJava/issues/1232
867
+ if (actualBaseType in ancestors) return defaultType
868
+
869
+ val inheritors = typeResolver.findOrConstructInheritorsIncludingTypes(defaultBaseType)
870
+
871
+ // If we have an actual type that is not a subclass of defaultBaseType and isTouched is true,
872
+ // it means that we have encountered unexpected aliasing between an object for which we resolve its type
873
+ // and some object in the system. The reason is `isTouched` means that we processed this object
874
+ // during the analysis, therefore we create correct type constraints for it. Since we have
875
+ // inappropriate actualType here, these constraints were supposed to define type for another object,
876
+ // and, in fact, we didn't touch our object.
877
+ // For example, we have an array of two elements: Integer[] = new Integer[2], and this instance: ThisClass.
878
+ // During the execution we `touched` only the first element of the array, and we know its type.
879
+ // During resolving we found that second element of the array has set in true `isTouched` field,
880
+ // and its actualType is `ThisClass`. So, we have wrong aliasing here and can return `null` to construct
881
+ // UtNullModel instead.
882
+ if (actualBaseType !in inheritors) return null
883
+
884
+ return null
844
885
}
845
886
846
887
/* *
0 commit comments