diff graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java @ 8252:0905d796944a

Refactored codegen error model to make error redirection a lot easier.
author Christian Humer <christian.humer@gmail.com>
date Wed, 13 Mar 2013 19:58:28 +0100
parents cb70ed101b5f
children 4dc7034317ec
line wrap: on
line diff
--- a/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java	Wed Mar 13 11:32:43 2013 +0100
+++ b/graal/com.oracle.truffle.codegen.processor/src/com/oracle/truffle/codegen/processor/node/NodeParser.java	Wed Mar 13 19:58:28 2013 +0100
@@ -28,6 +28,7 @@
 import javax.lang.model.element.*;
 import javax.lang.model.type.*;
 import javax.lang.model.util.*;
+import javax.tools.Diagnostic.*;
 
 import com.oracle.truffle.api.codegen.*;
 import com.oracle.truffle.api.nodes.Node.Child;
@@ -56,13 +57,36 @@
         try {
             parsedNodes = new HashMap<>();
             node = resolveNode((TypeElement) element);
+            if (Log.DEBUG) {
+                NodeData parsed = parsedNodes.get(Utils.getQualifiedName((TypeElement) element));
+                if (node != null) {
+                    String dump = parsed.dump();
+                    log.message(Kind.ERROR, null, null, null, dump);
+                    System.out.println(dump);
+                }
+            }
         } finally {
             parsedNodes = null;
         }
+
         return node;
     }
 
     @Override
+    protected NodeData filterErrorElements(NodeData model) {
+        for (Iterator<NodeData> iterator = model.getDeclaredChildren().iterator(); iterator.hasNext();) {
+            NodeData node = filterErrorElements(iterator.next());
+            if (node == null) {
+                iterator.remove();
+            }
+        }
+        if (model.hasErrors()) {
+            return null;
+        }
+        return model;
+    }
+
+    @Override
     public boolean isDelegateToRootDeclaredType() {
         return true;
     }
@@ -82,9 +106,11 @@
                 children.add(childNode);
             }
         }
+
         NodeData rootNode = parseNode(rootType);
-        if (rootNode == null && children.size() > 0) {
-            rootNode = new NodeData(rootType, null, rootType.getSimpleName().toString());
+        boolean hasErrors = rootNode != null ? rootNode.hasErrors() : false;
+        if ((rootNode == null || hasErrors) && children.size() > 0) {
+            rootNode = new NodeData(rootType, rootType.getSimpleName().toString());
         }
 
         parsedNodes.put(typeName, rootNode);
@@ -94,17 +120,6 @@
             rootNode.setDeclaredChildren(children);
         }
 
-        if (Log.DEBUG) {
-            NodeData parsed = parsedNodes.get(typeName);
-            if (parsed != null) {
-                String dump = parsed.dump();
-                String valid = rootNode != null ? "" : " failed";
-                String msg = String.format("Node parsing %s : %s", valid, dump);
-                log.error(msg);
-                System.out.println(msg);
-            }
-        }
-
         return rootNode;
     }
 
@@ -121,7 +136,8 @@
         }
 
         if (type.getModifiers().contains(Modifier.PRIVATE)) {
-            return null; // not visible
+            // TODO error message here!?
+            return null; // not visible, not a node
         }
 
         TypeElement nodeType;
@@ -135,43 +151,32 @@
         }
 
         NodeData nodeData = parseNodeData(type, nodeType);
