StaticChecker.java

package com.mackenziehigh.autumn.lang.compiler.compilers;

import autumn.lang.DefinedFunctor;
import autumn.lang.compiler.ast.commons.IBinaryOperation;
import autumn.lang.compiler.ast.commons.IConstruct;
import autumn.lang.compiler.ast.commons.IDatum;
import autumn.lang.compiler.ast.commons.IExpression;
import autumn.lang.compiler.ast.commons.IStatement;
import autumn.lang.compiler.ast.commons.IUnaryOperation;
import autumn.lang.compiler.ast.nodes.Annotation;
import autumn.lang.compiler.ast.nodes.AnnotationList;
import autumn.lang.compiler.ast.nodes.AsOperation;
import autumn.lang.compiler.ast.nodes.BreakStatement;
import autumn.lang.compiler.ast.nodes.ContinueStatement;
import autumn.lang.compiler.ast.nodes.DelegateStatement;
import autumn.lang.compiler.ast.nodes.Element;
import autumn.lang.compiler.ast.nodes.ExceptionHandler;
import autumn.lang.compiler.ast.nodes.FunctionDefinition;
import autumn.lang.compiler.ast.nodes.InstanceOfExpression;
import autumn.lang.compiler.ast.nodes.IsOperation;
import autumn.lang.compiler.ast.nodes.Label;
import autumn.lang.compiler.ast.nodes.Name;
import autumn.lang.compiler.ast.nodes.PrognExpression;
import autumn.lang.compiler.ast.nodes.RecurStatement;
import autumn.lang.compiler.ast.nodes.RedoStatement;
import autumn.lang.compiler.ast.nodes.StringDatum;
import autumn.lang.compiler.ast.nodes.TypeSpecifier;
import autumn.lang.compiler.ast.nodes.Variable;
import autumn.lang.compiler.errors.ErrorCode;
import autumn.lang.compiler.errors.ErrorReport;
import autumn.lang.compiler.errors.IErrorReporter;
import autumn.util.F;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IAnnotation;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IClassType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IDeclaredType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IEnumType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IExpressionType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IField;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IMethod;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IReferenceType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IReturnType;
import com.mackenziehigh.autumn.lang.compiler.typesystem.design.IType;
import com.mackenziehigh.autumn.lang.compiler.utils.CovarianceViolation;
import com.mackenziehigh.autumn.lang.compiler.utils.TypeSystemUtils;
import com.mackenziehigh.autumn.lang.compiler.utils.Utils;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Set;

/**
 * An instance of this class provides methods which check for and report static type errors.
 *
 * @author Mackenzie High
 */
final class StaticChecker
{
    /**
     * This is the program that is being compiled.
     */
    private final ProgramCompiler program;

    /**
     * This is the error-reporter that will be used to report any errors that are found.
     */
    private final IErrorReporter reporter;

    /**
     * Sole Constructor.
     *
     * @param program is the program that is being compiled.
     */
    public StaticChecker(final ProgramCompiler program)
    {
        assert program != null;

        this.program = program;
        this.reporter = program.reporter;
    }

    /**
     * This method is invoked in order to actually issue an error report,
     * regarding a type-usage.
     *
     * @param report is the error report to issue.
     * @throws TypeCheckFailed always.
     */
    private void report(final ErrorReport report)
    {
        reporter.reportFailedCheck(report);
        throw new TypeCheckFailed();
    }

