serviceImplementation="com.jetbrains.python.documentation.PyDocumentationSettings"/>
<psi.referenceContributor implementation="com.jetbrains.python.documentation.docstrings.DocStringReferenceContributor"/>
<psi.referenceContributor implementation="com.jetbrains.python.codeInsight.PythonFormattedStringReferenceContributor"/>
- <psi.referenceContributor implementation="com.jetbrains.python.codeInsight.PyFunctionTypeCommentReferenceContributor"/>
<completion.contributor language="Python" implementationClass="com.jetbrains.python.documentation.docstrings.DocStringTagCompletionContributor"/>
<completion.contributor language="Python" implementationClass="com.jetbrains.python.documentation.docstrings.DocStringSectionHeaderCompletionContributor"/>
<lang.parserDefinition language="PyDocstring" implementationClass="com.jetbrains.python.documentation.doctest.PyDocstringParserDefinition"/>
<highlightErrorFilter implementation="com.jetbrains.python.documentation.doctest.PyDocstringErrorFilter"/>
+ <!-- PyFunctionTypeAnnotation -->
+ <lang.parserDefinition language="PyFunctionTypeComment"
+ implementationClass="com.jetbrains.python.codeInsight.functionTypeComments.PyFunctionTypeAnnotationParserDefinition"/>
+
<!-- Packaging -->
<moduleService serviceInterface="com.jetbrains.python.packaging.PyPackageRequirementsSettings"
serviceImplementation="com.jetbrains.python.packaging.PyPackageRequirementsSettings"/>
+++ /dev/null
-/*
- * Copyright 2000-2016 JetBrains s.r.o.
- *
- * 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.codeInsight;
-
-import com.intellij.openapi.util.TextRange;
-import com.intellij.patterns.PatternCondition;
-import com.intellij.patterns.PsiElementPattern;
-import com.intellij.psi.*;
-import com.intellij.psi.util.PsiTreeUtil;
-import com.intellij.util.ArrayUtil;
-import com.intellij.util.ProcessingContext;
-import com.jetbrains.python.documentation.docstrings.DocStringTypeReference;
-import com.jetbrains.python.psi.PyFunction;
-import com.jetbrains.python.psi.PyImportElement;
-import com.jetbrains.python.psi.PyStatementList;
-import com.jetbrains.python.psi.types.PyType;
-import com.jetbrains.python.psi.types.PyTypeParser;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import static com.intellij.patterns.PlatformPatterns.psiElement;
-
-/**
- * @author Mikhail Golubev
- */
-public class PyFunctionTypeCommentReferenceContributor extends PsiReferenceContributor {
- public static final PsiElementPattern.Capture<PsiComment> TYPE_COMMENT_PATTERN = psiElement(PsiComment.class)
- .withParent(psiElement(PyStatementList.class).withParent(PyFunction.class))
- .with(new PatternCondition<PsiComment>("isPep484TypeComment") {
- @Override
- public boolean accepts(@NotNull PsiComment comment, ProcessingContext context) {
- final PyFunction func = PsiTreeUtil.getParentOfType(comment, PyFunction.class);
- return func != null && func.getTypeComment() == comment;
- }
- });
-
-
- @Override
- public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
- registrar.registerReferenceProvider(TYPE_COMMENT_PATTERN, new PsiReferenceProvider() {
- @NotNull
- @Override
- public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
- final PsiComment comment = (PsiComment)element;
- final String wholeText = comment.getText();
- final String typeText = PyTypingTypeProvider.getTypeCommentValue(wholeText);
- if (typeText != null) {
- final int prefixLength = wholeText.length() - typeText.length();
- final List<PsiReference> references = parseTypeReferences(element, typeText, prefixLength);
- return ArrayUtil.toObjectArray(references, PsiReference.class);
- }
- return PsiReference.EMPTY_ARRAY;
- }
- });
- }
-
- @SuppressWarnings("Duplicates")
- @NotNull
- private static List<PsiReference> parseTypeReferences(@NotNull PsiElement anchor, @NotNull String typeText, int offsetInComment) {
- final List<PsiReference> result = new ArrayList<>();
- final PyTypeParser.ParseResult parseResult = PyTypeParser.parsePep484FunctionTypeComment(anchor, typeText);
- final Map<TextRange, ? extends PyType> types = parseResult.getTypes();
- final Map<? extends PyType, TextRange> fullRanges = parseResult.getFullRanges();
- for (Map.Entry<TextRange, ? extends PyType> pair : types.entrySet()) {
- final PyType t = pair.getValue();
- final TextRange range = pair.getKey().shiftRight(offsetInComment);
- final TextRange fullRange = fullRanges.containsKey(t) ? fullRanges.get(t).shiftRight(offsetInComment) : range;
- final PyImportElement importElement = parseResult.getImports().get(t);
- result.add(new DocStringTypeReference(anchor, range, fullRange, t, importElement));
- }
- return result;
- }
-}
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiLanguageInjectionHost;
import com.intellij.psi.util.PsiTreeUtil;
+import com.jetbrains.python.codeInsight.functionTypeComments.PyFunctionTypeAnnotationDialect;
import com.jetbrains.python.documentation.doctest.PyDocstringLanguageDialect;
import com.jetbrains.python.psi.PyAnnotation;
+import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
/**
+ * Injects fragments for type annotations either in string literals (quoted annotations containing forward references) or
+ * in type comments starting with <tt># type:</tt>.
+ *
* @author vlan
*/
public class PyTypingAnnotationInjector extends PyInjectorBase {
final Matcher m = PyTypingTypeProvider.TYPE_COMMENT_PATTERN.matcher(text);
if (m.matches()) {
final String annotationText = m.group(1);
- if (annotationText != null && isTypingAnnotation(annotationText)) {
+ if (annotationText != null) {
final int start = m.start(1);
final int end = m.end(1);
if (start < end && allowInjectionInComment(host)) {
- final Language language = PyDocstringLanguageDialect.getInstance();
- registrar.startInjecting(language);
- registrar.addPlace("", "", host, TextRange.create(start, end));
- registrar.doneInjecting();
- return new PyInjectionUtil.InjectionResult(true, true);
+ Language language = null;
+ if (isFunctionTypeComment(host)) {
+ language = PyFunctionTypeAnnotationDialect.INSTANCE;
+ }
+ else if (isTypingAnnotation(annotationText)) {
+ language = PyDocstringLanguageDialect.getInstance();
+ }
+ if (language != null) {
+ registrar.startInjecting(language);
+ registrar.addPlace("", "", host, TextRange.create(start, end));
+ registrar.doneInjecting();
+ return new PyInjectionUtil.InjectionResult(true, true);
+ }
}
}
}
return PyInjectionUtil.InjectionResult.EMPTY;
}
+ private static boolean isFunctionTypeComment(@NotNull PsiElement comment) {
+ final PyFunction function = PsiTreeUtil.getParentOfType(comment, PyFunction.class);
+ return function != null && function.getTypeComment() == comment;
+ }
+
private static boolean isTypingAnnotation(@NotNull String s) {
return RE_TYPING_ANNOTATION.matcher(s).matches();
}
private static boolean allowInjectionInComment(@NotNull PsiLanguageInjectionHost host) {
// XXX: Don't inject PyDocstringLanguage during completion inside comments due to an exception related to finding ShredImpl's
// hostElementPointer
- if (CompletionUtil.getOriginalOrSelf(host) != host) {
- return false;
- }
- if (PyFunctionTypeCommentReferenceContributor.TYPE_COMMENT_PATTERN.accepts(host)) {
- return false;
- }
- return true;
+ return CompletionUtil.getOriginalOrSelf(host) == host;
}
}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments;
+
+import com.intellij.lang.InjectableLanguage;
+import com.intellij.lang.Language;
+import com.jetbrains.python.PythonLanguage;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyFunctionTypeAnnotationDialect extends Language implements InjectableLanguage {
+ public static final PyFunctionTypeAnnotationDialect INSTANCE = new PyFunctionTypeAnnotationDialect();
+
+ protected PyFunctionTypeAnnotationDialect() {
+ super(PythonLanguage.getInstance(), "PyFunctionTypeComment");
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments;
+
+import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeAnnotation;
+import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyParameterTypeList;
+import com.jetbrains.python.psi.PyElementType;
+
+/**
+ * @author Mikhail Golubev
+ */
+public interface PyFunctionTypeAnnotationElementTypes {
+ PyElementType FUNCTION_SIGNATURE = new PyElementType("FUNCTION_SIGNATURE", PyFunctionTypeAnnotation.class);
+ PyElementType PARAMETER_TYPE_LIST = new PyElementType("PARAMETER_TYPE_LIST", PyParameterTypeList.class);
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments;
+
+import com.jetbrains.python.psi.PyFileElementType;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyFunctionTypeAnnotationFileElementType extends PyFileElementType {
+ public static final PyFunctionTypeAnnotationFileElementType INSTANCE =
+ new PyFunctionTypeAnnotationFileElementType(PyFunctionTypeAnnotationDialect.INSTANCE);
+
+ public PyFunctionTypeAnnotationFileElementType(PyFunctionTypeAnnotationDialect instance) {
+ super(instance);
+ }
+
+ @NotNull
+ @Override
+ public String getExternalId() {
+ return "PyFunctionTypeComment.ID";
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments;
+
+import com.jetbrains.python.PythonFileType;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyFunctionTypeAnnotationFileType extends PythonFileType {
+ public static final PyFunctionTypeAnnotationFileType INSTANCE = new PyFunctionTypeAnnotationFileType();
+
+ public PyFunctionTypeAnnotationFileType() {
+ super(PyFunctionTypeAnnotationDialect.INSTANCE);
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return "PythonFunctionTypeComment";
+ }
+
+ @NotNull
+ @Override
+ public String getDescription() {
+ return "Python PEP-484 function type comment";
+ }
+
+ @NotNull
+ @Override
+ public String getDefaultExtension() {
+ return "functionTypeComment";
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments;
+
+import com.intellij.lang.PsiBuilder;
+import com.intellij.psi.tree.IElementType;
+import com.jetbrains.python.PyElementTypes;
+import com.jetbrains.python.PyTokenTypes;
+import com.jetbrains.python.documentation.doctest.PyDocstringTokenTypes;
+import com.jetbrains.python.parsing.ExpressionParsing;
+import com.jetbrains.python.parsing.ParsingContext;
+import com.jetbrains.python.parsing.PyParser;
+import com.jetbrains.python.parsing.StatementParsing;
+import com.jetbrains.python.psi.LanguageLevel;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyFunctionTypeAnnotationParser extends PyParser {
+ @Override
+ protected ParsingContext createParsingContext(PsiBuilder builder, LanguageLevel languageLevel, StatementParsing.FUTURE futureFlag) {
+ return new ParsingContext(builder, languageLevel, futureFlag) {
+ private StatementParsing myStatementParsing = new AnnotationParser(this, futureFlag);
+ private ExpressionParsing myExpressionParsing = new ExpressionParsing(this) {
+ @Override
+ protected IElementType getReferenceType() {
+ return PyDocstringTokenTypes.DOC_REFERENCE;
+ }
+ };
+
+ @Override
+ public StatementParsing getStatementParser() {
+ return myStatementParsing;
+ }
+
+ @Override
+ public ExpressionParsing getExpressionParser() {
+ return myExpressionParsing;
+ }
+ };
+ }
+
+ private static class AnnotationParser extends StatementParsing {
+ public AnnotationParser(ParsingContext context, @Nullable FUTURE futureFlag) {
+ super(context, futureFlag);
+ }
+
+ @Override
+ public void parseStatement() {
+ if (myBuilder.eof()) return;
+ parseFunctionType();
+ }
+
+ private void parseFunctionType() {
+ if (atToken(PyTokenTypes.LPAR)) {
+ final PsiBuilder.Marker funcTypeMark = myBuilder.mark();
+ parseParameterTypeList();
+ checkMatches(PyTokenTypes.RARROW, "'->' expected");
+ final boolean parsed = getExpressionParser().parseSingleExpression(false);
+ if (!parsed) {
+ myBuilder.error("expression expected");
+ }
+ funcTypeMark.done(PyFunctionTypeAnnotationElementTypes.FUNCTION_SIGNATURE);
+ }
+ recoverUntilMatches("unexpected tokens");
+ }
+
+ private void parseParameterTypeList() {
+ assert atToken(PyTokenTypes.LPAR);
+ final PsiBuilder.Marker listMark = myBuilder.mark();
+ myBuilder.advanceLexer();
+
+ final ExpressionParsing exprParser = getExpressionParser();
+ int paramCount = 0;
+ while (!(atAnyOfTokens(PyTokenTypes.RPAR, PyTokenTypes.RARROW, PyTokenTypes.STATEMENT_BREAK) || myBuilder.eof()) ) {
+ if (paramCount > 0) {
+ checkMatches(PyTokenTypes.COMMA, "',' expected");
+ }
+ boolean parsed;
+ if (atAnyOfTokens(PyTokenTypes.MULT, PyTokenTypes.EXP)) {
+ final PsiBuilder.Marker starredExprMarker = myBuilder.mark();
+ myBuilder.advanceLexer();
+ parsed = exprParser.parseSingleExpression(false);
+ starredExprMarker.done(PyElementTypes.STAR_EXPRESSION);
+ }
+ else {
+ parsed = exprParser.parseSingleExpression(false);
+ }
+ if (!parsed) {
+ myBuilder.error("expression expected");
+ recoverUntilMatches("expression expected", PyTokenTypes.COMMA, PyTokenTypes.RPAR, PyTokenTypes.RARROW, PyTokenTypes.STATEMENT_BREAK);
+ }
+ paramCount++;
+ }
+ checkMatches(PyTokenTypes.RPAR, "')' expected");
+ listMark.done(PyFunctionTypeAnnotationElementTypes.PARAMETER_TYPE_LIST);
+ }
+
+ private void recoverUntilMatches(@NotNull String errorMessage, @NotNull IElementType... types) {
+ final PsiBuilder.Marker errorMarker = myBuilder.mark();
+ boolean hasNonWhitespaceTokens = false;
+ while (!(atAnyOfTokens(types) || myBuilder.eof())) {
+ // Regular whitespace tokens are already skipped by advancedLexer()
+ if (!atToken(PyTokenTypes.STATEMENT_BREAK)) {
+ hasNonWhitespaceTokens = true;
+ }
+ myBuilder.advanceLexer();
+ }
+ if (hasNonWhitespaceTokens) {
+ errorMarker.error(errorMessage);
+ }
+ else {
+ errorMarker.drop();
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments;
+
+import com.intellij.lang.PsiParser;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.FileViewProvider;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.tree.IFileElementType;
+import com.intellij.psi.tree.TokenSet;
+import com.jetbrains.python.PythonParserDefinition;
+import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeAnnotationFile;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyFunctionTypeAnnotationParserDefinition extends PythonParserDefinition {
+
+ @NotNull
+ @Override
+ public TokenSet getCommentTokens() {
+ return TokenSet.EMPTY;
+ }
+
+ @Override
+ public PsiFile createFile(FileViewProvider viewProvider) {
+ return new PyFunctionTypeAnnotationFile(viewProvider);
+ }
+
+ @Override
+ public IFileElementType getFileNodeType() {
+ return PyFunctionTypeAnnotationFileElementType.INSTANCE;
+ }
+
+ @NotNull
+ @Override
+ public PsiParser createParser(Project project) {
+ return new PyFunctionTypeAnnotationParser();
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments.psi;
+
+import com.intellij.lang.ASTNode;
+import com.jetbrains.python.psi.PyExpression;
+import com.jetbrains.python.psi.impl.PyElementImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyFunctionTypeAnnotation extends PyElementImpl {
+ public PyFunctionTypeAnnotation(ASTNode astNode) {
+ super(astNode);
+ }
+
+ @NotNull
+ public PyParameterTypeList getParameterTypeList() {
+ return findNotNullChildByClass(PyParameterTypeList.class);
+ }
+
+ @Nullable
+ public PyExpression getReturnType() {
+ return findChildByClass(PyExpression.class);
+ }
+}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments.psi;
+
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.psi.FileViewProvider;
+import com.jetbrains.python.codeInsight.functionTypeComments.PyFunctionTypeAnnotationDialect;
+import com.jetbrains.python.codeInsight.functionTypeComments.PyFunctionTypeAnnotationFileType;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.impl.PyFileImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyFunctionTypeAnnotationFile extends PyFileImpl {
+
+ public PyFunctionTypeAnnotationFile(FileViewProvider viewProvider) {
+ super(viewProvider, PyFunctionTypeAnnotationDialect.INSTANCE);
+ }
+
+ @NotNull
+ @Override
+ public FileType getFileType() {
+ return PyFunctionTypeAnnotationFileType.INSTANCE;
+ }
+
+ @Override
+ public String toString() {
+ return "FunctionTypeComment:" + getName();
+ }
+
+ @Override
+ public LanguageLevel getLanguageLevel() {
+ // The same as for .pyi files
+ return LanguageLevel.PYTHON35;
+ }
+
+ @Nullable
+ public PyFunctionTypeAnnotation getAnnotation() {
+ return findChildByClass(PyFunctionTypeAnnotation.class);
+ }
+}
+
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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.codeInsight.functionTypeComments.psi;
+
+import com.intellij.lang.ASTNode;
+import com.jetbrains.python.PythonDialectsTokenSetProvider;
+import com.jetbrains.python.psi.PyExpression;
+import com.jetbrains.python.psi.impl.PyElementImpl;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyParameterTypeList extends PyElementImpl {
+ public PyParameterTypeList(ASTNode astNode) {
+ super(astNode);
+ }
+
+ @NotNull
+ public List<PyExpression> getParameterTypes() {
+ return findChildrenByType(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens());
+ }
+}
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.codeInsight.PyCustomMember;
-import com.jetbrains.python.codeInsight.PyFunctionTypeCommentReferenceContributor;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.codeInsight.imports.AutoImportHintAction;
if (comment instanceof PsiLanguageInjectionHost) {
processInjection((PsiLanguageInjectionHost)comment);
}
- if (PyFunctionTypeCommentReferenceContributor.TYPE_COMMENT_PATTERN.accepts(comment)) {
- for (PsiReference reference : comment.getReferences()) {
- if (reference instanceof PsiPolyVariantReference) {
- markTargetImportsAsUsed((PsiPolyVariantReference)reference);
- }
- }
- }
}
@Override
*/
package com.jetbrains.python.validation;
+import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyParameterTypeList;
import com.jetbrains.python.psi.PyStarExpression;
/**
@Override
public void visitPyStarExpression(PyStarExpression node) {
super.visitPyStarExpression(node);
- if (!node.isAssignmentTarget() && !node.isUnpacking()) {
+ if (!node.isAssignmentTarget() && !node.isUnpacking() && !(node.getParent() instanceof PyParameterTypeList)) {
getHolder().createErrorAnnotation(node, "Can't use starred expression here");
}
}
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyReferenceExpression: int
+ PsiElement(Py:IDENTIFIER)('int')
+ PsiElement(Py:COMMA)(',')
+ PsiErrorElement:expression expected
+ <empty list>
+ PsiElement(Py:RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(Py:RARROW)('->')
+ PsiWhiteSpace(' ')
+ PyNoneLiteralExpression
+ PsiElement(Py:NONE_KEYWORD)('None')
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PsiErrorElement:')' expected
+ <empty list>
+ PsiErrorElement:unexpected tokens
+ PsiElement(Py:DEF_KEYWORD)('def')
+ PsiWhiteSpace(' ')
+ PsiElement(Py:IDENTIFIER)('foo')
+ PsiElement(Py:RPAR)(')')
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyNoneLiteralExpression
+ PsiElement(Py:DOT)('.')
+ PsiElement(Py:DOT)('.')
+ PsiElement(Py:DOT)('.')
+ PsiElement(Py:RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(Py:RARROW)('->')
+ PsiWhiteSpace(' ')
+ PyNoneLiteralExpression
+ PsiElement(Py:NONE_KEYWORD)('None')
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PsiElement(Py:RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(Py:RARROW)('->')
+ PsiWhiteSpace(' ')
+ PyNoneLiteralExpression
+ PsiElement(Py:NONE_KEYWORD)('None')
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ <empty list>
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyLambdaExpression
+ PsiElement(Py:LAMBDA_KEYWORD)('lambda')
+ PyParameterList
+ <empty list>
+ PsiElement(Py:COLON)(':')
+ PsiWhiteSpace(' ')
+ PyNumericLiteralExpression
+ PsiElement(Py:INTEGER_LITERAL)('42')
+ PsiErrorElement:')' expected
+ <empty list>
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyReferenceExpression: int
+ PsiElement(Py:IDENTIFIER)('int')
+ PsiElement(Py:RPAR)(')')
+ PsiErrorElement:'->' expected
+ <empty list>
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyReferenceExpression: int
+ PsiElement(Py:IDENTIFIER)('int')
+ PsiErrorElement:')' expected
+ <empty list>
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyReferenceExpression: int
+ PsiElement(Py:IDENTIFIER)('int')
+ PsiElement(Py:RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(Py:RARROW)('->')
+ PsiErrorElement:expression expected
+ <empty list>
+ PsiWhiteSpace(' ')
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyStarExpression
+ PsiElement(Py:MULT)('*')
+ PsiErrorElement:expression expected
+ <empty list>
+ PsiElement(Py:RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(Py:RARROW)('->')
+ PsiWhiteSpace(' ')
+ PyReferenceExpression: int
+ PsiElement(Py:IDENTIFIER)('int')
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyReferenceExpression: int
+ PsiElement(Py:IDENTIFIER)('int')
+ PsiElement(Py:COMMA)(',')
+ PsiWhiteSpace(' ')
+ PyReferenceExpression: str
+ PsiElement(Py:IDENTIFIER)('str')
+ PsiElement(Py:RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(Py:RARROW)('->')
+ PsiWhiteSpace(' ')
+ PyNoneLiteralExpression
+ PsiElement(Py:NONE_KEYWORD)('None')
\ No newline at end of file
--- /dev/null
+FunctionTypeComment:a.functionTypeComment
+ PyFunctionTypeAnnotation
+ PyParameterTypeList
+ PsiElement(Py:LPAR)('(')
+ PyStarExpression
+ PsiElement(Py:MULT)('*')
+ PyReferenceExpression: int
+ PsiElement(Py:IDENTIFIER)('int')
+ PsiElement(Py:COMMA)(',')
+ PsiWhiteSpace(' ')
+ PyStarExpression
+ PsiElement(Py:EXP)('**')
+ PyReferenceExpression: str
+ PsiElement(Py:IDENTIFIER)('str')
+ PsiElement(Py:RPAR)(')')
+ PsiWhiteSpace(' ')
+ PsiElement(Py:RARROW)('->')
+ PsiWhiteSpace(' ')
+ PyNoneLiteralExpression
+ PsiElement(Py:NONE_KEYWORD)('None')
\ No newline at end of file
+++ /dev/null
-class MyClass:
- pass
-
-def f(*args):
- # type: (*MyClass) -> None
- <ref>
- pass
\ No newline at end of file
--- /dev/null
+class MyClass:
+ pass
+
+
+def f(x):
+ # type: (MyClass) -> Any
+ <ref>
+ pass
\ No newline at end of file
--- /dev/null
+class MyClass:
+ pass
+
+
+def f(x):
+ # type: (Any) -> MyClass
+ <ref>
+ pass
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * 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;
+
+import com.intellij.testFramework.ParsingTestCase;
+import com.jetbrains.python.codeInsight.functionTypeComments.PyFunctionTypeAnnotationParserDefinition;
+import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeAnnotationFile;
+import com.jetbrains.python.codeInsight.functionTypeComments.psi.PyFunctionTypeAnnotation;
+import com.jetbrains.python.documentation.doctest.PyDocstringTokenSetContributor;
+import com.jetbrains.python.psi.PyExpression;
+import com.jetbrains.python.psi.PyNoneLiteralExpression;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * @author Mikhail Golubev
+ */
+public class PyFunctionTypeAnnotationParsingTest extends ParsingTestCase {
+ public PyFunctionTypeAnnotationParsingTest() {
+ super("functionTypeComment/parsing", "functionTypeComment", new PyFunctionTypeAnnotationParserDefinition(), new PythonParserDefinition());
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ registerExtensionPoint(PythonDialectsTokenSetContributor.EP_NAME, PythonDialectsTokenSetContributor.class);
+ registerExtension(PythonDialectsTokenSetContributor.EP_NAME, new PythonTokenSetContributor());
+ registerExtension(PythonDialectsTokenSetContributor.EP_NAME, new PyDocstringTokenSetContributor());
+ }
+
+ protected void doCodeTest(@NotNull String typeAnnotation) {
+ try {
+ super.doCodeTest(typeAnnotation);
+ }
+ catch (IOException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Nullable
+ private PyFunctionTypeAnnotation getParsedAnnotation() {
+ final PyFunctionTypeAnnotationFile file = assertInstanceOf(myFile, PyFunctionTypeAnnotationFile.class);
+ return file.getAnnotation();
+ }
+
+ public void testEmpty() {
+ doCodeTest("() -> None");
+
+ final PyFunctionTypeAnnotation annotation = getParsedAnnotation();
+ assertNotNull(annotation);
+ assertEmpty(annotation.getParameterTypeList().getParameterTypes());
+ final PyExpression returnType = annotation.getReturnType();
+ assertNotNull(returnType);
+ assertEquals("None", returnType.getText());
+ }
+
+ public void testSimple() {
+ doCodeTest("(int, str) -> None");
+ }
+
+ public void testVarargs() {
+ doCodeTest("(*int, **str) -> None");
+ }
+
+ public void testEllipsis() {
+ doCodeTest("(...) -> None");
+ final PyFunctionTypeAnnotation annotation = getParsedAnnotation();
+ final List<PyExpression> paramTypes = annotation.getParameterTypeList().getParameterTypes();
+ assertSize(1, paramTypes);
+ assertInstanceOf(paramTypes.get(0), PyNoneLiteralExpression.class);
+ final PyExpression returnType = annotation.getReturnType();
+ assertNotNull(returnType);
+ }
+
+ public void testNoReturnType() {
+ doCodeTest("(int) -> ");
+ final PyFunctionTypeAnnotation annotation = getParsedAnnotation();
+ final List<PyExpression> paramTypes = annotation.getParameterTypeList().getParameterTypes();
+ assertSize(1, paramTypes);
+ assertNull(annotation.getReturnType());
+ }
+
+ public void testNoArrowAndReturnType() {
+ doCodeTest("(int)");
+ }
+
+ public void testNoClosingParenthesis() {
+ doCodeTest("(int");
+ }
+
+ public void testDanglingComma() {
+ doCodeTest("(int,) -> None");
+ }
+
+ public void testEmptyFunctionType() {
+ doCodeTest("");
+ assertNull(getParsedAnnotation());
+ }
+
+ public void testNoTypeAfterStar() {
+ doCodeTest("(*) -> int");
+ }
+
+ public void testLambdaAsFirstType() {
+ doCodeTest("(lambda: 42");
+ }
+
+ public void testDefAsFirstType() {
+ doCodeTest("(def foo)");
+ }
+
+ @Override
+ protected String getTestDataPath() {
+ return PythonTestUtil.getTestDataPath();
+ }
+}
public void testQuotedTypeReferenceTopLevel() {
assertResolvesTo(LanguageLevel.PYTHON30, PyClass.class, "MyClass");
}
+
+ // PY-20377
+ public void testFunctionTypeCommentParamTypeReference() {
+ assertResolvesTo(PyClass.class, "MyClass");
+ }
+
+ // PY-20377
+ public void testFunctionTypeCommentReturnTypeReference() {
+ assertResolvesTo(PyClass.class, "MyClass");
+ }
}
assertEquals("kwg", ((PyStringLiteralExpression)target).getStringValue());
}
- // PY-18254
- public void testFunctionTypeComment() {
- assertResolvesTo(PyClass.class, "MyClass");
- }
-
public void testPercentStringKeyWordArgWithParentheses() {
PsiElement target = resolve();
assertTrue(target instanceof PyStringLiteralExpression);
// PY-18521
public void testFunctionTypeCommentUsesImportsFromTyping() {
myFixture.copyDirectoryToProject("typing", "");
- doTest();
+ runWithLanguageLevel(LanguageLevel.PYTHON30, this::doTest);
}
// PY-19084
protected Class<? extends PyInspection> getInspectionClass() {
return PyUnresolvedReferencesInspection.class;
}
-}
+}
\ No newline at end of file