require "../../spec_helper" describe "Semantic: doc" do it "stores doc for class" do result = semantic <<-CRYSTAL, wants_doc: true # Hello class Foo end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") foo.locations.not_nil!.size.should eq(1) end it "stores doc for abstract class" do result = semantic <<-CRYSTAL, wants_doc: true # Hello abstract class Foo end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") end it "stores doc for struct" do result = semantic <<-CRYSTAL, wants_doc: true # Hello struct Foo end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") foo.locations.not_nil!.size.should eq(1) end it "stores doc for module" do result = semantic <<-CRYSTAL, wants_doc: true # Hello module Foo end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") foo.locations.not_nil!.size.should eq(1) end it "stores doc for def" do result = semantic <<-CRYSTAL, wants_doc: true class Foo # Hello def bar end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar").first bar.doc.should eq("Hello") end describe ":ditto:" do it "stores doc for const" do result = semantic <<-CRYSTAL, wants_doc: true # A number ONE = 1 # :ditto: TWO = 2 CRYSTAL program = result.program program.types["ONE"].doc.should eq "A number" program.types["TWO"].doc.should eq "A number" end it "stores doc for def" do result = semantic <<-CRYSTAL, wants_doc: true class Foo # Hello def bar end # :ditto: def bar2 end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar2").first bar.doc.should eq("Hello") end it "stores doc for macro" do result = semantic <<-CRYSTAL, wants_doc: true # Hello macro bar end # :ditto: macro bar2 end CRYSTAL program = result.program bar2 = program.lookup_macros("bar2").as(Array(Macro)).first bar2.doc.should eq("Hello") end it "amend previous doc" do result = semantic <<-CRYSTAL, wants_doc: true class Foo # Hello def bar end # :ditto: # # World def bar2 end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar2").first bar.doc.should eq("Hello\n\nWorld") end it "amend previous doc (without empty line)" do result = semantic <<-CRYSTAL, wants_doc: true class Foo # Hello def bar end # :ditto: # World def bar2 end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar2").first bar.doc.should eq("Hello\n\nWorld") end it ":ditto: references last non-ditto doc" do result = semantic <<-CRYSTAL, wants_doc: true class Foo # Hello def bar end # :ditto: # # World def bar2 end # :ditto: # # Crystal def bar3 end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar3").first bar.doc.should eq("Hello\n\nCrystal") end end it "stores doc for def with visibility" do result = semantic <<-CRYSTAL, wants_doc: true class Foo # Hello private def bar end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar").first bar.doc.should eq("Hello") end it "stores doc for def with annotation" do result = semantic <<-CRYSTAL, wants_doc: true class Foo # Hello @[AlwaysInline] def bar end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar").first bar.doc.should eq("Hello") end it "stores doc for def with annotation" do result = semantic <<-CRYSTAL, wants_doc: true # Hello @[AlwaysInline] fun bar : Int32 1 end CRYSTAL program = result.program bar = program.lookup_defs("bar").first bar.doc.should eq("Hello") end it "stores doc for abstract def" do result = semantic <<-CRYSTAL, wants_doc: true abstract class Foo # Hello abstract def bar end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar").first bar.doc.should eq("Hello") end {% for def_type in %w[def macro].map &.id %} it "overwrites doc for {{def_type}} when redefining" do result = semantic <<-CRYSTAL, wants_doc: true module Foo # Doc 1 {{def_type}} bar end end module Foo # Doc 2 {{def_type}} bar end end module Foo {{def_type}} bar end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_{{def_type}}s("bar").as(Array).first bar.doc.should eq("Doc 2") end {% end %} it "stores doc for macro" do result = semantic <<-CRYSTAL, wants_doc: true class Foo # Hello macro bar end end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.metaclass.lookup_macros("bar").as(Array(Macro)).first bar.doc.should eq("Hello") end it "stores doc for fun def" do result = semantic <<-CRYSTAL, wants_doc: true # Hello fun foo : Int32 1 end CRYSTAL program = result.program foo = program.lookup_defs("foo").first foo.doc.should eq("Hello") end it "stores doc for enum" do result = semantic <<-CRYSTAL, wants_doc: true # Hello enum Foo A end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") foo.locations.not_nil!.size.should eq(1) end it "stores doc for flags enum with base type" do result = semantic <<-CRYSTAL, wants_doc: true # Hello @[Flags] enum Foo : UInt8 A end CRYSTAL program = result.program ann = program.types["Flags"].as(Crystal::AnnotationType) foo = program.types["Foo"] foo.annotation(ann).should_not be_nil foo.doc.should eq("Hello") foo.locations.not_nil!.size.should eq(1) end it "stores doc for enum and doesn't mix with value" do result = semantic <<-CRYSTAL, wants_doc: true # Hello enum Foo # World World end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") foo.locations.not_nil!.size.should eq(1) end it "stores doc for enum with @[Flags]" do result = semantic <<-CRYSTAL, wants_doc: true # Hello @[Flags] enum Foo A end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") end it "stores doc for enum member" do result = semantic <<-CRYSTAL, wants_doc: true enum Foo # Hello A = 1 end CRYSTAL program = result.program foo = program.types["Foo"] a = foo.types["A"] a.doc.should eq("Hello") a.locations.not_nil!.size.should eq(1) end it "stores location for implicit flag enum members" do result = semantic <<-CRYSTAL, wants_doc: true @[Flags] enum Foo A = 1 B = 2 end CRYSTAL program = result.program foo = program.types["Foo"] a_loc = foo.types["All"].locations.should_not be_nil a_loc.should_not be_empty b_loc = foo.types["None"].locations.should_not be_nil b_loc.should_not be_empty end it "stores doc for constant" do result = semantic <<-CRYSTAL, wants_doc: true # Hello CONST = 1 CRYSTAL program = result.program a = program.types["CONST"] a.doc.should eq("Hello") a.locations.not_nil!.size.should eq(1) end it "stores doc for alias" do result = semantic <<-CRYSTAL, wants_doc: true # Hello alias Alias = Int32 CRYSTAL program = result.program a = program.types["Alias"] a.doc.should eq("Hello") a.locations.not_nil!.size.should eq(1) end it "stores doc for nodes defined in macro call" do result = semantic <<-CRYSTAL, wants_doc: true class Object macro property(name) def {{name}}=(@{{name}}) end def {{name}} @{{name}} end end end class Foo # Hello property bar end CRYSTAL program = result.program foo = program.types["Foo"] bar = foo.lookup_defs("bar").first bar.doc.should eq("Hello") bar_assign = foo.lookup_defs("bar=").first bar_assign.doc.should eq("Hello") end it "stores doc for nodes defined in macro call (2)" do result = semantic <<-CRYSTAL, wants_doc: true macro foo class Foo end end # Hello foo CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") end it "stores doc for macro defined in macro call" do result = semantic <<-CRYSTAL, wants_doc: true macro def_foo macro foo end end # Hello def_foo CRYSTAL program = result.program foo = program.macros.not_nil!["foo"].first foo.doc.should eq("Hello") end {% for module_type in %w[class struct module enum].map &.id %} it "stores doc for {{module_type}} when reopening" do result = semantic <<-CRYSTAL, wants_doc: true {{module_type}} Foo A = 1 end # Hello {{module_type}} Foo end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Hello") foo.locations.not_nil!.size.should eq(2) end it "overwrites doc for {{module_type}} when reopening" do result = semantic <<-CRYSTAL, wants_doc: true # Doc 1 {{module_type}} Foo A = 1 end # Doc 2 {{module_type}} Foo end {{module_type}} Foo end CRYSTAL program = result.program foo = program.types["Foo"] foo.doc.should eq("Doc 2") end {% end %} it "stores locations for auto-generated module" do result = semantic <<-CRYSTAL, wants_doc: true class Foo::Bar end CRYSTAL program = result.program foo = program.types["Foo"] foo.locations.not_nil!.size.should eq(1) end it "attaches doc in double macro expansion (#8463)" do result = semantic <<-CRYSTAL, wants_doc: true macro cls(nr) class MyClass{{nr}} end end macro cls2(nr) cls({{nr}}) end # Some description cls2(1) CRYSTAL program = result.program type = program.types["MyClass1"] type.doc.should eq("Some description") end it "attaches doc to annotation in macro expansion (#9628)" do result = semantic <<-CRYSTAL, wants_doc: true macro ann annotation MyAnnotation end end # Some description ann CRYSTAL program = result.program type = program.types["MyAnnotation"] type.doc.should eq("Some description") end context "doc before annotation" do it "attached to struct/class" do result = semantic <<-CRYSTAL, wants_doc: true # Some description @[Packed] struct Foo end CRYSTAL program = result.program type = program.types["Foo"] type.doc.should eq("Some description") end it "attached to module" do result = semantic <<-CRYSTAL, wants_doc: true annotation Ann end # Some description @[Ann] module Foo end CRYSTAL program = result.program type = program.types["Foo"] type.doc.should eq("Some description") end it "attached to enum" do result = semantic <<-CRYSTAL, wants_doc: true annotation Ann end # Some description @[Ann] enum Foo One end CRYSTAL program = result.program type = program.types["Foo"] type.doc.should eq("Some description") end it "attached to constant" do result = semantic <<-CRYSTAL, wants_doc: true annotation Ann end # Some description @[Ann] Foo = 1 CRYSTAL program = result.program type = program.types["Foo"] type.doc.should eq("Some description") end it "attached to alias" do result = semantic <<-CRYSTAL, wants_doc: true annotation Ann end # Some description @[Ann] alias Foo = Int32 CRYSTAL program = result.program type = program.types["Foo"] type.doc.should eq("Some description") end it "attached to def" do result = semantic <<-CRYSTAL, wants_doc: true annotation Ann end # Some description @[Ann] def foo end CRYSTAL program = result.program a_def = program.lookup_defs("foo").first a_def.doc.should eq("Some description") end it "attached to macro" do result = semantic <<-CRYSTAL, wants_doc: true annotation Ann end # Some description @[Ann] macro foo end CRYSTAL program = result.program type = program.lookup_macros("foo").as(Array(Macro)).first type.doc.should eq("Some description") end it "attached to macro call" do result = semantic <<-CRYSTAL, wants_doc: true annotation Ann end macro gen_type class Foo; end end # Some description @[Ann] gen_type CRYSTAL program = result.program type = program.types["Foo"] type.doc.should eq("Some description") end it "attached to macro call that produces multiple types" do result = semantic <<-CRYSTAL, wants_doc: true annotation Ann end class Foo macro getter(decl) @{{decl.var.id}} : {{decl.type.id}} def {{decl.var.id}} : {{decl.type.id}} @{{decl.var.id}} end end # Some description @[Ann] getter name : String? end CRYSTAL program = result.program a_def = program.types["Foo"].lookup_defs("name").first a_def.doc.should eq("Some description") end end it "expands record macro with comments (#16074)" do result = semantic <<-CRYSTAL, wants_doc: true require "macros" require "object/properties" record Foo, # This is a multiline # comment name : String?, # This is a single line comment val = 1 Foo.new("test").name Foo.new("test").val CRYSTAL foo = result.program.types["Foo"] foo.lookup_defs("val").first.doc.should eq("This is a single line comment") foo.lookup_defs("name").first.doc.should eq("This is a multiline\ncomment") end end