correctly parse default values for tuple parameters (PY-350)

This commit is contained in:
Dmitry Jemerov
2010-01-25 19:02:07 +03:00
parent 2f9e89cc7b
commit baa08ee6e0
14 changed files with 126 additions and 27 deletions
@@ -102,6 +102,7 @@ public class FunctionParsing extends Parsing {
}
else if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
parseParameterSubList();
continue;
}
else {
myBuilder.error(message("PARSE.expected.comma.lpar.rpar"));
@@ -168,6 +169,10 @@ public class FunctionParsing extends Parsing {
}
myBuilder.advanceLexer();
}
if (myBuilder.getTokenType() == PyTokenTypes.EQ) {
myBuilder.advanceLexer();
getExpressionParser().parseSingleExpression(false);
}
tuple.done(PyElementTypes.TUPLE_PARAMETER);
}
}
@@ -24,7 +24,7 @@ public class PyFileElementType extends IStubFileElementType {
@Override
public int getStubVersion() {
return 7;
return 8;
}
@Override
@@ -1,26 +1,9 @@
/*
* Copyright 2005 Pythonid Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS"; BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.jetbrains.python.psi;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.StubBasedPsiElement;
import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Represents a named parameter, as opposed to a tuple parameter.
@@ -30,9 +13,6 @@ public interface PyNamedParameter extends PyParameter, PyElement, PsiNamedElemen
boolean isKeywordContainer();
@Nullable
PyExpression getDefaultValue();
/**
* @param includeDefaultValue if true, include the default value after an " = ".
* @return Canonical representation of parameter. Includes asterisks for *param and **param, and name.
@@ -21,4 +21,7 @@ public interface PyParameter extends PyElement {
*/
@Nullable
PyTupleParameter getAsTuple();
@Nullable
PyExpression getDefaultValue();
}
@@ -4,6 +4,7 @@ import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyNamedParameter;
import com.jetbrains.python.psi.PySingleStarParameter;
import com.jetbrains.python.psi.PyTupleParameter;
@@ -34,4 +35,8 @@ public class PySingleStarParameterImpl extends PyPresentableElementImpl<PySingle
public PyTupleParameter getAsTuple() {
return null;
}
public PyExpression getDefaultValue() {
return null;
}
}
@@ -5,10 +5,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyNamedParameter;
import com.jetbrains.python.psi.PyParameter;
import com.jetbrains.python.psi.PyTupleParameter;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.stubs.PyTupleParameterStub;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
@@ -38,6 +35,14 @@ public class PyTupleParameterImpl extends PyPresentableElementImpl<PyTupleParame
return this;
}
public PyExpression getDefaultValue() {
ASTNode[] nodes = getNode().getChildren(PyElementTypes.EXPRESSIONS);
if (nodes.length > 0) {
return (PyExpression)nodes[0].getPsi();
}
return null;
}
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
throw new IncorrectOperationException("Can't rename a tuple parameter to '" + name +"'");
}
@@ -23,6 +23,7 @@ public class ParameterListAnnotator extends PyAnnotator {
boolean hadDefaultValue = false;
boolean hadSingleStar = false;
boolean hadParamsAfterSingleStar = false;
int inTuple = 0;
@Override
public void visitNamedParameter(PyNamedParameter parameter, boolean first, boolean last) {
if (parameterNames.contains(parameter.getName())) {
@@ -55,7 +56,7 @@ public class ParameterListAnnotator extends PyAnnotator {
hadDefaultValue = true;
}
else {
if (hadDefaultValue && !hadSingleStar && (!languageLevel.isPy3K() || !hadPositionalContainer)) {
if (hadDefaultValue && !hadSingleStar && (!languageLevel.isPy3K() || !hadPositionalContainer) && inTuple == 0) {
markError(parameter, PyBundle.message("ANN.non.default.param.after.default"));
}
}
@@ -64,10 +65,18 @@ public class ParameterListAnnotator extends PyAnnotator {
@Override
public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) {
super.enterTupleParameter(param, first, last);
inTuple++;
if (languageLevel.isPy3K()) {
markError(param, PyBundle.message("ANN.tuple.py3"));
}
else if (param.getDefaultValue() == null && hadDefaultValue) {
markError(param, PyBundle.message("ANN.non.default.param.after.default"));
}
}
@Override
public void leaveTupleParameter(PyTupleParameter param, boolean first, boolean last) {
inTuple--;
}
@Override
@@ -0,0 +1,2 @@
def comp_args(a=2, (b, c)=(3, 4)):
return a, b, c
@@ -0,0 +1,2 @@
def comp_args((a, b)=(3, 4)):
return a, b
@@ -0,0 +1,41 @@
PyFile:DefaultTupleArguments.py
PyFunction('comp_args')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('comp_args')
PyParameterList
PsiElement(Py:LPAR)('(')
PyTupleParameter
PsiElement(Py:LPAR)('(')
PyNamedParameter('a')
PsiElement(Py:IDENTIFIER)('a')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyNamedParameter('b')
PsiElement(Py:IDENTIFIER)('b')
PsiElement(Py:RPAR)(')')
PsiElement(Py:EQ)('=')
PyParenthesizedExpression
PsiElement(Py:LPAR)('(')
PyTupleExpression
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('3')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyNumericLiteralExpression
PsiElement(Py:INTEGER_LITERAL)('4')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace('\n ')
PyStatementList
PyReturnStatement
PsiElement(Py:RETURN_KEYWORD)('return')
PsiWhiteSpace(' ')
PyTupleExpression
PyReferenceExpression: a
PsiElement(Py:IDENTIFIER)('a')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyReferenceExpression: b
PsiElement(Py:IDENTIFIER)('b')
+1
View File
@@ -0,0 +1 @@
def foo((a, b), (c, d)): pass
+33
View File
@@ -0,0 +1,33 @@
PyFile:TupleArguments.py
PyFunction('foo')
PsiElement(Py:DEF_KEYWORD)('def')
PsiWhiteSpace(' ')
PsiElement(Py:IDENTIFIER)('foo')
PyParameterList
PsiElement(Py:LPAR)('(')
PyTupleParameter
PsiElement(Py:LPAR)('(')
PyNamedParameter('a')
PsiElement(Py:IDENTIFIER)('a')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyNamedParameter('b')
PsiElement(Py:IDENTIFIER)('b')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyTupleParameter
PsiElement(Py:LPAR)('(')
PyNamedParameter('c')
PsiElement(Py:IDENTIFIER)('c')
PsiElement(Py:COMMA)(',')
PsiWhiteSpace(' ')
PyNamedParameter('d')
PsiElement(Py:IDENTIFIER)('d')
PsiElement(Py:RPAR)(')')
PsiElement(Py:RPAR)(')')
PsiElement(Py:COLON)(':')
PsiWhiteSpace(' ')
PyStatementList
PyPassStatement
PsiElement(Py:PASS_KEYWORD)('pass')
@@ -91,6 +91,10 @@ public class PythonHighlightingTest extends PyLightFixtureTestCase {
doTest(LanguageLevel.PYTHON26, true, true);
}
public void testArgumentList() throws Exception {
doTest(true, false);
}
public void testRegularAfterVarArgs() throws Exception {
doTest(LanguageLevel.PYTHON30, true, false);
}
@@ -162,6 +162,15 @@ public class PythonParsingTest extends ParsingTestCase {
doTest();
}
public void testTupleArguments() throws Exception {
doTest();
}
public void testDefaultTupleArguments() throws Exception {
doTest();
}
public void doTest() throws Exception {
doTest(LanguageLevel.PYTHON25);
}