Skip to content
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

Shorten expression strings in assignment messages #153

Conversation

sonalmahajan15
Copy link
Contributor

This PR shortens the expression strings in assignment part of the error messages. For example, the expression x = s.foo(longVarName, &anotherLongVarName, "abc", true) is shortened to s.foo(...) to offer better readability. The changes have also have been performance evaluated internally.

Copy link

codecov bot commented Dec 15, 2023

Codecov Report

Attention: 24 lines in your changes are missing coverage. Please review.

Comparison is base (43b26d7) 89.24% compared to head (7ca80e6) 89.18%.

Files Patch % Lines
assertion/function/assertiontree/backprop.go 40.00% 8 Missing and 4 partials ⚠️
util/asthelper/asthelper.go 80.32% 7 Missing and 5 partials ⚠️
Additional details and impacted files
@@                              Coverage Diff                              @@
##           sonalmahajan15/fix-backprop-error-message     #153      +/-   ##
=============================================================================
- Coverage                                      89.24%   89.18%   -0.07%     
=============================================================================
  Files                                             54       55       +1     
  Lines                                           9087     9156      +69     
=============================================================================
+ Hits                                            8110     8166      +56     
- Misses                                           820      825       +5     
- Partials                                         157      165       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

util/util.go Outdated

for _, char := range expr {
switch char {
case '(', '{', '[':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this means the result will be incorrect if I have code like (m["("]), right? since it's checking for specific characters.

I'm thinking that we probably shouldn't post-process the expr string, but we should simply work on the AST nodes or printer logic. But after a bit of thoughts they all seem unachievable:

(1) traverse the AST node and remove the argument if it's too long and then print

We shouldn't modify the AST nodes since they are shared with other analyzers. Maybe https://github.com/go-toolsmith/astcopy to deep copy the AST nodes?

(2) Change the printer logic

The https://pkg.go.dev/go/printer package doesn't provide customizations unfortunately, so we'll have to copy the printer logic in NilAway and implement some customizations. But that seems too much of a burden to maintain.

Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. We could always handle quotes as well in the string processing approach. But I get the broader point that this may require us to deal with many corner cases. As you pointed out, the two options discussed above also have their own issues. So, as we discussed internally, I have updated the shortening logic to do a light-weight AST traversal and printing, with defaulting to printer package for all unhandled cases. I have tested it internally and seems to be working fairly well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, added many test cases around (m["("]) :)

util/util.go Outdated Show resolved Hide resolved
Copy link
Contributor

@yuxincs yuxincs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of suggestions, but no real blockers (the logic LGTM, nice work!)

return s.String()
}

func printExprHelper(e ast.Expr, pass *analysis.Pass, s *strings.Builder) {
Copy link
Contributor

@yuxincs yuxincs Jan 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, since strings.Builder actually satisfies io.Writer interface, let's take io.Writer instead for better generality (i.e., "accept interface, return structs")

Two nits:

  • We can actually drop the Helper suffix in the name since it's pretty common to have an unexported internal helper function for the exported API :)
  • We probably want to follow the function signature of printer.Fprint for consistency (i.e., func printExpr(writer io.Writer, fset *token.FileSet, e ast.Expr) error).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good suggestion! Updated the logic to incorporate the above points.

Comment on lines 40 to 39
if !isShortenExpr {
astExprToString(e, pass)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if !isShortenExpr {
astExprToString(e, pass)
}
if !isShortenExpr {
return astExprToString(e, pass)
}

Right?

Actually, we can get rid of the astExprToString helper function, since strings.Builder actually satisfies the io.Writer interface.

This means we can simply do the following:

func PrintExpr(....) (string, error) {
  builder := &strings.Builder{}

  if !isShortenExpr {
    err := printer.Fprint(builder, pass.Fset, e)
    return builder.String(), err
  }

  err := printExpr(builder, pass.Fset, e) 
  return builder.String(), err
}

If we really don't want to handle the errors, we can offer a MustPrintExpr variant that panics on error :) I think we should generally keep the errors internally no matter what, and at the API level, if we really want to offer the conveniences, we offer the Must variants.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Did not know that we could use printer.Fprint() directly here. Updated the code to reflect this. Also, I agree that we should handle errors when possible. So incorporated that approach in the code.

s.WriteString("]")

default:
s.WriteString(astExprToString(e, pass))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, we can keep using the buffer instead of "calling a function that creates a new buffer and returns the result and then writes the result to the old buffer".

return printer.Fprint(writer, fset, e)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done :)


switch node := e.(type) {
case *ast.Ident:
_, err = writer.Write([]byte(node.Name))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can still use io.WriteString (https://pkg.go.dev/io#WriteString) that handles conversions of strings to underlying bytes instead of manually constructing them. This should provide better utf-8 compatibility for languages other than English :)

(actually, this reminds me, can we add a case where the expression contains non-English characters?)

We can use fmt.Println("Hello, 世界") and fmt.Println("世界") from Go's official docs :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah nice, this is convenient! Updated the code. Also, added a test case for non-English expressions.

Copy link
Contributor

@yuxincs yuxincs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Great work 🎉

@sonalmahajan15 sonalmahajan15 force-pushed the sonalmahajan15/fix-backprop-error-message branch from 92d792c to 43b26d7 Compare January 17, 2024 21:14
@sonalmahajan15 sonalmahajan15 force-pushed the sonalmahajan15/prune-expr-to-string branch from 4e85a91 to 7ca80e6 Compare January 17, 2024 21:15
@sonalmahajan15 sonalmahajan15 merged commit 0127d3f into sonalmahajan15/fix-backprop-error-message Jan 17, 2024
5 checks passed
@sonalmahajan15 sonalmahajan15 deleted the sonalmahajan15/prune-expr-to-string branch January 17, 2024 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants