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

Improve error suggestion when using logical operators. Fixes #2715 #2738

Merged
merged 1 commit into from
Jun 4, 2016
Merged

Improve error suggestion when using logical operators. Fixes #2715 #2738

merged 1 commit into from
Jun 4, 2016

Conversation

fernandes
Copy link
Contributor

When using if foo and bar the compiler was suggestion the use of
rand method, and that is because rand is defined at top level and
Crystal::MatchesLookup was finding it as the nearest option.

Now it checks the missing method is and or or and suggest the right
&& and || operators.

@@ -282,6 +285,19 @@ class Crystal::Call
raise message, owner_trace
end

def logical_operator?(def_name)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

created these methods to avoid getting the raise_matches_not_found method getting bigger and bigger.

If makes sense to keep them, my idea is to keep extracting more methods (is is a good idea?)

@fernandes
Copy link
Contributor Author

totally missed the crystal tool format pushed the indented code 😎

def convert_def_name_to_logical_operator(def_name)
case def_name
when "and"; "&&"
when "or" ; "||"
Copy link
Member

Choose a reason for hiding this comment

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

Not sure about the formatting here, might just do a Hash(String, String) to a constant and look up?

Copy link
Member

Choose a reason for hiding this comment

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

It's fine with me, I'm not sure adding a Hash is worth it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah... my first solution was a hash, then I thought was polluting unnecessary, and changed to method hahahah what you think is the best approach, I'm ok with :)

@jhass
Copy link
Member

jhass commented Jun 3, 2016

Looking at this I wonder whether we should future proof this a bit, we could generically define a list of static overrides for the suggestion, mentioning logic operators in the identifiers chosen here doesn't even seem necessary.

@fernandes
Copy link
Contributor Author

@jhass in terms of code would be changing from logical_operator? to static_override? something like this?

@@ -90,6 +90,9 @@ class Crystal::Call
error_msg = String.build do |msg|
if obj && owner != mod
msg << "undefined method '#{def_name}' for #{owner}"
elsif logical_operator?(def_name)
msg << "undefined method '#{def_name}'"
similar_name = convert_def_name_to_logical_operator(def_name)
Copy link
Member

Choose a reason for hiding this comment

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

Why not just

similar_name =  STATIC_SUGGESTIONS[def_name]? || owner.lookup_similar_def_name(def_name, self.args.size, block)

in line 88 above?

Copy link
Member

Choose a reason for hiding this comment

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

Note that this shouldn't always be suggested, only when there's no receiver. Doing that in line 88 will always suggest it, which is not very good.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, that's what I missed.

@asterite
Copy link
Member

asterite commented Jun 3, 2016

We can add this if many feel this is OK. I tried doing if (1 and 2) in Java and C# and I don't get that suggestion. Should we start adding suggestions for every bit of Ruby syntax we are missing? Maybe it's too much.

(but I'm OK with adding these suggestions to make it easier for people coming from Ruby, the problem is when to stop)

@fernandes
Copy link
Contributor Author

and/or are commonly used in Ruby, specially because && and and have different behaviors and in a lot of cases and is used.

I don't think we should add for every bit of Ruby, just for most common use cases (I know, what is a "common" case? again, when to stop) - that's the reason I added only for and, or and not for xor, not

@asterite
Copy link
Member

asterite commented Jun 3, 2016

But "not" is a keyword in Ruby. Maybe we should add it too.

@fernandes
Copy link
Contributor Author

@asterite what would be the suggestion?

did you mean "!"?

@asterite
Copy link
Member

asterite commented Jun 3, 2016

@fernandes Yes

@fernandes
Copy link
Contributor Author

@asterite done! 😄

@asterite
Copy link
Member

asterite commented Jun 4, 2016

@fernandes Just a final refactor before merging this. There's no need for two methods, logical_operator? and convert_def_name_to_logical_operator. We can have just one, logical_operator? that returns nil if it's not an operator, or the replacement if it is (this is your current convert_def_name_to_logical_operator). Then do:

if obj && owner != mod
   msg << "undefined method '#{def_name}' for #{owner}"
elsif op = logical_operator?(def_name)
  msg << "undefined method '#{def_name}'"
  similar_name = op

That way there's less code and the method is only called once (a bit faster, but won't be noticeable).

@fernandes
Copy link
Contributor Author

@asterite thanks for the tips... what do you think about convert_to_logical_operator ?

usually methods like foo? are interest when returning boolean, is there any convention like this in crystal or do you prefer to keep logical_operator?

@asterite
Copy link
Member

asterite commented Jun 4, 2016

The convention of methods ending with ? is that they either return a bool or a nilable type. I think it's fine for logical_operator? to return the converted name, or nil if there's no conversion to be done, and then use that return value.

@asterite
Copy link
Member

asterite commented Jun 4, 2016

But you can use convert_to_logical_operator too, with the same meaning, choose what you like more :-)

When using `if foo and bar` the compiler was suggestion the use of
`rand` method, and that is because `rand` is defined at top level and
Crystal::MatchesLookup was finding it as the nearest option.

Now it checks the missing method is `and` or `or` and suggest the right
`&&` and `||` operators.
@fernandes
Copy link
Contributor Author

@asterite ready! 👍

@asterite
Copy link
Member

asterite commented Jun 4, 2016

Thanks!!

@asterite asterite merged commit 356bf1a into crystal-lang:master Jun 4, 2016
@fernandes
Copy link
Contributor Author

🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants