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

TypeNode#annotated_types #7648

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions spec/compiler/semantic/annotation_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -794,4 +794,116 @@ describe "Semantic: annotation" do
))
end
end

describe "#annotated_types" do
it "returns an empty array if there are none defined" do
assert_type(%(
annotation Foo
end

module Moo
end

{% if Foo.annotated_types.empty? %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end

it "includes annotated modules" do
assert_type(%(
annotation Foo
end

@[Foo]
module Moo
end

{% if Foo.annotated_types == [Moo] %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end

it "includes annotated classes" do
assert_type(%(
annotation Foo
end

@[Foo]
class Moo
end

{% if Foo.annotated_types == [Moo] %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end

it "includes annotated structs" do
assert_type(%(
annotation Foo
end

@[Foo]
struct Moo
end

{% if Foo.annotated_types == [Moo] %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end

it "does not duplicate types if multiple annotations of same type are applied" do
assert_type(%(
annotation Foo
end

@[Foo]
@[Foo]
struct Moo
end

{% if Foo.annotated_types == [Moo] %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end

it "works with multiple types/annotations" do
assert_type(%(
annotation Foo; end
annotation Bar; end

@[Foo]
struct Chicken; end

@[Foo]
@[Bar]
class Cow; end

@[Foo]
module Turkey; end

class Monkey; end

{% if Foo.annotated_types == [Chicken, Cow, Turkey] && Bar.annotated_types == [Cow] %}
1
{% else %}
'a'
{% end %}
)) { int32 }
end
end
end
4 changes: 4 additions & 0 deletions src/compiler/crystal/macros.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1755,6 +1755,10 @@ module Crystal::Macros
def annotations(type : TypeNode) : ArrayLiteral(Annotation)
end

# Returns an array of `TypeNode` that are annotated with `self`.
def annotated_types : ArrayLiteral(TypeNode)
end

# Returns the number of elements in this tuple type or tuple metaclass type.
# Gives a compile error if this is not one of those types.
def size : NumberLiteral
Expand Down
11 changes: 11 additions & 0 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1533,6 +1533,17 @@ module Crystal
return ArrayLiteral.new if annotations.nil?
ArrayLiteral.map(annotations, &.itself)
end
when "annotated_types"
interpret_argless_method(method, args) do
type = self.type.instance_type
case type
when Crystal::AnnotationType
types = type.class_types
ArrayLiteral.map(types) { |class_type| TypeNode.new class_type }
else
raise "undefined method 'annotated_types' for TypeNode of type #{type} (must be an annotation type)"
end
end
when "size"
interpret_argless_method(method, args) do
type = self.type.instance_type
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/crystal/semantic/top_level_visitor.cr
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
end
end

annotation_type.class_types << type
type.add_annotation(annotation_type, ann)
end

Expand Down Expand Up @@ -238,6 +239,7 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
attach_doc type, node

process_annotations(annotations) do |annotation_type, ann|
annotation_type.class_types << type
type.add_annotation(annotation_type, ann)
end

Expand Down
2 changes: 2 additions & 0 deletions src/compiler/crystal/types.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2685,6 +2685,8 @@ module Crystal
end

class AnnotationType < NamedType
property class_types : Set(Type) = Set(Type).new
Blacksmoke16 marked this conversation as resolved.
Show resolved Hide resolved

def type_desc
"annotation"
end
Expand Down