Refactor and cleanup

This commit is contained in:
Valentina Kiryushkina
2017-05-15 14:37:52 +03:00
parent bf1d370feb
commit 91f27172ee
5 changed files with 210 additions and 172 deletions
@@ -67,8 +67,6 @@ public class PyStringFormatInspection extends PyInspection {
public static class Visitor extends PyInspectionVisitor {
private static class Inspection {
private static final List<String> CHECKED_TYPES = Arrays.asList("str", "int", "long", "float", "complex", "None");
private static final List<String> NUMERIC_TYPES = Arrays.asList("int", "long", "float", "complex");
private static final ImmutableMap<Character, String> PERCENT_FORMAT_CONVERSIONS = ImmutableMap.<Character, String>builder()
.put('d', "int or long or float")
.put('i', "int or long or float")
@@ -258,37 +256,6 @@ public class PyStringFormatInspection extends PyInspection {
return -1;
}
private int inspectCallExpression(@NotNull PyCallExpression callExpression, @NotNull PyResolveContext resolveContext) {
final PyReturnStatement[] returnStatements = getFunctionReturnValues(callExpression, resolveContext);
int expressionsSize = -1;
for (PyReturnStatement returnStatement : returnStatements) {
if (returnStatement.getExpression() instanceof PyCallExpression) {
return -1;
}
final int argumentsSize = Math.max(PyUtil.flattenedParensAndTuples(returnStatement.getExpression()).size(),
PyUtil.flattenedParensAndLists(returnStatement.getExpression()).size());
if (expressionsSize < 0) {
expressionsSize = argumentsSize;
}
if (expressionsSize != argumentsSize) {
return -1;
}
}
return expressionsSize;
}
private PyReturnStatement[] getFunctionReturnValues(@NotNull PyCallExpression callExpression,
@NotNull PyResolveContext resolveContext) {
final PyCallable callable = callExpression.resolveCalleeFunction(resolveContext);
if (callable instanceof PyFunction && myTypeEvalContext.maySwitchToAST(callable)) {
PyStatementList statementList = ((PyFunction)callable).getStatementList();
return PyUtil.getAllChildrenOfType(statementList, PyReturnStatement.class);
}
return new PyReturnStatement[0];
}
private static Map<PyExpression, PyExpression> addSubscriptions(PsiFile file, String operand) {
Map<PyExpression, PyExpression> additionalExpressions = new HashMap<PyExpression, PyExpression>();
PySubscriptionExpression[] subscriptionExpressions = PyUtil.getAllChildrenOfType(file, PySubscriptionExpression.class);
@@ -464,41 +431,199 @@ public class PyStringFormatInspection extends PyInspection {
}
}
private void inspectNewStyleValues(@NotNull final PyStringLiteralExpression formatExpression) {
final String value = formatExpression.getStringValue();
private static boolean isBytesLiteral(@NotNull PyStringLiteralExpression expr, @NotNull TypeEvalContext context) {
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(expr);
final PyClassType bytesType = builtinCache.getBytesType(LanguageLevel.forElement(expr));
final PyType actualType = context.getType(expr);
return bytesType != null && actualType != null && PyTypeChecker.match(bytesType, actualType, context);
}
private void inspectWidth(@NotNull final PyStringLiteralExpression formatExpression, String width) {
if ("*".equals(width)) {
++myExpectedArguments;
if (myUsedMappingKeys.size() > 0) {
registerProblem(formatExpression, "Can't use \'*\' in formats when using a mapping");
}
}
}
public boolean isProblem() {
return myProblemRegister;
}
private void inspectValues(@Nullable final PyExpression rightExpression) {
if (rightExpression == null) {
return;
}
if (rightExpression instanceof PyParenthesizedExpression) {
inspectValues(((PyParenthesizedExpression)rightExpression).getContainedExpression());
}
else {
final PyClassType type = as(myTypeEvalContext.getType(rightExpression), PyClassType.class);
if (type != null) {
if (myUsedMappingKeys.size() > 0 && !PyABCUtil.isSubclass(type.getPyClass(), PyNames.MAPPING, null)) {
registerProblem(rightExpression, PyBundle.message("INSP.format.requires.mapping"));
return;
}
}
inspectArgumentsNumber(rightExpression);
}
}
private void inspectArgumentsNumber(@NotNull final PyExpression rightExpression) {
final int arguments = inspectArguments(rightExpression, rightExpression);
if (myUsedMappingKeys.isEmpty() && arguments >= 0) {
if (myExpectedArguments < arguments) {
registerProblem(rightExpression, PyBundle.message("INSP.too.many.args.for.fmt.string"));
}
else if (myExpectedArguments > arguments) {
registerProblem(rightExpression, PyBundle.message("INSP.too.few.args.for.fmt.string"));
}
}
}
}
private static class NewStyleInspection {
private static final List<String> CHECKED_TYPES = Arrays.asList("str", "int", "long", "float", "complex", "None");
private static final List<String> NUMERIC_TYPES = Arrays.asList("int", "long", "float", "complex");
private static final ImmutableMap<Character, String> NEW_STYLE_FORMAT_CONVERSIONS = ImmutableMap.<Character, String>builder()
.put('s', "str or None")
.put('b', "int")
.put('c', "int")
.put('d', "int")
.put('o', "int")
.put('x', "int")
.put('X', "int")
.put('n', "int or long or float or complex")
.put('e', "long or float or complex")
.put('E', "long or float or complex")
.put('f', "long or float or complex")
.put('F', "long or float or complex")
.put('g', "long or float or complex")
.put('G', "long or float or complex")
.put('%', "long or float")
.build();
private final PyStringLiteralExpression myFormatExpression;
private boolean myProblemRegister = false;
private final Visitor myVisitor;
private final TypeEvalContext myTypeEvalContext;
private final Map<String, String> myFormatSpec = new HashMap<String, String>();
public NewStyleInspection(PyStringLiteralExpression formatExpression, Visitor visitor, TypeEvalContext context) {
myFormatExpression = formatExpression;
myVisitor = visitor;
myTypeEvalContext = context;
}
public void inspect() {
final String value = myFormatExpression.getStringValue();
final List<PyStringFormatParser.SubstitutionChunk> chunks = filterSubstitutions(PyStringFormatParser.parseNewStyleFormat(value));
myExpectedArguments = chunks.size();
for (int i = 0; i < chunks.size(); i++) {
final PyStringFormatParser.NewStyleSubstitutionChunk chunk =
as(chunks.get(i), PyStringFormatParser.NewStyleSubstitutionChunk.class);
if (chunk != null) {
String mappingKey = inspectNewStyleChunk(formatExpression, i, chunk);
inspectArgumentsForNewStyleChunk(i, chunk, mappingKey, formatExpression);
chunk.setPosition(i);
String mappingKey = inspectNewStyleChunkAndGetMappingKey(chunk);
if (!isProblem()) {
inspectArguments(chunk, mappingKey);
}
}
}
}
private void inspectArgumentsForNewStyleChunk(int i,
@NotNull PyStringFormatParser.NewStyleSubstitutionChunk chunk,
@NotNull String mappingKey,
@NotNull PyStringLiteralExpression formatExpression) {
final PsiElement target = new PySubstitutionChunkReference(formatExpression, chunk, i).resolve();
private String inspectNewStyleChunkAndGetMappingKey(@NotNull PyStringFormatParser.NewStyleSubstitutionChunk chunk) {
final HashSet<String> types = new HashSet<>();
boolean hasTypeOptions = false;
final String mappingKey = chunk.getMappingKey() != null ? chunk.getMappingKey() : String.valueOf(chunk.getPosition());
// inspect options available only for numeric types
if (chunk.hasSignOption() || chunk.useAlternateForm() || chunk.hasZeroPadding() || chunk.hasThousandsSeparator()) {
addTypes(types, NUMERIC_TYPES);
hasTypeOptions = true;
}
if (chunk.getPrecision() != null) {
// TODO: actually availableTypes doesn't reject int, because int is compatible with float and complex
final List<String> availableTypes = Arrays.asList("str", "float", "complex");
addTypes(types, availableTypes);
hasTypeOptions = true;
}
final char conversionType = chunk.getConversionType();
if (NEW_STYLE_FORMAT_CONVERSIONS.containsKey(conversionType)) {
final String[] s = NEW_STYLE_FORMAT_CONVERSIONS.get(conversionType).split(" or ");
addTypes(types, Arrays.asList(s));
hasTypeOptions = true;
}
if (!types.isEmpty()) {
myFormatSpec.put(mappingKey, StringUtil.join(types, " or "));
}
else if (hasTypeOptions) {
registerProblem(myFormatExpression, PyBundle.message("INSP.incompatible.options", mappingKey));
}
return mappingKey;
}
private void inspectArguments(@NotNull PyStringFormatParser.NewStyleSubstitutionChunk chunk, @NotNull String mappingKey) {
// it's true because we set position manually in inspect()
assert chunk.getPosition() != null;
final PsiElement target = new PySubstitutionChunkReference(myFormatExpression, chunk, chunk.getPosition()).resolve();
if (target == null) {
final String chunkMapping = chunk.getMappingKey();
registerProblem(formatExpression, chunkMapping == null ? PyBundle.message("INSP.too.few.keys") :
PyBundle.message("INSP.unused.mapping", chunkMapping));
registerProblem(myFormatExpression, chunkMapping == null ? PyBundle.message("INSP.too.few.keys") :
PyBundle.message("INSP.unused.mapping", chunkMapping));
}
else {
if (chunk.getMappingKeyElementIndex() != null) {
inspectIndexElements(chunk, formatExpression, target, target, mappingKey);
inspectIndexElements(chunk, myFormatExpression, target, target, mappingKey);
}
checkTypesCompatibleForCheckedTypesOnly(mappingKey, formatExpression, target);
checkTypesCompatibleForCheckedTypesOnly(myFormatExpression, target, mappingKey);
}
}
private int inspectCallExpression(@NotNull PyCallExpression callExpression, @NotNull PyResolveContext resolveContext) {
final PyReturnStatement[] returnStatements = getFunctionReturnValues(callExpression, resolveContext);
int expressionsSize = -1;
for (PyReturnStatement returnStatement : returnStatements) {
if (returnStatement.getExpression() instanceof PyCallExpression) {
return -1;
}
final int argumentsSize = Math.max(PyUtil.flattenedParensAndTuples(returnStatement.getExpression()).size(),
PyUtil.flattenedParensAndLists(returnStatement.getExpression()).size());
if (expressionsSize < 0) {
expressionsSize = argumentsSize;
}
if (expressionsSize != argumentsSize) {
return -1;
}
}
return expressionsSize;
}
private PyReturnStatement[] getFunctionReturnValues(@NotNull PyCallExpression callExpression,
@NotNull PyResolveContext resolveContext) {
final PyCallable callable = callExpression.resolveCalleeFunction(resolveContext);
if (callable instanceof PyFunction && myTypeEvalContext.maySwitchToAST(callable)) {
PyStatementList statementList = ((PyFunction)callable).getStatementList();
return PyUtil.getAllChildrenOfType(statementList, PyReturnStatement.class);
}
return new PyReturnStatement[0];
}
private void registerProblem(@NotNull PsiElement problemTarget, @NotNull final String message) {
myProblemRegister = true;
myVisitor.registerProblem(problemTarget, message);
}
private void inspectIndexElements(@NotNull PyStringFormatParser.NewStyleSubstitutionChunk chunk,
@NotNull PyStringLiteralExpression formatExpression,
@NotNull final PsiElement problemElement,
@@ -506,6 +631,7 @@ public class PyStringFormatInspection extends PyInspection {
@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);
@@ -525,7 +651,7 @@ public class PyStringFormatInspection extends PyInspection {
registerProblem(problemElement, PyBundle.message("INSP.too.few.args.for.fmt.string"));
}
else {
checkTypesCompatibleForCheckedTypesOnly(indexElement, formatExpression, elements[index]);
checkTypesCompatibleForCheckedTypesOnly(formatExpression, elements[index], indexElement);
}
}
else if (inspectedElement instanceof PyReferenceExpression) {
@@ -534,10 +660,7 @@ public class PyStringFormatInspection extends PyInspection {
}
else if (inspectedElement instanceof PyCallExpression) {
final int callResultsArgumentsNumber = inspectCallExpression((PyCallExpression)inspectedElement, resolveContext);
if (index < callResultsArgumentsNumber) {
checkTypesCompatibleForCheckedTypesOnly(mappingKey, formatExpression, inspectedElement);
}
else {
if (callResultsArgumentsNumber <= index) {
registerProblem(inspectedElement, PyBundle.message("INSP.too.few.args.for.fmt.string"));
}
}
@@ -561,7 +684,7 @@ public class PyStringFormatInspection extends PyInspection {
final PyExpression[] arguments = ((PyCallExpression)valueExpression).getArguments();
for (PyExpression argument : arguments) {
if (argument instanceof PyKeywordArgument && indexElement.equals(((PyKeywordArgument)argument).getKeyword())) {
checkTypesCompatibleForCheckedTypesOnly(mappingKey, formatExpression, argument);
checkTypesCompatibleForCheckedTypesOnly(formatExpression, argument, mappingKey);
return;
}
}
@@ -626,7 +749,7 @@ public class PyStringFormatInspection extends PyInspection {
for (PyKeyValueExpression element : elements) {
final PyExpression key = element.getKey();
if (key instanceof PyNumericLiteralExpression && new Long(index).equals(((PyNumericLiteralExpression)key).getLongValue())) {
checkTypesCompatibleForCheckedTypesOnly(mappingKey, formatExpression, key);
checkTypesCompatibleForCheckedTypesOnly(formatExpression, key, mappingKey);
return;
}
}
@@ -644,7 +767,7 @@ public class PyStringFormatInspection extends PyInspection {
for (PyKeyValueExpression element : elements) {
final PyExpression key = element.getKey();
if (key instanceof PyStringLiteralExpression && indexElement.equals(((PyStringLiteralExpression)key).getStringValue())) {
checkTypesCompatibleForCheckedTypesOnly(mappingKey, formatExpression, key);
checkTypesCompatibleForCheckedTypesOnly(formatExpression, key, mappingKey);
return;
}
}
@@ -658,16 +781,16 @@ public class PyStringFormatInspection extends PyInspection {
@NotNull final PsiElement target) {
final PyExpression[] elements = ((PyListLiteralExpression)target).getElements();
if (elements.length > index) {
checkTypesCompatibleForCheckedTypesOnly(String.valueOf(index), formatExpression, elements[index]);
checkTypesCompatibleForCheckedTypesOnly(formatExpression, elements[index], String.valueOf(index));
}
else {
registerProblem(problemElement, PyBundle.message("INSP.too.few.args.for.fmt.string"));
}
}
private void checkTypesCompatibleForCheckedTypesOnly(@NotNull String mappingKey,
@NotNull PyStringLiteralExpression anchor,
@NotNull PsiElement target) {
private void checkTypesCompatibleForCheckedTypesOnly(@NotNull PyStringLiteralExpression anchor,
@NotNull PsiElement target,
@NotNull String mappingKey) {
final PyTypedElement typedElement = as(target, PyTypedElement.class);
if (typedElement != null && myFormatSpec.containsKey(mappingKey)) {
final PyType actual = myTypeEvalContext.getType(typedElement);
@@ -680,47 +803,6 @@ public class PyStringFormatInspection extends PyInspection {
}
}
private String inspectNewStyleChunk(@NotNull PyStringLiteralExpression formatExpression,
int i,
PyStringFormatParser.NewStyleSubstitutionChunk chunk) {
String mappingKey = Integer.toString(i + 1);
final HashSet<String> types = new HashSet<>();
boolean hasTypeOptions = false;
if (chunk.getMappingKey() != null) {
mappingKey = chunk.getMappingKey();
myUsedMappingKeys.put(mappingKey, false);
}
// inspect options available only for numeric types
if (chunk.hasSignOption() || chunk.useAlternateForm() || chunk.hasZeroPadding() || chunk.hasThousandsSeparator()) {
addTypes(types, NUMERIC_TYPES);
hasTypeOptions = true;
}
if (chunk.getPrecision() != null) {
// TODO: actually availableTypes doesn't reject int, because int is compatible with float and complex
final List<String> availableTypes = Arrays.asList("str", "float", "complex");
addTypes(types, availableTypes);
hasTypeOptions = true;
}
final char conversionType = chunk.getConversionType();
if (NEW_STYLE_FORMAT_CONVERSIONS.containsKey(conversionType)) {
final String[] s = NEW_STYLE_FORMAT_CONVERSIONS.get(conversionType).split(" or ");
addTypes(types, Arrays.asList(s));
hasTypeOptions = true;
}
if (!types.isEmpty()) {
myFormatSpec.put(mappingKey, StringUtil.join(types, " or "));
}
else if (hasTypeOptions) {
registerProblem(formatExpression, PyBundle.message("INSP.incompatible.options", i));
}
return mappingKey;
}
private static void addTypes(@NotNull final Set<String> types, @NotNull final List<String> availableTypes) {
if (!types.isEmpty()) {
types.retainAll(availableTypes);
@@ -730,56 +812,9 @@ public class PyStringFormatInspection extends PyInspection {
}
}
private static boolean isBytesLiteral(@NotNull PyStringLiteralExpression expr, @NotNull TypeEvalContext context) {
final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(expr);
final PyClassType bytesType = builtinCache.getBytesType(LanguageLevel.forElement(expr));
final PyType actualType = context.getType(expr);
return bytesType != null && actualType != null && PyTypeChecker.match(bytesType, actualType, context);
}
private void inspectWidth(@NotNull final PyStringLiteralExpression formatExpression, String width) {
if ("*".equals(width)) {
++myExpectedArguments;
if (myUsedMappingKeys.size() > 0) {
registerProblem(formatExpression, "Can't use \'*\' in formats when using a mapping");
}
}
}
public boolean isProblem() {
return myProblemRegister;
}
private void inspectValues(@Nullable final PyExpression rightExpression) {
if (rightExpression == null) {
return;
}
if (rightExpression instanceof PyParenthesizedExpression) {
inspectValues(((PyParenthesizedExpression)rightExpression).getContainedExpression());
}
else {
final PyClassType type = as(myTypeEvalContext.getType(rightExpression), PyClassType.class);
if (type != null) {
if (myUsedMappingKeys.size() > 0 && !PyABCUtil.isSubclass(type.getPyClass(), PyNames.MAPPING, myTypeEvalContext)) {
registerProblem(rightExpression, PyBundle.message("INSP.format.requires.mapping"));
return;
}
}
inspectArgumentsNumber(rightExpression);
}
}
private void inspectArgumentsNumber(@NotNull final PyExpression rightExpression) {
final int arguments = inspectArguments(rightExpression, rightExpression);
if (myUsedMappingKeys.isEmpty() && arguments >= 0) {
if (myExpectedArguments < arguments) {
registerProblem(rightExpression, PyBundle.message("INSP.too.many.args.for.fmt.string"));
}
else if (myExpectedArguments > arguments) {
registerProblem(rightExpression, PyBundle.message("INSP.too.few.args.for.fmt.string"));
}
}
}
}
public Visitor(final ProblemsHolder holder, LocalInspectionToolSession session) {
@@ -805,8 +840,8 @@ public class PyStringFormatInspection extends PyInspection {
if (callee != null && callee.getName() != null && callee.getName().equals(PyNames.FORMAT)) {
final PyStringLiteralExpression literalExpression = PsiTreeUtil.getChildOfType(callee, PyStringLiteralExpression.class);
if (literalExpression != null) {
final Inspection inspection = new Inspection(this, myTypeEvalContext);
inspection.inspectNewStyleValues(literalExpression);
final NewStyleInspection inspection = new NewStyleInspection(literalExpression, this, myTypeEvalContext);
inspection.inspect();
}
}
}
@@ -179,9 +179,10 @@ public class PyStringFormatParser {
myUnclosedMapping = unclosedMapping;
}
}
public static class NewStyleSubstitutionChunk extends SubstitutionChunk {
@Nullable private String myConversion;
@Nullable private String myFieldNameAttribute;
@Nullable private String myMappingKeyAttributeName;
@Nullable private String myMappingKeyElementIndex;
private char myConversionType;
private boolean signOption;
@@ -243,12 +244,12 @@ public class PyStringFormatParser {
}
@Nullable
public String getFieldNameAttribute() {
return myFieldNameAttribute;
public String getMappingKeyAttributeName() {
return myMappingKeyAttributeName;
}
public void setFieldNameAttribute(@NotNull String fieldNameAttribute) {
myFieldNameAttribute = fieldNameAttribute;
public void setMappingKeyAttributeName(@NotNull String mappingKeyAttributeName) {
myMappingKeyAttributeName = mappingKeyAttributeName;
}
@Nullable
@@ -298,7 +299,7 @@ public class PyStringFormatParser {
@NotNull
private List<FormatStringChunk> parse() {
myPos = 0;
while(myPos < myLiteral.length()) {
while (myPos < myLiteral.length()) {
int next = myLiteral.indexOf('%', myPos);
while(next >= 0 && next < myLiteral.length()-1 && myLiteral.charAt(next+1) == '%') {
next = myLiteral.indexOf('%', next+2);
@@ -350,7 +351,8 @@ public class PyStringFormatParser {
try {
final int number = Integer.parseInt(name);
chunk.setPosition(number);
} catch (NumberFormatException e) {
}
catch (NumberFormatException e) {
chunk.setMappingKey(name);
}
myPos = nameEnd;
@@ -361,13 +363,13 @@ public class PyStringFormatParser {
}
// parse field name attribute name
if (isAt('.') ) {
if (isAt('.')) {
myPos++;
final int attributeEnd = StringUtil.indexOfAny(myLiteral, "!:.[}", myPos, end);
if (attributeEnd > 0 && myPos < attributeEnd) {
final String attributeName = myLiteral.substring(myPos, attributeEnd);
chunk.setFieldNameAttribute(attributeName);
chunk.setMappingKeyAttributeName(attributeName);
myPos = attributeEnd;
}
}
@@ -399,7 +401,7 @@ public class PyStringFormatParser {
if (attributeEnd > 0 && myPos < attributeEnd) {
myPos = attributeEnd + 1;
}
};
}
}
// conversion
@@ -452,7 +454,8 @@ public class PyStringFormatParser {
if (isAtSet(NEW_STYLE_CONVERSION_TYPES)) {
chunk.setConversionType(myLiteral.charAt(myPos));
}
}
}
results.add(chunk);
return autoPositionedFieldsCount;
@@ -464,7 +467,7 @@ public class PyStringFormatParser {
myResult.add(chunk);
myPos++;
if (isAt('(')) {
int mappingEnd = myLiteral.indexOf(')', myPos+1);
int mappingEnd = myLiteral.indexOf(')', myPos + 1);
if (mappingEnd < 0) {
chunk.setEndIndex(myLiteral.length());
chunk.setMappingKey(myLiteral.substring(myPos + 1));
@@ -473,7 +476,7 @@ public class PyStringFormatParser {
return;
}
chunk.setMappingKey(myLiteral.substring(myPos + 1, mappingEnd));
myPos = mappingEnd+1;
myPos = mappingEnd + 1;
}
else {
chunk.setAutoPosition(mySubstitutionsCount);
@@ -516,7 +519,7 @@ public class PyStringFormatParser {
@NotNull
private String parseWhileCharacterInSet(@NotNull final String characterSet) {
int flagStart = myPos;
while(isAtSet(characterSet)) {
while (isAtSet(characterSet)) {
myPos++;
}
return myLiteral.substring(flagStart, myPos);
@@ -7,7 +7,7 @@ def f():
def g():
return 1, 2, 3
"{foo[1]}".format(foo=g())
"{foo[1]:d}".format(foo=g())
"{foo[3]}".format(foo=<warning descr="Too few arguments for format string">g()</warning>)
def ff():
@@ -625,14 +625,14 @@ public class PyResolveTest extends PyResolveTestCase {
//PY-2748
public void testFormatPositionalArgs() {
PsiElement target = resolve();
assertTrue(target instanceof PyReferenceExpression);
assertInstanceOf(target, PyReferenceExpression.class);
assertEquals("string", target.getText());
}
//PY-2748
public void testFormatArgsAndKWargs() {
PsiElement target = resolve();
assertTrue(target instanceof PyStringLiteralExpression);
assertInstanceOf(target, PyStringLiteralExpression.class);
}
//PY-2748
@@ -652,14 +652,14 @@ public class PyResolveTest extends PyResolveTestCase {
//PY-2748
public void testFormatStringWithPackedListAsArgument() {
PsiElement target = resolve();
assertTrue(target instanceof PyNumericLiteralExpression);
assertInstanceOf(target, PyNumericLiteralExpression.class);
assertEquals("1", target.getText());
}
//PY-2748
public void testFormatStringWithPackedTupleAsArgument() {
PsiElement target = resolve();
assertTrue(target instanceof PyStringLiteralExpression);
assertInstanceOf(target, PyStringLiteralExpression.class);
assertEquals("\"snd\"", target.getText());
}
@@ -506,8 +506,8 @@ public class PyStringFormatParserTest extends TestCase {
final NewStyleSubstitutionChunk chunk = (NewStyleSubstitutionChunk)chunks.get(0);
assertEquals(TextRange.create(0, 7), chunk.getTextRange());
assertEquals("foo", chunk.getMappingKey());
assertNotNull(chunk.getFieldNameAttribute());
assertEquals("a", chunk.getFieldNameAttribute());
assertNotNull(chunk.getMappingKeyAttributeName());
assertEquals("a", chunk.getMappingKeyAttributeName());
}
public void testNewStyleFiledNameWithElementIndex() {
@@ -526,8 +526,8 @@ public class PyStringFormatParserTest extends TestCase {
final NewStyleSubstitutionChunk chunk = (NewStyleSubstitutionChunk)chunks.get(0);
assertEquals(TextRange.create(0, 9), chunk.getTextRange());
assertEquals("foo", chunk.getMappingKey());
assertNotNull(chunk.getFieldNameAttribute());
assertEquals("a", chunk.getFieldNameAttribute());
assertNotNull(chunk.getMappingKeyAttributeName());
assertEquals("a", chunk.getMappingKeyAttributeName());
assertEquals('d', chunk.getConversionType());
}