Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Jemerov
2012-08-16 08:29:20 +02:00
8 changed files with 172 additions and 36 deletions
+10 -10
View File
@@ -90,10 +90,10 @@ target('default': "Build artifacts") {
loadProject()
project.stage("Cleaning up sandbox folder")
projectBuilder.stage("Cleaning up sandbox folder")
project.targetFolder = "${paths.sandbox}/classes"
project.dryRun = dryRun
projectBuilder.targetFolder = "${paths.sandbox}/classes"
projectBuilder.dryRun = dryRun
if (!dryRun) {
forceDelete(paths.sandbox)
@@ -131,12 +131,12 @@ target('default': "Build artifacts") {
def forbiddenJars = ["client-api", "ideaLicenseDecoder", "eawtstub.jar", "ui.jar", "exe4jlib.jar", "cglib", "jcip-annotations", "/y.jar", "ysvg.jar"]
project.clean()
projectBuilder.cleanOutput()
modules.each {
Module module = project[it] as Module
Module module = findModule(it)
if (module != null) {
project.info("Building $module.name")
module.runtimeClasspath().each {
projectBuilder.info("Building $module.name")
projectBuilder.moduleRuntimeClasspath(module, false).each {
File file = new File(it)
if (file.exists()) {
String path = file.canonicalPath.replace('\\', '/')
@@ -155,15 +155,15 @@ target('default': "Build artifacts") {
module.make()
}
else {
project.warning("$it is not a module")
projectBuilder.warning("$it is not a module")
}
}
usedJars.add("$home/community/lib/jgoodies-common-1.2.1.jar")
usedJars.add("$home/community/lib/jgoodies-looks-2.4.2.jar")
buildSearchableOptions("${project["platform-resources"].output}/search", ["$home/build/idea.license", "$home/build/pycharm.license"], {
project["python-ide"].runtimeClasspath().each {
buildSearchableOptions("${findModule("platform-resources").output}/search", ["$home/build/idea.license", "$home/build/pycharm.license"], {
projectBuilder.moduleRuntimeClasspath(findModule("python-ide"), false).each {
pathelement(location: it)
}
}, "-Didea.platform.prefix=Python -Didea.no.jre.check=true")
@@ -161,6 +161,9 @@ public class PyRequirement {
@NotNull
private List<String> parse(@Nullable String s) {
// Version parsing from pkg_resources ensures that all the "pre", "alpha", "rc", etc. are sorted correctly
if (s == null) {
return Collections.emptyList();
}
final Pattern COMPONENT_RE = Pattern.compile("\\d+|[a-z]+|\\.|-|.+");
final List<String> results = new ArrayList<String>();
final Matcher matcher = COMPONENT_RE.matcher(s);
@@ -181,7 +181,10 @@ public class PyDynamicMember {
if (myTypeCallback != null) {
return myTypeCallback.fun(myContext);
}
return PyPsiFacade.getInstance(getProject()).createClassType(myClass, !myResolveToInstance);
else if (myClass != null) {
return PyPsiFacade.getInstance(getProject()).createClassType(myClass, !myResolveToInstance);
}
return null;
}
}
}
@@ -8,35 +8,49 @@ import com.intellij.openapi.project.Project;
import com.intellij.profile.codeInspection.InspectionProfileManager;
import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import org.jetbrains.annotations.NotNull;
/**
* @author yole
*/
public class AddIgnoredIdentifierFix implements LocalQuickFix, LowPriorityAction {
private final String myIdentifier;
public class AddIgnoredIdentifierQuickFix implements LocalQuickFix, LowPriorityAction {
public static final String END_WILDCARD = ".*";
public AddIgnoredIdentifierFix(String identifier) {
@NotNull private final PyQualifiedName myIdentifier;
private final boolean myIgnoreAllAttributes;
public AddIgnoredIdentifierQuickFix(@NotNull PyQualifiedName identifier, boolean ignoreAllAttributes) {
myIdentifier = identifier;
myIgnoreAllAttributes = ignoreAllAttributes;
}
@NotNull
@Override
public String getName() {
return "Ignore unresolved identifier " + myIdentifier;
if (myIgnoreAllAttributes) {
return "Mark all unresolved attributes of '" + myIdentifier + "' as ignored";
}
else {
return "Ignore unresolved reference '" + myIdentifier + "'";
}
}
@NotNull
@Override
public String getFamilyName() {
return "Ignore unresolved identifier";
return "Ignore unresolved reference";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PyUnresolvedReferencesInspection instance = PyUnresolvedReferencesInspection.getInstance(descriptor.getPsiElement());
if (!instance.ignoredIdentifiers.contains(myIdentifier)) {
instance.ignoredIdentifiers.add(myIdentifier);
String name = myIdentifier.toString();
if (myIgnoreAllAttributes) {
name = name + END_WILDCARD;
}
if (!instance.ignoredIdentifiers.contains(name)) {
instance.ignoredIdentifiers.add(name);
final InspectionProfile profile = InspectionProjectProfileManager.getInstance(project).getInspectionProfile();
InspectionProfileManager.getInstance().fireProfileChanged(profile);
}
@@ -470,9 +470,12 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
@Nullable
private static PyClass inferClassOfParameter(PsiElement context) {
final PyType type = ((PyNamedParameter)context).getType(TypeEvalContext.fast());
if (type instanceof PyClassType)
return ((PyClassType)type).getPyClass();
if (context instanceof PyNamedParameter) {
final PyType type = ((PyNamedParameter)context).getType(TypeEvalContext.fast());
if (type instanceof PyClassType) {
return ((PyClassType)type).getPyClass();
}
}
return null;
}
@@ -49,7 +49,10 @@ import com.jetbrains.python.psi.impl.PyImportedModule;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.impl.references.PyImportReference;
import com.jetbrains.python.psi.impl.references.PyOperatorReference;
import com.jetbrains.python.psi.resolve.*;
import com.jetbrains.python.psi.resolve.ImportedResolveResult;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.Nls;
@@ -59,6 +62,8 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import static com.jetbrains.python.actions.AddIgnoredIdentifierQuickFix.END_WILDCARD;
/**
* Marks references that fail to resolve. Also tracks unused imports and provides "optimize imports" support.
* User: dcheryasov
@@ -108,7 +113,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
@Override
public JComponent createOptionsPanel() {
ListEditForm form = new ListEditForm("Ignore identifiers", ignoredIdentifiers);
ListEditForm form = new ListEditForm("Ignore references", ignoredIdentifiers);
return form.getContentPanel();
}
@@ -366,7 +371,8 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
}
}
private void registerUnresolvedReferenceProblem(final PyElement node, final PsiReference reference, HighlightSeverity severity) {
private void registerUnresolvedReferenceProblem(@NotNull PyElement node, @NotNull PsiReference reference,
@NotNull HighlightSeverity severity) {
String description = null;
final String text = reference.getElement().getText();
TextRange rangeInElement = reference.getRangeInElement();
@@ -378,9 +384,30 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
final List<LocalQuickFix> actions = new ArrayList<LocalQuickFix>(2);
if (ref_text.length() <= 0) return; // empty text, nothing to highlight
final String refname = (element instanceof PyQualifiedExpression) ? ((PyQualifiedExpression)element).getReferencedName() : ref_text;
final PyQualifiedName canonicalQName = getCanonicalName(reference, myTypeEvalContext);
final String canonicalName = canonicalQName != null ? canonicalQName.toString() : null;
if (canonicalName != null) {
for (String ignored : myIgnoredIdentifiers) {
if (ignored.endsWith(END_WILDCARD)) {
final String prefix = ignored.substring(0, ignored.length() - END_WILDCARD.length());
if (canonicalName.startsWith(prefix)) {
return;
}
}
else if (canonicalName.equals(ignored)) {
return;
}
}
}
// Legacy non-qualified ignore patterns
if (myIgnoredIdentifiers.contains(refname)) {
return;
}
if (element instanceof PyReferenceExpression) {
PyReferenceExpression refex = (PyReferenceExpression)element;
if (myIgnoredIdentifiers.contains(refname) || PyNames.COMPARISON_OPERATORS.contains(refname)) {
if (PyNames.COMPARISON_OPERATORS.contains(refname)) {
return;
}
if (CythonLanguageDialect.isInsideCythonFile(element) && CythonNames.BUILTINS.contains(text)) {
@@ -419,11 +446,6 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
// TODO: mark the node so that future references pointing to it won't result in a error, but in a warning
}
}
if (reference instanceof DocStringParameterReference) {
if (myIgnoredIdentifiers.contains(reference.getCanonicalText())) {
return;
}
}
if (reference instanceof PsiReferenceEx && description == null) {
description = ((PsiReferenceEx)reference).getUnresolvedDescription();
}
@@ -431,9 +453,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
boolean marked_qualified = false;
if (element instanceof PyQualifiedExpression) {
final PyQualifiedExpression qexpr = (PyQualifiedExpression)element;
if (myIgnoredIdentifiers.contains(ref_text) ||
PyNames.COMPARISON_OPERATORS.contains(qexpr.getReferencedName()) ||
refname == null) {
if (PyNames.COMPARISON_OPERATORS.contains(qexpr.getReferencedName()) || refname == null) {
return;
}
final PyExpression qualifier = qexpr.getQualifier();
@@ -487,7 +507,12 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
if (GenerateBinaryStubsFix.isApplicable(reference)) {
actions.add(new GenerateBinaryStubsFix(reference));
}
actions.add(new AddIgnoredIdentifierFix(ref_text));
if (canonicalQName != null) {
actions.add(new AddIgnoredIdentifierQuickFix(canonicalQName, false));
if (canonicalQName.getComponentCount() > 1) {
actions.add(new AddIgnoredIdentifierQuickFix(canonicalQName.removeLastComponent(), true));
}
}
addPluginQuickFixes(reference, actions);
PsiElement point = node.getLastChild(); // usually the identifier at the end of qual ref
@@ -515,6 +540,77 @@ public class PyUnresolvedReferencesInspection extends PyInspection {
registerProblem(point, description, hl_type, null, range, actions.toArray(new LocalQuickFix[actions.size()]));
}
/**
* Return the canonical qualified name for a reference (even for an unresolved one).
*/
@Nullable
private static PyQualifiedName getCanonicalName(@NotNull PsiReference reference, @NotNull TypeEvalContext context) {
final PsiElement element = reference.getElement();
if (reference instanceof PyOperatorReference && element instanceof PyQualifiedExpression) {
final PyExpression receiver = ((PyOperatorReference)reference).getReceiver();
if (receiver != null) {
final PyType type = receiver.getType(context);
if (type instanceof PyClassType) {
final String name = ((PyClassType)type).getClassQName();
if (name != null) {
return PyQualifiedName.fromDottedString(name).append(((PyQualifiedExpression)element).getReferencedName());
}
}
}
}
else if (element instanceof PyReferenceExpression) {
final PyReferenceExpression expr = (PyReferenceExpression)element;
final PyExpression qualifier = expr.getQualifier();
final String exprName = expr.getName();
if (exprName != null) {
if (qualifier != null) {
final PyType type = qualifier.getType(context);
if (type instanceof PyClassType) {
final String name = ((PyClassType)type).getClassQName();
if (name != null) {
return PyQualifiedName.fromDottedString(name).append(exprName);
}
}
else if (type instanceof PyModuleType) {
final PyFile file = ((PyModuleType)type).getModule();
final PyQualifiedName name = QualifiedNameFinder.findCanonicalImportPath(file, element);
if (name != null) {
return name.append(exprName);
}
}
}
else {
final PsiElement parent = element.getParent();
if (parent instanceof PyImportElement) {
final PyImportStatementBase importStmt = PsiTreeUtil.getParentOfType(parent, PyImportStatementBase.class);
if (importStmt instanceof PyImportStatement) {
return PyQualifiedName.fromComponents(exprName);
}
else if (importStmt instanceof PyFromImportStatement) {
final PsiElement resolved = ((PyFromImportStatement)importStmt).resolveImportSource();
if (resolved != null) {
final PyQualifiedName path = QualifiedNameFinder.findCanonicalImportPath(resolved, element);
if (path != null) {
return path.append(exprName);
}
}
}
}
else {
final PyQualifiedName path = QualifiedNameFinder.findCanonicalImportPath(element, element);
if (path != null) {
return path.append(exprName);
}
}
}
}
}
else if (reference instanceof DocStringParameterReference) {
return PyQualifiedName.fromDottedString(reference.getCanonicalText());
}
return null;
}
private static boolean ignoreUnresolvedMemberForType(@NotNull PyType qtype, PsiReference reference, String refText) {
if (qtype instanceof PyNoneType || PyTypeChecker.isUnknown(qtype)) {
// this almost always means that we don't know the type, so don't show an error in this case
@@ -76,6 +76,20 @@ public class PyOperatorReference extends PyReferenceImpl {
}
}
@Nullable
public PyExpression getReceiver() {
if (myElement instanceof PyBinaryExpression) {
return ((PyBinaryExpression)myElement).getLeftExpression();
}
else if (myElement instanceof PySubscriptionExpression) {
return ((PySubscriptionExpression)myElement).getOperand();
}
else if (myElement instanceof PyPrefixExpression) {
return ((PyPrefixExpression)myElement).getOperand();
}
return null;
}
private static String leftToRightOperatorName(String name) {
return name.replaceFirst("__([a-z]+)__", "__r$1__");
}
@@ -6,6 +6,8 @@ import com.jetbrains.python.inspections.PyInspection;
import com.jetbrains.python.inspections.PyUnresolvedReferencesInspection;
import com.jetbrains.python.inspections.PyUnusedLocalInspection;
import java.util.List;
/**
* @author yole
*/
@@ -39,8 +41,9 @@ public class PySuppressInspectionsTest extends PyTestCase {
public void testSuppressForStatement() {
myFixture.configureByFile("inspections/suppress/suppressForStatement.py");
myFixture.enableInspections(PyUnresolvedReferencesInspection.class);
final IntentionAction suppressAction = myFixture.findSingleIntention("Suppress for statement");
assertNotNull(suppressAction);
final List<IntentionAction> intentions = myFixture.filterAvailableIntentions("Suppress for statement");
assertEquals(2, intentions.size());
final IntentionAction suppressAction = intentions.get(0);
myFixture.launchAction(suppressAction);
myFixture.checkResultByFile("inspections/suppress/suppressForStatement.after.py");
}