diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index f18f9edb7aa5..295a2548bab4 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -1694,6 +1694,18 @@ module Crystal end end + describe "#warning" do + it "emits a warning at a specific node" do + assert_warning <<-CRYSTAL, "Oh noes" + macro test(node) + {% node.warning "Oh noes" %} + end + + test 10 + CRYSTAL + end + end + it "executes instance_vars" do assert_macro("{{x.instance_vars.map &.stringify}}", %(["bytesize", "length", "c"])) do |program| {x: TypeNode.new(program.string)} @@ -3066,6 +3078,18 @@ module Crystal assert_macro %({{compare_versions("1.10.3", "1.2.3")}}), %(1) end + describe "#warning" do + it "emits a top level warning" do + assert_warning <<-CRYSTAL, "Oh noes" + macro test + {% warning "Oh noes" %} + end + + test + CRYSTAL + end + end + describe "#parse_type" do it "path" do assert_type(%[class Bar; end; {{ parse_type("Bar").is_a?(Path) ? 1 : 'a'}}]) { int32 } diff --git a/src/compiler/crystal/macros.cr b/src/compiler/crystal/macros.cr index 8b5c6845c0b2..5f4268d36d09 100644 --- a/src/compiler/crystal/macros.cr +++ b/src/compiler/crystal/macros.cr @@ -228,6 +228,10 @@ module Crystal::Macros def raise(message) : NoReturn end + # Emits a compile-time warning with the given *message*. + def warning(message : StringLiteral) : NilLiteral + end + # Returns `true` if the given *filename* exists, `false` otherwise. def file_exists?(filename) : BoolLiteral end @@ -413,11 +417,16 @@ module Crystal::Macros def !=(other : ASTNode) : BoolLiteral end - # Gives a compile-time error with the given *message*. This will - # highlight this node in the error message. + # Gives a compile-time error with the given *message*. + # This will highlight this node in the error message. def raise(message) : NoReturn end + # Emits a compile-time warning with the given *message*. + # This will highlight this node in the warning message. + def warning(message : StringLiteral) : NilLiteral + end + # Returns `true` if this node's type is the given *type* or any of its # subclasses. # diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index 6894d3d60c15..00557567861f 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -69,6 +69,8 @@ module Crystal interpret_system(node) when "raise" interpret_raise(node) + when "warning" + interpret_warning(node) when "file_exists?" interpret_file_exists?(node) when "read_file" @@ -255,6 +257,10 @@ module Crystal macro_raise(node, node.args, self) end + def interpret_warning(node) + macro_warning(node, node.args, self) + end + def interpret_file_exists?(node) interpret_check_args_toplevel do |arg| arg.accept self @@ -382,6 +388,8 @@ module Crystal interpret_check_args { class_name } when "raise" macro_raise self, args, interpreter + when "warning" + macro_warning self, args, interpreter when "filename" interpret_check_args do filename = location.try &.original_filename @@ -2678,6 +2686,18 @@ private def macro_raise(node, args, interpreter) node.raise msg, exception_type: Crystal::MacroRaiseException end +private def macro_warning(node, args, interpreter) + msg = args.map do |arg| + arg.accept interpreter + interpreter.last.to_macro_id + end + msg = msg.join " " + + interpreter.warnings.add_warning_at(node.location, msg) + + Crystal::NilLiteral.new +end + private def empty_no_return_array Crystal::ArrayLiteral.new(of: Crystal::Path.global("NoReturn")) end