PY-13210 Unused star import in Django settings.py

This commit is contained in:
Ilya.Kazakevich
2014-06-09 22:16:27 +04:00
parent 491dc5f169
commit 879c23d443
18 changed files with 88 additions and 20 deletions
@@ -18,7 +18,7 @@ package com.jetbrains.jython;
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
import com.jetbrains.python.PythonTestUtil;
import com.jetbrains.python.inspections.PyCallingNonCallableInspection;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
/**
* @author yole
@@ -20,5 +20,5 @@ package com.jetbrains.python.psi;
*
* @author vlan
*/
public interface PyImportedNameDefiner extends NameDefiner {
public interface PyImportedNameDefiner extends NameDefiner, PyElement {
}
@@ -20,5 +20,5 @@ package com.jetbrains.python.psi;
* User: dcheryasov
* Date: Jul 28, 2008
*/
public interface PyStarImportElement extends PyElement, PyImportedNameDefiner {
public interface PyStarImportElement extends PyImportedNameDefiner {
}
+2 -1
View File
@@ -270,7 +270,7 @@
<localInspection language="Python" shortName="PyArgumentListInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.incorrect.call.arguments" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyArgumentListInspection"/>
<localInspection language="Python" shortName="PyRedeclarationInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.redeclaration" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyRedeclarationInspection"/>
<localInspection language="Python" shortName="PyUnresolvedReferencesInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.unresolved.refs" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyUnresolvedReferencesInspection"/>
<localInspection language="Python" shortName="PyUnresolvedReferencesInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.unresolved.refs" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection"/>
<localInspection language="Python" shortName="PyInterpreterInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.invalid.interpreter" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyInterpreterInspection"/>
<localInspection language="Python" shortName="PyMethodParametersInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.problematic.first.parameter" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WEAK WARNING" implementationClass="com.jetbrains.python.inspections.PyMethodParametersInspection"/>
<localInspection language="Python" shortName="PyUnreachableCodeInspection" bundle="com.jetbrains.python.PyBundle" key="INSP.NAME.unreachable.code" groupKey="INSP.GROUP.python" enabledByDefault="true" level="WARNING" implementationClass="com.jetbrains.python.inspections.PyUnreachableCodeInspection"/>
@@ -532,6 +532,7 @@
<extensionPoints>
<extensionPoint qualifiedName="Pythonid.importResolver" interface="com.jetbrains.python.psi.impl.PyImportResolver"/>
<extensionPoint qualifiedName="Pythonid.magicLiteral" interface="com.jetbrains.python.magicLiteral.PyMagicLiteralExtensionPoint"/>
<extensionPoint qualifiedName="Pythonid.unresolvedReferenceSkipper" interface="com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferenceSkipperExtPoint"/>
<extensionPoint qualifiedName="Pythonid.resolveResultRater" interface="com.jetbrains.python.psi.impl.PyResolveResultRater"/>
<extensionPoint qualifiedName="Pythonid.typeProvider" interface="com.jetbrains.python.psi.impl.PyTypeProvider"/>
<extensionPoint qualifiedName="Pythonid.pySuperMethodsSearch" interface="com.intellij.util.QueryExecutor"/>
@@ -21,7 +21,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileSystemItem;
import com.jetbrains.python.formatter.PyBlock;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -18,6 +18,7 @@ package com.jetbrains.python.documentation.doctest;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.psi.PsiFile;
import com.jetbrains.python.inspections.*;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.validation.DocStringAnnotator;
import com.jetbrains.python.validation.HighlightingAnnotator;
@@ -21,7 +21,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.intellij.psi.PsiElement;
import com.intellij.util.Consumer;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.intellij.psi.util.QualifiedName;
import org.jetbrains.annotations.NotNull;
@@ -28,7 +28,7 @@ import com.intellij.usageView.UsageInfo;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.refactoring.PyRefactoringUtil;
import org.jetbrains.annotations.NonNls;
@@ -0,0 +1,25 @@
package com.jetbrains.python.inspections.unresolvedReference;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.jetbrains.python.psi.PyImportedNameDefiner;
import org.jetbrains.annotations.NotNull;
/**
* Inject this point to ask "unused reference" inspection to skip some unused references.
* For example in Django you may import "I18N" to your "settings.py". It is not used in "settings.py", but used by Django
* and should not be marked as "unused".
*
* @author Ilya.Kazakevich
*/
public interface PyUnresolvedReferenceSkipperExtPoint {
@NotNull
ExtensionPointName<PyUnresolvedReferenceSkipperExtPoint> EP_NAME = ExtensionPointName.create("Pythonid.unresolvedReferenceSkipper");
/**
* Checks if some unused import should be skipped
*
* @param importNameDefiner unused import
* @return true if should be skipped
*/
boolean unusedImportShouldBeSkipped(@NotNull PyImportedNameDefiner importNameDefiner);
}
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.inspections;
package com.jetbrains.python.inspections.unresolvedReference;
import com.google.common.collect.ImmutableSet;
import com.intellij.codeInspection.*;
@@ -45,6 +45,7 @@ import com.jetbrains.python.codeInsight.imports.PythonReferenceImporter;
import com.jetbrains.python.console.PydevConsoleRunner;
import com.jetbrains.python.documentation.DocStringParameterReference;
import com.jetbrains.python.documentation.DocStringTypeReference;
import com.jetbrains.python.inspections.*;
import com.jetbrains.python.inspections.quickfix.*;
import com.jetbrains.python.packaging.PyPIPackageUtil;
import com.jetbrains.python.packaging.PyPackageManager;
@@ -68,6 +69,7 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import java.util.HashSet;
import static com.jetbrains.python.inspections.quickfix.AddIgnoredIdentifierQuickFix.END_WILDCARD;
@@ -78,7 +80,8 @@ import static com.jetbrains.python.inspections.quickfix.AddIgnoredIdentifierQuic
*/
public class PyUnresolvedReferencesInspection extends PyInspection {
private static Key<Visitor> KEY = Key.create("PyUnresolvedReferencesInspection.Visitor");
public static final Key<PyUnresolvedReferencesInspection> SHORT_NAME_KEY = Key.create(PyUnresolvedReferencesInspection.class.getSimpleName());
public static final Key<PyUnresolvedReferencesInspection> SHORT_NAME_KEY =
Key.create(PyUnresolvedReferencesInspection.class.getSimpleName());
public JDOMExternalizableStringList ignoredIdentifiers = new JDOMExternalizableStringList();
@@ -125,7 +128,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
public static class Visitor extends PyInspectionVisitor {
private Set<PsiElement> myUsedImports = Collections.synchronizedSet(new HashSet<PsiElement>());
private Set<NameDefiner> myAllImports = Collections.synchronizedSet(new HashSet<NameDefiner>());
private Set<PyImportedNameDefiner> myAllImports = Collections.synchronizedSet(new HashSet<PyImportedNameDefiner>());
private final ImmutableSet<String> myIgnoredIdentifiers;
private volatile Boolean myIsEnabled = null;
@@ -285,10 +288,10 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
private void processInjection(@Nullable PsiLanguageInjectionHost node) {
if (node == null) return;
final List<Pair<PsiElement,TextRange>>
final List<Pair<PsiElement, TextRange>>
files = InjectedLanguageManager.getInstance(node.getProject()).getInjectedPsiFiles(node);
if (files != null) {
for (Pair<PsiElement,TextRange> pair : files) {
for (Pair<PsiElement, TextRange> pair : files) {
new PyRecursiveElementVisitor() {
@Override
public void visitPyElement(PyElement element) {
@@ -478,7 +481,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
addAddSelfFix(node, expr, actions);
PyCallExpression callExpression = PsiTreeUtil.getParentOfType(element, PyCallExpression.class);
if (callExpression != null && (!(callExpression.getCallee() instanceof PyQualifiedExpression) ||
((PyQualifiedExpression)callExpression.getCallee()).getQualifier() == null)) {
((PyQualifiedExpression)callExpression.getCallee()).getQualifier() == null)) {
actions.add(new UnresolvedRefCreateFunctionQuickFix(callExpression, expr));
}
PyFunction parentFunction = PsiTreeUtil.getParentOfType(element, PyFunction.class);
@@ -780,8 +783,9 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
if (decoratorList != null) {
for (PyDecorator decorator : decoratorList.getDecorators()) {
final PyExpression callee = decorator.getCallee();
if (callee != null && PyNames.CLASSMETHOD.equals(callee.getText()))
if (callee != null && PyNames.CLASSMETHOD.equals(callee.getText())) {
isClassMethod = true;
}
}
}
for (PyTargetExpression target : containedClass.getInstanceAttributes()) {
@@ -926,8 +930,20 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
}
List<PsiElement> result = new ArrayList<PsiElement>();
Set<NameDefiner> unusedImports = new HashSet<NameDefiner>(myAllImports);
Set<PyImportedNameDefiner> unusedImports = new HashSet<PyImportedNameDefiner>(myAllImports);
unusedImports.removeAll(myUsedImports);
// Remove those unsed, that are reported to be skipped by extension points
final Set<PyImportedNameDefiner> unusedImportToSkip = new HashSet<PyImportedNameDefiner>();
for (final PyImportedNameDefiner unusedImport : unusedImports) {
if (importShouldBeSkippedByExtPoint(unusedImport)) { // Pass to extension points
unusedImportToSkip.add(unusedImport);
}
}
unusedImports.removeAll(unusedImportToSkip);
Set<String> usedImportNames = new HashSet<String>();
for (PsiElement usedImport : myUsedImports) {
if (usedImport instanceof NameDefiner) {
@@ -942,7 +958,8 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
QualifiedName packageQName = null;
List<String> dunderAll = null;
for (NameDefiner unusedImport : unusedImports) {
// TODO: Use strategies instead of pack of "continue"
for (PyImportedNameDefiner unusedImport : unusedImports) {
if (packageQName == null) {
final PsiFile file = unusedImport.getContainingFile();
if (file instanceof PyFile) {
@@ -1014,7 +1031,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
return result;
}
private static boolean areAllImportsUnused(PyImportStatementBase importStatement, Set<NameDefiner> unusedImports) {
private static boolean areAllImportsUnused(PyImportStatementBase importStatement, Set<PyImportedNameDefiner> unusedImports) {
final PyImportElement[] elements = importStatement.getImportElements();
for (PyImportElement element : elements) {
if (!unusedImports.contains(element)) {
@@ -1034,4 +1051,17 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
}
}
/**
* Checks if one or more extension points ask unused import to be skipped
* @param importNameDefiner unused import
* @return true of one or more asks
*/
private static boolean importShouldBeSkippedByExtPoint(@NotNull final PyImportedNameDefiner importNameDefiner) {
for (final PyUnresolvedReferenceSkipperExtPoint skipper : PyUnresolvedReferenceSkipperExtPoint.EP_NAME.getExtensions()) {
if (skipper.unusedImportShouldBeSkipped(importNameDefiner)) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,7 @@
/**
*
* Inspection that reports unresolved and unused references.
* You can inject logic to mark some unused imports as used. See extension points in this package.
* @author Ilya.Kazakevich
*/
package com.jetbrains.python.inspections.unresolvedReference;
@@ -23,6 +23,7 @@ import com.jetbrains.python.documentation.DocStringFormat;
import com.jetbrains.python.documentation.PyDocumentationSettings;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.inspections.*;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.jetbrains.python.psi.LanguageLevel;
import org.jetbrains.annotations.NonNls;
@@ -18,7 +18,7 @@ package com.jetbrains.python;
import com.intellij.codeInsight.intention.IntentionAction;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.inspections.PyInspection;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.PyUnusedLocalInspection;
import java.util.List;
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.LightProjectDescriptor;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.jetbrains.python.psi.LanguageLevel;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFile;
@@ -16,6 +16,7 @@
package com.jetbrains.python.inspections;
import com.jetbrains.python.fixtures.PyInspectionTestCase;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.jetbrains.python.psi.LanguageLevel;
import org.jetbrains.annotations.NotNull;
@@ -16,6 +16,7 @@
package com.jetbrains.python.inspections;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import org.jetbrains.annotations.NotNull;
/**
@@ -18,7 +18,7 @@ package com.jetbrains.python.quickFixes;
import com.intellij.testFramework.TestDataPath;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyQuickFixTestCase;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.PyUnusedLocalInspection;
/**
@@ -18,7 +18,7 @@ package com.jetbrains.python.quickFixes;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyQuickFixTestCase;
import com.jetbrains.python.inspections.PyClassHasNoInitInspection;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.unresolvedReference.PyUnresolvedReferencesInspection;
/**
* User: ktisha