diff --git a/python/openapi/src/com/jetbrains/python/psi/RootFileReferenceSet.java b/python/openapi/src/com/jetbrains/python/psi/RootFileReferenceSet.java index 62458697c662..b87484cf7be0 100644 --- a/python/openapi/src/com/jetbrains/python/psi/RootFileReferenceSet.java +++ b/python/openapi/src/com/jetbrains/python/psi/RootFileReferenceSet.java @@ -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 diff --git a/python/python-rest/resources/META-INF/python-rest-plugin.xml b/python/python-rest/resources/META-INF/python-rest-plugin.xml index 9079ba287038..82560a244d7f 100644 --- a/python/python-rest/resources/META-INF/python-rest-plugin.xml +++ b/python/python-rest/resources/META-INF/python-rest-plugin.xml @@ -15,6 +15,7 @@ + diff --git a/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java b/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java index f7834e3a826a..af8696d1b11a 100644 --- a/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java +++ b/python/python-rest/src/com/jetbrains/rest/RestPythonUtil.java @@ -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); + } } diff --git a/python/python-rest/src/com/jetbrains/rest/completion/SphinxDirectiveCompletionContributor.java b/python/python-rest/src/com/jetbrains/rest/completion/SphinxDirectiveCompletionContributor.java new file mode 100644 index 000000000000..8ab112dde9a0 --- /dev/null +++ b/python/python-rest/src/com/jetbrains/rest/completion/SphinxDirectiveCompletionContributor.java @@ -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 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() { + @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)); + } + } + } + } + } + ); + } +} diff --git a/python/python-rest/src/com/jetbrains/rest/inspections/RestRoleInspection.java b/python/python-rest/src/com/jetbrains/rest/inspections/RestRoleInspection.java index f7c89927a729..1663aa9cb2ad 100644 --- a/python/python-rest/src/com/jetbrains/rest/inspections/RestRoleInspection.java +++ b/python/python-rest/src/com/jetbrains/rest/inspections/RestRoleInspection.java @@ -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; diff --git a/python/python-rest/src/com/jetbrains/rest/sphinx/SphinxBaseCommand.java b/python/python-rest/src/com/jetbrains/rest/sphinx/SphinxBaseCommand.java index 7e0d79089cd3..1ef90a635916 100644 --- a/python/python-rest/src/com/jetbrains/rest/sphinx/SphinxBaseCommand.java +++ b/python/python-rest/src/com/jetbrains/rest/sphinx/SphinxBaseCommand.java @@ -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); } } diff --git a/python/resources/inspectionDescriptions/PyShadowingBuiltinsInspection.html b/python/resources/inspectionDescriptions/PyShadowingBuiltinsInspection.html new file mode 100644 index 000000000000..f25c27847c4f --- /dev/null +++ b/python/resources/inspectionDescriptions/PyShadowingBuiltinsInspection.html @@ -0,0 +1,5 @@ + + +This inspection detects shadowing built-in names, such as 'len' or 'list'. + + \ No newline at end of file diff --git a/python/resources/inspectionDescriptions/PyShadowingNamesInspection.html b/python/resources/inspectionDescriptions/PyShadowingNamesInspection.html index 3f32f07ca884..dabfaf0b94a1 100644 --- a/python/resources/inspectionDescriptions/PyShadowingNamesInspection.html +++ b/python/resources/inspectionDescriptions/PyShadowingNamesInspection.html @@ -1,5 +1,5 @@ -This inspection detects shadowing names defined in outer scopes, including built-in names. +This inspection detects shadowing names defined in outer scopes. \ No newline at end of file diff --git a/python/rest/src/com/jetbrains/rest/RestUtil.java b/python/rest/src/com/jetbrains/rest/RestUtil.java index 1ef5b1a8511b..7a177693112f 100644 --- a/python/rest/src/com/jetbrains/rest/RestUtil.java +++ b/python/rest/src/com/jetbrains/rest/RestUtil.java @@ -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::", diff --git a/python/rest/src/com/jetbrains/rest/completion/DirectiveCompletionContributor.java b/python/rest/src/com/jetbrains/rest/completion/DirectiveCompletionContributor.java index 97df88310566..22e127cc0659 100644 --- a/python/rest/src/com/jetbrains/rest/completion/DirectiveCompletionContributor.java +++ b/python/rest/src/com/jetbrains/rest/completion/DirectiveCompletionContributor.java @@ -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)); - } - } - } } } ); diff --git a/python/src/META-INF/python-plugin-core.xml b/python/src/META-INF/python-plugin-core.xml index 12f60d8d6362..8858ffbac17e 100644 --- a/python/src/META-INF/python-plugin-core.xml +++ b/python/src/META-INF/python-plugin-core.xml @@ -323,7 +323,8 @@ - + + diff --git a/python/src/com/jetbrains/python/inspections/PyRedeclarationInspection.java b/python/src/com/jetbrains/python/inspections/PyRedeclarationInspection.java index 6b1268c12e48..007ee734bf61 100644 --- a/python/src/com/jetbrains/python/inspections/PyRedeclarationInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyRedeclarationInspection.java @@ -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 quickFixes = new ArrayList(); 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; diff --git a/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java b/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java new file mode 100644 index 000000000000..94b06b54e474 --- /dev/null +++ b/python/src/com/jetbrains/python/inspections/PyShadowingBuiltinsInspection.java @@ -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 ignoredNames = new ArrayList(); + + @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 myIgnoredNames; + + public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session, @NotNull Collection 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() { + @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); + } + } + } + }); + } + } + } + } +} + diff --git a/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java b/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java index c19945da06df..aad248db7c95 100644 --- a/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java @@ -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 ignoredNames = new ArrayList(); - @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 myIgnoredNames; - - public Visitor(@Nullable ProblemsHolder holder, @NotNull LocalInspectionToolSession session, @NotNull Collection 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() { - @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()); } - }); + } } } } diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java index 55cb57bedc34..b82b3d6c6eb1 100644 --- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java @@ -299,8 +299,12 @@ public abstract class PythonCommandLineState extends CommandLineState { } private static void addIfNeeded(@NotNull final VirtualFile file, @NotNull final Collection pathList) { + addIfNeeded(pathList, file.getPath()); + } + + protected static void addIfNeeded(Collection pathList, String path) { final Set vals = Sets.newHashSet(pathList); - final String filePath = FileUtil.toSystemDependentName(file.getPath()); + final String filePath = FileUtil.toSystemDependentName(path); if (!vals.contains(filePath)) { pathList.add(filePath); } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index 8f266af4d084..543f251c0723 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -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; diff --git a/python/src/com/jetbrains/python/testing/unittest/PythonUnitTestCommandLineState.java b/python/src/com/jetbrains/python/testing/unittest/PythonUnitTestCommandLineState.java index 11a55713bbbe..1fcf5b4cd8cd 100644 --- a/python/src/com/jetbrains/python/testing/unittest/PythonUnitTestCommandLineState.java +++ b/python/src/com/jetbrains/python/testing/unittest/PythonUnitTestCommandLineState.java @@ -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 getTestSpecs() { List specs = new ArrayList(); + 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()); diff --git a/python/testData/inspections/PyRedeclarationInspection/test.py b/python/testData/inspections/PyRedeclarationInspection/test.py index 58a1f1541abf..a8895dade5f5 100644 --- a/python/testData/inspections/PyRedeclarationInspection/test.py +++ b/python/testData/inspections/PyRedeclarationInspection/test.py @@ -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 foo(): + pass + + +class TestForLoopNoRedeclaraion: + for foo in [1, 2, 3]: + x = 1 + + +class TestForLoopTarget: + def foo(): + pass + + for foo in [1, 2, 3]: + x = 1 + + +class TestForLoopBody: + def foo(): + pass + + for _ in [1, 2, 3]: + def foo(): + pass diff --git a/python/testData/inspections/PyShadowingBuiltinsInspection/test.py b/python/testData/inspections/PyShadowingBuiltinsInspection/test.py new file mode 100644 index 000000000000..4951bf1fdf50 --- /dev/null +++ b/python/testData/inspections/PyShadowingBuiltinsInspection/test.py @@ -0,0 +1,50 @@ +def test_import_builtin_names(): + import float + from foo import float + from bar import baz as float + + +def test_builtin_function_parameters(): + def test1(x, _, len, file=None): + pass + + +def test_builtin_function_name(): + def list(): + pass + + +def test_builtin_assignment_targets(): + foo = 2 + list = [] + for int in range(10): + print(int) + range = [] + list, _ = (1, 2) + return [int for int in range(10)] + + +def test_builtin_class_name(): + class list(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 diff --git a/python/testData/inspections/PyShadowingNamesInspection/test.py b/python/testData/inspections/PyShadowingNamesInspection/test.py index 98eec4846722..b87b8b27bae3 100644 --- a/python/testData/inspections/PyShadowingNamesInspection/test.py +++ b/python/testData/inspections/PyShadowingNamesInspection/test.py @@ -1,58 +1,6 @@ global_foo = 1 -def test_import_builtin_names(): - import float - from foo import float - from bar import baz as float - - -def test_builtin_function_parameters(): - def test1(x, _, len, file=None): - pass - - -def test_builtin_function_name(): - def list(): - pass - - -def test_builtin_assignment_targets(): - foo = 2 - list = [] - for int in range(10): - print(int) - range = [] - list, _ = (1, 2) - return [int for int in range(10)] - - -def test_builtin_class_name(): - class list(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, y): diff --git a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java index a1b195d9b8df..f9e844134b2c 100644 --- a/python/testSrc/com/jetbrains/python/PyQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/PyQuickFixTest.java @@ -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); diff --git a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java index 183bad8ed613..06ce32a3e896 100644 --- a/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java +++ b/python/testSrc/com/jetbrains/python/PythonInspectionsTest.java @@ -295,6 +295,10 @@ public class PythonInspectionsTest extends PyTestCase { } // PY-5807 + public void testPyShadowingBuiltinsInspection() { + doHighlightingTest(PyShadowingBuiltinsInspection.class); + } + public void testPyShadowingNamesInspection() { doHighlightingTest(PyShadowingNamesInspection.class); }