au.com.integradev.delphi.symbol.scope.DelphiScopeImpl Maven / Gradle / Ivy
The newest version!
/*
* Sonar Delphi Plugin
* Copyright (C) 2019 Integrated Application Development
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package au.com.integradev.delphi.symbol.scope;
import au.com.integradev.delphi.symbol.declaration.TypeNameDeclarationImpl;
import au.com.integradev.delphi.type.factory.StructTypeImpl;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Ordering;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.TreeMultimap;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.sonar.plugins.communitydelphi.api.symbol.Invocable;
import org.sonar.plugins.communitydelphi.api.symbol.NameOccurrence;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.GenerifiableDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.LabelNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.NameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.PropertyNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineDirective;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.RoutineNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.TypeNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.UnitImportNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.UnitNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.declaration.VariableNameDeclaration;
import org.sonar.plugins.communitydelphi.api.symbol.scope.DelphiScope;
import org.sonar.plugins.communitydelphi.api.symbol.scope.TypeScope;
import org.sonar.plugins.communitydelphi.api.type.Type;
import org.sonar.plugins.communitydelphi.api.type.Type.HelperType;
import org.sonar.plugins.communitydelphi.api.type.Type.StructType;
public class DelphiScopeImpl implements DelphiScope {
private final Set declarationSet;
private final ListMultimap occurrencesByDeclaration;
private final SetMultimap declarationsByName;
private final Set unitDeclarations;
private final Set importDeclarations;
private final Set typeDeclarations;
private final Set propertyDeclarations;
private final Set routineDeclarations;
private final Set variableDeclarations;
private final Set labelDeclarations;
private final Map helpersByType;
private DelphiScope parent;
protected DelphiScopeImpl() {
declarationSet = new HashSet<>();
occurrencesByDeclaration = ArrayListMultimap.create();
declarationsByName = TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, Ordering.natural());
unitDeclarations = new HashSet<>();
importDeclarations = new HashSet<>();
typeDeclarations = new HashSet<>();
propertyDeclarations = new HashSet<>();
routineDeclarations = new HashSet<>();
variableDeclarations = new HashSet<>();
labelDeclarations = new HashSet<>();
helpersByType = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
}
public Set addNameOccurrence(@Nonnull NameOccurrence occurrence) {
NameDeclaration declaration = getDeclaration(occurrence.getNameDeclaration());
occurrencesByDeclaration.put(declaration, occurrence);
return Set.of(declaration);
}
public void addDeclaration(NameDeclaration declaration) {
checkForwardTypeDeclarations(declaration);
checkForDuplicatedNameDeclaration(declaration);
declarationSet.add(declaration);
declarationsByName.put(declaration.getImage(), declaration);
addDeclarationByClass(declaration);
handleHelperDeclaration(declaration);
}
private void addDeclarationByClass(NameDeclaration declaration) {
if (declaration instanceof VariableNameDeclaration) {
variableDeclarations.add((VariableNameDeclaration) declaration);
} else if (declaration instanceof RoutineNameDeclaration) {
routineDeclarations.add((RoutineNameDeclaration) declaration);
} else if (declaration instanceof PropertyNameDeclaration) {
propertyDeclarations.add((PropertyNameDeclaration) declaration);
} else if (declaration instanceof UnitImportNameDeclaration) {
importDeclarations.add((UnitImportNameDeclaration) declaration);
} else if (declaration instanceof TypeNameDeclaration) {
typeDeclarations.add((TypeNameDeclaration) declaration);
} else if (declaration instanceof UnitNameDeclaration) {
unitDeclarations.add((UnitNameDeclaration) declaration);
} else if (declaration instanceof LabelNameDeclaration) {
labelDeclarations.add((LabelNameDeclaration) declaration);
}
}
private void checkForwardTypeDeclarations(NameDeclaration typeDeclaration) {
if (!(typeDeclaration instanceof TypeNameDeclaration)) {
return;
}
var fullTypeDeclaration = (TypeNameDeclarationImpl) typeDeclaration;
Type fullType = fullTypeDeclaration.getType();
if (!fullType.isStruct()) {
return;
}
declarationsByName
.get(typeDeclaration.getName())
.removeIf(
declaration -> {
if (declaration instanceof TypeNameDeclaration) {
var forwardDeclaration = (TypeNameDeclarationImpl) declaration;
// A generic type's forward declaration must also be generic, or vice versa
if (forwardDeclaration.isGeneric() != fullTypeDeclaration.isGeneric()) {
return false;
}
// A generic type's forward declaration must have matching type parameters
if (forwardDeclaration.isGeneric()
&& forwardDeclaration.getTypeParameters().size()
!= fullTypeDeclaration.getTypeParameters().size()) {
return false;
}
Type forwardType = forwardDeclaration.getType();
if (forwardType.isStruct()) {
((StructTypeImpl) forwardType).setFullType((StructType) fullType);
fullTypeDeclaration.setForwardDeclaration(forwardDeclaration);
forwardDeclaration.setIsForwardDeclaration();
return true;
}
}
return false;
});
}
private void checkForDuplicatedNameDeclaration(NameDeclaration declaration) {
if (!declarationsByName.containsKey(declaration.getName())) {
return;
}
if (isAcceptableDuplicate(declaration)) {
return;
}
throw new DuplicatedDeclarationException(
declaration.getImage() + " is already in the symbol table");
}
private boolean isAcceptableDuplicate(NameDeclaration declaration) {
if (declaration instanceof Invocable) {
return true;
}
Set duplicates = declarationsByName.get(declaration.getImage());
// Unit imports can clash with other declarations, except other imports
if (declaration instanceof UnitImportNameDeclaration) {
return duplicates.stream().noneMatch(UnitImportNameDeclaration.class::isInstance);
} else {
// Disregard unit imports when checking for duplicates
duplicates.removeIf(UnitImportNameDeclaration.class::isInstance);
}
if (declaration instanceof GenerifiableDeclaration) {
GenerifiableDeclaration generic = (GenerifiableDeclaration) declaration;
if (generic.isGeneric()) {
int typeParamSize = generic.getTypeParameters().size();
return duplicates.stream()
.filter(GenerifiableDeclaration.class::isInstance)
.map(GenerifiableDeclaration.class::cast)
.allMatch(duplicate -> duplicate.getTypeParameters().size() != typeParamSize);
}
}
return duplicates.stream()
.allMatch(
duplicate ->
duplicate instanceof GenerifiableDeclaration
&& ((GenerifiableDeclaration) duplicate).isGeneric());
}
private void handleHelperDeclaration(NameDeclaration declaration) {
if (declaration instanceof TypeNameDeclaration) {
TypeNameDeclaration typeDeclaration = (TypeNameDeclaration) declaration;
Type type = typeDeclaration.getType();
if (type.isHelper()) {
HelperType helper = (HelperType) type;
helpersByType.put(helper.extendedType().getImage(), helper);
}
}
}
@Override
public List getOccurrencesFor(NameDeclaration declaration) {
return occurrencesByDeclaration.get(getDeclaration(declaration));
}
@Override
public Set getAllDeclarations() {
return declarationSet;
}
private static void handleGenerics(NameOccurrence occurrence, Set result) {
int typeArgumentCount = occurrence.getTypeArguments().size();
result.removeIf(
declaration -> {
if (typeArgumentCount == 0 && declaration instanceof RoutineNameDeclaration) {
// Could be an implicit specialization
return false;
}
if (declaration instanceof GenerifiableDeclaration) {
GenerifiableDeclaration generifiable = (GenerifiableDeclaration) declaration;
return generifiable.getTypeParameters().size() != typeArgumentCount;
}
return occurrence.isGeneric();
});
}
/**
* If the result set is populated with only routine declarations that are marked as overloads,
* then additional overloads will be searched for and populated into the result set.
*
* @param occurrence The name occurrence that we're accumulating declarations for
* @param result The set of declarations that overloads will be added to, if applicable
*/
public void findRoutineOverloads(NameOccurrence occurrence, Set result) {
if (result.isEmpty() || !result.stream().allMatch(DelphiScopeImpl::canBeOverloaded)) {
return;
}
for (RoutineNameDeclaration declaration : this.getRoutineDeclarations()) {
if (isRoutineOverload(declaration, occurrence, result, overloadsRequireOverloadDirective())) {
result.add(declaration);
}
}
DelphiScope searchScope = overloadSearchScope();
if (searchScope != null) {
((DelphiScopeImpl) searchScope).findRoutineOverloads(occurrence, result);
}
}
@Nullable
protected DelphiScope overloadSearchScope() {
return parent;
}
private static boolean canBeOverloaded(NameDeclaration declaration) {
if (declaration instanceof RoutineNameDeclaration) {
RoutineNameDeclaration routineDeclaration = (RoutineNameDeclaration) declaration;
return !routineDeclaration.isCallable()
|| routineDeclaration.hasDirective(RoutineDirective.OVERLOAD)
|| isOverrideForOverloadedMethod(routineDeclaration);
}
return false;
}
private static boolean isOverrideForOverloadedMethod(RoutineNameDeclaration method) {
if (method.hasDirective(RoutineDirective.OVERRIDE)) {
DelphiScope scope = method.getScope().getEnclosingScope(TypeScope.class).getParentTypeScope();
while (scope instanceof TypeScope) {
RoutineNameDeclaration overridden =
scope.getRoutineDeclarations().stream()
.filter(ancestor -> ancestor.getImage().equalsIgnoreCase(method.getName()))
.filter(ancestor -> overridesMethodSignature(ancestor, method))
.findFirst()
.orElse(null);
if (overridden != null) {
return overridden.hasDirective(RoutineDirective.VIRTUAL)
|| overridden.hasDirective(RoutineDirective.DYNAMIC);
}
scope = ((TypeScope) scope).getParentTypeScope();
}
}
return false;
}
private static boolean isRoutineOverload(
RoutineNameDeclaration declaration,
NameOccurrence occurrence,
Set matchedRoutines,
boolean requireOverloadDirective) {
return (!requireOverloadDirective || declaration.hasDirective(RoutineDirective.OVERLOAD))
&& declaration.getImage().equalsIgnoreCase(occurrence.getImage())
&& matchedRoutines.stream()
.map(RoutineNameDeclaration.class::cast)
.noneMatch(matched -> overridesMethodSignature(matched, declaration));
}
protected boolean overloadsRequireOverloadDirective() {
return false;
}
private static boolean overridesMethodSignature(
RoutineNameDeclaration declaration, RoutineNameDeclaration overridden) {
return declaration.isCallable()
&& overridden.isCallable()
&& declaration.hasSameParameterTypes(overridden);
}
@Override
public Set findDeclaration(NameOccurrence occurrence) {
Set result = Collections.emptySet();
Set found = declarationsByName.get(occurrence.getImage());
if (occurrence.isAttributeReference()) {
found = new HashSet<>(found);
found.addAll(declarationsByName.get(occurrence.getImage() + "Attribute"));
}
if (!found.isEmpty()) {
result = new HashSet<>(found);
findRoutineOverloads(occurrence, result);
handleGenerics(occurrence, result);
}
return result;
}
@Nullable
@Override
public DelphiScope getParent() {
return parent;
}
public void setParent(DelphiScope parent) {
this.parent = parent;
}
@Override
public T getEnclosingScope(Class clazz) {
for (DelphiScope current = this; current != null; current = current.getParent()) {
if (clazz.isAssignableFrom(current.getClass())) {
return clazz.cast(current);
}
}
return null;
}
@Nullable
@Override
public HelperType getHelperForType(Type type) {
HelperType result = findHelper(type);
if (result == null && parent != null) {
result = parent.getHelperForType(type);
}
return result;
}
protected HelperType findHelper(Type type) {
if (helpersByType.isEmpty()) {
return null;
}
return helpersByType.get(type.getImage());
}
private static NameDeclaration getDeclaration(NameDeclaration declaration) {
NameDeclaration result = declaration;
while (result.isSpecializedDeclaration()) {
result = result.getGenericDeclaration();
}
return result;
}
@Override
public Set getUnitDeclarations() {
return Collections.unmodifiableSet(unitDeclarations);
}
@Override
public Set getImportDeclarations() {
return Collections.unmodifiableSet(importDeclarations);
}
@Override
public Set getTypeDeclarations() {
return Collections.unmodifiableSet(typeDeclarations);
}
@Override
public Set getPropertyDeclarations() {
return Collections.unmodifiableSet(propertyDeclarations);
}
@Override
public Set getRoutineDeclarations() {
return Collections.unmodifiableSet(routineDeclarations);
}
@Override
public Set getVariableDeclarations() {
return Collections.unmodifiableSet(variableDeclarations);
}
@Override
public Set getLabelDeclarations() {
return Collections.unmodifiableSet(labelDeclarations);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy