mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -49,3 +49,4 @@ pycharm-flask
|
||||
pycharm-numpy
|
||||
textmate
|
||||
restClient
|
||||
puppet
|
||||
|
||||
@@ -179,6 +179,7 @@ private layoutPlugins(layouts) {
|
||||
include(name: "joni.jar")
|
||||
}
|
||||
}
|
||||
layouts.layoutPlugin("puppet")
|
||||
}
|
||||
|
||||
layouts.layoutPlugins()
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
<id>org.jetbrains.plugins.rest</id>
|
||||
<description>This plugin enables support for reStructuredText files (*.rst)</description>
|
||||
<vendor>JetBrains</vendor>
|
||||
<version>122.SNAPSHOT</version>
|
||||
<idea-version since-build="124.0" until-build="126.9999"/>
|
||||
<version>126.SNAPSHOT</version>
|
||||
<idea-version since-build="126.0" until-build="126.9999"/>
|
||||
<depends>com.intellij.modules.lang</depends>
|
||||
|
||||
<xi:include href="/META-INF/rest.xml" xpointer="xpointer(/idea-plugin/*)"/>
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
<localInspection language="Python" shortName="PyPackageRequirementsInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.requirements" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyPackageRequirementsInspection"/>
|
||||
<localInspection language="Python" shortName="PyPep8Inspection" displayName="PEP 8 coding style violation" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyPep8Inspection"/>
|
||||
<localInspection language="Python" shortName="PyDocstringTypesInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.docstring.types" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyDocstringTypesInspection"/>
|
||||
<localInspection language="Python" shortName="PyShadowingBuiltinsInspection" displayName="Shadowing built-ins" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyShadowingBuiltinsInspection"/>
|
||||
|
||||
<liveTemplateContext implementation="com.jetbrains.python.codeInsight.liveTemplates.PythonTemplateContextType"/>
|
||||
<liveTemplateMacro implementation="com.jetbrains.python.codeInsight.liveTemplates.CollectionElementNameMacro"/>
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* User : catherine
|
||||
@@ -37,19 +36,18 @@ public class PyTestFinder implements TestFinder {
|
||||
PyDocStringOwner source = findSourceElement(element);
|
||||
if (source == null) return Collections.emptySet();
|
||||
|
||||
String klassName = source.getName();
|
||||
Pattern pattern = Pattern.compile(".*" + klassName + ".*");
|
||||
String sourceName = source.getName();
|
||||
|
||||
List<Pair<? extends PsiNamedElement, Integer>> classesWithProximities = new ArrayList<Pair<? extends PsiNamedElement, Integer>>();
|
||||
|
||||
if (source instanceof PyClass) {
|
||||
Collection<String> names = PyClassNameIndex.allKeys(element.getProject());
|
||||
for (String eachName : names) {
|
||||
if (pattern.matcher(eachName).matches()) {
|
||||
if (eachName.contains(sourceName)) {
|
||||
for (PyClass eachClass : PyClassNameIndex.find(eachName, element.getProject(), GlobalSearchScope.projectScope(element.getProject()))) {
|
||||
if (PythonUnitTestUtil.isTestCaseClass(eachClass) || PythonDocTestUtil.isDocTestClass(eachClass)) {
|
||||
classesWithProximities.add(
|
||||
new Pair<PsiNamedElement, Integer>(eachClass, TestFinderHelper.calcTestNameProximity(klassName, eachName)));
|
||||
new Pair<PsiNamedElement, Integer>(eachClass, TestFinderHelper.calcTestNameProximity(sourceName, eachName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,12 +56,12 @@ public class PyTestFinder implements TestFinder {
|
||||
else {
|
||||
Collection<String> names = PyFunctionNameIndex.allKeys(element.getProject());
|
||||
for (String eachName : names) {
|
||||
if (pattern.matcher(eachName).matches()) {
|
||||
if (eachName.contains(sourceName)) {
|
||||
for (PyFunction eachFunction : PyFunctionNameIndex.find(eachName, element.getProject(), GlobalSearchScope.projectScope(element.getProject()))) {
|
||||
if (PythonUnitTestUtil.isTestCaseFunction(
|
||||
eachFunction) || PythonDocTestUtil.isDocTestFunction(eachFunction)) {
|
||||
classesWithProximities.add(
|
||||
new Pair<PsiNamedElement, Integer>(eachFunction, TestFinderHelper.calcTestNameProximity(klassName, eachName)));
|
||||
new Pair<PsiNamedElement, Integer>(eachFunction, TestFinderHelper.calcTestNameProximity(sourceName, eachName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,10 +41,9 @@ import com.intellij.psi.impl.source.tree.FileElement;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IJSwingUtilities;
|
||||
import com.intellij.util.io.IOUtil;
|
||||
import com.intellij.util.net.NetUtils;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.jetbrains.django.run.Runner;
|
||||
import com.jetbrains.django.run.ProcessRunner;
|
||||
import com.intellij.remotesdk.RemoteSdkData;
|
||||
import com.intellij.remotesdk.RemoteSshProcess;
|
||||
import com.jetbrains.python.PythonHelpersLocator;
|
||||
@@ -247,7 +246,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
|
||||
}
|
||||
else {
|
||||
myCommandLine = myCommandLineArgumentsProvider.getCommandLineString();
|
||||
final Process server = Runner
|
||||
final Process server = ProcessRunner
|
||||
.createProcess(getWorkingDir(), myCommandLineArgumentsProvider.getAdditionalEnvs(), myCommandLineArgumentsProvider.getArguments());
|
||||
try {
|
||||
myPydevConsoleCommunication = new PydevConsoleCommunication(getProject(), myPorts[0], server, myPorts[1]);
|
||||
@@ -278,7 +277,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
|
||||
|
||||
|
||||
RemoteSshProcess remoteProcess =
|
||||
manager.createRemoteProcess(getProject(), data, commandLine, false);
|
||||
manager.createRemoteProcess(getProject(), data, commandLine, true);
|
||||
|
||||
|
||||
Pair<Integer, Integer> remotePorts = getRemotePortsFromProcess(remoteProcess);
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</constraints>
|
||||
<properties>
|
||||
<labelFor value="63296"/>
|
||||
<text value="&URL Pattern:"/>
|
||||
<text value="&URL/Path Pattern:"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="63296" class="javax.swing.JTextField" binding="myURLPatternTextField" default-binding="true">
|
||||
|
||||
@@ -49,11 +49,7 @@ public class PyCallingNonCallableInspection extends PyInspection {
|
||||
final PyExpression callee = decorator.getCallee();
|
||||
checkCallable(decorator, callee, null);
|
||||
if (decorator.hasArgumentList()) {
|
||||
final Callable callable = decorator.resolveCalleeFunction(resolveWithoutImplicits());
|
||||
if (callable != null) {
|
||||
final PyReferenceExpression callSite = callee instanceof PyReferenceExpression ? (PyReferenceExpression)callee : null;
|
||||
checkCallable(decorator, null, callable.getReturnType(myTypeEvalContext, callSite));
|
||||
}
|
||||
checkCallable(decorator, decorator, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Warns about shadowing built-in names.
|
||||
*
|
||||
* TODO: Merge into PyRedeclarationInspection and detect all shadowed names
|
||||
*
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyShadowingBuiltinsInspection extends PyInspection {
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
|
||||
boolean isOnTheFly,
|
||||
@NotNull LocalInspectionToolSession session) {
|
||||
return new Visitor(holder, session);
|
||||
}
|
||||
|
||||
private static class Visitor extends PyInspectionVisitor {
|
||||
public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
|
||||
super(holder, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyClass(@NotNull PyClass node) {
|
||||
processElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyFunction(@NotNull PyFunction node) {
|
||||
if (node.getContainingClass() == null) {
|
||||
processElement(node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyNamedParameter(@NotNull PyNamedParameter node) {
|
||||
processElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyTargetExpression(@NotNull PyTargetExpression node) {
|
||||
processElement(node);
|
||||
}
|
||||
|
||||
private void processElement(@NotNull PsiNameIdentifierOwner element) {
|
||||
final String name = element.getName();
|
||||
if (name != null) {
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(element);
|
||||
final PsiElement builtin = builtinCache.getByName(name);
|
||||
if (builtin != null && !PyUtil.inSameFile(builtin, element)) {
|
||||
final PsiElement identifier = element.getNameIdentifier();
|
||||
registerProblem(identifier != null ? identifier : element, "Shadows a built-in with the same name");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,7 @@ public class PyTypeCheckerInspection extends PyInspection {
|
||||
final Map<PyGenericType, PyType> substitutions = new LinkedHashMap<PyGenericType, PyType>();
|
||||
final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCallSite(callSite, myTypeEvalContext);
|
||||
if (results != null) {
|
||||
substitutions.putAll(PyTypeChecker.collectCallGenerics(results.getFunction(), results.getReceiver(), myTypeEvalContext));
|
||||
substitutions.putAll(PyTypeChecker.collectCallGenerics(results.getCallable(), results.getReceiver(), myTypeEvalContext));
|
||||
for (Map.Entry<PyExpression, PyNamedParameter> entry : results.getArguments().entrySet()) {
|
||||
final PyNamedParameter p = entry.getValue();
|
||||
if (p.isPositionalContainer() || p.isKeywordContainer()) {
|
||||
|
||||
@@ -672,8 +672,8 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
|
||||
return true;
|
||||
}
|
||||
if (qtype instanceof PyFunctionType) {
|
||||
final PyFunction function = ((PyFunctionType)qtype).getFunction();
|
||||
if (function.getDecoratorList() != null) {
|
||||
final Callable callable = ((PyFunctionType)qtype).getCallable();
|
||||
if (callable instanceof PyFunction && ((PyFunction)callable).getDecoratorList() != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileManager;
|
||||
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
|
||||
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
|
||||
import com.intellij.remotesdk.RemoteCredentials;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.SystemProperties;
|
||||
@@ -627,7 +628,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
|
||||
private String getHelperPath(String helper) {
|
||||
String helperPath;
|
||||
final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
|
||||
if (sdkData instanceof RemoteSdkData) {
|
||||
if (sdkData instanceof RemoteCredentials) {
|
||||
final RemoteSdkData remoteSdkData = (RemoteSdkData)sdkData;
|
||||
helperPath = new RemoteFile(remoteSdkData.getHelpersPath(),
|
||||
helper).getPath();
|
||||
@@ -644,7 +645,7 @@ public class PyPackageManagerImpl extends PyPackageManager {
|
||||
@Nullable String workingDir)
|
||||
throws PyExternalProcessException {
|
||||
final SdkAdditionalData sdkData = mySdk.getSdkAdditionalData();
|
||||
if (sdkData instanceof RemoteSdkData) { //remote interpreter
|
||||
if (sdkData instanceof RemoteCredentials) { //remote interpreter
|
||||
final RemoteSdkData remoteSdkData = (RemoteSdkData)sdkData;
|
||||
final PythonRemoteInterpreterManager manager = PythonRemoteInterpreterManager.getInstance();
|
||||
if (manager != null) {
|
||||
|
||||
@@ -111,7 +111,7 @@ public class PyBinaryExpressionImpl extends PyElementImpl implements PyBinaryExp
|
||||
public PyType getType(@NotNull TypeEvalContext context) {
|
||||
final PyTypeChecker.AnalyzeCallResults results = PyTypeChecker.analyzeCall(this, context);
|
||||
if (results != null) {
|
||||
final PyType type = results.getFunction().getReturnType(context, this);
|
||||
final PyType type = results.getCallable().getReturnType(context, this);
|
||||
if (!PyTypeChecker.isUnknown(type) && !(type instanceof PyNoneType)) {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -347,7 +347,7 @@ public class PyBuiltinCache {
|
||||
return true;
|
||||
}
|
||||
else if (type instanceof PyFunctionType) {
|
||||
return ((PyFunctionType)type).getFunction().isValid();
|
||||
return ((PyFunctionType)type).getCallable().isValid();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionUtil;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.ResolveResult;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.toolbox.Maybe;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -337,4 +341,199 @@ public class PyCallExpressionHelper {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static PyType getCallType(@NotNull PyCallExpression call, @NotNull TypeEvalContext context) {
|
||||
if (!TypeEvalStack.mayEvaluate(call)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
PyExpression callee = call.getCallee();
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
// hardwired special cases
|
||||
if (PyNames.SUPER.equals(callee.getText())) {
|
||||
final Maybe<PyType> superCallType = getSuperCallType(call, context);
|
||||
if (superCallType.isDefined()) {
|
||||
return superCallType.value();
|
||||
}
|
||||
}
|
||||
if ("type".equals(callee.getText())) {
|
||||
final PyExpression[] args = call.getArguments();
|
||||
if (args.length == 1) {
|
||||
final PyExpression arg = args[0];
|
||||
final PyType argType = arg.getType(context);
|
||||
if (argType instanceof PyClassType) {
|
||||
final PyClassType classType = (PyClassType)argType;
|
||||
if (!classType.isDefinition()) {
|
||||
final PyClass cls = classType.getPyClass();
|
||||
return cls.getType(context);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// normal cases
|
||||
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
|
||||
ResolveResult[] targets = ((PyReferenceExpression)callee).getReference(resolveContext).multiResolve(false);
|
||||
if (targets.length > 0) {
|
||||
PsiElement target = targets[0].getElement();
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
PyClass cls = null;
|
||||
PyFunction init = null;
|
||||
if (target instanceof PyClass) {
|
||||
cls = (PyClass)target;
|
||||
init = cls.findInitOrNew(true);
|
||||
}
|
||||
else if (target instanceof PyFunction) {
|
||||
final PyFunction f = (PyFunction)target;
|
||||
if (PyNames.INIT.equals(f.getName())) {
|
||||
init = f;
|
||||
cls = f.getContainingClass();
|
||||
}
|
||||
}
|
||||
if (init != null) {
|
||||
final PyType t = init.getReturnType(context, (PyReferenceExpression)callee);
|
||||
if (cls != null) {
|
||||
if (init.getContainingClass() != cls) {
|
||||
if (t instanceof PyCollectionType) {
|
||||
final PyType elementType = ((PyCollectionType)t).getElementType(context);
|
||||
return new PyCollectionTypeImpl(cls, false, elementType);
|
||||
}
|
||||
return new PyClassTypeImpl(cls, false);
|
||||
}
|
||||
}
|
||||
if (t != null && !(t instanceof PyNoneType)) {
|
||||
if (t instanceof PyTypeReference) {
|
||||
PyType resolved = ((PyTypeReference)t).resolve(callee, context);
|
||||
if (resolved == null && cls != null) {
|
||||
return PyUnionType.createWeakType(new PyClassTypeImpl(cls, false));
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
if (cls != null) {
|
||||
return new PyClassTypeImpl(cls, false);
|
||||
}
|
||||
final PyType providedType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(target, context, call);
|
||||
if (providedType != null) {
|
||||
if (providedType instanceof PyClassType) {
|
||||
return ((PyClassType)providedType).toInstance();
|
||||
}
|
||||
return providedType;
|
||||
}
|
||||
if (target instanceof Callable) {
|
||||
final Callable callable = (Callable)target;
|
||||
PyType returnType = callable.getReturnType(context, (PyReferenceExpression)callee);
|
||||
if (returnType != null) {
|
||||
return returnType;
|
||||
}
|
||||
return new PyReturnTypeReference(callable);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callee == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
final PyType type = context.getType(callee);
|
||||
if (type instanceof PyCallableType) {
|
||||
final PyQualifiedExpression callSite = callee instanceof PyQualifiedExpression ? (PyQualifiedExpression)callee : null;
|
||||
return ((PyCallableType) type).getCallType(context, callSite);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
TypeEvalStack.evaluated(call);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Maybe<PyType> getSuperCallType(@NotNull PyCallExpression call, TypeEvalContext context) {
|
||||
final PyExpression callee = call.getCallee();
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
PsiElement must_be_super_init = ((PyReferenceExpression)callee).getReference().resolve();
|
||||
if (must_be_super_init instanceof PyFunction) {
|
||||
PyClass must_be_super = ((PyFunction)must_be_super_init).getContainingClass();
|
||||
if (must_be_super == PyBuiltinCache.getInstance(call).getClass(PyNames.SUPER)) {
|
||||
PyArgumentList arglist = call.getArgumentList();
|
||||
if (arglist != null) {
|
||||
final PyClass containingClass = PsiTreeUtil.getParentOfType(call, PyClass.class);
|
||||
PyExpression[] args = arglist.getArguments();
|
||||
if (args.length > 1) {
|
||||
PyExpression first_arg = args[0];
|
||||
if (first_arg instanceof PyReferenceExpression) {
|
||||
final PyReferenceExpression firstArgRef = (PyReferenceExpression)first_arg;
|
||||
final PyExpression qualifier = firstArgRef.getQualifier();
|
||||
if (qualifier != null && PyNames.CLASS.equals(firstArgRef.getReferencedName())) {
|
||||
final PsiReference qRef = qualifier.getReference();
|
||||
final PsiElement element = qRef == null ? null : qRef.resolve();
|
||||
if (element instanceof PyParameter) {
|
||||
final PyParameterList parameterList = PsiTreeUtil.getParentOfType(element, PyParameterList.class);
|
||||
if (parameterList != null && element == parameterList.getParameters()[0]) {
|
||||
return new Maybe<PyType>(getSuperCallTypeForArguments(context, containingClass, args[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiElement possible_class = firstArgRef.getReference().resolve();
|
||||
if (possible_class instanceof PyClass && ((PyClass)possible_class).isNewStyleClass()) {
|
||||
final PyClass first_class = (PyClass)possible_class;
|
||||
return new Maybe<PyType>(getSuperCallTypeForArguments(context, first_class, args[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (((PyFile)call.getContainingFile()).getLanguageLevel().isPy3K() && containingClass != null) {
|
||||
return new Maybe<PyType>(getSuperClassUnionType(containingClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Maybe<PyType>();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType getSuperCallTypeForArguments(TypeEvalContext context, PyClass firstClass, PyExpression second_arg) {
|
||||
// check 2nd argument, too; it should be an instance
|
||||
if (second_arg != null) {
|
||||
PyType second_type = context.getType(second_arg);
|
||||
if (second_type instanceof PyClassType) {
|
||||
// imitate isinstance(second_arg, possible_class)
|
||||
PyClass secondClass = ((PyClassType)second_type).getPyClass();
|
||||
if (CompletionUtil.getOriginalOrSelf(firstClass) == secondClass) {
|
||||
return getSuperClassUnionType(firstClass);
|
||||
}
|
||||
if (secondClass.isSubclass(firstClass)) {
|
||||
final Iterator<PyClass> iterator = firstClass.iterateAncestorClasses().iterator();
|
||||
if (iterator.hasNext()) {
|
||||
return new PyClassTypeImpl(iterator.next(), false); // super(Foo, self) has type of Foo, modulo __get__()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType getSuperClassUnionType(@NotNull PyClass pyClass) {
|
||||
// TODO: this is closer to being correct than simply taking first superclass type but still not entirely correct;
|
||||
// super can also delegate to sibling types
|
||||
// TODO handle __mro__ here
|
||||
final PyClass[] supers = pyClass.getSuperClasses();
|
||||
if (supers.length > 0) {
|
||||
if (supers.length == 1) {
|
||||
return new PyClassTypeImpl(supers[0], false);
|
||||
}
|
||||
List<PyType> superTypes = new ArrayList<PyType>();
|
||||
for (PyClass aSuper : supers) {
|
||||
superTypes.add(new PyClassTypeImpl(aSuper, false));
|
||||
}
|
||||
return PyUnionType.union(superTypes);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,14 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.codeInsight.completion.CompletionUtil;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.ResolveResult;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.toolbox.Maybe;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -97,194 +88,6 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
|
||||
}
|
||||
|
||||
public PyType getType(@NotNull TypeEvalContext context) {
|
||||
if (!TypeEvalStack.mayEvaluate(this)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
PyExpression callee = getCallee();
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
// hardwired special cases
|
||||
if (PyNames.SUPER.equals(callee.getText())) {
|
||||
final Maybe<PyType> superCallType = getSuperCallType(callee, context);
|
||||
if (superCallType.isDefined()) {
|
||||
return superCallType.value();
|
||||
}
|
||||
}
|
||||
if ("type".equals(callee.getText())) {
|
||||
final PyExpression[] args = getArguments();
|
||||
if (args.length == 1) {
|
||||
final PyExpression arg = args[0];
|
||||
final PyType argType = arg.getType(context);
|
||||
if (argType instanceof PyClassType) {
|
||||
final PyClassType classType = (PyClassType)argType;
|
||||
if (!classType.isDefinition()) {
|
||||
final PyClass cls = classType.getPyClass();
|
||||
return cls.getType(context);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
// normal cases
|
||||
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context);
|
||||
ResolveResult[] targets = ((PyReferenceExpression)callee).getReference(resolveContext).multiResolve(false);
|
||||
if (targets.length > 0) {
|
||||
PsiElement target = targets[0].getElement();
|
||||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
PyClass cls = null;
|
||||
PyFunction init = null;
|
||||
if (target instanceof PyClass) {
|
||||
cls = (PyClass)target;
|
||||
init = cls.findInitOrNew(true);
|
||||
}
|
||||
else if (target instanceof PyFunction) {
|
||||
final PyFunction f = (PyFunction)target;
|
||||
if (PyNames.INIT.equals(f.getName())) {
|
||||
init = f;
|
||||
cls = f.getContainingClass();
|
||||
}
|
||||
}
|
||||
if (init != null) {
|
||||
final PyType t = init.getReturnType(context, (PyReferenceExpression)callee);
|
||||
if (cls != null) {
|
||||
if (init.getContainingClass() != cls) {
|
||||
if (t instanceof PyCollectionType) {
|
||||
final PyType elementType = ((PyCollectionType)t).getElementType(context);
|
||||
return new PyCollectionTypeImpl(cls, false, elementType);
|
||||
}
|
||||
return new PyClassTypeImpl(cls, false);
|
||||
}
|
||||
}
|
||||
if (t != null && !(t instanceof PyNoneType)) {
|
||||
if (t instanceof PyTypeReference) {
|
||||
PyType resolved = ((PyTypeReference)t).resolve(callee, context);
|
||||
if (resolved == null && cls != null) {
|
||||
return PyUnionType.createWeakType(new PyClassTypeImpl(cls, false));
|
||||
}
|
||||
}
|
||||
return t;
|
||||
}
|
||||
}
|
||||
if (cls != null) {
|
||||
return new PyClassTypeImpl(cls, false);
|
||||
}
|
||||
final PyType providedType = PyReferenceExpressionImpl.getReferenceTypeFromProviders(target, context, this);
|
||||
if (providedType != null) {
|
||||
if (providedType instanceof PyClassType) {
|
||||
return ((PyClassType)providedType).toInstance();
|
||||
}
|
||||
return providedType;
|
||||
}
|
||||
if (target instanceof Callable) {
|
||||
final Callable callable = (Callable)target;
|
||||
PyType returnType = callable.getReturnType(context, (PyReferenceExpression)callee);
|
||||
if (returnType != null) {
|
||||
return returnType;
|
||||
}
|
||||
return new PyReturnTypeReference(callable);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (callee == null) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
final PyType type = context.getType(callee);
|
||||
if (type instanceof PyCallableType) {
|
||||
final PyQualifiedExpression callSite = callee instanceof PyQualifiedExpression ? (PyQualifiedExpression)callee : null;
|
||||
return ((PyCallableType) type).getCallType(context, callSite);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
TypeEvalStack.evaluated(this);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Maybe<PyType> getSuperCallType(PyExpression callee, TypeEvalContext context) {
|
||||
PsiElement must_be_super_init = ((PyReferenceExpression)callee).getReference().resolve();
|
||||
if (must_be_super_init instanceof PyFunction) {
|
||||
PyClass must_be_super = ((PyFunction)must_be_super_init).getContainingClass();
|
||||
if (must_be_super == PyBuiltinCache.getInstance(this).getClass(PyNames.SUPER)) {
|
||||
PyArgumentList arglist = getArgumentList();
|
||||
if (arglist != null) {
|
||||
final PyClass containingClass = PsiTreeUtil.getParentOfType(this, PyClass.class);
|
||||
PyExpression[] args = arglist.getArguments();
|
||||
if (args.length > 1) {
|
||||
PyExpression first_arg = args[0];
|
||||
if (first_arg instanceof PyReferenceExpression) {
|
||||
final PyReferenceExpression firstArgRef = (PyReferenceExpression)first_arg;
|
||||
final PyExpression qualifier = firstArgRef.getQualifier();
|
||||
if (qualifier != null && PyNames.CLASS.equals(firstArgRef.getReferencedName())) {
|
||||
final PsiReference qRef = qualifier.getReference();
|
||||
final PsiElement element = qRef == null ? null : qRef.resolve();
|
||||
if (element instanceof PyParameter) {
|
||||
final PyParameterList parameterList = PsiTreeUtil.getParentOfType(element, PyParameterList.class);
|
||||
if (parameterList != null && element == parameterList.getParameters()[0]) {
|
||||
return new Maybe<PyType>(getSuperCallType(context, containingClass, args[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiElement possible_class = firstArgRef.getReference().resolve();
|
||||
if (possible_class instanceof PyClass && ((PyClass)possible_class).isNewStyleClass()) {
|
||||
final PyClass first_class = (PyClass)possible_class;
|
||||
return new Maybe<PyType>(getSuperCallType(context, first_class, args[1]));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (((PyFile)getContainingFile()).getLanguageLevel().isPy3K() && containingClass != null) {
|
||||
return new Maybe<PyType>(getSuperClassUnionType(containingClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Maybe<PyType>();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType getSuperCallType(TypeEvalContext context, PyClass firstClass, PyExpression second_arg) {
|
||||
// check 2nd argument, too; it should be an instance
|
||||
if (second_arg != null) {
|
||||
PyType second_type = context.getType(second_arg);
|
||||
if (second_type instanceof PyClassType) {
|
||||
// imitate isinstance(second_arg, possible_class)
|
||||
PyClass secondClass = ((PyClassType)second_type).getPyClass();
|
||||
if (CompletionUtil.getOriginalOrSelf(firstClass) == secondClass) {
|
||||
return getSuperClassUnionType(firstClass);
|
||||
}
|
||||
if (secondClass.isSubclass(firstClass)) {
|
||||
final Iterator<PyClass> iterator = firstClass.iterateAncestorClasses().iterator();
|
||||
if (iterator.hasNext()) {
|
||||
return new PyClassTypeImpl(iterator.next(), false); // super(Foo, self) has type of Foo, modulo __get__()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyType getSuperClassUnionType(@NotNull PyClass pyClass) {
|
||||
// TODO: this is closer to being correct than simply taking first superclass type but still not entirely correct;
|
||||
// super can also delegate to sibling types
|
||||
// TODO handle __mro__ here
|
||||
final PyClass[] supers = pyClass.getSuperClasses();
|
||||
if (supers.length > 0) {
|
||||
if (supers.length == 1) {
|
||||
return new PyClassTypeImpl(supers[0], false);
|
||||
}
|
||||
List<PyType> superTypes = new ArrayList<PyType>();
|
||||
for (PyClass aSuper : supers) {
|
||||
superTypes.add(new PyClassTypeImpl(aSuper, false));
|
||||
}
|
||||
return PyUnionType.union(superTypes);
|
||||
}
|
||||
return null;
|
||||
return PyCallExpressionHelper.getCallType(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,8 +166,7 @@ public class PyDecoratorImpl extends StubBasedPsiElementBase<PyDecoratorStub> im
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: create a custom version of public PyType getType()
|
||||
public PyType getType(@NotNull TypeEvalContext context) {
|
||||
return null; //To change body of implemented methods use File | Settings | File Templates.
|
||||
return PyCallExpressionHelper.getCallType(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.types.PyFunctionType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -23,7 +24,7 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp
|
||||
}
|
||||
|
||||
public PyType getType(@NotNull TypeEvalContext context) {
|
||||
return null;
|
||||
return new PyFunctionType(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
package com.jetbrains.python.psi.types;
|
||||
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.python.psi.AccessDirection;
|
||||
import com.jetbrains.python.psi.PyExpression;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyQualifiedExpression;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.RatedResolveResult;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -14,23 +11,21 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Type of a particular function that is represented as a {@link PyFunction} in the PSI tree.
|
||||
*
|
||||
* TODO: Maybe this class should extend PyClassType and have a Python mock class for <code>type 'function'</code> in the __builtins__.
|
||||
* Type of a particular function that is represented as a {@link Callable} in the PSI tree.
|
||||
*
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyFunctionType implements PyCallableType {
|
||||
@NotNull private final PyFunction myFunction;
|
||||
@NotNull private final Callable myCallable;
|
||||
|
||||
public PyFunctionType(@NotNull PyFunction function) {
|
||||
myFunction = function;
|
||||
public PyFunctionType(@NotNull Callable callable) {
|
||||
myCallable = callable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PyType getCallType(@NotNull TypeEvalContext context, @Nullable PyQualifiedExpression callSite) {
|
||||
return myFunction.getReturnType(context, callSite);
|
||||
return myCallable.getReturnType(context, callSite);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,7 +56,7 @@ public class PyFunctionType implements PyCallableType {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PyFunction getFunction() {
|
||||
return myFunction;
|
||||
public Callable getCallable() {
|
||||
return myCallable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ public class PyTypeChecker {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<PyGenericType, PyType> collectCallGenerics(@NotNull PyFunction function, @Nullable PyExpression receiver,
|
||||
public static Map<PyGenericType, PyType> collectCallGenerics(@NotNull Callable callable, @Nullable PyExpression receiver,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final Map<PyGenericType, PyType> substitutions = new LinkedHashMap<PyGenericType, PyType>();
|
||||
// Collect generic params of object type
|
||||
@@ -279,7 +279,7 @@ public class PyTypeChecker {
|
||||
for (PyGenericType t : generics) {
|
||||
substitutions.put(t, t);
|
||||
}
|
||||
final PyClass cls = function.getContainingClass();
|
||||
final PyClass cls = (callable instanceof PyFunction) ? ((PyFunction)callable).getContainingClass() : null;
|
||||
if (cls != null) {
|
||||
final PyFunction init = cls.findInitOrNew(true);
|
||||
// Unify generics in constructor
|
||||
@@ -346,17 +346,17 @@ public class PyTypeChecker {
|
||||
if (!(type instanceof PyFunctionType)) {
|
||||
return null;
|
||||
}
|
||||
final PyFunction function = ((PyFunctionType)type).getFunction();
|
||||
final Callable callable = ((PyFunctionType)type).getCallable();
|
||||
final boolean isRight = PyNames.isRightOperatorName(typedElement.getName());
|
||||
final PyExpression arg = isRight ? expr.getLeftExpression() : expr.getRightExpression();
|
||||
final PyExpression receiver = isRight ? expr.getRightExpression() : expr.getLeftExpression();
|
||||
final PyParameter[] parameters = function.getParameterList().getParameters();
|
||||
final PyParameter[] parameters = callable.getParameterList().getParameters();
|
||||
if (parameters.length >= 2) {
|
||||
final PyNamedParameter param = parameters[1].getAsNamed();
|
||||
if (arg != null && param != null) {
|
||||
final Map<PyExpression, PyNamedParameter> arguments = new LinkedHashMap<PyExpression, PyNamedParameter>();
|
||||
arguments.put(arg, param);
|
||||
final AnalyzeCallResults results = new AnalyzeCallResults(function, receiver, arguments);
|
||||
final AnalyzeCallResults results = new AnalyzeCallResults(callable, receiver, arguments);
|
||||
if (firstResults == null) {
|
||||
firstResults = results;
|
||||
}
|
||||
@@ -382,8 +382,8 @@ public class PyTypeChecker {
|
||||
if (resolved instanceof PyTypedElement) {
|
||||
final PyType type = ((PyTypedElement)resolved).getType(context);
|
||||
if (type instanceof PyFunctionType) {
|
||||
final PyFunction function = ((PyFunctionType)type).getFunction();
|
||||
final PyParameter[] parameters = function.getParameterList().getParameters();
|
||||
final Callable callable = ((PyFunctionType)type).getCallable();
|
||||
final PyParameter[] parameters = callable.getParameterList().getParameters();
|
||||
if (parameters.length == 2) {
|
||||
final PyNamedParameter param = parameters[1].getAsNamed();
|
||||
if (param != null) {
|
||||
@@ -391,7 +391,7 @@ public class PyTypeChecker {
|
||||
final PyExpression arg = expr.getIndexExpression();
|
||||
if (arg != null) {
|
||||
arguments.put(arg, param);
|
||||
return new AnalyzeCallResults(function, expr.getOperand(), arguments);
|
||||
return new AnalyzeCallResults(callable, expr.getOperand(), arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -423,20 +423,20 @@ public class PyTypeChecker {
|
||||
}
|
||||
|
||||
public static class AnalyzeCallResults {
|
||||
@NotNull private final PyFunction myFunction;
|
||||
@NotNull private final Callable myCallable;
|
||||
@Nullable private final PyExpression myReceiver;
|
||||
@NotNull private final Map<PyExpression, PyNamedParameter> myArguments;
|
||||
|
||||
public AnalyzeCallResults(@NotNull PyFunction function, @Nullable PyExpression receiver,
|
||||
public AnalyzeCallResults(@NotNull Callable callable, @Nullable PyExpression receiver,
|
||||
@NotNull Map<PyExpression, PyNamedParameter> arguments) {
|
||||
myFunction = function;
|
||||
myCallable = callable;
|
||||
myReceiver = receiver;
|
||||
myArguments = arguments;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PyFunction getFunction() {
|
||||
return myFunction;
|
||||
public Callable getCallable() {
|
||||
return myCallable;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkModificator;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.remotesdk.RemoteCredentials;
|
||||
import com.intellij.remotesdk.RemoteInterpreterException;
|
||||
import com.intellij.remotesdk.RemoteSdkData;
|
||||
import com.intellij.remotesdk.RemoteSshProcess;
|
||||
|
||||
@@ -6,9 +6,9 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.remotesdk.RemoteCredentials;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.remotesdk.RemoteSdkData;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -204,6 +204,6 @@ public class PySdkUtil {
|
||||
}
|
||||
|
||||
public static boolean isRemote(@Nullable Sdk sdk) {
|
||||
return sdk != null && sdk.getSdkAdditionalData() instanceof RemoteSdkData;
|
||||
return sdk != null && sdk.getSdkAdditionalData() instanceof RemoteCredentials;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
class D(object):
|
||||
def __init__(self, attribute, value):
|
||||
pass
|
||||
|
||||
def __call__(self, cls):
|
||||
return cls
|
||||
|
||||
|
||||
@D("value", 42)
|
||||
class C(object):
|
||||
pass
|
||||
|
||||
|
||||
a = C()
|
||||
print(a.value)
|
||||
@@ -0,0 +1,23 @@
|
||||
import float
|
||||
from foo import float
|
||||
from bar import baz as <warning descr="Shadows a built-in with the same name">float</warning>
|
||||
|
||||
def test1(x, <warning descr="Shadows a built-in with the same name">len</warning>, <warning descr="Shadows a built-in with the same name">file</warning>=None):
|
||||
foo = 2
|
||||
<warning descr="Shadows a built-in with the same name">list</warning> = []
|
||||
for <warning descr="Shadows a built-in with the same name">int</warning> in range(10):
|
||||
print(int)
|
||||
<warning descr="Shadows a built-in with the same name">range</warning> = []
|
||||
return [int for <warning descr="Shadows a built-in with the same name">int</warning> in range(10)]
|
||||
|
||||
|
||||
def <warning descr="Shadows a built-in with the same name">list</warning>():
|
||||
pass
|
||||
|
||||
|
||||
class <warning descr="Shadows a built-in with the same name">list</warning>(object):
|
||||
def foo(self):
|
||||
pass
|
||||
|
||||
def list(self):
|
||||
pass
|
||||
@@ -306,4 +306,9 @@ public class PythonInspectionsTest extends PyTestCase {
|
||||
public void testPyDecoratorInspection() { //PY-3348
|
||||
doHighlightingTest(PyDecoratorInspection.class);
|
||||
}
|
||||
|
||||
// PY-5807
|
||||
public void testPyShadowingBuiltinsInspection() {
|
||||
doHighlightingTest(PyShadowingBuiltinsInspection.class);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -59,6 +59,11 @@ public class PyCallingNonCallableInspectionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-5905
|
||||
public void testCallableClassDecorator() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
setLanguageLevel(LanguageLevel.PYTHON27);
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user