void (DifferentiateContext context, JvmClass changedClass, Iterable<JvmMethod> added, Utils future, Utils present) { debug("Processing added methods: "); for (JvmMethod addedMethod : added) { if (!addedMethod.isPrivate() && (changedClass.isInterface() || changedClass.isAbstract() || addedMethod.isAbstract())) { debug("Method: " + addedMethod.getName()); debug("Class is abstract, or is interface, or added non-private method is abstract => affecting all subclasses"); affectSubclasses(context, future, changedClass.getReferenceID(), false); break; } } if (changedClass.isInterface()) { for (JvmMethod addedMethod : added) { if (!addedMethod.isPrivate() && addedMethod.isAbstract()) { debug("Added non-private abstract method: ", addedMethod.getName()); affectLambdaInstantiations(context, present, changedClass.getReferenceID()); break; } } } for (JvmMethod addedMethod : added) { debug("Method: ", addedMethod.getName()); if (addedMethod.isPrivate()) { continue; } Iterable<JvmNodeReferenceID> propagated = Iterators.lazy(() -> future.collectSubclassesWithoutMethod(changedClass.getReferenceID(), addedMethod)); if (!Iterators.isEmpty(addedMethod.getArgTypes()) && !present.hasOverriddenMethods(changedClass, addedMethod)) { debug("Conservative case on overriding methods, affecting method usages"); context.affectUsage(addedMethod.createUsageQuery(changedClass.getReferenceID())); if (!addedMethod.isConstructor()) { // do not propagate constructors access, since constructors are always concrete and not accessible via references to subclasses for (JvmNodeReferenceID id : propagated) { context.affectUsage(new AffectionScopeMetaUsage(id)); context.affectUsage(addedMethod.createUsageQuery(id)); } } } if (addedMethod.isStatic()) { affectStaticMemberOnDemandUsages(context, changedClass.getReferenceID(), propagated); } Predicate<JvmMethod> lessSpecificCond = future.lessSpecific(addedMethod); for (JvmMethod lessSpecific : Iterators.filter(changedClass.getMethods(), lessSpecificCond::test)) { debug("Found less specific method, affecting method usages; ", lessSpecific.getName(), lessSpecific.getDescriptor()); affectMemberUsages(context, changedClass.getReferenceID(), lessSpecific, present.collectSubclassesWithoutMethod(changedClass.getReferenceID(), lessSpecific)); } debug("Processing affected by specificity methods"); for (Pair<JvmClass, JvmMethod> pair : future.getOverriddenMethods(changedClass, lessSpecificCond)) { JvmClass cls = pair.getFirst(); JvmMethod overriddenMethod = pair.getSecond(); // isInheritor(cls, changedClass) == false debug("Method: ", overriddenMethod.getName()); debug("Class : ", cls.getName()); debug("Affecting method usages for that found"); affectMemberUsages(context, changedClass.getReferenceID(), overriddenMethod, present.collectSubclassesWithoutMethod(changedClass.getReferenceID(), overriddenMethod)); } for (Pair<JvmClass, JvmMethod> pair : future.getOverridingMethods(changedClass, addedMethod, lessSpecificCond)) { JvmClass cls = pair.getFirst(); JvmMethod overridingMethod = pair.getSecond(); // isInheritor(cls, changedClass) == true debug("Method: ", overridingMethod.getName()); debug("Class : ", cls.getName()); if (overridingMethod.isSameByJavaRules(addedMethod)) { debug("Current method overrides the added method"); affectNodeSources(context, cls.getReferenceID(), "Affecting source "); } else { debug("Current method does not override the added method"); debug("Affecting method usages for the method"); affectMemberUsages(context, cls.getReferenceID(), overridingMethod, present.collectSubclassesWithoutMethod(cls.getReferenceID(), overridingMethod)); } } for (ReferenceID subClassId : future.allSubclasses(changedClass.getReferenceID())) { Iterable<NodeSource> sources = context.getGraph().getSources(subClassId); if (!Iterators.isEmpty(Iterators.filter(sources, s -> !context.isCompiled(s)))) { // has non-compiled sources for (JvmClass outerClass : Iterators.flat(Iterators.map(future.getNodes(subClassId, JvmClass.class), cl -> future.getNodes(new JvmNodeReferenceID(cl.getOuterFqName()), JvmClass.class)))) { if (future.isMethodVisible(outerClass, addedMethod) || future.inheritsFromLibraryClass(outerClass)) { for (NodeSource source : Iterators.filter(sources, context.getParams().affectionFilter()::test)) { debug("Affecting file due to local overriding: ", source.getPath()); context.affectNodeSource(source); } } } } } } debug("End of added methods processing"); }