@@ -162,7 +162,7 @@ private static class InstanceOfPatternReplacements {
162162 private final Map <J .InstanceOf , Set <J >> contexts = new HashMap <>();
163163 private final Map <J .InstanceOf , Set <Cursor >> contextScopes = new HashMap <>();
164164 private final Map <J .TypeCast , J .InstanceOf > replacements = new HashMap <>();
165- private final Map <J .InstanceOf , VariableAndTypeTree > variablesToDelete = new HashMap <>();
165+ private final Map <J .InstanceOf , List < VariableAndTypeTree > > variablesToDelete = new HashMap <>();
166166
167167 public void registerInstanceOf (J .InstanceOf instanceOf , Set <J > contexts ) {
168168 Expression expression = instanceOf .getExpression ();
@@ -201,9 +201,11 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) {
201201 if (isAcceptableTypeCast (typeCast .getType ()) && isTheSameAsOtherTypeCasts (typeCast , instanceOf ) && isAcceptableParentTypeCast (parent ) &&
202202 cursor .firstEnclosing (J .Try .Resource .class ) == null ) {
203203 if (parent .getValue () instanceof J .VariableDeclarations .NamedVariable &&
204- !variablesToDelete .containsKey (instanceOf )) {
205- variablesToDelete .put (instanceOf , new VariableAndTypeTree (parent .getValue (),
206- requireNonNull (requireNonNull (parent .firstEnclosing (J .VariableDeclarations .class )).getTypeExpression ())));
204+ shouldDeleteVariableDeclaration (parent , instanceOf )) {
205+ variablesToDelete
206+ .computeIfAbsent (instanceOf , i -> new ArrayList <>())
207+ .add (new VariableAndTypeTree (parent .getValue (),
208+ requireNonNull (requireNonNull (parent .firstEnclosing (J .VariableDeclarations .class )).getTypeExpression ())));
207209 } else {
208210 replacements .put (typeCast , instanceOf );
209211 }
@@ -284,6 +286,21 @@ public boolean isEmpty() {
284286 return replacements .isEmpty () && variablesToDelete .isEmpty ();
285287 }
286288
289+ private boolean shouldDeleteVariableDeclaration (Cursor parent , J .InstanceOf instanceOf ) {
290+ J .VariableDeclarations .NamedVariable namedVariable = parent .getValue ();
291+ TypeTree typeExpression = requireNonNull (requireNonNull (parent .firstEnclosing (J .VariableDeclarations .class )).getTypeExpression ());
292+ List <VariableAndTypeTree > existingVariables = variablesToDelete .get (instanceOf );
293+ if (existingVariables == null || existingVariables .isEmpty ()) {
294+ return true ;
295+ }
296+ VariableAndTypeTree primaryVariable = existingVariables .get (0 );
297+ JavaType currentType = typeExpression .getType ();
298+ JavaType primaryType = primaryVariable .getType ().getType ();
299+ return namedVariable .getSimpleName ().equals (primaryVariable .getVariable ().getSimpleName ()) &&
300+ ((currentType != null && primaryType != null && hasSameRawType (currentType , primaryType )) ||
301+ SemanticallyEqual .areEqual (typeExpression , primaryVariable .getType ()));
302+ }
303+
287304 public J .InstanceOf processInstanceOf (J .InstanceOf instanceOf , Cursor cursor , Set <String > usedNames ) {
288305 if (!contextScopes .containsKey (instanceOf )) {
289306 return instanceOf ;
@@ -320,27 +337,22 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor, Se
320337 }
321338
322339 private TypeTree computeTypeTreeFromTypeCasts (J .InstanceOf instanceOf ) {
323- TypeTree typeCastTypeTree = replacements
324- .entrySet ()
325- .stream ()
326- .filter (e -> e .getValue () == instanceOf )
327- .findFirst ()
328- .map (e -> e .getKey ().getClazz ().getTree ())
329- .orElse (null );
330- if (typeCastTypeTree == null ) {
331- VariableAndTypeTree variable = variablesToDelete .get (instanceOf );
332- if (variable != null ) {
333- typeCastTypeTree = variable .getType ();
334- }
335- }
336- return typeCastTypeTree ;
340+ List <TypeTree > candidateTypes = Stream .concat (
341+ replacements .entrySet ().stream ()
342+ .filter (e -> e .getValue () == instanceOf )
343+ .map (e -> e .getKey ().getClazz ().getTree ()),
344+ variablesToDelete .getOrDefault (instanceOf , emptyList ()).stream ()
345+ .map (VariableAndTypeTree ::getType )
346+ )
347+ .collect (toList ());
348+ return chooseTypeTree (candidateTypes , (TypeTree ) instanceOf .getClazz ());
337349 }
338350
339351 private String patternVariableName (J .InstanceOf instanceOf , Cursor cursor , Set <String > usedNames ) {
340352 VariableNameStrategy strategy ;
341353 JavaType type = ((TypeTree ) instanceOf .getClazz ()).getType ();
342354 if (root instanceof J .If ) {
343- VariableAndTypeTree variableData = variablesToDelete . get (instanceOf );
355+ VariableAndTypeTree variableData = getPrimaryVariableToDelete (instanceOf );
344356 if (variableData != null ) {
345357 // under the assumption that the code compiled previously we don't need to check for duplicates
346358 return VariableNameStrategy .exact (variableData .getVariable ().getSimpleName ()).variableName (type );
@@ -366,6 +378,68 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor, Set<S
366378 return VariableNameUtils .generateVariableName (baseName , cursor , INCREMENT_NUMBER );
367379 }
368380
381+ private @ Nullable VariableAndTypeTree getPrimaryVariableToDelete (J .InstanceOf instanceOf ) {
382+ List <VariableAndTypeTree > variables = variablesToDelete .get (instanceOf );
383+ if (variables == null || variables .isEmpty ()) {
384+ return null ;
385+ }
386+ return variables .get (0 );
387+ }
388+
389+ private TypeTree chooseTypeTree (List <TypeTree > candidates , TypeTree originalTypeTree ) {
390+ if (candidates .isEmpty ()) {
391+ return originalTypeTree ;
392+ }
393+
394+ JavaType originalType = originalTypeTree .getType ();
395+ TypeTree firstCandidate = candidates .get (0 );
396+ JavaType firstType = firstCandidate .getType ();
397+ if (originalType == null || firstType == null ) {
398+ return originalTypeTree ;
399+ }
400+
401+ boolean allSameRawType = candidates .stream ()
402+ .map (TypeTree ::getType )
403+ .allMatch (type -> type != null &&
404+ hasSameRawType (type , originalType ) &&
405+ hasSameRawType (type , firstType ));
406+ if (!allSameRawType ) {
407+ return originalTypeTree ;
408+ }
409+
410+ if (candidates .size () == 1 ) {
411+ return candidates .get (0 );
412+ }
413+
414+ List <TypeTree > wildcardCandidates = candidates .stream ()
415+ .filter (this ::isUnboundedWildcardParameterized )
416+ .collect (toList ());
417+
418+ if (!wildcardCandidates .isEmpty ()) {
419+ if (wildcardCandidates .stream ().allMatch (candidate -> SemanticallyEqual .areEqual (candidate , wildcardCandidates .get (0 )))) {
420+ return wildcardCandidates .get (0 );
421+ }
422+ return originalTypeTree ;
423+ }
424+
425+ if (candidates .stream ().allMatch (candidate -> SemanticallyEqual .areEqual (candidate , firstCandidate ))) {
426+ return firstCandidate ;
427+ }
428+
429+ return originalTypeTree ;
430+ }
431+
432+ private boolean isUnboundedWildcardParameterized (TypeTree typeTree ) {
433+ JavaType type = typeTree .getType ();
434+ if (!(type instanceof JavaType .Parameterized )) {
435+ return false ;
436+ }
437+ return requireNonNull (((JavaType .Parameterized ) type ).getTypeParameters ()).stream ()
438+ .allMatch (typeParameter -> typeParameter instanceof JavaType .GenericTypeVariable &&
439+ ((JavaType .GenericTypeVariable ) typeParameter ).getBounds ().isEmpty () &&
440+ "?" .equals (((JavaType .GenericTypeVariable ) typeParameter ).getName ()));
441+ }
442+
369443 public @ Nullable J processTypeCast (J .TypeCast typeCast , Cursor cursor ) {
370444 J .InstanceOf instanceOf = replacements .get (typeCast );
371445 if (instanceOf != null && instanceOf .getPattern () != null ) {
@@ -386,7 +460,9 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor, Set<S
386460 }
387461
388462 public @ Nullable J processVariableDeclarations (J .VariableDeclarations multiVariable ) {
389- return multiVariable .getVariables ().stream ().anyMatch (v -> variablesToDelete .values ().stream ().anyMatch (vd -> vd .getVariable () == v )) ? null : multiVariable ;
463+ return multiVariable .getVariables ().stream ().anyMatch (v -> variablesToDelete .values ().stream ()
464+ .flatMap (Collection ::stream )
465+ .anyMatch (vd -> vd .getVariable () == v )) ? null : multiVariable ;
390466 }
391467 }
392468
0 commit comments