Package org.openquark.cal.compiler

Source Code of org.openquark.cal.compiler.SourceMetricFinder$LintWalker

/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright notice,
*       this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of Business Objects nor the names of its contributors
*       may be used to endorse or promote products derived from this software
*       without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/


/*
* SourceDependencyFinder.java
* Creation date: (Mar 24, 2005)
* By: Jawright
*/
package org.openquark.cal.compiler;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.openquark.cal.compiler.SourceModel.Expr;
import org.openquark.cal.compiler.SourceModel.FieldPattern;
import org.openquark.cal.compiler.SourceModel.FunctionDefn;
import org.openquark.cal.compiler.SourceModel.FunctionTypeDeclaration;
import org.openquark.cal.compiler.SourceModel.Import;
import org.openquark.cal.compiler.SourceModel.InstanceDefn;
import org.openquark.cal.compiler.SourceModel.LocalDefn;
import org.openquark.cal.compiler.SourceModel.ModuleDefn;
import org.openquark.cal.compiler.SourceModel.Name;
import org.openquark.cal.compiler.SourceModel.Parameter;
import org.openquark.cal.compiler.SourceModel.Pattern;
import org.openquark.cal.compiler.SourceModel.SourceElement;
import org.openquark.cal.compiler.SourceModel.TopLevelSourceElement;
import org.openquark.cal.compiler.SourceModel.TypeClassDefn;
import org.openquark.cal.compiler.SourceModel.TypeExprDefn;
import org.openquark.cal.compiler.SourceModel.Expr.Application;
import org.openquark.cal.compiler.SourceModel.Expr.BinaryOp;
import org.openquark.cal.compiler.SourceModel.Expr.DataCons;
import org.openquark.cal.compiler.SourceModel.Expr.Lambda;
import org.openquark.cal.compiler.SourceModel.Expr.Let;
import org.openquark.cal.compiler.SourceModel.Expr.Parenthesized;
import org.openquark.cal.compiler.SourceModel.Expr.SelectDataConsField;
import org.openquark.cal.compiler.SourceModel.Expr.UnaryOp;
import org.openquark.cal.compiler.SourceModel.Expr.Unit;
import org.openquark.cal.compiler.SourceModel.Expr.Var;
import org.openquark.cal.compiler.SourceModel.Expr.BinaryOp.Apply;
import org.openquark.cal.compiler.SourceModel.Expr.BinaryOp.BackquotedOperator;
import org.openquark.cal.compiler.SourceModel.Expr.BinaryOp.Compose;
import org.openquark.cal.compiler.SourceModel.Expr.Case.Alt.UnpackDataCons;
import org.openquark.cal.compiler.SourceModel.Expr.Case.Alt.UnpackListCons;
import org.openquark.cal.compiler.SourceModel.Expr.Case.Alt.UnpackListNil;
import org.openquark.cal.compiler.SourceModel.Expr.Case.Alt.UnpackUnit;
import org.openquark.cal.compiler.SourceModel.Expr.UnaryOp.Negate;
import org.openquark.cal.compiler.SourceModel.FunctionDefn.Algebraic;
import org.openquark.cal.compiler.SourceModel.FunctionDefn.Foreign;
import org.openquark.cal.compiler.SourceModel.FunctionDefn.Primitive;
import org.openquark.cal.compiler.SourceModel.Import.UsingItem;
import org.openquark.cal.compiler.SourceModel.InstanceDefn.InstanceMethod;
import org.openquark.cal.compiler.SourceModel.InstanceDefn.InstanceTypeCons.TypeCons;
import org.openquark.cal.compiler.SourceModel.LocalDefn.Function.Definition;
import org.openquark.cal.compiler.SourceModel.TypeClassDefn.ClassMethodDefn;
import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn.AlgebraicType;
import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn.ForeignType;
import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn.AlgebraicType.DataConsDefn;
import org.openquark.cal.filter.AcceptAllQualifiedNamesFilter;
import org.openquark.cal.filter.QualifiedNameFilter;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.util.ArrayStack;
import org.openquark.util.Pair;


/**
* Provides methods for finding various metrics associated with CAL modules.
*
* Currently implemented metrics:
*
*    - Reference frequency: Each gem is given an associated reference frequency count, which
*                           represents the number of times that the gem is referred to in the
*                           body of other functions.  The raw data collected are the number of
*                           times each function refers to its dependees (referencees?).  These
*                           raw data data are aggregated by the ModuleSourceMetrics and
*                           WorkspaceSourceMetricsManager classes into the workspace-level
*                           reference frequency counts that are provided to clients.
*   
*    - Compositional frequency: Each pair of gems (A,B) is given an associated frequency count which
*                               represents the number of times the output of B is passed directly as
*                               an argument of A.
*     
*    - Lint warnings: The source is scanned for potential optimization issues and a list of
*                     warning objects is returned to the client.
*
*    - Reference and application locations: The source is scanned for references to or applications of
*                                           a specified QualifiedName, and a list of SourcePositions is
*                                           returned.  
*
* Creation date: (Mar 24, 2005)
* @author Jawright
*/
abstract class SourceMetricFinder {

    /** This class should never be instantiated */
    private SourceMetricFinder() {
    }

    /**
     * Provides functionality that is common to most of the SourceModelTraversers in
     * the SourceMetricFinder class.
     *
     * Creation date: (Jun 27, 2005)
     * @author Jawright
     */
    private static abstract class MetricVisitor extends BindingTrackingSourceModelTraverser<Void> {

        /** The name of the module being processed */
        private final ModuleName currentModule;
       
        /** Information about the module being processed */
        private final ModuleTypeInfo moduleTypeInfo;
       
        private MetricVisitor(ModuleTypeInfo moduleTypeInfo) {
           
            if (moduleTypeInfo == null) {
                throw new NullPointerException("MetricVisitor needs a non-null moduleTypeInfo to operate upon");
            }

            this.moduleTypeInfo = moduleTypeInfo;
            this.currentModule = moduleTypeInfo.getModuleName();
        }
       
        /**
         * Resolves the given module name in the context of the module being processed based on module name resolution rules
         * and returns the name of the module the given name resolves to. If the original name
         * is not unambiguously resolvable, then the original name is returned.
         *
         * @param moduleName the module name to be resolved. Cannot be null.
         * @return the name of the module the original name resolves to. If the original name
         *         is not unambiguously resolvable, then the original name is returned.
         */
        private ModuleName resolveModuleName(Name.Module moduleName) {
            ModuleNameResolver.ResolutionResult resolution = moduleTypeInfo.getModuleNameResolver().resolve(SourceModel.Name.Module.toModuleName(moduleName));
            return resolution.getResolvedModuleName();
        }
       
        /**
         * Check whether a variable reference counts as a reference to a dependee
         * @param varName The variable reference to check
         * @return true if varName refers to a dependee, false otherwise
         */
        private boolean isDependeeReference(Name.Function varName) {
           
            // References to the current function don't count (ie, a function
            // cannot be a dependee of itself).  All unqualified references to
            // the current function name are disqualified, since they either refer
            // to the current function or to a lexically-scoped variable, neither of
            // which counts as a dependee.
            String currentFunctionName = getCurrentFunction();
            if(currentFunctionName != null &&
               currentFunctionName.equals(varName.getUnqualifiedName()) &&
               (varName.getModuleName() == null || resolveModuleName(varName.getModuleName()).equals(currentModule))) {
                return false;
            }
           
            // All other explicitly-qualified references always count as dependees
            // (because they are guaranteed to be toplevel functional agents)
            if(varName.getModuleName() != null) {
                return true;
            }           
           
            // Unqualified references that match lexcially-scoped variables are not dependees
            if(isBound(varName.getUnqualifiedName())) {
                return false;
            }
           
            // Unqualified references that do not match lexically-scoped
            // variables count as dependees.
            return true;
        }
       
        /**
         * Looks up the named functional agent in the relevant ModuleTypeInfo object and returns it.
         * @param agentName The name (qualified or not) of the functional agent to look up.
         * @return The FunctionalAgent for the functional agent specified by entityName
         */
        private FunctionalAgent getFunctionalAgent(Name.Qualifiable agentName) {
           
            if(!(agentName instanceof Name.DataCons || agentName instanceof Name.Function)) {
                return null;
            }
           
            // Explicitly qualified, non-imported agent
            if (agentName.getModuleName() != null && resolveModuleName(agentName.getModuleName()).equals(currentModule)) {
                return moduleTypeInfo.getFunctionalAgent(agentName.getUnqualifiedName());
            }
           
            // Explicitly qualified, imported agent
            else if (agentName.getModuleName() != null) {
                ModuleTypeInfo externalModuleInfo = moduleTypeInfo.getDependeeModuleTypeInfo(resolveModuleName(agentName.getModuleName()));
                if(externalModuleInfo == null) {
                    //  Some sort of compilation problem
                    return null;
                }
               
                return externalModuleInfo.getFunctionalAgent(agentName.getUnqualifiedName());
            }
           
            // Unqualified, non-imported agent
            else if (agentName.getModuleName() == null && moduleTypeInfo.getFunctionalAgent(agentName.getUnqualifiedName()) != null) {
                return moduleTypeInfo.getFunctionalAgent(agentName.getUnqualifiedName());
            }
           
            // Unqualified, imported function
            else {
                ModuleName usingModuleName = moduleTypeInfo.getModuleOfUsingFunctionOrClassMethod(agentName.getUnqualifiedName());
                if (usingModuleName == null) {
                    usingModuleName = moduleTypeInfo.getModuleOfUsingDataConstructor(agentName.getUnqualifiedName());
                }
               
                ModuleTypeInfo externalModuleInfo = moduleTypeInfo.getDependeeModuleTypeInfo(usingModuleName);
                if(externalModuleInfo == null) {
                    // Some sort of compilation problem
                    return null;
                }
               
                return externalModuleInfo.getFunctionalAgent(agentName.getUnqualifiedName());
            }
        }
               
        /** If the provided expression is a Var or DataCons expression, returns the name of the referenced
         * function or datacons.
         * @param expr A Var or DataCons expression
         * @return unqualified name of the function or datacons referred to by the expression
         */
        private Name.Qualifiable getFunctionalAgentName(Expr expr) {
            if(expr instanceof Var) {
                return ((Var)expr).getVarName();
            } else if(expr instanceof DataCons) {
                return ((DataCons)expr).getDataConsName();
            } else {
                return null;
            }
        }
    }
   
    /**
     * This is an implementation of SourceModelTraverser that gathers the raw data for the reference-frequency metric.
     * Raw data are collected in a single traversal and stored into the associated EnvEntities.
     *
     * Creation date: (Mar 24, 2005)
     * @author Jawright
     */
    private static final class ReferenceFrequencyFinder extends MetricVisitor {
       
        /**
         * Map from (dependee, dependent) pair to number of times dependent references dependee
         */
        private final Map<Pair<QualifiedName, QualifiedName>, Integer> dependeeMap = new HashMap<Pair<QualifiedName, QualifiedName>, Integer>();
       
        /**
         * Set of QualifiedNames from imported modules that occur in this module. 
         */
        private final Set<QualifiedName> importedNameOccurrences = new HashSet<QualifiedName>();
       
        /** Filter for deciding whether a function is to be visited */
        private final QualifiedNameFilter functionFilter;
       
        /** When true, functions that are skipped (because they match the excludeFunctionRegexp)
         * will have their names dumped to the system console.  When false, such functions will
         * be skipped silently.
         */
        private final boolean traceSkippedFunctions;
       
        /**
         * Construct a ReferenceFrequencyFinder
         * @param moduleTypeInfo ModuleTypeInfo of module to be scanned
         * @param functionFilter Filter for deciding which functions will be processed. Cannot be null.
         * @param traceSkippedFunctions When true, each skipped function will have its name dumped to the console.
         *                               When false, skipped functions will be skipped silently
         */
        private ReferenceFrequencyFinder(ModuleTypeInfo moduleTypeInfo, QualifiedNameFilter functionFilter, boolean traceSkippedFunctions) {
            super(moduleTypeInfo);
            if (functionFilter == null) {
                throw new NullPointerException();
            }
            this.functionFilter = functionFilter;
            this.traceSkippedFunctions = traceSkippedFunctions;
        }
       
        /** {@inheritDoc} */
        // Don't visit functions matching the exclusion regexp
        @Override
        public Void visit_FunctionDefn_Algebraic(Algebraic algebraic, Object arg) {
           
            if(!functionFilter.acceptQualifiedName(QualifiedName.make(super.currentModule, algebraic.getName()))) {
                if(traceSkippedFunctions) {
                    System.out.println("Skipping test function " + super.currentModule + "." + algebraic.getName());
                }
                return null;
            }
           
            return super.visit_FunctionDefn_Algebraic(algebraic, arg);
        }
       
        /** {@inheritDoc} */
        // A local pattern match declaration with a data cons pattern counts as a reference to the data constructor.
        @Override
        public Void visit_LocalDefn_PatternMatch_UnpackDataCons(LocalDefn.PatternMatch.UnpackDataCons unpackDataCons, Object arg) {
            Name.DataCons dataName = unpackDataCons.getDataConsName();
            recordDependeeReference(dataName);
           
            return super.visit_LocalDefn_PatternMatch_UnpackDataCons(unpackDataCons, arg);
        }

        /** {@inheritDoc} */
        // A local pattern match declaration with a Cons (:) pattern counts as a reference to Prelude.Cons.
        @Override
        public Void visit_LocalDefn_PatternMatch_UnpackListCons(LocalDefn.PatternMatch.UnpackListCons unpackListCons, Object arg) {
            recordDependeeReference(Name.DataCons.make(CAL_Prelude.DataConstructors.Cons));
            return super.visit_LocalDefn_PatternMatch_UnpackListCons(unpackListCons, arg);
        }

        /** {@inheritDoc} */
        // Using a Cons (:) pattern in a case alternative counts as a reference to Prelude.Cons.
        @Override
        public Void visit_Expr_Case_Alt_UnpackListCons(UnpackListCons cons, Object arg) {
            recordDependeeReference(Name.DataCons.make(CAL_Prelude.DataConstructors.Cons));
            return super.visit_Expr_Case_Alt_UnpackListCons(cons, arg);
        }