    /**
     * This method ensures that a literal's value is within the bounds of its datatype.
     *
     * @param value is the value of the literal.
     * @param source the value as it appears in the source code.
     * @param min is the minimum allowed value.
     * @param max is the maximum allowed value.
     */
    public void checkLiteral(final IDatum node,
                             final Comparable value,
                             final String source,
                             final Comparable min,
                             final Comparable max)
    {

        /**
         * The value will be null, if it is not acceptable.
         */
        if (value != null)
        {
            return;
        }

        /**
         * Create the error-report.
         */
        final ErrorCode code = ErrorCode.INACCURATE_NUMERIC_LITERAL;
        final String message = "A literal cannot be accurately compiled.";
        final ErrorReport report = new ErrorReport(node, code, message);
        report.addDetail("Minimum", min.toString());
        report.addDetail("Maximum", max.toString());
        report.addDetail("Value", source);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a condition expression returns either primitive or boxed boolean.
     *
     * @param condition is the condition expression itself.
     */
    public void checkCondition(final IExpression condition)
    {
        assert condition != null;

        final IExpressionType type = (IExpressionType) program.symbols.expressions.get(condition);

        final boolean primitive_boolean = "Z".equals(type.getDescriptor());
        final boolean boxed_boolean = "Ljava/lang/Boolean;".equals(type.getDescriptor());

        if (primitive_boolean || boxed_boolean)
        {
            return;
        }

        /**
         * Create the error-report.
         */
        final ErrorCode code = ErrorCode.EXPECTED_CONDITION;
        final String message = "A boolean condition was expected.";
        final ErrorReport report = new ErrorReport(condition, code, message);
        report.addDetail("Expected", "boolean OR java.lang.Boolean");
        report.addDetail("Actual", Utils.sourceName(type));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a variable was already declared.
     *
     * @param scope should contain the variable.
     * @param allocator manages the allocation of local variables.
     */
    public void checkDeclared(final VariableAllocator allocator,
                              final Variable variable)
    {
        final boolean declared = allocator.isDeclared(variable.getName());

        if (declared)
        {
            return;
        }

        final ErrorReport report = new ErrorReport(variable,
                                                   ErrorCode.NO_SUCH_VARIABLE,
                                                   "A variable was used before it was declared.");

        report.addDetail("Variable", variable.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a variable is in scope.
     *
     * @param allocator manages the allocation of local variables.
     * @param variable is the variable itself.
     */
    public void checkScope(final VariableAllocator allocator,
                           final Variable variable)
    {
        final boolean usable = allocator.isUsable(variable.getName());

        if (usable)
        {
            return;
        }

        final ErrorReport report = new ErrorReport(variable,
                                                   ErrorCode.VARIABLE_OUTSIDE_OF_SCOPE,
                                                   "A variable was outside of its declared scope.");

        report.addDetail("Variable", variable.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that an annotation-list is valid.
     *
     * @param site is the Abstract-Syntax-Tree annotation-list.
     * @param annotations is the type-system representation of the annotation-list.
     */
    public void checkAnnotations(final AnnotationList site,
                                 final List<IAnnotation> annotations)
    {
        assert site.getAnnotations().size() <= annotations.size();

        final Set<IType> types = Sets.newHashSet();

        /**
         * Check for duplicate annotations.
         */
        for (IAnnotation anno : annotations)
        {
            if (types.contains(anno.getAnnotationType()))
            {
                this.reportDuplicateAnnotation(site, anno);
            }
            else
            {
                types.add(anno.getAnnotationType());
            }
        }

        /**
         * Check the validity of the annotation assignments.
         */
        for (int i = 0; i < site.getAnnotations().size(); i++)
        {
            //checkAnnotation(site.getAnnotations().get(i), annotations.get(i));
        }
    }

    /**
     * This method checks an annotation-value assignment.
     *
     * @param site is the Abstract-Syntax-Tree annotation.
     * @param annotation is the type-system representation of the annotation.
     */
    private void checkAnnotation(final Annotation site,
                                 final IAnnotation annotation)
    {
        final IMethod method = TypeSystemUtils.find(annotation.getAnnotationType().getMethods(),
                                                    "value");

        /**
         * 1. If a value is being assigned to the annotation, then it must have a value() method.
         * 2. Annotations written in Java *may* have too many methods.
         * 3. Autumn based annotations always have exactly one method.
         */
        if (site.getValues() != null && annotation.getAnnotationType().getMethods().size() != 1)
        {
            reportUnusableAnnotation(site, annotation);
        }

        /**
         * The return-type of the value() method must return a String.
         */
        if (method != null && !method.getReturnType().equals(program.typesystem.utils.STRING))
        {
            reportUnusableAnnotation(site, annotation);
        }
    }

    /**
     * This method ensures that an expression's static-type is a subtype of the expected type.
     *
     * @param expression is the expression to check.
     * @param expected is the widest static-type that the expression can have.
     */
    public void expectSubtype(final IExpression expression,
                              final IExpressionType expected)
    {
        final IExpressionType actual = program.symbols.expressions.get(expression);

        if (actual.isSubtypeOf(expected))
        {
            return;
        }

        final String MESSAGE = "The static-type of an expression was unexpected.";

        final ErrorReport report = new ErrorReport(expression, ErrorCode.WRONG_TYPE, MESSAGE);
        {
            report.addDetail("Expected", Utils.sourceName(expected));
            report.addDetail("Actual", Utils.sourceName(actual));
        }

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void expectVoidFunction(final IStatement statement,
                                   final IMethod function)
    {
//        if (function.getReturnType().isVoidType() == false)
//        {
//            return;
//        }
//
//        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_VOID_FUNCTION;
//
//        final String MESSAGE = "Void was found were it is not allowed.";
//
//        final ErrorReport report = new ErrorReport(statement, ERROR_CODE, MESSAGE);
//
//        reporter.reportFailedCheck(report);
//
//        throw new TypeUsageCheckFailed();
    }

    /**
     * This method ensures that an expression does not have a static-type of void.
     *
     * @param expression is the expression that cannot be void.
     */
    public void requireNonVoid(final IExpression expression)
    {
        final IType actual = program.symbols.expressions.get(expression);

        if ("V".equals(actual.getDescriptor()) == false)
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.VALUE_REQUIRED;

        final String MESSAGE = "The type of an expression is void when it is forbidden to be.";

        final ErrorReport report = new ErrorReport(expression, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that an expression does not have the null-type as its static-type.
     *
     * @param expression is the expression that cannot be of the null-type.
     */
    public void requireNonNull(final IExpression expression)
    {
    }

    /**
     * This method ensures that the static-type of an expression is a reference-type.
     *
     * @param expression is the expression that must be a reference-type.
     */
    public void requireReferenceType(final IExpression expression)
    {
        final IExpressionType actual = program.symbols.expressions.get(expression);

        if (actual.isReferenceType())
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_REFERENCE_TYPE;

        final String MESSAGE = "The type of a particular expression must be a reference-type.";

        final ErrorReport report = new ErrorReport(expression, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type is a variable-type.
     *
     * @param construct is the construct that is performing the type-check.
     * @param expression is the expression that must be a variable-type.
     */
    public void requireVariableType(final IConstruct construct,
                                    final IExpressionType type)
    {
        if (type.isPrimitiveType())
        {
            return;
        }
        else if (type.isReferenceType() && !type.isNullType())
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_VARIABLE_TYPE;

        final String MESSAGE = "A variable-type was expected.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type is a return-type.
     *
     * @param construct is the construct that is performing the type-check.
     * @param expression is the expression that must be a return-type.
     */
    public void requireReturnType(final IConstruct construct,
                                  final IExpressionType type)
    {
        if (type instanceof IReturnType)
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_RETURN_TYPE;

        final String MESSAGE = "A return-type was expected.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type is a reference-type.
     *
     * @param construct is the construct that is performing the type-check.
     * @param expression is the expression that must be a variable-type.
     */
    public void requireReferenceType(final IConstruct construct,
                                     final IExpressionType type)
    {
        if (type instanceof IReferenceType)
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_REFERENCE_TYPE;

        final String MESSAGE = "A reference-type was expected.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type is a class-type.
     *
     * @param construct is the construct that is performing the type-check.
     * @param expression is the expression that must be a variable-type.
     */
    public void requireClassType(final IConstruct construct,
                                 final IExpressionType type)
    {
        if (type.isReferenceType() && ((IReferenceType) type).isClassType())
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_CLASS_TYPE;

        final String MESSAGE = "A class-type was expected.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type is an interface-type.
     *
     * @param construct is the construct that is performing the type-check.
     * @param expression is the expression that must be an interface-type.
     */
    public void requireInterfaceType(final IConstruct construct,
                                     final IExpressionType type)
    {
        if (type.isReferenceType() && ((IReferenceType) type).isInterfaceType())
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_INTERFACE_TYPE;

        final String MESSAGE = "An interface-type was expected.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type is a design-type.
     *
     * @param construct is the construct that is performing the type-check.
     * @param expression is the expression that must be a design-type.
     */
    public void requireDesignType(final IConstruct construct,
                                  final IExpressionType type)
    {
        if (type.isReferenceType() && ((IReferenceType) type).isInterfaceType() && type.isSubtypeOf(program.typesystem.utils.RECORD))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_DESIGN_TYPE;

        final String MESSAGE = "A design-type was expected.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type is a declared-type.
     *
     * @param construct is the construct that is performing the type-check.
     * @param expression is the expression that must be a declared-type.
     */
    public void requireDeclaredType(final IConstruct construct,
                                    final IExpressionType type)
    {
        if (type.isReferenceType() && ((IReferenceType) type).isDeclaredType())
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_DECLARED_TYPE;

        final String MESSAGE = "A declared-type was expected.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void requireString(final IExpression expression)
    {
        final IType actual = program.symbols.expressions.get(expression);

        if ("Ljava/lang/String;".equals(actual.getDescriptor()))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_STRING;

        final String MESSAGE = "A java.lang.String was expected.";

        final ErrorReport report = new ErrorReport(expression, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that an expression produces an primitive-int result.
     *
     * @param expression is the expression to type-check.
     */
    public void requireInteger(final IExpression expression)
    {
        final IType actual = program.symbols.expressions.get(expression);

        if (program.typesystem.utils.assign(actual, program.typesystem.utils.PRIMITIVE_INT) != null)
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_INTEGER;

        final String MESSAGE = "An integer was expected.";

        final ErrorReport report = new ErrorReport(expression, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void requireIterable(final IExpression expression)
    {
        final IType actual = program.symbols.expressions.get(expression);

        if (actual.isSubtypeOf(program.typesystem.utils.ITERABLE))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_ITERABLE;

        final String MESSAGE = "A java.lang.Iterable was expected.";

        final ErrorReport report = new ErrorReport(expression, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void requireThrowable(final IExpression expression)
    {
        final IType actual = program.symbols.expressions.get(expression);

        if (actual.isSubtypeOf(program.typesystem.utils.THROWABLE))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_THROWABLE;

        final String MESSAGE = "A java.lang.Throwable was expected.";

        final ErrorReport report = new ErrorReport(expression, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type-specifier specifies a Throwable type.
     *
     * @param specifier must specify a Throwable type.
     * @param type must be a subtype of Throwable.
     */
    public void requireThrowable(final TypeSpecifier specifier,
                                 final IType type)
    {
        if (type.isSubtypeOf(program.typesystem.utils.THROWABLE))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_THROWABLE;

        final String MESSAGE = "A java.lang.Throwable was expected.";

        final ErrorReport report = new ErrorReport(specifier, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type-specifier specifies a defined-functor-type.
     *
     * @param specifier must specify a defined-functor-type.
     * @param type must be a subtype of DefinedFunctor.
     */
    public void requireDefinedFunctorType(final TypeSpecifier specifier,
                                          final IType type)
    {
        if (type.isSubtypeOf(program.typesystem.typefactory().fromClass(DefinedFunctor.class)))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_DEFINED_FUNCTOR_TYPE;

        final String MESSAGE = "An autumn.lang.DefinedFunctor was expected.";

        final ErrorReport report = new ErrorReport(specifier, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type-specifier specifies a functor-type.
     *
     * @param specifier must specify a functor-type.
     * @param type must be a subtype of Functor.
     */
    public void requireFunctorType(final TypeSpecifier specifier,
                                   final IType type)
    {
        if (type.isSubtypeOf(program.typesystem.utils.FUNCTOR))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_FUNCTOR_TYPE;

        final String MESSAGE = "An autumn.lang.Functor was expected.";

        final ErrorReport report = new ErrorReport(specifier, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type-specifier specifies a module-type.
     *
     * @param specifier must specify a module-type.
     * @param type must be a subtype of Module.
     */
    public void requireModule(final TypeSpecifier specifier,
                              final IType type)
    {
        if (type.isSubtypeOf(program.typesystem.utils.MODULE))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_MODULE_TYPE;

        final String MESSAGE = "An autumn.lang.Module was expected.";

        final ErrorReport report = new ErrorReport(specifier, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type is the void-type.
     *
     * @param site is the location of the type-check.
     * @param type must be the void-type.
     */
    public void requireVoid(final IConstruct site,
                            final IType type)
    {
        if (type.equals(program.typesystem.utils.VOID))
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.EXPECTED_VOID;

        final String MESSAGE = "Type void was expected.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a type-declaration is not a duplicate.
     *
     * @param type is the name of the type being declared.
     * @param descriptor is the type-descriptor of the type being declared.
     */
    public void requireNonDuplicateType(final Name type,
                                        final String descriptor)
    {
        if (program.typesystem.typefactory().findType(descriptor) == null)
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_TYPE;

        final String MESSAGE = "The fully-qualified name of a type must be unique.";

        final ErrorReport report = new ErrorReport(type, ERROR_CODE, MESSAGE);

        report.addDetail("Type", type.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a type is not accessible where it is used.
     *
     * @param specifier specifies the inaccessible type.
     * @param type is the type that is inaccessible.
     */
    public void reportInaccessibleType(final TypeSpecifier specifier,
                                       final IReturnType type)
    {
        final ErrorCode ERROR_CODE = ErrorCode.INACCESSIBLE_TYPE;

        final String MESSAGE = "The specified type is not accessible from where it is used.";

        final ErrorReport report = new ErrorReport(specifier, ERROR_CODE, MESSAGE);

        report.addDetail("Type", Utils.sourceName(type));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a type inherits from itself.
     *
     * @param declaration is the declaration of the erroneous type.
     * @param type is the type-system representation of the erroneous type.
     */
    public void reportCircularInheritance(final IConstruct declaration,
                                          final IReturnType type)
    {
        final ErrorCode ERROR_CODE = ErrorCode.CIRCULAR_INHERITANCE;

        final String MESSAGE = "A type cannot be a subtype of itself.";

        final ErrorReport report = new ErrorReport(declaration, ERROR_CODE, MESSAGE);

        report.addDetail("Type", Utils.sourceName(type));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports a duplicate enum-constant.
     *
     * @param enumeration is the type-system representation of the enum.
     * @param constant is the name of the duplicated enum-constant.
     */
    public void reportDuplicateEnumConstant(final IEnumType enumeration,
                                            final Name constant)
    {
        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_CONSTANT;

        final String MESSAGE = "No two enum-constants, in the same enum, can share their name.";

        final ErrorReport report = new ErrorReport(constant, ERROR_CODE, MESSAGE);

        report.addDetail("Enum Type", Utils.sourceName(enumeration));

        report.addDetail("Constant", constant.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void reportNoSuchAsConversion(final AsOperation conversion,
                                         final IReturnType type)
    {
        final IExpressionType actual = program.symbols.expressions.get(conversion.getValue());

        final ErrorCode ERROR_CODE = ErrorCode.IMPOSSIBLE_CONVERSION;

        final String MESSAGE = "An as-conversion is not possible.";

        final ErrorReport report = new ErrorReport(conversion, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void reportNoSuchIsConversion(final IsOperation conversion,
                                         final IReturnType type)
    {
        final IExpressionType actual = program.symbols.expressions.get(conversion.getValue());

        final ErrorCode ERROR_CODE = ErrorCode.IMPOSSIBLE_CONVERSION;

        final String MESSAGE = "An is-conversion is not possible.";

        final ErrorReport report = new ErrorReport(conversion, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that two operands are not compatible with each other.
     *
     * @param operation is the operation that owns the operands.
     * @param left is the left-operand.
     * @param right is the right-operand.
     */
    public void reportIncompatibleOperands(final IExpression operation,
                                           final IExpressionType left,
                                           final IExpressionType right)
    {
        final ErrorCode ERROR_CODE = ErrorCode.INCOMPATIBLE_OPERANDS;

        final String MESSAGE = "One of the operands must be a subtype of the other.";

        final ErrorReport report = new ErrorReport(operation, ERROR_CODE, MESSAGE);

        report.addDetail("left.type", Utils.simpleName(left));

        report.addDetail("right.type", Utils.simpleName(right));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that an instance-of operation is not viable.
     *
     * @param operation is the instance-of operation.
     * @param value is the static-type of the <i>value</i>.
     * @param type is the static-type of the <i>type</i>.
     */
    public void reportNonViableInstanceOf(final InstanceOfExpression operation,
                                          final IExpressionType value,
                                          final IDeclaredType type)
    {
        final ErrorCode ERROR_CODE = ErrorCode.NON_VIABLE_INSTANCEOF;

        final String MESSAGE = "An instance-of operation must be viable.";

        final ErrorReport report = new ErrorReport(operation, ERROR_CODE, MESSAGE);

        report.addDetail("Problem", Utils.simpleName(value) + " is never an instance-of " + Utils.simpleName(type));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that the resolution of a field failed.
     *
     *
     * @param construct is the invocation itself.
     * @param shared is true, iff a static method was being resolved.
     * @param owner is the type of the method's owner.
     * @param name is then name of the method.
     * @param arguments are the types of the method's arguments.
     */
    public void reportNoSuchField(final IConstruct construct,
                                  final boolean shared,
                                  final IReturnType owner,
                                  final String name)
    {
        final ErrorCode ERROR_CODE = ErrorCode.NO_SUCH_FIELD;

        final String MESSAGE = "No acceptable field was found.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        final String dot = shared ? "::" : ".";

        report.addDetail("Field", Utils.simpleName(owner) + dot + name);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that the resolution of a constructor failed.
     *
     * @param site is the location where the constructor is being invoked from.
     * @param owner is the type that owns the constructor.
     * @param arguments are the types of the arguments to the constructor.
     */
    public void reportNoSuchConstructor(final IConstruct site,
                                        final IReturnType owner,
                                        final List<IExpressionType> arguments)
    {
        final ErrorCode ERROR_CODE = ErrorCode.NO_SUCH_CONSTRUCTOR;

        final String MESSAGE = "No accessible constructor was found that will accept the given arguments.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Type", Utils.sourceName(owner));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that the resolution of a method failed.
     *
     * @param construct is the invocation itself.
     * @param shared is true, iff a static method was being resolved.
     * @param owner is the type of the method's owner.
     * @param name is then name of the method.
     * @param arguments are the types of the method's arguments.
     */
    public void reportNoSuchMethod(final IConstruct construct,
                                   final boolean shared,
                                   final IReturnType owner,
                                   final String name,
                                   final List<IExpressionType> arguments)
    {
        final ErrorCode ERROR_CODE = ErrorCode.NO_SUCH_METHOD;

        final String MESSAGE = "No acceptable method overload was found.";

        final ErrorReport report = new ErrorReport(construct, ERROR_CODE, MESSAGE);

        final String dot = shared ? "::" : ".";

        report.addDetail("Invocation", describeInvocation(owner, dot, name, arguments));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void requireType(final TypeSpecifier specifier,
                            final IType type)
    {
        if (type != null)
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.NO_SUCH_TYPE;

        final String MESSAGE = "The specified type does not exist.";

        final ErrorReport report = new ErrorReport(specifier, ERROR_CODE, MESSAGE);

        report.addDetail("Type", program.typesystem.utils.extractTypeName(specifier));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void requireArguments(final Iterable<IExpression> arguments)
    {
        for (IExpression x : arguments)
        {
            requireNonVoid(x);
        }
    }

    /**
     * This method ensures that an a string-literal does not contain malformed escapes-sequences.
     *
     * @param expression is the expression to type-check.
     */
    public void requireWellFormedStringLiteral(final StringDatum datum)
    {
        if (datum.getVerbatim())
        {
            return;
        }

        try
        {
            F.escape(datum.getValue());
        }
        catch (IllegalArgumentException ex)
        {
            final ErrorCode ERROR_CODE = ErrorCode.MALFORMED_STRING_LITERAL;

            final String MESSAGE = "A non-verbatim string cannot contain malformed escape-sequences.";

            final ErrorReport report = new ErrorReport(datum, ERROR_CODE, MESSAGE);

            report.addDetail("Detail", ex.getMessage());

            /**
             * Issue the error-report to the user.
             */
            report(report);
        }
    }

    public void reportDuplicateVariable(final Variable variable,
                                        boolean condition)
    {
        if (!condition)
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_VARIABLE;

        final String MESSAGE = "A variable was declared more than once in the same scope.";

        final ErrorReport report = new ErrorReport(variable, ERROR_CODE, MESSAGE);

        report.addDetail("Variable", variable.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void reportDuplicateExceptionHandler(final ExceptionHandler handler,
                                                final IExpressionType type)
    {
        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_EXCEPTION_HANDLER;

        final String MESSAGE = "Exception handlers in a try-catch block share a type.";

        final ErrorReport report = new ErrorReport(handler, ERROR_CODE, MESSAGE);

        report.details().put("Type", Utils.sourceName(type));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a break-statement is located outside of a loop.
     *
     * @param statement is the break-statement itself.
     */
    public void reportBreakOutsideOfLoop(final BreakStatement statement)
    {
        final ErrorCode ERROR_CODE = ErrorCode.BREAK_OUTSIDE_OF_LOOP;

        final String MESSAGE = "A break-statement can only be used inside of a loop construct.";

        final ErrorReport report = new ErrorReport(statement, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a continue-statement is located outside of a loop.
     *
     * @param statement is the continue-statement itself.
     */
    public void reportContinueOutsideOfLoop(final ContinueStatement statement)
    {
        final ErrorCode ERROR_CODE = ErrorCode.CONTINUE_OUTSIDE_OF_LOOP;

        final String MESSAGE = "A continue-statement can only be used inside of a loop construct.";

        final ErrorReport report = new ErrorReport(statement, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a redo-statement is located outside of a loop.
     *
     * @param statement is the redo-statement itself.
     */
    public void reportRedoOutsideOfLoop(final RedoStatement statement)
    {
        final ErrorCode ERROR_CODE = ErrorCode.REDO_OUTSIDE_OF_LOOP;

        final String MESSAGE = "A redo-statement can only be used inside of a loop construct.";

        final ErrorReport report = new ErrorReport(statement, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void requireTypedFunctor(final IExpressionType type)
    {
    }

    public void requireAutumnObject(final IExpressionType type)
    {
    }

    public void requireEnum(final IExpressionType type)
    {
    }

    public void reportDuplicateLabel(final Label label)
    {
        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_LABEL;

        final String MESSAGE = "A label was declared more than once in the same scope.";

        final ErrorReport report = new ErrorReport(label, ERROR_CODE, MESSAGE);

        report.addDetail("Label", label.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    public void reportUndeclaredLabel(final Label label)
    {
        final ErrorCode ERROR_CODE = ErrorCode.NO_SUCH_LABEL;

        final String MESSAGE = "A label was used, but not declared.";

        final ErrorReport report = new ErrorReport(label, ERROR_CODE, MESSAGE);

        report.addDetail("Label", label.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a unary operator cannot be applied to its operand.
     *
     * @param operation is the unary operation itself.
     */
    public void reportNoSuchUnaryOperator(final IUnaryOperation operation,
                                          final IExpressionType operand)
    {
        final ErrorCode ERROR_CODE = ErrorCode.NO_SUCH_UNARY_OPERATOR;

        final String MESSAGE = "A unary operator cannot be applied to the given operand type.";

        final ErrorReport report = new ErrorReport(operation, ERROR_CODE, MESSAGE);

        report.addDetail("operand.type", Utils.sourceName(operand));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a binary operator cannot be applied to its operands.
     *
     * @param operation is the binary operation itself.
     */
    public void reportNoSuchBinaryOperator(final IBinaryOperation operation,
                                           final IExpressionType left,
                                           final IExpressionType right)
    {
        final ErrorCode ERROR_CODE = ErrorCode.NO_SUCH_BINARY_OPERATOR;

        final String MESSAGE = "A binary operator cannot be applied to the given operand types.";

        final ErrorReport report = new ErrorReport(operation, ERROR_CODE, MESSAGE);

        report.addDetail("left.type  ", Utils.sourceName(left));
        report.addDetail("right.type ", Utils.sourceName(right));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method creates a string that describes a method invocation.
     *
     * @param owner is the owner-type.
     * @param dot is the separator between the owner and the name.
     * @param name is the name of the method.
     * @param arguments are the types of the arguments.
     * @return the description.
     */
    private String describeInvocation(final IExpressionType owner,
                                      final String dot,
                                      final String name,
                                      final Iterable<IExpressionType> arguments)
    {
        /**
         * Get the simple-names of the arguments.
         */
        final List<String> args = Lists.newLinkedList();

        for (IExpressionType arg : arguments)
        {
            args.add(Utils.simpleName(arg));
        }

        /**
         * Create the description.
         */
        final StringBuilder result = new StringBuilder();

        result.append(Utils.simpleName(owner));
        result.append(dot);
        result.append(name);
        result.append(F.str(args, "(", ", ", ")"));

        return result.toString();
    }

    /**
     * This method ensures that an expression is assignable to a particular type.
     *
     * <p>
     * This method takes into account boxing, unboxing, and coercion.
     * </p>
     *
     * @param site is the construct that is performing the assignment.
     * @param target is the type of the location where the value will be assigned.
     * @param expression is the expression that produces the value to assign.
     */
    public void checkAssign(final IConstruct site,
                            final IExpressionType target,
                            final IExpression expression)
    {
        final IExpressionType etype = program.symbols.expressions.get(expression);

        if (program.typesystem.utils.assign(etype, target) != null)
        {
            return; // OK
        }

        final ErrorCode ERROR_CODE = ErrorCode.IMPOSSIBLE_ASSIGNMENT;

        final String MESSAGE = "An impossible assignment operation was detected.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Assignment", Utils.simpleName(target) + " (Target) = " + Utils.simpleName(etype) + " (Value)");

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that an expression is returnable given a function's return-type.
     *
     * <p>
     * This method takes into account boxing, unboxing, and coercion.
     * </p>
     *
     * @param site is the construct that is performing the return.
     * @param target is the return-type of the function.
     * @param expression is the expression that produces the value to return.
     */
    public void checkReturn(final IConstruct site,
                            final IExpressionType target,
                            final IExpression expression)
    {
        final IExpressionType etype = program.symbols.expressions.get(expression);

        if (program.typesystem.utils.assign(etype, target) != null)
        {
            return; // OK
        }

        final ErrorCode ERROR_CODE = ErrorCode.WRONG_TYPE;

        final String MESSAGE = "The value being returned must be assignable to the return-type.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Return Type", Utils.simpleName(target));
        report.addDetail("Actual Type", Utils.simpleName(etype));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that an assignment is not to a readonly field.
     *
     * @param site is the construct that is performing the assignment.
     * @param field is the field being assigned to.
     */
    public void requireNonFinalFieldAssignment(final IConstruct site,
                                               final IField field)
    {
        if (Modifier.isFinal(field.getModifiers()) == false)
        {
            return; // OK
        }

        final ErrorCode ERROR_CODE = ErrorCode.ASSIGNMENT_TO_READONLY;

        final String MESSAGE = "A value cannot be assigned to a final field.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        final String dot = Modifier.isStatic(field.getModifiers()) ? "::" : ".";

        report.addDetail("Field", Utils.simpleName(field.getOwner()) + dot + field.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a direct supertype appeared twice in a type declaration.
     *
     * @param site is the struct that declares the same supertype twice.
     * @param type is the supertype that is declared twice.
     */
    public void reportDuplicateDirectSupertype(final IConstruct site,
                                               final IExpressionType type)
    {
        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_SUPERTYPE;

        final String MESSAGE = "A supertype can appear only once in an extends-clause.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Super Type", Utils.sourceName(type));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a design/struct/tuple contains a duplicate element.
     *
     * @param element is one of the duplicates.
     */
    public void reportDuplicateElement(final Element element)
    {
        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_ELEMENT;

        final String MESSAGE = "A record can only declare an element once per definition";

        final ErrorReport report = new ErrorReport(element, ERROR_CODE, MESSAGE);

        report.addDetail("Element", element.getName().getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports a duplicate annotation in an annotation-list.
     *
     * @param annotations is the list of annotations.
     * @param annotation is the duplicate annotation.
     */
    public void reportDuplicateAnnotation(final AnnotationList annotations,
                                          final IAnnotation annotation)
    {
        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_ANNOTATION;

        final String MESSAGE = "An annotation can only occur once in a list of annotations.";

        final ErrorReport report = new ErrorReport(annotations, ERROR_CODE, MESSAGE);

        report.addDetail("Type", Utils.sourceName(annotation.getAnnotationType()));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that an annotation cannot be used by Autumn.
     *
     * @param site is the annotation's AST node.
     * @param type is the type of the annotation.
     */
    public void reportUnusableAnnotation(final Annotation site,
                                         final IAnnotation type)
    {
        final ErrorCode ERROR_CODE = ErrorCode.UNUSABLE_ANNOTATION;

        final String MESSAGE = "An unusable annotation was encountered.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Type", Utils.sourceName(type.getAnnotationType()));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that an element in a design/struct/tuple cannot override another.
     *
     * @param site is the AST node of the definition.
     * @param violation describes the covariance violation.
     */
    public void reportCovarianceViolation(final IConstruct site,
                                          final CovarianceViolation violation)
    {
        final ErrorCode ERROR_CODE = ErrorCode.COVARIANCE_VIOLATION;

        final String MESSAGE = "A covariance violation was encountered.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Element", violation.lower.name);
        report.addDetail("Upper", Utils.sourceName(violation.upper.parameter));
        report.addDetail("Lower", Utils.sourceName(violation.lower.parameter));

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a progn-expression is empty.
     *
     * @param site is the AST node of the progn-expression.
     */
    public void reportEmptyProgn(final PrognExpression site)
    {
        final ErrorCode ERROR_CODE = ErrorCode.EMPTY_PROGN;

        final String MESSAGE = "A progn-expression cannot be empty.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a variable is not mutable.
     *
     * @param site is the AST node representation of the variable.
     * @param mutable is true, iff the variable is true.
     */
    public void requireMutableVariable(final Variable site,
                                       final boolean mutable)
    {
        if (mutable)
        {
            return;
        }

        final ErrorCode ERROR_CODE = ErrorCode.MUTABLE_VARIABLE_REQUIRED;

        final String MESSAGE = "A mutable variable is required.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Variable", site.getName());

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that the number of given arguments does not match the number of parameters.
     *
     * @param site is the recur-statement that is providing the arguments.
     * @param required is the number of parameters.
     */
    public void reportBadArgumentCount(final RecurStatement site,
                                       final int required)
    {
        final ErrorCode ERROR_CODE = ErrorCode.BAD_ARGUMENT_COUNT;

        final String MESSAGE = "The number of arguments must match the number of parameters.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Argument Count", "" + site.getArguments().size());
        report.addDetail("Parameter Count", "" + required);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a method does not exist.
     *
     * @param object is the site where the method is needed.
     * @param owner is the type that should contain the method.
     * @param name is the name of the method.
     */
    public void reportNoSuchMethod(final DelegateStatement site,
                                   final IClassType owner,
                                   final String name)
    {
        final ErrorCode ERROR_CODE = ErrorCode.NO_SUCH_METHOD;

        final String MESSAGE = "The handler function does not exist.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        report.addDetail("Owner", Utils.sourceName(owner));
        report.addDetail("Name", name);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a method is overloaded when it should not be overloaded.
     *
     * @param object is the site where the method is being used.
     * @param owner is the type that contains the method.
     * @param name is the name of the method.
     */
    public void reportOverloadedMethod(final DelegateStatement site,
                                       final IClassType owner,
                                       final String name)
    {
        final ErrorCode ERROR_CODE = ErrorCode.OVERLOADED_METHOD;

        final String MESSAGE = "The handler function cannot be overloaded.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a delegate is incompatible with the function that it refers to.
     *
     * @param site is the site where the delegate is being created.
     * @param delegate is the type of the delegate's invoke(*) method.
     * @param handler is the type of the function that the delegate refers to.
     */
    public void reportIncompatibleDelegate(final DelegateStatement site,
                                           final IMethod delegate,
                                           final IMethod handler)
    {
        final ErrorCode ERROR_CODE = ErrorCode.INCOMPATIBLE_DELEGATE;

        final String MESSAGE = "The handler function is not compatible with the delegate.";

        final ErrorReport report = new ErrorReport(site, ERROR_CODE, MESSAGE);

        // TODO: improve error details

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method reports that a function is a duplicate.
     *
     * @param function is basically the function itself.
     */
    public void reportDuplicateFunction(final FunctionCompiler function)
    {
        Preconditions.checkNotNull(function);

        final ErrorCode ERROR_CODE = ErrorCode.DUPLICATE_FUNCTION;

        final String MESSAGE = "Functions in the same module cannot share both their name and parameters.";

        final ErrorReport report = new ErrorReport(function.node, ERROR_CODE, MESSAGE);

        report.addDetail("Name", function.type.getName());

        // TODO: Improve error details

        /**
         * Issue the error-report to the user.
         */
        report(report);
    }

    /**
     * This method ensures that a name is not a given forbidden name.
     *
     * @param name is the name that may be forbidden.
     * @param forbidden is the name that is unspeakable.
     */
    public void checkName(final Name name,
                          final String forbidden)
    {
        Preconditions.checkNotNull(name);
        Preconditions.checkNotNull(forbidden);

        if (forbidden.equals(name.getName()))
        {

            final ErrorCode ERROR_CODE = ErrorCode.NAME_CONFLICT;

            final String MESSAGE = "An unspeakable name was spoken.";

            final ErrorReport report = new ErrorReport(name, ERROR_CODE, MESSAGE);

            report.addDetail("Name", forbidden);

            /**
             * Issue the error-report to the user.
             */
            report(report);
        }
    }

    /**
     * This method reports that a compilation-unit contains more than one entry-point.
     *
     * @param function is one of the entry-points.
     */
    public void reportTooManyStarts(final FunctionDefinition node)
    {
        Preconditions.checkNotNull(node);

        final ErrorCode ERROR_CODE = ErrorCode.TOO_MANY_STARTS;

        final String MESSAGE = "A compilation-unit can only contain a single start-function.";

        final ErrorReport report = new ErrorReport(node, ERROR_CODE, MESSAGE);

        /**
         * Issue the error-report to the user.
         */
        report(report);

    }

    /**
     * This method reports that the signature of a function is inappropriate given a particular annotation.
     *
     * @param function is basically the function itself..
     * @param code is the error-code to issue.
     * @param signature is a description of what the function signature should be.
     */
    public void reportWrongSignatureForAnnotation(final FunctionCompiler function,
                                                final ErrorCode code,
                                                final String signature)
    {
        Preconditions.checkNotNull(function);
        Preconditions.checkNotNull(code);

        final String MESSAGE = "A different function signature is required.";

        final ErrorReport report = new ErrorReport(function.node, code, MESSAGE);

        report.addDetail("Required Signature", signature);

        /**
         * Issue the error-report to the user.
         */
        report(report);

    }
}