boolean (DifferentiateContext context, JvmClass changedClass, Iterable<JvmField> added, Utils future, Utils present) { if (Iterators.isEmpty(added)) { return true; } debug("Processing added fields"); if (changedClass.getFlags().isEnum()) { debug("Constants added to enum, affecting class usages " + changedClass.getName()); // only mark synthetic classes used to implement switch statements: this will limit the number of recompiled classes to those where switch statements on changed enum are used context.affectUsage(new ClassUsage(changedClass.getReferenceID()), n -> n instanceof JVMClassNode<?, ?> && ((JVMClassNode<?, ?>)n).isSynthetic()); } for (JvmField addedField : added) { debug("Field: " + addedField.getName()); Set<JvmNodeReferenceID> changedClassWithSubclasses = future.collectSubclassesWithoutField(changedClass.getReferenceID(), addedField.getName()); changedClassWithSubclasses.add(changedClass.getReferenceID()); for (JvmNodeReferenceID subClass : changedClassWithSubclasses) { String affectReason = null; if (!addedField.isPrivate()) { for (JvmClass cl : future.getNodes(subClass, JvmClass.class)) { if (cl.isLocal()) { affectReason = "Affecting local subclass (introduced field can potentially hide surrounding method parameters/local variables): "; break; } else { String outerClassName = cl.getOuterFqName(); if (!outerClassName.isEmpty()) { Iterable<JvmClass> outerClasses = Iterators.collect(future.getClassesByName(outerClassName), new SmartList<>()); if (Iterators.isEmpty(outerClasses) || !Iterators.isEmpty(Iterators.filter(outerClasses, ocl -> future.isFieldVisible(ocl, addedField)))) { affectReason = "Affecting inner subclass (introduced field can potentially hide surrounding class fields): "; break; } } } } } if (affectReason != null) { affectNodeSources(context, subClass, affectReason); } if (!addedField.isPrivate() && addedField.isStatic()) { affectStaticMemberOnDemandUsages(context, subClass, Collections.emptyList()); } else { // ensure analysis scope includes classes that depend on the subclass context.affectUsage(new AffectionScopeMetaUsage(subClass)); } } context.affectUsage((n, u) -> { // affect all clients that access fields with the same name via subclasses, // if the added field is not visible to the client if (!(u instanceof FieldUsage) || !(n instanceof JvmClass)) { return false; } FieldUsage fieldUsage = (FieldUsage)u; return Objects.equals(fieldUsage.getName(), addedField.getName()) && changedClassWithSubclasses.contains(fieldUsage.getElementOwner()); }); } debug("End of added fields processing"); return true; }