From 70f162dcd11bc934bf98edf8df87c11d877ff6d2 Mon Sep 17 00:00:00 2001 From: TSUYUSATO Kitsune Date: Fri, 17 Nov 2017 20:55:02 +0900 Subject: [PATCH] Don't use `{{yield}}` outside a macro --- spec/compiler/macro/macro_expander_spec.cr | 4 ++++ src/compiler/crystal/macros/interpreter.cr | 11 ++++++++--- src/compiler/crystal/macros/macros.cr | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/spec/compiler/macro/macro_expander_spec.cr b/spec/compiler/macro/macro_expander_spec.cr index f2d610b10a3e..a637f79c0177 100644 --- a/spec/compiler/macro/macro_expander_spec.cr +++ b/spec/compiler/macro/macro_expander_spec.cr @@ -152,4 +152,8 @@ describe "MacroExpander" do it "does not expand when macro expression is {% ... %}" do assert_macro "", %({% 1 %}), [] of ASTNode, "" end + + it "can't use `yield` outside a macro" do + assert_error %({{yield}}), "can't use `{{yield}}` outside a macro" + end end diff --git a/src/compiler/crystal/macros/interpreter.cr b/src/compiler/crystal/macros/interpreter.cr index cc1486e23d8f..718833052cc0 100644 --- a/src/compiler/crystal/macros/interpreter.cr +++ b/src/compiler/crystal/macros/interpreter.cr @@ -3,7 +3,7 @@ module Crystal getter last : ASTNode property free_vars : Hash(String, TypeVar)? - def self.new(program, scope : Type, path_lookup : Type, a_macro : Macro, call, a_def : Def? = nil) + def self.new(program, scope : Type, path_lookup : Type, a_macro : Macro, call, a_def : Def? = nil, in_macro = false) vars = {} of String => ASTNode splat_index = a_macro.splat_index double_splat = a_macro.double_splat @@ -68,14 +68,15 @@ module Crystal vars[macro_block_arg.name] = call_block || Nop.new end - new(program, scope, path_lookup, a_macro.location, vars, call.block, a_def) + new(program, scope, path_lookup, a_macro.location, vars, call.block, a_def, in_macro) end record MacroVarKey, name : String, exps : Array(ASTNode)? def initialize(@program : Program, @scope : Type, @path_lookup : Type, @location : Location?, - @vars = {} of String => ASTNode, @block : Block? = nil, @def : Def? = nil) + @vars = {} of String => ASTNode, @block : Block? = nil, @def : Def? = nil, + @in_macro = false) @str = IO::Memory.new(512) # Can't be String::Builder because of `{{debug()}} @last = Nop.new end @@ -362,6 +363,10 @@ module Crystal end def visit(node : Yield) + unless @in_macro + node.raise "can't use `{{yield}}` outside a macro" + end + if block = @block if node.exps.empty? @last = block.body.clone diff --git a/src/compiler/crystal/macros/macros.cr b/src/compiler/crystal/macros/macros.cr index 32990905081a..ee4c55f68b10 100644 --- a/src/compiler/crystal/macros/macros.cr +++ b/src/compiler/crystal/macros/macros.cr @@ -37,13 +37,13 @@ class Crystal::Program end def expand_macro(a_macro : Macro, call : Call, scope : Type, path_lookup : Type? = nil, a_def : Def? = nil) - interpreter = MacroInterpreter.new self, scope, path_lookup || scope, a_macro, call, a_def + interpreter = MacroInterpreter.new self, scope, path_lookup || scope, a_macro, call, a_def, in_macro: true a_macro.body.accept interpreter interpreter.to_s end def expand_macro(node : ASTNode, scope : Type, path_lookup : Type? = nil, free_vars = nil, a_def : Def? = nil) - interpreter = MacroInterpreter.new self, scope, path_lookup || scope, node.location, def: a_def + interpreter = MacroInterpreter.new self, scope, path_lookup || scope, node.location, def: a_def, in_macro: false interpreter.free_vars = free_vars node.accept interpreter interpreter.to_s