From 42fc094e4e8fbfd4067c842862417493e6f208d7 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 28 Sep 2016 22:11:20 +0300 Subject: [PATCH] PY-16098 Fixed: Warn that async and await will become keywords in Python 3.7 Warn about variables, classes and functions called "await" or "async" in Pythons 3.5 and 3.6. Suggest quick fix to rename such nodes --- .../PyCompatibilityInspection.java | 37 +++++++++-- .../warningAboutAsyncAndAwaitInPy35.py | 14 +++++ .../warningAboutAsyncAndAwaitInPy36.py | 14 +++++ .../renameAsyncClassInPy35.py | 2 + .../renameAsyncClassInPy35_after.py | 2 + .../renameAsyncClassInPy36.py | 2 + .../renameAsyncClassInPy36_after.py | 2 + .../renameAsyncFunctionInPy35.py | 2 + .../renameAsyncFunctionInPy35_after.py | 2 + .../renameAsyncFunctionInPy36.py | 2 + .../renameAsyncFunctionInPy36_after.py | 2 + .../renameAsyncVariableInPy35.py | 1 + .../renameAsyncVariableInPy35_after.py | 1 + .../renameAsyncVariableInPy36.py | 1 + .../renameAsyncVariableInPy36_after.py | 1 + .../renameAwaitClassInPy35.py | 2 + .../renameAwaitClassInPy35_after.py | 2 + .../renameAwaitClassInPy36.py | 2 + .../renameAwaitClassInPy36_after.py | 2 + .../renameAwaitFunctionInPy35.py | 2 + .../renameAwaitFunctionInPy35_after.py | 2 + .../renameAwaitFunctionInPy36.py | 2 + .../renameAwaitFunctionInPy36_after.py | 2 + .../renameAwaitVariableInPy35.py | 1 + .../renameAwaitVariableInPy35_after.py | 1 + .../renameAwaitVariableInPy36.py | 1 + .../renameAwaitVariableInPy36_after.py | 1 + .../PyCompatibilityInspectionTest.java | 10 +++ .../quickFixes/RenameElementQuickFixTest.java | 62 ++++++++++++++++++- 29 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py create mode 100644 python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py create mode 100644 python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py diff --git a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java index 4ee578511eec..8b2d0ec7dac7 100644 --- a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java @@ -27,15 +27,14 @@ import com.intellij.openapi.util.JDOMExternalizableStringList; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.profile.codeInspection.InspectionProjectProfileManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiElementVisitor; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiReference; +import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; +import com.jetbrains.python.inspections.quickfix.PyRenameElementQuickFix; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.types.PyClassType; @@ -325,5 +324,35 @@ public class PyCompatibilityInspection extends PyInspection { } } } + + @Override + public void visitPyTargetExpression(PyTargetExpression node) { + super.visitPyTargetExpression(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + @Override + public void visitPyClass(PyClass node) { + super.visitPyClass(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + @Override + public void visitPyFunction(PyFunction node) { + super.visitPyFunction(node); + warnAboutAsyncAndAwaitInPy35AndPy36(node); + } + + private void warnAboutAsyncAndAwaitInPy35AndPy36(@NotNull PsiNameIdentifierOwner nameIdentifierOwner) { + final PsiElement nameIdentifier = nameIdentifierOwner.getNameIdentifier(); + + if (nameIdentifier != null && ArrayUtil.contains(nameIdentifierOwner.getName(), PyNames.AWAIT, PyNames.ASYNC)) { + registerOnFirstMatchingVersion(level -> LanguageLevel.PYTHON35.equals(level) || LanguageLevel.PYTHON36.equals(level), + "'async' and 'await' are not recommended to be used as variable, class, function or module names. " + + "They will become proper keywords in Python 3.7.", + nameIdentifier, + new PyRenameElementQuickFix()); + } + } } } \ No newline at end of file diff --git a/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py new file mode 100644 index 000000000000..eea16c40ef25 --- /dev/null +++ b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy35.py @@ -0,0 +1,14 @@ +class async(object): + pass + +class await(object): + pass + +def async(): + pass + +def await(): + pass + +async = 1 +await = 2 \ No newline at end of file diff --git a/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py new file mode 100644 index 000000000000..eea16c40ef25 --- /dev/null +++ b/python/testData/inspections/PyCompatibilityInspection/warningAboutAsyncAndAwaitInPy36.py @@ -0,0 +1,14 @@ +class async(object): + pass + +class await(object): + pass + +def async(): + pass + +def await(): + pass + +async = 1 +await = 2 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py new file mode 100644 index 000000000000..bc25733ddf5d --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35.py @@ -0,0 +1,2 @@ +class async(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy35_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py new file mode 100644 index 000000000000..bc25733ddf5d --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36.py @@ -0,0 +1,2 @@ +class async(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncClassInPy36_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py new file mode 100644 index 000000000000..f48d631547da --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35.py @@ -0,0 +1,2 @@ +def async(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy35_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py new file mode 100644 index 000000000000..f48d631547da --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36.py @@ -0,0 +1,2 @@ +def async(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncFunctionInPy36_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py new file mode 100644 index 000000000000..96bae926eb40 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35.py @@ -0,0 +1 @@ +async = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy35_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py new file mode 100644 index 000000000000..96bae926eb40 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36.py @@ -0,0 +1 @@ +async = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAsyncVariableInPy36_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py new file mode 100644 index 000000000000..c531a434721e --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35.py @@ -0,0 +1,2 @@ +class await(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy35_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py new file mode 100644 index 000000000000..c531a434721e --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36.py @@ -0,0 +1,2 @@ +class await(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py new file mode 100644 index 000000000000..1a98a9cfc6c4 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitClassInPy36_after.py @@ -0,0 +1,2 @@ +class a(object): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py new file mode 100644 index 000000000000..c1ef9d6ccfae --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35.py @@ -0,0 +1,2 @@ +def await(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy35_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py new file mode 100644 index 000000000000..c1ef9d6ccfae --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36.py @@ -0,0 +1,2 @@ +def await(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py new file mode 100644 index 000000000000..690f3c163030 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitFunctionInPy36_after.py @@ -0,0 +1,2 @@ +def a(): + pass \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py new file mode 100644 index 000000000000..1a28415ce838 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35.py @@ -0,0 +1 @@ +await = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy35_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py new file mode 100644 index 000000000000..1a28415ce838 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36.py @@ -0,0 +1 @@ +await = 1 \ No newline at end of file diff --git a/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py new file mode 100644 index 000000000000..d25d49e0ffc3 --- /dev/null +++ b/python/testData/quickFixes/RenameElementQuickFixTest/renameAwaitVariableInPy36_after.py @@ -0,0 +1 @@ +a = 1 \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java index 4bc87459c36f..7afe19f1f695 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyCompatibilityInspectionTest.java @@ -209,6 +209,16 @@ public class PyCompatibilityInspectionTest extends PyTestCase { doTest(LanguageLevel.PYTHON36); } + // PY-16098 + public void testWarningAboutAsyncAndAwaitInPy35() { + doTest(LanguageLevel.PYTHON35); + } + + // PY-16098 + public void testWarningAboutAsyncAndAwaitInPy36() { + doTest(LanguageLevel.PYTHON36); + } + private void doTest(@NotNull LanguageLevel level) { runWithLanguageLevel(level, this::doTest); } diff --git a/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java b/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java index 5a63b49b66e6..66a01ae20e1f 100644 --- a/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java +++ b/python/testSrc/com/jetbrains/python/quickFixes/RenameElementQuickFixTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * 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. @@ -17,6 +17,7 @@ package com.jetbrains.python.quickFixes; import com.intellij.testFramework.TestDataPath; import com.jetbrains.python.PyQuickFixTestCase; +import com.jetbrains.python.inspections.PyCompatibilityInspection; import com.jetbrains.python.inspections.PyPep8NamingInspection; import com.jetbrains.python.inspections.PyProtectedMemberInspection; import com.jetbrains.python.inspections.PyShadowingBuiltinsInspection; @@ -47,4 +48,63 @@ public class RenameElementQuickFixTest extends PyQuickFixTestCase { doQuickFixTest(PyShadowingBuiltinsInspection.class, "Rename element"); } + // PY-16098 + public void testRenameAsyncClassInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncClassInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitClassInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitClassInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncFunctionInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncFunctionInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitFunctionInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitFunctionInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncVariableInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAsyncVariableInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitVariableInPy35() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } + + // PY-16098 + public void testRenameAwaitVariableInPy36() { + doQuickFixTest(PyCompatibilityInspection.class, "Rename element"); + } }