[k1] add information about context receiver labels to ContextClassReceiver/ContextReceiver
This commit is contained in:
@@ -984,7 +984,7 @@ public class DescriptorResolver {
|
|||||||
KotlinType type = typeResolver.resolveType(scopeForDeclarationResolutionWithTypeParameters, typeReference, trace, true);
|
KotlinType type = typeResolver.resolveType(scopeForDeclarationResolutionWithTypeParameters, typeReference, trace, true);
|
||||||
AnnotationSplitter splitter = new AnnotationSplitter(storageManager, type.getAnnotations(), EnumSet.of(RECEIVER));
|
AnnotationSplitter splitter = new AnnotationSplitter(storageManager, type.getAnnotations(), EnumSet.of(RECEIVER));
|
||||||
return DescriptorFactory.createContextReceiverParameterForCallable(
|
return DescriptorFactory.createContextReceiverParameterForCallable(
|
||||||
propertyDescriptor, type, splitter.getAnnotationsForTarget(RECEIVER)
|
propertyDescriptor, type, contextReceiver.labelNameAsName(), splitter.getAnnotationsForTarget(RECEIVER)
|
||||||
);
|
);
|
||||||
}).collect(Collectors.toList());
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ import org.jetbrains.kotlin.resolve.scopes.LexicalWritableScope
|
|||||||
import org.jetbrains.kotlin.resolve.scopes.TraceBasedLocalRedeclarationChecker
|
import org.jetbrains.kotlin.resolve.scopes.TraceBasedLocalRedeclarationChecker
|
||||||
import org.jetbrains.kotlin.resolve.source.toSourceElement
|
import org.jetbrains.kotlin.resolve.source.toSourceElement
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
||||||
import org.jetbrains.kotlin.types.error.ErrorUtils
|
import org.jetbrains.kotlin.types.error.ErrorUtils
|
||||||
@@ -66,6 +67,7 @@ import org.jetbrains.kotlin.types.expressions.ExpressionTypingServices
|
|||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionExpression
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionExpression
|
||||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral
|
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral
|
||||||
|
import org.jetbrains.kotlin.types.isError
|
||||||
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
|
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@@ -201,8 +203,12 @@ class FunctionDescriptorResolver(
|
|||||||
val contextReceiverTypes =
|
val contextReceiverTypes =
|
||||||
if (function is KtFunctionLiteral) expectedFunctionType.getContextReceiversTypes()
|
if (function is KtFunctionLiteral) expectedFunctionType.getContextReceiversTypes()
|
||||||
else contextReceivers
|
else contextReceivers
|
||||||
.mapNotNull { it.typeReference() }
|
.mapNotNull {
|
||||||
.map { typeResolver.resolveType(headerScope, it, trace, true) }
|
val typeReference = it.typeReference() ?: return@mapNotNull null
|
||||||
|
val type = typeResolver.resolveType(headerScope, typeReference, trace, true)
|
||||||
|
ContextReceiverTypeWithLabel(type, it.labelNameAsName())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
val valueParameterDescriptors =
|
val valueParameterDescriptors =
|
||||||
createValueParameterDescriptors(function, functionDescriptor, headerScope, trace, expectedFunctionType, inferenceSession)
|
createValueParameterDescriptors(function, functionDescriptor, headerScope, trace, expectedFunctionType, inferenceSession)
|
||||||
@@ -234,10 +240,13 @@ class FunctionDescriptorResolver(
|
|||||||
functionDescriptor, it, splitter.getAnnotationsForTarget(AnnotationUseSiteTarget.RECEIVER)
|
functionDescriptor, it, splitter.getAnnotationsForTarget(AnnotationUseSiteTarget.RECEIVER)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val contextReceiverDescriptors = contextReceiverTypes.mapNotNull {
|
val contextReceiverDescriptors = contextReceiverTypes.mapNotNull { contextReceiver ->
|
||||||
val splitter = AnnotationSplitter(storageManager, it.annotations, EnumSet.of(AnnotationUseSiteTarget.RECEIVER))
|
val splitter = AnnotationSplitter(storageManager, contextReceiver.type.annotations, EnumSet.of(AnnotationUseSiteTarget.RECEIVER))
|
||||||
DescriptorFactory.createContextReceiverParameterForCallable(
|
DescriptorFactory.createContextReceiverParameterForCallable(
|
||||||
functionDescriptor, it, splitter.getAnnotationsForTarget(AnnotationUseSiteTarget.RECEIVER)
|
functionDescriptor,
|
||||||
|
contextReceiver.type,
|
||||||
|
contextReceiver.label,
|
||||||
|
splitter.getAnnotationsForTarget(AnnotationUseSiteTarget.RECEIVER),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,8 +367,12 @@ class FunctionDescriptorResolver(
|
|||||||
private fun KotlinType.getReceiverType(): KotlinType? =
|
private fun KotlinType.getReceiverType(): KotlinType? =
|
||||||
if (functionTypeExpected()) this.getReceiverTypeFromFunctionType() else null
|
if (functionTypeExpected()) this.getReceiverTypeFromFunctionType() else null
|
||||||
|
|
||||||
private fun KotlinType.getContextReceiversTypes(): List<KotlinType> =
|
private fun KotlinType.getContextReceiversTypes(): List<ContextReceiverTypeWithLabel> =
|
||||||
if (functionTypeExpected()) this.getContextReceiverTypesFromFunctionType() else emptyList()
|
if (functionTypeExpected()) {
|
||||||
|
this.getContextReceiverTypesFromFunctionType().map { ContextReceiverTypeWithLabel(it, label = null)}
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
private fun KotlinType.getValueParameters(owner: FunctionDescriptor): List<ValueParameterDescriptor>? =
|
private fun KotlinType.getValueParameters(owner: FunctionDescriptor): List<ValueParameterDescriptor>? =
|
||||||
if (functionTypeExpected()) {
|
if (functionTypeExpected()) {
|
||||||
@@ -514,4 +527,6 @@ class FunctionDescriptorResolver(
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private data class ContextReceiverTypeWithLabel(val type: KotlinType, val label: Name?)
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-4
@@ -308,17 +308,20 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements ClassDes
|
|||||||
}
|
}
|
||||||
List<KtContextReceiver> contextReceivers = classOrObject.getContextReceivers();
|
List<KtContextReceiver> contextReceivers = classOrObject.getContextReceivers();
|
||||||
List<ReceiverParameterDescriptor> contextReceiverDescriptors = contextReceivers.stream()
|
List<ReceiverParameterDescriptor> contextReceiverDescriptors = contextReceivers.stream()
|
||||||
.map(KtContextReceiver::typeReference)
|
.map(contextReceiver -> {
|
||||||
.filter(Objects::nonNull)
|
KtTypeReference typeReference = contextReceiver.typeReference();
|
||||||
.map(typeReference -> {
|
if (typeReference == null) return null;
|
||||||
KotlinType kotlinType =
|
KotlinType kotlinType =
|
||||||
c.getTypeResolver().resolveType(getScopeForClassHeaderResolution(), typeReference, c.getTrace(), true);
|
c.getTypeResolver().resolveType(getScopeForClassHeaderResolution(), typeReference, c.getTrace(), true);
|
||||||
return DescriptorFactory.createContextReceiverParameterForClass(
|
return DescriptorFactory.createContextReceiverParameterForClass(
|
||||||
this,
|
this,
|
||||||
kotlinType,
|
kotlinType,
|
||||||
|
contextReceiver.labelNameAsName(),
|
||||||
Annotations.Companion.getEMPTY()
|
Annotations.Companion.getEMPTY()
|
||||||
);
|
);
|
||||||
}).collect(Collectors.toList());
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (c.getLanguageVersionSettings().supportsFeature(LanguageFeature.ContextReceivers)) {
|
if (c.getLanguageVersionSettings().supportsFeature(LanguageFeature.ContextReceivers)) {
|
||||||
HashMultimap<String, ReceiverParameterDescriptor> labelNameToReceiverMap = HashMultimap.create();
|
HashMultimap<String, ReceiverParameterDescriptor> labelNameToReceiverMap = HashMultimap.create();
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationsKt;
|
|||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorFactory;
|
import org.jetbrains.kotlin.resolve.DescriptorFactory;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitContextReceiver;
|
||||||
import org.jetbrains.kotlin.types.*;
|
import org.jetbrains.kotlin.types.*;
|
||||||
import org.jetbrains.kotlin.utils.SmartList;
|
import org.jetbrains.kotlin.utils.SmartList;
|
||||||
|
|
||||||
@@ -635,6 +636,7 @@ public abstract class FunctionDescriptorImpl extends DeclarationDescriptorNonRoo
|
|||||||
}
|
}
|
||||||
ReceiverParameterDescriptor substitutedContextReceiverParameter =
|
ReceiverParameterDescriptor substitutedContextReceiverParameter =
|
||||||
DescriptorFactory.createContextReceiverParameterForCallable(substitutedDescriptor, substitutedContextReceiverType,
|
DescriptorFactory.createContextReceiverParameterForCallable(substitutedDescriptor, substitutedContextReceiverType,
|
||||||
|
((ImplicitContextReceiver)newContextReceiverParameter.getValue()).getCustomLabelName(),
|
||||||
newContextReceiverParameter.getAnnotations());
|
newContextReceiverParameter.getAnnotations());
|
||||||
substitutedContextReceiverParameters.add(substitutedContextReceiverParameter);
|
substitutedContextReceiverParameters.add(substitutedContextReceiverParameter);
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
|||||||
import org.jetbrains.kotlin.name.Name;
|
import org.jetbrains.kotlin.name.Name;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ContextReceiver;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ContextReceiver;
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver;
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitContextReceiver;
|
||||||
import org.jetbrains.kotlin.types.*;
|
import org.jetbrains.kotlin.types.*;
|
||||||
import org.jetbrains.kotlin.utils.SmartSet;
|
import org.jetbrains.kotlin.utils.SmartSet;
|
||||||
|
|
||||||
@@ -561,7 +562,10 @@ public class PropertyDescriptorImpl extends VariableDescriptorWithInitializerImp
|
|||||||
if (substitutedType == null) return null;
|
if (substitutedType == null) return null;
|
||||||
return new ReceiverParameterDescriptorImpl(
|
return new ReceiverParameterDescriptorImpl(
|
||||||
substitutedPropertyDescriptor,
|
substitutedPropertyDescriptor,
|
||||||
new ContextReceiver(substitutedPropertyDescriptor, substitutedType, receiverParameterDescriptor.getValue()),
|
new ContextReceiver(substitutedPropertyDescriptor,
|
||||||
|
substitutedType,
|
||||||
|
((ImplicitContextReceiver) receiverParameterDescriptor.getValue()).getCustomLabelName(),
|
||||||
|
receiverParameterDescriptor.getValue()),
|
||||||
receiverParameterDescriptor.getAnnotations()
|
receiverParameterDescriptor.getAnnotations()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
|||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.name.SpecialNames
|
import org.jetbrains.kotlin.name.SpecialNames
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||||
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitContextReceiver
|
||||||
import org.jetbrains.kotlin.storage.StorageManager
|
import org.jetbrains.kotlin.storage.StorageManager
|
||||||
import org.jetbrains.kotlin.storage.getValue
|
import org.jetbrains.kotlin.storage.getValue
|
||||||
import org.jetbrains.kotlin.types.*
|
import org.jetbrains.kotlin.types.*
|
||||||
@@ -209,6 +210,7 @@ class TypeAliasConstructorDescriptorImpl private constructor(
|
|||||||
DescriptorFactory.createContextReceiverParameterForClass(
|
DescriptorFactory.createContextReceiverParameterForClass(
|
||||||
classDescriptor,
|
classDescriptor,
|
||||||
substitutorForUnderlyingClass.safeSubstitute(it.type, Variance.INVARIANT),
|
substitutorForUnderlyingClass.safeSubstitute(it.type, Variance.INVARIANT),
|
||||||
|
(it.value as ImplicitContextReceiver).customLabelName,
|
||||||
Annotations.EMPTY
|
Annotations.EMPTY
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,21 +200,23 @@ public class DescriptorFactory {
|
|||||||
public static ReceiverParameterDescriptor createContextReceiverParameterForCallable(
|
public static ReceiverParameterDescriptor createContextReceiverParameterForCallable(
|
||||||
@NotNull CallableDescriptor owner,
|
@NotNull CallableDescriptor owner,
|
||||||
@Nullable KotlinType receiverParameterType,
|
@Nullable KotlinType receiverParameterType,
|
||||||
|
@Nullable Name customLabelName,
|
||||||
@NotNull Annotations annotations
|
@NotNull Annotations annotations
|
||||||
) {
|
) {
|
||||||
return receiverParameterType == null
|
return receiverParameterType == null
|
||||||
? null
|
? null
|
||||||
: new ReceiverParameterDescriptorImpl(owner, new ContextReceiver(owner, receiverParameterType, null), annotations);
|
: new ReceiverParameterDescriptorImpl(owner, new ContextReceiver(owner, receiverParameterType, customLabelName, null), annotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static ReceiverParameterDescriptor createContextReceiverParameterForClass(
|
public static ReceiverParameterDescriptor createContextReceiverParameterForClass(
|
||||||
@NotNull ClassDescriptor owner,
|
@NotNull ClassDescriptor owner,
|
||||||
@Nullable KotlinType receiverParameterType,
|
@Nullable KotlinType receiverParameterType,
|
||||||
|
@Nullable Name customLabelName,
|
||||||
@NotNull Annotations annotations
|
@NotNull Annotations annotations
|
||||||
) {
|
) {
|
||||||
return receiverParameterType == null
|
return receiverParameterType == null
|
||||||
? null
|
? null
|
||||||
: new ReceiverParameterDescriptorImpl(owner, new ContextClassReceiver(owner, receiverParameterType, null), annotations);
|
: new ReceiverParameterDescriptorImpl(owner, new ContextClassReceiver(owner, receiverParameterType, customLabelName, null), annotations);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -7,17 +7,19 @@ package org.jetbrains.kotlin.resolve.scopes.receivers
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
class ContextClassReceiver(
|
class ContextClassReceiver(
|
||||||
val classDescriptor: ClassDescriptor,
|
val classDescriptor: ClassDescriptor,
|
||||||
receiverType: KotlinType,
|
receiverType: KotlinType,
|
||||||
|
override val customLabelName: Name?,
|
||||||
original: ReceiverValue?
|
original: ReceiverValue?
|
||||||
): AbstractReceiverValue(receiverType, original), ImplicitReceiver {
|
): AbstractReceiverValue(receiverType, original), ImplicitContextReceiver {
|
||||||
override val declarationDescriptor: DeclarationDescriptor
|
override val declarationDescriptor: DeclarationDescriptor
|
||||||
get() = classDescriptor
|
get() = classDescriptor
|
||||||
|
|
||||||
override fun replaceType(newType: KotlinType): ReceiverValue = ContextClassReceiver(classDescriptor, newType, original)
|
override fun replaceType(newType: KotlinType): ReceiverValue = ContextClassReceiver(classDescriptor, newType, customLabelName, original)
|
||||||
|
|
||||||
override fun toString(): String = "$type: Ctx { $classDescriptor }"
|
override fun toString(): String = "$type: Ctx { $classDescriptor }"
|
||||||
}
|
}
|
||||||
+4
-2
@@ -6,14 +6,16 @@
|
|||||||
package org.jetbrains.kotlin.resolve.scopes.receivers
|
package org.jetbrains.kotlin.resolve.scopes.receivers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
|
||||||
class ContextReceiver(
|
class ContextReceiver(
|
||||||
override val declarationDescriptor: CallableDescriptor,
|
override val declarationDescriptor: CallableDescriptor,
|
||||||
receiverType: KotlinType,
|
receiverType: KotlinType,
|
||||||
|
override val customLabelName: Name?,
|
||||||
original: ReceiverValue?
|
original: ReceiverValue?
|
||||||
) : AbstractReceiverValue(receiverType, original), ImplicitReceiver {
|
) : AbstractReceiverValue(receiverType, original), ImplicitContextReceiver {
|
||||||
override fun replaceType(newType: KotlinType): ReceiverValue = ContextReceiver(declarationDescriptor, newType, original)
|
override fun replaceType(newType: KotlinType): ReceiverValue = ContextReceiver(declarationDescriptor, newType, customLabelName, original)
|
||||||
|
|
||||||
override fun toString(): String = "Cxt { $declarationDescriptor }"
|
override fun toString(): String = "Cxt { $declarationDescriptor }"
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.resolve.scopes.receivers
|
package org.jetbrains.kotlin.resolve.scopes.receivers
|
||||||
|
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes an implicit "this" receiver
|
* Describes an implicit "this" receiver
|
||||||
@@ -24,3 +25,8 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
|||||||
interface ImplicitReceiver : ReceiverValue {
|
interface ImplicitReceiver : ReceiverValue {
|
||||||
val declarationDescriptor: DeclarationDescriptor
|
val declarationDescriptor: DeclarationDescriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
interface ImplicitContextReceiver : ImplicitReceiver {
|
||||||
|
val customLabelName: Name?
|
||||||
|
}
|
||||||
|
|||||||
+6
-1
@@ -358,6 +358,11 @@ class MemberDeserializer(private val c: DeserializationContext) {
|
|||||||
callableDescriptor: CallableDescriptor
|
callableDescriptor: CallableDescriptor
|
||||||
): ReceiverParameterDescriptor? {
|
): ReceiverParameterDescriptor? {
|
||||||
val contextReceiverType = deserializationContext.typeDeserializer.type(this)
|
val contextReceiverType = deserializationContext.typeDeserializer.type(this)
|
||||||
return DescriptorFactory.createContextReceiverParameterForCallable(callableDescriptor, contextReceiverType, Annotations.EMPTY)
|
return DescriptorFactory.createContextReceiverParameterForCallable(
|
||||||
|
callableDescriptor,
|
||||||
|
contextReceiverType,
|
||||||
|
/* customLabelName = */ null/*todo store custom label name in metadata?*/,
|
||||||
|
Annotations.EMPTY
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -150,7 +150,12 @@ class DeserializedClassDescriptor(
|
|||||||
val contextReceiverType = c.typeDeserializer.type(it)
|
val contextReceiverType = c.typeDeserializer.type(it)
|
||||||
ReceiverParameterDescriptorImpl(
|
ReceiverParameterDescriptorImpl(
|
||||||
thisAsReceiverParameter,
|
thisAsReceiverParameter,
|
||||||
ContextClassReceiver(this, contextReceiverType, null),
|
ContextClassReceiver(
|
||||||
|
this,
|
||||||
|
contextReceiverType,
|
||||||
|
/* customLabelName = */ null/*todo store custom label name in metadata?*/,
|
||||||
|
null
|
||||||
|
),
|
||||||
Annotations.EMPTY
|
Annotations.EMPTY
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user