diff --git a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java index aa44bc27304b..9cfe433ae397 100644 --- a/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java +++ b/python/src/com/jetbrains/python/codeInsight/PySubstitutionChunkReference.java @@ -20,12 +20,14 @@ import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReferenceBase; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ArrayUtil; import com.jetbrains.python.inspections.PyStringFormatParser; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyPsiUtils; +import com.jetbrains.python.psi.resolve.PyResolveContext; import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,10 +36,11 @@ import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; -public class PySubstitutionChunkReference extends PsiReferenceBase implements PsiReferenceEx{ +public class PySubstitutionChunkReference extends PsiReferenceBase implements PsiReferenceEx { private final int myPosition; @NotNull private final PyStringFormatParser.SubstitutionChunk myChunk; private final boolean myIsPercent; + private final TypeEvalContext myTypeEvalContext; public PySubstitutionChunkReference(@NotNull final PyStringLiteralExpression element, @NotNull final PyStringFormatParser.SubstitutionChunk chunk, final int position) { @@ -45,8 +48,11 @@ public class PySubstitutionChunkReference extends PsiReferenceBase resolvedRef = resolvePositionalStarExpression(starArg, n); + final Ref resolvedRef = resolvePositionalStarExpression(starArg, n); if (resolvedRef != null) { final PsiElement resolved = resolvedRef.get(); if (resolved != null) { @@ -121,34 +127,252 @@ public class PySubstitutionChunkReference extends PsiReferenceBase resolveKeywordFormat(@NotNull PyArgumentList argumentList) { + final Ref valueExprRef = getKeyValueFromArguments(argumentList); + final String indexElement = ((PyStringFormatParser.NewStyleSubstitutionChunk)myChunk).getMappingKeyElementIndex(); + if (valueExprRef != null && !valueExprRef.isNull() && indexElement != null) { + final PyExpression valueExpr = PyPsiUtils.flattenParens(valueExprRef.get()); + assert valueExpr != null; + try { + final Integer index = Integer.valueOf(indexElement); + final Ref resolvedRef = resolveNumericIndex(valueExpr, index); + if (resolvedRef != null) return resolvedRef; + } + catch (NumberFormatException e) { + final Ref resolvedRef = resolveStringIndex(valueExpr, indexElement); + if (resolvedRef != null) return resolvedRef; + } } - else { - final List keywordStarArgs = getStarArguments(argumentList, true); - boolean notSureAboutStarArgs = false; + // valueExprRef is null only if there's no corresponding keyword argument and no star arguments + return valueExprRef == null ? Ref.create() : valueExprRef; + } + + @Nullable + private Ref getKeyValueFromArguments(@NotNull PyArgumentList argumentList) { + final PyKeywordArgument valueFromKeywordArg = argumentList.getKeywordArgument(myChunk.getMappingKey()); + final List keywordStarArgs = getStarArguments(argumentList, true); + + Ref valueExprRef = null; + if (valueFromKeywordArg != null) { + valueExprRef = Ref.create(valueFromKeywordArg.getValueExpression()); + } + else if (!keywordStarArgs.isEmpty()){ for (PyStarArgument arg : keywordStarArgs) { - final Ref resolvedRef = resolveKeywordStarExpression(arg); - if (resolvedRef != null) { - final PsiElement resolved = resolvedRef.get(); - if (resolved != null) { - return resolved; - } - } - else { - notSureAboutStarArgs = true; + final Ref resolvedRef = resolveKeywordStarExpression(arg); + if (resolvedRef != null && (valueExprRef == null || valueExprRef.get() == null)) { + valueExprRef = resolvedRef; } } - return notSureAboutStarArgs ? Iterables.getFirst(keywordStarArgs, null) : null; + if (valueExprRef == null) { + valueExprRef = Ref.create(Iterables.getFirst(keywordStarArgs, null)); + } } + return valueExprRef; + } + + @Nullable + private Ref resolveStringIndex(@NotNull PyExpression valueExpr, @NotNull String indexElement) { + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext); + if (valueExpr instanceof PyCallExpression) { + PyReturnStatement[] returnValues = getFunctionReturnValues((PyCallExpression)valueExpr, resolveContext, myTypeEvalContext); + for (PyReturnStatement value : returnValues) { + PyExpression returnValueExpr = PyPsiUtils.flattenParens(value.getExpression()); + if (returnValueExpr instanceof PyDictLiteralExpression) { + Ref resolvedRef = getElementFromDictLiteral((PyDictLiteralExpression)returnValueExpr, indexElement, resolveContext); + if (resolvedRef != null) return resolvedRef; + } + else if (returnValueExpr instanceof PyCallExpression) { + Ref resolvedRef = resolveDictCall((PyCallExpression)returnValueExpr, indexElement, true); + if (resolvedRef != null) return resolvedRef; + } + } + } + else if (valueExpr instanceof PyDictLiteralExpression) { + Ref resolvedRef = getElementFromDictLiteral((PyDictLiteralExpression)valueExpr, indexElement, resolveContext); + if (resolvedRef != null) return resolvedRef; + } + else if (valueExpr instanceof PyReferenceExpression) { + PsiElement element = ((PyReferenceExpression)valueExpr).followAssignmentsChain(resolveContext).getElement(); + + if (element != valueExpr && element instanceof PyExpression) { + return resolveStringIndex((PyExpression)element, indexElement); + } + } + + return null; + } + + @Nullable + private Ref resolveNumericIndex(@NotNull PyExpression valueExpr, @NotNull Integer index) { + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext); + if (PyUtil.instanceOf(valueExpr, PyListLiteralExpression.class, PyTupleExpression.class, PyStringLiteralExpression.class)) { + Ref elementRef = getElementByIndex(valueExpr, index); + if (elementRef != null) return elementRef; + } + else if (valueExpr instanceof PyDictLiteralExpression) { + return getElementFromDictLiteral((PyDictLiteralExpression)valueExpr, index, resolveContext); + } + else if (valueExpr instanceof PyCallExpression) { + PyReturnStatement[] returnValues = getFunctionReturnValues((PyCallExpression)valueExpr, resolveContext, myTypeEvalContext); + boolean allReturnValuesForSure = true; + for (PyReturnStatement value : returnValues) { + PyExpression returnValueExpr = PyPsiUtils.flattenParens(value.getExpression()); + if (PyUtil.instanceOf(returnValueExpr, PyListLiteralExpression.class, PyDictLiteralExpression.class, + PyTupleExpression.class, PyCallExpression.class)) { + Ref resolvedRef = resolveNumericIndex(returnValueExpr, index); + if (resolvedRef != null && !resolvedRef.isNull()) { + return resolvedRef; + } + else if (resolvedRef == null){ + allReturnValuesForSure = false; + } + } + } + return allReturnValuesForSure ? Ref.create() : null; + } + else if (valueExpr instanceof PyReferenceExpression) { + PsiElement element = ((PyReferenceExpression)valueExpr).followAssignmentsChain(resolveContext).getElement(); + + if (element != null && element != valueExpr && element instanceof PyExpression) { + //noinspection ConstantConditions + return resolveNumericIndex(PyPsiUtils.flattenParens((PyExpression)element), index); + } + } + return null; + } + + @Nullable + private Ref getElementFromDictLiteral(@NotNull PyDictLiteralExpression valueExpr, + @NotNull Integer index, + @NotNull PyResolveContext resolveContext) { + boolean allKeysForSure = true; + final PyKeyValueExpression[] elements = valueExpr.getElements(); + for (PyKeyValueExpression element : elements) { + final PyNumericLiteralExpression key = PyUtil.as(element.getKey(), PyNumericLiteralExpression.class); + if (key != null && new Long(index).equals(key.getLongValue())) { + return Ref.create(element.getValue()); + } + else if (!(element.getKey() instanceof PyLiteralExpression)) { + allKeysForSure = false; + } + } + + PyDoubleStarExpression[] starExpressions = PsiTreeUtil.getChildrenOfType(valueExpr, PyDoubleStarExpression.class); + if (starExpressions != null) { + for (PyDoubleStarExpression expression : starExpressions) { + PyExpression underStarExpr = PyPsiUtils.flattenParens(expression.getExpression()); + if (underStarExpr != null) { + if (underStarExpr instanceof PyDictLiteralExpression) { + return getElementFromDictLiteral((PyDictLiteralExpression)underStarExpr, index, resolveContext); + } + else if (underStarExpr instanceof PyCallExpression) { + return getElementFromCallExpression((PyCallExpression)underStarExpr, index.toString(), resolveContext, true); + } + } + } + } + return allKeysForSure ? Ref.create() : null; + } + + @Nullable + private Ref getElementFromCallExpression(@NotNull PyCallExpression valueExpr, + @NotNull String key, + @NotNull PyResolveContext resolveContext, + boolean goDeep) { + PyReturnStatement[] returnValues = getFunctionReturnValues(valueExpr, resolveContext, myTypeEvalContext); + boolean allReturnValuesForSure = true; + final PyExpression callee = valueExpr.getCallee(); + if (callee != null && "dict".equals(callee.getName())) { + return resolveDictCall(valueExpr, key, goDeep); + } + + for (PyReturnStatement value : returnValues) { + PyExpression returnValueExpr = PyPsiUtils.flattenParens(value.getExpression()); + if (returnValueExpr instanceof PyDictLiteralExpression) { + Ref resolvedRef; + try { + final Integer index = Integer.getInteger(key); + resolvedRef = getElementFromDictLiteral((PyDictLiteralExpression)returnValueExpr, index, resolveContext); + } + catch (NumberFormatException e) { + resolvedRef = getElementFromDictLiteral((PyDictLiteralExpression)returnValueExpr, key, resolveContext); + } + + if (resolvedRef != null && !resolvedRef.isNull()) { + return resolvedRef; + } + else if (resolvedRef == null){ + allReturnValuesForSure = false; + } + } + else if (returnValueExpr instanceof PyCallExpression) { + if (goDeep) return getElementFromCallExpression(valueExpr, key, resolveContext, false); + } + } + return allReturnValuesForSure ? Ref.create() : null; + } + + @Nullable + public static Ref getElementByIndex(@NotNull PyExpression listTupleExpr, int index) { + boolean noElementsForSure = true; + int seenElementsNumber = 0; + PyExpression[] elements = getElementsFromListOrTuple(listTupleExpr); + for (PyExpression element : elements) { + if (element instanceof PyStarExpression) { + if (!LanguageLevel.forElement(element).isAtLeast(LanguageLevel.PYTHON35)) continue; + final PyExpression underStarExpr = ((PyStarExpression)element).getExpression(); + if (PyUtil.instanceOf(underStarExpr, PyListLiteralExpression.class, PyTupleExpression.class)) { + PyExpression[] subsequenсeElements = getElementsFromListOrTuple(underStarExpr); + int subsequenceElementIndex = index - seenElementsNumber; + if (subsequenceElementIndex < subsequenсeElements.length) { + return Ref.create(subsequenсeElements[subsequenceElementIndex]); + } + if (noElementsForSure) noElementsForSure = Arrays.stream(subsequenсeElements).anyMatch(it -> it instanceof PyStarExpression); + seenElementsNumber += subsequenсeElements.length; + } + else { + noElementsForSure = false; + break; + } + } + else { + if (index == seenElementsNumber) { + return Ref.create(element); + } + seenElementsNumber++; + } + } + return noElementsForSure ? Ref.create() : null; + } + + public static PyExpression[] getElementsFromListOrTuple(@NotNull final PyExpression expression) { + if (expression instanceof PyListLiteralExpression) { + return ((PyListLiteralExpression)expression).getElements(); + } + else if (expression instanceof PyTupleExpression) { + return ((PyTupleExpression)expression).getElements(); + } + else if (expression instanceof PyStringLiteralExpression) { + String value = ((PyStringLiteralExpression)expression).getStringValue(); + if (value != null) { + // Strings might be packed as well as dicts, so we need to resolve somehow to string element. + // But string element isn't PyExpression so I decided to resolve to PyStringLiteralExpression for + // every string element + PyExpression[] result = new PyExpression[value.length()]; + for (int i = 0; i < value.length(); i++) { + result[i] = expression; + } + return result; + } + } + + return PyExpression.EMPTY_ARRAY; } @NotNull private static List getStarArguments(@NotNull PyArgumentList argumentList, boolean isKeyword) { - return Arrays.asList(argumentList.getArguments()).stream() + return Arrays.stream(argumentList.getArguments()) .map(expression -> PyUtil.as(expression, PyStarArgument.class)) .filter(argument -> argument != null && argument.isKeyword() == isKeyword).collect(Collectors.toList()); } @@ -162,16 +386,17 @@ public class PySubstitutionChunkReference extends PsiReferenceBase resolvedRef = resolveDictLiteralExpression((PyDictLiteralExpression)containedExpr); + final Ref resolvedRef = getElementFromDictLiteral((PyDictLiteralExpression)containedExpr, key, resolveContext); return resolvedRef != null ? resolvedRef.get() : containedExpr; } else if (PyUtil.instanceOf(containedExpr, PyLiteralExpression.class, PySetLiteralExpression.class, @@ -179,7 +404,10 @@ public class PySubstitutionChunkReference extends PsiReferenceBase elementRef = resolveDictCall((PyCallExpression)containedExpr, myChunk.getMappingKey(), true); + if (elementRef != null) return elementRef.get(); + } } return containedExpr; } @@ -208,7 +436,7 @@ public class PySubstitutionChunkReference extends PsiReferenceBase myPosition) { return leftTupleElements[myPosition]; } - if (right instanceof PyParenthesizedExpression) { - PyExpression rightTuple = PyPsiUtils.flattenParens(right); - if (rightTuple instanceof PyTupleExpression) { - PyExpression[] rightTupleElements = ((PyTupleExpression)rightTuple).getElements(); - int rightLength = rightTupleElements.length; - if (leftTupleLength + rightLength > myPosition) - return rightTupleElements[myPosition - leftTupleLength]; - } + if (right instanceof PyTupleExpression) { + PyExpression[] rightTupleElements = ((PyTupleExpression)right).getElements(); + int rightLength = rightTupleElements.length; + if (leftTupleLength + rightLength > myPosition) return rightTupleElements[myPosition - leftTupleLength]; } } } return containedExpression; } + @Nullable private static PyArgumentList getArgumentList(final PsiElement original) { final PsiElement pyReferenceExpression = PsiTreeUtil.getParentOfType(original, PyReferenceExpression.class); @@ -240,55 +465,36 @@ public class PySubstitutionChunkReference extends PsiReferenceBase resolveKeywordStarExpression(@NotNull PyStarArgument starArgument) { + private Ref resolveKeywordStarExpression(@NotNull PyStarArgument starArgument) { + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext); + // TODO: support call, reference expressions here final PyDictLiteralExpression dictExpr = PsiTreeUtil.getChildOfType(starArgument, PyDictLiteralExpression.class); - return dictExpr != null ? resolveDictLiteralExpression(dictExpr) : null; + return dictExpr != null ? getElementFromDictLiteral(dictExpr, myChunk.getMappingKey(), resolveContext) : null; } - + @Nullable - private Ref resolvePositionalStarExpression(@NotNull PyStarArgument starArgument, int argumentPosition) { - final PyExpression expr = PsiTreeUtil.getChildOfAnyType(starArgument, PyListLiteralExpression.class, PyParenthesizedExpression.class, - PyStringLiteralExpression.class); + private Ref resolvePositionalStarExpression(@NotNull PyStarArgument starArgument, int argumentPosition) { + final PyExpression expr = PyPsiUtils.flattenParens(PsiTreeUtil.getChildOfAnyType(starArgument, PyListLiteralExpression.class, PyParenthesizedExpression.class, + PyStringLiteralExpression.class)); if (expr == null) { return Ref.create(starArgument); } final int position = (myChunk.getPosition() != null ? myChunk.getPosition() : myPosition) - argumentPosition; - final PyExpression[] elements; - if (expr instanceof PyListLiteralExpression) { - elements = ((PyListLiteralExpression)expr).getElements(); - } - else if (expr instanceof PyParenthesizedExpression) { - final PyExpression expression = PyPsiUtils.flattenParens(expr); - final PyTupleExpression tupleExpr = PyUtil.as(expression, PyTupleExpression.class); - if (tupleExpr == null) { - return Ref.create(starArgument); - } - elements = tupleExpr.getElements(); - } - else if (expr instanceof PyStringLiteralExpression) { - if (position < ((PyStringLiteralExpression)expr).getStringValue().length()) { - return Ref.create(expr); - } - return Ref.create(); - } - else { - return null; - } - return position < elements.length ? Ref.create(elements[position]) : Ref.create(); + return getElementByIndex(expr, position); } @Nullable - private Ref resolveDictLiteralExpression(PyDictLiteralExpression expression) { + private Ref getElementFromDictLiteral(@NotNull PyDictLiteralExpression expression, + @NotNull String mappingKey, + @NotNull PyResolveContext resolveContext) { final PyKeyValueExpression[] keyValueExpressions = expression.getElements(); - if (keyValueExpressions.length == 0) { - return Ref.create(); - } + boolean allKeysForSure = true; for (PyKeyValueExpression keyValueExpression : keyValueExpressions) { PyExpression keyExpression = keyValueExpression.getKey(); if (keyExpression instanceof PyStringLiteralExpression) { final PyStringLiteralExpression key = (PyStringLiteralExpression)keyExpression; - if (key.getStringValue().equals(myChunk.getMappingKey())) { + if (key.getStringValue().equals(mappingKey)) { return Ref.create(keyValueExpression.getValue()); } } @@ -296,27 +502,78 @@ public class PySubstitutionChunkReference extends PsiReferenceBase resolveDictCall(@NotNull PyCallExpression expression, @NotNull String key, boolean goDeep) { final PyExpression callee = expression.getCallee(); + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext); + boolean allKeysForSure = true; + final LanguageLevel languageLevel = LanguageLevel.forElement(expression); if (callee != null) { final String name = callee.getName(); if ("dict".equals(name)) { + final PyArgumentList argumentList = expression.getArgumentList(); for (PyExpression arg : expression.getArguments()) { + if (languageLevel.isAtLeast(LanguageLevel.PYTHON35) && goDeep && arg instanceof PyStarExpression) { + PyExpression expr = ((PyStarExpression)arg).getExpression(); + if (expr instanceof PyDictLiteralExpression) { + Ref element = getElementFromDictLiteral((PyDictLiteralExpression)expr, key, resolveContext); + if (element != null) return element; + } + else if (expr instanceof PyCallExpression) { + Ref element = resolveDictCall((PyCallExpression)expr, key, false); + if (element != null) return element; + } + else { + allKeysForSure = false; + } + } if (!(arg instanceof PyKeywordArgument)) { - return expression; + allKeysForSure = false; } } - final PyArgumentList argumentList = expression.getArgumentList(); if (argumentList != null) { - return argumentList.getKeywordArgument(myChunk.getMappingKey()); + PyKeywordArgument argument = argumentList.getKeywordArgument(key); + if (argument != null) { + return Ref.create(argument); + } + else { + return allKeysForSure ? Ref.create() : null; + } } } } - return expression; + return Ref.create(expression); + } + + private static PyReturnStatement[] getFunctionReturnValues(@NotNull PyCallExpression callExpression, + @NotNull PyResolveContext resolveContext, + @NotNull TypeEvalContext evalContext) { + final PyCallable callable = callExpression.resolveCalleeFunction(resolveContext); + if (callable instanceof PyFunction && evalContext.maySwitchToAST(callable)) { + PyStatementList statementList = ((PyFunction)callable).getStatementList(); + return PyUtil.getAllChildrenOfType(statementList, PyReturnStatement.class); + } + return new PyReturnStatement[0]; } @NotNull diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java index 2131eb7d8d29..466cdd2048d1 100644 --- a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java @@ -546,16 +546,19 @@ public class PyStringFormatInspection extends PyInspection { // it's true because we set position manually in inspect() assert chunk.getPosition() != null; final PsiElement target = new PySubstitutionChunkReference(myFormatExpression, chunk, chunk.getPosition()).resolve(); + boolean hasElementIndex = chunk.getMappingKeyElementIndex() != null; if (target == null) { final String chunkMapping = chunk.getMappingKey(); - registerProblem(myFormatExpression, chunkMapping == null ? PyBundle.message("INSP.too.few.keys") : - PyBundle.message("INSP.unused.mapping", chunkMapping)); + if (chunkMapping != null) { + registerProblem(myFormatExpression, hasElementIndex ? + PyBundle.message("INSP.too.few.args.for.fmt.string") : + PyBundle.message("INSP.key.$0.has.no.arg", chunkMapping)); + } + else { + registerProblem(myFormatExpression, PyBundle.message("INSP.too.few.args.for.fmt.string")); + } } else { - if (chunk.getMappingKeyElementIndex() != null) { - inspectIndexElements(chunk, myFormatExpression, target, target, mappingKey); - } - checkTypesCompatibleForCheckedTypesOnly(myFormatExpression, target, mappingKey); } } @@ -565,171 +568,6 @@ public class PyStringFormatInspection extends PyInspection { myVisitor.registerProblem(problemTarget, message); } - private void inspectIndexElements(@NotNull PyStringFormatParser.NewStyleSubstitutionChunk chunk, - @NotNull PyStringLiteralExpression formatExpression, - @NotNull final PsiElement problemElement, - @NotNull PsiElement inspectedElement, - @NotNull String mappingKey) { - final String indexElement = chunk.getMappingKeyElementIndex(); - final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext); - - if (indexElement != null) { - try { - final Integer index = Integer.valueOf(indexElement); - if (inspectedElement instanceof PyListLiteralExpression) { - inspectListElements(formatExpression, problemElement, index, inspectedElement); - } - - if (inspectedElement instanceof PyParenthesizedExpression) { - final PyExpression flatten = PyPsiUtils.flattenParens((PyParenthesizedExpression)inspectedElement); - if (flatten != null) { - inspectIndexElements(chunk, formatExpression, problemElement, flatten, mappingKey); - } - } - else if (inspectedElement instanceof PyTupleExpression) { - final PyExpression[] elements = ((PyTupleExpression)inspectedElement).getElements(); - if (index >= elements.length) { - registerProblem(problemElement, PyBundle.message("INSP.too.few.args.for.fmt.string")); - } - else { - checkTypesCompatibleForCheckedTypesOnly(formatExpression, elements[index], indexElement); - } - } - else if (inspectedElement instanceof PyReferenceExpression) { - inspectReferenceExpressionForNumericKey(chunk, formatExpression, (PyReferenceExpression)inspectedElement, mappingKey, - indexElement); - } - else if (inspectedElement instanceof PyCallExpression) { - final int callResultsArgumentsNumber = inspectCallExpression((PyCallExpression)inspectedElement, resolveContext, - myTypeEvalContext, false); - if (callResultsArgumentsNumber <= index) { - registerProblem(inspectedElement, PyBundle.message("INSP.too.few.args.for.fmt.string")); - } - } - else if (inspectedElement instanceof PyDictLiteralExpression) { - inspectDictForKey(formatExpression, inspectedElement, (PyDictLiteralExpression)inspectedElement, mappingKey, index); - } - } - catch (NumberFormatException e) { - if (inspectedElement instanceof PyCallExpression) { - final PyReturnStatement[] returnValues = getFunctionReturnValues((PyCallExpression)inspectedElement, resolveContext, - myTypeEvalContext); - for (PyReturnStatement value : returnValues) { - PyExpression valueExpression = PyPsiUtils.flattenParens(value.getExpression()); - if (valueExpression instanceof PyDictLiteralExpression) { - inspectDictForKey(formatExpression, inspectedElement, (PyDictLiteralExpression)valueExpression, mappingKey, - indexElement); - } - else if (valueExpression instanceof PyCallExpression) { - final PyExpression callee = ((PyCallExpression)valueExpression).getCallee(); - if (callee != null && callee.getName() != null && callee.getName().equals("dict")) { - final PyExpression[] arguments = ((PyCallExpression)valueExpression).getArguments(); - for (PyExpression argument : arguments) { - if (argument instanceof PyKeywordArgument && indexElement.equals(((PyKeywordArgument)argument).getKeyword())) { - checkTypesCompatibleForCheckedTypesOnly(formatExpression, argument, mappingKey); - return; - } - } - - registerProblem(inspectedElement, PyBundle.message("INSP.too.few.keys")); - } - } - } - } - else if (inspectedElement instanceof PyReferenceExpression) { - inspectReferenceExpressionForNumericKey(chunk, formatExpression, (PyReferenceExpression)inspectedElement, mappingKey, - indexElement); - } - else if (inspectedElement instanceof PyDictLiteralExpression) { - inspectDictForKey(formatExpression, inspectedElement, (PyDictLiteralExpression)inspectedElement, mappingKey, indexElement); - } - else if (inspectedElement instanceof PyParenthesizedExpression) { - final PyExpression expression = PyPsiUtils.flattenParens((PyExpression)inspectedElement); - - if (expression != null) { - inspectIndexElements(chunk, formatExpression, expression, expression, mappingKey); - } - } - } - } - } - - private void inspectReferenceExpressionForNumericKey(@NotNull PyStringFormatParser.NewStyleSubstitutionChunk chunk, - @NotNull PyStringLiteralExpression formatExpression, - @NotNull PyReferenceExpression target, - @NotNull String mappingKey, - @NotNull String index) { - if (PyNames.DICT.equals(target.getName())) return; - final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(myTypeEvalContext); - final PsiElement element = target.followAssignmentsChain(resolveContext).getElement(); - - if (element == target || !(element instanceof PyExpression)) { - return; - } - - if (element instanceof PyDictLiteralExpression) { - try { - final Integer ind = Integer.valueOf(index); - inspectDictForKey(formatExpression, target, (PyDictLiteralExpression)element, mappingKey, ind); - } - catch (NumberFormatException e) { - inspectDictForKey(formatExpression, target, (PyDictLiteralExpression)element, mappingKey, index); - } - } - else { - inspectIndexElements(chunk, formatExpression, target, element, mappingKey); - } - } - - private void inspectDictForKey(@NotNull PyStringLiteralExpression formatExpression, - @NotNull PsiElement problemElement, - @NotNull PyDictLiteralExpression dict, - @NotNull String mappingKey, - @NotNull Integer index) { - final PyKeyValueExpression[] elements = dict.getElements(); - - for (PyKeyValueExpression element : elements) { - final PyExpression key = element.getKey(); - if (key instanceof PyNumericLiteralExpression && new Long(index).equals(((PyNumericLiteralExpression)key).getLongValue())) { - checkTypesCompatibleForCheckedTypesOnly(formatExpression, key, mappingKey); - return; - } - } - - registerProblem(problemElement, PyBundle.message("INSP.too.few.keys")); - } - - private void inspectDictForKey(@NotNull PyStringLiteralExpression formatExpression, - @NotNull PsiElement problemElement, - @NotNull PyDictLiteralExpression dict, - @NotNull String mappingKey, - @NotNull String indexElement) { - final PyKeyValueExpression[] elements = dict.getElements(); - - for (PyKeyValueExpression element : elements) { - final PyExpression key = element.getKey(); - if (key instanceof PyStringLiteralExpression && indexElement.equals(((PyStringLiteralExpression)key).getStringValue())) { - checkTypesCompatibleForCheckedTypesOnly(formatExpression, key, mappingKey); - return; - } - } - - registerProblem(problemElement, PyBundle.message("INSP.too.few.keys")); - } - - private void inspectListElements(@NotNull final PyStringLiteralExpression formatExpression, - @NotNull final PsiElement problemElement, - @NotNull final Integer index, - @NotNull final PsiElement target) { - final PyExpression[] elements = ((PyListLiteralExpression)target).getElements(); - if (elements.length > index) { - checkTypesCompatibleForCheckedTypesOnly(formatExpression, elements[index], String.valueOf(index)); - } - else { - registerProblem(problemElement, PyBundle.message("INSP.too.few.args.for.fmt.string")); - } - } - private void checkTypesCompatibleForCheckedTypesOnly(@NotNull PyStringLiteralExpression anchor, @NotNull PsiElement target, @NotNull String mappingKey) { diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleCallExpressionArgument.py b/python/testData/inspections/PyStringFormatInspection/NewStyleCallExpressionArgument.py index 888b8fbc0470..ad5c924ec152 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleCallExpressionArgument.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleCallExpressionArgument.py @@ -8,7 +8,7 @@ def f(mode): elif mode == "b": return True -"{}{}".format(f("i")) -"{}{}".format(f("f")) -"{}{}".format(f("s")) -"{}{}".format(f("b")) \ No newline at end of file +"{}{}".format(f("i")) +"{}{}".format(f("f")) +"{}{}".format(f("s")) +"{}{}".format(f("b")) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleDictLiteralExprInsideDictCall.py b/python/testData/inspections/PyStringFormatInspection/NewStyleDictLiteralExprInsideDictCall.py index 088f47884072..532e66f73f49 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleDictLiteralExprInsideDictCall.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleDictLiteralExprInsideDictCall.py @@ -1,2 +1,2 @@ print("{foo}".format(**dict({'foo': 'bar'}))) -"{}".format() \ No newline at end of file +"{}".format() \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleDictLiteralWithNumericKeys.py b/python/testData/inspections/PyStringFormatInspection/NewStyleDictLiteralWithNumericKeys.py index c66217b62525..42b6d7b76bbc 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleDictLiteralWithNumericKeys.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleDictLiteralWithNumericKeys.py @@ -1 +1 @@ -print ("first is {fst}".format(**{1: "3"}) \ No newline at end of file +print ("first is {fst}".format(**{1: "3"}) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleEmptyDictArg.py b/python/testData/inspections/PyStringFormatInspection/NewStyleEmptyDictArg.py index 3db74777d680..fd9e1ca1c188 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleEmptyDictArg.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleEmptyDictArg.py @@ -1 +1 @@ -print("{foo}".format(**{})) \ No newline at end of file +print("{foo}".format(**{})) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionDictArg.py b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionDictArg.py index 167cab4b620c..a7b7b5ae7cc3 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionDictArg.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionDictArg.py @@ -1,5 +1,5 @@ "{foo[a]}".format(foo={"a": 1}) -"{foo[b]}".format(foo={"a": 1}) +"{foo[b]}".format(foo={"a": 1}) "{foo[1]}".format(foo={1: 1}) -"{foo[2]}".format(foo={1: 1}) \ No newline at end of file +"{foo[2]}".format(foo={1: 1}) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionFuncArgs.py b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionFuncArgs.py index 44781f7f0647..a86e0742484b 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionFuncArgs.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionFuncArgs.py @@ -2,16 +2,16 @@ def f(): return [1, 2, 3] "{foo[1]}".format(foo=f()) -"{foo[3]}".format(foo=f()) +"{foo[3]}".format(foo=f()) def g(): return 1, 2, 3 "{foo[1]:d}".format(foo=g()) -"{foo[3]}".format(foo=g()) +"{foo[3]}".format(foo=g()) def ff(): return g() "{foo[1]}".format(foo=g()) -"{foo[3]}".format(foo=ff()) +"{foo[3]}".format(foo=ff()) diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionFuncDictArg.py b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionFuncDictArg.py index 84361ad2dbcb..c5e31d2c8ad6 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionFuncDictArg.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionFuncDictArg.py @@ -2,11 +2,11 @@ def d(): return {"a": 1} "{foo[a]}".format(foo=d()) -"{foo[b]}".format(foo=d()) +"{foo[b]}".format(foo=d()) def d_dict(): return dict(a=1) "{foo[a]}".format(foo=d_dict()) -"{foo[b]}".format(foo=d_dict()) \ No newline at end of file +"{foo[b]}".format(foo=d_dict()) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionListArg.py b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionListArg.py index 4e05345a8cf2..4c4c9087cea5 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionListArg.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionListArg.py @@ -1,2 +1,2 @@ "{foo[1]}".format(foo=[0, 1, 2]) -"{foo[2]}".format(foo=[0, 1]) \ No newline at end of file +"{foo[2]}".format(foo=[0, 1]) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionParenArg.py b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionParenArg.py index 14fced962925..2680a97c83c3 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionParenArg.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionParenArg.py @@ -4,7 +4,7 @@ "{foo[a]}".format(foo=({"a": 1})) -"{foo[3]}".format(foo=(1, 2, 3)) -"{foo[3]}".format(foo=({1: 1})) -"{foo[3]}".format(foo=([1, 2, 3])) -"{foo[b]}".format(foo=({"a": 1})) \ No newline at end of file +"{foo[3]}".format(foo=(1, 2, 3)) +"{foo[3]}".format(foo=({1: 1})) +"{foo[3]}".format(foo=([1, 2, 3])) +"{foo[b]}".format(foo=({"a": 1})) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionRefArgs.py b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionRefArgs.py index 84692d80e77b..a59c788127d3 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionRefArgs.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionRefArgs.py @@ -1,7 +1,7 @@ list = [1, 2, 3] "{foo[1]}".format(foo=list) -"{foo[3]}".format(foo=list) +"{foo[3]}".format(foo=list) tuple = (1, 2, 3) "{foo[1]}".format(foo=tuple) -"{foo[3]}".format(foo=tuple) \ No newline at end of file +"{foo[3]}".format(foo=tuple) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionRefDictArg.py b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionRefDictArg.py index 564cf5a1afc2..0721d9ea27d1 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionRefDictArg.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStyleMappingKeyWithSubscriptionRefDictArg.py @@ -1,7 +1,7 @@ d = {"a": 1} "{foo[a]}".format(foo=d) -"{foo[b]}".format(foo=d) +"{foo[b]}".format(foo=d) d_num = {1: 1} "{foo[1]}".format(foo=d_num) -"{foo[2]}".format(foo=d_num) \ No newline at end of file +"{foo[2]}".format(foo=d_num) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStylePackedFunctionCall.py b/python/testData/inspections/PyStringFormatInspection/NewStylePackedFunctionCall.py index ae4b437bde43..9975f8d86d2a 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStylePackedFunctionCall.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStylePackedFunctionCall.py @@ -3,4 +3,4 @@ def f(): '{foo}'.format(**f()) -"{}".format() \ No newline at end of file +"{}".format() \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStylePackedReference.py b/python/testData/inspections/PyStringFormatInspection/NewStylePackedReference.py index 2d042d15456b..04c8e6f7afbf 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStylePackedReference.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStylePackedReference.py @@ -1,4 +1,4 @@ ref = {"fst": 1, "snd": 2} print "first is {fst}, second is {snd}".format(**ref) -"{}".format() \ No newline at end of file +"{}".format() \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/NewStylePositionalSubstitutionWithDictArg.py b/python/testData/inspections/PyStringFormatInspection/NewStylePositionalSubstitutionWithDictArg.py index bce6628d4d40..19ea4b3a65c3 100644 --- a/python/testData/inspections/PyStringFormatInspection/NewStylePositionalSubstitutionWithDictArg.py +++ b/python/testData/inspections/PyStringFormatInspection/NewStylePositionalSubstitutionWithDictArg.py @@ -1 +1 @@ -print('{}'.format(foo='foo')) \ No newline at end of file +print('{}'.format(foo='foo')) \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/PackedStringTooFewArguments.py b/python/testData/inspections/PyStringFormatInspection/PackedStringTooFewArguments.py index ce6ab5a61977..978cb50dbd6c 100644 --- a/python/testData/inspections/PyStringFormatInspection/PackedStringTooFewArguments.py +++ b/python/testData/inspections/PyStringFormatInspection/PackedStringTooFewArguments.py @@ -1 +1 @@ -'{3}, {1}, {0}'.format(*'abc') \ No newline at end of file +'{3}, {1}, {0}'.format(*'abc') \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/TooFewArgumentsNewStyleFormat.py b/python/testData/inspections/PyStringFormatInspection/TooFewArgumentsNewStyleFormat.py index 049f92affc37..797af7c5f1e0 100644 --- a/python/testData/inspections/PyStringFormatInspection/TooFewArgumentsNewStyleFormat.py +++ b/python/testData/inspections/PyStringFormatInspection/TooFewArgumentsNewStyleFormat.py @@ -1,2 +1,2 @@ -"{} {}".format(1) -'{}'.format() \ No newline at end of file +"{} {}".format(1) +'{}'.format() \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/TooFewMappingKeys.py b/python/testData/inspections/PyStringFormatInspection/TooFewMappingKeys.py index 273c0acb0f56..6960ae551226 100644 --- a/python/testData/inspections/PyStringFormatInspection/TooFewMappingKeys.py +++ b/python/testData/inspections/PyStringFormatInspection/TooFewMappingKeys.py @@ -1,2 +1,2 @@ -'Hello, {1}'.format('World') -'Hello, {} {}!'.format('World') \ No newline at end of file +'Hello, {1}'.format('World') +'Hello, {} {}!'.format('World') \ No newline at end of file diff --git a/python/testData/inspections/PyStringFormatInspection/UnusedMappingNewStyleFormat.py b/python/testData/inspections/PyStringFormatInspection/UnusedMappingNewStyleFormat.py index 8db44f22ec77..804db2c741c0 100644 --- a/python/testData/inspections/PyStringFormatInspection/UnusedMappingNewStyleFormat.py +++ b/python/testData/inspections/PyStringFormatInspection/UnusedMappingNewStyleFormat.py @@ -1,2 +1,2 @@ -"{name}".format() -'{foo}'.format(boo=1) \ No newline at end of file +"{name}".format() +'{foo}'.format(boo=1) \ No newline at end of file