Skip to content

Commit

Permalink
Add several missing ASTNode macro methods (#10811)
Browse files Browse the repository at this point in the history
Adds:

* `Annotation#name`
  * #9326 converts the name to a `MacroId` on behalf of the user. This PR returns the `Path` directly (like `Generic`).
* `{Case,When}#exhaustive?`
* `Call#global?`
* `Def#{abstract?,free_vars}`
  • Loading branch information
HertzDevil authored Nov 15, 2021
1 parent fe810b3 commit b06c7b2
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 1 deletion.
37 changes: 37 additions & 0 deletions spec/compiler/macro/macro_methods_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2148,10 +2148,21 @@ module Crystal
assert_macro %({{x.return_type}}), "", {x: Def.new("some_def")}
end

it "executes free_vars" do
assert_macro %({{x.free_vars}}), "[] of ::NoReturn", {x: Def.new("some_def")}
assert_macro %({{x.free_vars}}), "[T]", {x: Def.new("some_def", free_vars: %w(T))}
assert_macro %({{x.free_vars}}), "[T, U, V]", {x: Def.new("some_def", free_vars: %w(T U V))}
end

it "executes receiver" do
assert_macro %({{x.receiver}}), "self", {x: Def.new("some_def", receiver: Var.new("self"))}
end

it "executes abstract?" do
assert_macro %({{x.abstract?}}), "false", {x: Def.new("some_def")}
assert_macro %({{x.abstract?}}), "true", {x: Def.new("some_def", abstract: true)}
end

it "executes visibility" do
assert_macro %({{x.visibility}}), ":public", {x: Def.new("some_def")}
assert_macro %({{x.visibility}}), ":private", {x: Def.new("some_def").tap { |d| d.visibility = Visibility::Private }}
Expand Down Expand Up @@ -2294,6 +2305,11 @@ module Crystal
it "executes named args value" do
assert_macro %({{x.named_args[0].value}}), "1", {x: Call.new(1.int32, "some_call", named_args: [NamedArgument.new("a", 1.int32), NamedArgument.new("b", 2.int32)])}
end

it "executes global?" do
assert_macro %({{x.global?}}), "false", {x: Call.new(1.int32, "some_call")}
assert_macro %({{x.global?}}), "true", {x: Call.new(nil, "some_call", global: true)}
end
end

describe "arg methods" do
Expand Down Expand Up @@ -2360,9 +2376,17 @@ module Crystal
assert_macro %({{x.whens[0].body}}), "4", {x: case_node}
end

it "executes when exhaustive?" do
assert_macro %({{x.whens[0].exhaustive?}}), "false", {x: case_node}
end

it "executes else" do
assert_macro %({{x.else}}), "5", {x: case_node}
end

it "executes exhaustive?" do
assert_macro %({{x.exhaustive?}}), "false", {x: case_node}
end
end

describe "in" do
Expand All @@ -2371,6 +2395,14 @@ module Crystal
it "executes whens" do
assert_macro %({{x.whens}}), "[in 2, 3\n 4\n]", {x: case_node}
end

it "executes when exhaustive?" do
assert_macro %({{x.whens[0].exhaustive?}}), "true", {x: case_node}
end

it "executes exhaustive?" do
assert_macro %({{x.exhaustive?}}), "true", {x: case_node}
end
end
end

Expand Down Expand Up @@ -2664,6 +2696,11 @@ module Crystal
end

describe "annotation methods" do
it "executes name" do
assert_macro %({{x.name}}), %(Foo), {x: Annotation.new(Path.new("Foo"))}
assert_macro %({{x.name}}), %(Foo::Bar), {x: Annotation.new(Path.new(["Foo", "Bar"]))}
end

it "executes [] with NumberLiteral" do
assert_macro %({{x[y]}}), %(42), {
x: Annotation.new(Path.new("Foo"), [42.int32] of ASTNode),
Expand Down
27 changes: 26 additions & 1 deletion src/compiler/crystal/macros.cr
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,10 @@ module Crystal::Macros

# An annotation on top of a type or variable.
class Annotation < ASTNode
# Returns the name of this annotation.
def name : Path
end

# Returns the value of a positional argument,
# or NilLiteral if out of bounds.
def [](index : NumberLiteral) : ASTNode
Expand Down Expand Up @@ -947,6 +951,10 @@ module Crystal::Macros
def receiver : ASTNode | Nop
end

# Returns `true` if this call refers to a global method (starts with `::`).
def global? : BoolLiteral
end

# Returns this call's arguments.
def args : ArrayLiteral
end
Expand Down Expand Up @@ -1140,6 +1148,11 @@ module Crystal::Macros
def return_type : ASTNode | Nop
end

# Returns the free variables of this method, or an empty `ArrayLiteral` if
# there are none.
def free_vars : ArrayLiteral(MacroId)
end

# Returns the body of this method.
def body : ASTNode
end
Expand All @@ -1149,6 +1162,10 @@ module Crystal::Macros
def receiver : ASTNode | Nop
end

# Returns `true` is this method is declared as abstract, `false` otherwise.
def abstract? : BoolLiteral
end

# Returns the visibility of this def: `:public`, `:protected` or `:private`.
def visibility : SymbolLiteral
end
Expand Down Expand Up @@ -1281,7 +1298,7 @@ module Crystal::Macros
end
end

# A `when` inside a `case`.
# A `when` or `in` inside a `case`.
class When < ASTNode
# Returns the conditions of this `when`.
def conds : ArrayLiteral
Expand All @@ -1290,6 +1307,10 @@ module Crystal::Macros
# Returns the body of this `when`.
def body : ASTNode
end

# Returns `true` if this is an `in`, or `false` if this is a `when`.
def exhaustive? : BoolLiteral
end
end

# A `case` expression.
Expand All @@ -1305,6 +1326,10 @@ module Crystal::Macros
# Returns the `else` of this `case`.
def else : ArrayLiteral(When)
end

# Returns whether this `case` is exhaustive (`case ... in`).
def exhaustive? : BoolLiteral
end
end

# A `select` expression.
Expand Down
18 changes: 18 additions & 0 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1324,6 +1324,14 @@ module Crystal
interpret_check_args { BoolLiteral.new(@yields != nil) }
when "return_type"
interpret_check_args { @return_type || Nop.new }
when "free_vars"
interpret_check_args do
if (free_vars = @free_vars) && !free_vars.empty?
ArrayLiteral.map(free_vars) { |free_var| MacroId.new(free_var) }
else
empty_no_return_array
end
end
when "body"
interpret_check_args { @body }
when "receiver"
Expand All @@ -1332,6 +1340,8 @@ module Crystal
interpret_check_args do
visibility_to_symbol(@visibility)
end
when "abstract?"
interpret_check_args { BoolLiteral.new(@abstract) }
when "annotation"
fetch_annotation(self, method, args, named_args, block) do |type|
self.annotation(type)
Expand Down Expand Up @@ -1897,6 +1907,8 @@ module Crystal
interpret_check_args { self.block || Nop.new }
when "block_arg"
interpret_check_args { self.block_arg || Nop.new }
when "global?"
interpret_check_args { BoolLiteral.new(@global) }
else
super
end
Expand Down Expand Up @@ -1948,6 +1960,8 @@ module Crystal
interpret_check_args { ArrayLiteral.map whens, &.itself }
when "else"
interpret_check_args { self.else || Nop.new }
when "exhaustive?"
interpret_check_args { BoolLiteral.new(@exhaustive) }
else
super
end
Expand All @@ -1961,6 +1975,8 @@ module Crystal
interpret_check_args { ArrayLiteral.new(conds) }
when "body"
interpret_check_args { body }
when "exhaustive?"
interpret_check_args { BoolLiteral.new(@exhaustive) }
else
super
end
Expand Down Expand Up @@ -2204,6 +2220,8 @@ module Crystal
class Annotation
def interpret(method : String, args : Array(ASTNode), named_args : Hash(String, ASTNode)?, block : Crystal::Block?, interpreter : Crystal::MacroInterpreter, name_loc : Location?)
case method
when "name"
interpret_check_args { @path }
when "[]"
interpret_check_args do |arg|
case arg
Expand Down

0 comments on commit b06c7b2

Please sign in to comment.