@@ -717,11 +717,16 @@ class Resolver(
717717 * Constructs a type for the addr.
718718 *
719719 * There are three options here:
720+ *
720721 * * it successfully constructs a type suitable with defaultType and returns it;
722+ *
721723 * * 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+ *
725730 * * it constructs a type that cannot be assigned in a variable with the [defaultType] and we did **not** `touched`
726731 * the [addr] during the analysis. It means we can create [UtNullModel] to represent such element. In such case
727732 * the method returns null as the result.
@@ -816,19 +821,35 @@ class Resolver(
816821 // as const or store model.
817822 if (defaultBaseType is PrimType ) return null
818823
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
819826 require(! defaultType.isJavaLangObject()) {
820827 " Object type $defaultType is unexpected in fallback to default type"
821828 }
822829
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"
825840 }
826841
827842 val actualBaseType = actualType.baseType
828843
829844 require(actualBaseType is RefType ) { " Expected RefType, but $actualBaseType found" }
830845 require(defaultBaseType is RefType ) { " Expected RefType, but $defaultBaseType found" }
831846
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+
832853 val ancestors = typeResolver.findOrConstructAncestorsIncludingTypes(defaultBaseType)
833854
834855 // This is intended to fix a specific problem. We have code:
@@ -840,7 +861,27 @@ class Resolver(
840861 // when the array is ColoredPoint[], but the first element of it got type Point from the solver.
841862 // In such case here we'll have ColoredPoint as defaultType and Point as actualType. It is obvious from the example
842863 // 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
844885 }
845886
846887 /* *
0 commit comments