-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
error reporting includes columns #2163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
05caa92
to
02695d5
Compare
(the last three commits are clean-ups from the rebase, I can interactive-rebase those away if desired) |
also, not sure if there's any CI verification? but
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(as someone totally new to this codebase) this generally makes sense to me, aside from a couple questions.
i = 0 | ||
while i < len(errors): | ||
dup = False | ||
j = i - 1 | ||
while (j >= 0 and errors[j][0] == errors[i][0] and | ||
errors[j][1] == errors[i][1]): | ||
if errors[j] == errors[i]: | ||
if (errors[j][3] == errors[i][3] and | ||
errors[j][4] == errors[i][4]): # ignore column |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't the 0th and first elements need to be compared too?
also, is ignoring the column necessary b/c of the TODO in TypeConverter#generic_visit
in fastparse.py
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
they're taken care of because we're within the while loop.
Ignoring the column here, so that we only get the first instance of this particular error within the line. This logic could be tweaked, for sure. Maybe we actually do want to expose all instances of a given error. E.g.
- invalid type at column 1
- invalid type at column 5
@@ -740,15 +747,18 @@ def lex_break(self) -> None: | |||
last_tok.string += self.pre_whitespace + s | |||
self.i += len(s) | |||
self.line += 1 | |||
self.column = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why the assignment of 0? the value of i
, or 1 character beyond the end of the string seems to make sense for describing a line break
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so we're incrementing the line here, so have to reset column back to 0.
in this class, self.column
is keeping track of the column we're at (i.e. within the line) whereas self.i
keeps track of the position within the overall string.
@@ -390,6 +405,10 @@ def set_line(self, target: Union[Token, Node, int]) -> Node: | |||
self.initialization_statement.set_line(self.line) | |||
self.initialization_statement.lvalues[0].set_line(self.line) | |||
|
|||
def set_column(self, target: Union[Token, Node, int]) -> Node: | |||
super().set_column(target) | |||
return self |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inconsistent to return self
? do we need to do the same stuff with self.initializer
, self.variable
, and self.initialization_statement
as above in set_line
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps? I just matched the behaviour of set_line. Not sure what the philosophy is generally regarding chaining.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just matching behaviour of set_line - not sure of philosophy around chaining generally
there are some places in the code where already we do
some_thing = SomeNode(blah).set_line(1)
so with this they become
some_thing = SomeNode(blah).set_line(1).set_column(2)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps this should be removed for now (until I figure out (in FLUP?) how the delegation should work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find return self
an anti-pattern that encourages unneeded cleverness and confuses side effects with functions.
Maybe set_line()
should get an optional column
method? That would also beautifully allow you to call it with a Token or Node and copy both line and column from there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Also, what's FLUP? Googling was inconclusive. :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(And, if you can remove the return self
from set_line()
and fix the fallout, if any, that would be great.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FLUP oops.. :P follow-up, as in - in a future PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and yep, will combine them/see how bad the fallout is if we take out the return self (think it shouldn't be that bad at all, from the places I've seen set_line
so far)
@@ -457,6 +476,10 @@ def set_line(self, target: Union[Token, Node, int]) -> Node: | |||
arg.set_line(self.line) | |||
return self | |||
|
|||
def set_column(self, target: Union[Token, Node, int]) -> Node: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to handle self.arguments
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, probably - was going to investigate that in future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
off the top of my head I can't remember how tokens -> argument node happens, it's possible they already have the column (and actually from editing the tests, my gut says they do)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps this should be removed for now, until I figure out how (in FLUP?) to set columns for args
Thanks! This PR clearely represents a significant contribution. Normally the tests run automatically on Travis-CI. I don't know why they didn't run this time but I suspect the sheer size of the diff might explain that. I find such a huge diff (66 files!) hard to review myself. Could you perhaps come up with a way to reduce most of the "trivial" changes e.g. the endless "E:" -> "E:0:" in the tests and the addition of of ", 0" in the errors.report() calls? (Maybe something with argument default values?) |
Not sure if there have been contributions from a fork repo before - not familiar with travis, but for circle (https://circleci.com/) there's some explicit setting to run builds for PRs from forks. Yep, the structure of the tests (that the tests test error messages and this changes the error messages, as a feature :P) makes this a very scary PR. Some thoughts of approaches here:
|
Travis is supposed to always run on PRs. @gvanrossum What if you try closing-and-reopening this PR? That sometimes works for me. |
OK, trying that... |
Looks like they're running now. |
And it's failed. Somehow GitHub then removed the Travis CI results, so here's the link: And indeed the breakage looks like it's been caused by some new tests that were added recently. Maybe we could add a command-line flag that must be set to enable column numbers? That way only a few tests would need to be updated (say, tests just for this feature). Finally, I'm getting crashes when running I would really like to merge this, but I want to see a smaller diff. Please? |
(Ih wait, now the test results are back. I wonder if it just takes a really long time for some reason?) |
Command line flag sounds reasonable - will try to get to it this weekend. Will also look into the error you're seeing with --fast-parser :( |
tests passing! diff now ~ 1/10th of the old size |
else: | ||
assert isinstance(typ, ast35.Expression) | ||
return TypeConverter(line=line).visit(typ.body) | ||
|
||
|
||
def with_line(f: Callable[['ASTConverter', T], U]) -> Callable[['ASTConverter', T], U]: | ||
def with_line_and_column(f: Callable[['ASTConverter', T], U]) -> Callable[['ASTConverter', T], U]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you rename this back? That would reduce the diff size a bit more. I know the name would not be completely covering the semantics but then again the name isn't all that intuitive either way. I don't think the churn caused by the rename is worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done - could totally change the name at some point in the future in a single-purpose PR if it becomes confusing
(But thanks for reducing the diff size!) |
main:4: error: Incompatible types in assignment (expression has type Callable[[], str], variable has type Callable[[], int]) | ||
main:4: error: Incompatible return value type (got "str", expected "int") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the churn here (and in some other *.test files) is because now errors on earlier columns come first
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine!
and thus.. - revert tests - modify the few tests so that they work with the new ordering (errors on smaller columns are shown first, regardless of whether the column number is printed or not)
…umbers in error reporting
7c976a4
to
7c17e92
Compare
additionally, set_line no longer returns self (and thus do associated cleanup)
@@ -95,7 +95,8 @@ def with_line(f: Callable[['ASTConverter', T], U]) -> Callable[['ASTConverter', | |||
@wraps(f) | |||
def wrapper(self: 'ASTConverter', ast: T) -> U: | |||
node = f(self, ast) | |||
node.set_line(ast.lineno) | |||
# some ast nodes (e.g. Return) do not come with col_offset |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heh, I figured out why (by putting a pdb trap here). It's because visit_lambda() synthesizes a Return node and sets only the lineno. Once you fix that I think the getattr() call is no longer necessary (and it shouldn't be!).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sweet, yep - works
@@ -804,7 +805,8 @@ def visit_raw_str(self, s: str) -> Type: | |||
return parse_type_comment(s.strip(), line=self.line) | |||
|
|||
def generic_visit(self, node: ast35.AST) -> None: | |||
raise TypeCommentParseError(TYPE_COMMENT_AST_ERROR, self.line) | |||
raise TypeCommentParseError(TYPE_COMMENT_AST_ERROR, self.line, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunately I think we still need the getattr here... not all AST nodes come with column info, only the ones deriving from stmt, expr, etc...
(https://github.com/python/typeshed/blob/master/third_party/3/typed_ast/ast35.pyi)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to merge this now, unless some quick tests reveal more issues. Thank you so much for your work on this PR, and for being flexible in response to my review comments!
@@ -804,7 +805,8 @@ def visit_raw_str(self, s: str) -> Type: | |||
return parse_type_comment(s.strip(), line=self.line) | |||
|
|||
def generic_visit(self, node: ast35.AST) -> None: | |||
raise TypeCommentParseError(TYPE_COMMENT_AST_ERROR, self.line) | |||
raise TypeCommentParseError(TYPE_COMMENT_AST_ERROR, self.line, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's fine!
fixes #1216