Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add typealias support #15

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ dependencies {
implementation group: 'systems.manifold', name: 'manifold-ext', version: manifoldVersion
implementation group: 'systems.manifold', name: 'manifold-props', version: manifoldVersion
implementation group: 'systems.manifold', name: 'manifold-delegation', version: manifoldVersion
implementation group: 'systems.manifold', name: 'manifold-typealias', version: manifoldVersion
implementation group: 'systems.manifold', name: 'manifold-strings', version: manifoldVersion
implementation group: 'systems.manifold', name: 'manifold-exceptions', version: manifoldVersion
implementation group: 'systems.manifold', name: 'manifold-preprocessor', version: manifoldVersion
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/manifold/ij/core/ManModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class ManModule extends SimpleModule
private final LocklessLazyVar<Boolean> _isPreprocessorEnabled;
private final LocklessLazyVar<Boolean> _isPropertiesEnabled;
private final LocklessLazyVar<Boolean> _isDelegationEnabled;
private final LocklessLazyVar<Boolean> _isTypeAliasEnabled;
private final LocklessLazyVar<Boolean> _isTuplesEnabled;

ManModule( ManProject manProject, Module ijModule, List<IDirectory> classpath, List<IDirectory> sourcePath, List<IDirectory> outputPath, List<IDirectory> excludedDirs )
Expand All @@ -95,6 +96,7 @@ public class ManModule extends SimpleModule
_isPreprocessorEnabled = LocklessLazyVar.make( () -> hasJar( "manifold-preprocessor" ) || hasJar( "manifold-all" ) );
_isPropertiesEnabled = LocklessLazyVar.make( () -> hasJar( "manifold-props" ) || hasJar( "manifold-all" ) );
_isDelegationEnabled = LocklessLazyVar.make( () -> hasJar( "manifold-delegation" ) || hasJar( "manifold-all" ) );
_isTypeAliasEnabled = LocklessLazyVar.make( () -> hasJar( "manifold-typealias" ) || hasJar( "manifold-all" ) );
_isTuplesEnabled = LocklessLazyVar.make( () -> hasJar( "manifold-tuple" ) || hasJar( "manifold-all" ) );
}

Expand Down Expand Up @@ -488,6 +490,11 @@ public boolean isDelegationEnabled()
return _isDelegationEnabled.get();
}

public boolean isTypeAliasEnabled()
{
return _isTypeAliasEnabled.get();
}

public boolean isTuplesEnabled()
{
return _isTuplesEnabled.get();
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/manifold/ij/core/ManProject.java
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,30 @@ public boolean isDelegationEnabledInAnyModules()
}
return modules.values().stream().anyMatch( m -> m.isDelegationEnabled() );
}
public static boolean isTypeAliasEnabledInAnyModules( PsiElement element )
{
ManProject manProject = ManProject.manProjectFrom( element.getProject() );
if( manProject == null )
{
return false;
}
return manProject.isTypeAliasEnabledInAnyModules();
}

public boolean isTypeAliasEnabledInAnyModules()
{
if( !isManifoldInUse() )
{
return false;
}

Map<Module, ManModule> modules = getModules();
if( modules == null )
{
return false;
}
return modules.values().stream().anyMatch( m -> m.isTypeAliasEnabled() );
}