-        if (nodeData == null) {
-            return null;
+        if (nodeData.hasErrors()) {
+            return nodeData; // error sync point
         }
 
         List<Element> elements = new ArrayList<>(context.getEnvironment().getElementUtils().getAllMembers(type));
-        nodeData.setExtensionElements(getExtensionParser().parseAll(type, elements));
+        nodeData.setExtensionElements(getExtensionParser().parseAll(nodeData, elements));
         if (nodeData.getExtensionElements() != null) {
             elements.addAll(nodeData.getExtensionElements());
         }
+        parseMethods(nodeData, elements);
 
-        if (!parseMethods(nodeData, elements)) {
-            return null;
+        if (nodeData.hasErrors()) {
+            return nodeData;
         }
 
         List<NodeData> nodes;
         if (needsSplit) {
             nodes = splitNodeData(nodeData);
-            if (nodes == null) {
-                return null;
-            }
         } else {
             nodes = new ArrayList<>();
             nodes.add(nodeData);
         }
 
-        boolean valid = true;
         for (NodeData splittedNode : nodes) {
-            if (!finalizeSpecializations(splittedNode)) {
-                valid = false;
-            }
-            if (!verifyNode(splittedNode)) {
-                valid = false;
-            }
-        }
-
-        if (!valid) {
-            return null;
+            finalizeSpecializations(splittedNode);
+            verifyNode(splittedNode);
         }
 
         if (needsSplit) {
@@ -237,31 +242,25 @@
         return grouped;
     }
 
-    private boolean parseMethods(final NodeData node, List<Element> elements) {
+    private void parseMethods(final NodeData node, List<Element> elements) {
         node.setGuards(new GuardParser(context, node, node.getTypeSystem()).parse(elements));
         node.setShortCircuits(new ShortCircuitParser(context, node).parse(elements));
         node.setSpecializationListeners(new SpecializationListenerParser(context, node).parse(elements));
         List<SpecializationData> generics = new GenericParser(context, node).parse(elements);
         List<SpecializationData> specializations = new SpecializationMethodParser(context, node).parse(elements);
 
-        if (generics == null || specializations == null || node.getGuards() == null || node.getShortCircuits() == null || node.getSpecializationListeners() == null) {
-            return false;
-        }
-
         List<SpecializationData> allSpecializations = new ArrayList<>();
         allSpecializations.addAll(generics);
         allSpecializations.addAll(specializations);
 
         node.setSpecializations(allSpecializations);
-
-        return true;
     }
 
-    private boolean finalizeSpecializations(final NodeData node) {
+    private void finalizeSpecializations(final NodeData node) {
         List<SpecializationData> specializations = new ArrayList<>(node.getSpecializations());
 
         if (specializations.isEmpty()) {
-            return true;
+            return;
         }
 
         List<SpecializationData> generics = new ArrayList<>();
@@ -273,22 +272,18 @@
 
         if (generics.size() == 1 && specializations.size() == 1) {
             for (SpecializationData generic : generics) {
-                log.error(generic.getMethod(), "@%s defined but no @%s.", Generic.class.getSimpleName(), Specialization.class.getSimpleName());
+                generic.addError("@%s defined but no @%s.", Generic.class.getSimpleName(), Specialization.class.getSimpleName());
             }
         }
 
         SpecializationData genericSpecialization = null;
         if (generics.size() > 1) {
             for (SpecializationData generic : generics) {
-                log.error(generic.getMethod(), "Only @%s is allowed per operation.", Generic.class.getSimpleName());
+                generic.addError("Only @%s is allowed per operation.", Generic.class.getSimpleName());
             }
-            return false;
+            return;
         } else if (generics.size() == 1) {
             genericSpecialization = generics.get(0);
-            if (!node.needsRewrites(context)) {
-                log.error(genericSpecialization.getMethod(), "Generic specialization is not reachable.", Generic.class.getSimpleName());
-                return false;
-            }
         } else if (node.needsRewrites(context)) {
             SpecializationData specialization = specializations.get(0);
             GenericParser parser = new GenericParser(context, node);
@@ -343,8 +338,6 @@
         for (SpecializationData specialization : specializations) {
             specialization.setId(findUniqueSpecializationId(specialization));
         }
-
-        return true;
     }
 
     private static String findUniqueSpecializationId(SpecializationData specialization) {
@@ -435,28 +428,18 @@
         }
     }
 
-    private boolean verifyNode(NodeData nodeData) {
+    private void verifyNode(NodeData nodeData) {
         // verify specialization parameter length
-        if (!verifySpecializationParameters(nodeData)) {
-            return false;
-        }
+        verifySpecializationParameters(nodeData);
 
         // verify order is not ambiguous
-        if (!verifySpecializationOrder(nodeData)) {
-            return false;
-        }
+        verifySpecializationOrder(nodeData);
 
-        if (!verifyMissingAbstractMethods(nodeData)) {
-            return false;
-        }
+        verifyMissingAbstractMethods(nodeData);
 
-        if (!assignShortCircuitsToSpecializations(nodeData)) {
-            return false;
-        }
+        assignShortCircuitsToSpecializations(nodeData);
 
-        if (!verifyConstructors(nodeData)) {
-            return false;
-        }
+        verifyConstructors(nodeData);
 
 // if (!verifyNamingConvention(specializations, "do")) {
 // return null;
@@ -466,54 +449,42 @@
 // return null;
 // }
 
-        if (!verifyNamingConvention(nodeData.getShortCircuits(), "needs")) {
-            return false;
-        }
+        verifyNamingConvention(nodeData.getShortCircuits(), "needs");
 
-        if (!verifySpecializationThrows(nodeData)) {
-            return false;
-        }
-
-        return true;
+        verifySpecializationThrows(nodeData);
     }
 
     private NodeData parseNodeData(TypeElement templateType, TypeElement nodeType) {
         List<Element> elements = new ArrayList<>(context.getEnvironment().getElementUtils().getAllMembers(nodeType));
         List<TypeElement> typeHierarchy = findSuperClasses(new ArrayList<TypeElement>(), nodeType);
         Collections.reverse(typeHierarchy);
+        NodeData nodeData = new NodeData(templateType, templateType.getSimpleName().toString());
 
         AnnotationMirror typeSystemMirror = findFirstAnnotation(typeHierarchy, TypeSystemReference.class);
         if (typeSystemMirror == null) {
-            log.error(templateType, "No @%s annotation found in type hierarchy of %s.", TypeSystemReference.class.getSimpleName(), nodeType.getQualifiedName().toString());
-            return null;
+            nodeData.addError("No @%s annotation found in type hierarchy of %s.", TypeSystemReference.class.getSimpleName(), nodeType.getQualifiedName().toString());
+            return nodeData;
         }
 
         TypeMirror typeSytemType = Utils.getAnnotationValue(TypeMirror.class, typeSystemMirror, "value");
         final TypeSystemData typeSystem = (TypeSystemData) context.getTemplate(typeSytemType, true);
         if (typeSystem == null) {
-            log.error(templateType, "The used type system '%s' is invalid.", Utils.getQualifiedName(typeSytemType));
-            return null;
+            nodeData.addError("The used type system '%s' is invalid.", Utils.getQualifiedName(typeSytemType));
+            return nodeData;
         }
 
-        NodeData nodeData = new NodeData(templateType, typeSystem, templateType.getSimpleName().toString());
         nodeData.setNodeType(nodeType.asType());
+        nodeData.setTypeSystem(typeSystem);
 
         List<ExecutableTypeData> executableTypes = filterExecutableTypes(new ExecutableTypeMethodParser(context, nodeData).parse(elements));
-
         nodeData.setExecutableTypes(executableTypes);
-
         parsedNodes.put(Utils.getQualifiedName(templateType), nodeData);
-
-        List<NodeFieldData> fields = parseFields(nodeData, elements, typeHierarchy);
-        if (fields == null) {
-            return null;
-        }
-        nodeData.setFields(fields);
+        nodeData.setFields(parseFields(nodeData, elements, typeHierarchy));
 
         return nodeData;
     }
 
-    private boolean verifySpecializationParameters(NodeData nodeData) {
+    private static void verifySpecializationParameters(NodeData nodeData) {
         boolean valid = true;
         int args = -1;
         for (SpecializationData specializationData : nodeData.getSpecializations()) {
@@ -531,17 +502,16 @@
         }
         if (!valid) {
             for (SpecializationData specialization : nodeData.getSpecializations()) {
-                context.getLog().error(specialization.getMethod(), specialization.getMarkerAnnotation(), "All specializations must have the same number of arguments.");
+                specialization.addError("All specializations must have the same number of arguments.");
             }
         }
-        return valid;
     }
 
-    private boolean verifyMissingAbstractMethods(NodeData nodeData) {
-        if (nodeData.needsFactory()) {
+    private void verifyMissingAbstractMethods(NodeData nodeData) {
+        if (!nodeData.needsFactory()) {
             // missing abstract methods only needs to be implemented
             // if we need go generate factory for it.
-            return true;
+            return;
         }
 
         List<Element> elements = new ArrayList<>(context.getEnvironment().getElementUtils().getAllMembers(nodeData.getTemplateType()));
@@ -554,24 +524,20 @@
             unusedElements.removeAll(nodeData.getExtensionElements());
         }
 
-        boolean valid = true;
         for (ExecutableElement unusedMethod : ElementFilter.methodsIn(unusedElements)) {
             if (unusedMethod.getModifiers().contains(Modifier.ABSTRACT)) {
-                context.getLog().error(nodeData.getTemplateType(), "The type %s must implement the inherited abstract method %s.", Utils.getSimpleName(nodeData.getTemplateType()),
-                                Utils.getReadableSignature(unusedMethod));
-                valid = false;
+                nodeData.addError("The type %s must implement the inherited abstract method %s.", Utils.getSimpleName(nodeData.getTemplateType()), Utils.getReadableSignature(unusedMethod));
             }
         }
+    }
+
+    private void verifyConstructors(NodeData nodeData) {
+        if (!nodeData.needsRewrites(context)) {
+            // no specialization constructor is needed if the node never rewrites.
+            return;
+        }
 
-        return valid;
-    }
-
-    private boolean verifyConstructors(NodeData nodeData) {
         TypeElement type = Utils.fromTypeMirror(nodeData.getNodeType());
-        if (!nodeData.needsRewrites(context)) {
-            // no specialization constructor is needed if the node never rewrites.
-            return true;
-        }
 
         List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
         for (ExecutableElement e : constructors) {
@@ -579,20 +545,17 @@
                 TypeMirror firstArg = e.getParameters().get(0).asType();
                 if (Utils.typeEquals(firstArg, nodeData.getNodeType())) {
                     if (e.getModifiers().contains(Modifier.PRIVATE)) {
-                        context.getLog().error(e, "The specialization constructor must not be private.");
-                        return false;
+                        nodeData.addError("The specialization constructor must not be private.");
                     } else if (constructors.size() <= 1) {
-                        context.getLog().error(e, "The specialization constructor must not be the only constructor. The definition of an alternative constructor is required.");
-                        return false;
+                        nodeData.addError("The specialization constructor must not be the only constructor. The definition of an alternative constructor is required.");
                     }
-                    return true;
+                    return;
                 }
             }
         }
 
         // not found
-        context.getLog().error(type, "Specialization constructor '%s(%s previousNode) { this(...); }' is required.", Utils.getSimpleName(type), Utils.getSimpleName(type));
-        return false;
+        nodeData.addError("Specialization constructor '%s(%s previousNode) { this(...); }' is required.", Utils.getSimpleName(type), Utils.getSimpleName(type));
     }
 
     private static List<ExecutableTypeData> filterExecutableTypes(List<ExecutableTypeData> executableTypes) {
@@ -657,8 +620,6 @@
             }
         }
 
-        boolean valid = true;
-
         List<NodeFieldData> fields = new ArrayList<>();
         for (VariableElement var : ElementFilter.fieldsIn(elements)) {
             if (var.getModifiers().contains(Modifier.STATIC)) {
@@ -672,20 +633,10 @@
             }
 
             NodeFieldData field = parseField(nodeData, var, shortCircuits);
-            if (field != null) {
-                if (field.getExecutionKind() != ExecutionKind.IGNORE) {
-                    fields.add(field);
-                }
-            } else {
-                valid = false;
+            if (field.getExecutionKind() != ExecutionKind.IGNORE) {
+                fields.add(field);
             }
         }
-
-        // TODO parse getters
-        if (!valid) {
-            return null;
-        }
-
         sortByExecutionOrder(fields, executionDefinition == null ? Collections.<String> emptyList() : executionDefinition, typeHierarchy);
         return fields;
     }
@@ -721,19 +672,15 @@
             kind = null;
         }
 
-        NodeData fieldNodeData = null;
+        NodeFieldData fieldData = new NodeFieldData(var, findAccessElement(var), mirror, kind, execution);
         if (nodeType != null) {
-            fieldNodeData = resolveNode(Utils.fromTypeMirror(nodeType));
-            Element errorElement = Utils.typeEquals(parentNodeData.getTemplateType().asType(), var.getEnclosingElement().asType()) ? var : parentNodeData.getTemplateType();
+            NodeData fieldNodeData = resolveNode(Utils.fromTypeMirror(nodeType));
+            fieldData.setNode(fieldNodeData);
 
             if (fieldNodeData == null) {
-                // TODO redirect errors from resolve.
-                context.getLog().error(errorElement, "Node type '%s' is invalid.", Utils.getQualifiedName(nodeType));
-                return null;
+                fieldData.addError("Node type '%s' is invalid.", Utils.getQualifiedName(nodeType));
             } else if (fieldNodeData.findGenericExecutableTypes(context).isEmpty()) {
-                // TODO better error handling for (no or multiple?)
-                context.getLog().error(errorElement, "No executable generic types found for node '%s'.", Utils.getQualifiedName(nodeType));
-                return null;
+                fieldData.addError("No executable generic types found for node '%s'.", Utils.getQualifiedName(nodeType));
             }
 
             // TODO correct handling of access elements
@@ -741,7 +688,7 @@
                 execution = ExecutionKind.IGNORE;
             }
         }
-        return new NodeFieldData(fieldNodeData, var, findAccessElement(var), mirror, kind, execution);
+        return fieldData;
     }
 
     private Element findAccessElement(VariableElement variableElement) {
@@ -795,17 +742,16 @@
         });
     }
 
