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

Add support for default values to TYPEDSIGNATURES #170

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

JamesWrigley
Copy link

This is done by making TypedMethodSignatures interpolatable and giving it the Expr of the thing the docstring is bound to. From the AST we can parse each arguments name, type annotation, default value, and whether or not it's variadic. Currently only the default value is used in TYPEDSIGNATURES, though in the future it might be an idea to use the type information as well.

I think this could do with some more tests (parsing is hard 😢), but it's pretty close to being complete. Fixes #107, fixes #19. Would make #97 possible if we're ok with getting the types from the AST.

This is done by making `TypedMethodSignatures` interpolatable and giving it the
Expr of the thing the docstring is bound to. From the AST we can parse each
arguments name, type annotation, default value, and whether or not it's
variadic. Currently only the default value is used in `TYPEDSIGNATURES`, though
in the future it might be an idea to use the type information as well.
@MichaelHatherly
Copy link
Member

Thanks @JamesWrigley! Would it make sense to also apply this to SIGNATURES rather than just TYPEDSIGNATURES?

src/parsing.jl Outdated
# Parse an argument with a type annotation.
# Example input: `x::Int`
function parse_arg_with_type(arg_expr::Expr)
if arg_expr.head != :(::)
Copy link
Member

Choose a reason for hiding this comment

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

There's Meta.isexpr which would be preferable to use to keep things consistent instead of reaching into the .head field.

Copy link
Member

Choose a reason for hiding this comment

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

Same applies to elsewhere that uses .head == comparisons.

Copy link
Author

Choose a reason for hiding this comment

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

Good point, fixed in 1c80865.

@JamesWrigley
Copy link
Author

Thanks @JamesWrigley! Would it make sense to also apply this to SIGNATURES rather than just TYPEDSIGNATURES?

Ah, yes you're right. I never use that so I forgot about it 😅 Also one question about the tests, the call to DSE.parsedocs(DSE) at the very end is failing because TypedMethodSignatures.expr is nothing. I guess that means that the interpolation didn't execute? How could I fix that? I've tried it with other docstrings and for those the interpolation definitely works.

@JamesWrigley
Copy link
Author

Applied to SIGNATURES in de0f36b. And I think I know why the templating tests are failing, I observed that within a template template_hook(::LineNumberNode, ::Module, ::Symbol) was being called, which will fall back to the default Core.atdoc(). I think the problem is that the Expr and the docstring are defined in separate places so template_hook(::LineNumberNode, ::Module, docstr, ::Expr) will never be called and we'll never capture the expression. Is there a way around that? Or will we just have to live with never getting Expr's in templates?

@JamesWrigley
Copy link
Author

JamesWrigley commented Aug 19, 2024

Made the expr optional in fe01fe3, and made the parser more robust in 1532071. I think this is ready to merge now 🤞

(BTW if you approve I can rebase before merging to clean up the commit history)

@MichaelHatherly
Copy link
Member

(I'll review when I'm back from vacation in a couple of weeks @JamesWrigley)

elseif n_expr_args == 2
# 'x::Int'
ASTArg(; name=arg_expr.args[1], type=arg_expr.args[2])
end
Copy link
Member

Choose a reason for hiding this comment

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

Adding an explicit failure branch here (even though we know that ::s always have one or two arguments) would be good.

Copy link
Author

Choose a reason for hiding this comment

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

Sure, added in f1c9145.

elseif obj isa Symbol || (obj isa Expr && isempty(obj.args))
# Base case: this is the end of a branch in the expression tree
return nothing
end
Copy link
Member

Choose a reason for hiding this comment

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

There's no case that covers any other potential types found in ASTs. This just assumes that it is now an Expr. Are we sure that that is a safe assumption to make?

Copy link
Author

Choose a reason for hiding this comment

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

Good point, probably not. Should be fixed in f1c9145.

@JamesWrigley
Copy link
Author

I'm looking into the failure on 1.10 👀

@JamesWrigley
Copy link
Author

Ok, should be fixed in 1efecf6. The problem is that Base.return_types() was failing on 1.10 for a generated function, which it is documented to be able to do. I figured that a try-catch is the safest way to fix it in case people do something like use a macro that creates a @generated expr internally.

@JamesWrigley
Copy link
Author

The issue with Julia 1.0 is that CodeTracking can't be installed for the tests. Do we dare just bump the minimum to Julia 1.10 since that's going to be the new LTS?

@MichaelHatherly
Copy link
Member

Do we dare just bump the minimum to Julia 1.10 since that's going to be the new LTS?

Ideally not just because of a test-only dependency needing to have a more recent version. Was using CodeTracking the only way you've found to be able to write those tests?

@JamesWrigley
Copy link
Author

It's by far the easiest, yeah. It's used for getting the Expr's for methods. Otherwise I think we'd need to either vendor in the relevant parts from CodeTracking.jl or rewrite the tests to pass Expr's to them explicitly. I tried lowering the compat bounds to 1.0 in 79783e4, let's see if that works 🤞

@MichaelHatherly
Copy link
Member

Looks to have gotten further, there's a syntax issue with some namedtupled code now, probably just needs rewriting to a form that supports 1.0.

@JamesWrigley
Copy link
Author

JamesWrigley commented Sep 17, 2024

How about bumping to Julia 1.6? 😅 Supporting Julia 1.0 is a bit tricky because this line gives different results from later versions: https://github.com/JamesWrigley/DocStringExtensions.jl/blob/79783e4e0228fcd82a427a743d4c7c9d906b2d7d/src/utilities.jl#L360-L362

I'm guessing we weren't hitting that codepath before. FWIW ebb95a9 has the changes that pass the tests on 1.6.

@MichaelHatherly
Copy link
Member

Supporting Julia 1.0 is a bit tricky because this line gives different results from later versions:

What are the kinds of differences you're seeing here? Perhaps there are ways to write the tests to not encounter them?

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.

Default argument value in function documentation Include the defaults for keyword arguments
2 participants