private void checkForBrokenFieldAccess( CheckerState state, Artifact artifact, DeclaredClass clazz, DeclaredMethod method, ImmutableList.Builder<Conflict> builder) { for (AccessedField field : method.fieldAccesses()) { final ClassTypeDescriptor owningClass = field.owner(); final DeclaredClass calledClass = state.knownClasses().get(owningClass); DeclaredField declaredField = new DeclaredFieldBuilder().descriptor(field.descriptor()).name(field.name()).build(); if (calledClass == null) { builder.add( conflict( ConflictCategory.CLASS_NOT_FOUND, "Class not found: " + owningClass, dependency(clazz, method, field), artifact.name(), state.sourceMappings().get(owningClass))); } else if (missingField(declaredField, calledClass, state.knownClasses())) { builder.add( conflict( ConflictCategory.FIELD_NOT_FOUND, "Field not found: " + field.name(), dependency(clazz, method, field), artifact.name(), state.sourceMappings().get(owningClass))); } else { // Everything is ok! } } }
private void checkForBrokenMethodCalls( CheckerState state, Artifact artifact, DeclaredClass clazz, DeclaredMethod method, ImmutableList.Builder<Conflict> builder) { for (CalledMethod calledMethod : method.methodCalls()) { final ClassTypeDescriptor owningClass = calledMethod.owner(); final DeclaredClass calledClass = state.knownClasses().get(owningClass); if (calledClass == null) { builder.add( conflict( ConflictCategory.CLASS_NOT_FOUND, "Class not found: " + owningClass, dependency(clazz, method, calledMethod), artifact.name(), state.sourceMappings().get(owningClass))); } else if (missingMethod(calledMethod, calledClass, state.knownClasses())) { builder.add( conflict( ConflictCategory.METHOD_SIGNATURE_NOT_FOUND, "Method not found: " + calledMethod.pretty(), dependency(clazz, method, calledMethod), artifact.name(), state.sourceMappings().get(owningClass))); } } }
/** * @param projectArtifact the main artifact of the project we're verifying (this is considered the * entry point for reachability) * @param artifactsToCheck all artifacts that are on the runtime classpath * @param allArtifacts all artifacts, including implicit artifacts (runtime provided artifacts) * @return a list of conflicts */ public ImmutableList<Conflict> check( Artifact projectArtifact, List<Artifact> artifactsToCheck, List<Artifact> allArtifacts) { final CheckerStateBuilder stateBuilder = new CheckerStateBuilder(); createCanonicalClassMapping(stateBuilder, allArtifacts); CheckerState state = stateBuilder.build(); // brute-force reachability analysis Set<TypeDescriptor> reachableClasses = reachableFrom(projectArtifact.classes().values(), state.knownClasses()); final ImmutableList.Builder<Conflict> builder = ImmutableList.builder(); // Then go through everything in the classpath to make sure all the method calls / field // references // are satisfied. for (Artifact artifact : artifactsToCheck) { for (DeclaredClass clazz : artifact.classes().values()) { if (!reachableClasses.contains(clazz.className())) { continue; } for (DeclaredMethod method : clazz.methods().values()) { checkForBrokenMethodCalls(state, artifact, clazz, method, builder); checkForBrokenFieldAccess(state, artifact, clazz, method, builder); } } } return builder.build(); }
/** * Create a canonical mapping of which classes are kept. First come first serve in the classpath * * @param stateBuilder conflict checker state we're populating * @param allArtifacts maven artifacts to populate checker state with */ private void createCanonicalClassMapping( CheckerStateBuilder stateBuilder, List<Artifact> allArtifacts) { for (Artifact artifact : allArtifacts) { for (DeclaredClass clazz : artifact.classes().values()) { if (stateBuilder.knownClasses().putIfAbsent(clazz.className(), clazz) == null) { stateBuilder.putSourceMapping(clazz.className(), artifact.name()); } } } }