-    private boolean assignShortCircuitsToSpecializations(NodeData node) {
+    private void assignShortCircuitsToSpecializations(NodeData node) {
         Map<String, List<ShortCircuitData>> groupedShortCircuits = groupShortCircuits(node.getShortCircuits());
 
         boolean valid = true;
-
         for (NodeFieldData field : node.filterFields(null, ExecutionKind.SHORT_CIRCUIT)) {
             String valueName = field.getName();
             List<ShortCircuitData> availableCircuits = groupedShortCircuits.get(valueName);
 
             if (availableCircuits == null || availableCircuits.isEmpty()) {
-                log.error(node.getTemplateType(), "@%s method for short cut value '%s' required.", ShortCircuit.class.getSimpleName(), valueName);
+                node.addError("@%s method for short cut value '%s' required.", ShortCircuit.class.getSimpleName(), valueName);
                 valid = false;
                 continue;
             }
@@ -820,7 +766,7 @@
 
             if (!sameMethodName) {
                 for (ShortCircuitData circuit : availableCircuits) {
-                    log.error(circuit.getMethod(), circuit.getMarkerAnnotation(), "All short circuits for short cut value '%s' must have the same method name.", valueName);
+                    circuit.addError("All short circuits for short cut value '%s' must have the same method name.", valueName);
                 }
                 valid = false;
                 continue;
@@ -835,7 +781,7 @@
             }
 
             if (genericCircuit == null) {
-                log.error(node.getTemplateType(), "No generic @%s method available for short cut value '%s'.", ShortCircuit.class.getSimpleName(), valueName);
+                node.addError("No generic @%s method available for short cut value '%s'.", ShortCircuit.class.getSimpleName(), valueName);
                 valid = false;
                 continue;
             }
@@ -848,62 +794,42 @@
         }
 
         if (!valid) {
-            return valid;
+            return;
         }
 
         NodeFieldData[] fields = node.filterFields(null, ExecutionKind.SHORT_CIRCUIT);
         for (SpecializationData specialization : node.getSpecializations()) {
-            ShortCircuitData[] assignedShortCuts = new ShortCircuitData[fields.length];
+            List<ShortCircuitData> assignedShortCuts = new ArrayList<>(fields.length);
 
             for (int i = 0; i < fields.length; i++) {
                 List<ShortCircuitData> availableShortCuts = groupedShortCircuits.get(fields[i].getName());
 
                 ShortCircuitData genericShortCircuit = null;
+                ShortCircuitData compatibleShortCircuit = null;
                 for (ShortCircuitData circuit : availableShortCuts) {
                     if (circuit.isGeneric()) {
                         genericShortCircuit = circuit;
                     } else if (circuit.isCompatibleTo(specialization)) {
-                        assignedShortCuts[i] = circuit;
+                        compatibleShortCircuit = circuit;
                     }
                 }
 
-                if (assignedShortCuts[i] == null) {
-                    assignedShortCuts[i] = genericShortCircuit;
+                if (compatibleShortCircuit == null) {
+                    compatibleShortCircuit = genericShortCircuit;
                 }
+                assignedShortCuts.add(compatibleShortCircuit);
             }
             specialization.setShortCircuits(assignedShortCuts);
         }
-        return true;
     }
 
-    private boolean verifyNamingConvention(List<? extends TemplateMethod> methods, String prefix) {
-        boolean valid = true;
+    private static void verifyNamingConvention(List<? extends TemplateMethod> methods, String prefix) {
         for (int i = 0; i < methods.size(); i++) {
             TemplateMethod m1 = methods.get(i);
             if (m1.getMethodName().length() < 3 || !m1.getMethodName().startsWith(prefix)) {
-                log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Naming convention: method name must start with '%s'.", prefix);
-                valid = false;
+                m1.addError("Naming convention: method name must start with '%s'.", prefix);
             }
         }
-        return valid;
-    }
-
-    @SuppressWarnings("unused")
-    private boolean verifyNamesUnique(List<? extends TemplateMethod> methods) {
-        boolean valid = true;
-        for (int i = 0; i < methods.size(); i++) {
-            TemplateMethod m1 = methods.get(i);
-            for (int j = i + 1; j < methods.size(); j++) {
-                TemplateMethod m2 = methods.get(j);
-
-                if (m1.getMethodName().equalsIgnoreCase(m2.getMethodName())) {
-                    log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Method name '%s' used multiple times", m1.getMethodName());
-                    log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Method name '%s' used multiple times", m1.getMethodName());
-                    return false;
-                }
-            }
-        }
-        return valid;
     }
 
     private boolean isGenericShortCutMethod(NodeData node, TemplateMethod method) {
@@ -958,7 +884,7 @@
         return collection;
     }
 
-    private boolean verifySpecializationOrder(NodeData node) {
+    private static void verifySpecializationOrder(NodeData node) {
         TypeSystemData typeSystem = node.getTypeSystem();
         List<SpecializationData> specializations = node.getSpecializations();
 
@@ -971,44 +897,39 @@
                 if (m1.getOrder() != Specialization.DEFAULT_ORDER && m2.getOrder() != Specialization.DEFAULT_ORDER) {
                     int specOrder = m1.getOrder() - m2.getOrder();
                     if (specOrder == 0) {
-                        log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Order value %d used multiple times", m1.getOrder());
-                        log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Order value %d used multiple times", m1.getOrder());
-                        return false;
+                        m1.addError("Order value %d used multiple times", m1.getOrder());
+                        m2.addError("Order value %d used multiple times", m1.getOrder());
+                        return;
                     } else if ((specOrder < 0 && inferredOrder > 0) || (specOrder > 0 && inferredOrder < 0)) {
-                        log.error(m1.getMethod(), m1.getMarkerAnnotation(), "Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
-                        log.error(m2.getMethod(), m2.getMarkerAnnotation(), "Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
-                        return false;
+                        m1.addError("Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
+                        m2.addError("Explicit order values %d and %d are inconsistent with type lattice ordering.", m1.getOrder(), m2.getOrder());
+                        return;
                     }
                 } else if (inferredOrder == 0) {
                     SpecializationData m = (m1.getOrder() == Specialization.DEFAULT_ORDER ? m1 : m2);
-                    log.error(m.getMethod(), m.getMarkerAnnotation(), "Cannot calculate a consistent order for this specialization. Define the order attribute to resolve this.");
-                    return false;
+                    m.addError("Cannot calculate a consistent order for this specialization. Define the order attribute to resolve this.");
+                    return;
                 }
             }
         }
-        return true;
     }
 
-    private boolean verifySpecializationThrows(NodeData node) {
+    private static void verifySpecializationThrows(NodeData node) {
         Map<String, SpecializationData> specializationMap = new HashMap<>();
         for (SpecializationData spec : node.getSpecializations()) {
             specializationMap.put(spec.getMethodName(), spec);
         }
-        boolean valid = true;
         for (SpecializationData sourceSpecialization : node.getSpecializations()) {
             if (sourceSpecialization.getExceptions() != null) {
                 for (SpecializationThrowsData throwsData : sourceSpecialization.getExceptions()) {
                     for (SpecializationThrowsData otherThrowsData : sourceSpecialization.getExceptions()) {
                         if (otherThrowsData != throwsData && Utils.typeEquals(otherThrowsData.getJavaClass(), throwsData.getJavaClass())) {
-                            AnnotationValue javaClassValue = Utils.getAnnotationValue(throwsData.getAnnotationMirror(), "rewriteOn");
-                            log.error(throwsData.getSpecialization().getMethod(), throwsData.getAnnotationMirror(), javaClassValue, "Duplicate exception type.");
-                            valid = false;
+                            throwsData.addError("Duplicate exception type.");
                         }
                     }
                 }
             }
         }
-        return valid;
     }
 
     private static int compareSpecialization(TypeSystemData typeSystem, SpecializationData m1, SpecializationData m2) {