mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Don't run code analysis for injections with interpolation in string formatting (PY-11962)
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
<orderEntry type="module" module-name="core-api" />
|
||||
<orderEntry type="module" module-name="IntelliLang" />
|
||||
<orderEntry type="module" module-name="python-community" />
|
||||
<orderEntry type="module" module-name="lang-impl" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
+11
@@ -1,7 +1,10 @@
|
||||
package com.jetbrains.python.intelliLang;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.injection.MultiHostRegistrar;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.jetbrains.python.codeInsight.PyInjectionUtil;
|
||||
import com.jetbrains.python.codeInsight.PyInjectorBase;
|
||||
import org.intellij.plugins.intelliLang.Configuration;
|
||||
import org.intellij.plugins.intelliLang.inject.InjectedLanguage;
|
||||
@@ -15,6 +18,14 @@ import org.jetbrains.annotations.Nullable;
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyConfigurationInjector extends PyInjectorBase {
|
||||
@Override
|
||||
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
|
||||
final PyInjectionUtil.InjectionResult result = registerInjection(registrar, context);
|
||||
if (!result.isStrict()) {
|
||||
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Language getInjectedLanguage(@NotNull PsiElement context) {
|
||||
|
||||
+7
-1
@@ -5,6 +5,8 @@ import com.intellij.lang.injection.MultiHostRegistrar;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiLanguageInjectionHost;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.jetbrains.python.codeInsight.PyInjectionUtil;
|
||||
import com.jetbrains.python.codeInsight.PyInjectorBase;
|
||||
import org.intellij.plugins.intelliLang.inject.InjectedLanguage;
|
||||
import org.intellij.plugins.intelliLang.inject.InjectorUtils;
|
||||
@@ -18,9 +20,13 @@ import org.jetbrains.annotations.Nullable;
|
||||
public class PyTemporaryInjector extends PyInjectorBase {
|
||||
@Override
|
||||
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
|
||||
if (registerInjection(registrar, context)) {
|
||||
final PyInjectionUtil.InjectionResult result = registerInjection(registrar, context);
|
||||
if (result.isInjected()) {
|
||||
final TemporaryPlacesRegistry registry = TemporaryPlacesRegistry.getInstance(context.getProject());
|
||||
InjectorUtils.registerSupport(registry.getLanguageInjectionSupport(), false, registrar);
|
||||
if (!result.isStrict()) {
|
||||
InjectorUtils.putInjectedFileUserData(registrar, InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,31 @@ import static com.jetbrains.python.inspections.PyStringFormatParser.*;
|
||||
* @author vlan
|
||||
*/
|
||||
public class PyInjectionUtil {
|
||||
|
||||
public static class InjectionResult {
|
||||
public static InjectionResult EMPTY = new InjectionResult(false, true);
|
||||
|
||||
private final boolean myInjected;
|
||||
private final boolean myStrict;
|
||||
|
||||
private InjectionResult(boolean injected, boolean strict) {
|
||||
myInjected = injected;
|
||||
myStrict = strict;
|
||||
}
|
||||
|
||||
public boolean isInjected() {
|
||||
return myInjected;
|
||||
}
|
||||
|
||||
public boolean isStrict() {
|
||||
return myStrict;
|
||||
}
|
||||
|
||||
public InjectionResult append(@NotNull InjectionResult result) {
|
||||
return new InjectionResult(myInjected || result.isInjected(), myStrict && result.isStrict());
|
||||
}
|
||||
}
|
||||
|
||||
public static final List<Class<? extends PyExpression>> ELEMENTS_TO_INJECT_IN =
|
||||
Arrays.asList(PyStringLiteralExpression.class, PyParenthesizedExpression.class, PyBinaryExpression.class, PyCallExpression.class);
|
||||
|
||||
@@ -54,8 +79,9 @@ public class PyInjectionUtil {
|
||||
* Registers language injections in the given registrar for the specified string literal element or its ancestor that contains
|
||||
* string concatenations or formatting.
|
||||
*/
|
||||
public static void registerStringLiteralInjection(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) {
|
||||
processStringLiteral(element, registrar, "", "", Formatting.PERCENT);
|
||||
@NotNull
|
||||
public static InjectionResult registerStringLiteralInjection(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar) {
|
||||
return processStringLiteral(element, registrar, "", "", Formatting.PERCENT);
|
||||
}
|
||||
|
||||
private static boolean isStringLiteralPart(@NotNull PsiElement element, @Nullable PsiElement context) {
|
||||
@@ -101,10 +127,13 @@ public class PyInjectionUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void processStringLiteral(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar, @NotNull String prefix,
|
||||
@NotNull String suffix, @NotNull Formatting formatting) {
|
||||
final String missingValue = "missing";
|
||||
@NotNull
|
||||
private static InjectionResult processStringLiteral(@NotNull PsiElement element, @NotNull MultiHostRegistrar registrar,
|
||||
@NotNull String prefix, @NotNull String suffix, @NotNull Formatting formatting) {
|
||||
final String missingValue = "missing_value";
|
||||
if (element instanceof PyStringLiteralExpression) {
|
||||
boolean injected = false;
|
||||
boolean strict = true;
|
||||
final PyStringLiteralExpression expr = (PyStringLiteralExpression)element;
|
||||
final List<TextRange> ranges = expr.getStringValueTextRanges();
|
||||
final String text = expr.getText();
|
||||
@@ -112,9 +141,8 @@ public class PyInjectionUtil {
|
||||
if (formatting != Formatting.NONE) {
|
||||
final String part = range.substring(text);
|
||||
final List<FormatStringChunk> chunks = formatting == Formatting.NEW_STYLE ? parseNewStyleFormat(part) : parsePercentFormat(part);
|
||||
if (chunks.isEmpty()) {
|
||||
registrar.addPlace(prefix, suffix, expr, range);
|
||||
continue;
|
||||
if (!filterSubstitutions(chunks).isEmpty()) {
|
||||
strict = false;
|
||||
}
|
||||
for (int i = 0; i < chunks.size(); i++) {
|
||||
final FormatStringChunk chunk = chunks.get(i);
|
||||
@@ -141,18 +169,21 @@ public class PyInjectionUtil {
|
||||
}
|
||||
final TextRange chunkRange = chunk.getTextRange().shiftRight(range.getStartOffset());
|
||||
registrar.addPlace(chunkPrefix, chunkSuffix, expr, chunkRange);
|
||||
injected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
registrar.addPlace(prefix, suffix, expr, range);
|
||||
injected = true;
|
||||
}
|
||||
}
|
||||
return new InjectionResult(injected, strict);
|
||||
}
|
||||
else if (element instanceof PyParenthesizedExpression) {
|
||||
final PyExpression contained = ((PyParenthesizedExpression)element).getContainedExpression();
|
||||
if (contained != null) {
|
||||
processStringLiteral(contained, registrar, prefix, suffix, formatting);
|
||||
return processStringLiteral(contained, registrar, prefix, suffix, formatting);
|
||||
}
|
||||
}
|
||||
else if (element instanceof PyBinaryExpression) {
|
||||
@@ -162,23 +193,26 @@ public class PyInjectionUtil {
|
||||
final boolean isLeftString = isStringLiteralPart(left, null);
|
||||
if (expr.isOperator("+")) {
|
||||
final boolean isRightString = right != null && isStringLiteralPart(right, null);
|
||||
InjectionResult result = InjectionResult.EMPTY;
|
||||
if (isLeftString) {
|
||||
processStringLiteral(left, registrar, prefix, isRightString ? "" : missingValue, formatting);
|
||||
result = result.append(processStringLiteral(left, registrar, prefix, isRightString ? "" : missingValue, formatting));
|
||||
}
|
||||
if (isRightString) {
|
||||
processStringLiteral(right, registrar, isLeftString ? "" : missingValue, suffix, formatting);
|
||||
result = result.append(processStringLiteral(right, registrar, isLeftString ? "" : missingValue, suffix, formatting));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if (expr.isOperator("%")) {
|
||||
processStringLiteral(left, registrar, prefix, suffix, Formatting.PERCENT);
|
||||
return processStringLiteral(left, registrar, prefix, suffix, Formatting.PERCENT);
|
||||
}
|
||||
}
|
||||
else if (element instanceof PyCallExpression) {
|
||||
final PyExpression qualifier = getFormatCallQualifier((PyCallExpression)element);
|
||||
if (qualifier != null) {
|
||||
processStringLiteral(qualifier, registrar, prefix, suffix, Formatting.NEW_STYLE);
|
||||
return processStringLiteral(qualifier, registrar, prefix, suffix, Formatting.NEW_STYLE);
|
||||
}
|
||||
}
|
||||
return InjectionResult.EMPTY;
|
||||
}
|
||||
|
||||
private enum Formatting {
|
||||
|
||||
@@ -27,17 +27,19 @@ public abstract class PyInjectorBase implements MultiHostInjector {
|
||||
@Nullable
|
||||
public abstract Language getInjectedLanguage(@NotNull PsiElement context);
|
||||
|
||||
protected boolean registerInjection(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
|
||||
protected PyInjectionUtil.InjectionResult registerInjection(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) {
|
||||
final Language language = getInjectedLanguage(context);
|
||||
if (language != null) {
|
||||
final PsiElement element = PyInjectionUtil.getLargestStringLiteral(context);
|
||||
if (element != null) {
|
||||
registrar.startInjecting(language);
|
||||
PyInjectionUtil.registerStringLiteralInjection(element, registrar);
|
||||
registrar.doneInjecting();
|
||||
return true;
|
||||
final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(element, registrar);
|
||||
if (result.isInjected()) {
|
||||
registrar.doneInjecting();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return PyInjectionUtil.InjectionResult.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,13 @@ package com.jetbrains.python.codeInsight.regexp;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.injection.MultiHostInjector;
|
||||
import com.intellij.lang.injection.MultiHostRegistrar;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiPolyVariantReference;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl;
|
||||
import com.intellij.psi.impl.source.tree.injected.Place;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.codeInsight.PyInjectionUtil;
|
||||
@@ -81,8 +86,16 @@ public class PythonRegexpInjector implements MultiHostInjector {
|
||||
element.getContainingFile().getName().equals("re.py") && isRegexpMethod(element, index)) {
|
||||
final Language language = isVerbose(call) ? PythonVerboseRegexpLanguage.INSTANCE : PythonRegexpLanguage.INSTANCE;
|
||||
registrar.startInjecting(language);
|
||||
PyInjectionUtil.registerStringLiteralInjection(context, registrar);
|
||||
registrar.doneInjecting();
|
||||
final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(context, registrar);
|
||||
if (result.isInjected()) {
|
||||
registrar.doneInjecting();
|
||||
if (!result.isStrict()) {
|
||||
final PsiFile file = getInjectedFile(registrar);
|
||||
if (file != null) {
|
||||
file.putUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,6 +103,13 @@ public class PythonRegexpInjector implements MultiHostInjector {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiFile getInjectedFile(@NotNull MultiHostRegistrar registrar) {
|
||||
// Don't add a dependency on IntelliLang here now, but this injector should become IntelliLang-based in the future
|
||||
final List<Pair<Place,PsiFile>> result = ((MultiHostRegistrarImpl)registrar).getResult();
|
||||
return result == null || result.isEmpty() ? null : result.get(result.size() - 1).second;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<? extends Class<? extends PsiElement>> elementsToInjectIn() {
|
||||
|
||||
@@ -127,7 +127,7 @@ public class PyRegexpTest extends PyTestCase {
|
||||
"\n" +
|
||||
"def f(x, y):\n" +
|
||||
" re.search('<caret>.*(' + x + ')' + y, 'foo')\n",
|
||||
".*(missing)missing");
|
||||
".*(missing_value)missing_value");
|
||||
}
|
||||
|
||||
public void testPercentFormattingRegexpAutoInjection() {
|
||||
@@ -135,7 +135,7 @@ public class PyRegexpTest extends PyTestCase {
|
||||
"\n" +
|
||||
"def f(x, y):\n" +
|
||||
" re.search('<caret>.*%s-%d' % (x, y), 'foo')\n",
|
||||
".*missing-missing");
|
||||
".*missing_value-missing_value");
|
||||
}
|
||||
|
||||
public void testNewStyleFormattingRegexpAutoInjection() {
|
||||
@@ -143,7 +143,7 @@ public class PyRegexpTest extends PyTestCase {
|
||||
"\n" +
|
||||
"def f(x, y):\n" +
|
||||
" re.search('<caret>.*{foo}-{}'.format(x, foo=y), 'foo')\n",
|
||||
".*missing-missing");
|
||||
".*missing_value-missing_value");
|
||||
}
|
||||
|
||||
public void testNewStyleFormattingEndsWithConstant() {
|
||||
@@ -151,7 +151,7 @@ public class PyRegexpTest extends PyTestCase {
|
||||
"\n" +
|
||||
"def f(**kwargs):" +
|
||||
" re.search('<caret>(foo{bar}baz$)'.format(**kwargs), 'foo')\n",
|
||||
"(foomissingbaz$)");
|
||||
"(foomissing_valuebaz$)");
|
||||
}
|
||||
|
||||
private void doTestInjectedText(@NotNull String text, @NotNull String expected) {
|
||||
|
||||
Reference in New Issue
Block a user