/*
* 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.
*/
/*
* IdentifierOccurrenceFinder.java
* Created: Sep 11, 2007
* By: Joseph Wong
*/
package org.openquark.cal.compiler;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.openquark.cal.compiler.IdentifierOccurrence.Binding;
import org.openquark.cal.compiler.IdentifierOccurrence.Reference;
import org.openquark.cal.compiler.IdentifierResolver.SymbolTable;
import org.openquark.cal.compiler.IdentifierResolver.TypeVariableScope;
import org.openquark.cal.compiler.IdentifierResolver.VisitorArgument;
import org.openquark.cal.compiler.IdentifierResolver.SymbolTable.LocalScope;
import org.openquark.cal.compiler.IdentifierResolver.SymbolTable.TopLevelScope;
import org.openquark.cal.compiler.SourceModel.CALDoc;
import org.openquark.cal.compiler.SourceModel.Expr;
import org.openquark.cal.compiler.SourceModel.FieldPattern;
import org.openquark.cal.compiler.SourceModel.Friend;
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.Name;
import org.openquark.cal.compiler.SourceModel.SourceElement;
import org.openquark.cal.compiler.SourceModel.TypeConstructorDefn;
import org.openquark.cal.compiler.SourceModel.TypeExprDefn;
import org.openquark.cal.compiler.SourceModel.Import.UsingItem;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.util.Pair;
import org.openquark.util.UnsafeCast;
/**
* A visitor class that traverses a source model, gathers up the bindings declared
* therein, visits each source element with the appropriate symbol table passed in as a
* visitation argument, and gathers up the references (non-binding occurrences) as well.
* <p>
* This is the base class that should be extended if one wants to analyze a
* source model by processing different kinds of identifier occurrences.
*
* @author Joseph Wong
* @author James Wright
*/
/*
* @history
*
* Many of this class's source model traversal functionalities have been supported by the
* class SourceMetricFinder.SearchWalker, by James Wright.
*/
public class IdentifierOccurrenceFinder<R> extends IdentifierResolver.Visitor<IdentifierOccurrenceFinder.FinderState, R> {
/**
* Encapsulates the traversal state of the {@link IdentifierOccurrenceFinder}, to be passed
* around as the user-specified state in the visitor argument.
* <p>
* This class is designed as an immutable class, with update methods which produce new copies
* with the appropriate fields updated. This means that, for example, one does not need to keep
* an explicitly stack of state as one traverses up and down the source model.
*
* @author Joseph Wong
*/
public static final class FinderState {
/**
* If non-null, this list represents the data constructor names that are associated with
* any field names encountered during the visitation.
*
* Can be null.
*/
private final List<Name.DataCons> associatedDataConsNamesForFieldName;
/**
* If non-null, this represents the name of the type class that is associated with
* any instance methods encountered during the visitation.
*
* Can be null.
*/
private final Name.TypeClass associatedTypeClassNameForInstanceMethod;
/**
* Constructs an instance of this class.
*
* @param associatedDataConsNamesForFieldName
* if non-null, this list represents the data constructor names that are
* associated with any field names encountered during the visitation. Can be null.
* @param associatedTypeClassNameForInstanceMethod
* if non-null, this represents the name of the type class that is associated
* with any instance methods encountered during the visitation. Can be null.
*/
private FinderState(final List<Name.DataCons> associatedDataConsNamesForFieldName, final Name.TypeClass associatedTypeClassNameForInstanceMethod) {
if (associatedDataConsNamesForFieldName == null) {
this.associatedDataConsNamesForFieldName = null;
} else {
this.associatedDataConsNamesForFieldName = Collections.unmodifiableList(new ArrayList<Name.DataCons>(associatedDataConsNamesForFieldName));
}
this.associatedTypeClassNameForInstanceMethod = associatedTypeClassNameForInstanceMethod;
}
/**
* Factory method for constructing an instance of this class.
* @return a new instance.
*/
static FinderState make() {
return new FinderState(null, null);
}
/**
* @return if non-null, this list represents the data constructor names that are associated
* with any field names encountered during the visitation. Can be null.
*/
List<Name.DataCons> getAssociatedDataConsNamesForFieldName() {
return associatedDataConsNamesForFieldName;
}
/**
* @return if non-null, this represents the name of the type class that is associated with
* any instance methods encountered during the visitation. Can be null.
*/
Name.TypeClass getAssociatedTypeClassNameForInstanceMethod() {
return associatedTypeClassNameForInstanceMethod;
}
/**
* Constructs a new state object based on this one, but with an updated name representing
* the data constructor that is associated with any field names encountered during the
* visitation.
*
* @param associatedDataConsNameForFieldName
* data constructor name that is associated with any field names encountered
* during the visitation. Can *not* be null.
* @return a new state object.
*/
FinderState updateAssociatedDataConsNameForFieldName(final Name.DataCons associatedDataConsNameForFieldName) {
return new FinderState(Collections.singletonList(associatedDataConsNameForFieldName), this.associatedTypeClassNameForInstanceMethod);
}
/**
* Constructs a new state object based on this one, but with an updated list representing
* the data constructor names that are associated with any field names encountered during
* the visitation.
*
* @param associatedDataConsNamesForFieldName
* this array represents the data constructor names that are associated with any
* field names encountered during the visitation. Can *not* be null.
* @return a new state object.
*/
FinderState updateAssociatedDataConsNamesForFieldName(final Name.DataCons[] associatedDataConsNamesForFieldName) {
return new FinderState(Arrays.asList(associatedDataConsNamesForFieldName), this.associatedTypeClassNameForInstanceMethod);
}
/**
* Constructs a new state object based on this one, but with a null list representing
* the data constructor names that are associated with any field names encountered during
* the visitation - the visitation is moving out of data constructor field territory.
*
* @return a new state object.
*/
FinderState clearAssociatedDataConsNamesForFieldName() {
return new FinderState(null, this.associatedTypeClassNameForInstanceMethod);
}
/**
* Constructs a new state object based on this one, but with an updated name representing
* the type class that is associated with any instance methods encountered during the
* visitation.
*
* @param associatedTypeClassNameForInstanceMethod
* if non-null, this represents the name of the type class that is associated
* with any instance methods encountered during the visitation. Can be null.
* @return a new state object.
*/
FinderState updateAssociatedTypeClassNameForInstanceMethod(final Name.TypeClass associatedTypeClassNameForInstanceMethod) {
return new FinderState(this.associatedDataConsNamesForFieldName, associatedTypeClassNameForInstanceMethod);
}
}
////
/// Constructor
//
/**
* Constructs an instance of this class.
* @param currentModuleName the name of the module associated with the source being visited.
*/
public IdentifierOccurrenceFinder(final ModuleName currentModuleName) {
super(currentModuleName);
}
////
/// Handler methods
//
/**
* Processes a module name binding occurrence.
* @param binding the binding occurrence.
* @param scope the scope declaring the binding.
*/
protected void handleModuleNameBinding(
final Binding<IdentifierInfo.Module> binding,
final TopLevelScope scope) {}
/**
* Processes a module name reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleModuleNameReference(
final Reference<IdentifierInfo.Module> reference,
final Binding<IdentifierInfo.Module> binding,
final SymbolTable scope) {}
/**
* Processes a top-level function or class method name binding occurrence.
* @param binding the binding occurrence.
* @param scope the scope declaring the binding.
*/
protected void handleTopLevelFunctionOrClassMethodBinding(
final Binding<IdentifierInfo.TopLevel.FunctionOrClassMethod> binding,
final TopLevelScope scope) {}
/**
* Processes a local variable name binding occurrence.
* @param binding the binding occurrence.
* @param scope the scope declaring the binding.
*/
protected void handleLocalVariableBinding(
final Binding<IdentifierInfo.Local> binding,
final LocalScope scope) {}
/**
* Processes a variable name reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleVarNameReference(
final Reference<? extends IdentifierInfo> reference,
final Binding<? extends IdentifierInfo> binding,
final SymbolTable scope) {}
/**
* Processes a top-level function or class method operator reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleTopLevelFunctionOrClassMethodOperatorReference(
final Reference.Operator<IdentifierInfo.TopLevel.FunctionOrClassMethod> reference,
final Binding<IdentifierInfo.TopLevel.FunctionOrClassMethod> binding,
final SymbolTable scope) {}
/**
* Processes a data constructor name binding occurrence.
* @param binding the binding occurrence.
* @param scope the scope declaring the binding.
*/
protected void handleDataConsBinding(
final Binding<IdentifierInfo.TopLevel.DataCons> binding,
final TopLevelScope scope) {}
/**
* Processes a data constructor name reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleDataConsNameReference(
final Reference<IdentifierInfo.TopLevel.DataCons> reference,
final Binding<IdentifierInfo.TopLevel.DataCons> binding,
final SymbolTable scope) {}
/**
* Processes a data constructor operator reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleDataConsOperatorReference(
final Reference.Operator<IdentifierInfo.TopLevel.DataCons> reference,
final Binding<IdentifierInfo.TopLevel.DataCons> binding,
final SymbolTable scope) {}
/**
* Processes a type constructor name binding occurrence.
* @param binding the binding occurrence.
* @param scope the scope declaring the binding.
*/
protected void handleTypeConsBinding(
final Binding<IdentifierInfo.TopLevel.TypeCons> binding,
final TopLevelScope scope) {}
/**
* Processes a type constructor name reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleTypeConsNameReference(
final Reference<IdentifierInfo.TopLevel.TypeCons> reference,
final Binding<IdentifierInfo.TopLevel.TypeCons> binding,
final SymbolTable scope) {}
/**
* Processes a type constructor operator reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleTypeConsOperatorReference(
final Reference.Operator<IdentifierInfo.TopLevel.TypeCons> reference,
final Binding<IdentifierInfo.TopLevel.TypeCons> binding,
final SymbolTable scope) {}
/**
* Processes a type class name binding occurrence.
* @param binding the binding occurrence.
* @param scope the scope declaring the binding.
*/
protected void handleTypeClassBinding(
final Binding<IdentifierInfo.TopLevel.TypeClass> binding,
final TopLevelScope scope) {}
/**
* Processes a type class name reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleTypeClassNameReference(
final Reference<IdentifierInfo.TopLevel.TypeClass> reference,
final Binding<IdentifierInfo.TopLevel.TypeClass> binding,
final SymbolTable scope) {}
/**
* Processes a data constructor field name binding occurrence.
* @param binding the binding occurrence.
* @param scope the scope declaring the binding.
*/
protected void handleDataConsFieldNameBinding(
final Binding<IdentifierInfo.DataConsFieldName> binding,
final TopLevelScope scope) {}
/**
* Processes a data constructor field name reference occurrence.
* @param reference the reference occurrence.
* @param bindings the list of bindings to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleDataConsFieldNameReference(
final Reference.DataConsFieldName reference,
final List<Binding<IdentifierInfo.DataConsFieldName>> bindings,
final SymbolTable scope) {}
/**
* Processes a record field name reference occurrence.
* @param reference the reference occurrence.
*/
protected void handleRecordFieldNameReference(final Reference.RecordFieldName reference) {}
/**
* Processes a type variable name binding occurrence.
* @param binding the binding occurrence.
* @param scope the scope declaring the binding.
*/
protected void handleTypeVariableBinding(
final Binding<IdentifierInfo.TypeVariable> binding,
final TypeVariableScope scope) {}
/**
* Processes a type variable name reference occurrence.
* @param reference the reference occurrence.
* @param binding the binding to which the reference is resolved.
* @param scope the innermost scope from which the resolution is initiated.
*/
protected void handleTypeVariableReference(
final Reference<IdentifierInfo.TypeVariable> reference,
final Binding<IdentifierInfo.TypeVariable> binding,
final TypeVariableScope scope) {}
////
/// Occurrence recording methods
//
/**
* Resolves a module name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param moduleName the module name.
* @param mustBeFullyQualified whether the module name must be fully qualified.
* @param allowForwardReference whether a forward reference is allowed in the context.
*/
protected void recordModuleName(final SymbolTable scope, final Name.Module moduleName, final boolean mustBeFullyQualified, final boolean allowForwardReference) {
final Binding<IdentifierInfo.Module> binding = scope.resolveMaybeModuleName(moduleName);
if (binding != null) {
if (binding.getSourceElement() != moduleName) {
handleModuleNameReference(
Reference.Module.make(mustBeFullyQualified, binding.getIdentifierInfo(), moduleName),
binding,
scope);
}
} else if (allowForwardReference) {
final IdentifierInfo.Module identifierInfo = new IdentifierInfo.Module(SourceModel.Name.Module.toModuleName(moduleName));
handleModuleNameReference(
Reference.Module.make(mustBeFullyQualified, identifierInfo, moduleName),
Binding.External.make(identifierInfo),
scope);
}
}
/**
* Resolves a module name appearing in a qualified name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param moduleNameReference the module name reference in the qualified name.
* @param allowForwardReference whether a forward reference is allowed in the context.
*/
protected void recordModuleNameInQualifiedName(final SymbolTable scope, final Reference.Module moduleNameReference, final boolean allowForwardReference) {
final Binding<IdentifierInfo.Module> binding = scope.resolveMaybeModuleName(moduleNameReference.getIdentifierInfo().getResolvedName());
if (binding != null) {
handleModuleNameReference(moduleNameReference, binding, scope);
} else if (allowForwardReference) {
final IdentifierInfo.Module identifierInfo = new IdentifierInfo.Module(moduleNameReference.getIdentifierInfo().getResolvedName());
handleModuleNameReference(moduleNameReference, Binding.External.make(identifierInfo), scope);
}
}
/**
* Resolves a type variable name, and if the occurrence is a reference occurrence, records it.
* @param scope the current type variable scope.
* @param typeVarName the type variable name.
*/
protected void recordTypeVarName(final TypeVariableScope scope, final Name.TypeVar typeVarName) {
final Binding<IdentifierInfo.TypeVariable> binding = scope.findTypeVar(typeVarName.getName());
if (binding != null && binding.getSourceElement() != typeVarName) {
handleTypeVariableReference(
Reference.TypeVariable.make(
new IdentifierInfo.TypeVariable(binding.getIdentifierInfo().getTypeVarName(), binding.getIdentifierInfo().getTypeVarUniqueIdentifier()),
typeVarName),
binding,
scope);
}
}
/**
* Resolves a function name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param functionName the function name.
* @param isExpression whether the occurrence appears in an expression context.
*/
protected void recordFunctionName(final SymbolTable scope, final Name.Function functionName, final boolean isExpression) {
final Binding<? extends IdentifierInfo> binding = scope.findFunction(functionName);
if (binding != null && binding.getSourceElement() != functionName) {
if (binding.getIdentifierInfo() instanceof IdentifierInfo.TopLevel.FunctionOrClassMethod) {
final IdentifierInfo.TopLevel.FunctionOrClassMethod topLevelFunctionOrClassMethodIdentifierInfo =
(IdentifierInfo.TopLevel.FunctionOrClassMethod)binding.getIdentifierInfo();
final Name.Module moduleName = functionName.getModuleName();
final Reference.Module moduleNameOccurrence;
if (moduleName == null) {
moduleNameOccurrence = null;
} else {
moduleNameOccurrence =
Reference.Module.make(
false,
new IdentifierInfo.Module(topLevelFunctionOrClassMethodIdentifierInfo.getResolvedName().getModuleName()),
moduleName);
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, false);
}
handleVarNameReference(
Reference.Qualifiable.make(topLevelFunctionOrClassMethodIdentifierInfo, isExpression, moduleNameOccurrence, functionName),
binding,
scope);
} else if (binding.getIdentifierInfo() instanceof IdentifierInfo.Local) {
final IdentifierInfo.Local localIdentifierInfo =
(IdentifierInfo.Local)binding.getIdentifierInfo();
handleVarNameReference(
Reference.Local.make(localIdentifierInfo, functionName), binding, scope);
} else {
throw new IllegalStateException("Unexpected binding:" + binding);
}
}
}
/**
* Resolves an unqualified variable name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param varName the variable name.
* @param sourceElement the associated source element.
* @param nameSourceRange the source range of the name.
*/
protected void recordUnqualifiedVarName(final SymbolTable scope, final String varName, final SourceElement sourceElement, final SourceRange nameSourceRange) {
final Binding<? extends IdentifierInfo> binding = scope.findVariableDefinition(varName);
if (binding != null && binding.getSourceElement() != sourceElement) {
handleVarNameReference(
Reference.NonQualifiable.make(binding.getIdentifierInfo(), sourceElement, nameSourceRange),
binding,
scope);
}
}
/**
* Resolves an argument name in a CALDoc arg block, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param argBlock the arg block.
* @param associatedDataConsNames if non-null, this list represents the data constructor names that are associated with
* any field names encountered during the visitation.
*/
protected void recordCALDocArgBlock(final SymbolTable scope, final CALDoc.TaggedBlock.Arg argBlock, final List<Name.DataCons> associatedDataConsNames) {
if (associatedDataConsNames == null) {
// a function argument reference
final String argName = argBlock.getArgName().getName().getCalSourceForm();
final Binding<? extends IdentifierInfo> binding = scope.findVariableDefinition(argName);
if (binding != null) {
if (binding.getSourceElement() != argBlock.getArgName()) {
if (binding.getIdentifierInfo() instanceof IdentifierInfo.Local.Parameter) {
final IdentifierInfo.Local.Parameter paramIdentifierInfo =
(IdentifierInfo.Local.Parameter)binding.getIdentifierInfo();
handleVarNameReference(
Reference.Local.make(paramIdentifierInfo, argBlock.getArgName()), binding, scope);
} else {
// the name resolved to something other than a parameter, so the source code is broken
// but this is not an error case to be reported as an exception.
}
}
} else {
// a binding could not be found - so this is in fact a definition of a non-lexical argument
// (e.g. Prelude.apply's second argument "argument" is declared only in CALDoc)
System.err.println("Bad @arg: " + argName + " at " + argBlock.getArgName().getSourceRange());
}
} else {
// a data cons argument reference
assert associatedDataConsNames.size() == 1;
recordNonBindingDataConsFieldName(scope, argBlock.getArgName(), associatedDataConsNames, false);
}
}
/**
* Resolves a top-level function name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param functionName the function name.
* @param isExpression whether the occurrence appears in an expression context.
* @param isUncheckedCALDocReference whether the occurrence is an unchecked CALDoc reference.
*/
protected void recordTopLevelFunctionName(final SymbolTable scope, final Name.Function functionName, final boolean isExpression, final boolean isUncheckedCALDocReference) {
if (isUncheckedCALDocReference) {
// It is an unchecked CALDoc reference, so we create an occurrence with an external binding
// but only if it has a module name that is not the current module name
// (otherwise it would have been local to the module, and we leave it to the code below to handle it)
final Name.Module moduleName = functionName.getModuleName();
if (moduleName != null) {
final ModuleName moduleNameInterpretedAsFullyQualified = SourceModel.Name.Module.toModuleName(moduleName);
if (!moduleNameInterpretedAsFullyQualified.equals(getCurrentModuleName())) {
final Reference.Module moduleNameOccurrence =
Reference.Module.make(
true,
new IdentifierInfo.Module(moduleNameInterpretedAsFullyQualified),
moduleName);
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, true);
final Binding<IdentifierInfo.TopLevel.FunctionOrClassMethod> externalBinding =
Binding.External.make(
new IdentifierInfo.TopLevel.FunctionOrClassMethod(
QualifiedName.make(moduleNameInterpretedAsFullyQualified, functionName.getUnqualifiedName())));
handleVarNameReference(
Reference.Qualifiable.make(externalBinding.getIdentifierInfo(), false, moduleNameOccurrence, functionName),
externalBinding,
scope);
// we're done, so return here to skip the code below
return;
}
}
}
final Binding<IdentifierInfo.TopLevel.FunctionOrClassMethod> binding = scope.findTopLevelFunctionOrClassMethodDefinition(functionName);
if (binding != null) {
if (binding.getSourceElement() != functionName) {
final Name.Module moduleName = functionName.getModuleName();
final Reference.Module moduleNameOccurrence;
if (moduleName == null) {
moduleNameOccurrence = null;
} else {
moduleNameOccurrence =
Reference.Module.make(
isUncheckedCALDocReference,
new IdentifierInfo.Module(binding.getIdentifierInfo().getResolvedName().getModuleName()),
moduleName);
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, false);
}
handleVarNameReference(
Reference.Qualifiable.make(binding.getIdentifierInfo(), isExpression, moduleNameOccurrence, functionName),
binding,
scope);
}
}
}
/**
* Resolves an instance method name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param instanceMethod the instance method.
* @param typeClassName the associated type class name.
*/
protected void recordInstanceMethodName(final SymbolTable scope, final InstanceDefn.InstanceMethod instanceMethod, final Name.TypeClass typeClassName) {
final Binding<IdentifierInfo.TopLevel.TypeClass> typeClassBinding = scope.findTypeClass(typeClassName);
if (typeClassBinding != null) {
final Name.Function classMethodName = Name.Function.make(
typeClassBinding.getIdentifierInfo().getResolvedName().getModuleName(), instanceMethod.getClassMethodName());
final Binding<? extends IdentifierInfo> classMethodBinding = scope.findFunction(classMethodName);
if (classMethodBinding != null) {
if (classMethodBinding.getIdentifierInfo() instanceof IdentifierInfo.TopLevel.FunctionOrClassMethod) {
final IdentifierInfo.TopLevel.FunctionOrClassMethod topLevelFunctionOrClassMethodIdentifierInfo =
(IdentifierInfo.TopLevel.FunctionOrClassMethod)classMethodBinding.getIdentifierInfo();
handleVarNameReference(
Reference.NonQualifiable.make(topLevelFunctionOrClassMethodIdentifierInfo, instanceMethod, instanceMethod.getClassMethodNameSourceRange()),
classMethodBinding,
scope);
} else {
throw new IllegalStateException("Unexpected binding:" + classMethodBinding);
}
}
}
}
/**
* Resolves a data constructor name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param dataConsName the data constructor name.
* @param isExpression whether the occurrence appears in an expression context.
* @param isUncheckedCALDocReference whether the occurrence is an unchecked CALDoc reference.
*/
protected void recordDataConsName(final SymbolTable scope, final Name.DataCons dataConsName, final boolean isExpression, final boolean isUncheckedCALDocReference) {
final Pair<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>, Binding<IdentifierInfo.TopLevel.DataCons>>
dataConsReferenceBindingPair = resolveDataConsOccurrence(scope, dataConsName, isExpression, isUncheckedCALDocReference, true);
if (dataConsReferenceBindingPair != null) {
handleDataConsNameReference(dataConsReferenceBindingPair.fst(), dataConsReferenceBindingPair.snd(), scope);
}
}
/**
* Resolves a data constructor name.
* @param scope the current scope.
* @param dataConsName the data constructor name.
* @param isExpression whether the occurrence appears in an expression context.
* @param isUncheckedCALDocReference whether the occurrence is an unchecked CALDoc reference.
* @param shouldRecordModuleNameOccurrence whether to record the module name occurrence in the data constructor name.
* @return a pair: the data constructor reference, and the associated binding.
*/
protected Pair<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>, Binding<IdentifierInfo.TopLevel.DataCons>>
resolveDataConsOccurrence(
final SymbolTable scope, final Name.DataCons dataConsName, final boolean isExpression, final boolean isUncheckedCALDocReference, final boolean shouldRecordModuleNameOccurrence) {
if (isUncheckedCALDocReference) {
// It is an unchecked CALDoc reference, so we create an occurrence with an external binding
// but only if it has a module name that is not the current module name
// (otherwise it would have been local to the module, and we leave it to the code below to handle it)
final Name.Module moduleName = dataConsName.getModuleName();
if (moduleName != null) {
final ModuleName moduleNameInterpretedAsFullyQualified = SourceModel.Name.Module.toModuleName(moduleName);
if (!moduleNameInterpretedAsFullyQualified.equals(getCurrentModuleName())) {
final Reference.Module moduleNameOccurrence =
Reference.Module.make(
true,
new IdentifierInfo.Module(moduleNameInterpretedAsFullyQualified),
moduleName);
if (shouldRecordModuleNameOccurrence) {
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, true);
}
final Binding<IdentifierInfo.TopLevel.DataCons> externalBinding =
Binding.External.make(
new IdentifierInfo.TopLevel.DataCons(
QualifiedName.make(moduleNameInterpretedAsFullyQualified, dataConsName.getUnqualifiedName())));
return Pair.make(
Reference.Qualifiable.make(externalBinding.getIdentifierInfo(), false, moduleNameOccurrence, dataConsName),
externalBinding);
}
}
}
final Binding<IdentifierInfo.TopLevel.DataCons> binding = scope.findDataCons(dataConsName);
if (binding != null) {
if (binding.getSourceElement() != dataConsName) {
final Name.Module moduleName = dataConsName.getModuleName();
final Reference.Module moduleNameOccurrence;
if (moduleName == null) {
moduleNameOccurrence = null;
} else {
moduleNameOccurrence =
Reference.Module.make(
isUncheckedCALDocReference,
new IdentifierInfo.Module(binding.getIdentifierInfo().getResolvedName().getModuleName()),
moduleName);
if (shouldRecordModuleNameOccurrence) {
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, false);
}
}
return Pair.make(
Reference.Qualifiable.make(binding.getIdentifierInfo(), isExpression, moduleNameOccurrence, dataConsName),
binding);
}
}
return null;
}
/**
* Resolves a type constructor name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param typeConsName the type constructor name.
* @param isUncheckedCALDocReference whether the occurrence is an unchecked CALDoc reference.
*/
protected void recordTypeConsName(final SymbolTable scope, final Name.TypeCons typeConsName, final boolean isUncheckedCALDocReference) {
if (isUncheckedCALDocReference) {
// It is an unchecked CALDoc reference, so we create an occurrence with an external binding
// but only if it has a module name that is not the current module name
// (otherwise it would have been local to the module, and we leave it to the code below to handle it)
final Name.Module moduleName = typeConsName.getModuleName();
if (moduleName != null) {
final ModuleName moduleNameInterpretedAsFullyQualified = SourceModel.Name.Module.toModuleName(moduleName);
if (!moduleNameInterpretedAsFullyQualified.equals(getCurrentModuleName())) {
final Reference.Module moduleNameOccurrence =
Reference.Module.make(
true,
new IdentifierInfo.Module(moduleNameInterpretedAsFullyQualified),
moduleName);
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, true);
final Binding<IdentifierInfo.TopLevel.TypeCons> externalBinding =
Binding.External.make(
new IdentifierInfo.TopLevel.TypeCons(
QualifiedName.make(moduleNameInterpretedAsFullyQualified, typeConsName.getUnqualifiedName())));
handleVarNameReference(
Reference.Qualifiable.make(externalBinding.getIdentifierInfo(), false, moduleNameOccurrence, typeConsName),
externalBinding,
scope);
// we're done, so return here to skip the code below
return;
}
}
}
final Binding<IdentifierInfo.TopLevel.TypeCons> binding = scope.findTypeCons(typeConsName);
if (binding != null) {
if (binding.getSourceElement() != typeConsName) {
final Name.Module moduleName = typeConsName.getModuleName();
final Reference.Module moduleNameOccurrence;
if (moduleName == null) {
moduleNameOccurrence = null;
} else {
moduleNameOccurrence =
Reference.Module.make(
isUncheckedCALDocReference,
new IdentifierInfo.Module(binding.getIdentifierInfo().getResolvedName().getModuleName()),
moduleName);
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, false);
}
handleTypeConsNameReference(
Reference.Qualifiable.make(binding.getIdentifierInfo(), false, moduleNameOccurrence, typeConsName),
binding,
scope);
}
}
}
/**
* Resolves a type class name, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param typeClassName the type class name.
* @param isUncheckedCALDocReference whether the occurrence is an unchecked CALDoc reference.
*/
protected void recordTypeClassName(final SymbolTable scope, final Name.TypeClass typeClassName, final boolean isUncheckedCALDocReference) {
if (isUncheckedCALDocReference) {
// It is an unchecked CALDoc reference, so we create an occurrence with an external binding
// but only if it has a module name that is not the current module name
// (otherwise it would have been local to the module, and we leave it to the code below to handle it)
final Name.Module moduleName = typeClassName.getModuleName();
if (moduleName != null) {
final ModuleName moduleNameInterpretedAsFullyQualified = SourceModel.Name.Module.toModuleName(moduleName);
if (!moduleNameInterpretedAsFullyQualified.equals(getCurrentModuleName())) {
final Reference.Module moduleNameOccurrence =
Reference.Module.make(
true,
new IdentifierInfo.Module(moduleNameInterpretedAsFullyQualified),
moduleName);
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, true);
final Binding<IdentifierInfo.TopLevel.TypeClass> externalBinding =
Binding.External.make(
new IdentifierInfo.TopLevel.TypeClass(
QualifiedName.make(moduleNameInterpretedAsFullyQualified, typeClassName.getUnqualifiedName())));
handleVarNameReference(
Reference.Qualifiable.make(externalBinding.getIdentifierInfo(), false, moduleNameOccurrence, typeClassName),
externalBinding,
scope);
// we're done, so return here to skip the code below
return;
}
}
}
final Binding<IdentifierInfo.TopLevel.TypeClass> binding = scope.findTypeClass(typeClassName);
if (binding != null) {
if (binding.getSourceElement() != typeClassName) {
final Name.Module moduleName = typeClassName.getModuleName();
final Reference.Module moduleNameOccurrence;
if (moduleName == null) {
moduleNameOccurrence = null;
} else {
moduleNameOccurrence =
Reference.Module.make(
isUncheckedCALDocReference,
new IdentifierInfo.Module(binding.getIdentifierInfo().getResolvedName().getModuleName()),
moduleName);
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, false);
}
handleTypeClassNameReference(
Reference.Qualifiable.make(binding.getIdentifierInfo(), false, moduleNameOccurrence, typeClassName),
binding,
scope);
}
}
}
/**
* Resolves a constructor name without context (as appearing in a CALDoc reference), and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param name the constructor name.
* @param isUncheckedCALDocReference whether the occurrence is an unchecked CALDoc reference.
*/
protected void recordConsNameWithoutContext(final SymbolTable scope, final Name.WithoutContextCons name, final boolean isUncheckedCALDocReference) {
if (isUncheckedCALDocReference) {
// unchecked CALDoc reference with constructor name *must* have context provided
return;
}
final Binding<? extends IdentifierInfo> binding = scope.findConsNameWithoutContext(name);
if (binding != null) {
if (binding.getSourceElement() != name) {
if (binding.getIdentifierInfo() instanceof IdentifierInfo.Module) {
final IdentifierInfo.Module moduleIdentifierInfo = (IdentifierInfo.Module)binding.getIdentifierInfo();
handleModuleNameReference(
Reference.Module.make(isUncheckedCALDocReference, moduleIdentifierInfo, name),
UnsafeCast.<Binding<IdentifierInfo.Module>>unsafeCast(binding),
scope);
} else if (binding.getIdentifierInfo() instanceof IdentifierInfo.TopLevel){
final IdentifierInfo.TopLevel topLevelIdentifierInfo = (IdentifierInfo.TopLevel)binding.getIdentifierInfo();
final Name.Module moduleName = name.getModuleName();
final Reference.Module moduleNameOccurrence;
if (moduleName == null) {
moduleNameOccurrence = null;
} else {
moduleNameOccurrence =
Reference.Module.make(
isUncheckedCALDocReference,
new IdentifierInfo.Module(topLevelIdentifierInfo.getResolvedName().getModuleName()),
moduleName);
recordModuleNameInQualifiedName(scope, moduleNameOccurrence, false);
}
if (topLevelIdentifierInfo instanceof IdentifierInfo.TopLevel.FunctionOrClassMethod) {
handleVarNameReference(
Reference.Qualifiable.make(topLevelIdentifierInfo, false, moduleNameOccurrence, name),
UnsafeCast.<Binding<IdentifierInfo.TopLevel.FunctionOrClassMethod>>unsafeCast(binding),
scope);
} else if (topLevelIdentifierInfo instanceof IdentifierInfo.TopLevel.DataCons) {
handleDataConsNameReference(
Reference.Qualifiable.make((IdentifierInfo.TopLevel.DataCons)topLevelIdentifierInfo, false, moduleNameOccurrence, name),
UnsafeCast.<Binding<IdentifierInfo.TopLevel.DataCons>>unsafeCast(binding),
scope);
} else if (topLevelIdentifierInfo instanceof IdentifierInfo.TopLevel.TypeCons) {
handleTypeConsNameReference(
Reference.Qualifiable.make((IdentifierInfo.TopLevel.TypeCons)topLevelIdentifierInfo, false, moduleNameOccurrence, name),
UnsafeCast.<Binding<IdentifierInfo.TopLevel.TypeCons>>unsafeCast(binding),
scope);
} else if (topLevelIdentifierInfo instanceof IdentifierInfo.TopLevel.TypeClass) {
handleTypeClassNameReference(
Reference.Qualifiable.make((IdentifierInfo.TopLevel.TypeClass)topLevelIdentifierInfo, false, moduleNameOccurrence, name),
UnsafeCast.<Binding<IdentifierInfo.TopLevel.TypeClass>>unsafeCast(binding),
scope);
} else {
throw new IllegalStateException("Unexpected binding: " + binding);
}
} else {
throw new IllegalStateException("Unexpected binding: " + binding);
}
}
}
// CALDoc constructor name references without context must be resolvable, otherwise they must be declared with context
// so if we fall out here, there's nothing we can do.
}
/**
* Resolves a data constructor field name appearing in a non-binding context, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param fieldName the field name.
* @param dataConsNames the names of the associated data constructors - there can be more than one.
* @param isPunnedOrdinal whether the field name is an ordinal field name in a punned context.
*/
protected void recordNonBindingDataConsFieldName(final SymbolTable scope, final Name.Field fieldName, final List<Name.DataCons> dataConsNames, final boolean isPunnedOrdinal) {
final List<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>> dataConsOccurrences =
new ArrayList<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>>();
final List<IdentifierInfo.TopLevel.DataCons> dataConsIdentifierInfos =
new ArrayList<IdentifierInfo.TopLevel.DataCons>();
final List<Binding<IdentifierInfo.DataConsFieldName>> bindings =
new ArrayList<Binding<IdentifierInfo.DataConsFieldName>>();
for (final Name.DataCons dataConsName : dataConsNames) {
final Pair<Reference.Qualifiable<IdentifierInfo.TopLevel.DataCons>, ?> dataConsReferenceBindingPair =
resolveDataConsOccurrence(scope, dataConsName, false, false, false);
if (dataConsReferenceBindingPair == null) {
continue;
}
final Binding<IdentifierInfo.DataConsFieldName> binding = scope.findDataConsFieldName(dataConsName, fieldName);
if (binding == null) {
continue;
}
dataConsOccurrences.add(dataConsReferenceBindingPair.fst());
dataConsIdentifierInfos.add(dataConsReferenceBindingPair.fst().getIdentifierInfo());
bindings.add(binding);
}
if (isPunnedOrdinal) {
handleDataConsFieldNameReference(
Reference.DataConsFieldName.PunnedOrdinal.make(
new IdentifierInfo.DataConsFieldName(fieldName.getName(), dataConsIdentifierInfos),
dataConsOccurrences,
fieldName),
bindings,
scope);
} else {
handleDataConsFieldNameReference(
Reference.DataConsFieldName.NonPunned.make(
new IdentifierInfo.DataConsFieldName(fieldName.getName(), dataConsIdentifierInfos),
dataConsOccurrences,
fieldName),
bindings,
scope);
}
}
/**
* Resolves a record field name appearing in a non-binding context, and if the occurrence is a reference occurrence, records it.
* @param scope the current scope.
* @param fieldName the field name.
* @param isPunnedOrdinal whether the field name is an ordinal field name in a punned context.
*/
protected void recordNonBindingRecordFieldName(final SymbolTable scope, final Name.Field fieldName, final boolean isPunnedOrdinal) {
if (isPunnedOrdinal) {
handleRecordFieldNameReference(
Reference.RecordFieldName.PunnedOrdinal.make(new IdentifierInfo.RecordFieldName(fieldName.getName()), fieldName));
} else {
handleRecordFieldNameReference(
Reference.RecordFieldName.NonPunned.make(new IdentifierInfo.RecordFieldName(fieldName.getName()), fieldName));
}
}
/**
* Resolves a function/method operator, and records the occurrence.
* @param scope the current scope.
* @param operatorFunctionName the name corresponding to the operator.
* @param sourceElement the operator's associated source element.
* @param operatorSourceRange the source range associated with the actual operator occurring in source. Can be null.
* @param isDelimiterPair whether the operator consists of a pair of delimiters, e.g. (), [].
*/
protected void recordTopLevelFunctionOrClassMethodOperator(
final SymbolTable scope, final QualifiedName operatorFunctionName, final SourceElement sourceElement, final SourceRange operatorSourceRange, final boolean isDelimiterPair) {
final Binding<IdentifierInfo.TopLevel.FunctionOrClassMethod> binding =
scope.findQualifiedFunctionOrClassMethod(Name.Function.make(operatorFunctionName));
if (binding == null) {
throw new IllegalStateException("Operators should always be resolvable");
}
handleTopLevelFunctionOrClassMethodOperatorReference(
Reference.Operator.make(binding.getIdentifierInfo(), true, sourceElement, isDelimiterPair, operatorSourceRange),
binding,
scope);
}
/**
* Resolves a data constructor operator, and records the occurrence.
* @param scope the current scope.
* @param operatorDataConsName the name corresponding to the operator.
* @param sourceElement the operator's associated source element.
* @param operatorSourceRange the source range associated with the actual operator occurring in source. Can be null.
* @param isExpressionContext whether the occurrence appears in an expression context.
* @param isDelimiterPair whether the operator consists of a pair of delimiters, e.g. (), [].
*/
protected void recordDataConsOperator(
final SymbolTable scope, final QualifiedName operatorDataConsName, final SourceElement sourceElement, final SourceRange operatorSourceRange, final boolean isExpressionContext, final boolean isDelimiterPair) {
final Binding<IdentifierInfo.TopLevel.DataCons> binding = scope.findDataCons(Name.DataCons.make(operatorDataConsName));
if (binding == null) {
throw new IllegalStateException("Operators should always be resolvable");
}
handleDataConsOperatorReference(
Reference.Operator.make(binding.getIdentifierInfo(), isExpressionContext, sourceElement, isDelimiterPair, operatorSourceRange),
binding,
scope);
}
/**
* Resolves a type constructor operator, and records the occurrence.
* @param scope the current scope.
* @param operatorTypeConsName the name corresponding to the operator.
* @param sourceElement the operator's associated source element.
* @param operatorSourceRange the source range associated with the actual operator occurring in source. Can be null.
* @param isDelimiterPair whether the operator consists of a pair of delimiters, e.g. (), [].
*/
protected void recordTypeConsOperator(
final SymbolTable scope, final QualifiedName operatorTypeConsName, final SourceElement sourceElement, final SourceRange operatorSourceRange, final boolean isDelimiterPair) {
final Binding<IdentifierInfo.TopLevel.TypeCons> binding;
if (operatorTypeConsName.equals(CAL_Prelude.TypeConstructors.Function)) {
// Prelude.Function is a very special type that does not have a type declaration
// so we just use an external binding
binding = Binding.External.make(new IdentifierInfo.TopLevel.TypeCons(CAL_Prelude.TypeConstructors.Function));
} else {
binding = scope.findTypeCons(Name.TypeCons.make(operatorTypeConsName));
if (binding == null) {
throw new IllegalStateException("Operators should always be resolvable");
}
}
handleTypeConsOperatorReference(
Reference.Operator.make(binding.getIdentifierInfo(), false, sourceElement, isDelimiterPair, operatorSourceRange),
binding,
scope);
}
////
/// Overridden handler methods for new scopes - translating them into binding occurrences
//
/**
* {@inheritDoc}
*/
@Override
protected void handleNewLocalScope(final LocalScope scope) {
for (final Binding<IdentifierInfo.Local> binding : scope.getBindings()) {
handleLocalVariableBinding(binding, scope);
}
super.handleNewLocalScope(scope);
}
/**
* {@inheritDoc}
*/
@Override
protected void handleNewTopLevelScope(final TopLevelScope scope) {
handleModuleNameBinding(scope.getCurrentModuleNameBinding(), scope);
for (final Binding<IdentifierInfo.TopLevel.FunctionOrClassMethod> binding : scope.getFunctionAndClassMethodBindings()) {
handleTopLevelFunctionOrClassMethodBinding(binding, scope);
}
for (final Binding<IdentifierInfo.TopLevel.TypeCons> binding : scope.getTypeConsBindings()) {
handleTypeConsBinding(binding, scope);
}
for (final Binding<IdentifierInfo.TopLevel.DataCons> binding : scope.getDataConsBindings()) {
handleDataConsBinding(binding, scope);
}
for (final Binding<IdentifierInfo.DataConsFieldName> binding : scope.getDataConsFieldNameBindings()) {
handleDataConsFieldNameBinding(binding, scope);
}
for (final Binding<IdentifierInfo.TopLevel.TypeClass> binding : scope.getTypeClassBindings()) {
handleTypeClassBinding(binding, scope);
}
super.handleNewTopLevelScope(scope);
}
/**
* {@inheritDoc}
*/
@Override
protected void handleNewTypeVariableScope(final TypeVariableScope scope) {
for (final Binding<IdentifierInfo.TypeVariable> binding : scope.getBindings()) {
handleTypeVariableBinding(binding, scope);
}
super.handleNewTypeVariableScope(scope);
}
////
/// Visitor methods - elements which introduce new references (bindings are taken care of by the superclass)
//
/**
* {@inheritDoc}
*/
@Override
public R visit_Name_Module(final Name.Module moduleName, final VisitorArgument<FinderState> arg) {
recordModuleName(arg.getScope(), moduleName, false, false);
return super.visit_Name_Module(moduleName, arg);
}
@Override
public R visit_Friend(final Friend friendDeclaration, final VisitorArgument<FinderState> arg) {
recordModuleName(arg.getScope(), friendDeclaration.getFriendModuleName(), true, true);
// we do not invoke the superclass implementation, which would delegate to visit_Name_Module
return null;
}
@Override
public R visit_Import(final Import importStmt, final VisitorArgument<FinderState> arg) {
// we do not invoke the superclass implementation, which would delegate to visit_Name_Module
// instead we do the traversal ourselves
recordModuleName(arg.getScope(), importStmt.getImportedModuleName(), true, false);
for (final UsingItem usingItem : importStmt.getUsingItems()) {
usingItem.accept(this, arg);
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_CALDoc_CrossReference_Module(final CALDoc.CrossReference.Module reference, final VisitorArgument<FinderState> arg) {
recordModuleName(arg.getScope(), reference.getName(), !reference.isChecked(), !reference.isChecked());
// we do not invoke the superclass implementation, which would delegate to visit_Name_Module
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Name_Function(final Name.Function function, final VisitorArgument<FinderState> arg) {
recordFunctionName(arg.getScope(), function, false);
// instead of invoking the superclass implementation, which would delegate to visit_Name_Module
// the recording method has already done the recording of the module name
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_FunctionTypeDeclaraction(final FunctionTypeDeclaration declaration, final VisitorArgument<FinderState> arg) {
recordUnqualifiedVarName(arg.getScope(), declaration.getFunctionName(), declaration, declaration.getSourceRangeOfName());
return super.visit_FunctionTypeDeclaraction(declaration, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_LocalDefn_Function_TypeDeclaration(final LocalDefn.Function.TypeDeclaration declaration, final VisitorArgument<FinderState> arg) {
recordUnqualifiedVarName(arg.getScope(), declaration.getName(), declaration, declaration.getNameSourceRange());
return super.visit_LocalDefn_Function_TypeDeclaration(declaration, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_Var(final Expr.Var var, final VisitorArgument<FinderState> arg) {
// instead of invoking the superclass implementation, which would delegate to visit_Name_Function
// we record the function name directly, and specify that it is in an expression context
recordFunctionName(arg.getScope(), var.getVarName(), true);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_CALDoc_CrossReference_Function(final CALDoc.CrossReference.Function reference, final VisitorArgument<FinderState> arg) {
// instead of invoking the superclass implementation, which would delegate to visit_Name_Function
// we record the function name directly, and specify that it is in a CALDoc context
recordTopLevelFunctionName(arg.getScope(), reference.getName(), false, !reference.isChecked());
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Name_DataCons(final Name.DataCons cons, final VisitorArgument<FinderState> arg) {
recordDataConsName(arg.getScope(), cons, false, false);
// instead of invoking the superclass implementation, which would delegate to visit_Name_Module
// the recording method has already done the recording of the module name
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_DataCons(final Expr.DataCons cons, final VisitorArgument<FinderState> arg) {
// instead of invoking the superclass implementation, which would delegate to visit_Name_DataCons
// we record the data cons name directly, and specify that it is in an expression context
recordDataConsName(arg.getScope(), cons.getDataConsName(), true, false);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_CALDoc_CrossReference_DataCons(final CALDoc.CrossReference.DataCons reference, final VisitorArgument<FinderState> arg) {
// instead of invoking the superclass implementation, which would delegate to visit_Name_DataCons
// we record the data cons name directly, and specify that it is in a CALDoc context
recordDataConsName(arg.getScope(), reference.getName(), false, !reference.isChecked());
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Name_TypeCons(final Name.TypeCons cons, final VisitorArgument<FinderState> arg) {
recordTypeConsName(arg.getScope(), cons, false);
// instead of invoking the superclass implementation, which would delegate to visit_Name_Module
// the recording method has already done the recording of the module name
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_CALDoc_CrossReference_TypeCons(final CALDoc.CrossReference.TypeCons reference, final VisitorArgument<FinderState> arg) {
// instead of invoking the superclass implementation, which would delegate to visit_Name_TypeCons
// we record the data cons name directly, and specify that it is in a CALDoc context
recordTypeConsName(arg.getScope(), reference.getName(), !reference.isChecked());
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Name_TypeClass(final Name.TypeClass typeClass, final VisitorArgument<FinderState> arg) {
recordTypeClassName(arg.getScope(), typeClass, false);
// instead of invoking the superclass implementation, which would delegate to visit_Name_Module
// the recording method has already done the recording of the module name
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_CALDoc_CrossReference_TypeClass(final CALDoc.CrossReference.TypeClass reference, final VisitorArgument<FinderState> arg) {
// instead of invoking the superclass implementation, which would delegate to visit_Name_TypeClass
// we record the data cons name directly, and specify that it is in a CALDoc context
recordTypeClassName(arg.getScope(), reference.getName(), !reference.isChecked());
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_CALDoc_CrossReference_WithoutContextCons(final CALDoc.CrossReference.WithoutContextCons reference, final VisitorArgument<FinderState> arg) {
recordConsNameWithoutContext(arg.getScope(), reference.getName(), !reference.isChecked());
// instead of invoking the superclass implementation, which would ultimately delegate to visit_Name_Module
// the recording method has already done the recording of the module name (and figured out whether the whole thing
// is a module name, or whether the reference is to a qualified name)
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_InstanceDefn(final InstanceDefn defn, final VisitorArgument<FinderState> arg) {
final FinderState newState = arg.getUserState().updateAssociatedTypeClassNameForInstanceMethod(defn.getTypeClassName());
return super.visit_InstanceDefn(defn, arg.updateUserState(newState));
}
/**
* {@inheritDoc}
*/
@Override
public R visit_InstanceDefn_InstanceMethod(final InstanceDefn.InstanceMethod method, final VisitorArgument<FinderState> arg) {
recordInstanceMethodName(arg.getScope(), method, arg.getUserState().getAssociatedTypeClassNameForInstanceMethod());
// invoke the superclass implementation to traverse the other bits of the declaration, especially
// the resolving function name
return super.visit_InstanceDefn_InstanceMethod(method, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_TypeConstructorDefn_AlgebraicType_DataConsDefn(final TypeConstructorDefn.AlgebraicType.DataConsDefn defn, final VisitorArgument<FinderState> arg) {
final Name.DataCons dataConsName = Name.DataCons.make(getCurrentModuleName(), defn.getDataConsName());
final FinderState newState = arg.getUserState().updateAssociatedDataConsNameForFieldName(dataConsName);
return super.visit_TypeConstructorDefn_AlgebraicType_DataConsDefn(defn, arg.updateUserState(newState));
}
/**
* {@inheritDoc}
*/
@Override
public R visit_TypeConstructorDefn_AlgebraicType_DataConsDefn_TypeArgument(final TypeConstructorDefn.AlgebraicType.DataConsDefn.TypeArgument argument, final VisitorArgument<FinderState> arg) {
// we explicitly do not traverse the field name, since it has already been captured as a binding by the superclass
// also, we moving into the type expression, where field names are *not* associated with the data constructor...
// any Name.Field encountered would be a record field name!
final FinderState newState = arg.getUserState().clearAssociatedDataConsNamesForFieldName();
argument.getTypeExprDefn().accept(this, arg.updateUserState(newState));
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Name_Field(final Name.Field name, final VisitorArgument<FinderState> arg) {
final List<Name.DataCons> associatedDataConsNames = arg.getUserState().getAssociatedDataConsNamesForFieldName();
if (associatedDataConsNames == null) {
// a record field name
recordNonBindingRecordFieldName(arg.getScope(), name, false);
} else {
// a data cons field name
recordNonBindingDataConsFieldName(arg.getScope(), name, associatedDataConsNames, false);
}
return super.visit_Name_Field(name, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_CALDoc_TaggedBlock_Arg(final CALDoc.TaggedBlock.Arg argBlock, final VisitorArgument<FinderState> arg) {
// record the argument name first
// (we do not want it traversed by visit_Name_Field, which cannot handle the case for when the arg is really a function parameter)
recordCALDocArgBlock(arg.getScope(), argBlock, arg.getUserState().getAssociatedDataConsNamesForFieldName());
// then traverse the rest of the block
return visit_CALDoc_TaggedBlock_TaggedBlockWithText_Helper(argBlock, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_Case_Alt_UnpackDataCons(final Expr.Case.Alt.UnpackDataCons cons, final VisitorArgument<FinderState> arg) {
final VisitorArgument<FinderState> newArg = updateScopeFor(cons, arg);
for (final Name.DataCons dataConsName : cons.getDataConsNames()) {
dataConsName.accept(this, newArg);
}
cons.getArgBindings().accept(
this, newArg.updateUserState(newArg.getUserState().updateAssociatedDataConsNamesForFieldName(cons.getDataConsNames())));
cons.getAltExpr().accept(this, newArg);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_FieldPattern(final FieldPattern fieldPattern, final VisitorArgument<FinderState> arg) {
// a punned textual pattern has already been recorded by the superclass as a binding, so we only deal
// if a non-punned textual pattern, or an ordinal field pattern
if (fieldPattern.getPattern() != null) {
// not a punned textual pattern, so record the field name
fieldPattern.getFieldName().accept(this, arg);
} else if (fieldPattern.getFieldName().getName() instanceof FieldName.Ordinal) {
// a punned ordinal pattern... we do not defer to visit_Name_Field
final List<Name.DataCons> associatedDataConsNames = arg.getUserState().getAssociatedDataConsNamesForFieldName();
if (associatedDataConsNames == null) {
// a record field name
recordNonBindingRecordFieldName(arg.getScope(), fieldPattern.getFieldName(), true);
} else {
// a data cons field name
recordNonBindingDataConsFieldName(arg.getScope(), fieldPattern.getFieldName(), associatedDataConsNames, true);
}
}
if (fieldPattern.getPattern() != null) {
fieldPattern.getPattern().accept(this, arg);
}
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_LocalDefn_PatternMatch_UnpackTuple(final LocalDefn.PatternMatch.UnpackTuple unpackTuple, final VisitorArgument<FinderState> arg) {
// we will skip the patterns as their bindings have been recorded by the superclass
unpackTuple.getDefiningExpr().accept(this, arg);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_LocalDefn_PatternMatch_UnpackDataCons(final LocalDefn.PatternMatch.UnpackDataCons unpackDataCons, final VisitorArgument<FinderState> arg) {
unpackDataCons.getDataConsName().accept(this, arg);
unpackDataCons.getArgBindings().accept(
this, arg.updateUserState(arg.getUserState().updateAssociatedDataConsNameForFieldName(unpackDataCons.getDataConsName())));
unpackDataCons.getDefiningExpr().accept(this, arg);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_SelectDataConsField(final Expr.SelectDataConsField field, final VisitorArgument<FinderState> arg) {
field.getDataConsValuedExpr().accept(this, arg);
field.getDataConsName().accept(this, arg);
field.getFieldName().accept(
this, arg.updateUserState(arg.getUserState().updateAssociatedDataConsNameForFieldName(field.getDataConsName())));
return null;
}
////
/// Visitor methods - elements which contain type variables
//
/**
* {@inheritDoc}
*/
@Override
public R visit_Name_TypeVar(final Name.TypeVar name, final VisitorArgument<FinderState> arg) {
recordTypeVarName(arg.getTypeVariableScope(), name);
return super.visit_Name_TypeVar(name, arg);
}
////
/// Visitor methods - elements which contain operators
//
/**
* {@inheritDoc}
*/
@Override
protected R visit_Expr_BinaryOp_Helper(final Expr.BinaryOp binop, final VisitorArgument<FinderState> arg) {
final QualifiedName operatorEntityName = OperatorInfo.getTextualName(binop.getOpText());
if (LanguageInfo.isValidDataConstructorName(operatorEntityName.getUnqualifiedName())) {
// a data cons, e.g. : (Prelude.Cons)
recordDataConsOperator(
arg.getScope(), operatorEntityName, binop, binop.getOperatorSourceRange(), true, false);
} else {
// must be a function name
recordTopLevelFunctionOrClassMethodOperator(
arg.getScope(), operatorEntityName, binop, binop.getOperatorSourceRange(), false);
}
return super.visit_Expr_BinaryOp_Helper(binop, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_Case_Alt_UnpackListCons(final Expr.Case.Alt.UnpackListCons cons, final VisitorArgument<FinderState> arg) {
recordDataConsOperator(arg.getScope(), CAL_Prelude.DataConstructors.Cons, cons, cons.getOperatorSourceRange(), false, false);
return super.visit_Expr_Case_Alt_UnpackListCons(cons, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_Case_Alt_UnpackListNil(final Expr.Case.Alt.UnpackListNil nil, final VisitorArgument<FinderState> arg) {
recordDataConsOperator(arg.getScope(), CAL_Prelude.DataConstructors.Nil, nil, nil.getSourceRange(), false, true);
return super.visit_Expr_Case_Alt_UnpackListNil(nil, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_Case_Alt_UnpackUnit(final Expr.Case.Alt.UnpackUnit unit, final VisitorArgument<FinderState> arg) {
recordDataConsOperator(arg.getScope(), CAL_Prelude.DataConstructors.Unit, unit, unit.getSourceRange(), false, true);
return super.visit_Expr_Case_Alt_UnpackUnit(unit, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_List(final Expr.List list, final VisitorArgument<FinderState> arg) {
// if the list is empty, then it is [], or Prelude.Nil
if (list.getNElements() == 0) {
recordDataConsOperator(arg.getScope(), CAL_Prelude.DataConstructors.Nil, list, list.getSourceRange(), true, true);
}
return super.visit_Expr_List(list, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_UnaryOp_Negate(final Expr.UnaryOp.Negate negate, final VisitorArgument<FinderState> arg) {
recordTopLevelFunctionOrClassMethodOperator(
arg.getScope(), CAL_Prelude.Functions.negate, negate, negate.getOperatorSourceRange(), false);
return super.visit_Expr_UnaryOp_Negate(negate, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_Expr_Unit(final Expr.Unit unit, final VisitorArgument<FinderState> arg) {
recordDataConsOperator(arg.getScope(), CAL_Prelude.DataConstructors.Unit, unit, unit.getSourceRange(), true, true);
return super.visit_Expr_Unit(unit, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_InstanceDefn_InstanceTypeCons_Function(final InstanceDefn.InstanceTypeCons.Function function, final VisitorArgument<FinderState> arg) {
recordTypeConsOperator(arg.getScope(), CAL_Prelude.TypeConstructors.Function, function, function.getOperatorSourceRange(), false);
return super.visit_InstanceDefn_InstanceTypeCons_Function(function, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_InstanceDefn_InstanceTypeCons_List(final InstanceDefn.InstanceTypeCons.List list, final VisitorArgument<FinderState> arg) {
recordTypeConsOperator(arg.getScope(), CAL_Prelude.TypeConstructors.List, list, list.getSourceRange(), true);
return super.visit_InstanceDefn_InstanceTypeCons_List(list, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_InstanceDefn_InstanceTypeCons_Unit(final InstanceDefn.InstanceTypeCons.Unit unit, final VisitorArgument<FinderState> arg) {
recordTypeConsOperator(arg.getScope(), CAL_Prelude.TypeConstructors.Unit, unit, unit.getSourceRange(), true);
return super.visit_InstanceDefn_InstanceTypeCons_Unit(unit, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_LocalDefn_PatternMatch_UnpackListCons(final LocalDefn.PatternMatch.UnpackListCons unpackListCons, final VisitorArgument<FinderState> arg) {
recordDataConsOperator(arg.getScope(), CAL_Prelude.DataConstructors.Cons, unpackListCons, unpackListCons.getOperatorSourceRange(), false, false);
// we will skip the head and tail patterns because their bindings have been recorded by the superclass
unpackListCons.getDefiningExpr().accept(this, arg);
return null;
}
/**
* {@inheritDoc}
*/
@Override
public R visit_TypeExprDefn_Function(final TypeExprDefn.Function function, final VisitorArgument<FinderState> arg) {
recordTypeConsOperator(arg.getScope(), CAL_Prelude.TypeConstructors.Function, function, function.getOperatorSourceRange(), false);
return super.visit_TypeExprDefn_Function(function, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_TypeExprDefn_List(final TypeExprDefn.List list, final VisitorArgument<FinderState> arg) {
recordTypeConsOperator(arg.getScope(), CAL_Prelude.TypeConstructors.List, list, list.getSourceRange(), true);
return super.visit_TypeExprDefn_List(list, arg);
}
/**
* {@inheritDoc}
*/
@Override
public R visit_TypeExprDefn_Unit(final TypeExprDefn.Unit unit, final VisitorArgument<FinderState> arg) {
recordTypeConsOperator(arg.getScope(), CAL_Prelude.TypeConstructors.Unit, unit, unit.getSourceRange(), true);
return super.visit_TypeExprDefn_Unit(unit, arg);
}
}