        /** {@inheritDoc} */
        // Using a data constructor in a case alternative pattern counts as a reference to the data constructor.
        @Override
        public Void visit_Expr_Case_Alt_UnpackDataCons(UnpackDataCons cons, Object arg) {

            for (int i = 0, nDataConsNames = cons.getNDataConsNames(); i < nDataConsNames; i++) {
                Name.DataCons dataConsName = cons.getNthDataConsName(i);
                recordDependeeReference(dataConsName);
            }
           
            return super.visit_Expr_Case_Alt_UnpackDataCons(cons, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Case_Alt_UnpackListNil(UnpackListNil nil, Object arg) {
            recordDependeeReference(Name.DataCons.make(CAL_Prelude.DataConstructors.Nil));
            return super.visit_Expr_Case_Alt_UnpackListNil(nil, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Case_Alt_UnpackUnit(UnpackUnit unit, Object arg) {
            recordDependeeReference(Name.DataCons.make(CAL_Prelude.DataConstructors.Unit));
            return super.visit_Expr_Case_Alt_UnpackUnit(unit, arg);
        }

        /** {@inheritDoc} */
        // Field selection from a dataCons-valued expression counts as a reference to the data constructor.
        @Override
        public Void visit_Expr_SelectDataConsField(SelectDataConsField field, Object arg) {
            Name.DataCons dataName = field.getDataConsName();
            recordDependeeReference(dataName);
           
            return super.visit_Expr_SelectDataConsField(field, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_DataCons(DataCons cons, Object arg) {
            recordDependeeReference(cons.getDataConsName());
            return super.visit_Expr_DataCons(cons, arg);
        }       
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Var(Var var, Object arg) {
           
            if(super.isDependeeReference(var.getVarName())) {
                recordDependeeReference(var.getVarName());
            }
           
            return super.visit_Expr_Var(var, arg);
        }

        // Binary operations are translated to textual form and then
        // recorded as dependees
        /** {@inheritDoc} */
        @Override
        protected Void visit_Expr_BinaryOp_Helper(BinaryOp binop, Object arg) {
            QualifiedName textualName = OperatorInfo.getTextualName(binop.getOpText());

            // Some operators represent function/method applications, and some (:) represent data constructor applications
            if (textualName.lowercaseFirstChar()) {
                recordDependeeReference(SourceModel.Name.Function.make(textualName));
            } else {
                recordDependeeReference(SourceModel.Name.DataCons.make(textualName));
            }
            return super.visit_Expr_BinaryOp_Helper(binop, arg);
        }
       
        /** {@inheritDoc} */
        // Unary negation is also translated to textual form and then recorded
        // as a dependee
        @Override
        public Void visit_Expr_UnaryOp_Negate(Negate negate, Object arg) {
            recordDependeeReference(SourceModel.Name.Function.make(CAL_Prelude.Functions.negate));
            return super.visit_Expr_UnaryOp_Negate(negate, arg);
        }
       
        /** {@inheritDoc} */
        // The Unit datacons () is translated to textual form and recorded as a dependee
        @Override
        public Void visit_Expr_Unit(Unit unit, Object arg) {
            recordDependeeReference(SourceModel.Name.DataCons.make(CAL_Prelude.DataConstructors.Unit));
            return super.visit_Expr_Unit(unit, arg);
        }
       
        /** {@inheritDoc} */
        // [] is a reference to Prelude.Nil
        @Override
        public Void visit_Expr_List(SourceModel.Expr.List list, Object arg) {
            if(list.getNElements() == 0) {
                recordDependeeReference(SourceModel.Name.DataCons.make(CAL_Prelude.DataConstructors.Nil));
            }
            return super.visit_Expr_List(list, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Import_UsingItem_Function(UsingItem.Function usingItemFunction, Object arg) {

            if(arg == null || !(arg instanceof ModuleName)) {
                throw new IllegalArgumentException("ReferenceFrequencyFinder.visitImportUsingItemFunction expects to be passed a module name as its arg");
            }
           
            ModuleName importedModuleName = (ModuleName)arg;
            String[] usingNames = usingItemFunction.getUsingNames();
            for (final String usingName : usingNames) {
                recordImportedNameOccurrence(QualifiedName.make(importedModuleName, usingName));
            }
           
            return super.visit_Import_UsingItem_Function(usingItemFunction, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Import_UsingItem_DataConstructor(UsingItem.DataConstructor usingItemDataConstructor, Object arg) {

            if(arg == null || !(arg instanceof ModuleName)) {
                throw new IllegalArgumentException("ReferenceFrequencyFinder.visitImportUsingItemDataConstructor expects to be passed a module name as its arg");
            }
           
            ModuleName importedModuleName = (ModuleName)arg;
            String[] usingNames = usingItemDataConstructor.getUsingNames();
            for (final String usingName : usingNames) {
                recordImportedNameOccurrence(QualifiedName.make(importedModuleName, usingName));
            }
           
            return super.visit_Import_UsingItem_DataConstructor(usingItemDataConstructor, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Import_UsingItem_TypeConstructor(UsingItem.TypeConstructor usingItemTypeConstructor, Object arg) {

            if(arg == null || !(arg instanceof ModuleName)) {
                throw new IllegalArgumentException("ReferenceFrequencyFinder.visitImportUsingItemTypeConstructor expects to be passed a module name as its arg");
            }
           
            ModuleName importedModuleName = (ModuleName)arg;
            String[] usingNames = usingItemTypeConstructor.getUsingNames();
            for (final String usingName : usingNames) {
                recordImportedNameOccurrence(QualifiedName.make(importedModuleName, usingName));
            }
           
            return super.visit_Import_UsingItem_TypeConstructor(usingItemTypeConstructor, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Import_UsingItem_TypeClass(UsingItem.TypeClass usingItemTypeClass, Object arg) {

            if(arg == null || !(arg instanceof ModuleName)) {
                throw new IllegalArgumentException("ReferenceFrequencyFinder.visitImportUsingItemTypeClass expects to be passed a module name as its arg");
            }
           
            ModuleName importedModuleName = (ModuleName)arg;
            String[] usingNames = usingItemTypeClass.getUsingNames();
            for (final String usingName : usingNames) {
                recordImportedNameOccurrence(QualifiedName.make(importedModuleName, usingName));
            }
           
            return super.visit_Import_UsingItem_TypeClass(usingItemTypeClass, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Constraint_TypeClass(SourceModel.Constraint.TypeClass typeClass, Object arg) {

            recordImportedNameOccurrence(typeClass.getTypeClassName());
            return super.visit_Constraint_TypeClass(typeClass, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeExprDefn_Function(TypeExprDefn.Function function, Object arg) {

            recordImportedNameOccurrence(CAL_Prelude.TypeConstructors.Function);
            return super.visit_TypeExprDefn_Function(function, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeExprDefn_List(TypeExprDefn.List list, Object arg) {

            recordImportedNameOccurrence(CAL_Prelude.TypeConstructors.List);
            return super.visit_TypeExprDefn_List(list, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeExprDefn_TypeCons(TypeExprDefn.TypeCons cons, Object arg) {
           
            recordImportedNameOccurrence(cons.getTypeConsName());
            return super.visit_TypeExprDefn_TypeCons(cons, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeExprDefn_Unit(TypeExprDefn.Unit unit, Object arg) {
           
            recordImportedNameOccurrence(CAL_Prelude.TypeConstructors.Unit);
            return super.visit_TypeExprDefn_Unit(unit, arg);
        }

        /**
         * Add an entry to the internal tracking set noting that the gem specified by entityName
         * is referred to by the current function.
         * @param entityName Name of the gem referred to by the current function
         */
        private void recordDependeeReference(Name.Qualifiable entityName) {
            FunctionalAgent dependeeEntity = super.getFunctionalAgent(entityName);

            if(dependeeEntity != null) {
                Function currentEntity = super.moduleTypeInfo.getFunction(getCurrentFunction());
                Pair<QualifiedName, QualifiedName> key = new Pair<QualifiedName, QualifiedName>(dependeeEntity.getName(), currentEntity.getName());
               
                if(dependeeMap.get(key) != null) {
                    Integer oldFrequency = dependeeMap.get(key);
                    dependeeMap.put(key, Integer.valueOf(oldFrequency.intValue() + 1));
                } else {
                    dependeeMap.put(key, Integer.valueOf(1));
                }
            }
       
            recordImportedNameOccurrence(entityName);
        }
       
        /**
         * Add an entry to the importedNameOccurrences Set, but not to the dependeeMap.
         * Does nothing if referenceName is from the current module
         * @param occurrenceName QualifiedName to add to tracking
         */
        private void recordImportedNameOccurrence(QualifiedName occurrenceName) {
           
            if(occurrenceName == null) {
                return;
            }
           
            if(occurrenceName.getModuleName().equals(super.currentModule)) {
                return;
            }
           
            importedNameOccurrences.add(occurrenceName);
        }
       
        /**
         * Add an entry to the importedNameOccurrences Set, but not to the dependeeMap.
         * Does nothing if referenceName is from the current module
         * @param occurrenceName Name to add to tracking
         */
        private void recordImportedNameOccurrence(Name.Qualifiable occurrenceName) {
           
            // Short-circuit return won't catch all the cases, but it'll allow us to avoid
            // doing a "using" lookup in some cases.  The QualifiedName-accepting version that
            // we delegate to will catch any same-module cases that fall through this check.
            if(occurrenceName.getModuleName() != null && super.resolveModuleName(occurrenceName.getModuleName()).equals(super.currentModule)) {
                return;
            }
           
            QualifiedName qualifiedName = getQualifiedName(occurrenceName, super.moduleTypeInfo.getModuleNameResolver());
            recordImportedNameOccurrence(qualifiedName);
        }
       
        /**
         * @return A Map from (dependee, dependent) to number of times dependent references dependee
         */
        Map<Pair<QualifiedName, QualifiedName>, Integer> getDependeeMap() {
            return Collections.unmodifiableMap(dependeeMap);
        }
       
        /**
         * @return The Set of names imported from other modules that occur in this module.
         */
        Set<QualifiedName> getImportedNameOccurrences() {
            return Collections.unmodifiableSet(importedNameOccurrences);
        }
    }
       
    /**
     * A SourceModelTraverser that scans a single module for compositional frequencies.
     * Compositional frequency measures how often the return value from one gem is passed
     * directly as an argument to another gem.
     *
     * For example, in the following module:
     *
     *      module Foo;
     *      import Prelude;
     *
     *      bar = 50;
     *      baz arg = 20 + arg;
     *
     *      quux = Prelude.add bar (baz 54);
     *
     *      quuux val = Prelude.add (baz 10) (baz val);
     *
     * The compositional frequency of (Prelude.add, Foo.bar) is 1 (since the output of bar is
     * passed directly to add once in quux). 
     * The compositional frequency of (Prelude.add, Foo.baz) is 3 (since the output of baz is
     * passed directly to add once in quux and twice in quuux).
     *
     * Use this class by instantiating it bound to a module and then passing in a SourceModel
     * representing the same module to visitModuleDefn.  After the module has been walked, calling
     * getCompositionalFrequencyMap will return a Map from gem pair to compositional frequency of the
     * pair.
     *
     * The module to walk and all the modules that it imports is assumed to have already been
     * successfully compiled.
     *
     * This class has no external side effects.  It is mutable (its state is different after it has
     * walked a module).
     *
     * Creation date: (Jun 27, 2005)
     * @author Jawright
     */
    private static final class CompositionalFrequencyFinder extends MetricVisitor {
       
        /**
         * Map from (consumer, producer) Pair to compositional frequency of the pair.
         */ 
        private final Map<Pair<QualifiedName, QualifiedName>, Integer> compositionalFrequencyMap = new HashMap<Pair<QualifiedName, QualifiedName>, Integer>();
       
        /** Filter for deciding whether a function is to be visited */
        private final QualifiedNameFilter functionFilter;
       
        /** When true, functions that are skipped (because they match the excludeFunctionRegexp)
         * will have their names dumped to the system console.  When false, such functions will
         * be skipped silently.
         */
        private final boolean traceSkippedFunctions;
       
        private static final QualifiedName COMPOSE = CAL_Prelude.Functions.compose;
        private static final QualifiedName APPLY = CAL_Prelude.Functions.apply;
       
        /**
         * Construct a CompositionalFrequencyFinder for a specific module.
         * @param moduleTypeInfo ModuleTypeInfo for the module to scan for compositional relationships
         * @param functionFilter Filter for deciding which functions will be processed. Cannot be null.
         * @param traceSkippedFunctions When true, each skipped function will have its name dumped to the console.
         *                               When false, skipped functions will be skipped silently
         */
        private CompositionalFrequencyFinder(ModuleTypeInfo moduleTypeInfo, QualifiedNameFilter functionFilter, boolean traceSkippedFunctions) {
            super(moduleTypeInfo);
            if (functionFilter == null) {
                throw new NullPointerException();
            }
            this.functionFilter = functionFilter;
            this.traceSkippedFunctions = traceSkippedFunctions;
        }
       
        /**
         * @return Map from (consumer, producer) to the compositional frequency of the pair.
         */
        Map<Pair<QualifiedName, QualifiedName>, Integer> getCompositionalFrequencyMap() {
            return Collections.unmodifiableMap(compositionalFrequencyMap);
        }
       
        /** {@inheritDoc} */
        // Don't visit functions matching the exclusion regexp
        @Override
        public Void visit_FunctionDefn_Algebraic(Algebraic algebraic, Object arg) {
           
            if(!functionFilter.acceptQualifiedName(QualifiedName.make(super.currentModule, algebraic.getName()))) {
                if(traceSkippedFunctions) {
                    System.out.println("Skipping test function " + super.currentModule + "." + algebraic.getName());
                }
                return null;
            }
           
            return super.visit_FunctionDefn_Algebraic(algebraic, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Application(Application application, Object arg) {

            Expr consumerExpr = application.getNthExpression(0);
            Name.Qualifiable consumerName = getTopLevelConsumerName(consumerExpr);
            FunctionalAgent consumerEntity = null;
           
            if(consumerName != null && consumerExpr instanceof Var) {
                consumerEntity = super.getFunctionalAgent(consumerName);
            }
           
            // Process the standard case where an expression with a statically-identifiable consumer name
            // accepts some arguments
            if(consumerName != null) {
                for(int i = 1; i < application.getNExpressions(); i++) {
                    processArgumentExpression(consumerName, i, application.getNthExpression(i));
                }
            }
           
            // Special cases for apply and compose calls
            if(consumerEntity != null) {
               
                // `apply arg1 arg2` is effectively `arg1 arg2`
                if(consumerEntity.getName().equals(APPLY)) {
                    Expr effectiveConsumer = application.getNthExpression(1);
                    Name.Qualifiable effectiveConsumerName = getTopLevelConsumerName(effectiveConsumer);
                   
                    if(effectiveConsumerName != null) {
                        processArgumentExpression(effectiveConsumerName, 1, application.getNthExpression(2));
                    }
                }
               
                // `compose arg1 arg2 arg3 ... argn` is effectively `arg1 (arg2 arg3 ... argn)`
                if(consumerEntity.getName().equals(COMPOSE)) {
                    Expr effectiveConsumer1 = application.getNthExpression(1);
                    Name.Qualifiable effectiveConsumer1Name = getTopLevelConsumerName(effectiveConsumer1);
                    Expr effectiveConsumer2 = application.getNthExpression(2);
                    Name.Qualifiable effectiveConsumer2Name = getTopLevelConsumerName(effectiveConsumer2);
                   
                    if(effectiveConsumer1Name != null) {
                        processArgumentExpression(effectiveConsumer1Name, 1, effectiveConsumer2);
                    }
                   
                    if(effectiveConsumer2Name != null) {
                        for(int i = 3; i < application.getNExpressions(); i++) {
                            processArgumentExpression(effectiveConsumer2Name, i, application.getNthExpression(i));
                        }
                    }
                }
            }
           
            return super.visit_Expr_Application(application, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_BinaryOp_BackquotedOperator_Var(BackquotedOperator.Var backquotedOperator, Object arg) {
            Expr consumerExpr = backquotedOperator.getOperatorVarExpr();
            Name.Qualifiable consumerName = getTopLevelConsumerName(consumerExpr);

            processArgumentExpression(consumerName, 1, backquotedOperator.getLeftExpr());
            processArgumentExpression(consumerName, 2, backquotedOperator.getRightExpr());
           
            return super.visit_Expr_BinaryOp_BackquotedOperator_Var(backquotedOperator, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_BinaryOp_BackquotedOperator_DataCons(BackquotedOperator.DataCons backquotedOperator, Object arg) {
            Expr consumerExpr = backquotedOperator.getOperatorDataConsExpr();
            Name.Qualifiable consumerName = getTopLevelConsumerName(consumerExpr);

            processArgumentExpression(consumerName, 1, backquotedOperator.getLeftExpr());
            processArgumentExpression(consumerName, 2, backquotedOperator.getRightExpr());
           
            return super.visit_Expr_BinaryOp_BackquotedOperator_DataCons(backquotedOperator, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_BinaryOp_Apply(Apply apply, Object arg) {
            Name.Qualifiable consumerName = getTopLevelConsumerName(apply.getLeftExpr());
           
            // `leftArg $ rightArg` is effectively `leftArg rightArg`
            if(consumerName != null) {
                processArgumentExpression(consumerName, 1, apply.getRightExpr());
            }
           
            return super.visit_Expr_BinaryOp_Apply(apply, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_BinaryOp_Compose(Compose compose, Object arg) {
            Name.Qualifiable consumerName = getTopLevelConsumerName(compose.getLeftExpr());
           
            if(consumerName != null) {
                processArgumentExpression(consumerName, 1, compose.getRightExpr());
            }
           
            return super.visit_Expr_BinaryOp_Compose(compose, arg);
        }
       
        /**
         * Returns the name of the consumer represented by an expression if it is possible to
         * statically determine this.  So, for DataCons and Var expressions, it just returns
         * the name (if it isn't the name of a bound local variable).  These represent the base
         * cases.
         *
         * For compose expressions, it is sometimes also possible to statically determine
         * the name of a consumer that will pop out of the expression.
         *
         * @param arg  An expression which may represent a consumer
         * @return Name of the consumer represented by the expression if statically determinable,
         *          or null otherwise. 
         */
        private Name.Qualifiable getTopLevelConsumerName(Expr arg) {
           
            Expr potentialConsumer;
            //we ignore paren expressions and just consider their contents           
            if (arg instanceof Parenthesized) {
                potentialConsumer = ((Parenthesized)arg).getExpression();
            } else {
                potentialConsumer = arg;
            }
           
            // Standard `foo x` case
            if(potentialConsumer instanceof Var) {
                Name.Function consumerName = ((Var)potentialConsumer).getVarName();
               
                if(super.isDependeeReference(consumerName)) {
                    return consumerName;
                } else {
                    return null;
                }

            // Standard `Just x` case   
            } else if(potentialConsumer instanceof DataCons) {
                return ((DataCons)potentialConsumer).getDataConsName();
           
            // Special case for expressions of the form `(compose foo bar) baz`
            } else if(potentialConsumer instanceof Application) {
                Application app = (Application)potentialConsumer;
                Name.Qualifiable consumerName = getTopLevelConsumerName(app.getNthExpression(0));
               
                if(consumerName != null) {
                    FunctionalAgent consumer = super.getFunctionalAgent(consumerName);
                   
                    if(consumer.getName().equals(COMPOSE) && app.getNExpressions() == 3) {
                        return getTopLevelConsumerName(app.getNthExpression(2));
                    }
                }
           
            // Special case for expressions of the form `(foo # bar) baz`
            } else if(potentialConsumer instanceof BinaryOp.Compose) {
                BinaryOp.Compose composeOp = (BinaryOp.Compose)potentialConsumer;
                return getTopLevelConsumerName(composeOp.getRightExpr());
           
            }
           
            return null;
        }
       
        /**
         * Record that the functional agent specified by consumerName consumes the expression
         * argumentExpression as its argumentNumberth parameter.  If either consumerName or
         * argumentExpression refers to a local function definition, then no action is taken.
         * @param consumerName
         * @param argumentNumber
         * @param argument
         */
        private void processArgumentExpression(Name.Qualifiable consumerName, int argumentNumber, Expr argument) {
           
            FunctionalAgent producer = null;
            FunctionalAgent consumer = super.getFunctionalAgent(consumerName);
           
            Expr argumentExpression;
            //we ignore paren expressions and just consider their contents           
            if (argument instanceof Parenthesized) {
                argumentExpression = ((Parenthesized)argument).getExpression();
            } else {
                argumentExpression = argument;
            }
           
            if(argumentExpression instanceof Var) {
                Var var = (Var)argumentExpression;
                if (super.isDependeeReference(var.getVarName())) {
                    producer = super.getFunctionalAgent(var.getVarName());
                }
            }
           
            else if (argumentExpression instanceof DataCons) {
                DataCons cons = (DataCons)argumentExpression;
                producer = super.getFunctionalAgent(cons.getDataConsName());
            }
           
            else if (argumentExpression instanceof Application) {
                Application app = (Application)argumentExpression;
                processArgumentExpression(consumerName, argumentNumber, app.getNthExpression(0));
                return;
            }
           
            else if (argumentExpression instanceof BinaryOp.Apply) {
                BinaryOp.Apply app = (BinaryOp.Apply)argumentExpression;
                processArgumentExpression(consumerName, argumentNumber, app.getLeftExpr());
                return;
            }
           
            else if (argumentExpression instanceof BinaryOp.Compose) {
                BinaryOp.Compose composeOp = (BinaryOp.Compose)argumentExpression;
                processArgumentExpression(consumerName, argumentNumber, composeOp.getLeftExpr());
                return;
            }
           
            if(producer != null && consumer != null) {
                recordComposition(consumer.getName(), producer.getName());
            }
        }
       
        private void recordComposition(QualifiedName consumer, QualifiedName producer) {
            Pair<QualifiedName, QualifiedName> key = new Pair<QualifiedName, QualifiedName>(consumer, producer);
            Integer frequency = compositionalFrequencyMap.get(key);
            if(frequency != null) {
                compositionalFrequencyMap.put(key, Integer.valueOf(frequency.intValue() + 1));
            } else {
                compositionalFrequencyMap.put(key, Integer.valueOf(1));
            }
        }
    }
   
    /**
     * Represents a warning about a piece of code flagged by the lint process.
     * This is an immutable class.
     *
     * Creation date: (Jul 8, 2005)
     * @author Jawright
     */
    static final class LintWarning {
       
        /** Typesafe enum representing the type of the warning */
        static final class WarningType {
           
            /** Name of this warning type */
            private final String name;
           
            /** Private constructor for a warning type */
            private WarningType(String name) {
                this.name = name;
            }
           
            /** {@inheritDoc} */
            @Override
            public String toString() {
                return name;
            }
           
            static final WarningType REDUNDANT_LAMBDA = new WarningType("Possibly redundant lambda");
            static final WarningType UNPLINGED_PRIMITIVE_ARG = new WarningType("Unplinged primitive lexical argument");
            static final WarningType UNUSED_PRIVATE_FUNCTION = new WarningType("Unreferenced private function");
            static final WarningType MISMATCHED_ALIAS_PLINGS = new WarningType("Alias function's arguments do not have same strictness as aliased function/data constructor");
            static final WarningType UNREFERENCED_LET_VARIABLE = new WarningType("Unreferenced let variable");
        }
       
        /** Construct a new LintWarning
         *
         * @param warningType Type of problem that we are warning about
         * @param flaggedElement Object representing the SourceElement that caused the warning.  Normally this
         *                        this will just be the SourceElement, but occasionally we may want to custom-print
         *                        the flagged element (eg, for brevity), so a String may also be passed in.
         * @param sourceRange SourceRange of the element that caused the warning
         * @param moduleName Name of the module that the source element causing the warning occurs in
         * @param functionName Name of the top-level function that the source element that caused the warning occurs in.
         */
        private LintWarning(WarningType warningType, Object flaggedElement, SourceRange sourceRange,
                    ModuleName moduleName, String functionName) {
           
            if(warningType == null) {
                throw new IllegalArgumentException("LintWarning: warningType must not be null");
            }
           
            if(flaggedElement == null) {
                throw new IllegalArgumentException("LintWarning: flaggedElement must not be null");
            }
           
            if(functionName == null) {
                throw new IllegalArgumentException("LintWarning: functionName must not be null");
            }
           
            if(moduleName == null) {
                throw new IllegalArgumentException("LintWarning: moduleName must not be null");
            }
           
            // (no check for sourcePosition because it is allowed to be null)
           
            this.warningType = warningType;
            this.flaggedElement = flaggedElement;
            this.functionName = QualifiedName.make(moduleName, functionName);
            this.sourceRange = sourceRange;
        }
       
        /** The type of warning */
        private final WarningType warningType;
       
        /** A printable representation of the flagged expression.  This is usually just the
         * SourceElement that caused the warning, but it will occasionally be a String representing some
         * subset of the element (e.g., the lefthand side of an algebraic function definition). */
        private final Object flaggedElement;
       
        /** The position range of the flagged expression */
        private final SourceRange sourceRange;
       
        /** The name of the top-level function in which the error occurs */
        private final QualifiedName functionName;
       
        /**
         * @return a string representation of the source element that caused the warning
         */
        String getFlaggedElement() {
            return flaggedElement.toString();
        }

        /**
         * @return the top-level function in which the flagged expression occurs
         */
        QualifiedName getFunctionName() {
            return functionName;
        }
       
        /**
         * @return the start position of the flagged expression, if available, or null if not available
         */
        SourcePosition getSourcePosition() {
            if(sourceRange != null) {
                return sourceRange.getStartSourcePosition();
            } else {
                return null;
            }
        }
       
        /**
         * @return the location of the flagged expression, if available, or null if not available
         */
        SourceRange getSourceRange() {
            return sourceRange;
        }
       
        /**
         * @return the type of the warning
         */
        WarningType getWarningType() {
            return warningType;
        }
   
        /** {@inheritDoc} */
        @Override
        public String toString() {
            if(sourceRange != null) {
                return warningType + " in " + functionName + " " + sourceRange + ": " + flaggedElement;
            } else {
                return warningType + " in " + functionName + ": " + flaggedElement;
            }
        }
    }
   
    /**
     * A SourceModelTraverser that scans a single module for various style problems that
     * are statically detectable.  Currently scans for the following problems:
     *
     *   - Redundant lambdas
     *   - Unreferenced private functions
     *   - Lexical arguments of primitive types that are unplinged
     *   - Alias functions whose arguments are plinged incompatibly from the aliased function
     *   - Let variables that are never referenced.
     *
     * Warnings are accumulated as a list of LintWarnings, which can be retrieved using the
     * getWarningList method.
     *
     * The module to walk and all the modules that it imports is assumed to have already been
     * successfully compiled.
     *
     * The let-variable check relies on the assumption that the component SourceElements of the
     * SourceModel that we walk are not shared (ie, it assumes that the graph is a tree).
     * This assumption can be enforced by copying the incoming tree using the SourceModelCopier;
     * we don't perform that (expensive) step currently because the SourceModelBuilder does
     * generate trees, and those are the only models that we're currently checking. 
     * 
     *
     * Creation date: (Jul 4, 2005)
     * @author Jawright
     */
    private static class LintWalker extends MetricVisitor {
       
        /** List of warnings found so far */
        private final List<LintWarning> warningList = new ArrayList<LintWarning>();
       
        /** Filter for deciding which functions will be processed. */
        private final QualifiedNameFilter functionFilter;
       
        /** Set of functions in the module with at least one reference to them within the module. */
        private final Set<QualifiedName> referencedFunctions = new HashSet<QualifiedName>();
       
        /** Map from unqualified function or datacons name to array of parameter strictness */
        private final Map<String, boolean[]> parameterStrictnessMap = new HashMap<String, boolean[]>();
       
        /** Set of SourceElements of bound names whose names have been referenced */
        private final Set<SourceModel.SourceElement> referencedBoundNames = new HashSet<SourceModel.SourceElement>();
       
        /** When true, dump the names of skipped functions to the console */
        private final boolean traceSkippedFunctions;
       
        /** When true, return warnings about unplinged function arguments with primitive types. */
        private final boolean includeUnplingedPrimitiveArgs;
       
        /** When true, return warnings about potentially redundant lambdas */
        private final boolean includeRedundantLambdas;
       
        /** When true, return warnings about unreferenced private functions */
        private final boolean includeUnusedPrivates;
       
        /** When true, return warnings about alias functions whose argument strictness does not exactly match that of the wrapped function. */
        private final boolean includeMismatchedAliasPlings;

        /** When true, return warnings about unused let variables */
        private final boolean includeUnreferencedLetVariables;
       
        /**
         * Construct a LintWalker
         * @param moduleTypeInfo moduleTypeInfo for the module that will be walked
         * @param functionFilter Filter for deciding which functions will be processed. Cannot be null.
         * @param traceSkippedFunctions If true, all functions that are skipped will have their names
         *                               dumped to the console.
         * @param includeUnplingedPrimitiveArgs
         * @param includeRedundantLambdas
         * @param includeUnusedPrivates
         * @param includeMismatchedAliasPlings
         * @param includeUnreferencedLetVariables
         */
        private LintWalker(ModuleTypeInfo moduleTypeInfo, QualifiedNameFilter functionFilter, boolean traceSkippedFunctions,
                           boolean includeUnplingedPrimitiveArgs, boolean includeRedundantLambdas, boolean includeUnusedPrivates, boolean includeMismatchedAliasPlings, boolean includeUnreferencedLetVariables) {
            super(moduleTypeInfo);
            if (functionFilter == null) {
                throw new NullPointerException();
            }
            this.functionFilter = functionFilter;
            this.traceSkippedFunctions = traceSkippedFunctions;
            this.includeUnplingedPrimitiveArgs = includeUnplingedPrimitiveArgs;
            this.includeRedundantLambdas = includeRedundantLambdas;
            this.includeUnusedPrivates = includeUnusedPrivates;
            this.includeMismatchedAliasPlings = includeMismatchedAliasPlings;
            this.includeUnreferencedLetVariables = includeUnreferencedLetVariables;
           
            if(includeUnusedPrivates) {
                computeReferencedFunctions();
            }
        }
        /** get an expressions without parens */   
        private Expr getExprWithoutParen(Expr expr) {
            if (expr instanceof Parenthesized) {
                return ((Parenthesized)expr).getExpression();
            } else {
                return expr;
            }
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_ModuleDefn(ModuleDefn defn, Object arg) {

            // We do a pretraversal to find out about parameter strictness
            if(includeMismatchedAliasPlings) {
                ParameterStrictnessWalker strictnessWalker = new ParameterStrictnessWalker();
                strictnessWalker.visit_ModuleDefn(defn, null);
                parameterStrictnessMap.putAll(strictnessWalker.getStrictnessMap());
            }
           
            return super.visit_ModuleDefn(defn, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Lambda(Lambda lambda, Object arg) {
            boolean isRedundant = false;

            Expr lambdaExp = getExprWithoutParen(lambda.getDefiningExpr());
           
            if(lambdaExp instanceof Application) {
                Application definingExpr = (Application)lambdaExp;
                isRedundant = (definingExpr.getNExpressions() >= lambda.getNParameters() + 1);
               
                // Walk backwards along the parameters so that we catch cases like
                // (\x -> map add x)
                int i = lambda.getNParameters() - 1;
                int j = definingExpr.getNExpressions() - 1;
                for(;isRedundant && i >= 0 && j >= 1;i--, j--) {
                   
                    if(!isParameterVar(lambda.getNthParameter(i), definingExpr.getNthExpression(j))) {
                        isRedundant = false;
                    }
                }
               
            } else if(lambdaExp instanceof BinaryOp) {
                BinaryOp definingExpr = (BinaryOp)lambdaExp;
               
                if(lambda.getNParameters() == 2 &&
                   isParameterVar(lambda.getNthParameter(0), definingExpr.getLeftExpr()) &&
                   isParameterVar(lambda.getNthParameter(1), definingExpr.getRightExpr())) {
                   
                    isRedundant = true;
                   
                } else if(lambda.getNParameters() == 1 &&
                   isParameterVar(lambda.getNthParameter(0), definingExpr.getRightExpr()) &&
                   !containsParameterReference(lambda.getNthParameter(0), definingExpr.getLeftExpr())) {
                   
                    isRedundant = true;
                }
           
            } else if(lambdaExp instanceof UnaryOp) {
                UnaryOp definingExpr = (UnaryOp)lambdaExp;
                if(lambda.getNParameters() == 1 &&
                   isParameterVar(lambda.getNthParameter(0), definingExpr.getExpr())) {
                   
                    isRedundant = true;
                }
            }
           
            if(isRedundant && includeRedundantLambdas) {
                warningList.add(new LintWarning(LintWarning.WarningType.REDUNDANT_LAMBDA, lambda, lambda.getSourceRange(), super.currentModule, getCurrentFunction()));
            }
           
            return super.visit_Expr_Lambda(lambda, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_FunctionDefn_Algebraic(Algebraic algebraic, Object arg) {
           
            String functionName = algebraic.getName();
            Function functionEntity = super.moduleTypeInfo.getFunction(functionName);
           
            // Don't process functions that match the exclusion expression
            if(!functionFilter.acceptQualifiedName(QualifiedName.make(super.currentModule, algebraic.getName()))) {
                if(traceSkippedFunctions) {
                    System.out.println("Skipping test function " + super.currentModule + "." + algebraic.getName());
                }
                return null;
            }
           
            if(includeUnplingedPrimitiveArgs) {
                checkForUnplingedPrimitives(algebraic, functionName, functionEntity);
            }
           
            if(includeUnusedPrivates) {
                checkForUnusedPrivates(algebraic, functionName, functionEntity);
            }
           
            if(includeMismatchedAliasPlings) {
                checkForMismatchedAliasPlings(algebraic, functionName, functionEntity);
            }
           
            return super.visit_FunctionDefn_Algebraic(algebraic, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_FunctionDefn_Foreign(FunctionDefn.Foreign foreign, Object arg) {
           
            String functionName = foreign.getName();
            Function functionEntity = super.moduleTypeInfo.getFunction(functionName);
           
            // Don't process functions that match the exclusion expression
            if(!functionFilter.acceptQualifiedName(QualifiedName.make(super.currentModule, foreign.getName()))) {
                if(traceSkippedFunctions) {
                    System.out.println("Skipping test function " + super.currentModule + "." + foreign.getName());
                }
                return null;
            }
           
            if(includeUnusedPrivates) {
                checkForUnusedPrivates(foreign, functionName, functionEntity);
            }
           
            return super.visit_FunctionDefn_Foreign(foreign, arg);
        }
       
        /** {@inheritDoc} */
        /*  Unused let-variable checks
         * 
         *  We first call the super-method, which will visit each subexpression
         *  of the let-expression with bindings fully set up.  Our override of
         *  visitFunctionName adds the referenced SourceElement to the set
         *  referencedBoundNames each time it encounters a reference to a bound
         *  name during this traversal of the subexpressions.
         * 
         *  So, when the super-method returns, we have a set containing the
         *  SourceElement of each bound name that was referenced in the
         *  subexpressions.  We iterate over each of our local definitions;
         *  if the local definition is an element of the referencedBoundNames
         *  set, we remove it (since it scopes out after we return).
         *  If the local defninition is NOT contained in referencedBoundNames,
         *  we spit out a warning about an unreferenced let variable.
         */
        @Override
        public Void visit_Expr_Let(Let let, Object arg) {
            final Void ret = super.visit_Expr_Let(let, arg);
           
            final ModuleName currentModule = super.currentModule;
           
            /**
             * Handles the identification of unused let variables.
             * @author Joseph Wong
             */
            class UnusedLetVariablesCollector extends SourceModelTraverser<Void, Void> {

                @Override
                public Void visit_LocalDefn_Function_Definition(final Definition function, final Void arg) {
                    if(referencedBoundNames.contains(function)) {
                        referencedBoundNames.remove(function);
                   
                    } else if(includeUnreferencedLetVariables) {
                        warningList.add(new LintWarning(LintWarning.WarningType.UNREFERENCED_LET_VARIABLE, function.getName(), function.getSourceRange(), currentModule, getCurrentFunction()));
                    }
                    return null;
                }

                @Override
                public Void visit_Pattern_Var(final Pattern.Var var, final Void arg) {
                    if(referencedBoundNames.contains(var)) {
                        referencedBoundNames.remove(var);
                   
                    } else if(includeUnreferencedLetVariables) {
                        warningList.add(new LintWarning(LintWarning.WarningType.UNREFERENCED_LET_VARIABLE, var.getName(), var.getSourceRange(), currentModule, getCurrentFunction()));
                    }
                    return null;
                }

                @Override
                public Void visit_FieldPattern(final FieldPattern fieldPattern, final Void arg) {
                    // Handle punning
                    if (fieldPattern.getPattern() == null) {
                        // punning.
                       
                        // Textual field names become Vars of the same name.
                        // Ordinal field names become wildcards ("_").
                        final FieldName fieldName = fieldPattern.getFieldName().getName();
                        if (fieldName instanceof FieldName.Textual) {
                            if(referencedBoundNames.contains(fieldPattern)) {
                                referencedBoundNames.remove(fieldPattern);
                           
                            } else if(includeUnreferencedLetVariables) {
                                warningList.add(new LintWarning(LintWarning.WarningType.UNREFERENCED_LET_VARIABLE, fieldName.getCalSourceForm(), fieldPattern.getSourceRange(), currentModule, getCurrentFunction()));
                            }
                        }
                    }
                   
                    // call the superclass impl to reach the pattern and visit it (if it is non-null)
                    return super.visit_FieldPattern(fieldPattern, arg);
                }
               
                @Override
                public Void visit_LocalDefn_PatternMatch_UnpackDataCons(final LocalDefn.PatternMatch.UnpackDataCons unpackDataCons, final Void arg) {
                    // visit only the patterns
                    unpackDataCons.getArgBindings().accept(this, arg);
                    return null;
                }

                @Override
                public Void visit_LocalDefn_PatternMatch_UnpackListCons(final LocalDefn.PatternMatch.UnpackListCons unpackListCons, final Void arg) {
                    // visit only the patterns
                    unpackListCons.getHeadPattern().accept(this, arg);
                    unpackListCons.getTailPattern().accept(this, arg);
                    return null;
                }

                @Override
                public Void visit_LocalDefn_PatternMatch_UnpackRecord(final LocalDefn.PatternMatch.UnpackRecord unpackRecord, final Void arg) {
                    // visit only the field patterns (and not the base record pattern - since we do not support them in local pattern match decl)
                    final int nFieldPatterns = unpackRecord.getNFieldPatterns();
                    for (int i = 0; i < nFieldPatterns; i++) {
                        unpackRecord.getNthFieldPattern(i).accept(this, arg);
                    }
                    return null;
                }

                @Override
                public Void visit_LocalDefn_PatternMatch_UnpackTuple(final LocalDefn.PatternMatch.UnpackTuple unpackTuple, final Void arg) {
                    // visit only the patterns
                    final int nPatterns = unpackTuple.getNPatterns();
                    for (int i = 0; i < nPatterns; i++) {
                        unpackTuple.getNthPattern(i).accept(this, arg);
                    }
                    return null;
                }
            }

            // Run the collector through the defintions
            final UnusedLetVariablesCollector unusedLetVariablesCollector = new UnusedLetVariablesCollector();
            for(int i = 0; i < let.getNLocalDefinitions(); i++) {
                let.getNthLocalDefinition(i).accept(unusedLetVariablesCollector, null);
            }
           
            return ret;
        }


        /** {@inheritDoc} */
        @Override
        public Void visit_Name_Function(Name.Function function, Object arg) {
           
            if(!function.isQualified()) {
                SourceModel.SourceElement boundElement = getBoundElement(function.getUnqualifiedName());
                if(boundElement != null) {
                    referencedBoundNames.add(boundElement);
                }
            }

            return super.visit_Name_Function(function, arg);
        }


        /**
         * Check whether a function is private and unreferenced.  If it is, add an appropriate
         * warning to the list.
         *
         * @param functionDefn SourceModel of the function to check
         * @param functionName name of the function to check
         * @param functionEntity Function corresponding to the function to check
         */
        private void checkForUnusedPrivates(FunctionDefn functionDefn, String functionName, Function functionEntity) {
            if(functionEntity.getScope() == Scope.PRIVATE && !referencedFunctions.contains(functionEntity.getName())) {
                warningList.add(new LintWarning(LintWarning.WarningType.UNUSED_PRIVATE_FUNCTION, leftHandString(functionDefn), functionDefn.getNameSourceRange(), super.currentModule, functionName));
            }
        }

        /**
         * Check whether a function has any lexical arguments of primitive type that are not plinged.
         * If it does, then add an approriate LintWarning to the list.
         * 
         * @param algebraic SourceModel of the function to check
         * @param functionName name of the function to check
         * @param functionEntity Function corresponding to the function to check
         */
        private void checkForUnplingedPrimitives(Algebraic algebraic, String functionName, Function functionEntity) {
            TypeExpr[] arguments = functionEntity.getTypeExpr().getTypePieces();
           
            for(int i = 0; i < arguments.length - 1 && i < algebraic.getNParameters(); i++) {
                Parameter parameter = algebraic.getNthParameter(i);
               
                if(!parameter.isStrict() && isPrimitiveType(arguments[i])) {
                    warningList.add(new LintWarning(LintWarning.WarningType.UNPLINGED_PRIMITIVE_ARG, parameter, parameter.getSourceRangeOfNameNotIncludingPotentialPling(), super.currentModule, functionName));
                }
            }
        }
       
        /**
         * Check whether a function is an alias function whose plings do not match those of the aliased function
         * @param algebraic SourceModel of the function to check
         * @param functionName name of the function to check
         * @param functionEntity Function corresponding to the function to check
         */
        private void checkForMismatchedAliasPlings(Algebraic algebraic, String functionName, Function functionEntity) {
           
            Expr definingExpr = algebraic.getDefiningExpr();
            Expr aliasedEntityExpr = null;
           
            boolean isPotentialAlias = false;
            if(definingExpr instanceof Application) {
                Application application = (Application)definingExpr;
                aliasedEntityExpr = application.getNthExpression(0);
               
                // An alias is an application of a function or data constructor to exactly the same
                // arguments in exactly the same order.
                if((aliasedEntityExpr instanceof DataCons || aliasedEntityExpr instanceof Var)
                   && application.getNExpressions() == algebraic.getNParameters() + 1) {
                   
                    isPotentialAlias = true;
                    for(int i = 1; isPotentialAlias && i < application.getNExpressions(); i++) {
                        if(!isParameterVar(algebraic.getNthParameter(i - 1), application.getNthExpression(i))) {
                            isPotentialAlias = false;
                        }
                    }
                }
            }

            if(isPotentialAlias) {
                String aliasedEntityName = null;
                if(super.getFunctionalAgentName(aliasedEntityExpr) != null) {
                    aliasedEntityName = super.getFunctionalAgentName(aliasedEntityExpr).getUnqualifiedName();
                }
                final boolean[] aliasedEntityStrictness = parameterStrictnessMap.get(aliasedEntityName);
               
                // If we can't find the wrapped entity, we can hardly check its argument strictness
                if(aliasedEntityStrictness == null) {
                    return;
                }
               
                // Alias functions and wrapped functions must have precisely the same arity as each other.
                if( aliasedEntityStrictness.length != algebraic.getNParameters()) {
                    return;
                }
               
                // Alias functions and aliased functions must have precisely the same
                // plinging UP TO the last plinged argument of the alias function.
                // ie, it's okay for the aliased function to have extra plings, so long
                // as they are to the right of the alias function's last pling.
               
                int lastPlingOnAlias = algebraic.getNParameters() - 1;
                for(; lastPlingOnAlias >= 0; lastPlingOnAlias--) {
                    if(algebraic.getNthParameter(lastPlingOnAlias).isStrict()) {
                        break;
                    }
                }
               
                for (int i = 0; i <= lastPlingOnAlias; i++) {
                    if (aliasedEntityStrictness[i] != algebraic.getNthParameter(i).isStrict()) {
                        warningList.add(new LintWarning(LintWarning.WarningType.MISMATCHED_ALIAS_PLINGS, algebraic, algebraic.getNameSourceRange(), super.currentModule, algebraic.getName()));
                        return;
                    }
                }
            }
        }

        /**
         * @param type TypeExpr to check
         * @return true if type is a primitive type suitable for unboxing
         */
        private static boolean isPrimitiveType(TypeExpr type) {
            return type.isNonParametricType(CAL_Prelude.TypeConstructors.Boolean) ||                           
                   isForeignPrimitiveDataType(type) ||
                   isEnumDataType(type);
                  
        }

        /**
         * Returns true if type is an enumeration type that is suitable for unboxing.
         * This function is intended to mirror the results of SCJavaDefn.isEnumDataType
         * (which is not visible in the compiler package).
         *
         * @param type TypeExpr to check
         * @return true if type is an enumeration data type
         */
        private static boolean isEnumDataType(TypeExpr type) {

            TypeConsApp typeConsApp = type.rootTypeConsApp();
            if(typeConsApp == null) {
                return false;
            }
           
            return TypeExpr.isEnumType(typeConsApp.getRoot());
        }
       
        /**
         * @param type TypeExpr to check
         * @return true if type is a foreign type that represents a primitive,
         *          or false otherwise.
         */
        private static boolean isForeignPrimitiveDataType(TypeExpr type) {

            TypeConsApp typeConsApp = type.rootTypeConsApp();
            if(typeConsApp == null) {
                return false;
            }
           
            ForeignTypeInfo foreignTypeInfo = typeConsApp.getForeignTypeInfo();
            if(foreignTypeInfo == null) {
                return false;
            }
           
            try {
                return foreignTypeInfo.getForeignType().isPrimitive();
            } catch (UnableToResolveForeignEntityException e) {
                // The foreign Java type cannot be resolved, so we print out the error message, and return the default value (false)
                System.out.println(e.getCompilerMessage().toString());
                return false;
            }
        }
       
        /**
         * @param parameter A parameter
         * @param expression Expression to check
         * @return true if expression is a Var that refers to Parameter (assuming that there are
         *          no intermediate same-name bindings between parameter's scope and expression's).
         */
        private boolean isParameterVar(Parameter parameter, Expr expression) {
            if(expression instanceof Var) {
                Var var = (Var)expression;
               
                return (var.getVarName().getUnqualifiedName().equals(parameter.getName()) &&
                        (var.getVarName().getModuleName() == null || super.resolveModuleName(var.getVarName().getModuleName()).equals(super.currentModule)));
               
            }
           
            return false;
        }
       
        /**
         * @param functionDefn  An algebraic function definition
         * @return A string representing the left-hand side of the definition
         */
        private String leftHandString(FunctionDefn functionDefn) {
            if (functionDefn instanceof Algebraic) {
                Algebraic algebraic = (Algebraic)functionDefn;

                StringBuilder buffer = new StringBuilder(algebraic.getScope().toString());
                buffer.append(' ');
                buffer.append(algebraic.getName());

                for(int i = 0; i < algebraic.getNParameters(); i++) {
                    buffer.append(' ');
                    buffer.append(algebraic.getNthParameter(i));
                }

                buffer.append(" = ...");

                return buffer.toString();
               
            } else if (functionDefn instanceof Foreign) {
                Foreign foreign = (Foreign)functionDefn;
               
                final StringBuilder buffer = new StringBuilder();
               
                buffer.append("foreign unsafe import jvm ");               

                buffer.append(StringEncoder.encodeString(foreign.getExternalName()));

                if (foreign.isScopeExplicitlySpecified()) {
                    buffer.append(' ').append(foreign.getScope().toString());
                }
                buffer.append(' ').append(foreign.getName()).append(" :: ");
               
                buffer.append("...");
               
                return buffer.toString();

            } else {
                return functionDefn.toString();
            }
        }
       
        /**
         * Helper class for discovering whether an expression contains a reference
         * to a parameter.  The parameter is assumed to be in scope at the level
         * of expression (although expression itself may contain lets or whatnot
         * that mask the target parameter).
         *
         * Creation date: (Jul 5, 2005)
         * @author Jawright
         */
        private class ParameterReferenceFinder extends MetricVisitor {
           
            /** True if we encounter a variable referring to targetParameter */
            private boolean foundReference = false;
           
            /** The parameter that we are trying to find references to */
            private final Parameter targetParameter;
           
            /**
             * Construct a ParameterReferenceFinder
             * @param targetParameter Parameter to search for
             */
            ParameterReferenceFinder(Parameter targetParameter, ModuleTypeInfo moduleTypeInfo) {
                super(moduleTypeInfo);
                this.targetParameter = targetParameter;
            }
           
            /**
             * @return true if a variable referring to the targetParameter was found
             */
            boolean getFoundReference() {
                return foundReference;
            }
           
            @Override
            public Void visit_Expr_Var(Var var, Object arg) {
               
                if(!isBound(targetParameter.getName()) &&
                   isParameterVar(targetParameter, var)) {
                    foundReference = true;
                }
               
                return super.visit_Expr_Var(var, arg);
            }
        }
       
        /**
         * Helper class for accumulating the strictness of each parameter of each top-level function in a module.
         *
         * Creation date: (Jul 14, 2005)
         * @author Jawright
         */
        private class ParameterStrictnessWalker extends SourceModelTraverser<Void, Void> {
            /** Map from unqualified functional agent to its strictness array (array of isStrict flags for each parameter) */
            private final Map<String, boolean[]> strictnessMap = new HashMap<String, boolean[]>();
           
            /** @return Map from unqualified functional agent to its strictness array (array of isStrict flags for each parameter) */
            Map<String, boolean[]> getStrictnessMap() {
                return Collections.unmodifiableMap(strictnessMap);
            }
           
            /** {@inheritDoc} */
            @Override
            public Void visit_FunctionDefn_Algebraic(Algebraic algebraic, Void arg) {

                boolean[] parameterStrictness = new boolean[algebraic.getNParameters()];
                for(int i = 0; i < algebraic.getNParameters(); i++) {
                    parameterStrictness[i] = algebraic.getNthParameter(i).isStrict();
                }
               
                strictnessMap.put(algebraic.getName(), parameterStrictness);
               
                return super.visit_FunctionDefn_Algebraic(algebraic, arg);
            }
           
            /** {@inheritDoc} */
            @Override
            public Void visit_FunctionDefn_Foreign(Foreign foreign, Void arg) {
               
                int arity = getArity(foreign.getDeclaredType().getTypeExprDefn());
                boolean[] parameterStrictness = new boolean[arity];
                for(int i = 0; i < arity; i++) {
                    parameterStrictness[i] = true// Foreign functions are fully strict
                }
                strictnessMap.put(foreign.getName(), parameterStrictness);
               
                return super.visit_FunctionDefn_Foreign(foreign, arg);
            }
           
            @Override
            public Void visit_TypeConstructorDefn_AlgebraicType_DataConsDefn(DataConsDefn defn, Void arg) {

                boolean[] parameterStrictness = new boolean[defn.getNTypeArgs()];
                for(int i = 0; i < defn.getNTypeArgs(); i++) {
                    parameterStrictness[i] = defn.getNthTypeArg(i).isStrict();
                }
                strictnessMap.put(defn.getDataConsName(), parameterStrictness);
               
                return super.visit_TypeConstructorDefn_AlgebraicType_DataConsDefn(defn, arg);
            }

            /**
             * Returns the arity of the function represented by typeExprDefn. e.g., a function of
             * type (Foo -> Bar -> Baz) has arity of 2.
             * @param typeExprDefn A TypeExprDefn
             * @return int the arity of typeExprDefn
             */
            private int getArity(TypeExprDefn typeExprDefn) {
                if(typeExprDefn instanceof TypeExprDefn.Function) {
                    TypeExprDefn.Function functionType = (TypeExprDefn.Function)typeExprDefn;
                    return 1 + getArity(functionType.getCodomain());
                } else {
                    return 0;
                }
            }
        }
       
        /**
         * Checks whether expression contains a reference to the specified parameter.  Parameter
         * is assumed to be in scope at the level of expression.  Subexpressions that
         * make bindings that hide parameter will be handled correctly.
         * @param parameter The parameter to check for references to
         * @param expression The expression to check
         * @return true if expression contains references to parameter
         */
        private boolean containsParameterReference(final Parameter parameter, Expr expression) {
            ParameterReferenceFinder parameterReferenceFinder = new ParameterReferenceFinder(parameter, super.moduleTypeInfo);
           
            expression.accept(parameterReferenceFinder, ArrayStack.make());
            return parameterReferenceFinder.getFoundReference();
        }
   
        /**
         * Fill the referencedFunctions Set with every function in the current module
         * that has been called from within the current module.
         *
         */
        private void computeReferencedFunctions() {

            // First find all of the private functions that get referenced as default methods of some type class
            for(int i = 0; i < super.moduleTypeInfo.getNTypeClasses(); i++) {
                TypeClass typeClass = super.moduleTypeInfo.getNthTypeClass(i);
               
                for(int j = 0; j < typeClass.getNClassMethods(); j++) {
                    ClassMethod classMethod = typeClass.getNthClassMethod(j);
                    final QualifiedName defaultClassMethodName = classMethod.getDefaultClassMethodName();
                    if (defaultClassMethodName != null) {
                        referencedFunctions.add(defaultClassMethodName);
                    }
                }
            }
           
            // Then find all of the private functions that get referenced as methods of some instance
            for(int i = 0; i < super.moduleTypeInfo.getNClassInstances(); i++) {
                ClassInstance instance = super.moduleTypeInfo.getNthClassInstance(i);
               
                for(int j = 0; j < instance.getNInstanceMethods(); j++) {
                    QualifiedName methodName = instance.getInstanceMethod(j);
                    if (methodName != null) {
                        //will be null in the case when an instance does not define the class method i.e. the intent is to use
                        //the default class method
                        referencedFunctions.add(methodName);
                    }
                }
            }
           
            // Now walk over all of the functions in the module checking who references whom
            for(int i = 0; i < super.moduleTypeInfo.getNFunctions(); i++) {
                Function function = super.moduleTypeInfo.getNthFunction(i);
               
                for (final QualifiedName dependeeName : function.getDependeeToFrequencyMap().keySet()) {
                    if(dependeeName.getModuleName().equals(super.currentModule)) {
                        referencedFunctions.add(dependeeName);
                    }
                }           
            }
        }
       
        /**
         * @return The list of warnings found by this traversal
         */
        List<LintWarning> getWarningList() {
            return Collections.unmodifiableList(warningList);
        }
    }
       
    /** Typesafe enum representing the possible types of search */
    static final class SearchType {
       
        /** Name of this search type */
        private final String name;
       
        /** Private constructor for a warning type */
        private SearchType(String name) {
            this.name = name;
        }
       
        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return name;
        }

        static final SearchType ALL = new SearchType("Find all occurrences of an entity");
        static final SearchType REFERENCES = new SearchType("Find references in expressions to a gem");
        static final SearchType DEFINITION = new SearchType("Find all top-level definitions of a type or gem");
        static final SearchType INSTANCES = new SearchType("Find instances of a type class");
        static final SearchType CLASSES = new SearchType("Find all classes that a type is an instance of");
        static final SearchType POSITION = new SearchType("Find the entity defined at the given location");
        static final SearchType REFERENCES_CONSTRUCTIONS = new SearchType("Find all constructions of the given data type.");
        static final SearchType SOURCE_TEXT = new SearchType("Find the source text for the entity defined at the given location");
    }
       
    /**
     * A SourceModelTraverser used to perform searches on a particular QualifiedName.  Behaviour
     * is defined for each type in the SearchType enumeration.
     *
     * Creation date: (Jul 28, 2005)
     * @author Jawright
     */
    private static final class SearchWalker extends MetricVisitor {
       
        /** List of QualifiedNames of the entities to search for. Will be null if the search type is POSITION. */
        private final List<? extends org.openquark.cal.compiler.Name> targetNames;
       
        /** Type of search to perform */
        private final SearchType searchType;
       
        /** List of locations that have matched the search criteria */
        private final List<SearchResult.Precise> searchResults = new ArrayList<SearchResult.Precise>();
       
        /**
         * The position of the symbol to search for. Only set if the searchType is POSITION.
         */
        private final SourcePosition searchPosition;
       
        /**
         * Construct a new SearchWalker
         * @param moduleTypeInfo ModuleTypeInfo of module to search.  It is assumed to have been compiled already.
         * @param targetNames List of QualifiedName of the entities to search for
         * @param searchType Type of search to perform (i.e., for references, for applications, etc.)
         * @param searchPosition The position of the symbol to search for. This will only be set if the searchType is position.
         */
        private SearchWalker(ModuleTypeInfo moduleTypeInfo, List<? extends org.openquark.cal.compiler.Name> targetNames, SearchType searchType, SourcePosition searchPosition) {
            super(moduleTypeInfo);
           
            if(targetNames == null && searchType != SearchType.POSITION) {
                throw new IllegalArgumentException("SearchWalker must be given a non-null list of targetNames");
            }
            if(searchType == SearchType.POSITION && searchPosition == null){
                throw new IllegalArgumentException("SearchWalker must be given a non-null sourcePosition if the search type is by position");
            }
           
            this.targetNames = targetNames;
            this.searchType = searchType;
            this.searchPosition = searchPosition;
        }

        @Override
        public Void visit_Name_Module(Name.Module moduleName, Object arg) {
            final SourceRange sourceRange = moduleName.getSourceRange();
            if (searchType == SearchType.POSITION){
                if (sourceRange.containsPositionInclusive(searchPosition)){
                    addSearchResult(sourceRange, super.resolveModuleName(moduleName), SourceIdentifier.Category.MODULE_NAME);
                }
            }
            else{
                final ModuleName candidateName = SourceModel.Name.Module.toModuleName(moduleName);
                for (final org.openquark.cal.compiler.Name targetName : targetNames) {
                    if(targetName.equals(candidateName)) {
                        addSearchResult(sourceRange, candidateName, SourceIdentifier.Category.MODULE_NAME);
                    }
                }
            }
            return super.visit_Name_Module(moduleName, arg);
        }

        /** (@inheritDoc} */
        @Override
        public Void visit_Import(Import importStmt, Object arg) {
            final SourceRange sourceRange = importStmt.getImportedModuleName().getSourceRange();
            if (searchType == SearchType.POSITION){
                if (sourceRange.containsPositionInclusive(searchPosition)){
                    addSearchResult(sourceRange, SourceModel.Name.Module.toModuleName(importStmt.getImportedModuleName()), SourceIdentifier.Category.MODULE_NAME);
                }
            }
            else{
                final ModuleName candidateName = SourceModel.Name.Module.toModuleName(importStmt.getImportedModuleName());
                for (final org.openquark.cal.compiler.Name targetName : targetNames) {
                    if(targetName.equals(candidateName)) {
                        addSearchResult(sourceRange, candidateName, SourceIdentifier.Category.MODULE_NAME);
                    }
                }
            }

            return super.visit_Import(importStmt, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Import_UsingItem_Function(UsingItem.Function usingItemFunction, Object arg) {
            if(arg == null || !(arg instanceof ModuleName)) {
                throw new IllegalArgumentException("SearchWalker.visitImportUsingItemFunction expects to be passed a module name as its arg");
            }
           
            ModuleName importedModuleName = (ModuleName)arg;
            String[] usingNames = usingItemFunction.getUsingNames();
            SourceRange[] usingNameSourceRanges = usingItemFunction.getUsingNameSourceRanges();

            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                for(int i = 0, nNames = usingNames.length; i < nNames; i++) {
                    checkReference(QualifiedName.make(importedModuleName, usingNames[i]), usingNameSourceRanges[i], SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                }
            }
           
            return super.visit_Import_UsingItem_Function(usingItemFunction, arg);
        }       
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Import_UsingItem_DataConstructor(UsingItem.DataConstructor usingItemDataConstructor, Object arg) {
            if(arg == null || !(arg instanceof ModuleName)) {
                throw new IllegalArgumentException("SearchWalker.visitImportUsingItemDataConstructor expects to be passed a module name as its arg");
            }
           
            ModuleName importedModuleName = (ModuleName)arg;
            String[] usingNames = usingItemDataConstructor.getUsingNames();
            SourceRange[] usingNameSourceRanges = usingItemDataConstructor.getUsingNameSourceRanges();

            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                for(int i = 0, nNames = usingNames.length; i < nNames; i++) {
                    checkReference(QualifiedName.make(importedModuleName, usingNames[i]), usingNameSourceRanges[i], SourceIdentifier.Category.DATA_CONSTRUCTOR);
                }
            }
           
            return super.visit_Import_UsingItem_DataConstructor(usingItemDataConstructor, arg);
        }       
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Import_UsingItem_TypeConstructor(UsingItem.TypeConstructor usingItemTypeConstructor, Object arg) {
            if(arg == null || !(arg instanceof ModuleName)) {
                throw new IllegalArgumentException("SearchWalker.visitImportUsingItemTypeConstructor expects to be passed a module name as its arg");
            }
           
            ModuleName importedModuleName = (ModuleName)arg;
            String[] usingNames = usingItemTypeConstructor.getUsingNames();
            SourceRange[] usingNameSourceRanges = usingItemTypeConstructor.getUsingNameSourceRanges();

            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                for(int i = 0, nNames = usingNames.length; i < nNames; i++) {
                    checkReference(QualifiedName.make(importedModuleName, usingNames[i]), usingNameSourceRanges[i], SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                }
            }
           
            return super.visit_Import_UsingItem_TypeConstructor(usingItemTypeConstructor, arg);
        }       
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Import_UsingItem_TypeClass(UsingItem.TypeClass usingItemTypeClass, Object arg) {
            if(arg == null || !(arg instanceof ModuleName)) {
                throw new IllegalArgumentException("SearchWalker.visitImportUsingItemTypeClass expects to be passed a module name as its arg");
            }
           
            ModuleName importedModuleName = (ModuleName)arg;
            String[] usingNames = usingItemTypeClass.getUsingNames();
            SourceRange[] usingNameSourceRanges = usingItemTypeClass.getUsingNameSourceRanges();

            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                for(int i = 0, nNames = usingNames.length; i < nNames; i++) {
                    checkReference(QualifiedName.make(importedModuleName, usingNames[i]), usingNameSourceRanges[i], SourceIdentifier.Category.TYPE_CLASS);
                }
            }
           
            return super.visit_Import_UsingItem_TypeClass(usingItemTypeClass, arg);
        }       
       
        /** {@inheritDoc} */
        @Override
        public Void visit_FunctionDefn_Algebraic(Algebraic algebraic, Object arg) {

            if (searchType == SearchType.POSITION){
                if (algebraic.getNameSourceRange().containsPositionInclusive(searchPosition)){
                    addSearchResult(algebraic.getNameSourceRange(), toQualifiedName(algebraic.getName()), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if (
                                algebraic.getName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())){
                            if(
                                    searchType == SearchType.DEFINITION ||
                                    searchType == SearchType.ALL ||
                                    searchType == SearchType.POSITION
                            ){
                                addSearchResult(algebraic.getNameSourceRange(), targetName, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                            }
                            else if (searchType == SearchType.SOURCE_TEXT){
                                addSearchResult(algebraic.getSourceRange(), targetName, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                            }
                        }
                    }
                }
            }

            return super.visit_FunctionDefn_Algebraic(algebraic, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_FunctionDefn_Primitive(Primitive primitive, Object arg) {

            if (searchType == SearchType.POSITION){
                if (primitive.getNameSourceRange().containsPositionInclusive(searchPosition)){
                    addSearchResult(primitive.getNameSourceRange(), toQualifiedName(primitive.getName()), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if (primitive.getName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())) {
                            if(searchType == SearchType.DEFINITION ||
                               searchType == SearchType.ALL ||
                               searchType == SearchType.POSITION){
                                addSearchResult(primitive.getNameSourceRange(), targetName, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                            }
                            else if (searchType == SearchType.SOURCE_TEXT){
                                addSearchResult(primitive.getSourceRangeExcludingCaldoc(), targetName, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                            }
                        }
                    }
                }
            }
           
            return super.visit_FunctionDefn_Primitive(primitive, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_InstanceDefn_InstanceTypeCons_Function(InstanceDefn.InstanceTypeCons.Function function, Object arg) {
            if((searchType == SearchType.CLASSES || searchType == SearchType.ALL || searchType == SearchType.POSITION)) {
                checkReference(CAL_Prelude.TypeConstructors.Function, function.getOperatorSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
            }
            return super.visit_InstanceDefn_InstanceTypeCons_Function(function, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_InstanceDefn_InstanceTypeCons_List(InstanceDefn.InstanceTypeCons.List list, Object arg) {
            if((searchType == SearchType.CLASSES || searchType == SearchType.ALL || searchType == SearchType.POSITION)) {
                checkReference(CAL_Prelude.TypeConstructors.List, list.getSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
            }
            return super.visit_InstanceDefn_InstanceTypeCons_List(list, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_InstanceDefn_InstanceTypeCons_TypeCons(TypeCons cons, Object arg) {
            if((searchType == SearchType.CLASSES || searchType == SearchType.ALL || searchType == SearchType.POSITION)) {
                checkReference(cons.getTypeConsName(), cons.getSourceRangeOfName(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
            }
            return super.visit_InstanceDefn_InstanceTypeCons_TypeCons(cons, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_InstanceDefn_InstanceTypeCons_Unit(InstanceDefn.InstanceTypeCons.Unit unit, Object arg) {
            if((searchType == SearchType.CLASSES || searchType == SearchType.ALL || searchType == SearchType.POSITION)) {
                checkReference(CAL_Prelude.TypeConstructors.Unit, unit.getSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
            }
            return super.visit_InstanceDefn_InstanceTypeCons_Unit(unit, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_InstanceDefn(InstanceDefn defn, Object arg) {

            // Override default visitation order to ensure that constraints
            // are visited before the typeclass or typecons.  This ensures
            // that results will be found in textual order.
            if (defn.getCALDocComment() != null) {
                defn.getCALDocComment().accept(this, arg);
            }

            final int nConstraints = defn.getNConstraints();
            for (int i = 0; i < nConstraints; i++) {
                defn.getNthConstraint(i).accept(this, arg);
            }
   
            if(searchType == SearchType.INSTANCES || searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                Name.TypeClass typeClassName = defn.getTypeClassName();
                checkReference(typeClassName, typeClassName.getSourceRange(), SourceIdentifier.Category.TYPE_CLASS);
            }
           
            defn.getTypeClassName().accept(this, arg);
            defn.getInstanceTypeCons().accept(this, arg);
   
            final int nInstanceMethods = defn.getNInstanceMethods();
            for (int i = 0; i < nInstanceMethods; i++) {
                defn.getNthInstanceMethod(i).accept(this, arg);
            }
   
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_InstanceDefn_InstanceMethod(InstanceMethod method, Object arg) {

            if(searchType == SearchType.REFERENCES || searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(method.getResolvingFunctionName(), method.getSourceRangeOfResolvingFunctionName(), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
            }
           
            return super.visit_InstanceDefn_InstanceMethod(method, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_TypeClassDefn(TypeClassDefn defn, Object arg) {
           
            if (searchType == SearchType.POSITION){
                if (defn.getSourceRangeOfName().containsPositionInclusive(searchPosition)){
                    addSearchResult(defn.getSourceRangeOfName(), toQualifiedName(defn.getTypeClassName()), SourceIdentifier.Category.TYPE_CLASS);
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if (defn.getTypeClassName().equals(targetName.getUnqualifiedName()) &&
                            super.currentModule.equals(targetName.getModuleName())){
                            if (searchType == SearchType.DEFINITION || searchType == SearchType.ALL || searchType == SearchType.POSITION){
                                addSearchResult(defn.getSourceRangeOfName(), targetName, SourceIdentifier.Category.TYPE_CLASS);
                            }
                            else if (searchType == SearchType.SOURCE_TEXT){
                                addSearchResult(defn.getSourceRangeOfDefn(), targetName, SourceIdentifier.Category.TYPE_CLASS);
                            }
                        }
                    }
                }
            }
           
            return super.visit_TypeClassDefn(defn, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_TypeClassDefn_ClassMethodDefn(ClassMethodDefn defn, Object arg) {

            if (searchType == SearchType.POSITION){
                if (defn.getSourceRangeOfName() != null){
                    if (defn.getSourceRangeOfName().containsPositionInclusive(searchPosition)){
                        addSearchResult(defn.getSourceRangeOfName(), toQualifiedName(defn.getMethodName()), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                    }
                }
                if (defn.getDefaultClassMethodName() != null){
                    if (defn.getDefaultClassMethodName().getSourceRange().containsPositionInclusive(searchPosition)){
                        addSearchResult(defn.getDefaultClassMethodName().getSourceRange(), toQualifiedName(defn.getDefaultClassMethodName()), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                    }
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if (defn.getMethodName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())) {
                            if(searchType == SearchType.DEFINITION || searchType == SearchType.ALL || searchType == SearchType.POSITION){
                                addSearchResult(defn.getSourceRangeOfName(), targetName, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                            }
                            else if (searchType == SearchType.SOURCE_TEXT){
                                addSearchResult(defn.getSourceRangeOfClassDefn(), targetName, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                            }
                        }
                    }
                }
               
                if (searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                    Name.Function defaultClassMethodName = defn.getDefaultClassMethodName();
                    if (defaultClassMethodName != null) {
                        checkReference(defaultClassMethodName, defaultClassMethodName.getSourceRange(), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                    }
                }
            }

            return super.visit_TypeClassDefn_ClassMethodDefn(defn, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_FunctionTypeDeclaraction(FunctionTypeDeclaration declaration, Object arg) {       
            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(QualifiedName.make(super.currentModule, declaration.getFunctionName()), declaration.getSourceRangeOfName(), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
            }
           
            return super.visit_FunctionTypeDeclaraction(declaration, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_Constraint_TypeClass(SourceModel.Constraint.TypeClass typeClass, Object arg) {
            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(typeClass.getTypeClassName(), typeClass.getTypeClassName().getSourceRange(), SourceIdentifier.Category.TYPE_CLASS);
            }
            return super.visit_Constraint_TypeClass(typeClass, arg);
        }

       
        /** {@inheritDoc} */
        @Override
        public Void visit_TypeExprDefn_Function(TypeExprDefn.Function function, Object arg) {
            // Force an in-order traversal rather than relying on the super method to
            // make the recursive calls.  Doing an in-order traversal will ensure that the
            // the results are returned in source-order without needing to be explicitly sorted.
            function.getDomain().accept(this, arg);

            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(CAL_Prelude.TypeConstructors.Function, function.getOperatorSourceRange(), null);
            }

            function.getCodomain().accept(this, arg);
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeExprDefn_List(TypeExprDefn.List list, Object arg) {
            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(CAL_Prelude.TypeConstructors.List, list.getSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
            }
            return super.visit_TypeExprDefn_List(list, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeExprDefn_TypeCons(TypeExprDefn.TypeCons cons, Object arg) {
            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(cons.getTypeConsName(), cons.getSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
            }
            return super.visit_TypeExprDefn_TypeCons(cons, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeExprDefn_Unit(TypeExprDefn.Unit unit, Object arg) {
            if(searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(CAL_Prelude.TypeConstructors.Unit, unit.getSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
            }
            return super.visit_TypeExprDefn_Unit(unit, arg);
        }

        private QualifiedName toQualifiedName(String unqualifiedName){
            return QualifiedName.make(super.currentModule, unqualifiedName);
        }
       
        private QualifiedName toQualifiedName(Name.Function name){
            if (name.getModuleName() == null){
                return toQualifiedName(name.getUnqualifiedName());
            }
            else{
                return QualifiedName.make(super.resolveModuleName(name.getModuleName()), name.getUnqualifiedName());
            }
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_TypeConstructorDefn_AlgebraicType(AlgebraicType type, Object arg) {           
            if (searchType == SearchType.POSITION){
                if(type.getSourceRangeOfName().containsPositionInclusive(searchPosition)){
                    addSearchResult(type.getSourceRangeOfName(), toQualifiedName(type.getTypeConsName()), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                }
               
                Name.TypeClass[] derivedTypeClasses = type.getDerivingClauseTypeClassNames();
                for (final SourceModel.Name.TypeClass typeClassName : derivedTypeClasses) {
                    checkReference(typeClassName, typeClassName.getSourceRange(), SourceIdentifier.Category.TYPE_CLASS);
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if((searchType == SearchType.DEFINITION || searchType == SearchType.ALL) &&
                                type.getTypeConsName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())) {
                            addSearchResult(type.getSourceRangeOfName(), targetName, SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                        }

                        if((searchType == SearchType.SOURCE_TEXT) &&
                            type.getTypeConsName().equals(targetName.getUnqualifiedName()) &&
                            super.currentModule.equals(targetName.getModuleName())) {
                            addSearchResult(type.getSourceRangeOfDefn(), targetName, SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                        }

                        if(searchType == SearchType.INSTANCES || searchType == SearchType.ALL) {
                            Name.TypeClass[] derivedTypeClasses = type.getDerivingClauseTypeClassNames();
                            for (final Name.TypeClass typeClassName : derivedTypeClasses) {
                                checkReference(typeClassName, typeClassName.getSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                            }
                        }

                        if((searchType == SearchType.CLASSES || searchType == SearchType.ALL) &&
                                type.getTypeConsName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())) {
                            Name.TypeClass[] derivedTypeClasses = type.getDerivingClauseTypeClassNames();
                            for (final Name.TypeClass derivedTypeClass : derivedTypeClasses) {
                                addSearchResult(derivedTypeClass.getSourceRange(), targetName, SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                            }
                        }
                    }
                }
            }
           
            return super.visit_TypeConstructorDefn_AlgebraicType(type, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeConstructorDefn_AlgebraicType_DataConsDefn(DataConsDefn defn, Object arg) {

            if (searchType == SearchType.POSITION){
                if (defn.getSourceRangeOfName().containsPositionInclusive(searchPosition)){
                    addSearchResult(defn.getSourceRangeOfName(), toQualifiedName(defn.getDataConsName()), SourceIdentifier.Category.DATA_CONSTRUCTOR);
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if (defn.getDataConsName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())){
                            if(searchType == SearchType.DEFINITION || searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                                addSearchResult(defn.getSourceRangeOfName(), targetName, SourceIdentifier.Category.DATA_CONSTRUCTOR);
                            }
                            else if (searchType == SearchType.SOURCE_TEXT){
                                addSearchResult(defn.getSourceRangeOfDefn(), targetName, SourceIdentifier.Category.DATA_CONSTRUCTOR);
                            }
                        }
                    }
                }
            }
           
            return super.visit_TypeConstructorDefn_AlgebraicType_DataConsDefn(defn, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_FunctionDefn_Foreign(Foreign foreign, Object arg) {
            if (searchType == SearchType.POSITION){
                // TODO in the future have the goto definition find the definition in the java code.
                // That would be cool.
                if (foreign.getNameSourceRange().containsPositionInclusive(searchPosition)){
                    addSearchResult(foreign.getNameSourceRange(), toQualifiedName(foreign.getName()), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                }
               
                if (foreign.getExternalNameSourceRange().containsPositionInclusive(searchPosition)){
                    addSearchResult(foreign.getSourceRange(), toQualifiedName(foreign.getName()), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD, true);               
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if (foreign.getName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())) {
                            if((searchType == SearchType.DEFINITION || searchType == SearchType.ALL || searchType == SearchType.POSITION)) {
                                addSearchResult(foreign.getNameSourceRange(), targetName, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                            }
                            else if (searchType == SearchType.SOURCE_TEXT){
                                addSearchResult(foreign.getSourceRangeExcludingCaldoc(), targetName, SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
                            }
                        }
                    }
                }
            }
            return super.visit_FunctionDefn_Foreign(foreign, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_TypeConstructorDefn_ForeignType(ForeignType type, Object arg) {
            if (searchType == SearchType.POSITION){         
                if (type.getSourceRangeOfName().containsPositionInclusive(searchPosition)){
                    addSearchResult(type.getSourceRangeOfName(), toQualifiedName(type.getTypeConsName()), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                }
               
                if (type.getExternalNameSourceRange().containsPositionInclusive(searchPosition)){
                    addSearchResult(type.getSourceRangeOfName(), toQualifiedName(type.getTypeConsName()), SourceIdentifier.Category.TYPE_CONSTRUCTOR, true);               
                }
               
                // TODO in the future have the goto definition find the definition in the java code.
                // That would be cool.
                for(int i = 0; i < type.getNDerivingClauseTypeClassNames(); i++) {
                    Name.TypeClass typeClassName = type.getDerivingClauseTypeClassName(i);
                    checkReference(typeClassName, typeClassName.getSourceRange(), SourceIdentifier.Category.TYPE_CLASS);
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if((searchType == SearchType.DEFINITION || searchType == SearchType.ALL || searchType == SearchType.POSITION) &&
                                type.getTypeConsName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())) {
                            addSearchResult(type.getSourceRangeOfName(), targetName, SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                        }

                        if((searchType == SearchType.SOURCE_TEXT) &&
                                type.getTypeConsName().equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())) {
                            addSearchResult(type.getSourceRangeOfDefn(), targetName, SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                        }
                       
                        if(searchType == SearchType.INSTANCES || searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                            for(int i = 0; i < type.getNDerivingClauseTypeClassNames(); i++) {
                                Name.TypeClass typeClassName = type.getDerivingClauseTypeClassName(i);
                                checkReference(typeClassName, typeClassName.getSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                            }
                        }

                        if((searchType == SearchType.CLASSES || searchType == SearchType.ALL || searchType == SearchType.POSITION) &&
                                targetName.getUnqualifiedName().equals(type.getTypeConsName()) &&
                                targetName.getModuleName().equals(super.currentModule)) {
                            for(int i = 0; i < type.getNDerivingClauseTypeClassNames(); i++) {
                                final Name.TypeClass typeClass = type.getDerivingClauseTypeClassName(i);
                                checkReference(typeClass, typeClass.getSourceRange(), SourceIdentifier.Category.TYPE_CONSTRUCTOR);
                            }
                        }
                    }
                }
            }
           
            return super.visit_TypeConstructorDefn_ForeignType(type, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_DataCons(DataCons cons, Object arg) {
           
            // No work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.REFERENCES_CONSTRUCTIONS && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_Expr_DataCons(cons, arg);
            }
           
            Name.Qualifiable candidateName = cons.getDataConsName();
           
            // If this is a reference to one of the targetNames, add it to the list
            checkReference(candidateName, cons.getSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
           
            return super.visit_Expr_DataCons(cons, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_LocalDefn_PatternMatch_UnpackDataCons(LocalDefn.PatternMatch.UnpackDataCons unpackDataCons, Object arg) {
           
            // Process the pattern-bound variables first
            processLocalPatternMatchDeclarationPattern(unpackDataCons.getArgBindings());
           
            // No further work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_LocalDefn_PatternMatch_UnpackDataCons(unpackDataCons, arg);
            }

            Name.Qualifiable candidateName = unpackDataCons.getDataConsName();   

            // If this is a reference to one of the targetNames, add it to the list
            checkReference(candidateName, candidateName.getSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);

            return super.visit_LocalDefn_PatternMatch_UnpackDataCons(unpackDataCons, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_LocalDefn_PatternMatch_UnpackListCons(LocalDefn.PatternMatch.UnpackListCons unpackListCons, Object arg) {
           
            // Process the pattern-bound variables first
            processLocalPatternMatchDeclarationPattern(unpackListCons.getHeadPattern());
            processLocalPatternMatchDeclarationPattern(unpackListCons.getTailPattern());
           
            // No further work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_LocalDefn_PatternMatch_UnpackListCons(unpackListCons, arg);
            }
           
            checkReference(CAL_Prelude.DataConstructors.Cons, unpackListCons.getOperatorSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
           
            return super.visit_LocalDefn_PatternMatch_UnpackListCons(unpackListCons, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Case_Alt_UnpackDataCons(UnpackDataCons cons, Object arg) {
           
            // No work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_Expr_Case_Alt_UnpackDataCons(cons, arg);
            }
           
            for(int i = 0; i < cons.getNDataConsNames(); i++) {
                Name.Qualifiable candidateName = cons.getNthDataConsName(i);   
               
                // If this is a reference to one of the targetNames, add it to the list
                checkReference(candidateName, candidateName.getSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
            }
           
            return super.visit_Expr_Case_Alt_UnpackDataCons(cons, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Case_Alt_UnpackListNil(UnpackListNil nil, Object arg) {

            // No work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_Expr_Case_Alt_UnpackListNil(nil, arg);
            }
           
            checkReference(CAL_Prelude.DataConstructors.Nil, nil.getSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
           
            return super.visit_Expr_Case_Alt_UnpackListNil(nil, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Case_Alt_UnpackListCons(UnpackListCons cons, Object arg) {
            // No work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_Expr_Case_Alt_UnpackListCons(cons, arg);
            }
           
            checkReference(CAL_Prelude.DataConstructors.Cons, cons.getOperatorSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
           
            return super.visit_Expr_Case_Alt_UnpackListCons(cons, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Case_Alt_UnpackUnit(UnpackUnit unit, Object arg) {
            // No work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_Expr_Case_Alt_UnpackUnit(unit, arg);
            }
           
            checkReference(CAL_Prelude.DataConstructors.Unit, unit.getSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
           
            return super.visit_Expr_Case_Alt_UnpackUnit(unit, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_SelectDataConsField(SelectDataConsField field, Object arg) {

            // No work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_Expr_SelectDataConsField(field, arg);
            }
           
            Name.Qualifiable candidateName = field.getDataConsName();
           
            // If this is a reference to targetName, add it to the list
            checkReference(candidateName, field.getSourceRangeOfName(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
           
            return super.visit_Expr_SelectDataConsField(field, arg);
        }
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Var(Var var, Object arg) {
           
            // No work to do here unless we're looking for references
            if(searchType != SearchType.REFERENCES && searchType != SearchType.REFERENCES_CONSTRUCTIONS && searchType != SearchType.ALL && searchType != SearchType.POSITION) {
                return super.visit_Expr_Var(var, arg);
            }
           
            Name.Qualifiable candidateName = var.getVarName();

            // figure out the category of this symbol
            SourceIdentifier.Category category = SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD;
            if (candidateName.getModuleName() == null || SourceModel.Name.Module.toModuleName(candidateName.getModuleName()).equals(getModuleName())){
                if (getBoundLocalFunctionIdentifier(candidateName.getUnqualifiedName()) != null){
                    category = SourceIdentifier.Category.LOCAL_VARIABLE;
                }
            }
            // If this is a reference to targetName, add it to the list
            checkReference(candidateName, var.getSourceRange(), category);
           
            return super.visit_Expr_Var(var, arg);
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_BinaryOp_BackquotedOperator_DataCons(org.openquark.cal.compiler.SourceModel.Expr.BinaryOp.BackquotedOperator.DataCons backquotedOperator, Object arg) {
            // Force an in-order traversal rather than relying on the super method to
            // make the recursive calls.  Doing an in-order traversal will ensure that the
            // the results are returned in source-order without needing to be explicitly sorted.
            backquotedOperator.getLeftExpr().accept(this, arg);
            backquotedOperator.getOperatorDataConsExpr().accept(this, arg);
            backquotedOperator.getRightExpr().accept(this, arg);
            return null;
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_BinaryOp_BackquotedOperator_Var(org.openquark.cal.compiler.SourceModel.Expr.BinaryOp.BackquotedOperator.Var backquotedOperator, Object arg) {
            // Force an in-order traversal rather than relying on the super method to
            // make the recursive calls.  Doing an in-order traversal will ensure that the
            // the results are returned in source-order without needing to be explicitly sorted.
            backquotedOperator.getLeftExpr().accept(this, arg);
            backquotedOperator.getOperatorVarExpr().accept(this, arg);
            backquotedOperator.getRightExpr().accept(this, arg);
            return null;
        }
       
        /** {@inheritDoc} */
        @Override
        protected Void visit_Expr_BinaryOp_Helper(BinaryOp binop, Object arg) {
           
            // Force an in-order traversal rather than relying on the super method to
            // make the recursive calls.  Doing an in-order traversal will ensure that the
            // the results are returned in source-order without needing to be explicitly sorted.
            binop.getLeftExpr().accept(this, arg);
           
            if(searchType == SearchType.REFERENCES || searchType == SearchType.REFERENCES_CONSTRUCTIONS || searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(OperatorInfo.getTextualName(binop.getOpText()), binop.getOperatorSourceRange(), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
            }
           
            binop.getRightExpr().accept(this, arg);
            return null;
        }
       
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_List(org.openquark.cal.compiler.SourceModel.Expr.List list, Object arg) {
           
            if(searchType == SearchType.REFERENCES || searchType == SearchType.REFERENCES_CONSTRUCTIONS || searchType == SearchType.ALL || searchType == SearchType.POSITION) {

                // All empty list literals reference the Prelude.Nil datacons
                if(list.getNElements() == 0) {
                    checkReference(CAL_Prelude.DataConstructors.Nil, list.getSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
                }
            }
            return super.visit_Expr_List(list, arg);
        }

        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_UnaryOp_Negate(Negate negate, Object arg) {
            if(searchType == SearchType.REFERENCES || searchType == SearchType.REFERENCES_CONSTRUCTIONS || searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(CAL_Prelude.Functions.negate, negate.getSourceRange(), SourceIdentifier.Category.TOP_LEVEL_FUNCTION_OR_CLASS_METHOD);
            }
            return super.visit_Expr_UnaryOp_Negate(negate, arg);
        }
        
        /** {@inheritDoc} */
        @Override
        public Void visit_Expr_Unit(Unit unit, Object arg) {
            if(searchType == SearchType.REFERENCES || searchType == SearchType.REFERENCES_CONSTRUCTIONS || searchType == SearchType.ALL || searchType == SearchType.POSITION) {
                checkReference(CAL_Prelude.DataConstructors.Unit, unit.getSourceRange(), SourceIdentifier.Category.DATA_CONSTRUCTOR);
            }
            return super.visit_Expr_Unit(unit, arg);
        }
       
        /**
         * Check whether candidateName refers to any of the targetNames.
         * If it does, then add the specified source position to sourcePositions.
         * @param candidateName Name to check
         * @param sourceRange SourceRange to record if candidateName is a match
         */
        private void checkReference(QualifiedName candidateName, SourceRange sourceRange, SourceIdentifier.Category category) {
           
            // Skip bound names
            if(candidateName.getModuleName().equals(super.currentModule) &&
               isBound(candidateName.getUnqualifiedName())) {
                return;
            }
           
            if (searchType == SearchType.POSITION){
                if (sourceRange.containsPositionInclusive(searchPosition)){
                    addSearchResult(sourceRange, candidateName, category);
                    return;
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        if(targetName.equals(candidateName)) {
                            addSearchResult(sourceRange, candidateName, category);
                            return;
                        }
                    }
                }
            }
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Void visit_Parameter(Parameter parameter, Object arg) {
            if (searchType == SearchType.POSITION){
                if (parameter.getSourceRangeOfNameNotIncludingPotentialPling() != null){
                    if (parameter.getSourceRangeOfNameNotIncludingPotentialPling().containsPositionInclusive(searchPosition)){
                        final LocalFunctionIdentifier lfi = getBoundLocalFunctionIdentifier(parameter.getName());
                        if (lfi != null){
                            final QualifiedName resolvedCandidateName = QualifiedName.makeFromCompoundName(lfi.toString());
                            addSearchResult(parameter.getSourceRangeOfNameNotIncludingPotentialPling(), resolvedCandidateName, SourceIdentifier.Category.LOCAL_VARIABLE_DEFINITION);
                        }
                    }
                }
            }

            return super.visit_Parameter(parameter, arg);
        }
       
        /**
         * {@inheritDoc}
         */
        @Override
        public Void visit_LocalDefn_Function_TypeDeclaration(
                LocalDefn.Function.TypeDeclaration declaration, Object arg) {
            if (searchType == SearchType.POSITION){
                if (declaration.getNameSourceRange() != null){
                    if (declaration.getNameSourceRange().containsPositionInclusive(searchPosition)){
                        final LocalFunctionIdentifier lfi = getBoundLocalFunctionIdentifier(declaration.getName());
                        if (lfi != null){
                            final QualifiedName resolvedCandidateName = QualifiedName.makeFromCompoundName(lfi.toString());
                            addSearchResult(declaration.getNameSourceRange(), resolvedCandidateName, SourceIdentifier.Category.LOCAL_VARIABLE_DEFINITION);
                        }
                    }
                }
            }
            return super.visit_LocalDefn_Function_TypeDeclaration(declaration, arg);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Void visit_LocalDefn_PatternMatch_UnpackRecord(final LocalDefn.PatternMatch.UnpackRecord unpackRecord, final Object arg) {
            // Process the pattern-bound variables first
            final int nFieldPatterns = unpackRecord.getNFieldPatterns();
            for (int i = 0; i < nFieldPatterns; i++) {
                processLocalPatternMatchDeclarationPattern(unpackRecord.getNthFieldPattern(i));
            }
           
            return super.visit_LocalDefn_PatternMatch_UnpackRecord(unpackRecord, arg);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Void visit_LocalDefn_PatternMatch_UnpackTuple(final LocalDefn.PatternMatch.UnpackTuple unpackTuple, final Object arg) {
            // Process the pattern-bound variables first
            final int nPatterns = unpackTuple.getNPatterns();
            for (int i = 0; i < nPatterns; i++) {
                processLocalPatternMatchDeclarationPattern(unpackTuple.getNthPattern(i));
            }
           
            return super.visit_LocalDefn_PatternMatch_UnpackTuple(unpackTuple, arg);
        }
       
        /**
         * Processes a Pattern, a FieldPattern or an ArgBindings element contained in a local
         * pattern match declaration for the pattern-bound variables defined therein.
         * @param element the element to process.
         */
        private void processLocalPatternMatchDeclarationPattern(final SourceModel.SourceElement element) {
            /**
             * A helper class for processing locally bound names for the purpose of identifying search hits and location of
             * local definitions.
             * @author Joseph Wong
             */
            class LocalNamesProcessor extends SourceModelTraverser<Void, Void> {
                /**
                 * {@inheritDoc}
                 */
                @Override
                public Void visit_Pattern_Var(final Pattern.Var var, final Void arg) {
                    processLocallyBoundName(var.getName(), var.getSourceRange());
                    return null;
                }

                /**
                 * {@inheritDoc}
                 */
                @Override
                public Void visit_FieldPattern(final FieldPattern fieldPattern, final Void arg) {
                    // Handle punning
                    if (fieldPattern.getPattern() == null) {
                        // punning.
                       
                        // Textual field names become Vars of the same name.
                        // Ordinal field names become wildcards ("_").
                        final FieldName fieldName = fieldPattern.getFieldName().getName();
                        if (fieldName instanceof FieldName.Textual) {
                            processLocallyBoundName(fieldName.getCalSourceForm(), fieldPattern.getFieldName().getSourceRange());
                        }
                    }
                   
                    // call the superclass impl to reach the pattern and visit it (if it is non-null)
                    return super.visit_FieldPattern(fieldPattern, arg);
                }
            }
           
            // run the LocalNamesProcessor on the element
            element.accept(new LocalNamesProcessor(), null);
        }
       
        /**
         * {@inheritDoc}
         */
        @Override
        public Void visit_LocalDefn_Function_Definition(final LocalDefn.Function.Definition function, final Object arg) {
            // Process the local function binding first
            processLocallyBoundName(function.getName(), function.getNameSourceRange());
           
            return super.visit_LocalDefn_Function_Definition(function, arg);
        }
       
        /**
         * Processes a locally defined name (either a local function definition, or a local pattern-bound variable).
         * @param name the locally defined name.
         * @param nameSourceRange the source range of the name. Can be null.
         */
        private void processLocallyBoundName(final String name, final SourceRange nameSourceRange) {
            //todo-jowong rewrite the position search as a separate walker to handle searching through all identifiers
            if (searchType == SearchType.POSITION){
                if (nameSourceRange != null){
                    if (nameSourceRange.containsPositionInclusive(searchPosition)){
                        final LocalFunctionIdentifier lfi = getBoundLocalFunctionIdentifier(name);
                        if (lfi != null){
                            final QualifiedName resolvedCandidateName = QualifiedName.makeFromCompoundName(lfi.toString());
                            addSearchResult(nameSourceRange, resolvedCandidateName, SourceIdentifier.Category.LOCAL_VARIABLE_DEFINITION);
                        }
                    }
                }
            }
            else{
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        final LocalFunctionIdentifier lfi = getBoundLocalFunctionIdentifier(name);
                        if (lfi != null){
                            final QualifiedName resolvedCandidateName = QualifiedName.makeFromCompoundName(lfi.toString());
                            if((searchType == SearchType.DEFINITION || searchType == SearchType.ALL || searchType == SearchType.POSITION) &&
                                    resolvedCandidateName.getUnqualifiedName().equals(targetName.getUnqualifiedName()) &&
                                    resolvedCandidateName.getModuleName().equals(targetName.getModuleName())) {
                                addSearchResult(nameSourceRange, targetName, SourceIdentifier.Category.LOCAL_VARIABLE_DEFINITION);
                            }
                            else if((searchType == SearchType.SOURCE_TEXT) &&
                                    resolvedCandidateName.getUnqualifiedName().equals(targetName.getUnqualifiedName()) &&
                                    resolvedCandidateName.getModuleName().equals(targetName.getModuleName())) {
                                SourceModel.SourceElement se = getBoundElement(name);
                                if (se != null){                               
                                    addSearchResult(se.getSourceRange(), targetName, SourceIdentifier.Category.LOCAL_VARIABLE_DEFINITION);
                                }
                            }
                        }
                        else if((searchType == SearchType.DEFINITION || searchType == SearchType.ALL || searchType == SearchType.POSITION) &&
                                name.equals(targetName.getUnqualifiedName()) &&
                                super.currentModule.equals(targetName.getModuleName())) {
                            addSearchResult(nameSourceRange, targetName, SourceIdentifier.Category.LOCAL_VARIABLE_DEFINITION);
                        }
                    }
                }
            }
        }
       
        /**
         * Check whether candidateName refers to any of the targetNames. 
         * If it does, then add the specified source position to sourcePositions.
         * @param candidateName Name to check
         *
         * @param sourceRange SourceRange to add to searchResults if candidateName is a match
         */
        private void checkReference(Name.Qualifiable candidateName, SourceRange sourceRange, SourceIdentifier.Category category) {
           
            // Null names can sometimes get passed in, but they are obviously not a match
            if(candidateName == null) {
                return;
            }
           
            if (searchType == SearchType.POSITION){
                if (sourceRange.containsPositionInclusive(searchPosition)){
                   
                    if (candidateName.getModuleName() == null) {
                       
                        // We have an unqualified name.  If it is not currently bound, then
                        // look up the FunctionalAgent and compare the QualifiedNames.
                        if(!isBound(candidateName.getUnqualifiedName())) {
                            ScopedEntity candidateEntity = getScopedEntityFromUnboundCandidateName(candidateName);
                           
                            if(candidateEntity != null) {
                                addSearchResult(sourceRange, candidateEntity.getName(), category);
                            }
                        }
                        else{
                            LocalFunctionIdentifier lfi = getBoundLocalFunctionIdentifier(candidateName.getUnqualifiedName());
                            if (lfi != null){
                                final QualifiedName resolvedCandidateName = QualifiedName.makeFromCompoundName(lfi.toString());
                                addSearchResult(sourceRange, resolvedCandidateName, category);
                            }
                        }
                       
                    } else {
                        final QualifiedName resolvedCandidateName = QualifiedName.make(super.resolveModuleName(candidateName.getModuleName()), candidateName.getUnqualifiedName());
                        addSearchResult(sourceRange, resolvedCandidateName, category);
                    }
                }

            } else {
                for (final org.openquark.cal.compiler.Name next : targetNames) {
                    if (next instanceof QualifiedName){
                        QualifiedName targetName = (QualifiedName) next;

                        // If the gem being checked has a different unqualified name, we can do a trivial reject
                        if(!candidateName.getUnqualifiedName().equals(targetName.getUnqualifiedName())) {
                            continue;
                        }

                        // If the gem being applied is fully-qualified, then we can do a simple check without looking up the FunctionalAgent
                        if(candidateName.getModuleName() != null) {
                            if(super.resolveModuleName(candidateName.getModuleName()).equals(targetName.getModuleName())) {
                                addSearchResult(sourceRange, targetName, category);
                            }

                            continue;
                        }

                        // Otherwise, we have an unqualified name.  If it is not currently bound, then
                        // look up the FunctionalAgent and compare the QualifiedNames.
                        if (!isBound(candidateName.getUnqualifiedName())) {
                            ScopedEntity candidateEntity = getScopedEntityFromUnboundCandidateName(candidateName);

                            if(candidateEntity == null) {
                                continue;
                            }

                            if(candidateEntity.getName().equals(targetName)) {
                                addSearchResult(sourceRange, targetName, category);
                            }
                        }
                    }
                }
            }
        }

        /**
         * Fetches the named entity (function/data cons/method/type/class).
         * @param candidateName the name of the entity.
         * @return the entity named, or null if no such entity exists.
         */
        private ScopedEntity getScopedEntityFromUnboundCandidateName(Name.Qualifiable candidateName) {
            String unqualifiedCandidate = candidateName.getUnqualifiedName();
            ScopedEntity candidateEntity = super.getFunctionalAgent(candidateName);
           
            // Wasn't a functional agent.  Maybe a type.
            if(candidateEntity == null && candidateName instanceof Name.TypeCons) {
                candidateEntity = super.moduleTypeInfo.getTypeConstructor(unqualifiedCandidate);
               
                if(candidateEntity == null) {
                    ModuleName dependeeModuleName = super.moduleTypeInfo.getModuleOfUsingTypeConstructor(unqualifiedCandidate);
                    ModuleTypeInfo dependeeModuleTypeInfo = super.moduleTypeInfo.getDependeeModuleTypeInfo(dependeeModuleName);
                    if(dependeeModuleTypeInfo != null) {
                        candidateEntity = dependeeModuleTypeInfo.getTypeConstructor(unqualifiedCandidate);
                    }
                }
            }
           
            // Maybe a type class.
            if(candidateEntity == null && candidateName instanceof Name.TypeClass) {
                candidateEntity = super.moduleTypeInfo.getTypeClass(unqualifiedCandidate);
               
                if(candidateEntity == null) {
                    ModuleName dependeeModuleName = super.moduleTypeInfo.getModuleOfUsingTypeClass(unqualifiedCandidate);
                    ModuleTypeInfo dependeeModuleTypeInfo = super.moduleTypeInfo.getDependeeModuleTypeInfo(dependeeModuleName);
                    if(dependeeModuleTypeInfo != null) {
                        candidateEntity = dependeeModuleTypeInfo.getTypeClass(unqualifiedCandidate);
                    }
                }
            }
           
            return candidateEntity;
        }

        /**
         * Adds the specified sourceRange to searchResults if it contains a source name,
         * or (if it does not) a SourceRange augmented by the name of the current module.
         * @param sourceRange SourceRange to add (possibly in augmented form)
         * @param name QualifiedName that was found
         */
        private void addSearchResult(SourceRange sourceRange, org.openquark.cal.compiler.Name name, SourceIdentifier.Category category, boolean refersToJavaSource) {
            if(sourceRange == null || sourceRange.getSourceName() != null) {
                // TODO this is really wrong - SearchResult.Precise REQUIRES a non-null sourceRange
                searchResults.add(new SearchResult.Precise(sourceRange, name, category, refersToJavaSource));
            } else {
                SourcePosition startPosition = new SourcePosition(sourceRange.getStartLine(), sourceRange.getStartColumn(), super.currentModule);
                SourcePosition endPosition = new SourcePosition(sourceRange.getEndLine(), sourceRange.getEndColumn(), super.currentModule);
                searchResults.add(new SearchResult.Precise(new SourceRange(startPosition, endPosition), name, category, refersToJavaSource));
            }
        }
       
        /**
         * Adds the specified sourceRange to searchResults if it contains a source name,
         * or (if it does not) a SourceRange augmented by the name of the current module.
         * @param sourceRange SourceRange to add (possibly in augmented form)
         * @param name QualifiedName that was found
         */
        private void addSearchResult(SourceRange sourceRange, org.openquark.cal.compiler.Name name, SourceIdentifier.Category category) {
            addSearchResult(sourceRange, name, category, false);
        }
       
        /**
         * @return List of all the search hits
         */
        List<SearchResult.Precise> getSearchResults() {
            return Collections.unmodifiableList(searchResults);
        }
    }
   
    /**
     * Performs the compile-time collection of source metric data and updates the appropriate
     * compiler structures with the raw data from the results.  Collects data for:
     *
     * - Reference frequency metric.  Collects information on which top-level gems are referenced
     *   by each top-level function in the module.  In this example function, foo has one reference
     *   to baz and two to quux, but none to bar:
     *      foo x =
     *          let
     *              bar y = quux y;
     *          in
     *              quux (bar (baz x));
     *             
     * - Module references: Collects a list of all QualifiedNames from other modules that this
     *   module refers to.
     *
     * Note that this method assumes that the specified module and all it imported modules have all been compiled
     * already and that ModuleTypeInfo objects are therefore available for all of them.
     *
     * This method call MODIFIES the raw data sets of the specified module's FunctionEntities as a side effect,
     * so it should only be called as a part of the compilation process.
     * 
     * @param moduleDefn SourceModel of the module to process
     * @param moduleTypeInfo The typeinfo object for the module specified by moduleSourceDefn
     */
    static void updateRawMetricData(SourceModel.ModuleDefn moduleDefn, ModuleTypeInfo moduleTypeInfo) {

        // Compute the set of references from this module
        ReferenceFrequencyFinder finder = new ReferenceFrequencyFinder(moduleTypeInfo, new AcceptAllQualifiedNamesFilter(), false);
        finder.visit_ModuleDefn(moduleDefn, null);
        Map<Pair<QualifiedName, QualifiedName>, Integer> dependeeMap = finder.getDependeeMap();
       
        // Add each reference to the appropriate Function
        for (final Map.Entry<Pair<QualifiedName, QualifiedName>, Integer> entry : dependeeMap.entrySet()) {
            Pair<QualifiedName, QualifiedName> referencePair = entry.getKey();
            Integer frequency = entry.getValue();
           
            QualifiedName dependeeName = referencePair.fst();
            QualifiedName dependentName = referencePair.snd();
           
            // Every dependent was found in this scan, so it must be a member of this module
            Function dependentEntity = moduleTypeInfo.getFunction(dependentName.getUnqualifiedName());
            dependentEntity.addDependee(dependeeName, frequency.intValue());
        }
       
        moduleTypeInfo.setImportedNameOccurrences(finder.getImportedNameOccurrences());
    }
   
    /**
     * Finds all the pairs of (referred_to_gem, referred_by_function) in the provided module and returns
     * them in a Set of Pairs of QualifiedNames.  This is the raw data used to calculate the reference
     * frequency heuristic.
     *
     * This function has no external side effects.
     * 
     * @param moduleDefn SourceModel of the module to process
     * @param moduleTypeInfo ModuleTypeInfo object corresponding to the module specified by moduleSourceDefn.
     * @param functionFilter Filter for deciding which functions will be processed. Cannot be null.
     * @param traceSkippedFunctions When true, each skipped function will have its name dumped to the console.
     *                               When false, skipped functions will be skipped silently     
     * @return List of (dependee, dependent) pairs (one for each reference).
     */
    static Map<Pair<QualifiedName, QualifiedName>, Integer> computeReferenceFrequencies(SourceModel.ModuleDefn moduleDefn, ModuleTypeInfo moduleTypeInfo, QualifiedNameFilter functionFilter, boolean traceSkippedFunctions) {
        ReferenceFrequencyFinder finder = new ReferenceFrequencyFinder(moduleTypeInfo, functionFilter, traceSkippedFunctions);
        finder.visit_ModuleDefn(moduleDefn, null);
        return finder.getDependeeMap();
    }
   
    /**
     * Scans the provided module for instances where the output of one gem is used
     * directly as the input of another.  Each (consumer, producer) pair is associated with a count
     * of the number of times that the output of producer is used as input to consumer.  This metric
     * is called the "compositional frequency" of (consumer, producer).
     *
     * For example, in the following module:
     *
     *      module Foo;
     *      import Prelude;
     *
     *      bar = 50;
     *      baz arg = 20 + arg;
     *
     *      quux = Prelude.add bar (baz 54);
     *
     *      quuux val = Prelude.add (baz 10) (baz val);
     *
     * The compositional frequency of (Prelude.add, Foo.bar) is 1 (since the output of bar is
     * passed directly to add once in quux). 
     * The compositional frequency of (Prelude.add, Foo.baz) is 3 (since the output of baz is
     * passed directly to add once in quux and twice in quuux).
     * 
     * @param moduleDefn A SourceModel of a module to scan
     * @param moduleTypeInfo The ModuleTypeInfo object corresponding to the moduleDefn's module
     * @param functionFilter Filter for deciding which functions will be included in the frequency counts. Cannot be null.
     * @param traceSkippedFunctions When true, each skipped function will have its name dumped to the console.
     *                               When false, skipped functions will be skipped silently
     * @return A map from (consumer, producer) to
     * the number of times the output of producer is used as the input to consumer.
     */
    static Map<Pair<QualifiedName, QualifiedName>, Integer> computeCompositionalFrequencies(SourceModel.ModuleDefn moduleDefn, ModuleTypeInfo moduleTypeInfo, QualifiedNameFilter functionFilter, boolean traceSkippedFunctions) {
        CompositionalFrequencyFinder finder = new CompositionalFrequencyFinder(moduleTypeInfo, functionFilter, traceSkippedFunctions);
        finder.visit_ModuleDefn(moduleDefn, null);
        return finder.getCompositionalFrequencyMap();
    }
   
    /**
     * Scans the provided module for questionable expressions (currently only finds redundant lambdas).
     * Dumps a warning to the console for each flagged expression.
     *
     * @param moduleDefn A SourceModel of a module to scan
     * @param moduleTypeInfo The ModuleTypeInfo object corresponding to the moduleDefn's module
     * @param functionFilter Filter for deciding which functions will be processed. Cannot be null.
     * @param traceSkippedFunctions When true, each skipped function will have its name dumped to the console.
     *                               When false, skipped functions will be skipped silently
     * @param includeUnplingedPrimitives When true, check for unplinged arguments of primitive type
     * @param includeRedundantLambdas When true, check for possibly-redundant lambdas
     * @param includeUnusedPrivates When true, check for unused private top-level functions.
     * @param includeMismatchedAliasPlings When true, check for alias functions whose plinging does not
     *                                        match that of the underlying data constructor / foreign function exactly.
     * @param includeUnreferencedLetVariables When true, check for let variables that are not referenced                                       
     * @return List of warnings found for this module
     */
    static List<LintWarning> computeLintWarnings(SourceModel.ModuleDefn moduleDefn, ModuleTypeInfo moduleTypeInfo, QualifiedNameFilter functionFilter, boolean traceSkippedFunctions,
                                    boolean includeUnplingedPrimitives, boolean includeRedundantLambdas, boolean includeUnusedPrivates, boolean includeMismatchedAliasPlings, boolean includeUnreferencedLetVariables) {
       
        LintWalker walker = new LintWalker(moduleTypeInfo, functionFilter, traceSkippedFunctions,
                                           includeUnplingedPrimitives, includeRedundantLambdas, includeUnusedPrivates, includeMismatchedAliasPlings, includeUnreferencedLetVariables);
        walker.visit_ModuleDefn(moduleDefn, null);
       
        return walker.getWarningList();
    }

    /**
     * Searches a module for references, definitions, or instances, and returns a list of SearchResults
     * representing the hits.
     *
     * @param moduleDefn SourceModel.ModuleDefn of the module to search
     * @param moduleTypeInfo ModuleTypeInfo corresponding to the moduleDefn's module
     * @param searchType SearchType type of search to perform
     * @param targetGemNames List of QualifiedNames of the entities to search for
     * @return List of SourcePositions of the hits
     */
    static List<SearchResult.Precise> performSearch(SourceModel.ModuleDefn moduleDefn, ModuleTypeInfo moduleTypeInfo, SearchType searchType, List<? extends org.openquark.cal.compiler.Name> targetGemNames) {
       
        SearchWalker searchWalker = new SearchWalker(moduleTypeInfo, targetGemNames, searchType, null);
       
        searchWalker.visit_ModuleDefn(moduleDefn, null);
        return searchWalker.getSearchResults();
    }
   
    /**
     * Searches a module for a SourcePosition, and returns a list of SearchResults
     * representing the hits.
     *
     * @param moduleDefn SourceModel.ModuleDefn of the module to search
     * @param moduleTypeInfo ModuleTypeInfo corresponding to the moduleDefn's module
     * @return List of SourcePositions of the hits
     */
    static List<SearchResult.Precise> findSymbolAt(SourceModel.ModuleDefn moduleDefn, ModuleTypeInfo moduleTypeInfo, SourcePosition position) {       
        SearchWalker searchWalker = new SearchWalker(moduleTypeInfo, null, SearchType.POSITION, position);       
        searchWalker.visit_ModuleDefn(moduleDefn, null);
        return searchWalker.getSearchResults();
    }

    /**
     * @param defn the top level element to get the name source range of.
     * @return the source range of the name of the given top level element. If not
     * available the source range of the entire element is returned. Otherwise null
     * is returned.
     */
    private static SourceRange getScopedEntityNameSourceRange(TopLevelSourceElement defn){
        SourceRange result = null;
        if (defn instanceof SourceModel.FunctionDefn){
            SourceModel.FunctionDefn functionDefn = (FunctionDefn) defn;
            result = functionDefn.getNameSourceRange();
        }
        else if (defn instanceof SourceModel.TypeConstructorDefn){
            SourceModel.TypeConstructorDefn typeConstructorDefn = (SourceModel.TypeConstructorDefn) defn;
            result = typeConstructorDefn.getSourceRangeOfName();
        }
        else if (defn instanceof SourceModel.InstanceDefn){
            SourceModel.InstanceDefn instanceDefn = (SourceModel.InstanceDefn) defn;
            result = instanceDefn.getSourceRangeOfName();
        }
        else if (defn instanceof SourceModel.TypeClassDefn){
            SourceModel.TypeClassDefn typeClassDefn = (SourceModel.TypeClassDefn) defn;
            result = typeClassDefn.getSourceRangeOfName();
        }
       
        if (result == null){
            return defn.getSourceRangeOfDefn();
        }
        else{
            return result;
        }
    }
   
    /**
     * Get the next top level element definition that is defined after the given position.
     * @param moduleDefn the source model to search
     * @param position the position to look after
     * @return the next top level element defined after the given position. This may be null.
     */
    static SourceRange findNextTopLevelElement(SourceModel.ModuleDefn moduleDefn, SourcePosition position){
        final int nTopLevelDefns = moduleDefn.getNTopLevelDefns();
        SourceRange next = null;
        for (int i = 0; i < nTopLevelDefns; i++) {
            TopLevelSourceElement nthTopLevelDefn = moduleDefn.getNthTopLevelDefn(i);
            // skip the type declarations
            if (nthTopLevelDefn instanceof SourceModel.FunctionTypeDeclaration){
                continue;
            }
            SourceRange testSourcePosition = getScopedEntityNameSourceRange(nthTopLevelDefn);
            if (testSourcePosition.getStartSourcePosition().isAfter(position)){
                if (next == null){
                    next = testSourcePosition;
                }
                else if (testSourcePosition.getStartSourcePosition().isBefore(next.getStartSourcePosition())){
                    next = testSourcePosition;
                }
            }
        }
       
        if (next != null){
            return next;
        }
        else{
            return null;
        }
    }

    /**
     * Get the previous top level element definition that is defined before the given position.
     * @param moduleDefn the source model to search
     * @param position the position to look after
     * @return the previous top level element defined before the given position. This may be null.
     */
    static SourceRange findPreviousTopLevelElement(SourceModel.ModuleDefn moduleDefn, SourcePosition position){
        final int nTopLevelDefns = moduleDefn.getNTopLevelDefns();
        SourceRange next = null;
        for (int i = 0; i < nTopLevelDefns; i++) {
            TopLevelSourceElement nthTopLevelDefn = moduleDefn.getNthTopLevelDefn(i);
            // skip the type declarations
            if (nthTopLevelDefn instanceof SourceModel.FunctionTypeDeclaration){
                continue;
            }
            SourceRange testSourcePosition = getScopedEntityNameSourceRange(nthTopLevelDefn);
            if (testSourcePosition.getStartSourcePosition().isBefore(position)){
                if (next == null){
                    next = testSourcePosition;
                }
                else if (testSourcePosition.getStartSourcePosition().isAfter(next.getStartSourcePosition())){
                    next = testSourcePosition;
                }
            }
        }
       
        if (next != null){
            return next;
        }
        else{
            return null;
        }
    }

    /**
     * Finds the inner most definition containing the given position. This will return
     * null if there is no inner definition containing the given position.
     * @return the innermost definition containing the given position or null if there is no such definition.
     */
    private static Pair<SourceElement, SourceRange> findInnermostDef(LocalDefn.Function.Definition def, SourcePosition position){
        SourceRange next = null;
        SourceElement element = null;
        if (def.getDefiningExpr() instanceof SourceModel.Expr.Let){
            SourceModel.Expr.Let let = (SourceModel.Expr.Let) def.getDefiningExpr();
            for(LocalDefn localDefn : let.getLocalDefinitions()){
                SourceRange testSourcePosition = localDefn.getSourceRange();
                if (testSourcePosition.containsPosition(position)){
                    if (next == null || next.contains(testSourcePosition)){
                        next = testSourcePosition;
                        element = localDefn;

                        if (localDefn instanceof LocalDefn.Function){
                            SourceModel.LocalDefn.Function localFunction = (SourceModel.LocalDefn.Function) localDefn;

                            // if there is a type declaration then use that to put the
                            // cal doc in front of
                            for(LocalDefn localTypeDefn : let.getLocalDefinitions()){                                   
                                if (localTypeDefn instanceof SourceModel.LocalDefn.Function.TypeDeclaration){
                                    SourceModel.LocalDefn.Function.TypeDeclaration type = (SourceModel.LocalDefn.Function.TypeDeclaration) localTypeDefn;
                                    if (type.getName().equals(localFunction.getName())){
                                        element = type;     
                                        break;
                                    }
                                }
                            }
                        }
                       
                        if (localDefn instanceof LocalDefn.Function.Definition){
                            LocalDefn.Function.Definition def2 = (Definition) localDefn;
                            Pair<SourceElement, SourceRange> innerMatch = findInnermostDef(def2, position);
                            if (innerMatch != null){
                                return innerMatch;
                            }
                        }
                       
                        return new Pair<SourceElement, SourceRange>(element, next);
                    }
                }
            }
        }
        return null;
    }

    /**
     * Get the source element definition that is at the given position.
     * @param moduleDefn the source model to search
     * @param position the position to look after
     * @return A pair where the first element is the source element that
     * contains the given position and the second element is the
     * source range of the element. This may be null.
     */
    static Pair<SourceElement, SourceRange> findContainingSourceElement(SourceModel.ModuleDefn moduleDefn, SourcePosition position){
        final int nTopLevelDefns = moduleDefn.getNTopLevelDefns();
        SourceRange next = null;
        SourceElement element = null;
        for (int i = 0; i < nTopLevelDefns; i++) {
            TopLevelSourceElement nthTopLevelDefn = moduleDefn.getNthTopLevelDefn(i);
            // Check for matching data constructor
            if (nthTopLevelDefn instanceof SourceModel.TypeConstructorDefn.AlgebraicType){
                SourceModel.TypeConstructorDefn.AlgebraicType algebraicType = (AlgebraicType) nthTopLevelDefn;
                for(DataConsDefn dataConsDefn : algebraicType.getDataConstructors()){
                    SourceRange testSourcePosition = dataConsDefn.getSourceRangeOfDefn();
                    if (testSourcePosition.containsPosition(position)){
                        if (next == null || next.contains(testSourcePosition)){
                            next = testSourcePosition;
                            element = dataConsDefn;
                            return new Pair<SourceElement, SourceRange>(element, element.getSourceRange());
                        }
                    }
                }
            }
            // check for matching class method
            else if (nthTopLevelDefn instanceof SourceModel.TypeClassDefn){
                SourceModel.TypeClassDefn typeClassDefn = (SourceModel.TypeClassDefn) nthTopLevelDefn;
                for(ClassMethodDefn classMethodDefn : typeClassDefn.getClassMethodDefns()){
                    SourceRange testSourcePosition = classMethodDefn.getSourceRangeOfClassDefn();
                    if (testSourcePosition.containsPosition(position)){
                        if (next == null || next.contains(testSourcePosition)){
                            next = testSourcePosition;
                            element = classMethodDefn;
                            return new Pair<SourceElement, SourceRange>(element, element.getSourceRange());
                        }
                    }
                }
            }
            // check for let expressions
            else if (nthTopLevelDefn instanceof SourceModel.FunctionDefn.Algebraic){
                SourceModel.FunctionDefn.Algebraic algebraic = (SourceModel.FunctionDefn.Algebraic) nthTopLevelDefn;
                if (algebraic.getDefiningExpr() instanceof SourceModel.Expr.Let){
                    SourceModel.Expr.Let let = (SourceModel.Expr.Let) algebraic.getDefiningExpr();
                    for(LocalDefn localDefn : let.getLocalDefinitions()){
                        SourceRange testSourcePosition = localDefn.getSourceRange();
                        if (testSourcePosition.containsPosition(position)){
                            if (next == null || next.contains(testSourcePosition)){
                                next = testSourcePosition;
                                element = localDefn;

                                if (localDefn instanceof LocalDefn.Function){
                                    SourceModel.LocalDefn.Function localFunction = (SourceModel.LocalDefn.Function) localDefn;
                                   
                                    // if there is a type declaration then use that to put the
                                    // cal doc in front of
                                    for(LocalDefn localTypeDefn : let.getLocalDefinitions()){                                   
                                        if (localTypeDefn instanceof SourceModel.LocalDefn.Function.TypeDeclaration){
                                            SourceModel.LocalDefn.Function.TypeDeclaration type = (SourceModel.LocalDefn.Function.TypeDeclaration) localTypeDefn;
                                            if (type.getName().equals(localFunction.getName())){
                                                element = type;                                               
                                            }
                                        }
                                    }
                                }
                                if (localDefn instanceof LocalDefn.Function.Definition){
                                    LocalDefn.Function.Definition def = (Definition) localDefn;
                                    Pair<SourceElement, SourceRange> innerMatch = findInnermostDef(def, position);
                                    if (innerMatch != null){
                                        element = innerMatch.fst();
                                        next = innerMatch.snd();
                                    }
                                }
                            }
                        }
                    }
                    if (element != null){
                        break;
                    }
                }
               
            }

            // check the current element
            SourceRange testSourcePosition = nthTopLevelDefn.getSourceRangeOfDefn();
            if (testSourcePosition.containsPosition(position)){
                if (next == null || next.contains(testSourcePosition)){
                    next = testSourcePosition;
                    element = nthTopLevelDefn;
                }
            }
        }

        if (element != null){
            // Find type declaration if any
            if (element instanceof SourceModel.FunctionDefn){
                SourceModel.FunctionDefn functionDefn = (FunctionDefn) element;
                String name = functionDefn.getName();
                for (int i = 0; i < nTopLevelDefns; i++) {
                    TopLevelSourceElement nthTopLevelDefn = moduleDefn.getNthTopLevelDefn(i);
                    if (nthTopLevelDefn instanceof FunctionTypeDeclaration){
                        FunctionTypeDeclaration type = (FunctionTypeDeclaration) nthTopLevelDefn;
                        if (type.getFunctionName().equals(name)){
                            element = type;
                            break;
                        }
                    }
                }
            }
           
            return new Pair<SourceElement, SourceRange>(element, element.getSourceRange());
        }
        else{
            if (moduleDefn.getSourceRange().containsPosition(position)){
                return new Pair<SourceElement, SourceRange>(moduleDefn, moduleDefn.getSourceRange());
            }
            return null;
        }
    }
}
TOP

Related Classes of org.openquark.cal.compiler.SourceMetricFinder$LintWalker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
nalytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');