From 910bac9cd84aa315e7952c6e73ccb0fb706b93b7 Mon Sep 17 00:00:00 2001 From: Dmitry Cheryasov Date: Sat, 25 Oct 2008 07:17:55 +0400 Subject: [PATCH] Turn qualifiers of assignment targets into references. --- .../python/parsing/ExpressionParsing.java | 86 +++++++++++-------- .../jetbrains/python/psi/PyResolveUtil.java | 16 ++++ python/testData/psi/QualifiedTarget.py | 1 + python/testData/psi/QualifiedTarget.txt | 13 +++ python/testData/resolve/QualifiedTarget.py | 5 ++ .../com/jetbrains/python/PyResolveTest.java | 5 ++ 6 files changed, 89 insertions(+), 37 deletions(-) create mode 100644 python/testData/psi/QualifiedTarget.py create mode 100644 python/testData/psi/QualifiedTarget.txt create mode 100644 python/testData/resolve/QualifiedTarget.py diff --git a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java index 1fc20f639c88..7275e08f92b3 100644 --- a/python/src/com/jetbrains/python/parsing/ExpressionParsing.java +++ b/python/src/com/jetbrains/python/parsing/ExpressionParsing.java @@ -207,54 +207,66 @@ public class ExpressionParsing extends Parsing { } public boolean parseMemberExpression(PsiBuilder builder, boolean isTargetExpression) { - PsiBuilder.Marker expr = builder.mark(); - if (!parsePrimaryExpression(builder, isTargetExpression)) { - expr.drop(); - return false; - } + // in sequence a.b.... .c all members but last are always references, and the last may be target. + boolean recast_first_identifier = false; + do { + boolean first_identifier_is_target = isTargetExpression && ! recast_first_identifier; + PsiBuilder.Marker expr = builder.mark(); + if (!parsePrimaryExpression(builder, first_identifier_is_target)) { + expr.drop(); + return false; + } - while (true) { - final IElementType tokenType = builder.getTokenType(); - if (tokenType == PyTokenTypes.DOT) { - builder.advanceLexer(); - checkMatches(PyTokenTypes.IDENTIFIER, "name expected"); - if (isTargetExpression && builder.getTokenType() != PyTokenTypes.DOT) { - expr.done(PyElementTypes.TARGET_EXPRESSION); + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == PyTokenTypes.DOT) { + if (first_identifier_is_target) { + recast_first_identifier = true; + expr.rollbackTo(); + break; + } + else recast_first_identifier = false; + builder.advanceLexer(); + checkMatches(PyTokenTypes.IDENTIFIER, "name expected"); + if (isTargetExpression && builder.getTokenType() != PyTokenTypes.DOT) { + expr.done(PyElementTypes.TARGET_EXPRESSION); + } + else { + expr.done(PyElementTypes.REFERENCE_EXPRESSION); + } + expr = expr.precede(); } - else { - expr.done(PyElementTypes.REFERENCE_EXPRESSION); + else if (tokenType == PyTokenTypes.LPAR) { + parseArgumentList(builder); + expr.done(PyElementTypes.CALL_EXPRESSION); + expr = expr.precede(); } - expr = expr.precede(); - } - else if (tokenType == PyTokenTypes.LPAR) { - parseArgumentList(builder); - expr.done(PyElementTypes.CALL_EXPRESSION); - expr = expr.precede(); - } - else if (tokenType == PyTokenTypes.LBRACKET) { - builder.advanceLexer(); - if (builder.getTokenType() == PyTokenTypes.COLON) { - PsiBuilder.Marker sliceMarker = builder.mark(); - sliceMarker.done(PyElementTypes.EMPTY_EXPRESSION); - parseSliceEnd(builder, expr); - } - else { - parseExpressionOptional(); + else if (tokenType == PyTokenTypes.LBRACKET) { + builder.advanceLexer(); if (builder.getTokenType() == PyTokenTypes.COLON) { + PsiBuilder.Marker sliceMarker = builder.mark(); + sliceMarker.done(PyElementTypes.EMPTY_EXPRESSION); parseSliceEnd(builder, expr); } else { - checkMatches(PyTokenTypes.RBRACKET, "] expected"); - expr.done(PyElementTypes.SUBSCRIPTION_EXPRESSION); + parseExpressionOptional(); + if (builder.getTokenType() == PyTokenTypes.COLON) { + parseSliceEnd(builder, expr); + } + else { + checkMatches(PyTokenTypes.RBRACKET, "] expected"); + expr.done(PyElementTypes.SUBSCRIPTION_EXPRESSION); + } } + expr = expr.precede(); + } + else { + expr.drop(); + break; } - expr = expr.precede(); - } - else { - expr.drop(); - break; } } + while (recast_first_identifier); return true; } diff --git a/python/src/com/jetbrains/python/psi/PyResolveUtil.java b/python/src/com/jetbrains/python/psi/PyResolveUtil.java index 87b3d1e3bd44..c9318a285950 100644 --- a/python/src/com/jetbrains/python/psi/PyResolveUtil.java +++ b/python/src/com/jetbrains/python/psi/PyResolveUtil.java @@ -255,6 +255,22 @@ public class PyResolveUtil { return path; } + public static String toPath(PyQualifiedExpression expr, String separator) { + if (expr == null) return ""; + List path = unwindQualifiers(expr); + if (path != null) { + StringBuilder buf = new StringBuilder(); + boolean is_not_first = false; + for (PyQualifiedExpression ex : path) { + if (is_not_first) buf.append(separator); + else is_not_first = true; + buf.append(ex.getName()); + } + return buf.toString(); + } + else return expr.getName(); + } + public static class CollectProcessor implements PsiScopeProcessor { Class[] my_collectables; diff --git a/python/testData/psi/QualifiedTarget.py b/python/testData/psi/QualifiedTarget.py new file mode 100644 index 000000000000..c1fe825a8c2d --- /dev/null +++ b/python/testData/psi/QualifiedTarget.py @@ -0,0 +1 @@ +a.b = 23 diff --git a/python/testData/psi/QualifiedTarget.txt b/python/testData/psi/QualifiedTarget.txt new file mode 100644 index 000000000000..125c72a06762 --- /dev/null +++ b/python/testData/psi/QualifiedTarget.txt @@ -0,0 +1,13 @@ +PyFile:QualifiedTarget.py + PyAssignmentStatement + PyTargetExpression: b + PyReferenceExpression: a + PsiElement(Py:IDENTIFIER)('a') + PsiElement(Py:DOT)('.') + PsiElement(Py:IDENTIFIER)('b') + PsiWhiteSpace(' ') + PsiElement(Py:EQ)('=') + PsiWhiteSpace(' ') + PyNumericLiteralExpression + PsiElement(Py:INTEGER_LITERAL)('23') + diff --git a/python/testData/resolve/QualifiedTarget.py b/python/testData/resolve/QualifiedTarget.py new file mode 100644 index 000000000000..dbeb89801fc0 --- /dev/null +++ b/python/testData/resolve/QualifiedTarget.py @@ -0,0 +1,5 @@ +class Foo: pass + +foo = Foo(); +foo.bar = 1 + diff --git a/python/testSrc/com/jetbrains/python/PyResolveTest.java b/python/testSrc/com/jetbrains/python/PyResolveTest.java index bca28b32aa59..bedbe219363d 100644 --- a/python/testSrc/com/jetbrains/python/PyResolveTest.java +++ b/python/testSrc/com/jetbrains/python/PyResolveTest.java @@ -51,6 +51,11 @@ public class PyResolveTest extends ResolveTestCase { assertTrue(targetElement instanceof PyTargetExpression); } + public void testQualifiedTarget() throws Exception { + PsiElement targetElement = resolve(); + assertTrue(targetElement instanceof PyTargetExpression); + } + public void testInnerFuncVar() throws Exception { PsiElement targetElement = resolve(); assertTrue(targetElement instanceof PyTargetExpression);