diff --git a/spec/compiler/semantic/macro_spec.cr b/spec/compiler/semantic/macro_spec.cr index c66ee3d902f5..02a4d685d37f 100644 --- a/spec/compiler/semantic/macro_spec.cr +++ b/spec/compiler/semantic/macro_spec.cr @@ -1227,6 +1227,46 @@ describe "Semantic: macro" do CRYSTAL end + it "finds macro through generic type" do + assert_type(<<-CRYSTAL) { int32.metaclass } + module Foo(T) + macro foo + {{ T }} + end + end + + Foo(Int32).foo + CRYSTAL + end + + it "finds macro through alias to generic instance" do + assert_type(<<-CRYSTAL) { int32.metaclass } + module Foo(T) + macro foo + {{ T }} + end + end + + alias Bar = Foo(Int32) + + Bar.foo + CRYSTAL + end + + it "finds macro through alias to generic type" do + assert_type(<<-CRYSTAL) { int32.metaclass } + module Foo(T) + macro foo + {{ T }} + end + end + + alias Bar = Foo + + Bar(Int32).foo + CRYSTAL + end + it "can override macro (#2773)" do assert_type(<<-CRYSTAL) { char } macro foo diff --git a/src/compiler/crystal/semantic/semantic_visitor.cr b/src/compiler/crystal/semantic/semantic_visitor.cr index ada6d392f626..52d1b6de3e4e 100644 --- a/src/compiler/crystal/semantic/semantic_visitor.cr +++ b/src/compiler/crystal/semantic/semantic_visitor.cr @@ -292,9 +292,15 @@ abstract class Crystal::SemanticVisitor < Crystal::Visitor obj = node.obj case obj - when Path + when Path, Generic base_type = @path_lookup || @scope || @current_type - macro_scope = base_type.lookup_type_var?(obj, free_vars: free_vars, raise: raise_on_missing_const) + macro_scope = + if obj.is_a?(Path) + base_type.lookup_type_var?(obj, free_vars: free_vars, raise: raise_on_missing_const) + else + base_type.lookup_type?(obj, allow_typeof: false, free_vars: free_vars, raise: raise_on_missing_const, raise_typeof: false) + end + return false unless macro_scope.is_a?(Type) macro_scope = macro_scope.remove_alias diff --git a/src/compiler/crystal/semantic/type_lookup.cr b/src/compiler/crystal/semantic/type_lookup.cr index ba538ed0323d..493b2c56b5a2 100644 --- a/src/compiler/crystal/semantic/type_lookup.cr +++ b/src/compiler/crystal/semantic/type_lookup.cr @@ -46,8 +46,8 @@ class Crystal::Type end # Similar to `lookup_type`, but returns `nil` if a type can't be found. - def lookup_type?(node : ASTNode, self_type = self.instance_type, allow_typeof = true, free_vars : Hash(String, TypeVar)? = nil, find_root_generic_type_parameters = true) : Type? - TypeLookup.new(self, self_type, false, allow_typeof, free_vars, find_root_generic_type_parameters).lookup(node) + def lookup_type?(node : ASTNode, self_type = self.instance_type, allow_typeof = true, free_vars : Hash(String, TypeVar)? = nil, raise = false, raise_typeof = raise, find_root_generic_type_parameters = true) : Type? + TypeLookup.new(self, self_type, raise, allow_typeof, free_vars, find_root_generic_type_parameters, raise_typeof: raise_typeof).lookup(node) end # Similar to `lookup_type`, but the result might also be an ASTNode, for example when @@ -62,7 +62,7 @@ class Crystal::Type end private struct TypeLookup - def initialize(@root : Type, @self_type : Type, @raise : Bool, @allow_typeof : Bool, @free_vars : Hash(String, TypeVar)? = nil, @find_root_generic_type_parameters = true, @remove_alias = true) + def initialize(@root : Type, @self_type : Type, @raise : Bool, @allow_typeof : Bool, @free_vars : Hash(String, TypeVar)? = nil, @find_root_generic_type_parameters = true, @remove_alias = true, @raise_typeof : Bool = raise) @in_generic_args = 0 # If we are looking types inside a non-instantiated generic type, @@ -274,7 +274,7 @@ class Crystal::Type end type = in_generic_args { lookup(type_var) } - return if !@raise && !type + return if (!@raise || !@raise_typeof) && !type type = type.not_nil! case instance_type @@ -369,7 +369,7 @@ class Crystal::Type def lookup(node : TypeOf) unless @allow_typeof - if @raise + if @raise_typeof node.raise "can't use 'typeof' here" else return