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:
@@ -35,7 +35,12 @@ public class RootFileReferenceSet extends FileReferenceSet {
|
||||
}
|
||||
|
||||
public boolean isAbsolutePathReference() {
|
||||
return FileUtil.isAbsolute(getPathString());
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
return FileUtil.isAbsolute(getPathString());
|
||||
}
|
||||
else {
|
||||
return super.isAbsolutePathReference();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<lang.substitutor language="TEXT" implementationClass="com.jetbrains.rest.RestLanguageSubstitutor"/>
|
||||
|
||||
<languageInjector implementation="com.jetbrains.rest.PyRestDocstringLanguageInjector"/>
|
||||
<completion.contributor language="ReST" implementationClass="com.jetbrains.rest.completion.SphinxDirectiveCompletionContributor"/>
|
||||
</extensions>
|
||||
|
||||
<actions>
|
||||
|
||||
@@ -8,11 +8,13 @@ import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.jetbrains.python.packaging.PyExternalProcessException;
|
||||
import com.jetbrains.python.packaging.PyPackage;
|
||||
import com.jetbrains.python.packaging.PyPackageManager;
|
||||
import com.jetbrains.python.packaging.PyPackageManagerImpl;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* User : catherine
|
||||
@@ -36,7 +38,7 @@ public class RestPythonUtil {
|
||||
PyPackageManagerImpl manager = (PyPackageManagerImpl)PyPackageManager.getInstance(sdk);
|
||||
try {
|
||||
final PyPackage sphinx = manager.findPackage("Sphinx");
|
||||
String quickStart = RestUtil.findQuickStart(sdk.getHomePath());
|
||||
String quickStart = findQuickStart(sdk);
|
||||
presentation.setEnabled(sphinx != null && quickStart != null);
|
||||
}
|
||||
catch (PyExternalProcessException ignored) {
|
||||
@@ -47,4 +49,9 @@ public class RestPythonUtil {
|
||||
return presentation;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String findQuickStart(final Sdk sdkHome) {
|
||||
final String runnerName = "sphinx-quickstart" + (SystemInfo.isWindows ? ".exe" : "");
|
||||
return PythonSdkType.getExecutablePath(sdkHome, runnerName);
|
||||
}
|
||||
}
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package com.jetbrains.rest.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.patterns.PsiElementPattern;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
import com.jetbrains.rest.RestPythonUtil;
|
||||
import com.jetbrains.rest.RestTokenTypes;
|
||||
import com.jetbrains.rest.RestUtil;
|
||||
import com.jetbrains.rest.psi.RestReferenceTarget;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.patterns.PlatformPatterns.psiElement;
|
||||
import static com.intellij.patterns.StandardPatterns.or;
|
||||
|
||||
/**
|
||||
* User : ktisha
|
||||
*/
|
||||
public class SphinxDirectiveCompletionContributor extends CompletionContributor {
|
||||
public static final PsiElementPattern.Capture<PsiElement> DIRECTIVE_PATTERN = psiElement().afterSibling(or(psiElement().
|
||||
withElementType(RestTokenTypes.WHITESPACE).afterSibling(psiElement(RestReferenceTarget.class)),
|
||||
psiElement().withElementType(RestTokenTypes.EXPLISIT_MARKUP_START)));
|
||||
|
||||
public SphinxDirectiveCompletionContributor() {
|
||||
extend(CompletionType.BASIC, DIRECTIVE_PATTERN,
|
||||
new CompletionProvider<CompletionParameters>() {
|
||||
@Override
|
||||
protected void addCompletions(@NotNull CompletionParameters parameters,
|
||||
ProcessingContext context,
|
||||
@NotNull CompletionResultSet result) {
|
||||
Sdk sdk = ProjectRootManager.getInstance(parameters.getPosition().getProject()).getProjectSdk();
|
||||
if (sdk != null) {
|
||||
String sphinx = RestPythonUtil.findQuickStart(sdk);
|
||||
if (sphinx != null) {
|
||||
for (String tag : RestUtil.SPHINX_DIRECTIVES) {
|
||||
result.addElement(LookupElementBuilder.create(tag));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,7 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.ReSTService;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.rest.RestBundle;
|
||||
import com.jetbrains.rest.RestFile;
|
||||
import com.jetbrains.rest.RestTokenTypes;
|
||||
import com.jetbrains.rest.RestUtil;
|
||||
import com.jetbrains.rest.*;
|
||||
import com.jetbrains.rest.psi.RestDirectiveBlock;
|
||||
import com.jetbrains.rest.psi.RestRole;
|
||||
import com.jetbrains.rest.quickfixes.AddIgnoredRoleFix;
|
||||
@@ -114,7 +111,7 @@ public class RestRoleInspection extends RestInspection {
|
||||
|
||||
Sdk sdk = ProjectRootManager.getInstance(node.getProject()).getProjectSdk();
|
||||
if (sdk != null) {
|
||||
String sphinx = RestUtil.findQuickStart(sdk.getHomePath());
|
||||
String sphinx = RestPythonUtil.findQuickStart(sdk);
|
||||
if (sphinx != null) {
|
||||
if (RestUtil.SPHINX_ROLES.contains(node.getText()) || RestUtil.SPHINX_ROLES.contains(":py"+node.getText())
|
||||
|| mySphinxRoles.contains(node.getRoleName())) return;
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.jetbrains.python.run.PythonCommandLineState;
|
||||
import com.jetbrains.python.run.PythonProcessRunner;
|
||||
import com.jetbrains.python.run.PythonTracebackFilter;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import com.jetbrains.rest.RestUtil;
|
||||
import com.jetbrains.rest.RestPythonUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -169,6 +169,6 @@ public class SphinxBaseCommand {
|
||||
|
||||
@Nullable
|
||||
private static String getCommandPath(Sdk sdk) {
|
||||
return RestUtil.findQuickStart(sdk.getHomePath());
|
||||
return RestPythonUtil.findQuickStart(sdk);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection detects shadowing built-in names, such as 'len' or 'list'.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection detects shadowing names defined in outer scopes, including built-in names.
|
||||
This inspection detects shadowing names defined in outer scopes.
|
||||
</body>
|
||||
</html>
|
||||
@@ -2,11 +2,7 @@ package com.jetbrains.rest;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -16,32 +12,6 @@ import java.util.Set;
|
||||
public class RestUtil {
|
||||
private RestUtil() {}
|
||||
|
||||
@Nullable
|
||||
public static String findQuickStart(final String sdkHome) {
|
||||
final String runnerName = "sphinx-quickstart" + (SystemInfo.isWindows ? ".exe" : "");
|
||||
File binPath = new File(sdkHome);
|
||||
File binDir = binPath.getParentFile();
|
||||
if (binDir == null) return null;
|
||||
File runner = new File(binDir, runnerName);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File(binDir, "scripts"), runnerName);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File(binDir.getParentFile(), "scripts"), runnerName);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File(binDir.getParentFile(), "local"), runnerName);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File (new File(binDir.getParentFile(), "local"), "bin"), runnerName);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
|
||||
// Search in standard unix path
|
||||
runner = new File(new File("/usr", "bin"), runnerName);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File (new File("/usr", "local"), "bin"), runnerName);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] SPHINX_DIRECTIVES = new String[] {
|
||||
"module::" , "automodule::" , "autoclass::" , "toctree::" , "glossary::" , "code-block::", "versionadded::",
|
||||
"versionchanged::", "deprecated::", "seealso::", "centered::", "hlist::", "index::", "productionlist::", "highlight::",
|
||||
|
||||
@@ -2,8 +2,6 @@ package com.jetbrains.rest.completion;
|
||||
|
||||
import com.intellij.codeInsight.completion.*;
|
||||
import com.intellij.codeInsight.lookup.LookupElementBuilder;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.patterns.PsiElementPattern;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ProcessingContext;
|
||||
@@ -33,15 +31,6 @@ public class DirectiveCompletionContributor extends CompletionContributor {
|
||||
for (String tag : RestUtil.getDirectives()) {
|
||||
result.addElement(LookupElementBuilder.create(tag));
|
||||
}
|
||||
Sdk sdk = ProjectRootManager.getInstance(parameters.getPosition().getProject()).getProjectSdk();
|
||||
if (sdk != null) {
|
||||
String sphinx = RestUtil.findQuickStart(sdk.getHomePath());
|
||||
if (sphinx != null) {
|
||||
for (String tag : RestUtil.SPHINX_DIRECTIVES) {
|
||||
result.addElement(LookupElementBuilder.create(tag));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -323,7 +323,8 @@
|
||||
<localInspection language="Python" shortName="PyProtectedMemberInspection" displayName="Access to a protected member of a class" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyProtectedMemberInspection"/>
|
||||
<localInspection language="Python" shortName="PyMethodMayBeStaticInspection" displayName="Method may be static" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyMethodMayBeStaticInspection"/>
|
||||
<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="PyShadowingNamesInspection" displayName="Shadowing names" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyShadowingNamesInspection"/>
|
||||
<localInspection language="Python" shortName="PyShadowingBuiltinsInspection" displayName="Shadowing built-ins" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyShadowingBuiltinsInspection"/>
|
||||
<localInspection language="Python" shortName="PyShadowingNamesInspection" displayName="Shadowing names from outer scopes" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyShadowingNamesInspection"/>
|
||||
<localInspection language="Python" shortName="PyAbstractClassInspection" displayName="Class must implement all abstract methods" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyAbstractClassInspection"/>
|
||||
<localInspection language="Python" shortName="PyPep8NamingInspection" displayName="PEP 8 naming convention violation" groupKey="INSP.GROUP.python" enabledByDefault="true" level="INFO" implementationClass="com.jetbrains.python.inspections.PyPep8NamingInspection"/>
|
||||
|
||||
|
||||
@@ -74,8 +74,7 @@ public class PyRedeclarationInspection extends PyInspection {
|
||||
}
|
||||
|
||||
private static boolean isConditional(@NotNull PsiElement node) {
|
||||
return PsiTreeUtil.getParentOfType(node, PyIfStatement.class, PyConditionalExpression.class, PyLoopStatement.class,
|
||||
PyComprehensionElement.class, PyTryExceptStatement.class) != null;
|
||||
return PsiTreeUtil.getParentOfType(node, PyIfStatement.class, PyConditionalExpression.class, PyTryExceptStatement.class) != null;
|
||||
}
|
||||
|
||||
private static boolean isDecorated(@NotNull PyDecoratable node) {
|
||||
@@ -118,17 +117,20 @@ public class PyRedeclarationInspection extends PyInspection {
|
||||
if (rwInstruction.getAccess().isWriteAccess()) {
|
||||
final List<LocalQuickFix> quickFixes = new ArrayList<LocalQuickFix>();
|
||||
final PsiElement originalElement = rwInstruction.getElement();
|
||||
if (originalElement != null && suggestRename(element, originalElement)) {
|
||||
quickFixes.add(new PyRenameElementQuickFix());
|
||||
if (originalElement != null && originalElement != element) {
|
||||
if (suggestRename(element, originalElement)) {
|
||||
quickFixes.add(new PyRenameElementQuickFix());
|
||||
}
|
||||
final PsiElement identifier = element.getNameIdentifier();
|
||||
registerProblem(identifier != null ? identifier : element,
|
||||
PyBundle.message("INSP.redeclared.name", name),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
null,
|
||||
quickFixes.toArray(new LocalQuickFix[quickFixes.size()]));
|
||||
return ControlFlowUtil.Operation.BREAK;
|
||||
}
|
||||
final PsiElement identifier = element.getNameIdentifier();
|
||||
registerProblem(identifier != null ? identifier : element,
|
||||
PyBundle.message("INSP.redeclared.name", name),
|
||||
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
|
||||
null,
|
||||
quickFixes.toArray(new LocalQuickFix[quickFixes.size()]));
|
||||
}
|
||||
return ControlFlowUtil.Operation.BREAK;
|
||||
return ControlFlowUtil.Operation.CONTINUE;
|
||||
}
|
||||
}
|
||||
return ControlFlowUtil.Operation.NEXT;
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.ui.ListEditForm;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Warns about shadowing built-in names.
|
||||
*
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyShadowingBuiltinsInspection extends PyInspection {
|
||||
// Persistent settings
|
||||
public List<String> ignoredNames = new ArrayList<String>();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Shadowing built-ins";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
final ListEditForm form = new ListEditForm("Ignore built-ins", ignoredNames);
|
||||
return form.getContentPanel();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
|
||||
boolean isOnTheFly,
|
||||
@NotNull LocalInspectionToolSession session) {
|
||||
return new Visitor(holder, session, ignoredNames);
|
||||
}
|
||||
|
||||
private static class Visitor extends PyInspectionVisitor {
|
||||
private final Set<String> myIgnoredNames;
|
||||
|
||||
public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session, @NotNull Collection<String> ignoredNames) {
|
||||
super(holder, session);
|
||||
myIgnoredNames = ImmutableSet.copyOf(ignoredNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyClass(@NotNull PyClass node) {
|
||||
processElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyFunction(@NotNull PyFunction node) {
|
||||
processElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyNamedParameter(@NotNull PyNamedParameter node) {
|
||||
processElement(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyTargetExpression(@NotNull PyTargetExpression node) {
|
||||
if (node.getQualifier() == null) {
|
||||
processElement(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void processElement(@NotNull PsiNameIdentifierOwner element) {
|
||||
final ScopeOwner owner = ScopeUtil.getScopeOwner(element);
|
||||
if (owner instanceof PyClass) {
|
||||
return;
|
||||
}
|
||||
final String name = element.getName();
|
||||
if (name != null && !myIgnoredNames.contains(name)) {
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(element);
|
||||
final PsiElement builtin = builtinCache.getByName(name);
|
||||
if (builtin != null && !PyUtil.inSameFile(builtin, element)) {
|
||||
final PsiElement identifier = element.getNameIdentifier();
|
||||
final PsiElement problemElement = identifier != null ? identifier : element;
|
||||
registerProblem(problemElement, String.format("Shadows built-in name '%s'", name),
|
||||
ProblemHighlightType.WEAK_WARNING, null, new PyRenameElementQuickFix(), new PyIgnoreBuiltinQuickFix(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class PyIgnoreBuiltinQuickFix implements LocalQuickFix, LowPriorityAction {
|
||||
@NotNull private final String myName;
|
||||
|
||||
private PyIgnoreBuiltinQuickFix(@NotNull String name) {
|
||||
myName = name;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return getFamilyName() + " \"" + myName + "\"";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Ignore shadowed built-in name";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (element != null) {
|
||||
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile();
|
||||
profile.modifyProfile(new Consumer<ModifiableModel>() {
|
||||
@Override
|
||||
public void consume(ModifiableModel model) {
|
||||
final String toolName = PyShadowingBuiltinsInspection.class.getSimpleName();
|
||||
final PyShadowingBuiltinsInspection inspection = (PyShadowingBuiltinsInspection)model.getUnwrappedTool(toolName, element);
|
||||
if (inspection != null) {
|
||||
if (!inspection.ignoredNames.contains(myName)) {
|
||||
inspection.ignoredNames.add(myName);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,32 @@
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.intellij.codeInsight.intention.LowPriorityAction;
|
||||
import com.intellij.codeInspection.*;
|
||||
import com.intellij.codeInspection.ui.ListEditForm;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession;
|
||||
import com.intellij.codeInspection.ProblemHighlightType;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiNameIdentifierOwner;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveUtil;
|
||||
import com.jetbrains.python.psi.resolve.ResolveProcessor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Warns about shadowing names defined in outer scopes.
|
||||
*
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyShadowingNamesInspection extends PyInspection {
|
||||
// Persistent settings
|
||||
public List<String> ignoredNames = new ArrayList<String>();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Shadowing names";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
final ListEditForm form = new ListEditForm("Ignore built-ins", ignoredNames);
|
||||
return form.getContentPanel();
|
||||
return "Shadowing names from outer scopes";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -54,15 +34,12 @@ public class PyShadowingNamesInspection extends PyInspection {
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder,
|
||||
boolean isOnTheFly,
|
||||
@NotNull LocalInspectionToolSession session) {
|
||||
return new Visitor(holder, session, ignoredNames);
|
||||
return new Visitor(holder, session);
|
||||
}
|
||||
|
||||
private static class Visitor extends PyInspectionVisitor {
|
||||
private final Set<String> myIgnoredNames;
|
||||
|
||||
public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session, @NotNull Collection<String> ignoredNames) {
|
||||
public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session) {
|
||||
super(holder, session);
|
||||
myIgnoredNames = ImmutableSet.copyOf(ignoredNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,99 +69,35 @@ public class PyShadowingNamesInspection extends PyInspection {
|
||||
|
||||
private void processElement(@NotNull PsiNameIdentifierOwner element) {
|
||||
final ScopeOwner owner = ScopeUtil.getScopeOwner(element);
|
||||
// Class-level names are rarely accessed inside at the class level, usually they are accessed as attributes
|
||||
if (owner instanceof PyClass) {
|
||||
return;
|
||||
}
|
||||
final String name = element.getName();
|
||||
if (name != null && !myIgnoredNames.contains(name)) {
|
||||
if (name != null) {
|
||||
final PsiElement identifier = element.getNameIdentifier();
|
||||
final PsiElement problemElement = identifier != null ? identifier : element;
|
||||
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(element);
|
||||
final PsiElement builtin = builtinCache.getByName(name);
|
||||
if (builtin != null) {
|
||||
processBuiltin(element, name, problemElement, builtin);
|
||||
if ("_".equals(name)) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
processOuterScope(element, name, problemElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void processBuiltin(@NotNull PsiNameIdentifierOwner element,
|
||||
@NotNull String name,
|
||||
@NotNull PsiElement problemElement,
|
||||
@NotNull PsiElement builtin) {
|
||||
if (!PyUtil.inSameFile(builtin, element)) {
|
||||
registerProblem(problemElement, String.format("Shadows built-in name '%s'", name),
|
||||
ProblemHighlightType.WEAK_WARNING, null, new PyRenameElementQuickFix(),
|
||||
new PyIgnoreBuiltinQuickFix(name));
|
||||
}
|
||||
}
|
||||
|
||||
private void processOuterScope(@NotNull PsiNameIdentifierOwner element, @NotNull String name, @NotNull PsiElement problemElement) {
|
||||
if ("_".equals(name)) {
|
||||
return;
|
||||
}
|
||||
final ScopeOwner owner = ScopeUtil.getScopeOwner(element);
|
||||
if (owner != null) {
|
||||
final ScopeOwner nextOwner = ScopeUtil.getScopeOwner(owner);
|
||||
if (nextOwner != null) {
|
||||
final ResolveProcessor processor = new ResolveProcessor(name);
|
||||
PyResolveUtil.scopeCrawlUp(processor, nextOwner, null, name, null);
|
||||
final PsiElement resolved = processor.getResult();
|
||||
if (resolved != null) {
|
||||
final PyComprehensionElement comprehension = PsiTreeUtil.getParentOfType(resolved, PyComprehensionElement.class);
|
||||
if (comprehension != null && PyUtil.isOwnScopeComprehension(comprehension)) {
|
||||
return;
|
||||
}
|
||||
final Scope scope = ControlFlowCache.getScope(owner);
|
||||
if (scope.isGlobal(name) || scope.isNonlocal(name)) {
|
||||
return;
|
||||
}
|
||||
registerProblem(problemElement, String.format("Shadows name '%s' from outer scope", name),
|
||||
ProblemHighlightType.WEAK_WARNING, null, new PyRenameElementQuickFix());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class PyIgnoreBuiltinQuickFix implements LocalQuickFix, LowPriorityAction {
|
||||
@NotNull private final String myName;
|
||||
|
||||
private PyIgnoreBuiltinQuickFix(@NotNull String name) {
|
||||
myName = name;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return getFamilyName() + " \"" + myName + "\"";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return "Ignore shadowed built-in name";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
final PsiElement element = descriptor.getPsiElement();
|
||||
if (element != null) {
|
||||
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile();
|
||||
profile.modifyProfile(new Consumer<ModifiableModel>() {
|
||||
@Override
|
||||
public void consume(ModifiableModel model) {
|
||||
final String toolName = PyShadowingNamesInspection.class.getSimpleName();
|
||||
final PyShadowingNamesInspection inspection = (PyShadowingNamesInspection)model.getUnwrappedTool(toolName, element);
|
||||
if (inspection != null) {
|
||||
if (!inspection.ignoredNames.contains(myName)) {
|
||||
inspection.ignoredNames.add(myName);
|
||||
}
|
||||
if (owner != null) {
|
||||
final ScopeOwner nextOwner = ScopeUtil.getScopeOwner(owner);
|
||||
if (nextOwner != null) {
|
||||
final ResolveProcessor processor = new ResolveProcessor(name);
|
||||
PyResolveUtil.scopeCrawlUp(processor, nextOwner, null, name, null);
|
||||
final PsiElement resolved = processor.getResult();
|
||||
if (resolved != null) {
|
||||
final PyComprehensionElement comprehension = PsiTreeUtil.getParentOfType(resolved, PyComprehensionElement.class);
|
||||
if (comprehension != null && PyUtil.isOwnScopeComprehension(comprehension)) {
|
||||
return;
|
||||
}
|
||||
final Scope scope = ControlFlowCache.getScope(owner);
|
||||
if (scope.isGlobal(name) || scope.isNonlocal(name)) {
|
||||
return;
|
||||
}
|
||||
registerProblem(problemElement, String.format("Shadows name '%s' from outer scope", name),
|
||||
ProblemHighlightType.WEAK_WARNING, null, new PyRenameElementQuickFix());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,8 +299,12 @@ public abstract class PythonCommandLineState extends CommandLineState {
|
||||
}
|
||||
|
||||
private static void addIfNeeded(@NotNull final VirtualFile file, @NotNull final Collection<String> pathList) {
|
||||
addIfNeeded(pathList, file.getPath());
|
||||
}
|
||||
|
||||
protected static void addIfNeeded(Collection<String> pathList, String path) {
|
||||
final Set<String> vals = Sets.newHashSet(pathList);
|
||||
final String filePath = FileUtil.toSystemDependentName(file.getPath());
|
||||
final String filePath = FileUtil.toSystemDependentName(path);
|
||||
if (!vals.contains(filePath)) {
|
||||
pathList.add(filePath);
|
||||
}
|
||||
|
||||
@@ -867,6 +867,37 @@ public class PythonSdkType extends SdkType {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getExecutablePath(@NotNull final String homeDirectory, @NotNull String name) {
|
||||
File binPath = new File(homeDirectory);
|
||||
File binDir = binPath.getParentFile();
|
||||
if (binDir == null) return null;
|
||||
File runner = new File(binDir, name);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File(binDir, "Scripts"), name);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File(binDir.getParentFile(), "Scripts"), name);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File(binDir.getParentFile(), "local"), name);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File(new File(binDir.getParentFile(), "local"), "bin"), name);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
|
||||
// Search in standard unix path
|
||||
runner = new File(new File("/usr", "bin"), name);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
runner = new File(new File (new File("/usr", "local"), "bin"), name);
|
||||
if (runner.exists()) return LocalFileSystem.getInstance().extractPresentableUrl(runner.getPath());
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getExecutablePath(@NotNull final Sdk sdk, @NotNull String name) {
|
||||
final String homeDirectory = sdk.getHomePath();
|
||||
if (homeDirectory == null) return null;
|
||||
return getExecutablePath(homeDirectory, name);
|
||||
}
|
||||
|
||||
private static String[] getBinaryNames() {
|
||||
if (SystemInfo.isUnix) {
|
||||
return UNIX_BINARY_NAMES;
|
||||
|
||||
+6
-4
@@ -4,6 +4,7 @@ import com.intellij.execution.ExecutionException;
|
||||
import com.intellij.execution.configurations.GeneralCommandLine;
|
||||
import com.intellij.execution.configurations.ParamsGroup;
|
||||
import com.intellij.execution.runners.ExecutionEnvironment;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.testing.AbstractPythonTestRunConfiguration;
|
||||
@@ -37,15 +38,16 @@ public class PythonUnitTestCommandLineState extends
|
||||
protected List<String> getTestSpecs() {
|
||||
List<String> specs = new ArrayList<String>();
|
||||
|
||||
final String scriptName = FileUtil.toSystemDependentName(myConfig.getScriptName());
|
||||
switch (myConfig.getTestType()) {
|
||||
case TEST_SCRIPT:
|
||||
specs.add(myConfig.getScriptName());
|
||||
specs.add(scriptName);
|
||||
break;
|
||||
case TEST_CLASS:
|
||||
specs.add(myConfig.getScriptName() + "::" + myConfig.getClassName());
|
||||
specs.add(scriptName + "::" + myConfig.getClassName());
|
||||
break;
|
||||
case TEST_METHOD:
|
||||
specs.add(myConfig.getScriptName() + "::" + myConfig.getClassName() + "::" + myConfig.getMethodName());
|
||||
specs.add(scriptName + "::" + myConfig.getClassName() + "::" + myConfig.getMethodName());
|
||||
break;
|
||||
case TEST_FOLDER:
|
||||
if (!StringUtil.isEmpty(myConfig.getPattern()) && myConfig.usePattern()) {
|
||||
@@ -56,7 +58,7 @@ public class PythonUnitTestCommandLineState extends
|
||||
}
|
||||
break;
|
||||
case TEST_FUNCTION:
|
||||
specs.add(myConfig.getScriptName() + "::::" + myConfig.getMethodName());
|
||||
specs.add(scriptName + "::::" + myConfig.getMethodName());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown test type: " + myConfig.getTestType());
|
||||
|
||||
@@ -43,7 +43,7 @@ def test_local_variable():
|
||||
x = 2
|
||||
|
||||
|
||||
def conditional(c):
|
||||
def test_conditional(c):
|
||||
def foo():
|
||||
pass
|
||||
|
||||
@@ -51,12 +51,39 @@ def conditional(c):
|
||||
def foo():
|
||||
pass
|
||||
|
||||
while c:
|
||||
def foo():
|
||||
pass
|
||||
|
||||
try:
|
||||
def foo():
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def test_while_loop(c):
|
||||
def foo():
|
||||
pass
|
||||
|
||||
while c:
|
||||
def <warning descr="Redeclared 'foo' defined above without usage">foo</warning>():
|
||||
pass
|
||||
|
||||
|
||||
class TestForLoopNoRedeclaraion:
|
||||
for foo in [1, 2, 3]:
|
||||
x = 1
|
||||
|
||||
|
||||
class TestForLoopTarget:
|
||||
def foo():
|
||||
pass
|
||||
|
||||
for <warning descr="Redeclared 'foo' defined above without usage">foo</warning> in [1, 2, 3]:
|
||||
x = 1
|
||||
|
||||
|
||||
class TestForLoopBody:
|
||||
def foo():
|
||||
pass
|
||||
|
||||
for _ in [1, 2, 3]:
|
||||
def <warning descr="Redeclared 'foo' defined above without usage">foo</warning>():
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
def test_import_builtin_names():
|
||||
import float
|
||||
from foo import float
|
||||
from bar import baz as <weak_warning descr="Shadows built-in name 'float'">float</weak_warning>
|
||||
|
||||
|
||||
def test_builtin_function_parameters():
|
||||
def test1(x, _, <weak_warning descr="Shadows built-in name 'len'">len</weak_warning>, <weak_warning descr="Shadows built-in name 'file'">file</weak_warning>=None):
|
||||
pass
|
||||
|
||||
|
||||
def test_builtin_function_name():
|
||||
def <weak_warning descr="Shadows built-in name 'list'">list</weak_warning>():
|
||||
pass
|
||||
|
||||
|
||||
def test_builtin_assignment_targets():
|
||||
foo = 2
|
||||
<weak_warning descr="Shadows built-in name 'list'">list</weak_warning> = []
|
||||
for <weak_warning descr="Shadows built-in name 'int'">int</weak_warning> in range(10):
|
||||
print(int)
|
||||
<weak_warning descr="Shadows built-in name 'range'">range</weak_warning> = []
|
||||
<weak_warning descr="Shadows built-in name 'list'">list</weak_warning>, _ = (1, 2)
|
||||
return [int for <weak_warning descr="Shadows built-in name 'int'">int</weak_warning> in range(10)]
|
||||
|
||||
|
||||
def test_builtin_class_name():
|
||||
class <weak_warning descr="Shadows built-in name 'list'">list</weak_warning>(object):
|
||||
pass
|
||||
|
||||
|
||||
def test_builtin_method_name():
|
||||
class C:
|
||||
def list(self):
|
||||
pass
|
||||
|
||||
|
||||
# PY-8646
|
||||
def test_builtin_qualified_name():
|
||||
test1.range = float()
|
||||
|
||||
class C:
|
||||
def foo(self):
|
||||
self.list = []
|
||||
|
||||
|
||||
# PY-10164
|
||||
def test_builtin_class_attribute():
|
||||
class C:
|
||||
id = 1
|
||||
@@ -1,58 +1,6 @@
|
||||
global_foo = 1
|
||||
|
||||
|
||||
def test_import_builtin_names():
|
||||
import float
|
||||
from foo import float
|
||||
from bar import baz as <weak_warning descr="Shadows built-in name 'float'">float</weak_warning>
|
||||
|
||||
|
||||
def test_builtin_function_parameters():
|
||||
def test1(x, _, <weak_warning descr="Shadows built-in name 'len'">len</weak_warning>, <weak_warning descr="Shadows built-in name 'file'">file</weak_warning>=None):
|
||||
pass
|
||||
|
||||
|
||||
def test_builtin_function_name():
|
||||
def <weak_warning descr="Shadows built-in name 'list'">list</weak_warning>():
|
||||
pass
|
||||
|
||||
|
||||
def test_builtin_assignment_targets():
|
||||
foo = 2
|
||||
<weak_warning descr="Shadows built-in name 'list'">list</weak_warning> = []
|
||||
for <weak_warning descr="Shadows built-in name 'int'">int</weak_warning> in range(10):
|
||||
print(int)
|
||||
<weak_warning descr="Shadows built-in name 'range'">range</weak_warning> = []
|
||||
<weak_warning descr="Shadows built-in name 'list'">list</weak_warning>, _ = (1, 2)
|
||||
return [int for <weak_warning descr="Shadows built-in name 'int'">int</weak_warning> in range(10)]
|
||||
|
||||
|
||||
def test_builtin_class_name():
|
||||
class <weak_warning descr="Shadows built-in name 'list'">list</weak_warning>(object):
|
||||
pass
|
||||
|
||||
|
||||
def test_builtin_method_name():
|
||||
class C:
|
||||
def list(self):
|
||||
pass
|
||||
|
||||
|
||||
# PY-8646
|
||||
def test_builtin_qualified_name():
|
||||
test1.range = float()
|
||||
|
||||
class C:
|
||||
def foo(self):
|
||||
self.list = []
|
||||
|
||||
|
||||
# PY-10164
|
||||
def test_builtin_class_attribute():
|
||||
class C:
|
||||
id = 1
|
||||
|
||||
|
||||
def test_outer_function():
|
||||
foo = 1
|
||||
def bar():
|
||||
@@ -91,7 +39,7 @@ def test_outer_global():
|
||||
global_foo = 2
|
||||
|
||||
|
||||
def test_comprehensions():
|
||||
def test_outer_comprehensions():
|
||||
print(x for x in range(10))
|
||||
print([y for y in range(10)])
|
||||
def f(x, <weak_warning descr="Shadows name 'y' from outer scope">y</weak_warning>):
|
||||
|
||||
@@ -433,7 +433,7 @@ public class PyQuickFixTest extends PyTestCase {
|
||||
public void testRenameShadowingBuiltins() {
|
||||
final String fileName = "RenameShadowingBuiltins.py";
|
||||
myFixture.configureByFile(fileName);
|
||||
myFixture.enableInspections(PyShadowingNamesInspection.class);
|
||||
myFixture.enableInspections(PyShadowingBuiltinsInspection.class);
|
||||
myFixture.checkHighlighting(true, false, true);
|
||||
final IntentionAction intentionAction = myFixture.getAvailableIntention("Rename element");
|
||||
assertNotNull(intentionAction);
|
||||
@@ -445,7 +445,7 @@ public class PyQuickFixTest extends PyTestCase {
|
||||
public void testRenameFunctionShadowingBuiltins() {
|
||||
final String fileName = "RenameFunctionShadowingBuiltins.py";
|
||||
myFixture.configureByFile(fileName);
|
||||
myFixture.enableInspections(PyShadowingNamesInspection.class);
|
||||
myFixture.enableInspections(PyShadowingBuiltinsInspection.class);
|
||||
myFixture.checkHighlighting(true, false, true);
|
||||
final IntentionAction intentionAction = myFixture.getAvailableIntention("Rename element");
|
||||
assertNotNull(intentionAction);
|
||||
|
||||
@@ -295,6 +295,10 @@ public class PythonInspectionsTest extends PyTestCase {
|
||||
}
|
||||
|
||||
// PY-5807
|
||||
public void testPyShadowingBuiltinsInspection() {
|
||||
doHighlightingTest(PyShadowingBuiltinsInspection.class);
|
||||
}
|
||||
|
||||
public void testPyShadowingNamesInspection() {
|
||||
doHighlightingTest(PyShadowingNamesInspection.class);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user