Skip to content

Commit

Permalink
Analyse type annotations defined as strings.
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Aug 19, 2023
1 parent c1fa097 commit de41a7d
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -894,7 +894,9 @@ public Object visitAssign(Assign node) throws Exception {
}

if (node.type != null) {
startScope(Scope.SCOPE_TYPE_ANNOTATION, node.type);
node.type.accept(this);
endScope(node.type);
}

//in 'target1 = target2 = a', this is 'target1, target2'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,22 +248,37 @@ public Object visitAssign(Assign node) throws Exception {

@Override
public Object visitStr(Str node) throws Exception {
if (this.scope.isVisitingTypeAnnotation()) {
String s = node.s;
IGrammar grammar = PyParser.createGrammar(true, this.nature.getGrammarVersion(), s.toCharArray());
Throwable errorOnParsing = null;
int startInternalStrColOffset = getStrOffset(node);
try {
SimpleNode typingNode = grammar.file_input();
errorOnParsing = grammar.getErrorOnParsing();
if (errorOnParsing == null) {
new FixLinesVisitor(node.beginLine - 1, node.beginColumn + startInternalStrColOffset - 1)
.traverse(typingNode);
this.traverse(typingNode);
}
} catch (Exception e) {
if (errorOnParsing == null) {
errorOnParsing = e;
}
}
if (errorOnParsing != null) {
IDocument doc = new Document(s);
reportParserError(node, node.beginLine, node.beginColumn + startInternalStrColOffset, doc,
errorOnParsing);
}
}
if (node.fstring && (node.fstring_nodes == null || node.fstring_nodes.length == 0)) {
// Note: if fstring_nodes have been pre-processed (i.e.: in cython parsing), we don't parse
// it here and just visit those contents in super.visitStr.
String s = node.s;
@SuppressWarnings("rawtypes")
List parseErrors = null;
int startInternalStrColOffset = 2; // +1 for 'f' and +1 for the quote.
if (node.raw) {
startInternalStrColOffset += 1;
}
if (node.unicode) {
startInternalStrColOffset += 1;
}
if (node.type == str_typeType.TripleDouble || node.type == str_typeType.TripleSingle) {
startInternalStrColOffset += 2;
}
int startInternalStrColOffset = getStrOffset(node);
FStringsAST ast = null;
if (s.trim().length() > 0) {
try {
Expand Down Expand Up @@ -293,6 +308,23 @@ public Object visitStr(Str node) throws Exception {
return super.visitStr(node);
}

public static int getStrOffset(Str node) {
int startInternalStrColOffset = 1; // +1 for the quote.
if (node.raw) {
startInternalStrColOffset += 1;
}
if (node.unicode) {
startInternalStrColOffset += 1;
}
if (node.fstring) {
startInternalStrColOffset += 1;
}
if (node.type == str_typeType.TripleDouble || node.type == str_typeType.TripleSingle) {
startInternalStrColOffset += 2;
}
return startInternalStrColOffset;
}

private void analyzeFStringAst(Str node, int startInternalStrColOffset, FStringsAST ast, IDocument doc)
throws Exception {
for (FStringExpressionContent content : ast.getFStringExpressionsContent(doc)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ public static String getScopeTypeStr(int scopeType) {

private AbstractScopeAnalyzerVisitor visitor;

/**
* If == 0 not visiting type annotation, otherwise we're in
* a type annotation.
*/
private int visitingTypeAnnotation = 0;

public boolean isVisitingTypeAnnotation() {
return this.visitingTypeAnnotation > 0;
}

private int getNewId() {
scopeUnique++;
return scopeUnique;
Expand Down Expand Up @@ -274,6 +284,9 @@ public ScopeItems getCurrScopeItems() {
* initializes a new scope
*/
public void startScope(int scopeType) {
if (scopeType == SCOPE_TYPE_ANNOTATION) {
this.visitingTypeAnnotation += 1;
}
int newId = getNewId();
scope.push(new ScopeItems(newId, scopeType));
scopeId.push(newId);
Expand All @@ -286,7 +299,11 @@ public int getCurrScopeId() {

public ScopeItems endScope() {
scopeId.pop();
return scope.pop();
ScopeItems scopeItems = scope.pop();
if (scopeItems.getScopeType() == SCOPE_TYPE_ANNOTATION) {
this.visitingTypeAnnotation -= 1;
}
return scopeItems;
}

public int size() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
*/
package com.python.pydev.analysis;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
Expand Down Expand Up @@ -153,6 +154,24 @@ protected IMessage[] checkError(int numberOfErrors) {
return msgs;
}

public interface IErrorFilter {
public boolean accept(String m);
}

protected IMessage[] checkErrorWithFilter(IErrorFilter filter) {
analyzer = new OccurrencesAnalyzer();
msgs = analyze();

List<IMessage> ret = new ArrayList<>();
for (IMessage msg : msgs) {
if (filter.accept(msg.getMessage().trim())) {
ret.add(msg);
}
}

return ret.toArray(new IMessage[0]);
}

/**
* Uses the doc attribute as the module and makes the analysis, checking if no error is found.
* @return the messages that were reported as errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -543,4 +543,50 @@ public void testRestInMatch() {
+ "swallow_report(b1)");
checkNoError();
}

public void testTypingInfoInStr() {
doc = new Document("from typing import Hashable\n"
+ "a: \"Hashable\"\n"
+ "");
checkNoError();
}

public void testTypingInfoInStrBad() {
doc = new Document("\n"
+ "a: 'Hashable'\n"
+ "");
IMessage[] messages = checkError("Undefined variable: Hashable\n");
assertEquals(1, messages.length);
assertEquals(2, messages[0].getStartLine(doc));
assertEquals(2, messages[0].getEndLine(doc));
assertEquals(5, messages[0].getStartCol(doc));
assertEquals(5 + 8, messages[0].getEndCol(doc));
}

public void testTypingInfoInStrBad2() {
doc = new Document("\n"
+ "def method(b: 'Hashable'):\n"
+ " pass\n"
+ "");
IMessage[] messages = checkError("Undefined variable: Hashable\n");
assertEquals(1, messages.length);
assertEquals(2, messages[0].getStartLine(doc));
assertEquals(2, messages[0].getEndLine(doc));
assertEquals(16, messages[0].getStartCol(doc));
assertEquals(16 + 8, messages[0].getEndCol(doc));
}

public void testTypingInfoInStrBad3() {
doc = new Document("\n"
+ "def method(b: 'this.. is not ..correct'):\n"
+ " pass\n"
+ "");
IMessage[] messages = checkErrorWithFilter((msg) -> {
return msg.startsWith("SyntaxError: ");
});
assertEquals(1, messages.length);
assertEquals(2, messages[0].getStartLine(doc));
assertEquals(2, messages[0].getEndLine(doc));
assertEquals(32, messages[0].getStartCol(doc));
}
}

0 comments on commit de41a7d

Please sign in to comment.