package com.siberika.idea.pascal.lang.references;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.psi.search.FileTypeIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.SmartList;
import com.intellij.util.StringLenComparator;
import com.intellij.util.indexing.FileBasedIndex;
import com.siberika.idea.pascal.DCUFileType;
import com.siberika.idea.pascal.PPUFileType;
import com.siberika.idea.pascal.PascalFileType;
import com.siberika.idea.pascal.PascalRTException;
import com.siberika.idea.pascal.lang.parser.NamespaceRec;
import com.siberika.idea.pascal.lang.parser.PascalParserUtil;
import com.siberika.idea.pascal.lang.psi.PasCallExpr;
import com.siberika.idea.pascal.lang.psi.PasClassProperty;
import com.siberika.idea.pascal.lang.psi.PasEntityScope;
import com.siberika.idea.pascal.lang.psi.PasEnumType;
import com.siberika.idea.pascal.lang.psi.PasExpression;
import com.siberika.idea.pascal.lang.psi.PasFullyQualifiedIdent;
import com.siberika.idea.pascal.lang.psi.PasHandler;
import com.siberika.idea.pascal.lang.psi.PasInvalidScopeException;
import com.siberika.idea.pascal.lang.psi.PasModule;
import com.siberika.idea.pascal.lang.psi.PasNamedIdent;
import com.siberika.idea.pascal.lang.psi.PasTypeDecl;
import com.siberika.idea.pascal.lang.psi.PasTypeID;
import com.siberika.idea.pascal.lang.psi.PasWithStatement;
import com.siberika.idea.pascal.lang.psi.PascalNamedElement;
import com.siberika.idea.pascal.lang.psi.PascalStructType;
import com.siberika.idea.pascal.lang.psi.impl.PasArrayTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasClassTypeTypeDeclImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasEnumTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasField;
import com.siberika.idea.pascal.lang.psi.impl.PasFileTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasPointerTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasProcedureTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasSetTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasStringTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasStructTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasSubRangeTypeImpl;
import com.siberika.idea.pascal.lang.psi.impl.PasTypeIDImpl;
import com.siberika.idea.pascal.lang.psi.impl.PascalExpression;
import com.siberika.idea.pascal.lang.psi.impl.PascalModuleImpl;
import com.siberika.idea.pascal.lang.psi.impl.PascalRoutineImpl;
import com.siberika.idea.pascal.sdk.BuiltinsParser;
import com.siberika.idea.pascal.util.PsiUtil;
import com.siberika.idea.pascal.util.SyncUtil;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Author: George Bakhtadze
* Date: 25/04/2013
*/
public class PasReferenceUtil {
private static final Logger LOG = Logger.getInstance(PasReferenceUtil.class.getName());
private static final int MAX_RECURSION_COUNT = 1000;
private static final int MAX_NAMESPACES = 300;
/**
* Finds and returns unit in path by name
* @param moduleName - unit name
* @return unit element
*/
@Nullable
public static PasEntityScope findUnit(@NotNull Project project, @NotNull List<VirtualFile> unitFiles, @NotNull final String moduleName) {
VirtualFile file = findUnitFile(unitFiles, moduleName);
if (file != null) {
PsiFile pascalFile = PsiManager.getInstance(project).findFile(file);
PasModule pasModule = PsiTreeUtil.findChildOfType(pascalFile, PasModule.class);
if (pasModule != null) {
return pasModule;
} else {
LOG.info(String.format("ERROR: No module found in file %s", file.getName()));
}
} else {
LOG.info(String.format("ERROR: No file found for unit %s", moduleName));
}
return null;
}
/**
* Finds and returns file of a module with the given name
* If more than one file matches the one with longest name is returned
* @return list of PsiFiles
*/
@Nullable
public static VirtualFile findUnitFile(@NotNull List<VirtualFile> unitFiles, @NotNull final String moduleName) {
List<VirtualFile> candidates = new ArrayList<VirtualFile>();
for (VirtualFile virtualFile : unitFiles) {
if (isFileOfModuleWithName(virtualFile, moduleName)) {
candidates.add(virtualFile);
}
}
Collections.sort(candidates, new Comparator<VirtualFile>() {
@Override
public int compare(VirtualFile o1, VirtualFile o2) {
return o2.getNameWithoutExtension().length() - o1.getNameWithoutExtension().length();
}
});
return !candidates.isEmpty() ? candidates.get(0) : null;
}
/**
* Finds and returns module files in search path
* @param module - IDEA module to include its compiled dependencies
* @return list of PsiFiles
*/
@NotNull
public static List<VirtualFile> findUnitFiles(@NotNull Project project, @Nullable final Module module) {
final List<VirtualFile> virtualFiles = new SmartList<VirtualFile>();
if (module != null) {
virtualFiles.addAll(FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, PascalFileType.INSTANCE,
GlobalSearchScope.allScope(project)));
virtualFiles.addAll(FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, PPUFileType.INSTANCE,
GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module)));
virtualFiles.addAll(FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, DCUFileType.INSTANCE,
GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module)));
} else {
virtualFiles.addAll(FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, PascalFileType.INSTANCE, ProjectScope.getLibrariesScope(project)));
virtualFiles.addAll(FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, PPUFileType.INSTANCE, ProjectScope.getLibrariesScope(project)));
virtualFiles.addAll(FileBasedIndex.getInstance().getContainingFiles(FileTypeIndex.NAME, DCUFileType.INSTANCE, ProjectScope.getLibrariesScope(project)));
}
virtualFiles.add(BuiltinsParser.getBuiltinsSource());
return virtualFiles;
}
// Returns True if the file is the file of a module with the given name
public static boolean isFileOfModuleWithName(VirtualFile virtualFile, @NotNull String name) {
return isUnitExtension(virtualFile) && isFileUnitName(virtualFile.getNameWithoutExtension(), name);
}
private static boolean isFileUnitName(@NotNull String fileNameWoExt, @NotNull String name) {
return fileNameWoExt.equalsIgnoreCase(name) || ((name.length() > 8) && (name.substring(0, 8).equalsIgnoreCase(fileNameWoExt)));
}
private static boolean isUnitExtension(VirtualFile virtualFile) {
String ext = virtualFile.getExtension();
return (ext != null) && PascalFileType.UNIT_EXTENSIONS.contains(ext.toLowerCase())
|| PPUFileType.INSTANCE.getDefaultExtension().equalsIgnoreCase(ext)
|| DCUFileType.INSTANCE.getDefaultExtension().equalsIgnoreCase(ext);
}
private static boolean isVisibleWithinUnit(@NotNull PasField field, @NotNull NamespaceRec fqn) {
if ((field.getElement() != null) && (field.getElement().getContainingFile() == fqn.getParentIdent().getContainingFile())) {
// check if declaration comes earlier then usage or declaration allows forward mode
int offs;
offs = fqn.getParentIdent().getTextRange().getStartOffset();
return (field.offset <= offs)
|| PsiUtil.allowsForwardReference(fqn.getParentIdent());
} else {
// check if field visibility allows usage from another unit
return fqn.isIgnoreVisibility() || (field.fieldType == PasField.FieldType.ROUTINE) || PasField.isAllowed(field.visibility, PasField.Visibility.STRICT_PROTECTED);
}
}
//-------------------------------------------------------------------
private static PasField.ValueType resolveFieldType(PasField field, boolean includeLibrary, int recursionCount) {
final PascalNamedElement element = field.getElement();
if (recursionCount > PascalParserUtil.MAX_STRUCT_TYPE_RESOLVE_RECURSION) {
throw new PascalRTException("Too much recursion during resolving type for: " + element);
}
PasTypeID typeId = null;
PasField.ValueType res = null;
if (element instanceof PasClassProperty) {
typeId = resolvePropertyType(field, (PasClassProperty) element);
} else if (element instanceof PascalRoutineImpl) { // routine declaration case
typeId = ((PascalRoutineImpl) element).getFunctionTypeIdent();
} else if ((element != null) && (element.getParent() instanceof PasHandler)) { // exception handler case
typeId = ((PasHandler) element.getParent()).getTypeID();
} else {
if ((element != null) && PsiUtil.isTypeDeclPointingToSelf(element)) {
res = PasField.getValueType(element.getName());
} else {
res = retrieveAnonymousType(PsiUtil.getTypeDeclaration(element), includeLibrary, recursionCount);
}
}
if (typeId != null) {
res = resolveTypeId(typeId, includeLibrary, recursionCount);
}
if (res != null) {
res.field = field;
}
return res;
}
public static PasTypeID resolvePropertyType(PasField field, PasClassProperty element) {
PasTypeID typeId = element.getTypeID();
if ((null == typeId) && (field.owner instanceof PascalStructType)) {
for (SmartPsiElementPointer<PasEntityScope> parentPtr : field.owner.getParentScope()) {
PasEntityScope parent = parentPtr.getElement();
PasField propField = parent != null ? parent.getField(field.name) : null;
if (propField != null && propField.fieldType == PasField.FieldType.PROPERTY) {
PascalNamedElement propEl = propField.getElement();
if (propEl instanceof PasClassProperty) {
typeId = resolvePropertyType(propField, (PasClassProperty) propEl);
if (typeId != null) {
break;
}
}
}
}
}
return typeId;
}
@Nullable
private static PasField.ValueType resolveTypeId(@NotNull PasTypeID typeId, boolean includeLibrary, int recursionCount) {
Collection<PasField> types = resolve(null, NamespaceRec.fromElement(typeId.getFullyQualifiedIdent()), PasField.TYPES_TYPE, includeLibrary, ++recursionCount);
if (!types.isEmpty()) {
return resolveFieldType(types.iterator().next(), includeLibrary, ++recursionCount); // resolve next type in chain
}
return null;
}
private static PasField.ValueType retrieveAnonymousType(PasTypeDecl decl, boolean includeLibrary, int recursionCount) {
PasField.Kind kind = null;
PasField.ValueType baseType = null;
if (decl != null) {
PsiElement type = decl.getFirstChild();
if (type.getClass() == PasTypeIDImpl.class) {
return resolveTypeId((PasTypeID) type, includeLibrary, recursionCount);
} else if (type.getClass() == PasClassTypeTypeDeclImpl.class) {
kind = PasField.Kind.CLASSREF;
baseType = resolveTypeId(((PasClassTypeTypeDeclImpl) type).getTypeID(), includeLibrary, recursionCount);
} else if (type instanceof PasStructTypeImpl) {
kind = PasField.Kind.STRUCT;
} else if (type.getClass() == PasArrayTypeImpl.class) {
kind = PasField.Kind.ARRAY;
baseType = retrieveAnonymousType(((PasArrayTypeImpl) type).getTypeDecl(), includeLibrary, ++recursionCount);
} else if (type.getClass() == PasSetTypeImpl.class) {
kind = PasField.Kind.SET;
baseType = retrieveAnonymousType(((PasSetTypeImpl) type).getTypeDecl(), includeLibrary, ++recursionCount);
} else if (type.getClass() == PasFileTypeImpl.class) {
kind = PasField.Kind.FILE;
} else if (type.getClass() == PasPointerTypeImpl.class) {
kind = PasField.Kind.POINTER;
baseType = retrieveAnonymousType(((PasPointerTypeImpl) type).getTypeDecl(), includeLibrary, ++recursionCount);
} else if (type.getClass() == PasProcedureTypeImpl.class) {
kind = PasField.Kind.PROCEDURE;
} else if (type.getClass() == PasStringTypeImpl.class) {
kind = PasField.Kind.STRING;
} else if (type.getClass() == PasEnumTypeImpl.class) {
kind = PasField.Kind.ENUM;
} else if (type.getClass() == PasSubRangeTypeImpl.class) {
kind = PasField.Kind.SUBRANGE;
}
}
return new PasField.ValueType(null, kind, baseType, decl);
}
@Nullable
private static PasEntityScope retrieveFieldUnitScope(PasField field, boolean includeLibrary) {
return (field.getElement() != null) && (includeLibrary || !PsiUtil.isFromLibrary(field.getElement())) ? (PasEntityScope) field.getElement() : null;
}
@Nullable
private static PasEntityScope retrieveFieldTypeScope(@NotNull PasField field, int recursionCount) {
if (SyncUtil.tryLockQuiet(field.getTypeLock(), SyncUtil.LOCK_TIMEOUT_MS)) {
try {
if (!field.isTypeResolved()) {
field.setValueType(resolveFieldType(field, true, recursionCount));
}
return field.getValueType() != null ? field.getValueType().getTypeScope() : null;
} finally {
field.getTypeLock().unlock();
}
} else {
return null;
}
}
@Nullable
public static PasEntityScope retrieveFieldTypeScope(@NotNull PasField field) {
return retrieveFieldTypeScope(field, 0);
}
@Nullable
public static PasEntityScope resolveTypeScope(NamespaceRec fqn, boolean includeLibrary) {
Collection<PasField> types = resolve(null, fqn, PasField.TYPES_TYPE, includeLibrary, 0);
for (PasField field : types) {
PasEntityScope struct = field.getElement() != null ? PascalParserUtil.getStructTypeByIdent(field.getElement(), 0) : null;
if (struct != null) {
return struct;
}
}
return null;
}
public static Collection<PasField> resolveExpr(final List<PsiElement> resultScope, final NamespaceRec fqn, Set<PasField.FieldType> fieldTypesOrig, boolean includeLibrary, int recursionCount) {
PsiElement expr = fqn.getParentIdent().getParent();
expr = expr != null ? expr.getFirstChild() : null;
if (expr instanceof PascalExpression) {
List<PasField.ValueType> types = PascalExpression.getTypes((PascalExpression) expr);
if (!types.isEmpty()) {
fqn.setNested(true);
Set<PasField.FieldType> fieldTypes = new HashSet<PasField.FieldType>(fieldTypesOrig);
fieldTypes.remove(PasField.FieldType.PSEUDO_VARIABLE);
return resolve(resultScope, PascalExpression.retrieveScope(types), fqn, fieldTypes, includeLibrary, recursionCount);
}
}
return resolve(resultScope, fqn, fieldTypesOrig, includeLibrary, recursionCount);
}
public static Collection<PasField> resolve(final List<PsiElement> resultScope, final NamespaceRec fqn, Set<PasField.FieldType> fieldTypesOrig, boolean includeLibrary, int recursionCount) {
return resolve(resultScope, PsiUtil.getNearestAffectingScope(fqn.getParentIdent()), fqn, fieldTypesOrig, includeLibrary, recursionCount);
}
/**
* for each entry in FQN before target:
* find entity corresponding to NS in current scope
* if the entity represents a namespace - retrieve and make current
* for namespace of target entry add all its entities
*/
public static Collection<PasField> resolve(final List<PsiElement> resultScope, PasEntityScope scope, final NamespaceRec fqn, final Set<PasField.FieldType> fieldTypesOrig, final boolean includeLibrary, final int recursionCount) {
ProgressManager.checkCanceled();
if (recursionCount > MAX_RECURSION_COUNT) {
throw new PascalRTException("Too much recursion during resolving identifier: " + fqn.getParentIdent());
}
boolean implAffects = fqn.isFirst()
&& PsiUtil.isBefore(PsiUtil.getModuleImplementationSection(fqn.getParentIdent().getContainingFile()), fqn.getParentIdent());
if (null == scope) {
scope = PsiUtil.getNearestAffectingScope(fqn.getParentIdent());
}
// First entry in FQN
List<PasEntityScope> namespaces = new SmartList<PasEntityScope>();
Collection<PasField> result = new HashSet<PasField>();
Set<PasField.FieldType> fieldTypes = EnumSet.copyOf(fieldTypesOrig);
try {
handleWith(namespaces, scope, fqn.getParentIdent());
// Retrieve all namespaces affecting first FQN level
while (scope != null) {
addFirstNamespaces(namespaces, scope, includeLibrary, implAffects);
scope = fqn.isFirst() ? PsiUtil.getNearestAffectingScope(scope) : null;
}
List<PasEntityScope> newNs = checkUnitScope(result, namespaces, fqn);
if (newNs != null) {
namespaces = newNs;
fieldTypes.remove(PasField.FieldType.UNIT); // Unit qualifier can be only first
}
while (fqn.isBeforeTarget() && (namespaces != null)) {
PasField field = null;
// Scan namespaces and get one matching field
for (PasEntityScope namespace : namespaces) {
field = namespace.getField(fqn.getCurrentName());
if (field != null) {
break;
}
}
namespaces = null;
if (field != null) {
PasEntityScope newNS;
if (field.fieldType == PasField.FieldType.UNIT) {
newNS = fqn.isFirst() && implAffects && PasField.isAllowed(field.visibility, PasField.Visibility.PRIVATE) ?
retrieveFieldUnitScope(field, includeLibrary) : null; // First qualifier can be unit name
} else {
newNS = retrieveFieldTypeScope(field, recursionCount);
boolean isDefault = "DEFAULT".equals(fqn.getLastName().toUpperCase());
if ((fqn.getRestLevels() == 1) && ((null == newNs) || isDefault) // "default" type pseudo value
&& (field.fieldType == PasField.FieldType.TYPE)) { // Enumerated type member
if (isDefault) {
fqn.next();
PasField defaultField = new PasField(field.owner, field.getElement(), "default", PasField.FieldType.CONSTANT, field.visibility);
if (isFieldMatches(defaultField, fqn, fieldTypes)) {
result.add(defaultField);
}
saveScope(resultScope, newNS, true);
return result;
}
if (field.getValueType() != null) {
SmartPsiElementPointer<PasTypeDecl> typePtr = field.getValueType().declaration;
PasTypeDecl enumType = typePtr != null ? typePtr.getElement() : null;
PasEnumType enumDecl = enumType != null ? PsiTreeUtil.findChildOfType(enumType, PasEnumType.class) : null;
if (enumDecl != null) {
fqn.next();
saveScope(resultScope, enumDecl, true);
return collectEnumFields(result, field, enumDecl, fqn, fieldTypes);
}
}
}
}
namespaces = newNS != null ? new SmartList<PasEntityScope>(newNS) : null;
addParentNamespaces(namespaces, newNS, false);
}
fqn.next();
fieldTypes.remove(PasField.FieldType.UNIT); // Unit qualifier can be only first in FQN
fieldTypes.remove(PasField.FieldType.PSEUDO_VARIABLE); // Pseudo variables can be only first in FQN
}
if (!fqn.isComplete() && (namespaces != null)) {
for (PasEntityScope namespace : namespaces) {
if (null == namespace) {
LOG.info(String.format("===*** null namespace! %s", fqn));
continue;
}
for (PasField pasField : namespace.getAllFields()) {
if ((pasField.getElementPtr() != null) && isFieldMatches(pasField, fqn, fieldTypes) &&
!result.contains(pasField) &&
isVisibleWithinUnit(pasField, fqn)) {
saveScope(resultScope, namespace, false);
result.add(pasField);
if (!isCollectingAll(fqn)) {
break;
}
}
}
if (!result.isEmpty() && !isCollectingAll(fqn)) {
break;
}
}
if (result.isEmpty() && (resultScope != null)) {
resultScope.clear();
resultScope.addAll(namespaces);
}
}
} catch (PasInvalidScopeException e) {
/*if (namespaces != null) {
for (PasEntityScope namespace : namespaces) {
namespace.invalidateCache();
}
}*/
/*} catch (Throwable e) {
//LOG.error(String.format("Error parsing scope %s, file %s", scope, scope != null ? scope.getContainingFile().getName() : ""), e);
throw e;*/
}
return result;
}
private static void saveScope(List<PsiElement> resultScope, PsiElement namespace, boolean clear) {
if (resultScope != null) {
if (clear) {
resultScope.clear();
}
resultScope.add(namespace);
}
}
// Advances fqn
private static Collection<PasField> collectEnumFields(Collection<PasField> result, PasField field, PasEnumType enumDecl, NamespaceRec fqn, Set<PasField.FieldType> fieldTypes) {
for (PasNamedIdent ident : enumDecl.getNamedIdentList()) {
PasField enumField = new PasField(field.owner, ident, ident.getName(), PasField.FieldType.CONSTANT, field.visibility);
if (isFieldMatches(enumField, fqn, fieldTypes)) {
result.add(enumField);
}
}
return result;
}
private static List<PasEntityScope> checkUnitScope(Collection<PasField> result, List<PasEntityScope> namespaces, NamespaceRec fqn) {
ArrayList<PasEntityScope> sorted = new ArrayList<PasEntityScope>(namespaces.size());
for (PasEntityScope namespace : namespaces) {
if (namespace instanceof PasModule) {
if (!StringUtils.isEmpty(namespace.getName())) {
sorted.add(namespace);
}
}
}
// sort namespaces by name length in reverse order to check longer named namespaces first
Collections.sort(sorted, new Comparator<PasEntityScope>() {
@Override
public int compare(PasEntityScope o1, PasEntityScope o2) {
return StringLenComparator.getInstance().compare(o2.getName(), o1.getName());
}
});
for (PasEntityScope namespace : sorted) {
if (fqn.advance(namespace.getName())) {
if (fqn.isComplete()) {
result.add(namespace.getField(namespace.getName()));
}
return new SmartList<PasEntityScope>(namespace);
}
}
return null;
}
private static void handleWith(List<PasEntityScope> namespaces, PasEntityScope scope, PsiElement ident) {
if (null == scope) {
return;
}
Collection<PasWithStatement> statements = PsiTreeUtil.findChildrenOfType(scope, PasWithStatement.class);
for (PasWithStatement ws : statements) {
if (PsiUtil.isParentOf(ident, ws.getStatement()) && PsiUtil.isParentOf(ws, scope)) {
for (PasExpression expr : ws.getExpressionList()) {
if ((expr != null) && (expr.getExpr() instanceof PascalExpression)) {
List<PasField.ValueType> types = PascalExpression.getTypes((PascalExpression) expr.getExpr());
if (!types.isEmpty()) {
PasEntityScope ns = PascalExpression.retrieveScope(types);
if (ns != null) {
namespaces.add(ns);
if (ns instanceof PascalStructType) {
for (SmartPsiElementPointer<PasEntityScope> scopePtr : ns.getParentScope()) {
namespaces.add(scopePtr.getElement());
}
}
}
}
}
}
}
}
}
private static boolean isCollectingAll(NamespaceRec fqn) {
return "".equals(fqn.getCurrentName());
}
private static boolean isFieldMatches(PasField field, NamespaceRec fqn, Set<PasField.FieldType> fieldTypes) {
return (!fqn.isTarget() || fieldTypes.contains(field.fieldType)) &&
(isCollectingAll(fqn) || field.name.equalsIgnoreCase(fqn.getCurrentName()));
}
/*
#. WITH blocks EntityScope (dep)
. local declaration blocks (need pos check) RoutineImpl (strict)
. parameters section RoutineImpl (private)
. implementation block (need pos check) ModuleImpl (private)
. interface block (need pos check) ModuleImpl (public)
. interface of units used in implementation (need pos check) ModuleImpl (public)
. interface of units used in interface ModuleImpl (public)
. builtins
. SELF - in method context
. RESULT - in routine context
*/
private static void addFirstNamespaces(List<PasEntityScope> namespaces, PasEntityScope section, boolean includeLibrary, boolean implAffects) {
namespaces.add(section);
if (section instanceof PascalModuleImpl) {
if (implAffects) {
addUnitNamespaces(namespaces, ((PascalModuleImpl) section).getPrivateUnits(), includeLibrary);
}
addUnitNamespaces(namespaces, ((PascalModuleImpl) section).getPublicUnits(), includeLibrary);
}
addParentNamespaces(namespaces, section, true);
}
private static void addUnitNamespaces(List<PasEntityScope> namespaces, List<SmartPsiElementPointer<PasEntityScope>> units, boolean includeLibrary) {
for (SmartPsiElementPointer<PasEntityScope> unitPtr : units) {
PasEntityScope unit = unitPtr.getElement();
if ((unit != null) && (includeLibrary || !PsiUtil.isFromLibrary(unit))) {
namespaces.add(unit);
}
}
}
private static void addParentNamespaces(@Nullable List<PasEntityScope> namespaces, @Nullable PasEntityScope section, boolean first) {
if ((null == namespaces) || (namespaces.size() > MAX_NAMESPACES)) {
return;
}
if (null == section) {
return;
}
for (SmartPsiElementPointer<PasEntityScope> scopePtr : section.getParentScope()) {
PasEntityScope scope = scopePtr.getElement();
if (first || (scope instanceof PascalStructType)) { // Search for parents for first namespace (method) or any for structured types
if (null != scope) {
namespaces.add(scope);
addParentNamespaces(namespaces, scope, first);
}
}
}
}
@NotNull
public static Collection<PasField> resolveRoutines(PasCallExpr callExpr) {
PasFullyQualifiedIdent ident = callExpr != null ? PsiTreeUtil.findChildOfType(callExpr.getExpr(), PasFullyQualifiedIdent.class) : null;
if (null == ident) {
return Collections.emptyList();
}
Collection<PasField> routines = resolveExpr(null, NamespaceRec.fromElement(ident), PasField.TYPES_ROUTINE, true, 0);
return !routines.isEmpty() ? routines : Collections.<PasField>emptyList();
}
}