public boolean isPreprocessorEnabledInAnyModules()
{
Expand Down
117 changes: 117 additions & 0 deletions src/main/java/manifold/ij/extensions/ManJavaClassSupersImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package manifold.ij.extensions;

import com.intellij.openapi.util.Key;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.psi.impl.JavaClassSupersImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.JavaClassSupers;
import manifold.ij.core.ManProject;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

public class ManJavaClassSupersImpl extends JavaClassSupers
{
private static final Key<CachedValue<Map<String, PsiSubstitutor>>> KEY_CACHED_TYPE_ALIAS_SUBSTITUTES = new Key<>("KEY_CACHED_TYPE_ALIAS_SUBSTITUTES");
private final JavaClassSupersImpl impl = new JavaClassSupersImpl();

@Nullable
@Override
public PsiSubstitutor getSuperClassSubstitutor( @NotNull PsiClass superClass, @NotNull PsiClass derivedClass, @NotNull GlobalSearchScope resolveScope, @NotNull PsiSubstitutor derivedSubstitutor )
{
// for performance reasons, check only if non-inherited.
PsiSubstitutor substitutor = impl.getSuperClassSubstitutor( superClass, derivedClass, resolveScope, derivedSubstitutor );
if( substitutor == null )
{
substitutor = getAliasedClassSubstitutor( superClass, derivedClass );
}
return substitutor;
}

@Override
public void reportHierarchyInconsistency( @NotNull PsiClass superClass, @NotNull PsiClass derivedClass )
{
impl.reportHierarchyInconsistency( superClass, derivedClass );
}

private PsiSubstitutor getAliasedClassSubstitutor( @NotNull PsiClass superClass, @NotNull PsiClass derivedClass )
{
if( !ManProject.isTypeAliasEnabledInAnyModules( derivedClass ) )
{
// Manifold jars are not used in the project
return null;
}
// AliasedType = OriginType
PsiSubstitutor substitutor = getAliasedClassSubstitutor0( superClass, derivedClass );
if( substitutor != null )
{
return substitutor;
}
// OriginType = AliasedType
return getAliasedClassSubstitutor0( derivedClass, superClass );
}

private PsiSubstitutor getAliasedClassSubstitutor0( @NotNull PsiClass superClass, @NotNull PsiClass derivedClass )
{
return CachedValuesManager.getCachedValue( superClass, KEY_CACHED_TYPE_ALIAS_SUBSTITUTES, new MyCachedValueProvider( superClass ) ).get( derivedClass.getQualifiedName() );
}

private static class MyCachedValueProvider implements CachedValueProvider<Map<String, PsiSubstitutor>>
{
private final SmartPsiElementPointer<PsiClass> _psiClassPointer;

public MyCachedValueProvider( PsiClass psiClass )
{
_psiClassPointer = SmartPointerManager.createPointer( psiClass );
}

@Nullable
@Override
public Result<Map<String, PsiSubstitutor>> compute()
{
HashMap<String, PsiSubstitutor> substitutes = new HashMap<>();
PsiClass psiClass = _psiClassPointer.getElement();
if( psiClass == null )
{
return Result.create( substitutes );
}
Set<PsiElement> dependencies = new LinkedHashSet<>();
PsiClass newPsiClass = TypeAliasMaker.getAliasedType( psiClass );
if( newPsiClass != null )
{
substitutes.put( newPsiClass.getQualifiedName(), PsiSubstitutor.EMPTY );
dependencies.add( TypeAliasMaker.getAnnotation( psiClass ) );
}
dependencies.add( psiClass );
return Result.create( substitutes, dependencies.toArray() );
}

@Override
public int hashCode()
{
return Objects.hashCode( _psiClassPointer.getElement() );
}

@Override
public boolean equals( Object obj )
{
if( obj instanceof MyCachedValueProvider other)
{
return Objects.equals( other._psiClassPointer.getElement(), _psiClassPointer.getElement() );
}
return false;
}
}
}
111 changes: 111 additions & 0 deletions src/main/java/manifold/ij/extensions/ManMethodSuperSearcher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package manifold.ij.extensions;

import com.intellij.openapi.application.QueryExecutorBase;
import com.intellij.psi.HierarchicalMethodSignature;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiType;
import com.intellij.psi.PsiTypeParameter;
import com.intellij.psi.search.searches.SuperMethodsSearch;
import com.intellij.psi.util.MethodSignature;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.MethodSignatureUtil;
import com.intellij.util.Processor;
import manifold.ij.core.ManProject;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ManMethodSuperSearcher extends QueryExecutorBase<MethodSignatureBackedByPsiMethod, SuperMethodsSearch.SearchParameters>
{
@Override
public void processQuery( @NotNull SuperMethodsSearch.SearchParameters queryParameters, @NotNull Processor<? super MethodSignatureBackedByPsiMethod> consumer )
{
PsiMethod method = queryParameters.getMethod();
if( !ManProject.isTypeAliasEnabledInAnyModules( method ) )
{
return;
}
HierarchicalMethodSignature signature = method.getHierarchicalMethodSignature();
List<HierarchicalMethodSignature> supers = signature.getSuperSignatures();
if( !supers.isEmpty() )
{
return;
}
for( HierarchicalMethodSignature superSignature : getSameNameSuperSignatures( method, signature ) )
{
if( MethodSignatureUtil.isSubsignature( superSignature, resolve( superSignature, signature ) ) )
{
consumer.process( superSignature );
}
}
}

private static MethodSignature resolve( @NotNull MethodSignature superSignature, @NotNull MethodSignature subSignature )
{
// resolve parameter type
PsiType[] oldParameterTypes = subSignature.getParameterTypes();
PsiType[] superParameterTypes = superSignature.getParameterTypes();
PsiType[] newParameterTypes = oldParameterTypes;
for( int i = 0; i < newParameterTypes.length; ++i )
{
if( newParameterTypes[i].isAssignableFrom(superParameterTypes[i]) )
{
if( newParameterTypes == oldParameterTypes )
{
newParameterTypes = oldParameterTypes.clone();
}
newParameterTypes[i] = superParameterTypes[i];
}
}
// resolve type parameter.
PsiTypeParameter[] oldTypeParameterList = subSignature.getTypeParameters();
PsiTypeParameter[] superTypeParameterList = superSignature.getTypeParameters();
PsiTypeParameter[] newTypeParameterList = oldTypeParameterList;
for( int i = 0; i < newTypeParameterList.length; ++i )
{
if( newTypeParameterList[i].isInheritor( superTypeParameterList[i], false ) )
{
if( newTypeParameterList == oldTypeParameterList )
{
newTypeParameterList = oldTypeParameterList.clone();
}
newTypeParameterList[i] = superTypeParameterList[i];
}
}
if( newParameterTypes == oldParameterTypes && newTypeParameterList == oldTypeParameterList )
{
return subSignature;
}
String name = subSignature.getName();
PsiSubstitutor substitutor = subSignature.getSubstitutor();
boolean isConstructor = subSignature.isConstructor();
return MethodSignatureUtil.createMethodSignature( name, newParameterTypes, newTypeParameterList, substitutor, isConstructor );
}

private static List<HierarchicalMethodSignature> getSameNameSuperSignatures( PsiMethod method, HierarchicalMethodSignature signature )
{
if( !(method.getParent() instanceof PsiClass psiClass) )
{
return Collections.emptyList();
}
ArrayList<HierarchicalMethodSignature> methodSignatures = new ArrayList<>();
int parameterCount = signature.getParameterTypes().length;
int typeParameterCount = signature.getTypeParameters().length;
for( PsiClass psiSuperClass : psiClass.getSupers() )
{
for( PsiMethod psiSuperMethod : psiSuperClass.findMethodsByName( method.getName(), true ) )
{
HierarchicalMethodSignature superSignature = psiSuperMethod.getHierarchicalMethodSignature();
if( parameterCount == superSignature.getParameterTypes().length && typeParameterCount == superSignature.getTypeParameters().length )
{
methodSignatures.add( superSignature );
}
}
}
return methodSignatures;
}
}
Loading