diff --git a/Gemfile.lock b/Gemfile.lock index 52f66b9b8..9be1bfbd4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,6 +3,7 @@ PATH specs: rbs (4.0.0.dev) logger + prism (>= 1.3.0) PATH remote: test/assets/test-gem @@ -39,7 +40,7 @@ GEM diff-lcs (1.6.0) digest (3.2.0) drb (2.2.1) - ffi (1.17.1) + ffi (1.17.1-arm64-darwin) fileutils (1.7.3) goodcheck (3.1.0) marcel (>= 1.0, < 2.0) @@ -60,7 +61,6 @@ GEM logger (1.6.6) marcel (1.0.4) memory_profiler (1.1.0) - mini_portile2 (2.8.8) minitest (5.25.5) mutex_m (0.3.0) net-protocol (0.2.2) @@ -68,8 +68,7 @@ GEM net-smtp (0.5.1) net-protocol nkf (0.2.0) - nokogiri (1.18.4) - mini_portile2 (~> 2.8.2) + nokogiri (1.18.4-arm64-darwin) racc (~> 1.4) ostruct (0.6.1) parallel (1.26.3) @@ -78,6 +77,7 @@ GEM racc pathname (0.4.0) power_assert (2.0.5) + prism (1.3.0) pstore (0.2.0) psych (4.0.6) stringio @@ -122,9 +122,10 @@ GEM unicode-display_width (>= 2.4.0, < 4.0) rubocop-ast (1.38.1) parser (>= 3.3.1.0) - rubocop-on-rbs (1.4.2) + rubocop-on-rbs (1.5.0) + lint_roller (~> 1.1) rbs (~> 3.5) - rubocop (~> 1.61) + rubocop (>= 1.72.1, < 2.0) zlib rubocop-rubycw (0.2.2) lint_roller (~> 1.1) @@ -169,7 +170,7 @@ GEM zlib (3.2.1) PLATFORMS - ruby + arm64-darwin-24 DEPENDENCIES abbrev diff --git a/Steepfile b/Steepfile index de1a0b4e9..60689d543 100644 --- a/Steepfile +++ b/Steepfile @@ -9,6 +9,7 @@ target :lib do ) library "pathname", "json", "logger", "monitor", "tsort", "uri", 'dbm', 'pstore', 'singleton', 'shellwords', 'fileutils', 'find', 'digest', 'prettyprint', 'yaml', "psych", "securerandom" + library "prism" signature "stdlib/strscan/0/" signature "stdlib/optparse/0/" signature "stdlib/rdoc/0/" diff --git a/config.yml b/config.yml index b17f864a7..417bcaeff 100644 --- a/config.yml +++ b/config.yml @@ -315,3 +315,156 @@ nodes: fields: - name: name - name: location + - name: RBS::AST::Ruby::Annotation::SkipAnnotation + fields: + - name: location + - name: comment + - name: prefix_location + - name: skip_location + - name: RBS::AST::Ruby::Annotation::ColonMethodTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: annotations + - name: method_type + - name: RBS::AST::Ruby::Annotation::MethodTypesAnnotation + fields: + - name: location + - name: prefix_location + - name: overloads + - name: vertical_bar_locations + - name: RBS::AST::Ruby::Annotation::MethodTypesAnnotation::Overload + fields: + - name: annotations + - name: method_type + - name: RBS::AST::Ruby::Annotation::ReturnTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: return_location + - name: colon_location + - name: return_type + - name: comment + - name: RBS::AST::Ruby::Annotation::ParamTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: param_name_location + - name: colon_location + - name: type + - name: comment + - name: RBS::AST::Ruby::Annotation::SplatParamTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: operator_location + - name: param_name_location + - name: colon_location + - name: type + - name: comment + - name: RBS::AST::Ruby::Annotation::DoubleSplatParamTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: operator_location + - name: param_name_location + - name: colon_location + - name: type + - name: comment + - name: RBS::AST::Ruby::Annotation::BlockParamTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: operator_location + - name: param_name_location + - name: colon_location + - name: question_mark_location + - name: block + - name: comment + - name: RBS::AST::Ruby::Annotation::OverrideAnnotation + fields: + - name: location + - name: prefix_location + - name: override_location + - name: RBS::AST::Ruby::Annotation::GenericAnnotation + fields: + - name: location + - name: prefix_location + - name: generic_location + - name: unchecked_location + - name: variance_location + - name: name_location + - name: upper_bound_operator_location + - name: upper_bound + - name: default_type_operator_location + - name: default_type + - name: comment + - name: RBS::AST::Ruby::Annotation::RBSAnnotationAnnotation + fields: + - name: location + - name: prefix_location + - name: annotations + - name: RBS::AST::Ruby::Annotation::NodeTypeAssertion + fields: + - name: location + - name: prefix_location + - name: type + - name: RBS::AST::Ruby::Annotation::NodeApplication + fields: + - name: location + - name: prefix_location + - name: types + - name: suffix_location + - name: RBS::AST::Ruby::Annotation::ModuleSelfAnnotation + fields: + - name: location + - name: prefix_location + - name: module_self_location + - name: type_name + - name: type_name_location + - name: open_paren_location + - name: type_args + - name: close_paren_location + - name: comment + - name: RBS::AST::Ruby::Annotation::InheritsAnnotation + fields: + - name: location + - name: prefix_location + - name: inherits_location + - name: type_name + - name: type_name_location + - name: open_paren_location + - name: type_args + - name: close_paren_location + - name: comment + - name: RBS::AST::Ruby::Annotation::IvarTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: var_name_location + - name: colon_location + - name: type + - name: comment + - name: RBS::AST::Ruby::Annotation::ClassVarTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: var_name_location + - name: colon_location + - name: type + - name: comment + - name: RBS::AST::Ruby::Annotation::ClassIvarTypeAnnotation + fields: + - name: location + - name: prefix_location + - name: self_location + - name: dot_location + - name: var_name_location + - name: colon_location + - name: type + - name: comment + - name: RBS::AST::Ruby::Annotation::EmbeddedRBSAnnotation + fields: + - name: location + - name: prefix_location + - name: members diff --git a/ext/rbs_extension/lexer.c b/ext/rbs_extension/lexer.c index 73984cf84..e3f96c459 100644 --- a/ext/rbs_extension/lexer.c +++ b/ext/rbs_extension/lexer.c @@ -88,40 +88,40 @@ token rbsparser_next_token(lexstate *state) { case 'd': goto yy49; case 'e': goto yy50; case 'f': goto yy51; - case 'g': + case 'g': goto yy52; case 'h': case 'j': case 'k': case 'l': case 'q': - case 'r': case 'w': case 'x': case 'y': - case 'z': goto yy52; - case 'i': goto yy54; - case 'm': goto yy55; - case 'n': goto yy56; - case 'o': goto yy57; - case 'p': goto yy58; - case 's': goto yy59; - case 't': goto yy60; - case 'u': goto yy61; - case 'v': goto yy62; - case '{': goto yy63; - case '|': goto yy64; - case '}': goto yy65; + case 'z': goto yy53; + case 'i': goto yy55; + case 'm': goto yy56; + case 'n': goto yy57; + case 'o': goto yy58; + case 'p': goto yy59; + case 'r': goto yy60; + case 's': goto yy61; + case 't': goto yy62; + case 'u': goto yy63; + case 'v': goto yy64; + case '{': goto yy65; + case '|': goto yy66; + case '}': goto yy67; default: goto yy2; } yy1: rbs_skip(state); -#line 144 "ext/rbs_extension/lexer.re" +#line 154 "ext/rbs_extension/lexer.re" { return next_eof_token(state); } #line 121 "ext/rbs_extension/lexer.c" yy2: rbs_skip(state); yy3: -#line 145 "ext/rbs_extension/lexer.re" +#line 155 "ext/rbs_extension/lexer.re" { return next_token(state, ErrorToken); } #line 127 "ext/rbs_extension/lexer.c" yy4: @@ -130,7 +130,7 @@ token rbsparser_next_token(lexstate *state) { if (yych == '\t') goto yy4; if (yych == ' ') goto yy4; yy5: -#line 143 "ext/rbs_extension/lexer.re" +#line 153 "ext/rbs_extension/lexer.re" { return next_token(state, tTRIVIA); } #line 136 "ext/rbs_extension/lexer.c" yy6: @@ -142,7 +142,7 @@ token rbsparser_next_token(lexstate *state) { if (yych == '=') goto yy24; if (yych == '~') goto yy24; yy8: -#line 48 "ext/rbs_extension/lexer.re" +#line 49 "ext/rbs_extension/lexer.re" { return next_token(state, tOPERATOR); } #line 148 "ext/rbs_extension/lexer.c" yy9: @@ -151,14 +151,14 @@ token rbsparser_next_token(lexstate *state) { backup = *state; yych = peek(state); if (yych <= 0x00000000) goto yy3; - goto yy67; + goto yy69; yy10: rbs_skip(state); yych = peek(state); if (yych <= 0x00000000) goto yy11; if (yych != '\n') goto yy10; yy11: -#line 59 "ext/rbs_extension/lexer.re" +#line 60 "ext/rbs_extension/lexer.re" { return next_token( state, @@ -173,42 +173,42 @@ token rbsparser_next_token(lexstate *state) { if (yych <= 0x0000001F) { if (yych <= '\n') { if (yych <= 0x00000000) goto yy3; - if (yych <= 0x00000008) goto yy71; + if (yych <= 0x00000008) goto yy73; goto yy3; } else { if (yych == '\r') goto yy3; - goto yy71; + goto yy73; } } else { if (yych <= '#') { if (yych <= ' ') goto yy3; - if (yych <= '"') goto yy73; - goto yy71; + if (yych <= '"') goto yy75; + goto yy73; } else { if (yych == '%') goto yy3; - if (yych <= '\'') goto yy73; + if (yych <= '\'') goto yy75; goto yy3; } } } else { if (yych <= 'Z') { if (yych <= '/') { - if (yych == '-') goto yy71; - goto yy73; + if (yych == '-') goto yy73; + goto yy75; } else { - if (yych <= '9') goto yy71; - if (yych <= '>') goto yy73; - goto yy71; + if (yych <= '9') goto yy73; + if (yych <= '>') goto yy75; + goto yy73; } } else { if (yych <= '^') { - if (yych == '\\') goto yy73; + if (yych == '\\') goto yy75; goto yy3; } else { - if (yych <= 'z') goto yy71; + if (yych <= 'z') goto yy73; if (yych <= '}') goto yy3; - if (yych <= '~') goto yy73; - goto yy71; + if (yych <= '~') goto yy75; + goto yy73; } } } @@ -217,7 +217,7 @@ token rbsparser_next_token(lexstate *state) { rbs_skip(state); backup = *state; yych = peek(state); - if (yych == 'a') goto yy74; + if (yych == 'a') goto yy76; goto yy8; yy14: rbs_skip(state); @@ -230,7 +230,7 @@ token rbsparser_next_token(lexstate *state) { backup = *state; yych = peek(state); if (yych <= 0x00000000) goto yy3; - goto yy76; + goto yy78; yy16: rbs_skip(state); #line 24 "ext/rbs_extension/lexer.re" @@ -244,7 +244,7 @@ token rbsparser_next_token(lexstate *state) { yy18: rbs_skip(state); yych = peek(state); - if (yych == '*') goto yy80; + if (yych == '*') goto yy82; #line 35 "ext/rbs_extension/lexer.re" { return next_token(state, pSTAR); } #line 251 "ext/rbs_extension/lexer.c" @@ -263,25 +263,32 @@ token rbsparser_next_token(lexstate *state) { yy21: rbs_skip(state); yych = peek(state); - if (yych <= '=') { - if (yych <= '/') goto yy8; - if (yych <= '9') goto yy25; - goto yy8; - } else { - if (yych <= '>') goto yy81; - if (yych == '@') goto yy24; - goto yy8; + switch (yych) { + case '-': goto yy83; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': goto yy25; + case '>': goto yy84; + case '@': goto yy24; + default: goto yy8; } yy22: yyaccept = 2; rbs_skip(state); backup = *state; yych = peek(state); - if (yych == '.') goto yy82; + if (yych == '.') goto yy85; yy23: #line 37 "ext/rbs_extension/lexer.re" { return next_token(state, pDOT); } -#line 285 "ext/rbs_extension/lexer.c" +#line 292 "ext/rbs_extension/lexer.c" yy24: rbs_skip(state); goto yy8; @@ -292,34 +299,34 @@ token rbsparser_next_token(lexstate *state) { if (yych <= '9') goto yy25; if (yych == '_') goto yy25; yy26: -#line 51 "ext/rbs_extension/lexer.re" +#line 52 "ext/rbs_extension/lexer.re" { return next_token(state, tINTEGER); } -#line 298 "ext/rbs_extension/lexer.c" +#line 305 "ext/rbs_extension/lexer.c" yy27: yyaccept = 3; rbs_skip(state); backup = *state; yych = peek(state); switch (yych) { - case '!': goto yy83; - case '"': goto yy85; - case '$': goto yy86; + case '!': goto yy86; + case '"': goto yy88; + case '$': goto yy89; case '%': case '&': case '/': case '^': case '`': case '|': - case '~': goto yy87; - case '\'': goto yy88; - case '*': goto yy89; + case '~': goto yy90; + case '\'': goto yy91; + case '*': goto yy92; case '+': - case '-': goto yy90; - case ':': goto yy91; - case '<': goto yy92; - case '=': goto yy93; - case '>': goto yy94; - case '@': goto yy95; + case '-': goto yy93; + case ':': goto yy94; + case '<': goto yy95; + case '=': goto yy96; + case '>': goto yy97; + case '@': goto yy98; case 'A': case 'B': case 'C': @@ -372,38 +379,38 @@ token rbsparser_next_token(lexstate *state) { case 'w': case 'x': case 'y': - case 'z': goto yy96; - case '[': goto yy98; + case 'z': goto yy99; + case '[': goto yy101; default: goto yy28; } yy28: #line 44 "ext/rbs_extension/lexer.re" { return next_token(state, pCOLON); } -#line 383 "ext/rbs_extension/lexer.c" +#line 390 "ext/rbs_extension/lexer.c" yy29: rbs_skip(state); yych = peek(state); if (yych <= ';') goto yy30; if (yych <= '<') goto yy24; - if (yych <= '=') goto yy99; + if (yych <= '=') goto yy102; yy30: #line 46 "ext/rbs_extension/lexer.re" { return next_token(state, pLT); } -#line 393 "ext/rbs_extension/lexer.c" +#line 400 "ext/rbs_extension/lexer.c" yy31: rbs_skip(state); yych = peek(state); if (yych <= '>') { if (yych <= '<') goto yy32; - if (yych <= '=') goto yy100; - goto yy101; + if (yych <= '=') goto yy103; + goto yy104; } else { if (yych == '~') goto yy24; } yy32: #line 43 "ext/rbs_extension/lexer.re" { return next_token(state, pEQ); } -#line 407 "ext/rbs_extension/lexer.c" +#line 414 "ext/rbs_extension/lexer.c" yy33: rbs_skip(state); yych = peek(state); @@ -414,31 +421,40 @@ token rbsparser_next_token(lexstate *state) { rbs_skip(state); #line 34 "ext/rbs_extension/lexer.re" { return next_token(state, pQUESTION); } -#line 418 "ext/rbs_extension/lexer.c" +#line 425 "ext/rbs_extension/lexer.c" yy35: yyaccept = 0; rbs_skip(state); backup = *state; yych = peek(state); - if (yych <= '^') { - if (yych <= '?') goto yy3; - if (yych <= '@') goto yy102; - if (yych <= 'Z') goto yy103; - goto yy3; + if (yych <= '_') { + if (yych <= '@') { + if (yych <= '?') goto yy3; + goto yy105; + } else { + if (yych <= 'Z') goto yy106; + if (yych <= '^') goto yy3; + goto yy106; + } } else { - if (yych == '`') goto yy3; - if (yych <= 'z') goto yy103; - goto yy3; + if (yych <= 'q') { + if (yych <= '`') goto yy3; + goto yy106; + } else { + if (yych <= 'r') goto yy109; + if (yych <= 'z') goto yy106; + goto yy3; + } } yy36: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { if (yych <= '9') goto yy36; - if (yych >= '=') goto yy106; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { @@ -450,50 +466,50 @@ token rbsparser_next_token(lexstate *state) { } } yy37: -#line 129 "ext/rbs_extension/lexer.re" +#line 136 "ext/rbs_extension/lexer.re" { return next_token(state, tUIDENT); } -#line 456 "ext/rbs_extension/lexer.c" +#line 472 "ext/rbs_extension/lexer.c" yy38: rbs_skip(state); yych = peek(state); - if (yych == ']') goto yy107; + if (yych == ']') goto yy112; #line 26 "ext/rbs_extension/lexer.re" { return next_token(state, pLBRACKET); } -#line 463 "ext/rbs_extension/lexer.c" +#line 479 "ext/rbs_extension/lexer.c" yy39: rbs_skip(state); #line 27 "ext/rbs_extension/lexer.re" { return next_token(state, pRBRACKET); } -#line 468 "ext/rbs_extension/lexer.c" +#line 484 "ext/rbs_extension/lexer.c" yy40: rbs_skip(state); #line 32 "ext/rbs_extension/lexer.re" { return next_token(state, pHAT); } -#line 473 "ext/rbs_extension/lexer.c" +#line 489 "ext/rbs_extension/lexer.c" yy41: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy108; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy113; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { if (yych <= '@') goto yy42; - if (yych <= 'Z') goto yy111; + if (yych <= 'Z') goto yy116; } else { - if (yych <= '_') goto yy113; + if (yych <= '_') goto yy118; if (yych <= '`') goto yy42; - if (yych <= 'z') goto yy108; + if (yych <= 'z') goto yy113; } } yy42: -#line 132 "ext/rbs_extension/lexer.re" +#line 139 "ext/rbs_extension/lexer.re" { return next_token(state, tULLIDENT); } -#line 497 "ext/rbs_extension/lexer.c" +#line 513 "ext/rbs_extension/lexer.c" yy43: yyaccept = 4; rbs_skip(state); @@ -501,161 +517,178 @@ token rbsparser_next_token(lexstate *state) { yych = peek(state); if (yych <= ' ') { if (yych <= 0x00000000) goto yy44; - if (yych <= 0x0000001F) goto yy114; + if (yych <= 0x0000001F) goto yy119; } else { - if (yych != ':') goto yy114; + if (yych != ':') goto yy119; } yy44: #line 39 "ext/rbs_extension/lexer.re" { return next_token(state, tOPERATOR); } -#line 512 "ext/rbs_extension/lexer.c" +#line 528 "ext/rbs_extension/lexer.c" yy45: rbs_skip(state); yych = peek(state); if (yych <= 'r') { - if (yych == 'l') goto yy115; - goto yy53; + if (yych == 'l') goto yy120; + goto yy54; } else { - if (yych <= 's') goto yy116; - if (yych <= 't') goto yy118; - goto yy53; + if (yych <= 's') goto yy121; + if (yych <= 't') goto yy123; + goto yy54; } yy46: -#line 128 "ext/rbs_extension/lexer.re" +#line 135 "ext/rbs_extension/lexer.re" { return next_token(state, tLIDENT); } -#line 527 "ext/rbs_extension/lexer.c" +#line 543 "ext/rbs_extension/lexer.c" yy47: rbs_skip(state); yych = peek(state); - if (yych == 'o') goto yy119; - goto yy53; + if (yych == 'o') goto yy124; + goto yy54; yy48: rbs_skip(state); yych = peek(state); - if (yych == 'l') goto yy120; - goto yy53; + if (yych == 'l') goto yy125; + goto yy54; yy49: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy121; - goto yy53; + if (yych == 'e') goto yy126; + goto yy54; yy50: rbs_skip(state); yych = peek(state); - if (yych == 'n') goto yy122; - if (yych == 'x') goto yy123; - goto yy53; + if (yych == 'n') goto yy127; + if (yych == 'x') goto yy128; + goto yy54; yy51: rbs_skip(state); yych = peek(state); - if (yych == 'a') goto yy124; - goto yy53; + if (yych == 'a') goto yy129; + goto yy54; yy52: rbs_skip(state); yych = peek(state); + if (yych == 'e') goto yy130; + goto yy54; yy53: + rbs_skip(state); + yych = peek(state); +yy54: if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; goto yy46; } else { - if (yych <= '9') goto yy52; + if (yych <= '9') goto yy53; if (yych <= '<') goto yy46; - goto yy106; + goto yy111; } } else { if (yych <= '^') { if (yych <= '@') goto yy46; - if (yych <= 'Z') goto yy52; + if (yych <= 'Z') goto yy53; goto yy46; } else { if (yych == '`') goto yy46; - if (yych <= 'z') goto yy52; + if (yych <= 'z') goto yy53; goto yy46; } } -yy54: - rbs_skip(state); - yych = peek(state); - if (yych == 'n') goto yy125; - goto yy53; yy55: rbs_skip(state); yych = peek(state); - if (yych == 'o') goto yy127; - goto yy53; + if (yych == 'n') goto yy131; + goto yy54; yy56: rbs_skip(state); yych = peek(state); - if (yych == 'i') goto yy128; - goto yy53; + if (yych == 'o') goto yy133; + goto yy54; yy57: rbs_skip(state); yych = peek(state); - if (yych == 'u') goto yy129; - goto yy53; + if (yych == 'i') goto yy134; + goto yy54; yy58: rbs_skip(state); yych = peek(state); - if (yych == 'r') goto yy130; - if (yych == 'u') goto yy131; - goto yy53; + if (yych <= 't') goto yy54; + if (yych <= 'u') goto yy135; + if (yych <= 'v') goto yy136; + goto yy54; yy59: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy132; - if (yych == 'i') goto yy133; - goto yy53; + if (yych == 'r') goto yy137; + if (yych == 'u') goto yy138; + goto yy54; yy60: rbs_skip(state); yych = peek(state); - if (yych <= 'q') { - if (yych == 'o') goto yy134; - goto yy53; - } else { - if (yych <= 'r') goto yy135; - if (yych == 'y') goto yy136; - goto yy53; - } + if (yych == 'e') goto yy139; + goto yy54; yy61: rbs_skip(state); yych = peek(state); - if (yych == 'n') goto yy137; - if (yych == 's') goto yy138; - goto yy53; + if (yych <= 'h') { + if (yych == 'e') goto yy140; + goto yy54; + } else { + if (yych <= 'i') goto yy141; + if (yych == 'k') goto yy142; + goto yy54; + } yy62: rbs_skip(state); yych = peek(state); - if (yych == 'o') goto yy139; - goto yy53; + if (yych <= 'q') { + if (yych == 'o') goto yy143; + goto yy54; + } else { + if (yych <= 'r') goto yy144; + if (yych == 'y') goto yy145; + goto yy54; + } yy63: rbs_skip(state); + yych = peek(state); + if (yych == 'n') goto yy146; + if (yych == 's') goto yy147; + goto yy54; +yy64: + rbs_skip(state); + yych = peek(state); + if (yych == 'o') goto yy148; + goto yy54; +yy65: + rbs_skip(state); #line 28 "ext/rbs_extension/lexer.re" { return next_token(state, pLBRACE); } -#line 636 "ext/rbs_extension/lexer.c" -yy64: +#line 669 "ext/rbs_extension/lexer.c" +yy66: rbs_skip(state); #line 31 "ext/rbs_extension/lexer.re" { return next_token(state, pBAR); } -#line 641 "ext/rbs_extension/lexer.c" -yy65: +#line 674 "ext/rbs_extension/lexer.c" +yy67: rbs_skip(state); #line 29 "ext/rbs_extension/lexer.re" { return next_token(state, pRBRACE); } -#line 646 "ext/rbs_extension/lexer.c" -yy66: +#line 679 "ext/rbs_extension/lexer.c" +yy68: rbs_skip(state); yych = peek(state); -yy67: +yy69: if (yych <= '"') { - if (yych <= 0x00000000) goto yy68; - if (yych <= '!') goto yy66; - goto yy69; + if (yych <= 0x00000000) goto yy70; + if (yych <= '!') goto yy68; + goto yy71; } else { - if (yych == '\\') goto yy70; - goto yy66; + if (yych == '\\') goto yy72; + goto yy68; } -yy68: +yy70: *state = backup; if (yyaccept <= 3) { if (yyaccept <= 1) { @@ -676,2053 +709,2365 @@ token rbsparser_next_token(lexstate *state) { if (yyaccept == 4) { goto yy44; } else { - goto yy78; + goto yy80; } } else { - goto yy155; + if (yyaccept == 6) { + goto yy164; + } else { + goto yy303; + } } } -yy69: +yy71: rbs_skip(state); -#line 106 "ext/rbs_extension/lexer.re" +#line 113 "ext/rbs_extension/lexer.re" { return next_token(state, tDQSTRING); } -#line 690 "ext/rbs_extension/lexer.c" -yy70: +#line 727 "ext/rbs_extension/lexer.c" +yy72: rbs_skip(state); yych = peek(state); - if (yych == 'u') goto yy140; - if (yych == 'x') goto yy141; - goto yy66; -yy71: + if (yych == 'u') goto yy149; + if (yych == 'x') goto yy150; + goto yy68; +yy73: rbs_skip(state); yych = peek(state); if (yych <= ',') { if (yych <= '\f') { - if (yych <= 0x00000000) goto yy72; - if (yych <= 0x00000008) goto yy71; - if (yych >= '\v') goto yy71; + if (yych <= 0x00000000) goto yy74; + if (yych <= 0x00000008) goto yy73; + if (yych >= '\v') goto yy73; } else { if (yych <= 0x0000001F) { - if (yych >= 0x0000000E) goto yy71; + if (yych >= 0x0000000E) goto yy73; } else { - if (yych == '#') goto yy71; + if (yych == '#') goto yy73; } } } else { if (yych <= '>') { - if (yych <= '-') goto yy71; - if (yych <= '/') goto yy72; - if (yych <= '9') goto yy71; + if (yych <= '-') goto yy73; + if (yych <= '/') goto yy74; + if (yych <= '9') goto yy73; } else { if (yych <= '^') { - if (yych <= 'Z') goto yy71; + if (yych <= 'Z') goto yy73; } else { - if (yych <= 'z') goto yy71; - if (yych >= 0x0000007F) goto yy71; + if (yych <= 'z') goto yy73; + if (yych >= 0x0000007F) goto yy73; } } } -yy72: -#line 139 "ext/rbs_extension/lexer.re" +yy74: +#line 149 "ext/rbs_extension/lexer.re" { return next_token(state, tGIDENT); } -#line 729 "ext/rbs_extension/lexer.c" -yy73: +#line 766 "ext/rbs_extension/lexer.c" +yy75: rbs_skip(state); - goto yy72; -yy74: + goto yy74; +yy76: rbs_skip(state); yych = peek(state); if (yych <= 'Z') { if (yych <= '(') { - if (yych <= '\'') goto yy68; - goto yy142; + if (yych <= '\'') goto yy70; + goto yy151; } else { - if (yych == '<') goto yy143; - goto yy68; + if (yych == '<') goto yy152; + goto yy70; } } else { if (yych <= 'z') { - if (yych <= '[') goto yy144; - goto yy68; + if (yych <= '[') goto yy153; + goto yy70; } else { - if (yych <= '{') goto yy145; - if (yych <= '|') goto yy146; - goto yy68; + if (yych <= '{') goto yy154; + if (yych <= '|') goto yy155; + goto yy70; } } -yy75: +yy77: rbs_skip(state); yych = peek(state); -yy76: +yy78: if (yych <= '\'') { - if (yych <= 0x00000000) goto yy68; - if (yych <= '&') goto yy75; + if (yych <= 0x00000000) goto yy70; + if (yych <= '&') goto yy77; } else { - if (yych == '\\') goto yy79; - goto yy75; + if (yych == '\\') goto yy81; + goto yy77; } -yy77: +yy79: rbs_skip(state); -yy78: -#line 107 "ext/rbs_extension/lexer.re" +yy80: +#line 114 "ext/rbs_extension/lexer.re" { return next_token(state, tSQSTRING); } -#line 770 "ext/rbs_extension/lexer.c" -yy79: +#line 807 "ext/rbs_extension/lexer.c" +yy81: rbs_skip(state); yych = peek(state); if (yych <= '\'') { - if (yych <= 0x00000000) goto yy68; - if (yych <= '&') goto yy75; - goto yy147; + if (yych <= 0x00000000) goto yy70; + if (yych <= '&') goto yy77; + goto yy156; } else { - if (yych == '\\') goto yy79; - goto yy75; + if (yych == '\\') goto yy81; + goto yy77; } -yy80: +yy82: rbs_skip(state); #line 36 "ext/rbs_extension/lexer.re" { return next_token(state, pSTAR2); } -#line 786 "ext/rbs_extension/lexer.c" -yy81: +#line 823 "ext/rbs_extension/lexer.c" +yy83: + rbs_skip(state); + yych = peek(state); + if (yych >= 0x00000001) goto yy83; +#line 48 "ext/rbs_extension/lexer.re" + { return next_token(state, pMINUS2); } +#line 830 "ext/rbs_extension/lexer.c" +yy84: rbs_skip(state); #line 41 "ext/rbs_extension/lexer.re" { return next_token(state, pARROW); } -#line 791 "ext/rbs_extension/lexer.c" -yy82: +#line 835 "ext/rbs_extension/lexer.c" +yy85: rbs_skip(state); yych = peek(state); - if (yych == '.') goto yy148; - goto yy68; -yy83: + if (yych == '.') goto yy157; + goto yy70; +yy86: rbs_skip(state); yych = peek(state); - if (yych == '=') goto yy87; - if (yych == '~') goto yy87; -yy84: -#line 126 "ext/rbs_extension/lexer.re" + if (yych == '=') goto yy90; + if (yych == '~') goto yy90; +yy87: +#line 133 "ext/rbs_extension/lexer.re" { return next_token(state, tSYMBOL); } -#line 805 "ext/rbs_extension/lexer.c" -yy85: +#line 849 "ext/rbs_extension/lexer.c" +yy88: rbs_skip(state); yych = peek(state); if (yych <= '"') { - if (yych <= 0x00000000) goto yy68; - if (yych <= '!') goto yy85; - goto yy149; + if (yych <= 0x00000000) goto yy70; + if (yych <= '!') goto yy88; + goto yy158; } else { - if (yych == '\\') goto yy150; - goto yy85; + if (yych == '\\') goto yy159; + goto yy88; } -yy86: +yy89: rbs_skip(state); yych = peek(state); if (yych <= ')') { if (yych <= 0x0000001F) { if (yych <= '\n') { - if (yych <= 0x00000000) goto yy68; - if (yych <= 0x00000008) goto yy151; - goto yy68; + if (yych <= 0x00000000) goto yy70; + if (yych <= 0x00000008) goto yy160; + goto yy70; } else { - if (yych == '\r') goto yy68; - goto yy151; + if (yych == '\r') goto yy70; + goto yy160; } } else { if (yych <= '#') { - if (yych <= ' ') goto yy68; - if (yych <= '"') goto yy153; - goto yy151; + if (yych <= ' ') goto yy70; + if (yych <= '"') goto yy162; + goto yy160; } else { - if (yych == '%') goto yy68; - if (yych <= '\'') goto yy153; - goto yy68; + if (yych == '%') goto yy70; + if (yych <= '\'') goto yy162; + goto yy70; } } } else { if (yych <= 'Z') { if (yych <= '/') { - if (yych == '-') goto yy151; - goto yy153; + if (yych == '-') goto yy160; + goto yy162; } else { - if (yych <= '9') goto yy151; - if (yych <= '>') goto yy153; - goto yy151; + if (yych <= '9') goto yy160; + if (yych <= '>') goto yy162; + goto yy160; } } else { if (yych <= '^') { - if (yych == '\\') goto yy153; - goto yy68; + if (yych == '\\') goto yy162; + goto yy70; } else { - if (yych <= 'z') goto yy151; - if (yych <= '}') goto yy68; - if (yych <= '~') goto yy153; - goto yy151; + if (yych <= 'z') goto yy160; + if (yych <= '}') goto yy70; + if (yych <= '~') goto yy162; + goto yy160; } } } -yy87: +yy90: rbs_skip(state); - goto yy84; -yy88: + goto yy87; +yy91: rbs_skip(state); yych = peek(state); if (yych <= '\'') { - if (yych <= 0x00000000) goto yy68; - if (yych <= '&') goto yy88; - goto yy154; + if (yych <= 0x00000000) goto yy70; + if (yych <= '&') goto yy91; + goto yy163; } else { - if (yych == '\\') goto yy156; - goto yy88; + if (yych == '\\') goto yy165; + goto yy91; } -yy89: +yy92: rbs_skip(state); yych = peek(state); - if (yych == '*') goto yy87; - goto yy84; -yy90: + if (yych == '*') goto yy90; + goto yy87; +yy93: rbs_skip(state); yych = peek(state); - if (yych == '@') goto yy87; - goto yy84; -yy91: + if (yych == '@') goto yy90; + goto yy87; +yy94: rbs_skip(state); #line 45 "ext/rbs_extension/lexer.re" { return next_token(state, pCOLON2); } -#line 891 "ext/rbs_extension/lexer.c" -yy92: +#line 935 "ext/rbs_extension/lexer.c" +yy95: rbs_skip(state); yych = peek(state); - if (yych <= ';') goto yy84; - if (yych <= '<') goto yy87; - if (yych <= '=') goto yy157; - goto yy84; -yy93: + if (yych <= ';') goto yy87; + if (yych <= '<') goto yy90; + if (yych <= '=') goto yy166; + goto yy87; +yy96: rbs_skip(state); yych = peek(state); - if (yych == '=') goto yy158; - if (yych == '~') goto yy87; - goto yy68; -yy94: + if (yych == '=') goto yy167; + if (yych == '~') goto yy90; + goto yy70; +yy97: rbs_skip(state); yych = peek(state); - if (yych <= '<') goto yy84; - if (yych <= '>') goto yy87; - goto yy84; -yy95: + if (yych <= '<') goto yy87; + if (yych <= '>') goto yy90; + goto yy87; +yy98: rbs_skip(state); yych = peek(state); if (yych <= '^') { - if (yych <= '?') goto yy68; - if (yych <= '@') goto yy159; - if (yych <= 'Z') goto yy160; - goto yy68; + if (yych <= '?') goto yy70; + if (yych <= '@') goto yy168; + if (yych <= 'Z') goto yy169; + goto yy70; } else { - if (yych == '`') goto yy68; - if (yych <= 'z') goto yy160; - goto yy68; + if (yych == '`') goto yy70; + if (yych <= 'z') goto yy169; + goto yy70; } -yy96: +yy99: rbs_skip(state); yych = peek(state); if (yych <= '>') { if (yych <= '/') { - if (yych == '!') goto yy162; + if (yych == '!') goto yy171; } else { - if (yych <= '9') goto yy96; - if (yych == '=') goto yy162; + if (yych <= '9') goto yy99; + if (yych == '=') goto yy171; } } else { if (yych <= '^') { - if (yych <= '?') goto yy162; - if (yych <= '@') goto yy97; - if (yych <= 'Z') goto yy96; + if (yych <= '?') goto yy171; + if (yych <= '@') goto yy100; + if (yych <= 'Z') goto yy99; } else { - if (yych == '`') goto yy97; - if (yych <= 'z') goto yy96; + if (yych == '`') goto yy100; + if (yych <= 'z') goto yy99; } } -yy97: -#line 122 "ext/rbs_extension/lexer.re" +yy100: +#line 129 "ext/rbs_extension/lexer.re" { return next_token(state, tSYMBOL); } -#line 947 "ext/rbs_extension/lexer.c" -yy98: +#line 991 "ext/rbs_extension/lexer.c" +yy101: rbs_skip(state); yych = peek(state); - if (yych == ']') goto yy158; - goto yy68; -yy99: + if (yych == ']') goto yy167; + goto yy70; +yy102: rbs_skip(state); yych = peek(state); if (yych == '>') goto yy24; goto yy8; -yy100: +yy103: rbs_skip(state); yych = peek(state); if (yych == '=') goto yy24; goto yy8; -yy101: +yy104: rbs_skip(state); #line 42 "ext/rbs_extension/lexer.re" { return next_token(state, pFATARROW); } -#line 967 "ext/rbs_extension/lexer.c" -yy102: +#line 1011 "ext/rbs_extension/lexer.c" +yy105: rbs_skip(state); yych = peek(state); if (yych <= '^') { - if (yych <= '@') goto yy68; - if (yych <= 'Z') goto yy163; - goto yy68; + if (yych <= '@') goto yy70; + if (yych <= 'Z') goto yy172; + goto yy70; } else { - if (yych == '`') goto yy68; - if (yych <= 'z') goto yy163; - goto yy68; + if (yych == '`') goto yy70; + if (yych <= 'z') goto yy172; + goto yy70; } -yy103: +yy106: rbs_skip(state); yych = peek(state); +yy107: if (yych <= 'Z') { - if (yych <= '/') goto yy104; - if (yych <= '9') goto yy103; - if (yych >= 'A') goto yy103; + if (yych <= '/') goto yy108; + if (yych <= '9') goto yy106; + if (yych >= 'A') goto yy106; } else { if (yych <= '_') { - if (yych >= '_') goto yy103; + if (yych >= '_') goto yy106; } else { - if (yych <= '`') goto yy104; - if (yych <= 'z') goto yy103; + if (yych <= '`') goto yy108; + if (yych <= 'z') goto yy106; } } -yy104: -#line 136 "ext/rbs_extension/lexer.re" +yy108: +#line 146 "ext/rbs_extension/lexer.re" { return next_token(state, tAIDENT); } -#line 998 "ext/rbs_extension/lexer.c" -yy105: +#line 1043 "ext/rbs_extension/lexer.c" +yy109: rbs_skip(state); -#line 133 "ext/rbs_extension/lexer.re" + yych = peek(state); + if (yych == 'b') goto yy174; + goto yy107; +yy110: + rbs_skip(state); +#line 140 "ext/rbs_extension/lexer.re" { return next_token(state, tBANGIDENT); } -#line 1003 "ext/rbs_extension/lexer.c" -yy106: +#line 1053 "ext/rbs_extension/lexer.c" +yy111: rbs_skip(state); -#line 134 "ext/rbs_extension/lexer.re" +#line 141 "ext/rbs_extension/lexer.re" { return next_token(state, tEQIDENT); } -#line 1008 "ext/rbs_extension/lexer.c" -yy107: +#line 1058 "ext/rbs_extension/lexer.c" +yy112: rbs_skip(state); yych = peek(state); if (yych == '=') goto yy24; #line 47 "ext/rbs_extension/lexer.re" { return next_token(state, pAREF_OPR); } -#line 1015 "ext/rbs_extension/lexer.c" -yy108: +#line 1065 "ext/rbs_extension/lexer.c" +yy113: rbs_skip(state); yych = peek(state); -yy109: +yy114: if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy108; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy113; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy110; - if (yych <= 'Z') goto yy108; + if (yych <= '@') goto yy115; + if (yych <= 'Z') goto yy113; } else { - if (yych == '`') goto yy110; - if (yych <= 'z') goto yy108; + if (yych == '`') goto yy115; + if (yych <= 'z') goto yy113; } } -yy110: -#line 130 "ext/rbs_extension/lexer.re" +yy115: +#line 137 "ext/rbs_extension/lexer.re" { return next_token(state, tULLIDENT); } -#line 1039 "ext/rbs_extension/lexer.c" -yy111: +#line 1089 "ext/rbs_extension/lexer.c" +yy116: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy111; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy116; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy112; - if (yych <= 'Z') goto yy111; + if (yych <= '@') goto yy117; + if (yych <= 'Z') goto yy116; } else { - if (yych == '`') goto yy112; - if (yych <= 'z') goto yy111; + if (yych == '`') goto yy117; + if (yych <= 'z') goto yy116; } } -yy112: -#line 131 "ext/rbs_extension/lexer.re" +yy117: +#line 138 "ext/rbs_extension/lexer.re" { return next_token(state, tULIDENT); } -#line 1062 "ext/rbs_extension/lexer.c" -yy113: +#line 1112 "ext/rbs_extension/lexer.c" +yy118: rbs_skip(state); yych = peek(state); - if (yych == 't') goto yy165; - goto yy109; -yy114: + if (yych == 't') goto yy175; + goto yy114; +yy119: rbs_skip(state); yych = peek(state); - if (yych <= 0x00000000) goto yy68; - if (yych == '`') goto yy166; - goto yy114; -yy115: + if (yych <= 0x00000000) goto yy70; + if (yych == '`') goto yy176; + goto yy119; +yy120: rbs_skip(state); yych = peek(state); - if (yych == 'i') goto yy167; - goto yy53; -yy116: + if (yych == 'i') goto yy177; + goto yy54; +yy121: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy117; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy122; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy117; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy122; + if (yych <= 'z') goto yy53; } } -yy117: -#line 96 "ext/rbs_extension/lexer.re" +yy122: +#line 97 "ext/rbs_extension/lexer.re" { return next_token(state, kAS); } -#line 1101 "ext/rbs_extension/lexer.c" -yy118: +#line 1151 "ext/rbs_extension/lexer.c" +yy123: rbs_skip(state); yych = peek(state); - if (yych == 't') goto yy168; - goto yy53; -yy119: + if (yych == 't') goto yy178; + goto yy54; +yy124: rbs_skip(state); yych = peek(state); - if (yych == 'o') goto yy169; - if (yych == 't') goto yy170; - goto yy53; -yy120: + if (yych == 'o') goto yy179; + if (yych == 't') goto yy180; + goto yy54; +yy125: rbs_skip(state); yych = peek(state); - if (yych == 'a') goto yy172; - goto yy53; -yy121: + if (yych == 'a') goto yy182; + goto yy54; +yy126: rbs_skip(state); yych = peek(state); - if (yych == 'f') goto yy173; - goto yy53; -yy122: + if (yych == 'f') goto yy183; + goto yy54; +yy127: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy175; - goto yy53; -yy123: + if (yych == 'd') goto yy185; + goto yy54; +yy128: rbs_skip(state); yych = peek(state); - if (yych == 't') goto yy177; - goto yy53; -yy124: + if (yych == 't') goto yy187; + goto yy54; +yy129: rbs_skip(state); yych = peek(state); - if (yych == 'l') goto yy178; - goto yy53; -yy125: + if (yych == 'l') goto yy188; + goto yy54; +yy130: rbs_skip(state); yych = peek(state); - if (yych <= '^') { - if (yych <= '9') { - if (yych == '!') goto yy105; - if (yych >= '0') goto yy52; + if (yych == 'n') goto yy189; + goto yy54; +yy131: + rbs_skip(state); + yych = peek(state); + if (yych <= '_') { + if (yych <= '<') { + if (yych <= '!') { + if (yych >= '!') goto yy110; + } else { + if (yych <= '/') goto yy132; + if (yych <= '9') goto yy53; + } } else { - if (yych <= '=') { - if (yych >= '=') goto yy106; + if (yych <= '@') { + if (yych <= '=') goto yy111; } else { - if (yych <= '@') goto yy126; - if (yych <= 'Z') goto yy52; + if (yych <= 'Z') goto yy53; + if (yych >= '_') goto yy53; } } } else { - if (yych <= 'c') { - if (yych == '`') goto yy126; - if (yych <= 'b') goto yy52; - goto yy179; + if (yych <= 'h') { + if (yych <= 'b') { + if (yych >= 'a') goto yy53; + } else { + if (yych <= 'c') goto yy190; + if (yych <= 'g') goto yy53; + goto yy191; + } } else { if (yych <= 's') { - if (yych <= 'r') goto yy52; - goto yy180; + if (yych <= 'r') goto yy53; + goto yy192; } else { - if (yych <= 't') goto yy181; - if (yych <= 'z') goto yy52; + if (yych <= 't') goto yy193; + if (yych <= 'z') goto yy53; } } } -yy126: -#line 77 "ext/rbs_extension/lexer.re" - { return next_token(state, kIN); } -#line 1171 "ext/rbs_extension/lexer.c" -yy127: - rbs_skip(state); - yych = peek(state); - if (yych == 'd') goto yy182; - goto yy53; -yy128: - rbs_skip(state); - yych = peek(state); - if (yych == 'l') goto yy183; - goto yy53; -yy129: - rbs_skip(state); - yych = peek(state); - if (yych == 't') goto yy185; - goto yy53; -yy130: - rbs_skip(state); - yych = peek(state); - if (yych == 'e') goto yy187; - if (yych == 'i') goto yy188; - goto yy53; -yy131: - rbs_skip(state); - yych = peek(state); - if (yych == 'b') goto yy189; - goto yy53; yy132: - rbs_skip(state); - yych = peek(state); - if (yych == 'l') goto yy190; - goto yy53; +#line 78 "ext/rbs_extension/lexer.re" + { return next_token(state, kIN); } +#line 1234 "ext/rbs_extension/lexer.c" yy133: rbs_skip(state); yych = peek(state); - if (yych == 'n') goto yy191; - goto yy53; + if (yych == 'd') goto yy194; + goto yy54; yy134: rbs_skip(state); yych = peek(state); - if (yych == 'p') goto yy192; - goto yy53; + if (yych == 'l') goto yy195; + goto yy54; yy135: rbs_skip(state); yych = peek(state); - if (yych == 'u') goto yy194; - goto yy53; + if (yych == 't') goto yy197; + goto yy54; yy136: rbs_skip(state); yych = peek(state); - if (yych == 'p') goto yy195; - goto yy53; + if (yych == 'e') goto yy199; + goto yy54; yy137: rbs_skip(state); yych = peek(state); - if (yych == 'c') goto yy196; - if (yych == 't') goto yy197; - goto yy53; + if (yych == 'e') goto yy200; + if (yych == 'i') goto yy201; + goto yy54; yy138: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy198; - goto yy53; + if (yych == 'b') goto yy202; + goto yy54; yy139: rbs_skip(state); yych = peek(state); - if (yych == 'i') goto yy200; - goto yy53; + if (yych == 't') goto yy203; + goto yy54; yy140: rbs_skip(state); yych = peek(state); - if (yych <= '@') { - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy201; - goto yy68; - } else { - if (yych <= 'F') goto yy201; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy201; - goto yy68; - } + if (yych == 'l') goto yy204; + goto yy54; yy141: rbs_skip(state); yych = peek(state); - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy66; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy66; - goto yy68; + if (yych == 'n') goto yy205; + goto yy54; yy142: rbs_skip(state); yych = peek(state); - if (yych <= 0x00000000) goto yy68; - if (yych == ')') goto yy202; - goto yy142; + if (yych == 'i') goto yy206; + goto yy54; yy143: rbs_skip(state); yych = peek(state); - if (yych <= 0x00000000) goto yy68; - if (yych == '>') goto yy203; - goto yy143; + if (yych == 'p') goto yy207; + goto yy54; yy144: rbs_skip(state); yych = peek(state); - if (yych <= 0x00000000) goto yy68; - if (yych == ']') goto yy204; - goto yy144; + if (yych == 'u') goto yy209; + goto yy54; yy145: rbs_skip(state); yych = peek(state); - if (yych <= 0x00000000) goto yy68; - if (yych == '}') goto yy205; - goto yy145; + if (yych == 'p') goto yy210; + goto yy54; yy146: rbs_skip(state); yych = peek(state); - if (yych <= 0x00000000) goto yy68; - if (yych == '|') goto yy206; - goto yy146; + if (yych == 'c') goto yy211; + if (yych == 't') goto yy212; + goto yy54; yy147: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy213; + goto yy54; +yy148: + rbs_skip(state); + yych = peek(state); + if (yych == 'i') goto yy215; + goto yy54; +yy149: + rbs_skip(state); + yych = peek(state); + if (yych <= '@') { + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy216; + goto yy70; + } else { + if (yych <= 'F') goto yy216; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy216; + goto yy70; + } +yy150: + rbs_skip(state); + yych = peek(state); + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy68; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy68; + goto yy70; +yy151: + rbs_skip(state); + yych = peek(state); + if (yych <= 0x00000000) goto yy70; + if (yych == ')') goto yy217; + goto yy151; +yy152: + rbs_skip(state); + yych = peek(state); + if (yych <= 0x00000000) goto yy70; + if (yych == '>') goto yy218; + goto yy152; +yy153: + rbs_skip(state); + yych = peek(state); + if (yych <= 0x00000000) goto yy70; + if (yych == ']') goto yy219; + goto yy153; +yy154: + rbs_skip(state); + yych = peek(state); + if (yych <= 0x00000000) goto yy70; + if (yych == '}') goto yy220; + goto yy154; +yy155: + rbs_skip(state); + yych = peek(state); + if (yych <= 0x00000000) goto yy70; + if (yych == '|') goto yy221; + goto yy155; +yy156: yyaccept = 5; rbs_skip(state); backup = *state; yych = peek(state); if (yych <= '\'') { - if (yych <= 0x00000000) goto yy78; - if (yych <= '&') goto yy75; - goto yy77; + if (yych <= 0x00000000) goto yy80; + if (yych <= '&') goto yy77; + goto yy79; } else { - if (yych == '\\') goto yy79; - goto yy75; + if (yych == '\\') goto yy81; + goto yy77; } -yy148: +yy157: rbs_skip(state); #line 38 "ext/rbs_extension/lexer.re" { return next_token(state, pDOT3); } -#line 1307 "ext/rbs_extension/lexer.c" -yy149: +#line 1385 "ext/rbs_extension/lexer.c" +yy158: rbs_skip(state); -#line 108 "ext/rbs_extension/lexer.re" +#line 115 "ext/rbs_extension/lexer.re" { return next_token(state, tDQSYMBOL); } -#line 1312 "ext/rbs_extension/lexer.c" -yy150: +#line 1390 "ext/rbs_extension/lexer.c" +yy159: rbs_skip(state); yych = peek(state); - if (yych == 'u') goto yy207; - if (yych == 'x') goto yy208; - goto yy85; -yy151: + if (yych == 'u') goto yy222; + if (yych == 'x') goto yy223; + goto yy88; +yy160: rbs_skip(state); yych = peek(state); if (yych <= ',') { if (yych <= '\f') { - if (yych <= 0x00000000) goto yy152; - if (yych <= 0x00000008) goto yy151; - if (yych >= '\v') goto yy151; + if (yych <= 0x00000000) goto yy161; + if (yych <= 0x00000008) goto yy160; + if (yych >= '\v') goto yy160; } else { if (yych <= 0x0000001F) { - if (yych >= 0x0000000E) goto yy151; + if (yych >= 0x0000000E) goto yy160; } else { - if (yych == '#') goto yy151; + if (yych == '#') goto yy160; } } } else { if (yych <= '>') { - if (yych <= '-') goto yy151; - if (yych <= '/') goto yy152; - if (yych <= '9') goto yy151; + if (yych <= '-') goto yy160; + if (yych <= '/') goto yy161; + if (yych <= '9') goto yy160; } else { if (yych <= '^') { - if (yych <= 'Z') goto yy151; + if (yych <= 'Z') goto yy160; } else { - if (yych <= 'z') goto yy151; - if (yych >= 0x0000007F) goto yy151; + if (yych <= 'z') goto yy160; + if (yych >= 0x0000007F) goto yy160; } } } -yy152: -#line 125 "ext/rbs_extension/lexer.re" +yy161: +#line 132 "ext/rbs_extension/lexer.re" { return next_token(state, tSYMBOL); } -#line 1351 "ext/rbs_extension/lexer.c" -yy153: +#line 1429 "ext/rbs_extension/lexer.c" +yy162: rbs_skip(state); - goto yy152; -yy154: + goto yy161; +yy163: rbs_skip(state); -yy155: -#line 109 "ext/rbs_extension/lexer.re" +yy164: +#line 116 "ext/rbs_extension/lexer.re" { return next_token(state, tSQSYMBOL); } -#line 1360 "ext/rbs_extension/lexer.c" -yy156: +#line 1438 "ext/rbs_extension/lexer.c" +yy165: rbs_skip(state); yych = peek(state); if (yych <= '\'') { - if (yych <= 0x00000000) goto yy68; - if (yych <= '&') goto yy88; - goto yy209; + if (yych <= 0x00000000) goto yy70; + if (yych <= '&') goto yy91; + goto yy224; } else { - if (yych == '\\') goto yy156; - goto yy88; + if (yych == '\\') goto yy165; + goto yy91; } -yy157: +yy166: rbs_skip(state); yych = peek(state); - if (yych == '>') goto yy87; - goto yy84; -yy158: + if (yych == '>') goto yy90; + goto yy87; +yy167: rbs_skip(state); yych = peek(state); - if (yych == '=') goto yy87; - goto yy84; -yy159: + if (yych == '=') goto yy90; + goto yy87; +yy168: rbs_skip(state); yych = peek(state); if (yych <= '^') { - if (yych <= '@') goto yy68; - if (yych <= 'Z') goto yy210; - goto yy68; + if (yych <= '@') goto yy70; + if (yych <= 'Z') goto yy225; + goto yy70; } else { - if (yych == '`') goto yy68; - if (yych <= 'z') goto yy210; - goto yy68; + if (yych == '`') goto yy70; + if (yych <= 'z') goto yy225; + goto yy70; } -yy160: +yy169: rbs_skip(state); yych = peek(state); if (yych <= '>') { if (yych <= '/') { - if (yych == '!') goto yy212; + if (yych == '!') goto yy227; } else { - if (yych <= '9') goto yy160; - if (yych == '=') goto yy212; + if (yych <= '9') goto yy169; + if (yych == '=') goto yy227; } } else { if (yych <= '^') { - if (yych <= '?') goto yy212; - if (yych <= '@') goto yy161; - if (yych <= 'Z') goto yy160; + if (yych <= '?') goto yy227; + if (yych <= '@') goto yy170; + if (yych <= 'Z') goto yy169; } else { - if (yych == '`') goto yy161; - if (yych <= 'z') goto yy160; + if (yych == '`') goto yy170; + if (yych <= 'z') goto yy169; } } -yy161: -#line 123 "ext/rbs_extension/lexer.re" +yy170: +#line 130 "ext/rbs_extension/lexer.re" { return next_token(state, tSYMBOL); } -#line 1417 "ext/rbs_extension/lexer.c" -yy162: +#line 1495 "ext/rbs_extension/lexer.c" +yy171: rbs_skip(state); - goto yy97; -yy163: + goto yy100; +yy172: rbs_skip(state); yych = peek(state); if (yych <= 'Z') { - if (yych <= '/') goto yy164; - if (yych <= '9') goto yy163; - if (yych >= 'A') goto yy163; + if (yych <= '/') goto yy173; + if (yych <= '9') goto yy172; + if (yych >= 'A') goto yy172; } else { if (yych <= '_') { - if (yych >= '_') goto yy163; + if (yych >= '_') goto yy172; } else { - if (yych <= '`') goto yy164; - if (yych <= 'z') goto yy163; + if (yych <= '`') goto yy173; + if (yych <= 'z') goto yy172; } } -yy164: -#line 137 "ext/rbs_extension/lexer.re" +yy173: +#line 147 "ext/rbs_extension/lexer.re" { return next_token(state, tA2IDENT); } -#line 1439 "ext/rbs_extension/lexer.c" -yy165: +#line 1517 "ext/rbs_extension/lexer.c" +yy174: rbs_skip(state); yych = peek(state); - if (yych == 'o') goto yy213; - goto yy109; -yy166: + if (yych == 's') goto yy228; + goto yy107; +yy175: + rbs_skip(state); + yych = peek(state); + if (yych == 'o') goto yy230; + goto yy114; +yy176: rbs_skip(state); #line 40 "ext/rbs_extension/lexer.re" { return next_token(state, tQIDENT); } -#line 1449 "ext/rbs_extension/lexer.c" -yy167: +#line 1532 "ext/rbs_extension/lexer.c" +yy177: rbs_skip(state); yych = peek(state); - if (yych == 'a') goto yy214; - goto yy53; -yy168: + if (yych == 'a') goto yy231; + goto yy54; +yy178: rbs_skip(state); yych = peek(state); - if (yych == 'r') goto yy215; - goto yy53; -yy169: + if (yych == 'r') goto yy232; + goto yy54; +yy179: rbs_skip(state); yych = peek(state); - if (yych == 'l') goto yy216; - goto yy53; -yy170: + if (yych == 'l') goto yy233; + goto yy54; +yy180: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy171; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy181; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy171; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy181; + if (yych <= 'z') goto yy53; } } -yy171: -#line 71 "ext/rbs_extension/lexer.re" +yy181: +#line 72 "ext/rbs_extension/lexer.re" { return next_token(state, kBOT); } -#line 1487 "ext/rbs_extension/lexer.c" -yy172: +#line 1570 "ext/rbs_extension/lexer.c" +yy182: rbs_skip(state); yych = peek(state); - if (yych == 's') goto yy218; - goto yy53; -yy173: + if (yych == 's') goto yy235; + goto yy54; +yy183: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy174; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy184; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy174; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy184; + if (yych <= 'z') goto yy53; } } -yy174: -#line 73 "ext/rbs_extension/lexer.re" +yy184: +#line 74 "ext/rbs_extension/lexer.re" { return next_token(state, kDEF); } -#line 1515 "ext/rbs_extension/lexer.c" -yy175: +#line 1598 "ext/rbs_extension/lexer.c" +yy185: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy176; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy186; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy176; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy186; + if (yych <= 'z') goto yy53; } } -yy176: -#line 74 "ext/rbs_extension/lexer.re" +yy186: +#line 75 "ext/rbs_extension/lexer.re" { return next_token(state, kEND); } -#line 1538 "ext/rbs_extension/lexer.c" -yy177: +#line 1621 "ext/rbs_extension/lexer.c" +yy187: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy219; - goto yy53; -yy178: + if (yych == 'e') goto yy236; + goto yy54; +yy188: rbs_skip(state); yych = peek(state); - if (yych == 's') goto yy220; - goto yy53; -yy179: + if (yych == 's') goto yy237; + goto yy54; +yy189: rbs_skip(state); yych = peek(state); - if (yych == 'l') goto yy221; - goto yy53; -yy180: + if (yych == 'e') goto yy238; + goto yy54; +yy190: rbs_skip(state); yych = peek(state); - if (yych == 't') goto yy222; - goto yy53; -yy181: + if (yych == 'l') goto yy239; + goto yy54; +yy191: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy223; - goto yy53; -yy182: + if (yych == 'e') goto yy240; + goto yy54; +yy192: rbs_skip(state); yych = peek(state); - if (yych == 'u') goto yy224; - goto yy53; -yy183: + if (yych == 't') goto yy241; + goto yy54; +yy193: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy242; + goto yy54; +yy194: + rbs_skip(state); + yych = peek(state); + if (yych == 'u') goto yy243; + goto yy54; +yy195: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy184; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy196; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy184; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy196; + if (yych <= 'z') goto yy53; } } -yy184: -#line 82 "ext/rbs_extension/lexer.re" +yy196: +#line 83 "ext/rbs_extension/lexer.re" { return next_token(state, kNIL); } -#line 1591 "ext/rbs_extension/lexer.c" -yy185: +#line 1684 "ext/rbs_extension/lexer.c" +yy197: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy186; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy198; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy186; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy198; + if (yych <= 'z') goto yy53; } } -yy186: -#line 83 "ext/rbs_extension/lexer.re" +yy198: +#line 84 "ext/rbs_extension/lexer.re" { return next_token(state, kOUT); } -#line 1614 "ext/rbs_extension/lexer.c" -yy187: +#line 1707 "ext/rbs_extension/lexer.c" +yy199: rbs_skip(state); yych = peek(state); - if (yych == 'p') goto yy225; - goto yy53; -yy188: + if (yych == 'r') goto yy244; + goto yy54; +yy200: rbs_skip(state); yych = peek(state); - if (yych == 'v') goto yy226; - goto yy53; -yy189: + if (yych == 'p') goto yy245; + goto yy54; +yy201: rbs_skip(state); yych = peek(state); - if (yych == 'l') goto yy227; - goto yy53; -yy190: + if (yych == 'v') goto yy246; + goto yy54; +yy202: rbs_skip(state); yych = peek(state); - if (yych == 'f') goto yy228; - goto yy53; -yy191: + if (yych == 'l') goto yy247; + goto yy54; +yy203: rbs_skip(state); yych = peek(state); - if (yych == 'g') goto yy230; - goto yy53; -yy192: + if (yych == 'u') goto yy248; + goto yy54; +yy204: + rbs_skip(state); + yych = peek(state); + if (yych == 'f') goto yy249; + goto yy54; +yy205: + rbs_skip(state); + yych = peek(state); + if (yych == 'g') goto yy251; + goto yy54; +yy206: + rbs_skip(state); + yych = peek(state); + if (yych == 'p') goto yy252; + goto yy54; +yy207: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy193; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy208; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy193; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy208; + if (yych <= 'z') goto yy53; } } -yy193: -#line 89 "ext/rbs_extension/lexer.re" +yy208: +#line 90 "ext/rbs_extension/lexer.re" { return next_token(state, kTOP); } -#line 1662 "ext/rbs_extension/lexer.c" -yy194: +#line 1770 "ext/rbs_extension/lexer.c" +yy209: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy231; - goto yy53; -yy195: + if (yych == 'e') goto yy254; + goto yy54; +yy210: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy233; - goto yy53; -yy196: + if (yych == 'e') goto yy256; + goto yy54; +yy211: rbs_skip(state); yych = peek(state); - if (yych == 'h') goto yy235; - goto yy53; -yy197: + if (yych == 'h') goto yy258; + goto yy54; +yy212: rbs_skip(state); yych = peek(state); - if (yych == 'y') goto yy236; - goto yy53; -yy198: + if (yych == 'y') goto yy259; + goto yy54; +yy213: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy199; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy214; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy199; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy214; + if (yych <= 'z') goto yy53; } } -yy199: -#line 95 "ext/rbs_extension/lexer.re" +yy214: +#line 96 "ext/rbs_extension/lexer.re" { return next_token(state, kUSE); } -#line 1705 "ext/rbs_extension/lexer.c" -yy200: +#line 1813 "ext/rbs_extension/lexer.c" +yy215: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy237; - goto yy53; -yy201: + if (yych == 'd') goto yy260; + goto yy54; +yy216: rbs_skip(state); yych = peek(state); if (yych <= '@') { - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy239; - goto yy68; + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy262; + goto yy70; } else { - if (yych <= 'F') goto yy239; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy239; - goto yy68; + if (yych <= 'F') goto yy262; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy262; + goto yy70; } -yy202: +yy217: rbs_skip(state); -#line 54 "ext/rbs_extension/lexer.re" +#line 55 "ext/rbs_extension/lexer.re" { return next_token(state, tANNOTATION); } -#line 1728 "ext/rbs_extension/lexer.c" -yy203: +#line 1836 "ext/rbs_extension/lexer.c" +yy218: rbs_skip(state); -#line 57 "ext/rbs_extension/lexer.re" +#line 58 "ext/rbs_extension/lexer.re" { return next_token(state, tANNOTATION); } -#line 1733 "ext/rbs_extension/lexer.c" -yy204: +#line 1841 "ext/rbs_extension/lexer.c" +yy219: rbs_skip(state); -#line 55 "ext/rbs_extension/lexer.re" +#line 56 "ext/rbs_extension/lexer.re" { return next_token(state, tANNOTATION); } -#line 1738 "ext/rbs_extension/lexer.c" -yy205: +#line 1846 "ext/rbs_extension/lexer.c" +yy220: rbs_skip(state); -#line 53 "ext/rbs_extension/lexer.re" +#line 54 "ext/rbs_extension/lexer.re" { return next_token(state, tANNOTATION); } -#line 1743 "ext/rbs_extension/lexer.c" -yy206: +#line 1851 "ext/rbs_extension/lexer.c" +yy221: rbs_skip(state); -#line 56 "ext/rbs_extension/lexer.re" +#line 57 "ext/rbs_extension/lexer.re" { return next_token(state, tANNOTATION); } -#line 1748 "ext/rbs_extension/lexer.c" -yy207: +#line 1856 "ext/rbs_extension/lexer.c" +yy222: rbs_skip(state); yych = peek(state); if (yych <= '@') { - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy240; - goto yy68; + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy263; + goto yy70; } else { - if (yych <= 'F') goto yy240; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy240; - goto yy68; + if (yych <= 'F') goto yy263; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy263; + goto yy70; } -yy208: +yy223: rbs_skip(state); yych = peek(state); - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy85; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy85; - goto yy68; -yy209: + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy88; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy88; + goto yy70; +yy224: yyaccept = 6; rbs_skip(state); backup = *state; yych = peek(state); if (yych <= '\'') { - if (yych <= 0x00000000) goto yy155; - if (yych <= '&') goto yy88; - goto yy154; + if (yych <= 0x00000000) goto yy164; + if (yych <= '&') goto yy91; + goto yy163; } else { - if (yych == '\\') goto yy156; - goto yy88; + if (yych == '\\') goto yy165; + goto yy91; } -yy210: +yy225: rbs_skip(state); yych = peek(state); if (yych <= '>') { if (yych <= '/') { - if (yych == '!') goto yy241; + if (yych == '!') goto yy264; } else { - if (yych <= '9') goto yy210; - if (yych == '=') goto yy241; + if (yych <= '9') goto yy225; + if (yych == '=') goto yy264; } } else { if (yych <= '^') { - if (yych <= '?') goto yy241; - if (yych <= '@') goto yy211; - if (yych <= 'Z') goto yy210; + if (yych <= '?') goto yy264; + if (yych <= '@') goto yy226; + if (yych <= 'Z') goto yy225; } else { - if (yych == '`') goto yy211; - if (yych <= 'z') goto yy210; + if (yych == '`') goto yy226; + if (yych <= 'z') goto yy225; } } -yy211: -#line 124 "ext/rbs_extension/lexer.re" +yy226: +#line 131 "ext/rbs_extension/lexer.re" { return next_token(state, tSYMBOL); } -#line 1806 "ext/rbs_extension/lexer.c" -yy212: +#line 1914 "ext/rbs_extension/lexer.c" +yy227: rbs_skip(state); - goto yy161; -yy213: + goto yy170; +yy228: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy242; - goto yy109; -yy214: + if (yych <= '@') { + if (yych <= '!') { + if (yych >= '!') goto yy265; + } else { + if (yych <= '/') goto yy229; + if (yych <= '9') goto yy106; + } + } else { + if (yych <= '_') { + if (yych <= 'Z') goto yy106; + if (yych >= '_') goto yy106; + } else { + if (yych <= '`') goto yy229; + if (yych <= 'z') goto yy106; + } + } +yy229: +#line 144 "ext/rbs_extension/lexer.re" + { return next_token(state, kATRBS); } +#line 1940 "ext/rbs_extension/lexer.c" +yy230: rbs_skip(state); yych = peek(state); - if (yych == 's') goto yy243; - goto yy53; -yy215: + if (yych == 'd') goto yy266; + goto yy114; +yy231: rbs_skip(state); yych = peek(state); - if (yych == '_') goto yy245; - goto yy53; -yy216: + if (yych == 's') goto yy267; + goto yy54; +yy232: + rbs_skip(state); + yych = peek(state); + if (yych == '_') goto yy269; + goto yy54; +yy233: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy217; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy234; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy217; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy234; + if (yych <= 'z') goto yy53; } } -yy217: -#line 70 "ext/rbs_extension/lexer.re" +yy234: +#line 71 "ext/rbs_extension/lexer.re" { return next_token(state, kBOOL); } -#line 1847 "ext/rbs_extension/lexer.c" -yy218: +#line 1978 "ext/rbs_extension/lexer.c" +yy235: rbs_skip(state); yych = peek(state); - if (yych == 's') goto yy246; - goto yy53; -yy219: + if (yych == 's') goto yy270; + goto yy54; +yy236: rbs_skip(state); yych = peek(state); - if (yych == 'n') goto yy248; - goto yy53; -yy220: + if (yych == 'n') goto yy272; + goto yy54; +yy237: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy249; - goto yy53; -yy221: + if (yych == 'e') goto yy273; + goto yy54; +yy238: rbs_skip(state); yych = peek(state); - if (yych == 'u') goto yy251; - goto yy53; -yy222: + if (yych == 'r') goto yy275; + goto yy54; +yy239: rbs_skip(state); yych = peek(state); - if (yych == 'a') goto yy252; - goto yy53; -yy223: + if (yych == 'u') goto yy276; + goto yy54; +yy240: rbs_skip(state); yych = peek(state); - if (yych == 'r') goto yy253; - goto yy53; -yy224: + if (yych == 'r') goto yy277; + goto yy54; +yy241: rbs_skip(state); yych = peek(state); - if (yych == 'l') goto yy254; - goto yy53; -yy225: + if (yych == 'a') goto yy278; + goto yy54; +yy242: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy255; - goto yy53; -yy226: + if (yych == 'r') goto yy279; + goto yy54; +yy243: rbs_skip(state); yych = peek(state); - if (yych == 'a') goto yy256; - goto yy53; -yy227: + if (yych == 'l') goto yy280; + goto yy54; +yy244: rbs_skip(state); yych = peek(state); - if (yych == 'i') goto yy257; - goto yy53; -yy228: + if (yych == 'r') goto yy281; + goto yy54; +yy245: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy282; + goto yy54; +yy246: + rbs_skip(state); + yych = peek(state); + if (yych == 'a') goto yy283; + goto yy54; +yy247: + rbs_skip(state); + yych = peek(state); + if (yych == 'i') goto yy284; + goto yy54; +yy248: + rbs_skip(state); + yych = peek(state); + if (yych == 'r') goto yy285; + goto yy54; +yy249: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy229; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy250; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy229; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy250; + if (yych <= 'z') goto yy53; } } -yy229: -#line 87 "ext/rbs_extension/lexer.re" +yy250: +#line 88 "ext/rbs_extension/lexer.re" { return next_token(state, kSELF); } -#line 1920 "ext/rbs_extension/lexer.c" -yy230: +#line 2071 "ext/rbs_extension/lexer.c" +yy251: rbs_skip(state); yych = peek(state); - if (yych == 'l') goto yy258; - goto yy53; -yy231: + if (yych == 'l') goto yy286; + goto yy54; +yy252: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy232; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy253; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy232; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy253; + if (yych <= 'z') goto yy53; } } -yy232: -#line 90 "ext/rbs_extension/lexer.re" - { return next_token(state, kTRUE); } -#line 1948 "ext/rbs_extension/lexer.c" -yy233: +yy253: +#line 104 "ext/rbs_extension/lexer.re" + { return next_token(state, kSKIP); } +#line 2099 "ext/rbs_extension/lexer.c" +yy254: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy234; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy255; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy234; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy255; + if (yych <= 'z') goto yy53; } } -yy234: +yy255: #line 91 "ext/rbs_extension/lexer.re" + { return next_token(state, kTRUE); } +#line 2122 "ext/rbs_extension/lexer.c" +yy256: + rbs_skip(state); + yych = peek(state); + if (yych <= '=') { + if (yych <= '/') { + if (yych == '!') goto yy110; + } else { + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy257; + if (yych <= 'Z') goto yy53; + } else { + if (yych == '`') goto yy257; + if (yych <= 'z') goto yy53; + } + } +yy257: +#line 92 "ext/rbs_extension/lexer.re" { return next_token(state, kTYPE); } -#line 1971 "ext/rbs_extension/lexer.c" -yy235: +#line 2145 "ext/rbs_extension/lexer.c" +yy258: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy259; - goto yy53; -yy236: + if (yych == 'e') goto yy287; + goto yy54; +yy259: rbs_skip(state); yych = peek(state); - if (yych == 'p') goto yy260; - goto yy53; -yy237: + if (yych == 'p') goto yy288; + goto yy54; +yy260: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy238; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy261; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy238; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy261; + if (yych <= 'z') goto yy53; } } -yy238: -#line 94 "ext/rbs_extension/lexer.re" +yy261: +#line 95 "ext/rbs_extension/lexer.re" { return next_token(state, kVOID); } -#line 2004 "ext/rbs_extension/lexer.c" -yy239: +#line 2178 "ext/rbs_extension/lexer.c" +yy262: rbs_skip(state); yych = peek(state); if (yych <= '@') { - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy261; - goto yy68; + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy289; + goto yy70; } else { - if (yych <= 'F') goto yy261; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy261; - goto yy68; + if (yych <= 'F') goto yy289; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy289; + goto yy70; } -yy240: +yy263: rbs_skip(state); yych = peek(state); if (yych <= '@') { - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy262; - goto yy68; + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy290; + goto yy70; } else { - if (yych <= 'F') goto yy262; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy262; - goto yy68; + if (yych <= 'F') goto yy290; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy290; + goto yy70; } -yy241: +yy264: rbs_skip(state); - goto yy211; -yy242: + goto yy226; +yy265: + rbs_skip(state); +#line 143 "ext/rbs_extension/lexer.re" + { return next_token(state, kATRBSB); } +#line 2212 "ext/rbs_extension/lexer.c" +yy266: rbs_skip(state); yych = peek(state); - if (yych == 'o') goto yy263; - goto yy109; -yy243: + if (yych == 'o') goto yy291; + goto yy114; +yy267: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy244; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy268; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy244; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy268; + if (yych <= 'z') goto yy53; } } -yy244: -#line 66 "ext/rbs_extension/lexer.re" +yy268: +#line 67 "ext/rbs_extension/lexer.re" { return next_token(state, kALIAS); } -#line 2061 "ext/rbs_extension/lexer.c" -yy245: +#line 2240 "ext/rbs_extension/lexer.c" +yy269: rbs_skip(state); yych = peek(state); if (yych <= 'q') { - if (yych == 'a') goto yy264; - goto yy53; + if (yych == 'a') goto yy292; + goto yy54; } else { - if (yych <= 'r') goto yy265; - if (yych == 'w') goto yy266; - goto yy53; + if (yych <= 'r') goto yy293; + if (yych == 'w') goto yy294; + goto yy54; } -yy246: +yy270: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy247; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy271; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy247; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy271; + if (yych <= 'z') goto yy53; } } -yy247: -#line 72 "ext/rbs_extension/lexer.re" +yy271: +#line 73 "ext/rbs_extension/lexer.re" { return next_token(state, kCLASS); } -#line 2095 "ext/rbs_extension/lexer.c" -yy248: +#line 2274 "ext/rbs_extension/lexer.c" +yy272: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy267; - goto yy53; -yy249: + if (yych == 'd') goto yy295; + goto yy54; +yy273: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy250; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy274; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy250; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy274; + if (yych <= 'z') goto yy53; } } -yy250: -#line 76 "ext/rbs_extension/lexer.re" +yy274: +#line 77 "ext/rbs_extension/lexer.re" { return next_token(state, kFALSE); } -#line 2123 "ext/rbs_extension/lexer.c" -yy251: +#line 2302 "ext/rbs_extension/lexer.c" +yy275: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy269; - goto yy53; -yy252: + if (yych == 'i') goto yy297; + goto yy54; +yy276: rbs_skip(state); yych = peek(state); - if (yych == 'n') goto yy270; - goto yy53; -yy253: + if (yych == 'd') goto yy298; + goto yy54; +yy277: rbs_skip(state); yych = peek(state); - if (yych == 'f') goto yy271; - goto yy53; -yy254: + if (yych == 'i') goto yy299; + goto yy54; +yy278: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy272; - goto yy53; -yy255: + if (yych == 'n') goto yy300; + goto yy54; +yy279: rbs_skip(state); yych = peek(state); - if (yych == 'n') goto yy274; - goto yy53; -yy256: + if (yych == 'f') goto yy301; + goto yy54; +yy280: rbs_skip(state); yych = peek(state); - if (yych == 't') goto yy275; - goto yy53; -yy257: + if (yych == 'e') goto yy302; + goto yy54; +yy281: rbs_skip(state); yych = peek(state); - if (yych == 'c') goto yy276; - goto yy53; -yy258: + if (yych == 'i') goto yy304; + goto yy54; +yy282: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy278; - goto yy53; -yy259: + if (yych == 'n') goto yy305; + goto yy54; +yy283: rbs_skip(state); yych = peek(state); - if (yych == 'c') goto yy279; - goto yy53; -yy260: + if (yych == 't') goto yy306; + goto yy54; +yy284: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy280; - goto yy53; -yy261: + if (yych == 'c') goto yy307; + goto yy54; +yy285: + rbs_skip(state); + yych = peek(state); + if (yych == 'n') goto yy309; + goto yy54; +yy286: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy311; + goto yy54; +yy287: + rbs_skip(state); + yych = peek(state); + if (yych == 'c') goto yy312; + goto yy54; +yy288: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy313; + goto yy54; +yy289: rbs_skip(state); yych = peek(state); if (yych <= '@') { - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy66; - goto yy68; + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy68; + goto yy70; } else { - if (yych <= 'F') goto yy66; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy66; - goto yy68; + if (yych <= 'F') goto yy68; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy68; + goto yy70; } -yy262: +yy290: rbs_skip(state); yych = peek(state); if (yych <= '@') { - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy281; - goto yy68; + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy314; + goto yy70; } else { - if (yych <= 'F') goto yy281; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy281; - goto yy68; + if (yych <= 'F') goto yy314; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy314; + goto yy70; } -yy263: +yy291: rbs_skip(state); yych = peek(state); - if (yych == '_') goto yy282; - goto yy109; -yy264: + if (yych == '_') goto yy315; + goto yy114; +yy292: rbs_skip(state); yych = peek(state); - if (yych == 'c') goto yy283; - goto yy53; -yy265: + if (yych == 'c') goto yy316; + goto yy54; +yy293: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy284; - goto yy53; -yy266: + if (yych == 'e') goto yy317; + goto yy54; +yy294: rbs_skip(state); yych = peek(state); - if (yych == 'r') goto yy285; - goto yy53; -yy267: + if (yych == 'r') goto yy318; + goto yy54; +yy295: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy268; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy296; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy268; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy296; + if (yych <= 'z') goto yy53; } } -yy268: -#line 75 "ext/rbs_extension/lexer.re" +yy296: +#line 76 "ext/rbs_extension/lexer.re" { return next_token(state, kEXTEND); } -#line 2242 "ext/rbs_extension/lexer.c" -yy269: +#line 2441 "ext/rbs_extension/lexer.c" +yy297: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy286; - goto yy53; -yy270: + if (yych == 'c') goto yy319; + goto yy54; +yy298: rbs_skip(state); yych = peek(state); - if (yych == 'c') goto yy288; - goto yy53; -yy271: + if (yych == 'e') goto yy321; + goto yy54; +yy299: rbs_skip(state); yych = peek(state); - if (yych == 'a') goto yy289; - goto yy53; -yy272: + if (yych == 't') goto yy323; + goto yy54; +yy300: rbs_skip(state); yych = peek(state); - if (yych <= '=') { - if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == 'c') goto yy324; + goto yy54; +yy301: + rbs_skip(state); + yych = peek(state); + if (yych == 'a') goto yy325; + goto yy54; +yy302: + yyaccept = 7; + rbs_skip(state); + backup = *state; + yych = peek(state); + if (yych <= '<') { + if (yych <= ',') { + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '-') goto yy326; + if (yych <= '/') goto yy303; + if (yych <= '9') goto yy53; } } else { if (yych <= '^') { - if (yych <= '@') goto yy273; - if (yych <= 'Z') goto yy52; + if (yych <= '=') goto yy111; + if (yych <= '@') goto yy303; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy273; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy303; + if (yych <= 'z') goto yy53; } } -yy273: -#line 81 "ext/rbs_extension/lexer.re" +yy303: +#line 82 "ext/rbs_extension/lexer.re" { return next_token(state, kMODULE); } -#line 2280 "ext/rbs_extension/lexer.c" -yy274: +#line 2493 "ext/rbs_extension/lexer.c" +yy304: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy290; - goto yy53; -yy275: + if (yych == 'd') goto yy327; + goto yy54; +yy305: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy292; - goto yy53; -yy276: + if (yych == 'd') goto yy328; + goto yy54; +yy306: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy330; + goto yy54; +yy307: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy277; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy308; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy277; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy308; + if (yych <= 'z') goto yy53; } } -yy277: -#line 86 "ext/rbs_extension/lexer.re" +yy308: +#line 87 "ext/rbs_extension/lexer.re" { return next_token(state, kPUBLIC); } -#line 2313 "ext/rbs_extension/lexer.c" -yy278: +#line 2531 "ext/rbs_extension/lexer.c" +yy309: rbs_skip(state); yych = peek(state); - if (yych == 't') goto yy294; - goto yy53; -yy279: + if (yych <= '=') { + if (yych <= '/') { + if (yych == '!') goto yy110; + } else { + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy310; + if (yych <= 'Z') goto yy53; + } else { + if (yych == '`') goto yy310; + if (yych <= 'z') goto yy53; + } + } +yy310: +#line 100 "ext/rbs_extension/lexer.re" + { return next_token(state, kRETURN); } +#line 2554 "ext/rbs_extension/lexer.c" +yy311: rbs_skip(state); yych = peek(state); - if (yych == 'k') goto yy295; - goto yy53; -yy280: + if (yych == 't') goto yy332; + goto yy54; +yy312: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy296; - goto yy53; -yy281: + if (yych == 'k') goto yy333; + goto yy54; +yy313: + rbs_skip(state); + yych = peek(state); + if (yych == 'd') goto yy334; + goto yy54; +yy314: rbs_skip(state); yych = peek(state); if (yych <= '@') { - if (yych <= '/') goto yy68; - if (yych <= '9') goto yy85; - goto yy68; + if (yych <= '/') goto yy70; + if (yych <= '9') goto yy88; + goto yy70; } else { - if (yych <= 'F') goto yy85; - if (yych <= '`') goto yy68; - if (yych <= 'f') goto yy85; - goto yy68; + if (yych <= 'F') goto yy88; + if (yych <= '`') goto yy70; + if (yych <= 'f') goto yy88; + goto yy70; } -yy282: +yy315: rbs_skip(state); yych = peek(state); - if (yych == '_') goto yy298; - goto yy109; -yy283: + if (yych == '_') goto yy336; + goto yy114; +yy316: rbs_skip(state); yych = peek(state); - if (yych == 'c') goto yy300; - goto yy53; -yy284: + if (yych == 'c') goto yy338; + goto yy54; +yy317: rbs_skip(state); yych = peek(state); - if (yych == 'a') goto yy301; - goto yy53; -yy285: + if (yych == 'a') goto yy339; + goto yy54; +yy318: rbs_skip(state); yych = peek(state); - if (yych == 'i') goto yy302; - goto yy53; -yy286: + if (yych == 'i') goto yy340; + goto yy54; +yy319: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy287; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy320; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy287; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy320; + if (yych <= 'z') goto yy53; } } -yy287: -#line 78 "ext/rbs_extension/lexer.re" +yy320: +#line 103 "ext/rbs_extension/lexer.re" + { return next_token(state, kGENERIC); } +#line 2625 "ext/rbs_extension/lexer.c" +yy321: + rbs_skip(state); + yych = peek(state); + if (yych <= '=') { + if (yych <= '/') { + if (yych == '!') goto yy110; + } else { + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy322; + if (yych <= 'Z') goto yy53; + } else { + if (yych == '`') goto yy322; + if (yych <= 'z') goto yy53; + } + } +yy322: +#line 79 "ext/rbs_extension/lexer.re" { return next_token(state, kINCLUDE); } -#line 2384 "ext/rbs_extension/lexer.c" -yy288: +#line 2648 "ext/rbs_extension/lexer.c" +yy323: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy303; - goto yy53; -yy289: + if (yych == 's') goto yy341; + goto yy54; +yy324: rbs_skip(state); yych = peek(state); - if (yych == 'c') goto yy305; - goto yy53; -yy290: + if (yych == 'e') goto yy343; + goto yy54; +yy325: + rbs_skip(state); + yych = peek(state); + if (yych == 'c') goto yy345; + goto yy54; +yy326: + rbs_skip(state); + yych = peek(state); + if (yych == 's') goto yy346; + goto yy70; +yy327: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy347; + goto yy54; +yy328: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy291; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy329; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy291; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy329; + if (yych <= 'z') goto yy53; } } -yy291: -#line 84 "ext/rbs_extension/lexer.re" +yy329: +#line 85 "ext/rbs_extension/lexer.re" { return next_token(state, kPREPEND); } -#line 2417 "ext/rbs_extension/lexer.c" -yy292: +#line 2696 "ext/rbs_extension/lexer.c" +yy330: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy293; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy331; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy293; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy331; + if (yych <= 'z') goto yy53; } } -yy293: -#line 85 "ext/rbs_extension/lexer.re" +yy331: +#line 86 "ext/rbs_extension/lexer.re" { return next_token(state, kPRIVATE); } -#line 2440 "ext/rbs_extension/lexer.c" -yy294: +#line 2719 "ext/rbs_extension/lexer.c" +yy332: rbs_skip(state); yych = peek(state); - if (yych == 'o') goto yy306; - goto yy53; -yy295: + if (yych == 'o') goto yy349; + goto yy54; +yy333: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy307; - goto yy53; -yy296: + if (yych == 'e') goto yy350; + goto yy54; +yy334: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy297; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy335; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy297; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy335; + if (yych <= 'z') goto yy53; } } -yy297: -#line 93 "ext/rbs_extension/lexer.re" +yy335: +#line 94 "ext/rbs_extension/lexer.re" { return next_token(state, kUNTYPED); } -#line 2473 "ext/rbs_extension/lexer.c" -yy298: +#line 2752 "ext/rbs_extension/lexer.c" +yy336: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy108; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy113; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy299; - if (yych <= 'Z') goto yy108; + if (yych <= '@') goto yy337; + if (yych <= 'Z') goto yy113; } else { - if (yych == '`') goto yy299; - if (yych <= 'z') goto yy108; + if (yych == '`') goto yy337; + if (yych <= 'z') goto yy113; } } -yy299: -#line 97 "ext/rbs_extension/lexer.re" +yy337: +#line 98 "ext/rbs_extension/lexer.re" { return next_token(state, k__TODO__); } -#line 2496 "ext/rbs_extension/lexer.c" -yy300: +#line 2775 "ext/rbs_extension/lexer.c" +yy338: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy308; - goto yy53; -yy301: + if (yych == 'e') goto yy351; + goto yy54; +yy339: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy309; - goto yy53; -yy302: + if (yych == 'd') goto yy352; + goto yy54; +yy340: rbs_skip(state); yych = peek(state); - if (yych == 't') goto yy310; - goto yy53; -yy303: + if (yych == 't') goto yy353; + goto yy54; +yy341: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy304; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy342; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy304; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy342; + if (yych <= 'z') goto yy53; } } -yy304: -#line 79 "ext/rbs_extension/lexer.re" +yy342: +#line 99 "ext/rbs_extension/lexer.re" + { return next_token(state, kINHERITS); } +#line 2813 "ext/rbs_extension/lexer.c" +yy343: + rbs_skip(state); + yych = peek(state); + if (yych <= '=') { + if (yych <= '/') { + if (yych == '!') goto yy110; + } else { + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy344; + if (yych <= 'Z') goto yy53; + } else { + if (yych == '`') goto yy344; + if (yych <= 'z') goto yy53; + } + } +yy344: +#line 80 "ext/rbs_extension/lexer.re" { return next_token(state, kINSTANCE); } -#line 2534 "ext/rbs_extension/lexer.c" -yy305: +#line 2836 "ext/rbs_extension/lexer.c" +yy345: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy311; - goto yy53; -yy306: + if (yych == 'e') goto yy354; + goto yy54; +yy346: rbs_skip(state); yych = peek(state); - if (yych == 'n') goto yy313; - goto yy53; -yy307: + if (yych == 'e') goto yy356; + goto yy70; +yy347: rbs_skip(state); yych = peek(state); - if (yych == 'd') goto yy315; - goto yy53; -yy308: + if (yych <= '=') { + if (yych <= '/') { + if (yych == '!') goto yy110; + } else { + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; + } + } else { + if (yych <= '^') { + if (yych <= '@') goto yy348; + if (yych <= 'Z') goto yy53; + } else { + if (yych == '`') goto yy348; + if (yych <= 'z') goto yy53; + } + } +yy348: +#line 101 "ext/rbs_extension/lexer.re" + { return next_token(state, kOVERRIDE); } +#line 2869 "ext/rbs_extension/lexer.c" +yy349: rbs_skip(state); yych = peek(state); - if (yych == 's') goto yy317; - goto yy53; -yy309: + if (yych == 'n') goto yy357; + goto yy54; +yy350: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy318; - goto yy53; -yy310: + if (yych == 'd') goto yy359; + goto yy54; +yy351: rbs_skip(state); yych = peek(state); - if (yych == 'e') goto yy319; - goto yy53; -yy311: + if (yych == 's') goto yy361; + goto yy54; +yy352: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy362; + goto yy54; +yy353: + rbs_skip(state); + yych = peek(state); + if (yych == 'e') goto yy363; + goto yy54; +yy354: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy312; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy355; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy312; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy355; + if (yych <= 'z') goto yy53; } } -yy312: -#line 80 "ext/rbs_extension/lexer.re" +yy355: +#line 81 "ext/rbs_extension/lexer.re" { return next_token(state, kINTERFACE); } -#line 2587 "ext/rbs_extension/lexer.c" -yy313: +#line 2917 "ext/rbs_extension/lexer.c" +yy356: + rbs_skip(state); + yych = peek(state); + if (yych == 'l') goto yy364; + goto yy70; +yy357: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy314; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy358; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy314; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy358; + if (yych <= 'z') goto yy53; } } -yy314: -#line 88 "ext/rbs_extension/lexer.re" +yy358: +#line 89 "ext/rbs_extension/lexer.re" { return next_token(state, kSINGLETON); } -#line 2610 "ext/rbs_extension/lexer.c" -yy315: +#line 2945 "ext/rbs_extension/lexer.c" +yy359: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy316; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy360; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy316; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy360; + if (yych <= 'z') goto yy53; } } -yy316: -#line 92 "ext/rbs_extension/lexer.re" +yy360: +#line 93 "ext/rbs_extension/lexer.re" { return next_token(state, kUNCHECKED); } -#line 2633 "ext/rbs_extension/lexer.c" -yy317: +#line 2968 "ext/rbs_extension/lexer.c" +yy361: rbs_skip(state); yych = peek(state); - if (yych == 's') goto yy320; - goto yy53; -yy318: + if (yych == 's') goto yy365; + goto yy54; +yy362: rbs_skip(state); yych = peek(state); - if (yych == 'r') goto yy321; - goto yy53; -yy319: + if (yych == 'r') goto yy366; + goto yy54; +yy363: rbs_skip(state); yych = peek(state); - if (yych == 'r') goto yy323; - goto yy53; -yy320: + if (yych == 'r') goto yy368; + goto yy54; +yy364: rbs_skip(state); yych = peek(state); - if (yych == 'o') goto yy325; - goto yy53; -yy321: + if (yych == 'f') goto yy370; + goto yy70; +yy365: + rbs_skip(state); + yych = peek(state); + if (yych == 'o') goto yy371; + goto yy54; +yy366: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy322; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy367; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy322; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy367; + if (yych <= 'z') goto yy53; } } -yy322: -#line 68 "ext/rbs_extension/lexer.re" +yy367: +#line 69 "ext/rbs_extension/lexer.re" { return next_token(state, kATTRREADER); } -#line 2676 "ext/rbs_extension/lexer.c" -yy323: +#line 3016 "ext/rbs_extension/lexer.c" +yy368: rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy324; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy369; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy324; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy369; + if (yych <= 'z') goto yy53; } } -yy324: -#line 69 "ext/rbs_extension/lexer.re" +yy369: +#line 70 "ext/rbs_extension/lexer.re" { return next_token(state, kATTRWRITER); } -#line 2699 "ext/rbs_extension/lexer.c" -yy325: +#line 3039 "ext/rbs_extension/lexer.c" +yy370: + rbs_skip(state); +#line 102 "ext/rbs_extension/lexer.re" + { return next_token(state, kMODULESELF); } +#line 3044 "ext/rbs_extension/lexer.c" +yy371: rbs_skip(state); yych = peek(state); - if (yych != 'r') goto yy53; + if (yych != 'r') goto yy54; rbs_skip(state); yych = peek(state); if (yych <= '=') { if (yych <= '/') { - if (yych == '!') goto yy105; + if (yych == '!') goto yy110; } else { - if (yych <= '9') goto yy52; - if (yych >= '=') goto yy106; + if (yych <= '9') goto yy53; + if (yych >= '=') goto yy111; } } else { if (yych <= '^') { - if (yych <= '@') goto yy326; - if (yych <= 'Z') goto yy52; + if (yych <= '@') goto yy372; + if (yych <= 'Z') goto yy53; } else { - if (yych == '`') goto yy326; - if (yych <= 'z') goto yy52; + if (yych == '`') goto yy372; + if (yych <= 'z') goto yy53; } } -yy326: -#line 67 "ext/rbs_extension/lexer.re" +yy372: +#line 68 "ext/rbs_extension/lexer.re" { return next_token(state, kATTRACCESSOR); } -#line 2725 "ext/rbs_extension/lexer.c" +#line 3070 "ext/rbs_extension/lexer.c" } -#line 146 "ext/rbs_extension/lexer.re" +#line 156 "ext/rbs_extension/lexer.re" } diff --git a/ext/rbs_extension/lexer.h b/ext/rbs_extension/lexer.h index 55a8a3994..fba84f288 100644 --- a/ext/rbs_extension/lexer.h +++ b/ext/rbs_extension/lexer.h @@ -61,7 +61,14 @@ enum TokenType { kUSE, /* use */ kAS, /* as */ k__TODO__, /* __todo__ */ - + kINHERITS, /* inherits */ + kRETURN, /* return */ + kOVERRIDE, /* override */ + kMODULESELF, /* module-self */ + kGENERIC, /* generic */ + kSKIP, /* skip */ + kATRBS, /* @rbs */ + kATRBSB, /* @rbs! */ tLIDENT, /* Identifiers starting with lower case */ tUIDENT, /* Identifiers starting with upper case */ tULIDENT, /* Identifiers starting with `_` followed by upper case */ @@ -73,10 +80,12 @@ enum TokenType { tEQIDENT, /* Identifiers ending with `=` */ tQIDENT, /* Quoted identifier */ pAREF_OPR, /* [] */ + pMINUS2, /* -- */ tOPERATOR, /* Operator identifier */ tCOMMENT, /* Comment */ tLINECOMMENT, /* Comment of all line */ + tINLINECOMMENT, /* Special token inserted in parser */ tTRIVIA, /* Trivia tokens -- space and new line */ diff --git a/ext/rbs_extension/lexer.re b/ext/rbs_extension/lexer.re index aa1b94746..8f9c355cd 100644 --- a/ext/rbs_extension/lexer.re +++ b/ext/rbs_extension/lexer.re @@ -45,6 +45,7 @@ token rbsparser_next_token(lexstate *state) { "::" { return next_token(state, pCOLON2); } "<" { return next_token(state, pLT); } "[]" { return next_token(state, pAREF_OPR); } + "--" [^\x00]* { return next_token(state, pMINUS2); } operator { return next_token(state, tOPERATOR); } number = [0-9] [0-9_]*; @@ -95,6 +96,12 @@ token rbsparser_next_token(lexstate *state) { "use" { return next_token(state, kUSE); } "as" { return next_token(state, kAS); } "__todo__" { return next_token(state, k__TODO__); } + "inherits" { return next_token(state, kINHERITS); } + "return" { return next_token(state, kRETURN); } + "override" { return next_token(state, kOVERRIDE); } + "module-self" { return next_token(state, kMODULESELF); } + "generic" { return next_token(state, kGENERIC); } + "skip" { return next_token(state, kSKIP); } unicode_char = "\\u" [0-9a-fA-F]{4}; oct_char = "\\x" [0-9a-f]{1,2}; @@ -133,6 +140,9 @@ token rbsparser_next_token(lexstate *state) { [a-zA-Z_] word* "!" { return next_token(state, tBANGIDENT); } [a-zA-Z_] word* "=" { return next_token(state, tEQIDENT); } + "@rbs!" { return next_token(state, kATRBSB); } + "@rbs" { return next_token(state, kATRBS); } + "@" [a-zA-Z_] word* { return next_token(state, tAIDENT); } "@@" [a-zA-Z_] word* { return next_token(state, tA2IDENT); } diff --git a/ext/rbs_extension/lexstate.c b/ext/rbs_extension/lexstate.c index ed32fd06a..96acef2ee 100644 --- a/ext/rbs_extension/lexstate.c +++ b/ext/rbs_extension/lexstate.c @@ -60,6 +60,14 @@ static const char *RBS_TOKENTYPE_NAMES[] = { "kUSE", /* use */ "kAS", /* as */ "k__TODO__", /* __todo__ */ + "kINHERITS", /* inherits */ + "kRETURN", /* return */ + "kOVERRIDE", /* override */ + "kMODULESELF", /* module-self */ + "kGENERIC", /* generic */ + "kSKIP", /* skip */ + "kATRBS", /* @rbs */ + "kATRBSB", /* @rbs! */ "tLIDENT", /* Identifiers starting with lower case */ "tUIDENT", /* Identifiers starting with upper case */ @@ -72,10 +80,12 @@ static const char *RBS_TOKENTYPE_NAMES[] = { "tEQIDENT", "tQIDENT", /* Quoted identifier */ "pAREF_OPR", /* [] */ + "pMINUS2", /* -- */ "tOPERATOR", /* Operator identifier */ "tCOMMENT", "tLINECOMMENT", + "tINLINECOMMENT", "tTRIVIA", diff --git a/ext/rbs_extension/parser.c b/ext/rbs_extension/parser.c index 77f583c41..b33d90b86 100644 --- a/ext/rbs_extension/parser.c +++ b/ext/rbs_extension/parser.c @@ -48,6 +48,11 @@ case kUSE: \ case kAS: \ case k__TODO__: \ + case kINHERITS: \ + case kRETURN: \ + case kOVERRIDE: \ + case kGENERIC: \ + case kSKIP: \ /* nop */ typedef struct { @@ -673,6 +678,13 @@ static void parse_function(parserstate *state, VALUE *function, VALUE *block, VA } } + // Passing `NULL` to `block` means it cannot have block + if (!block) { + if (state->next_token.type != pARROW) { + raise_syntax_error(state, state->next_token2, "Cannot have block"); + } + } + VALUE required = Qtrue; if (state->next_token.type == pQUESTION && state->next_token2.type == pLBRACE) { // Optional block @@ -712,7 +724,9 @@ static void parse_function(parserstate *state, VALUE *function, VALUE *block, VA ); } - *block = rbs_block(block_function, required, block_self_type); + if (block) { + *block = rbs_block(block_function, required, block_self_type); + } parser_advance_assert(state, pRBRACE); } @@ -1142,101 +1156,176 @@ VALUE parse_type(parserstate *state) { } /* - type_params ::= {} `[` type_param `,` ... <`]`> - | {<>} + type_param ::= {} kUNCHECKED? (kIN|kOUT|) (module_type_params == true) - type_param ::= kUNCHECKED? (kIN|kOUT|) tUIDENT upper_bound? default_type? (module_type_params == true) + type_param ::= {} (module_type_params == false) - type_param ::= tUIDENT upper_bound? default_type? (module_type_params == false) + `ranges` must be 8-array or more to store ranges for: + + 0: all range + 1: unchecked keyword + 2: variance + 3: name + 4: upper_bound_operator + 5: upper_bound + 6: default_type_operator + 7: default_type + + `values` must be 5-array or more to store objects for: + + 0: unchecked (Qtrue or Qnil) + 1: variance (:invariant, :covariant, :contravariant, or Qnil) + 2: name (Symbol) + 3: upper_bound (type or Qnil) + 4: default_type (type or Qnil) */ -static VALUE parse_type_params(parserstate *state, range *rg, bool module_type_params) { - VALUE params = EMPTY_ARRAY; +static void parse_type_param(parserstate *state, bool module_type_params, bool required_param_allowed, range ranges[], VALUE values[]) { + range all_range = NULL_RANGE; - bool required_param_allowed = true; + range unchecked_range = NULL_RANGE; + VALUE unchecked = Qnil; - if (state->next_token.type == pLBRACKET) { - parser_advance(state); + parser_advance(state); - rg->start = state->current_token.range.start; + if (module_type_params) { + if (state->current_token.type == kUNCHECKED) { + unchecked_range = state->current_token.range; + unchecked = Qtrue; + all_range.start = unchecked_range.start; + parser_advance(state); + } + } - while (true) { - VALUE unchecked = Qfalse; - VALUE variance = ID2SYM(rb_intern("invariant")); - VALUE upper_bound = Qnil; - VALUE default_type = Qnil; - - range param_range; - param_range.start = state->next_token.range.start; - - range variance_range = NULL_RANGE; - range unchecked_range = NULL_RANGE; - if (module_type_params) { - if (state->next_token.type == kUNCHECKED) { - unchecked = Qtrue; - parser_advance(state); - unchecked_range = state->current_token.range; + range variance_range = NULL_RANGE; + VALUE variance = Qnil; + if (module_type_params) { + switch (state->current_token.type) { + case kIN: + variance = ID2SYM(rb_intern("contravariant")); + variance_range = state->current_token.range; + if (null_position_p(all_range.start)) { + all_range.start = variance_range.start; + } + parser_advance(state); + break; + case kOUT: + variance = ID2SYM(rb_intern("covariant")); + variance_range = state->current_token.range; + if (null_position_p(all_range.start)) { + all_range.start = variance_range.start; } + parser_advance(state); + break; + default: + // nop + break; + } + } - if (state->next_token.type == kIN || state->next_token.type == kOUT) { - switch (state->next_token.type) { - case kIN: - variance = ID2SYM(rb_intern("contravariant")); - break; - case kOUT: - variance = ID2SYM(rb_intern("covariant")); - break; - default: - rbs_abort(); - } + range name_range = state->current_token.range; + rbs_constant_id_t id = rbs_constant_pool_insert_shared( + &state->constant_pool, + (const uint8_t *) peek_token(state->lexstate, state->current_token), + token_bytes(state->current_token) + ); - parser_advance(state); - variance_range = state->current_token.range; - } - } + if (null_position_p(all_range.start)) { + all_range.start = name_range.start; + } + all_range.end = name_range.end; + VALUE name = ID2SYM(INTERN_TOKEN(state, state->current_token)); + parser_insert_typevar(state, id); + + range upper_bound_range = NULL_RANGE; + range upper_bound_operator_range = NULL_RANGE; + VALUE upper_bound = Qnil; + + if (state->next_token.type == pLT) { + parser_advance(state); + upper_bound_operator_range = state->current_token.range; + upper_bound = parse_type(state); + upper_bound_range = (range) { + .start = upper_bound_operator_range.start, + .end = state->current_token.range.end + }; + all_range.end = state->current_token.range.end; + } - parser_advance_assert(state, tUIDENT); - range name_range = state->current_token.range; + range default_type_range = NULL_RANGE; + range default_type_operator_range = NULL_RANGE; + VALUE default_type = Qnil; - rbs_constant_id_t id = rbs_constant_pool_insert_shared( - &state->constant_pool, - (const uint8_t *) peek_token(state->lexstate, state->current_token), - token_bytes(state->current_token) + if (state->next_token.type == pEQ) { + parser_advance(state); + default_type_operator_range = state->current_token.range; + default_type = parse_type(state); + default_type_range = (range) { + .start = default_type_operator_range.start, + .end = state->current_token.range.end + }; + all_range.end = state->current_token.range.end; + } else { + if (!required_param_allowed) { + raise_syntax_error( + state, + state->current_token, + "required type parameter is not allowed after optional type parameter" ); + } + } - VALUE name = ID2SYM(INTERN_TOKEN(state, state->current_token)); + ranges[0] = all_range; + ranges[1] = unchecked_range; + ranges[2] = variance_range; + ranges[3] = name_range; + ranges[4] = upper_bound_operator_range; + ranges[5] = upper_bound_range; + ranges[6] = default_type_operator_range; + ranges[7] = default_type_range; - parser_insert_typevar(state, id); + values[0] = unchecked; + values[1] = variance; + values[2] = name; + values[3] = upper_bound; + values[4] = default_type; +} - range upper_bound_range = NULL_RANGE; - if (state->next_token.type == pLT) { - parser_advance(state); - upper_bound_range.start = state->current_token.range.start; - upper_bound = parse_type(state); - upper_bound_range.end = state->current_token.range.end; - } +/* + type_params ::= {} `[` type_param `,` ... <`]`> + | {<>} - range default_type_range = NULL_RANGE; - if (module_type_params) { - if (state->next_token.type == pEQ) { - parser_advance(state); + type_param ::= kUNCHECKED? (kIN|kOUT|) tUIDENT upper_bound? default_type? (module_type_params == true) - default_type_range.start = state->current_token.range.start; - default_type = parse_type(state); - default_type_range.end = state->current_token.range.end; + type_param ::= tUIDENT upper_bound? default_type? (module_type_params == false) +*/ +static VALUE parse_type_params(parserstate *state, range *rg, bool module_type_params) { + VALUE params = EMPTY_ARRAY; - required_param_allowed = false; - } else { - if (!required_param_allowed) { - raise_syntax_error( - state, - state->current_token, - "required type parameter is not allowed after optional type parameter" - ); - } - } - } + bool required_param_allowed = true; + + if (state->next_token.type == pLBRACKET) { + rg->start = state->next_token.range.start; + + parser_advance(state); + + while (true) { + range ranges[8]; + VALUE values[5]; - param_range.end = state->current_token.range.end; + parse_type_param(state, module_type_params, required_param_allowed, ranges, values); + + range param_range = ranges[0]; + range unchecked_range = ranges[1]; + range variance_range = ranges[2]; + range name_range = ranges[3]; + range upper_bound_range = ranges[5]; + range default_type_range = ranges[7]; + + VALUE unchecked = values[0]; + VALUE variance = RB_TEST(values[1]) ? values[1] : ID2SYM(rb_intern("invariant")); + VALUE name = values[2]; + VALUE upper_bound = values[3]; + VALUE default_type = values[4]; VALUE location = rbs_new_location(state->buffer, param_range); rbs_loc *loc = rbs_check_location(location); @@ -1249,6 +1338,14 @@ static VALUE parse_type_params(parserstate *state, range *rg, bool module_type_p VALUE param = rbs_ast_type_param(name, variance, upper_bound, default_type, unchecked, location); + if (RB_TEST(default_type)) { + required_param_allowed = false; + } + + if (RB_TEST(unchecked)) { + rb_funcall(param, rb_intern("unchecked!"), 0); + } + melt_array(¶ms); rb_ary_push(params, param); @@ -1764,22 +1861,48 @@ static VALUE parse_member_def(parserstate *state, bool instance_only, bool accep * class_instance_name ::= {} * | {} class_name `[` type args <`]`> * - * @param kind + * @param ranges + * An 5-elements array of ranges to store: + * - 0: The range of the whole name. + * - 1: The range of the type name. + * - 2: The range of the open bracket. + * - 3: The range of the close bracket. + * - 4: The range of the type args. * */ -void class_instance_name(parserstate *state, TypeNameKind kind, VALUE *name, VALUE *args, range *name_range, range *args_range) { +void class_instance_name(parserstate *state, TypeNameKind kind, VALUE *name, VALUE *args, range ranges[5]) { parser_advance(state); - *name = parse_type_name(state, kind, name_range); + range all_range; + range name_range; + range open_bracket_range; + range close_bracket_range; + range args_range; + + *name = parse_type_name(state, kind, &name_range); + all_range = name_range; if (state->next_token.type == pLBRACKET) { parser_advance(state); - args_range->start = state->current_token.range.start; + open_bracket_range = state->current_token.range; parse_type_list(state, pRBRACKET, args); parser_advance_assert(state, pRBRACKET); - args_range->end = state->current_token.range.end; + close_bracket_range = state->current_token.range; + args_range = (range) { + .start = open_bracket_range.start, + .end = close_bracket_range.end, + }; + all_range.end = close_bracket_range.end; } else { - *args_range = NULL_RANGE; + open_bracket_range = NULL_RANGE; + close_bracket_range = NULL_RANGE; + args_range = NULL_RANGE; } + + ranges[0] = all_range; + ranges[1] = name_range; + ranges[2] = open_bracket_range; + ranges[3] = close_bracket_range; + ranges[4] = args_range; } /** @@ -1827,13 +1950,14 @@ static VALUE parse_mixin_member(parserstate *state, bool from_interface, positio VALUE name; VALUE args = EMPTY_ARRAY; - range name_range; - range args_range = NULL_RANGE; + range ranges[5]; class_instance_name( state, from_interface ? INTERFACE_NAME : (INTERFACE_NAME | CLASS_NAME), - &name, &args, &name_range, &args_range + &name, &args, ranges ); + range name_range = ranges[1]; + range args_range = ranges[4]; parser_pop_typevar_table(state); @@ -1942,7 +2066,9 @@ static VALUE parse_variable_member(parserstate *state, position comment_pos, VAL switch (state->current_token.type) { - case tAIDENT: { + case tAIDENT: + case kATRBS: + { range name_range = state->current_token.range; VALUE name = ID2SYM(INTERN_TOKEN(state, state->current_token)); @@ -1989,7 +2115,15 @@ static VALUE parse_variable_member(parserstate *state, position comment_pos, VAL }; parser_advance_assert(state, pDOT); - parser_advance_assert(state, tAIDENT); + if (state->next_token.type == tAIDENT || state->next_token.type == kATRBS) { + parser_advance(state); + } else { + raise_syntax_error( + state, + state->current_token, + "expected a instance variable name" + ); + } range name_range = state->current_token.range; VALUE name = ID2SYM(INTERN_TOKEN(state, state->current_token)); @@ -2242,31 +2376,53 @@ static VALUE parse_interface_decl(parserstate *state, position comment_pos, VALU ); } +/** + module_self_type ::= {} + | {} module_name `[` type_list <`]`> + + + * @param state + * @param module_name Pointer to VALUE to store a TypeName object + * @param type_args Pointer to an array to store type arguments + * @param ranges An array of 4-range to store three ranges: module name, open paren, type args, and the close paren. + */ +static void parse_module_self(parserstate *state, VALUE *module_name, VALUE *type_args, range ranges[4]) { + parser_advance(state); + + ranges[0].start = state->current_token.range.start; + *module_name = parse_type_name(state, CLASS_NAME | INTERFACE_NAME, &ranges[0]); + ranges[0].end = state->current_token.range.end; + + if (state->next_token.type == pLBRACKET) { + parser_advance(state); + ranges[1] = state->current_token.range; + ranges[2].start = state->next_token.range.start; + parse_type_list(state, pRBRACKET, type_args); + ranges[2].end = state->next_token.range.end; + parser_advance_assert(state, pRBRACKET); + ranges[3] = state->current_token.range; + } else { + ranges[1] = NULL_RANGE; + ranges[2] = NULL_RANGE; + ranges[3] = NULL_RANGE; + } +} + /* module_self_types ::= {`:`} module_self_type `,` ... `,` - - module_self_type ::= - | module_name `[` type_list <`]`> */ static void parse_module_self_types(parserstate *state, VALUE *array) { while (true) { - parser_advance(state); + range ranges[4]; + VALUE module_name; + VALUE type_args = EMPTY_ARRAY; - range self_range; - self_range.start = state->current_token.range.start; - range name_range; - VALUE module_name = parse_type_name(state, CLASS_NAME | INTERFACE_NAME, &name_range); - self_range.end = name_range.end; + range self_range = state->current_token.range; + parse_module_self(state, &module_name, &type_args, ranges); - VALUE args = EMPTY_ARRAY; - range args_range = NULL_RANGE; - if (state->next_token.type == pLBRACKET) { - parser_advance(state); - args_range.start = state->current_token.range.start; - parse_type_list(state, pRBRACKET, &args); - parser_advance(state); - self_range.end = args_range.end = state->current_token.range.end; - } + range name_range = ranges[0]; + range args_range = ranges[2]; + self_range.end = state->current_token.range.end; VALUE location = rbs_new_location(state->buffer, self_range); rbs_loc *loc = rbs_check_location(location); @@ -2274,7 +2430,7 @@ static void parse_module_self_types(parserstate *state, VALUE *array) { rbs_loc_add_required_child(loc, INTERN("name"), name_range); rbs_loc_add_optional_child(loc, INTERN("args"), args_range); - VALUE self_type = rbs_ast_decl_module_self(module_name, args, location); + VALUE self_type = rbs_ast_decl_module_self(module_name, type_args, location); melt_array(array); rb_ary_push(*array, self_type); @@ -2289,7 +2445,8 @@ static void parse_module_self_types(parserstate *state, VALUE *array) { static VALUE parse_nested_decl(parserstate *state, const char *nested_in, position annot_pos, VALUE annotations); /* - module_members ::= {} ... kEND + module_members ::= {} module_member ... kEND + | {<>} kEND module_member ::= def_member | variable_member @@ -2299,10 +2456,10 @@ static VALUE parse_nested_decl(parserstate *state, const char *nested_in, positi | `public` | `private` */ -static VALUE parse_module_members(parserstate *state) { +static VALUE parse_module_members(parserstate *state, enum TokenType end_token) { VALUE members = EMPTY_ARRAY; - while (state->next_token.type != kEND) { + while (state->next_token.type != end_token) { VALUE annotations = EMPTY_ARRAY; position annot_pos = NullPosition; parse_annotations(state, &annotations, &annot_pos); @@ -2330,6 +2487,7 @@ static VALUE parse_module_members(parserstate *state) { } case tAIDENT: + case kATRBS: case tA2IDENT: case kSELF: { member = parse_variable_member(state, annot_pos, annotations); @@ -2404,7 +2562,7 @@ static VALUE parse_module_decl0(parserstate *state, range keyword_range, VALUE m self_types_range = NULL_RANGE; } - VALUE members = parse_module_members(state); + VALUE members = parse_module_members(state, kEND); parser_advance_assert(state, kEND); range end_range = state->current_token.range; @@ -2471,7 +2629,8 @@ static VALUE parse_module_decl(parserstate *state, position comment_pos, VALUE a return rbs_ast_decl_module_alias(module_name, old_name, location, comment, annotations); } else { - return parse_module_decl0(state, keyword_range, module_name, module_name_range, comment, annotations); + VALUE decl = parse_module_decl0(state, keyword_range, module_name, module_name_range, comment, annotations); + return decl; } } @@ -2488,8 +2647,10 @@ static VALUE parse_class_decl_super(parserstate *state, range *lt_range) { VALUE name; VALUE args = EMPTY_ARRAY; - range name_range, args_range; - class_instance_name(state, CLASS_NAME, &name, &args, &name_range, &args_range); + range ranges[5]; + class_instance_name(state, CLASS_NAME, &name, &args, ranges); + range name_range = ranges[1]; + range args_range = ranges[4]; super_range.end = state->current_token.range.end; @@ -2521,7 +2682,7 @@ static VALUE parse_class_decl0(parserstate *state, range keyword_range, VALUE na range lt_range; VALUE super = parse_class_decl_super(state, <_range); - VALUE members = parse_module_members(state); + VALUE members = parse_module_members(state, kEND); parser_advance_assert(state, kEND); @@ -2853,6 +3014,605 @@ VALUE parse_signature(parserstate *state) { return ret; } +/* +inline_comment ::= {} // nil + | {} <`--` any string> // comment +*/ +VALUE parse_inline_comment(parserstate *state) { + if (state->next_token.type != pMINUS2) { + return Qnil; + } + + parser_advance(state); + + return rbs_new_location(state->buffer, state->current_token.range); +} + +/* + annotated_method_type ::= {} annotations + + Returns a pair of an array of annotation and method type. +*/ +VALUE parse_annotated_method_type(parserstate *state) { + VALUE annotations = EMPTY_ARRAY; + position annot_pos = NullPosition; + + parse_annotations(state, &annotations, &annot_pos); + + VALUE method_type = parse_method_type(state); + + return rbs_ast_ruby_annotation_method_types_annotation_overload(annotations, method_type); +} + +/** + inline_skip_annotation ::= {} <`skip`> + | {} `skip` + */ +VALUE parse_inline_skip_annotation(parserstate *state, range rbs_range) { + range skip_range = state->next_token.range; + parser_advance(state); + + VALUE rbs_location = rbs_new_location(state->buffer, rbs_range); + VALUE skip_location = rbs_new_location(state->buffer, skip_range); + + VALUE comment = parse_inline_comment(state); + + return rbs_ast_ruby_annotation_skip_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + comment, + rbs_location, + skip_location + ); +} + +/** + inline_override_annotation ::= {} <`override`> + */ +VALUE parse_override_annotation(parserstate *state, range rbs_range) { + range override_range = state->next_token.range; + parser_advance(state); + + return rbs_ast_ruby_annotation_override_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, override_range) + ); +} + +/** + inline_return_type_annotation ::= {} `return` `:` + | {} `return` `:` type + */ +VALUE parse_inline_return_type_annotation(parserstate *state, range rbs_range) { + range return_range = state->next_token.range; + parser_advance(state); + + range colon_range = state->next_token.range; + parser_advance(state); + + VALUE type = parse_type(state); + VALUE comment = parse_inline_comment(state); + + return rbs_ast_ruby_annotation_return_type_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, return_range), + rbs_new_location(state->buffer, colon_range), + type, + comment + ); +} + +/** + * inline_block_param_type_annotation ::= {} `& ident `:` + * | {} `& ident `:` block + */ +VALUE parse_inline_block_param_type_annotation(parserstate *state, range rbs_range) { + range operator_range = state->next_token.range; + parser_advance(state); + + range param_name_range = NULL_RANGE; + if (state->next_token.type != pCOLON) { + param_name_range = state->next_token.range; + parser_advance(state); + } + + range colon_range = state->next_token.range; + parser_advance(state); + + range question_mark_range = NULL_RANGE; + if (state->next_token.type == pQUESTION) { + question_mark_range = state->next_token.range; + parser_advance(state); + } + + VALUE function = Qnil; + VALUE function_self_type = Qnil; + parse_function(state, &function, NULL, &function_self_type); + + VALUE comment = parse_inline_comment(state); + + position end_pos = state->current_token.range.end; + + return rbs_ast_ruby_annotation_block_param_type_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = end_pos }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, operator_range), + null_range_p(param_name_range) ? Qnil : rbs_new_location(state->buffer, param_name_range), + rbs_new_location(state->buffer, colon_range), + null_range_p(question_mark_range) ? Qnil : rbs_new_location(state->buffer, question_mark_range), + rbs_block(function, null_range_p(question_mark_range) ? Qtrue : Qfalse, function_self_type), + comment + ); +} + +/** + inline-generic-annotation ::= {} `generic` + */ +VALUE parse_generic_annotation(parserstate *state, range rbs_range) { + range generic_range = state->next_token.range; + parser_advance_assert(state, kGENERIC); + + range ranges[8]; + VALUE values[5]; + + parse_type_param(state, true, true, ranges, values); + VALUE comment = parse_inline_comment(state); + + range annotation_range = { + .start = rbs_range.start, + .end = state->current_token.range.end + }; + range unchecked_range = ranges[1]; + range variance_range = ranges[2]; + range name_range = ranges[3]; + range upper_bound_operator_range = ranges[4]; + range default_type_operator_range = ranges[6]; + + VALUE upper_bound = values[3]; + VALUE default_type = values[4]; + + return rbs_ast_ruby_annotation_generic_annotation( + rbs_new_location(state->buffer, annotation_range), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, generic_range), + null_range_p(unchecked_range) ? Qnil : rbs_new_location(state->buffer, unchecked_range), + null_range_p(variance_range) ? Qnil : rbs_new_location(state->buffer, variance_range), + rbs_new_location(state->buffer, name_range), + null_range_p(upper_bound_operator_range) ? Qnil : rbs_new_location(state->buffer, upper_bound_operator_range), + upper_bound, + null_range_p(default_type_operator_range) ? Qnil : rbs_new_location(state->buffer, default_type_operator_range), + default_type, + comment + ); +} + +/** + * inherits ::= {} `inherits` + */ +static VALUE parse_inline_inherits_annotation(parserstate *state, range rbs_range) { + range annotation_range = rbs_range; + + parser_advance_assert(state, kINHERITS); + range inherits_range = state->current_token.range; + + VALUE name; + VALUE args = EMPTY_ARRAY; + range ranges[5]; + class_instance_name(state, CLASS_NAME, &name, &args, ranges); + range name_range = ranges[1]; + range open_bracket_range = ranges[2]; + range close_bracket_range = ranges[3]; + + VALUE comment = parse_inline_comment(state); + + annotation_range.end = state->current_token.range.end; + + return rbs_ast_ruby_annotation_inherits_annotation( + rbs_new_location(state->buffer, annotation_range), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, inherits_range), + name, + rbs_new_location(state->buffer, name_range), + null_range_p(open_bracket_range) ? Qnil : rbs_new_location(state->buffer, open_bracket_range), + args, + null_range_p(close_bracket_range) ? Qnil : rbs_new_location(state->buffer, close_bracket_range), + comment + ); +} + +/** + * self_ivar_annotation ::= {} `self` `.` ivar_name `:` + */ +static VALUE parse_inline_self_ivar_annotation(parserstate *state, range rbs_range) { + range annotation_range = rbs_range; + + parser_advance_assert(state, kSELF); + range self_range = state->current_token.range; + + parser_advance_assert(state, pDOT); + range dot_range = state->current_token.range; + + range var_name_range = NULL_RANGE; + if (state->next_token.type == tAIDENT || state->next_token.type == kATRBS) { + parser_advance(state); + var_name_range = state->current_token.range; + } else { + raise_syntax_error(state, state->next_token, "instance variable name is expected"); + } + + parser_advance_assert(state, pCOLON); + range colon_range = state->current_token.range; + + VALUE type = parse_type(state); + VALUE comment = parse_inline_comment(state); + + annotation_range.end = state->current_token.range.end; + + return rbs_ast_ruby_annotation_class_ivar_type_annotation( + rbs_new_location(state->buffer, annotation_range), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, self_range), + rbs_new_location(state->buffer, dot_range), + rbs_new_location(state->buffer, var_name_range), + rbs_new_location(state->buffer, colon_range), + type, + comment + ); +} + +VALUE parse_inline_annotation(parserstate *state) { + switch (state->next_token.type) { + case kATRBS: { + // @rbs + range rbs_range = state->next_token.range; + parser_advance(state); + + switch (state->next_token.type) { + case tLIDENT: + KEYWORD_CASES { + if (state->next_token.type == kSKIP && state->next_token2.type != pCOLON) { + return parse_inline_skip_annotation(state, rbs_range); + } + if (state->next_token.type == kRETURN && state->next_token2.type == pCOLON) { + return parse_inline_return_type_annotation(state, rbs_range); + } + if (state->next_token.type == kOVERRIDE && state->next_token2.type != pCOLON) { + return parse_override_annotation(state, rbs_range); + } + if (state->next_token.type == kGENERIC && state->next_token2.type != pCOLON) { + return parse_generic_annotation(state, rbs_range); + } + if (state->next_token.type == kINHERITS && state->next_token2.type != pCOLON) { + return parse_inline_inherits_annotation(state, rbs_range); + } + if (state->next_token.type == kSELF && state->next_token2.type == pDOT) { + return parse_inline_self_ivar_annotation(state, rbs_range); + } + + // @rbs x: type + + range param_name_range = state->next_token.range; + parser_advance(state); + + range colon_range = state->next_token.range; + parser_advance_assert(state, pCOLON); + + VALUE type = parse_type(state); + VALUE comment = parse_inline_comment(state); + + return rbs_ast_ruby_annotation_param_type_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, param_name_range), + rbs_new_location(state->buffer, colon_range), + type, + comment + ); + } + case pSTAR: { + // @rbs *args: String + range operator_range = state->next_token.range; + parser_advance(state); + + range param_name_range = NULL_RANGE; + if (state->next_token.type != pCOLON) { + param_name_range = state->next_token.range; + parser_advance(state); + } + + range colon_range = state->next_token.range; + parser_advance_assert(state, pCOLON); + + VALUE type = parse_type(state); + VALUE comment = parse_inline_comment(state); + + return rbs_ast_ruby_annotation_splat_param_type_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, operator_range), + null_range_p(param_name_range) ? Qnil : rbs_new_location(state->buffer, param_name_range), + rbs_new_location(state->buffer, colon_range), + type, + comment + ); + } + case pSTAR2: { + // @rbs **args: String + range operator_range = state->next_token.range; + parser_advance(state); + + range param_name_range = NULL_RANGE; + if (state->next_token.type != pCOLON) { + param_name_range = state->next_token.range; + parser_advance(state); + } + + range colon_range = state->next_token.range; + parser_advance_assert(state, pCOLON); + + VALUE type = parse_type(state); + VALUE comment = parse_inline_comment(state); + + return rbs_ast_ruby_annotation_double_splat_param_type_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, operator_range), + null_range_p(param_name_range) ? Qnil : rbs_new_location(state->buffer, param_name_range), + rbs_new_location(state->buffer, colon_range), + type, + comment + ); + } + case pAMP: { + // @rbs &block: type + return parse_inline_block_param_type_annotation(state, rbs_range); + } + case tANNOTATION: { + // @rbs %a{foo} + + position annot_pos = NullPosition; + + VALUE annotations = EMPTY_ARRAY; + parse_annotations(state, &annotations, &annot_pos); + + if (state->next_token.type == pEOF) { + return rbs_ast_ruby_annotation_rbs_annotation_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + annotations + ); + } else { + VALUE method_type = parse_method_type(state); + + VALUE overloads = rb_ary_new(); + VALUE bar_locations = rb_ary_new(); + rb_ary_push(overloads, rbs_ast_ruby_annotation_method_types_annotation_overload(annotations, method_type)); + + if (state->next_token.type == pBAR) { + rb_ary_push(bar_locations, rbs_new_location(state->buffer, state->next_token.range)); + + while (true) { + VALUE overload = parse_annotated_method_type(state); + rb_ary_push(overloads, overload); + + if (state->next_token.type == pBAR) { + rb_ary_push(bar_locations, rbs_new_location(state->buffer, state->next_token.range)); + parser_advance(state); + } else { + break; + } + } + } + + position end_pos = state->current_token.range.end; + + return rbs_ast_ruby_annotation_method_types_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = end_pos }), + rbs_new_location(state->buffer, rbs_range), + overloads, + bar_locations + ); + } + } + case kMODULESELF: { + // @rbs module-self TypeName `[` args `]` -- comment + + parser_advance_assert(state, kMODULESELF); + + range module_self_range = state->current_token.range; + + VALUE type_name; + VALUE type_args = EMPTY_ARRAY; + range ranges[4]; + + parse_module_self(state, &type_name, &type_args, ranges); + + range module_name_range = ranges[0]; + range open_paren_range = ranges[1]; + range close_paren_range = ranges[3]; + + VALUE comment = parse_inline_comment(state); + + return rbs_ast_ruby_annotation_module_self_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, module_self_range), + type_name, + rbs_new_location(state->buffer, module_name_range), + null_range_p(open_paren_range) ? Qnil : rbs_new_location(state->buffer, open_paren_range), + type_args, + null_range_p(close_paren_range) ? Qnil : rbs_new_location(state->buffer, close_paren_range), + comment + ); + } + case tAIDENT: + case kATRBS: + { + // @rbs a: type + + range var_name_range = state->next_token.range; + parser_advance(state); + + range colon_range = state->next_token.range; + parser_advance_assert(state, pCOLON); + + VALUE type = parse_type(state); + VALUE comment = parse_inline_comment(state); + + return rbs_ast_ruby_annotation_ivar_type_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, var_name_range), + rbs_new_location(state->buffer, colon_range), + type, + comment + ); + } + case tA2IDENT: { + // @@rbs a: type + + range var_name_range = state->next_token.range; + parser_advance(state); + + range colon_range = state->next_token.range; + parser_advance_assert(state, pCOLON); + + VALUE type = parse_type(state); + VALUE comment = parse_inline_comment(state); + + return rbs_ast_ruby_annotation_class_var_type_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, rbs_range), + rbs_new_location(state->buffer, var_name_range), + rbs_new_location(state->buffer, colon_range), + type, + comment + ); + } + default: { + VALUE overloads = rb_ary_new(); + VALUE bar_locations = rb_ary_new(); + + while (true) { + VALUE overload = parse_annotated_method_type(state); + rb_ary_push(overloads, overload); + + if (state->next_token.type == pBAR) { + rb_ary_push(bar_locations, rbs_new_location(state->buffer, state->next_token.range)); + parser_advance(state); + } else { + break; + } + } + + position end_pos = state->current_token.range.end; + + return rbs_ast_ruby_annotation_method_types_annotation( + rbs_new_location(state->buffer, (range) { .start = rbs_range.start, .end = end_pos }), + rbs_new_location(state->buffer, rbs_range), + overloads, + bar_locations + ); + } + } + } + case kATRBSB: { + // @rbs! + range prefix_range = state->next_token.range; + parser_advance_assert(state, kATRBSB); + + VALUE members = parse_module_members(state, pEOF); + + return rbs_ast_ruby_annotation_embedded_rbs_annotation( + rbs_new_location(state->buffer, (range) { .start = prefix_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, prefix_range), + members + ); + } + case pCOLON: { + // : + range colon_range = state->next_token.range; + parser_advance(state); + + VALUE annotations = EMPTY_ARRAY; + position annot_pos = NullPosition; + + if (state->next_token.type == tANNOTATION) { + parse_annotations(state, &annotations, &annot_pos); + } + + switch (state->next_token.type) { + case pLPAREN: + case pLBRACKET: + case pLBRACE: + case tANNOTATION: { + VALUE method_type = parse_method_type(state); + + range location_range = { + .start = colon_range.start, + .end = state->current_token.range.end + }; + + return rbs_ast_ruby_annotation_colon_method_type_annotation( + rbs_new_location(state->buffer, location_range), + rbs_new_location(state->buffer, colon_range), + annotations, + method_type + ); + default: { + raise_syntax_error( + state, + state->next_token, + "unexpected token for method type annotation" + ); + } + } + } + } + } +} + +static VALUE parse_inline_assertion(parserstate *state) { + range prefix_range = state->next_token.range; + + switch (state->next_token.type) { + case pCOLON: { + parser_advance(state); + + VALUE type = parse_type(state); + + return rbs_ast_ruby_annotation_node_type_assertion( + rbs_new_location(state->buffer, (range) { .start = prefix_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, prefix_range), + type + ); + } + case pLBRACKET: { + parser_advance_assert(state, pLBRACKET); + + VALUE types = rb_ary_new(); + parse_type_list(state, pRBRACKET, &types); + + parser_advance_assert(state, pRBRACKET); + + return rbs_ast_ruby_annotation_node_application( + rbs_new_location(state->buffer, (range) { .start = prefix_range.start, .end = state->current_token.range.end }), + rbs_new_location(state->buffer, prefix_range), + types, + rbs_new_location(state->buffer, state->current_token.range) + ); + } + default: { + raise_syntax_error( + state, + state->next_token, + "unexpected token for inline assertion" + ); + } + } +} + struct parse_type_arg { parserstate *parser; VALUE require_eof; @@ -2942,6 +3702,34 @@ rbsparser_parse_signature(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_p return rb_ensure(parse_signature_try, (VALUE)parser, ensure_free_parser, (VALUE)parser); } +static VALUE parse_inline_try(VALUE a) { + parserstate *parser = (parserstate *)a; + return parse_inline_annotation(parser); +} + +static VALUE +rbsparser_parse_inline(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables) +{ + VALUE string = rb_funcall(buffer, rb_intern("content"), 0); + StringValue(string); + lexstate *lexer = alloc_lexer(string, FIX2INT(start_pos), FIX2INT(end_pos)); + parserstate *parser = alloc_parser(buffer, lexer, FIX2INT(start_pos), FIX2INT(end_pos), variables); + return rb_ensure(parse_inline_try, (VALUE)parser, ensure_free_parser, (VALUE)parser); +} + +static VALUE parse_inline_assertion_try(VALUE a) { + parserstate *parser = (parserstate *)a; + return parse_inline_assertion(parser); +} + +static VALUE rbsparser_parse_inline_assertion(VALUE self, VALUE buffer, VALUE start_pos, VALUE end_pos, VALUE variables) { + VALUE string = rb_funcall(buffer, rb_intern("content"), 0); + StringValue(string); + lexstate *lexer = alloc_lexer(string, FIX2INT(start_pos), FIX2INT(end_pos)); + parserstate *parser = alloc_parser(buffer, lexer, FIX2INT(start_pos), FIX2INT(end_pos), variables); + return rb_ensure(parse_inline_assertion_try, (VALUE)parser, ensure_free_parser, (VALUE)parser); +} + static VALUE rbsparser_lex(VALUE self, VALUE buffer, VALUE end_pos) { VALUE string = rb_funcall(buffer, rb_intern("content"), 0); @@ -2978,5 +3766,7 @@ void rbs__init_parser(void) { rb_define_singleton_method(RBS_Parser, "_parse_type", rbsparser_parse_type, 5); rb_define_singleton_method(RBS_Parser, "_parse_method_type", rbsparser_parse_method_type, 5); rb_define_singleton_method(RBS_Parser, "_parse_signature", rbsparser_parse_signature, 3); + rb_define_singleton_method(RBS_Parser, "_parse_inline", rbsparser_parse_inline, 4); + rb_define_singleton_method(RBS_Parser, "_parse_inline_assertion", rbsparser_parse_inline_assertion, 4); rb_define_singleton_method(RBS_Parser, "_lex", rbsparser_lex, 2); } diff --git a/include/rbs/constants.h b/include/rbs/constants.h index 995f5f923..c306feb64 100644 --- a/include/rbs/constants.h +++ b/include/rbs/constants.h @@ -14,6 +14,9 @@ extern VALUE RBS_AST; extern VALUE RBS_AST_Declarations; extern VALUE RBS_AST_Directives; extern VALUE RBS_AST_Members; +extern VALUE RBS_AST_Ruby; +extern VALUE RBS_AST_Ruby_Annotation; +extern VALUE RBS_AST_Ruby_Annotation_MethodTypesAnnotation; extern VALUE RBS_Types; extern VALUE RBS_Types_Bases; extern VALUE RBS_ParsingError; @@ -47,6 +50,26 @@ extern VALUE RBS_AST_Members_MethodDefinition_Overload; extern VALUE RBS_AST_Members_Prepend; extern VALUE RBS_AST_Members_Private; extern VALUE RBS_AST_Members_Public; +extern VALUE RBS_AST_Ruby_Annotation_BlockParamTypeAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_ClassIvarTypeAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_ClassVarTypeAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_ColonMethodTypeAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_DoubleSplatParamTypeAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_EmbeddedRBSAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_GenericAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_InheritsAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_IvarTypeAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_MethodTypesAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_MethodTypesAnnotation_Overload; +extern VALUE RBS_AST_Ruby_Annotation_ModuleSelfAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_NodeApplication; +extern VALUE RBS_AST_Ruby_Annotation_NodeTypeAssertion; +extern VALUE RBS_AST_Ruby_Annotation_OverrideAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_ParamTypeAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_RBSAnnotationAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_ReturnTypeAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_SkipAnnotation; +extern VALUE RBS_AST_Ruby_Annotation_SplatParamTypeAnnotation; extern VALUE RBS_AST_TypeParam; extern VALUE RBS_MethodType; extern VALUE RBS_Namespace; diff --git a/include/rbs/ruby_objs.h b/include/rbs/ruby_objs.h index 102e57d40..20e42a411 100644 --- a/include/rbs/ruby_objs.h +++ b/include/rbs/ruby_objs.h @@ -39,6 +39,26 @@ VALUE rbs_ast_members_method_definition_overload(VALUE annotations, VALUE method VALUE rbs_ast_members_prepend(VALUE name, VALUE args, VALUE annotations, VALUE location, VALUE comment); VALUE rbs_ast_members_private(VALUE location); VALUE rbs_ast_members_public(VALUE location); +VALUE rbs_ast_ruby_annotation_block_param_type_annotation(VALUE location, VALUE prefix_location, VALUE operator_location, VALUE param_name_location, VALUE colon_location, VALUE question_mark_location, VALUE block, VALUE comment); +VALUE rbs_ast_ruby_annotation_class_ivar_type_annotation(VALUE location, VALUE prefix_location, VALUE self_location, VALUE dot_location, VALUE var_name_location, VALUE colon_location, VALUE type, VALUE comment); +VALUE rbs_ast_ruby_annotation_class_var_type_annotation(VALUE location, VALUE prefix_location, VALUE var_name_location, VALUE colon_location, VALUE type, VALUE comment); +VALUE rbs_ast_ruby_annotation_colon_method_type_annotation(VALUE location, VALUE prefix_location, VALUE annotations, VALUE method_type); +VALUE rbs_ast_ruby_annotation_double_splat_param_type_annotation(VALUE location, VALUE prefix_location, VALUE operator_location, VALUE param_name_location, VALUE colon_location, VALUE type, VALUE comment); +VALUE rbs_ast_ruby_annotation_embedded_rbs_annotation(VALUE location, VALUE prefix_location, VALUE members); +VALUE rbs_ast_ruby_annotation_generic_annotation(VALUE location, VALUE prefix_location, VALUE generic_location, VALUE unchecked_location, VALUE variance_location, VALUE name_location, VALUE upper_bound_operator_location, VALUE upper_bound, VALUE default_type_operator_location, VALUE default_type, VALUE comment); +VALUE rbs_ast_ruby_annotation_inherits_annotation(VALUE location, VALUE prefix_location, VALUE inherits_location, VALUE type_name, VALUE type_name_location, VALUE open_paren_location, VALUE type_args, VALUE close_paren_location, VALUE comment); +VALUE rbs_ast_ruby_annotation_ivar_type_annotation(VALUE location, VALUE prefix_location, VALUE var_name_location, VALUE colon_location, VALUE type, VALUE comment); +VALUE rbs_ast_ruby_annotation_method_types_annotation(VALUE location, VALUE prefix_location, VALUE overloads, VALUE vertical_bar_locations); +VALUE rbs_ast_ruby_annotation_method_types_annotation_overload(VALUE annotations, VALUE method_type); +VALUE rbs_ast_ruby_annotation_module_self_annotation(VALUE location, VALUE prefix_location, VALUE module_self_location, VALUE type_name, VALUE type_name_location, VALUE open_paren_location, VALUE type_args, VALUE close_paren_location, VALUE comment); +VALUE rbs_ast_ruby_annotation_node_application(VALUE location, VALUE prefix_location, VALUE types, VALUE suffix_location); +VALUE rbs_ast_ruby_annotation_node_type_assertion(VALUE location, VALUE prefix_location, VALUE type); +VALUE rbs_ast_ruby_annotation_override_annotation(VALUE location, VALUE prefix_location, VALUE override_location); +VALUE rbs_ast_ruby_annotation_param_type_annotation(VALUE location, VALUE prefix_location, VALUE param_name_location, VALUE colon_location, VALUE type, VALUE comment); +VALUE rbs_ast_ruby_annotation_rbs_annotation_annotation(VALUE location, VALUE prefix_location, VALUE annotations); +VALUE rbs_ast_ruby_annotation_return_type_annotation(VALUE location, VALUE prefix_location, VALUE return_location, VALUE colon_location, VALUE return_type, VALUE comment); +VALUE rbs_ast_ruby_annotation_skip_annotation(VALUE location, VALUE comment, VALUE prefix_location, VALUE skip_location); +VALUE rbs_ast_ruby_annotation_splat_param_type_annotation(VALUE location, VALUE prefix_location, VALUE operator_location, VALUE param_name_location, VALUE colon_location, VALUE type, VALUE comment); VALUE rbs_ast_type_param(VALUE name, VALUE variance, VALUE upper_bound, VALUE default_type, VALUE unchecked, VALUE location); VALUE rbs_method_type(VALUE type_params, VALUE type, VALUE block, VALUE location); VALUE rbs_namespace(VALUE path, VALUE absolute); diff --git a/lib/rbs.rb b/lib/rbs.rb index d0b8d24d4..4a1b2fa5e 100644 --- a/lib/rbs.rb +++ b/lib/rbs.rb @@ -10,6 +10,7 @@ require "logger" require "tsort" require "strscan" +require "prism" require "rbs/errors" require "rbs/buffer" @@ -24,7 +25,15 @@ require "rbs/ast/members" require "rbs/ast/annotation" require "rbs/ast/visitor" +require "rbs/ast/ruby/comment_block" +require "rbs/ast/ruby/helpers" +require "rbs/ast/ruby/declarations" +require "rbs/ast/ruby/members" +require "rbs/ast/ruby/annotation" +require "rbs/source" require "rbs/environment" +require "rbs/environment/class_entry" +require "rbs/environment/module_entry" require "rbs/environment/use_map" require "rbs/environment_loader" require "rbs/builtin_names" @@ -57,6 +66,9 @@ require "rbs/type_alias_regularity" require "rbs/collection" +require "rbs/inline/comment_association" +require "rbs/inline_parser" + require "rbs_extension" require "rbs/parser_aux" require "rbs/location_aux" diff --git a/lib/rbs/ast/members.rb b/lib/rbs/ast/members.rb index 258a6df1f..fe0e9f8e0 100644 --- a/lib/rbs/ast/members.rb +++ b/lib/rbs/ast/members.rb @@ -199,6 +199,8 @@ def initialize(name:, args:, annotations:, location:, comment:) @comment = comment end + alias module_name name + def ==(other) other.is_a?(self.class) && other.name == name && other.args == args end diff --git a/lib/rbs/ast/ruby/annotation.rb b/lib/rbs/ast/ruby/annotation.rb new file mode 100644 index 000000000..3714dda66 --- /dev/null +++ b/lib/rbs/ast/ruby/annotation.rb @@ -0,0 +1,410 @@ +module RBS + module AST + module Ruby + module Annotation + class Base + attr_reader :location, :prefix_location + + def initialize(location, prefix_location) + @location = location + @prefix_location = prefix_location + end + end + + class NodeTypeAssertion < Base + attr_reader :type + + def initialize(location:, prefix_location:, type:) + super(location, prefix_location) + @type = type + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + type: type.map_type_name { yield(_1) } + ) #: self + end + end + + class NodeApplication < Base + attr_reader :types, :suffix_location + + def initialize(location:, prefix_location:, types:, suffix_location:) + super(location, prefix_location) + @types = types + @suffix_location = suffix_location + end + end + + class SkipAnnotation < Base + attr_reader :comment, :skip_location + + def initialize(location:, comment:, prefix_location:, skip_location:) + super(location, prefix_location) + @comment = comment + @skip_location = skip_location + end + end + + class ColonMethodTypeAnnotation < Base + attr_reader :annotations, :method_type + + def initialize(location:, prefix_location:, annotations:, method_type:) + super(location, prefix_location) + @annotations = annotations + @method_type = method_type + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + annotations: annotations, + method_type: method_type.map_type do |type| + type.map_type_name { yield(_1) } + end + ) #: self + end + end + + class MethodTypesAnnotation < Base + class Overload + attr_reader :annotations, :method_type + + def initialize(annotations:, method_type:) + @annotations = annotations + @method_type = method_type + end + end + + attr_reader :overloads, :vertical_bar_locations + + def initialize(location:, prefix_location:, overloads:, vertical_bar_locations:) + super(location, prefix_location) + @overloads = overloads + @vertical_bar_locations = vertical_bar_locations + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + overloads: overloads.map do |overload| + Overload.new( + annotations: overload.annotations, + method_type: overload.method_type.map_type do |type| + type.map_type_name { yield(_1) } + end + ) + end, + vertical_bar_locations: vertical_bar_locations + ) #: self + end + end + + class ReturnTypeAnnotation < Base + attr_reader :return_type, :colon_location, :return_location, :comment + + def initialize(location:, prefix_location:, colon_location:, return_type:, return_location:, comment:) + super(location, prefix_location) + @colon_location = colon_location + @return_type = return_type + @return_location = return_location + @comment = comment + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + colon_location: colon_location, + return_type: return_type.map_type_name { yield(_1) }, + return_location: return_location, + comment: comment + ) #: self + end + end + + class ParamTypeAnnotation < Base + attr_reader :param_name_location, :colon_location, :type, :comment + + def initialize(location:, prefix_location:, param_name_location:, colon_location:, type:, comment:) + super(location, prefix_location) + @param_name_location = param_name_location + @colon_location = colon_location + @type = type + @comment = comment + end + + def param_name + param_name_location.source.to_sym + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + param_name_location: param_name_location, + colon_location: colon_location, + type: type.map_type_name { yield(_1) }, + comment: comment + ) #: self + end + end + + class SplatParamTypeAnnotation < Base + attr_reader :operator_location, :param_name_location, :colon_location, :type, :comment + + def initialize(location:, prefix_location:, operator_location:, param_name_location:, colon_location:, type:, comment:) + super(location, prefix_location) + @operator_location = operator_location + @param_name_location = param_name_location + @colon_location = colon_location + @type = type + @comment = comment + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + operator_location: operator_location, + param_name_location: param_name_location, + colon_location: colon_location, + type: type.map_type_name { yield(_1) }, + comment: comment + ) #: self + end + end + + class DoubleSplatParamTypeAnnotation < Base + attr_reader :operator_location, :param_name_location, :colon_location, :type, :comment + + def initialize(location:, prefix_location:, operator_location:, param_name_location:, colon_location:, type:, comment:) + super(location, prefix_location) + @operator_location = operator_location + @param_name_location = param_name_location + @colon_location = colon_location + @type = type + @comment = comment + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + operator_location: operator_location, + param_name_location: param_name_location, + colon_location: colon_location, + type: type.map_type_name { yield(_1) }, + comment: comment + ) #: self + end + end + + class BlockParamTypeAnnotation < Base + attr_reader :operator_location, :param_name_location, :colon_location, :question_mark_location, :block, :comment + + def initialize(location:, prefix_location:, operator_location:, param_name_location:, colon_location:, question_mark_location:, block:, comment:) + super(location, prefix_location) + @operator_location = operator_location + @param_name_location = param_name_location + @colon_location = colon_location + @question_mark_location = question_mark_location + @block = block + @comment = comment + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + operator_location: operator_location, + param_name_location: param_name_location, + colon_location: colon_location, + question_mark_location: question_mark_location, + block: self.block.map_type {|type| type.map_type_name { yield(_1) } }, + comment: comment + ) #: self + end + end + + class OverrideAnnotation < Base + attr_reader :override_location + + def initialize(location:, prefix_location:, override_location:) + super(location, prefix_location) + @override_location = override_location + end + end + + class GenericAnnotation < Base + attr_reader :generic_location, :unchecked_location, :variance_location, :name_location, :upper_bound_operator_location + attr_reader :upper_bound, :default_type_operator_location, :default_type, :comment + + def initialize(location:, prefix_location:, generic_location:, unchecked_location:, variance_location:, name_location:, upper_bound_operator_location:, upper_bound:, default_type_operator_location:, default_type:, comment:) + super(location, prefix_location) + @generic_location = generic_location + @unchecked_location = unchecked_location + @variance_location = variance_location + @name_location = name_location + @upper_bound_operator_location = upper_bound_operator_location + @upper_bound = upper_bound + @default_type_operator_location = default_type_operator_location + @default_type = default_type + @comment = comment + end + + def unchecked? + unchecked_location ? true : false + end + + def variance + if variance_location + case variance_location.source + when "in" + :contravariant + when "out" + :covariant + else + raise + end + else + :invariant + end + end + + def upper_bound_location + if (op = upper_bound_operator_location) && (bound = upper_bound&.location) + Location.new(op.buffer, op.start_pos, bound.end_pos) + end + end + + def default_type_location + if (op = default_type_operator_location) && (default = default_type&.location) + Location.new(op.buffer, op.start_pos, default.end_pos) + end + end + + def name + name_location.source.to_sym + end + + def map_type_name(&block) + self.class.new( + location: location, + prefix_location: prefix_location, + generic_location: generic_location, + unchecked_location: unchecked_location, + variance_location: variance_location, + name_location: name_location, + upper_bound_operator_location: upper_bound_operator_location, + upper_bound: upper_bound&.map_type_name { yield(_1) }, + default_type_operator_location: default_type_operator_location, + default_type: default_type&.map_type_name { yield(_1) }, + comment: comment + ) #: self + end + end + + class RBSAnnotationAnnotation < Base + attr_reader :annotations + + def initialize(location:, prefix_location:, annotations:) + super(location, prefix_location) + @annotations = annotations + end + end + + class ModuleSelfAnnotation < Base + attr_reader :module_self_location + attr_reader :type_name, :type_name_location + attr_reader :open_paren_location + attr_reader :type_args + attr_reader :close_paren_location + attr_reader :comment + + def initialize(location:, prefix_location:, module_self_location:, type_name:, type_name_location:, open_paren_location:, type_args:, close_paren_location:, comment:) + super(location, prefix_location) + @module_self_location = module_self_location + @type_name = type_name + @type_name_location = type_name_location + @open_paren_location = open_paren_location + @type_args = type_args + @close_paren_location = close_paren_location + @comment = comment + end + end + + class InheritsAnnotation < Base + attr_reader :inherits_location + attr_reader :type_name, :type_name_location + attr_reader :open_paren_location + attr_reader :type_args + attr_reader :close_paren_location + attr_reader :comment + + def initialize(location:, prefix_location:, inherits_location:, type_name:, type_name_location:, open_paren_location:, type_args:, close_paren_location:, comment:) + super(location, prefix_location) + @inherits_location = inherits_location + @type_name = type_name + @type_name_location = type_name_location + @open_paren_location = open_paren_location + @type_args = type_args + @close_paren_location = close_paren_location + @comment = comment + end + end + + class IvarTypeAnnotation < Base + attr_reader :var_name_location, :colon_location, :type, :comment + + def initialize(location:, prefix_location:, var_name_location:, colon_location:, type:, comment:) + super(location, prefix_location) + @var_name_location = var_name_location + @colon_location = colon_location + @type = type + @comment = comment + end + end + + class ClassVarTypeAnnotation < Base + attr_reader :var_name_location, :colon_location, :type, :comment + + def initialize(location:, prefix_location:, var_name_location:, colon_location:, type:, comment:) + super(location, prefix_location) + @var_name_location = var_name_location + @colon_location = colon_location + @type = type + @comment = comment + end + end + + class ClassIvarTypeAnnotation < Base + attr_reader :self_location, :dot_location, :var_name_location, :colon_location, :type, :comment + + def initialize(location:, prefix_location:, self_location:, dot_location:, var_name_location:, colon_location:, type:, comment:) + super(location, prefix_location) + @self_location = self_location + @dot_location = dot_location + @var_name_location = var_name_location + @colon_location = colon_location + @type = type + @comment = comment + end + end + + class EmbeddedRBSAnnotation < Base + attr_reader :members + + def initialize(location:, prefix_location:, members:) + super(location, prefix_location) + @members = members + end + end + end + end + end +end diff --git a/lib/rbs/ast/ruby/comment_block.rb b/lib/rbs/ast/ruby/comment_block.rb new file mode 100644 index 000000000..0b11257a9 --- /dev/null +++ b/lib/rbs/ast/ruby/comment_block.rb @@ -0,0 +1,220 @@ +module RBS + module AST + module Ruby + class CommentBlock + attr_reader :name, :offsets, :comment_buffer + + def initialize(source_buffer, comments) + @name = source_buffer.name + + @offsets = [] + + # Assume the comment starts with a prefix whitespace + prefix_str = "# " + + ranges = [] #: Array[Range[Integer]] + + comments.each do |comment| + tuple = [comment, 2] #: [Prism::Comment, Integer] + + unless comment.location.slice.start_with?(prefix_str) + tuple[1] = 1 + end + + offsets << tuple + + start_char = comment.location.start_character_offset + tuple[1] + end_char = comment.location.end_character_offset + ranges << (start_char ... end_char) + end + + @comment_buffer = source_buffer.sub_buffer(lines: ranges) + end + + def leading? + comment = offsets[0][0] or raise + comment.location.start_line_slice.index(/\S/) ? false : true + end + + def trailing? + comment = offsets[0][0] or raise + comment.location.start_line_slice.index(/\S/) ? true : false + end + + def start_line + comments[0].location.start_line + end + + def end_line + comments[-1].location.end_line + end + + def line_starts + offsets.map do |comment, prefix_size| + comment.location.start_character_offset + prefix_size + end + end + + def self.build(buffer, comments) + blocks = [] #: Array[CommentBlock] + + comments = comments.filter {|comment| comment.is_a?(Prism::InlineComment) } + + until comments.empty? + block_comments = [] #: Array[Prism::Comment] + + until comments.empty? + comment = comments.first or raise + last_comment = block_comments.last + + if last_comment + if last_comment.location.end_line + 1 == comment.location.start_line + if last_comment.location.start_column == comment.location.start_column + unless comment.location.start_line_slice.index(/\S/) + block_comments << comments.shift + next + end + end + end + + break + else + block_comments << comments.shift + end + end + + unless block_comments.empty? + blocks << CommentBlock.new(buffer, block_comments.dup) + end + end + + blocks + end + + AnnotationSyntaxError = _ = Data.define(:location, :error) + + def each_paragraph(variables, &block) + if block + if leading_annotation?(0) + yield_annotation(0, 0, 0, variables, &block) + else + yield_paragraph(0, 0, variables, &block) + end + else + enum_for :each_paragraph, variables + end + end + + def yield_paragraph(start_line, current_line, variables, &block) + # We already know at start_line..current_line are paragraph. + # binding.irb + + while true + next_line = current_line + 1 + + if next_line >= comment_buffer.line_count + yield line_location(start_line, current_line) + return + end + + if leading_annotation?(next_line) + yield line_location(start_line, current_line) + return yield_annotation(next_line, next_line, next_line, variables, &block) + else + current_line = next_line + end + end + end + + def yield_annotation(start_line, end_line, current_line, variables, &block) + # We already know at start_line..end_line are annotation. + while true + next_line = current_line + 1 + + if next_line >= comment_buffer.line_count + annotation = parse_annotation_lines(start_line, end_line, variables) + yield annotation + + if end_line > current_line + yield_paragraph(end_line + 1, end_line + 1, variables, &block) + end + + return + end + + line_text = text(next_line) + if leading_spaces = line_text.index(/\S/) + if leading_spaces == 0 + # End of annotation + yield parse_annotation_lines(start_line, end_line, variables) + + if leading_annotation?(end_line + 1) + yield_annotation(end_line + 1, end_line + 1, end_line + 1, variables, &block) + else + yield_paragraph(end_line + 1, end_line + 1, variables, &block) + end + + return + else + current_line = next_line + end_line = next_line + end + else + current_line = next_line + end + end + end + + def text(comment_index) + range = comment_buffer.ranges[comment_index] + comment_buffer.content[range] or raise + end + + def line_location(start_line, end_line) + start_offset = comment_buffer.ranges[start_line].begin + end_offset = comment_buffer.ranges[end_line].end + Location.new(comment_buffer, start_offset, end_offset) + end + + def parse_annotation_lines(start_line, end_line, variables) + start_pos = comment_buffer.ranges[start_line].begin + end_pos = comment_buffer.ranges[end_line].end + begin + Parser.parse_inline(comment_buffer, start_pos...end_pos, variables: variables) + rescue ParsingError => error + AnnotationSyntaxError.new(line_location(start_line, end_line), error) + end + end + + def trailing_annotation(variables) + if trailing? + comment = comments[0] or raise + if comment.location.slice.start_with?(/#[:\[]/) + begin + Parser.parse_inline_assertion(comment_buffer, 0...comment_buffer.last_position, variables: variables) + rescue ParsingError => error + location = line_location(0, offsets.size - 1) + AnnotationSyntaxError.new(location, error) + end + end + end + end + + def comments + offsets.map { _1[0]} + end + + def leading_annotation?(index) + if index < comment_buffer.line_count + text(index).start_with?(/@rbs\b|@rbs!/) and return true + + comment = offsets[index][0] + comment.location.slice.start_with?(/#:/) and return true + end + + false + end + end + end + end +end diff --git a/lib/rbs/ast/ruby/declarations.rb b/lib/rbs/ast/ruby/declarations.rb new file mode 100644 index 000000000..29c5742cd --- /dev/null +++ b/lib/rbs/ast/ruby/declarations.rb @@ -0,0 +1,365 @@ +module RBS + module AST + module Ruby + module Declarations + class Base + attr_reader :buffer + + def constant_as_type_name(node) + case node + when Prism::ConstantPathNode, Prism::ConstantReadNode + TypeName.parse(node.full_name) + end + end + + def rbs_location(location) + Location.new(buffer, location.start_character_offset, location.end_character_offset) + end + + def initialize(buffer) + @buffer = buffer + end + end + + class GenericsTypeParams + attr_reader :annotations + + def initialize(annotations) + @annotations = annotations + end + + def param_names + type_params.map(&:name) + end + + def type_params + @type_params ||= annotations.map do |type_param| + TypeParam.new( + name: type_param.name, + variance: type_param.variance, + upper_bound: type_param.upper_bound, + default_type: type_param.default_type, + location: nil + ) + end + end + + def map_type_name(&block) + self.class.new( + annotations.map { |annotation| annotation.map_type_name { yield(_1) } } + ) #: self + end + + def self.build(annotations) + generic_annotations, other_annotations = annotations.partition do |annotation| + annotation.is_a?(Annotation::GenericAnnotation) + end #: [Array[Annotation::GenericAnnotation], Array[Annotation::leading_annotation]] + + [new(generic_annotations), other_annotations] + end + end + + class ClassDecl < Base + class SuperNode + attr_reader :class_name + attr_reader :type_args + attr_reader :location + attr_reader :class_name_location + attr_reader :open_paren_location + attr_reader :close_paren_location + + def initialize(class_name:, type_args:, location:, class_name_location:, open_paren_location:, close_paren_location:) + @class_name = class_name + @type_args = type_args + @location = location + @class_name_location = class_name_location + @open_paren_location = open_paren_location + @close_paren_location = close_paren_location + end + + def map_type_name(&block) + self.class.new( + class_name: yield(class_name), + type_args: type_args.map {|type| type.map_type_name { yield(_1) } }, + location: location, + class_name_location: class_name_location, + open_paren_location: open_paren_location, + close_paren_location: close_paren_location + ) #: self + end + + alias name class_name + + alias args type_args + end + + class SuperAnnotation + attr_reader :class_name + attr_reader :type_args + attr_reader :annotation + + def initialize(class_name, type_args, annotation) + @class_name = class_name + @type_args = type_args + @annotation = annotation + end + + def map_type_name(&block) + self.class.new( + yield(class_name), + type_args.map {|type| type.map_type_name { yield(_1) } }, + annotation + ) #: self + end + + alias name class_name + + alias args type_args + + def self.build(annotations) + super_annotation = nil #: Annotation::InheritsAnnotation? + other_annotations = [] #: Array[Annotation::leading_annotation] + + annotations.each do |annotation| + if !super_annotation && annotation.is_a?(Annotation::InheritsAnnotation) + super_annotation = annotation + else + other_annotations << annotation + end + end + + [ + if super_annotation + SuperAnnotation.new( + super_annotation.type_name, + super_annotation.type_args, + super_annotation + ) #: instance + end, + other_annotations + ] + end + end + + attr_reader :node + attr_reader :location + attr_reader :class_name + attr_reader :class_name_location + attr_reader :generics + attr_reader :members + attr_reader :super_node + attr_reader :super_annotation + + def initialize(buffer, node, location:, class_name:, class_name_location:, generics:, super_node:, super_annotation:) + super(buffer) + @node = node + @location = location + @class_name = class_name + @class_name_location = class_name_location + @generics = generics + @super_node = super_node + @super_annotation = super_annotation + @members = [] + end + + alias name class_name + + def super_class + super_annotation || super_node + end + + def each_member(&block) + if block + members.each do |member| + case member + when Members::Base + yield member + when EmbeddedRBSDecl + member.each_member(&block) + end + end + else + enum_for :each_member + end + end + + def type_params + generics&.type_params || [] + end + + def each_decl(&block) + if block + members.each do |member| + yield member if member.is_a?(Base) + end + else + enum_for :each_decl + end + end + end + + class SingletonClassDecl < Base + attr_reader :node + attr_reader :members + + def initialize(buffer, node) + super(buffer) + @node = node + @members = [] + end + + def location + rbs_location(node.location) + end + end + + class ModuleDecl < Base + class SelfConstraint + attr_reader :name + attr_reader :args + attr_reader :annotation + + def initialize(name, args, annotation) + @name = name + @args = args + @annotation = annotation + end + + def map_type_name + SelfConstraint.new( + yield(name), + args.map { |type| type.map_type_name { yield(_1) } }, + annotation + ) #: self + end + + def self.build(annotations) + self_annotations = [] #: Array[Annotation::ModuleSelfAnnotation] + other_annotations = [] #: Array[Annotation::leading_annotation] + + annotations.each do |annotation| + if annotation.is_a?(Annotation::ModuleSelfAnnotation) + self_annotations << annotation + else + other_annotations << annotation + end + end + + [ + self_annotations.map { new(_1.type_name, _1.type_args, _1) }, + other_annotations + ] + end + + def location + annotation.location + annotation.close_paren_location + end + end + + attr_reader :node + attr_reader :location + attr_reader :module_name + attr_reader :module_name_location + attr_reader :generics + attr_reader :members + attr_reader :self_constraints + + def initialize(buffer, node, location:, module_name:, module_name_location:, generics:, self_constraints:) + super(buffer) + @node = node + @location = location + @module_name = module_name + @module_name_location = module_name_location + @generics = generics + @self_constraints = self_constraints + @members = [] + end + + alias name module_name + + def type_params + generics.type_params + end + + def self_types + self_constraints + end + + def each_member(&block) + if block + members.each do |member| + case member + when Members::Base + yield member + when EmbeddedRBSDecl + member.each_member(&block) + end + end + else + enum_for :each_member + end + end + + def each_decl(&block) + if block + members.each do |member| + yield member if member.is_a?(Base) + end + else + enum_for :each_decl + end + end + end + + class ConstantDecl < Base + attr_reader :node + + def initialize(buffer, node) + super(buffer) + @node = node + end + + def type + Types::Bases::Any.new(location: nil) + end + end + + class EmbeddedRBSDecl < Base + attr_reader :location + + attr_reader :members + + def initialize(buffer, location, members) + super(buffer) + @location = location + @members = members + end + + def each_decl(&block) + if block + members.each do |member| + if member.is_a?(AST::Declarations::Base) + yield member + end + end + else + enum_for(:each_decl) + end + end + + def each_member(&block) + if block + members.each do |member| + if member.is_a?(AST::Members::Base) + yield member + end + end + else + enum_for(:each_member) + end + end + end + end + end + end +end diff --git a/lib/rbs/ast/ruby/helpers.rb b/lib/rbs/ast/ruby/helpers.rb new file mode 100644 index 000000000..a1f7f7b63 --- /dev/null +++ b/lib/rbs/ast/ruby/helpers.rb @@ -0,0 +1,20 @@ +module RBS + module AST + module Ruby + module Helpers + module ConstantHelper + module_function + + def constant_as_type_name(node) + case node + when Prism::ConstantPathNode, Prism::ConstantReadNode + TypeName.parse(node.full_name) + end + rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError + nil + end + end + end + end + end +end diff --git a/lib/rbs/ast/ruby/members.rb b/lib/rbs/ast/ruby/members.rb new file mode 100644 index 000000000..cfb8eb511 --- /dev/null +++ b/lib/rbs/ast/ruby/members.rb @@ -0,0 +1,628 @@ +module RBS + module AST + module Ruby + module Members + class Base + attr_reader :buffer + + def initialize(buffer) + @buffer = buffer + end + + include Helpers::ConstantHelper + end + + class Overload + attr_reader :method_type + attr_reader :annotations + + def initialize(method_type, annotations) + @method_type = method_type + @annotations = annotations + end + end + + class DefAnnotations + class DocStyleTypeAnnotations + attr_reader :return_type_annotation + attr_reader :param_type_annotations + attr_reader :splat_type_annotation + attr_reader :kwsplat_type_annotation + attr_reader :block_type_annotation + + def initialize(return_type_annotation:, param_type_annotations:, splat_type_annotation:, kwsplat_type_annotation:, block_type_annotation:) + @return_type_annotation = return_type_annotation + @param_type_annotations = param_type_annotations + @splat_type_annotation = splat_type_annotation + @kwsplat_type_annotation = kwsplat_type_annotation + @block_type_annotation = block_type_annotation + end + + def self.empty + new( + return_type_annotation: nil, + param_type_annotations: {}, + splat_type_annotation: nil, + kwsplat_type_annotation: nil, + block_type_annotation: nil + ) + end + + def map_type_name(&block) + DocStyleTypeAnnotations.new( + return_type_annotation: return_type_annotation&.map_type_name(&block), + param_type_annotations: param_type_annotations.transform_values { _1.map_type_name(&block) }, + splat_type_annotation: splat_type_annotation&.map_type_name(&block), + kwsplat_type_annotation: kwsplat_type_annotation&.map_type_name(&block), + block_type_annotation: block_type_annotation&.map_type_name(&block) + ) #: self + end + + def update!(return_type_annotation: self.return_type_annotation, splat_type_annotation: self.splat_type_annotation, kwsplat_type_annotation: self.kwsplat_type_annotation, block_type_annotation: self.block_type_annotation) + @return_type_annotation = return_type_annotation + @splat_type_annotation = splat_type_annotation + @kwsplat_type_annotation = kwsplat_type_annotation + @block_type_annotation = block_type_annotation + end + + def construct_method_type(parameters) + any_type = Types::Bases::Any.new(location: nil) + + required_positionals = [] #: Array[Types::Function::Param] + optional_positionals = [] #: Array[Types::Function::Param] + rest_positionals = nil #: Types::Function::Param? + required_keywords = {} #: Hash[Symbol, Types::Function::Param] + optional_keywords = {} #: Hash[Symbol, Types::Function::Param] + rest_keywords = nil #: Types::Function::Param? + + if parameters + parameters.requireds.each do |param| + case param + when Prism::RequiredParameterNode + type = param_type_annotations[param.name]&.type || any_type + required_positionals << Types::Function::Param.new(type: type, name: param.name, location: nil) + else + required_positionals << Types::Function::Param.new(type: any_type, name: nil, location: nil) + end + end + parameters.optionals.each do |param| + type = param_type_annotations[param.name]&.type || any_type + optional_positionals << Types::Function::Param.new(type: type, name: param.name, location: nil) + end + if (rest = parameters.rest).is_a?(Prism::RestParameterNode) + rest_positionals = Types::Function::Param.new( + type: splat_type_annotation&.type || any_type, + name: rest.name, + location: nil + ) + end + parameters.keywords.each do |node| + if node.is_a?(Prism::RequiredKeywordParameterNode) + type = param_type_annotations[node.name]&.type || any_type + required_keywords[node.name] = Types::Function::Param.new(type: type, name: nil, location: nil) + end + + if node.is_a?(Prism::OptionalKeywordParameterNode) + type = param_type_annotations[node.name]&.type || any_type + optional_keywords[node.name] = Types::Function::Param.new(type: type, name: nil, location: nil) + end + end + if (rest = parameters.keyword_rest).is_a?(Prism::KeywordRestParameterNode) + rest_keywords = Types::Function::Param.new( + type: kwsplat_type_annotation&.type || any_type, + name: rest.name, + location: nil + ) + end + if parameters.block + block = Types::Block.new( + type: Types::UntypedFunction.new(return_type: Types::Bases::Any.new(location: nil)), + required: false, + self_type: nil + ) + end + end + + if blk = block_type_annotation + block = blk.block + end + + return_type = + case return_type_annotation + when Annotation::ReturnTypeAnnotation + return_type_annotation.return_type + when Annotation::NodeTypeAssertion + return_type_annotation.type + else + any_type + end + + MethodType.new( + type_params: [], + type: Types::Function.new( + required_positionals: required_positionals, + optional_positionals: optional_positionals, + rest_positionals: rest_positionals, + trailing_positionals: [], + required_keywords: required_keywords, + optional_keywords: optional_keywords, + rest_keywords: rest_keywords, + return_type: return_type + ), + block: block, + location: nil + ) + end + + def empty? + !return_type_annotation && + param_type_annotations.empty? && + !splat_type_annotation && + !kwsplat_type_annotation && + !block_type_annotation + end + end + + attr_reader :type_annotations, :annotations, :override + + def initialize(type_annotations:, annotations:, override:) + @type_annotations = type_annotations + @annotations = annotations + @override = override + end + + def map_type_name(&block) + type = + case type_annotations + when DocStyleTypeAnnotations + type_annotations.map_type_name(&block) + when Array + type_annotations.map do |annotation| + annotation.map_type_name(&block) + end + when Annotation::MethodTypesAnnotation + type_annotations.map_type_name(&block) + end + DefAnnotations.new( + type_annotations: type, + annotations: annotations, + override: override + ) #: self + end + + def self.build(leading_block, trailing_block, variables) + unused_annotations = [] #: Array[Annotation::leading_annotation | CommentBlock::AnnotationSyntaxError] + unused_trailing_annotation = nil #: Annotation::trailing_annotation | CommentBlock::AnnotationSyntaxError | nil + + override = nil #: Annotation::OverrideAnnotation? + annotations = [] #: Array[Annotation::RBSAnnotationAnnotation] + type_annotations = nil #: type_annotations + + if trailing_block + case annotation = trailing_block.trailing_annotation(variables) + when Annotation::NodeTypeAssertion + type_annotations = DocStyleTypeAnnotations.empty.tap do + _1.update!(return_type_annotation: annotation) + end + else + unused_trailing_annotation = annotation + end + end + + if leading_block + leading_block.each_paragraph(variables) do |paragraph| + next if paragraph.is_a?(Location) + if paragraph.is_a?(CommentBlock::AnnotationSyntaxError) + unused_annotations << paragraph + next + end + + case paragraph + when Annotation::RBSAnnotationAnnotation + annotations << paragraph + next + when Annotation::OverrideAnnotation + override = paragraph + next + when Annotation::MethodTypesAnnotation, Annotation::ColonMethodTypeAnnotation + type_annotations = [] unless type_annotations + if type_annotations.is_a?(Array) + type_annotations << paragraph + next + end + when Annotation::ParamTypeAnnotation + type_annotations = DocStyleTypeAnnotations.empty unless type_annotations + if type_annotations.is_a?(DocStyleTypeAnnotations) + unless type_annotations.param_type_annotations.key?(paragraph.param_name) + type_annotations.param_type_annotations[paragraph.param_name] = paragraph + next + end + end + when Annotation::SplatParamTypeAnnotation + type_annotations = DocStyleTypeAnnotations.empty unless type_annotations + if type_annotations.is_a?(DocStyleTypeAnnotations) + unless type_annotations.splat_type_annotation + type_annotations.update!(splat_type_annotation: paragraph) + next + end + end + when Annotation::DoubleSplatParamTypeAnnotation + type_annotations = DocStyleTypeAnnotations.empty unless type_annotations + if type_annotations.is_a?(DocStyleTypeAnnotations) + type_annotations.update!(kwsplat_type_annotation: paragraph) + next + end + when Annotation::BlockParamTypeAnnotation + type_annotations = DocStyleTypeAnnotations.empty unless type_annotations + if type_annotations.is_a?(DocStyleTypeAnnotations) + type_annotations.update!(block_type_annotation: paragraph) + next + end + when Annotation::ReturnTypeAnnotation + type_annotations = DocStyleTypeAnnotations.empty unless type_annotations + if type_annotations.is_a?(DocStyleTypeAnnotations) + unless type_annotations.return_type_annotation + type_annotations.update!(return_type_annotation: paragraph) + next + end + end + end + + unused_annotations << paragraph + end + end + + [ + DefAnnotations.new( + type_annotations: type_annotations, + annotations: annotations, + override: override + ), + unused_annotations, + unused_trailing_annotation + ] + end + + def empty? + annotations.empty? && override.nil? && ( + type_annotations.nil? || type_annotations.empty? + ) + end + end + + class DefMember < Base + attr_reader :node, :inline_annotations, :name, :visibility + + def initialize(buffer, node, name:, inline_annotations:, visibility:) + super(buffer) + @node = node + @inline_annotations = inline_annotations + @name = name + @visibility = visibility + end + + def overloads + case inline_annotations.type_annotations + when DefAnnotations::DocStyleTypeAnnotations, nil + annots = inline_annotations.type_annotations || DefAnnotations::DocStyleTypeAnnotations.empty + method_type = annots.construct_method_type(node.parameters) + [ + Overload.new(method_type, []) + ] + when Array + inline_annotations.type_annotations.flat_map do |annotation| + case annotation + when Annotation::ColonMethodTypeAnnotation + [Overload.new(annotation.method_type.update(location: nil), annotation.annotations)] + when Annotation::MethodTypesAnnotation + annotation.overloads.map do + Overload.new(_1.method_type.update(location: nil), _1.annotations) + end + end + end + end + end + + def override? + inline_annotations.override ? true : false + end + + def annotations + inline_annotations.annotations.flat_map do |annotation| + annotation.annotations + end + end + + def map_type_name(&block) + DefMember.new( + buffer, + node, + name: name, + inline_annotations: inline_annotations.map_type_name(&block), + visibility: visibility + ) #: self + end + + def location + buffer.rbs_location(node.location) + end + + def name_location + buffer.rbs_location(node.name_loc) + end + end + + class DefSingletonMember < Base + attr_reader :node, :name, :inline_annotations, :visibility + + def initialize(buffer, node, name:, inline_annotations:, visibility:) + super(buffer) + @node = node + @name = name + @inline_annotations = inline_annotations + @visibility = visibility + end + + def overloads + case inline_annotations.type_annotations + when DefAnnotations::DocStyleTypeAnnotations, nil + annots = inline_annotations.type_annotations || DefAnnotations::DocStyleTypeAnnotations.empty + method_type = annots.construct_method_type(node.parameters) + [ + Overload.new(method_type, []) + ] + when Array + inline_annotations.type_annotations.flat_map do |annotation| + case annotation + when Annotation::ColonMethodTypeAnnotation + [Overload.new(annotation.method_type.update(location: nil), annotation.annotations)] + when Annotation::MethodTypesAnnotation + annotation.overloads.map do + Overload.new(_1.method_type.update(location: nil), _1.annotations) + end + end + end + end + end + + def self? + node.receiver.is_a?(Prism::SelfNode) + end + + def override? + inline_annotations.override ? true : false + end + + def annotations + inline_annotations.annotations.flat_map do |annotation| + annotation.annotations + end + end + + def location + buffer.rbs_location(node.location) + end + + def map_type_name(&block) + DefSingletonMember.new( + buffer, + node, + name: name, + inline_annotations: inline_annotations.map_type_name(&block), + visibility: visibility + ) #: self + end + end + + class AliasMember < Base + attr_reader :node + + def initialize(buffer, node) + @node = node + super(buffer) + end + + def location + buffer.rbs_location(node.location) + end + + def old_name + case old_name = node.old_name + when Prism::SymbolNode + (old_name.value || "").to_sym + else + raise + end + end + + def new_name + case new_name = node.new_name + when Prism::SymbolNode + (new_name.value || "").to_sym + else + raise + end + end + end + + class MixinMember < Base + attr_reader :node + + attr_reader :location + attr_reader :module_name + attr_reader :module_name_location + attr_reader :open_paren_location + attr_reader :close_paren_location + attr_reader :type_args + + def initialize(buffer, node, location:, module_name:, type_args:, module_name_location:, open_paren_location:, close_paren_location:) + super(buffer) + @node = node + @location = location + @module_name = module_name + @type_args = type_args + @module_name_location = module_name_location + @open_paren_location = open_paren_location + @close_paren_location = close_paren_location + end + + def map_type_name(&block) + self.class.new( + buffer, + node, + location: location, + module_name: yield(module_name), + type_args: type_args.map {|type| type.map_type_name { yield(_1) } }, + module_name_location: module_name_location, + open_paren_location: open_paren_location, + close_paren_location: close_paren_location, + ) #: self + end + end + + class IncludeMember < MixinMember + end + + class ExtendMember < MixinMember + end + + class PrependMember < MixinMember + end + + class AttributeMember < Base + attr_reader :node, :names, :annotation, :visibility + + def initialize(buffer, node, names, annotation, visibility) + super(buffer) + @node = node + @names = names + @annotation = annotation + @visibility = visibility + end + + def map_type_name(&block) + if annotation + self.class.new( + buffer, + node, + names, + annotation.map_type_name { yield(_1) }, + visibility + ) #: self + else + self + end + end + + def location + buffer.rbs_location(node.location) + end + + def type + annotation&.type + end + end + + class AttrReaderMember < AttributeMember + end + + class AttrWriterMember < AttributeMember + end + + class AttrAccessorMember < AttributeMember + end + + class VisibilityMember < Base + attr_reader :node + + def initialize(buffer, node) + super(buffer) + @node = node + end + + def location + buffer.rbs_location(node.location) + end + end + + class PublicMember < VisibilityMember + end + + class PrivateMember < VisibilityMember + end + + class InstanceVariableMember < Base + attr_reader :name, :type, :annotation + + def initialize(buffer, name, type, annotation) + super(buffer) + @name = name + @type = type + @annotation = annotation + end + + def location + annotation.location + end + + def map_type_name(&block) + self.class.new( + buffer, + name, + type.map_type_name { yield(_1) }, + annotation + ) #: self + end + end + + class ClassVariableMember < Base + attr_reader :name, :type, :annotation + + def initialize(buffer, name, type, annotation) + super(buffer) + @name = name + @type = type + @annotation = annotation + end + + def location + annotation.location + end + + def map_type_name(&block) + self.class.new( + buffer, + name, + type.map_type_name { yield(_1) }, + annotation + ) #: self + end + end + + class ClassInstanceVariableMember < Base + attr_reader :name, :type, :annotation + + def initialize(buffer, name, type, annotation) + super(buffer) + @name = name + @type = type + @annotation = annotation + end + + def location + annotation.location + end + + def map_type_name(&block) + self.class.new( + buffer, + name, + type.map_type_name { yield(_1) }, + annotation + ) #: self + end + end + end + end + end +end diff --git a/lib/rbs/buffer.rb b/lib/rbs/buffer.rb index c5f48dcfe..3c6fdc709 100644 --- a/lib/rbs/buffer.rb +++ b/lib/rbs/buffer.rb @@ -4,39 +4,51 @@ module RBS class Buffer attr_reader :name attr_reader :content + attr_reader :parent - def initialize(name:, content:) - @name = name - @content = content + def initialize(name: nil, content:, parent: nil) + case + when name && content + @name = name + @content = content + @parent = nil + when parent && content + @name = parent[0].name + @content = content + @parent = parent + end end def lines - @lines ||= content.lines + ranges.map { self.content[_1] || raise } #$ String + end + + def line_count + ranges.size end def ranges - @ranges ||= - begin - @ranges = [] - offset = 0 - lines.each do |line| - size = line.size - range = offset...(offset+size) - @ranges << range - offset += size - end - - if !content.end_with?("\n") && content.size > 0 - @ranges[-1] = @ranges[-1].begin...(@ranges[-1].end+1) - end - - @ranges + unless @ranges + lines = content.lines(chomp: true) + lines << "" if content.end_with?("\n") + + @ranges = [] + offset = 0 + + lines.each do |line| + size = line.size + range = offset...(offset+size) + @ranges << range + offset += size + 1 end + end + + @ranges end def pos_to_loc(pos) index = ranges.bsearch_index do |range| - pos < range.end ? true : false + pos <= range.end ? true : false end if index @@ -57,11 +69,69 @@ def loc_to_pos(loc) end def last_position - content.size + ranges[-1].end end def inspect - "#" + "#" + end + + def rbs_location(location, loc2=nil) + if loc2 + Location.new(self, location.start_character_offset, loc2.end_character_offset) + else + Location.new(self, location.start_character_offset, location.end_character_offset) + end + end + + def sub_buffer(lines:) + buf = +"" + lines.each_with_index do |range, index| + start_pos = range.begin + end_pos = range.end + slice = content[start_pos...end_pos] or raise + if slice.include?("\n") + raise "Line #{index + 1} cannot contain newline character." + end + buf << slice + buf << "\n" + end + + buf.chomp! + + Buffer.new(content: buf, parent: [self, lines]) + end + + def parent_buffer + if parent + parent[0] + end + end + + def parent_position(position) + parent or raise "#parent_position is unavailable with buffer without parent" + return nil unless position <= last_position + + line, column = pos_to_loc(position) + parent_range = parent[1][line - 1] + parent_range.begin + column + end + + def absolute_position(position) + if parent_buffer + pos = parent_position(position) or return + parent_buffer.absolute_position(pos) + else + position + end + end + + def top_buffer + if parent_buffer + parent_buffer.top_buffer + else + self + end end end end diff --git a/lib/rbs/cli.rb b/lib/rbs/cli.rb index a47d2aac0..3ad10ea24 100644 --- a/lib/rbs/cli.rb +++ b/lib/rbs/cli.rb @@ -160,38 +160,38 @@ def run_ast(args, options) EOB end.order!(args) - patterns = args.map do |arg| - path = Pathname(arg) - if path.exist? - # Pathname means a directory or a file - path - else - # String means a `fnmatch` pattern - arg - end - end - - loader = options.loader() - - env = Environment.from_loader(loader).resolve_type_names - - decls = env.declarations.select do |decl| - loc = decl.location or raise - # @type var name: String - name = loc.buffer.name - - patterns.empty? || patterns.any? do |pat| - case pat - when Pathname - Pathname(name).ascend.any? {|p| p == pat } - when String - name.end_with?(pat) || File.fnmatch(pat, name, File::FNM_EXTGLOB) - end - end - end - - stdout.print JSON.generate(decls) - stdout.flush + # patterns = args.map do |arg| + # path = Pathname(arg) + # if path.exist? + # # Pathname means a directory or a file + # path + # else + # # String means a `fnmatch` pattern + # arg + # end + # end + + # loader = options.loader() + + # env = Environment.from_loader(loader).resolve_type_names + + # decls = env.each_decl.select do |decl| + # loc = decl.location or raise + # # @type var name: String + # name = loc.buffer.name + + # patterns.empty? || patterns.any? do |pat| + # case pat + # when Pathname + # Pathname(name).ascend.any? {|p| p == pat } + # when String + # name.end_with?(pat) || File.fnmatch(pat, name, File::FNM_EXTGLOB) + # end + # end + # end + + # stdout.print JSON.generate(decls) + # stdout.flush end def run_list(args, options) @@ -913,7 +913,7 @@ def run_parse(args, options) Buffer.new(content: file_path.read, name: file_path) end end - bufs << Buffer.new(content: e_code, name: '-e') if e_code + bufs << Buffer.new(content: e_code, name: Pathname('-e')) if e_code bufs.each do |buf| RBS.logger.info "Parsing #{buf.name}..." diff --git a/lib/rbs/definition.rb b/lib/rbs/definition.rb index d97da8c06..d231bd0f3 100644 --- a/lib/rbs/definition.rb +++ b/lib/rbs/definition.rb @@ -62,7 +62,11 @@ def hash end def comment - member.comment + if member.is_a?(AST::Members::Base) + member.comment + else + nil + end end def update(type: self.type, member: self.member, defined_in: self.defined_in, implemented_in: self.implemented_in) diff --git a/lib/rbs/definition_builder.rb b/lib/rbs/definition_builder.rb index 4c4ede267..ce1dfb310 100644 --- a/lib/rbs/definition_builder.rb +++ b/lib/rbs/definition_builder.rb @@ -126,32 +126,30 @@ def define_instance(definition, type_name, subst, define_class_vars:) entry = env.class_decls[type_name] or raise "Unknown name for build_instance: #{type_name}" args = entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) } - entry.decls.each do |d| - subst_ = subst + Substitution.build(d.decl.type_params.each.map(&:name), args) + entry.each_decl do |decl| + subst_ = subst + Substitution.build(decl.type_params.each.map(&:name), args) - d.decl.members.each do |member| + each_instance_variable_member(decl) do |member| case member when AST::Members::AttrReader, AST::Members::AttrAccessor, AST::Members::AttrWriter - if member.kind == :instance - ivar_name = case member.ivar_name - when false - nil - else - member.ivar_name || :"@#{member.name}" - end - - if ivar_name - insert_variable( - type_name, - definition.instance_variables, - name: ivar_name, - type: member.type.sub(subst_), - source: member - ) - end + ivar_name = case member.ivar_name + when false + nil + else + member.ivar_name || :"@#{member.name}" + end + + if ivar_name + insert_variable( + type_name, + definition.instance_variables, + name: ivar_name, + type: member.type.sub(subst_), + source: member + ) end - when AST::Members::InstanceVariable + when AST::Members::InstanceVariable, AST::Ruby::Members::InstanceVariableMember insert_variable( type_name, definition.instance_variables, @@ -160,9 +158,9 @@ def define_instance(definition, type_name, subst, define_class_vars:) source: member ) - when AST::Members::ClassVariable + when AST::Members::ClassVariable, AST::Ruby::Members::ClassVariableMember if define_class_vars - insert_variable(type_name, definition.class_variables, name: member.name, type: member.type, source: member) + insert_variable(type_name, definition.class_variables, name: member.name, type: member.type) end end end @@ -174,7 +172,7 @@ def build_instance(type_name) try_cache(type_name, cache: instance_cache) do entry = env.class_decls[type_name] or raise "Unknown name for build_instance: #{type_name}" - ensure_namespace!(type_name.namespace, location: entry.decls[0].decl.location) + ensure_namespace!(type_name.namespace, location: entry.primary_decl.location) ancestors = ancestor_builder.instance_ancestors(type_name) args = entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) } @@ -234,7 +232,7 @@ def build_instance(type_name) def build_singleton0(type_name) try_cache type_name, cache: singleton0_cache do entry = env.class_decls[type_name] or raise "Unknown name for build_singleton0: #{type_name}" - ensure_namespace!(type_name.namespace, location: entry.decls[0].decl.location) + ensure_namespace!(type_name.namespace, location: entry.primary_decl.location) ancestors = ancestor_builder.singleton_ancestors(type_name) self_type = Types::ClassSingleton.new(name: type_name, location: nil) @@ -272,25 +270,27 @@ def build_singleton0(type_name) interface_methods = interface_methods(all_interfaces) import_methods(definition, type_name, methods, interface_methods, Substitution.new, nil) - entry.decls.each do |d| - d.decl.members.each do |member| + entry.each_decl do |decl| + each_singleton_variable_member(decl) do |member| case member when AST::Members::AttrReader, AST::Members::AttrAccessor, AST::Members::AttrWriter - if member.kind == :singleton - ivar_name = case member.ivar_name - when false - nil - else - member.ivar_name || :"@#{member.name}" - end - - if ivar_name - insert_variable(type_name, definition.instance_variables, name: ivar_name, type: member.type, source: member) - end + ivar_name = case member.ivar_name + when false + nil + else + member.ivar_name || :"@#{member.name}" + end + + if ivar_name + insert_variable(type_name, definition.instance_variables, name: ivar_name, type: member.type, source: member) end - when AST::Members::ClassInstanceVariable - insert_variable(type_name, definition.instance_variables, name: member.name, type: member.type, source: member) + when AST::Members::ClassInstanceVariable, AST::Ruby::Members::ClassInstanceVariableMember + ClassInstanceVariableDuplicationError.check!(variables: definition.instance_variables, member: member, type_name: type_name) + insert_variable(type_name, definition.instance_variables, name: member.name, type: member.type) + + when AST::Members::ClassVariable, AST::Ruby::Members::ClassVariableMember + insert_variable(type_name, definition.class_variables, name: member.name, type: member.type) end end end @@ -306,7 +306,7 @@ def build_singleton(type_name) try_cache type_name, cache: singleton_cache do entry = env.class_decls[type_name] or raise "Unknown name for build_singleton: #{type_name}" - ensure_namespace!(type_name.namespace, location: entry.decls[0].decl.location) + ensure_namespace!(type_name.namespace, location: entry.primary_decl.location) ancestors = ancestor_builder.singleton_ancestors(type_name) self_type = Types::ClassSingleton.new(name: type_name, location: nil) @@ -452,6 +452,8 @@ def source_location(source, decl) case decl when AST::Declarations::Class decl.super_class&.location + when AST::Ruby::Declarations::ClassDecl + decl.super_class&.location end else source.location @@ -471,7 +473,7 @@ def validate_type_params(definition, ancestors:, methods:) validate_params_with(type_params, result: result) do |param| decl = case entry = definition.entry when Environment::ModuleEntry, Environment::ClassEntry - entry.primary.decl + entry.primary_decl when Environment::SingleEntry entry.decl end @@ -569,11 +571,11 @@ def validate_variable(var) case l.source when AST::Members::InstanceVariable if r.source.instance_of?(AST::Members::InstanceVariable) && l.declared_in == r.declared_in - raise InstanceVariableDuplicationError.new(type_name: l.declared_in, variable_name: l.source.name, location: l.source.location) + raise InstanceVariableDuplicationError.new(type_name: l.declared_in, member: l.source) end when AST::Members::ClassInstanceVariable if r.source.instance_of?(AST::Members::ClassInstanceVariable) && l.declared_in == r.declared_in - raise ClassInstanceVariableDuplicationError.new(type_name: l.declared_in, variable_name: l.source.name, location: l.source.location) + raise ClassInstanceVariableDuplicationError.new(type_name: l.declared_in, member: l.source.name) end end end @@ -762,6 +764,64 @@ def define_method(methods, definition, method, subst, self_type_methods, defined ) method_definition.annotations.replace(original.annotations) + when AST::Ruby::Members::DefMember + if duplicated_method = methods[method.name] + raise DuplicatedMethodDefinitionError.new( + type: definition.self_type, + method_name: method.name, + members: [original, *duplicated_method.members] + ) + end + + defs = original.overloads.map do |overload| + Definition::Method::TypeDef.new( + type: subst.empty? ? overload.method_type : overload.method_type.sub(subst), + member: original, + defined_in: defined_in, + implemented_in: implemented_in, + overload_annotations: overload.annotations + ) + end + + # @type var accessibility: RBS::Definition::accessibility + accessibility = + if [:initialize, :initialize_copy, :initialize_clone, :initialize_dup, :respond_to_missing?].include?(method.name) + :private + else + method.accessibility + end + + method_definition = Definition::Method.new( + super_method: existing_method, + defs: defs, + accessibility: accessibility, + alias_of: nil + ) + when AST::Ruby::Members::DefSingletonMember + if duplicated_method = methods[method.name] + raise DuplicatedMethodDefinitionError.new( + type: definition.self_type, + method_name: method.name, + members: [original, *duplicated_method.members] + ) + end + + defs = original.overloads.map do |overload| + Definition::Method::TypeDef.new( + type: subst.empty? ? overload.method_type : overload.method_type.sub(subst), + member: original, + defined_in: defined_in, + implemented_in: implemented_in, + overload_annotations: overload.annotations + ) + end + + method_definition = Definition::Method.new( + super_method: existing_method, + defs: defs, + accessibility: method.accessibility, + alias_of: nil + ) when nil # Overloading method definition only @@ -893,5 +953,69 @@ def validate_type_name(name, location) raise NoTypeFoundError.new(type_name: name, location: location) end + + def each_instance_variable_member(module_decl) + module_decl.members.each do |member| + case member + when AST::Members::Attribute + if member.kind == :instance + yield member + end + yield member + when AST::Members::InstanceVariable, AST::Members::ClassVariable + yield member + # when AST::Ruby::Members::AttributeMember + # yield member + when AST::Ruby::Members::InstanceVariableMember, AST::Ruby::Members::ClassVariableMember + yield member + when AST::Ruby::Declarations::EmbeddedRBSDecl + member.members.each do |member| + case member + when AST::Members::Attribute + if member.kind == :instance + yield member + end + when AST::Members::InstanceVariable, AST::Members::ClassVariable + yield member + end + end + end + end + end + + def each_singleton_variable_member(module_decl) + module_decl.members.each do |member| + case member + when AST::Members::Attribute + if member.kind == :singleton + yield member + end + when AST::Members::ClassInstanceVariable, AST::Members::ClassVariable + yield member + # when AST::Ruby::Members::AttributeMember + # yield member + when AST::Ruby::Members::ClassInstanceVariableMember, AST::Ruby::Members::ClassVariableMember + yield member + when AST::Ruby::Declarations::EmbeddedRBSDecl + member.members.each do |member| + case member + when AST::Members::Attribute + if member.kind == :singleton + yield member + end + when AST::Members::ClassInstanceVariable, AST::Members::ClassVariable + yield member + end + end + when AST::Ruby::Declarations::SingletonClassDecl + member.members.each do |member| + # case member + # when AST::Ruby::Members::AttributeMember + # yield member + # end + end + end + end + end end end diff --git a/lib/rbs/definition_builder/ancestor_builder.rb b/lib/rbs/definition_builder/ancestor_builder.rb index 0b513779b..5e6086a68 100644 --- a/lib/rbs/definition_builder/ancestor_builder.rb +++ b/lib/rbs/definition_builder/ancestor_builder.rb @@ -173,12 +173,12 @@ def initialize(env:) end def validate_super_class!(type_name, entry) - with_super_classes = entry.decls.select {|d| d.decl.super_class } + with_super_classes = entry.each_decl.select {|decl| decl.super_class } return if with_super_classes.size <= 1 - super_types = with_super_classes.map do |d| - super_class = d.decl.super_class or raise + super_types = with_super_classes.map do |decl| + super_class = decl.super_class or raise Types::ClassInstance.new(name: super_class.name, args: super_class.args, location: nil) end @@ -200,8 +200,8 @@ def one_instance_ancestors(type_name) case entry when Environment::ClassEntry validate_super_class!(type_name, entry) - primary = entry.primary - super_class = primary.decl.super_class + primary = entry.primary_decl + super_class = primary.super_class if type_name != BuiltinNames::BasicObject.name if super_class @@ -214,7 +214,7 @@ def one_instance_ancestors(type_name) super_name = env.normalize_module_name(super_name) - NoSuperclassFoundError.check!(super_name, env: env, location: primary.decl.location) + NoSuperclassFoundError.check!(super_name, env: env, location: primary.location) if super_class InheritModuleError.check!(super_class, env: env) InvalidTypeApplicationError.check2!(type_name: super_class.name, args: super_class.args, env: env, location: super_class.location) @@ -283,8 +283,8 @@ def one_singleton_ancestors(type_name) case entry when Environment::ClassEntry validate_super_class!(type_name, entry) - primary = entry.primary - super_class = primary.decl.super_class + primary = entry.primary_decl + super_class = primary.super_class if type_name != BuiltinNames::BasicObject.name if super_class @@ -295,7 +295,7 @@ def one_singleton_ancestors(type_name) super_name = env.normalize_module_name(super_name) - NoSuperclassFoundError.check!(super_name, env: env, location: primary.decl.location) + NoSuperclassFoundError.check!(super_name, env: env, location: primary.location) if super_class InheritModuleError.check!(super_class, env: env) end @@ -347,8 +347,28 @@ def one_interface_ancestors(type_name) end end + def each_mixin(decl, &block) + if block + case decl + when AST::Ruby::Declarations::Base + decl.each_member do |member| + case member + when AST::Ruby::Members::MixinMember + yield member + when AST::Members::Mixin + yield member + end + end + when AST::Declarations::Base + decl.each_mixin(&block) + end + else + enum_for :each_mixin, decl + end + end + def mixin_ancestors0(decl, type_name, align_params:, included_modules:, included_interfaces:, extended_modules:, prepended_modules:, extended_interfaces:) - decl.each_mixin do |member| + each_mixin(decl) do |member| case member when AST::Members::Include module_name = member.name @@ -409,27 +429,73 @@ def mixin_ancestors0(decl, type_name, align_params:, included_modules:, included extended_interfaces << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member) end + + when AST::Ruby::Members::IncludeMember + if included_modules + module_name = member.module_name + module_args = member.type_args.map {|type| align_params ? type.sub(align_params) : type } + + MixinClassError.check!(type_name: type_name, env: env, member: member) + NoMixinFoundError.check!(module_name, env: env, member: member) + + module_decl = env.normalized_module_entry(module_name) or raise + module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args) + + module_name = env.normalize_module_name(module_name) + + included_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member) + end + + when AST::Ruby::Members::PrependMember + if prepended_modules + module_name = member.module_name + module_args = member.type_args.map {|type| align_params ? type.sub(align_params) : type } + + MixinClassError.check!(type_name: type_name, env: env, member: member) + NoMixinFoundError.check!(module_name, env: env, member: member) + + module_decl = env.normalized_module_entry(module_name) or raise + module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args) + + module_name = env.normalize_module_name(module_name) + + prepended_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member) + end end end end def mixin_ancestors(entry, type_name, included_modules:, included_interfaces:, extended_modules:, prepended_modules:, extended_interfaces:) - entry.decls.each do |d| - decl = d.decl - + entry.each_decl.each do |decl| align_params = Substitution.build( decl.type_params.each.map(&:name), entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) } ) - mixin_ancestors0(decl, - type_name, - align_params: align_params, - included_modules: included_modules, - included_interfaces: included_interfaces, - extended_modules: extended_modules, - prepended_modules: prepended_modules, - extended_interfaces: extended_interfaces) + case decl + when AST::Declarations::Class, AST::Declarations::Module + mixin_ancestors0( + decl, + type_name, + align_params: align_params, + included_modules: included_modules, + included_interfaces: included_interfaces, + extended_modules: extended_modules, + prepended_modules: prepended_modules, + extended_interfaces: extended_interfaces + ) + when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl + mixin_ancestors0( + decl, + type_name, + align_params: align_params, + included_modules: included_modules, + included_interfaces: included_interfaces, + extended_modules: extended_modules, + prepended_modules: prepended_modules, + extended_interfaces: extended_interfaces + ) + end end end @@ -445,7 +511,7 @@ def instance_ancestors(type_name, building_ancestors: []) RecursiveAncestorError.check!(self_ancestor, ancestors: building_ancestors, - location: entry.primary.decl.location) + location: entry.primary_decl.location) building_ancestors.push self_ancestor one_ancestors = one_instance_ancestors(type_name) @@ -462,7 +528,7 @@ def instance_ancestors(type_name, building_ancestors: []) super_ancestors = instance_ancestors(super_name, building_ancestors: building_ancestors) - .apply(super_args, env: env, location: entry.primary.decl.super_class&.location) + .apply(super_args, env: env, location: entry.primary_decl.super_class&.location) super_ancestors.map! {|ancestor| fill_ancestor_source(ancestor, name: super_name, source: :super) } ancestors.unshift(*super_ancestors) end @@ -522,7 +588,7 @@ def singleton_ancestors(type_name, building_ancestors: []) RecursiveAncestorError.check!(self_ancestor, ancestors: building_ancestors, - location: entry.primary.decl.location) + location: entry.primary_decl.location) building_ancestors.push self_ancestor one_ancestors = one_singleton_ancestors(type_name) diff --git a/lib/rbs/definition_builder/method_builder.rb b/lib/rbs/definition_builder/method_builder.rb index f137aed4e..9185b25a2 100644 --- a/lib/rbs/definition_builder/method_builder.rb +++ b/lib/rbs/definition_builder/method_builder.rb @@ -100,12 +100,14 @@ def build_instance(type_name) instance_methods[type_name] ||= begin entry = env.class_decls[type_name] + args = entry.type_params.map {|param| Types::Variable.new(name: param.name, location: param.location) } type = Types::ClassInstance.new(name: type_name, args: args, location: nil) + Methods.new(type: type).tap do |methods| - entry.decls.each do |d| - subst = Substitution.build(d.decl.type_params.each.map(&:name), args) - each_member_with_accessibility(d.decl.members) do |member, accessibility| + entry.each_decl do |decl| + subst = Substitution.build(decl.type_params.each.map(&:name), args) + each_member_with_accessibility(decl) do |member, accessibility, sclass| case member when AST::Members::MethodDefinition case member.kind @@ -135,6 +137,10 @@ def build_instance(type_name) if member.kind == :instance build_alias(methods, type, member: member) end + when AST::Ruby::Members::DefMember + unless sclass + build_ruby_method(methods, type, member: member, accessibility: accessibility) + end end end end @@ -149,8 +155,8 @@ def build_singleton(type_name) type = Types::ClassSingleton.new(name: type_name, location: nil) Methods.new(type: type).tap do |methods| - entry.decls.each do |d| - d.decl.members.each do |member| + entry.each_decl do |decl| + each_member_with_accessibility(decl) do |member, accessibility, sclass| case member when AST::Members::MethodDefinition if member.singleton? @@ -164,6 +170,14 @@ def build_singleton(type_name) if member.kind == :singleton build_alias(methods, type, member: member) end + when AST::Ruby::Members::DefSingletonMember + if member.self? + build_ruby_method(methods, type, member: member, accessibility: :public) + end + when AST::Ruby::Members::DefMember + if sclass + build_ruby_method(methods, type, member: member, accessibility: :public) + end end end end @@ -223,15 +237,60 @@ def build_method(methods, type, member:, accessibility:) end end - def each_member_with_accessibility(members, accessibility: :public) - members.each do |member| - case member - when AST::Members::Public - accessibility = :public - when AST::Members::Private - accessibility = :private - else - yield member, accessibility + def build_ruby_method(methods, type, member:, accessibility:) + defn = methods.methods[member.name] ||= Methods::Definition.empty(type: type, name: member.name) + + defn.accessibilities << accessibility + defn.originals << member + end + + def each_member_with_accessibility(decl, accessibility: :public) + case decl + when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl + decl.members.each do |member| + case member + when AST::Ruby::Members::PublicMember + accessibility = :public + when AST::Ruby::Members::PrivateMember + accessibility = :private + when AST::Ruby::Members::Base + yield member, accessibility, nil + when AST::Ruby::Declarations::EmbeddedRBSDecl + member.members.each do |member| + case member + when AST::Members::Public + accessibility = :public + when AST::Members::Private + accessibility = :private + when AST::Members::Base + yield member, accessibility, nil + end + end + when AST::Ruby::Declarations::SingletonClassDecl + singleton_accessibility = :public #: Definition::accessibility + sclass = member + member.members.each do |member| + case member + when AST::Ruby::Members::PublicMember + singleton_accessibility = :public + when AST::Ruby::Members::PrivateMember + singleton_accessibility = :private + when AST::Ruby::Members::Base + yield member, singleton_accessibility, sclass + end + end + end + end + when AST::Declarations::Class, AST::Declarations::Module + decl.members.each do |member| + case member + when AST::Members::Public + accessibility = :public + when AST::Members::Private + accessibility = :private + when AST::Members::Base + yield member, accessibility, nil + end end end end diff --git a/lib/rbs/environment.rb b/lib/rbs/environment.rb index 03c27ba4c..3af7a1279 100644 --- a/lib/rbs/environment.rb +++ b/lib/rbs/environment.rb @@ -2,8 +2,6 @@ module RBS class Environment - attr_reader :declarations - attr_reader :class_decls attr_reader :interface_decls attr_reader :type_alias_decls @@ -12,115 +10,17 @@ class Environment attr_reader :class_alias_decls attr_reader :signatures - - module ContextUtil - def calculate_context(decls) - decls.inject(nil) do |context, decl| #$ Resolver::context - if (_, last = context) - last or raise - [context, last + decl.name] - else - [nil, decl.name.absolute!] - end - end - end - end - - class MultiEntry - D = _ = Struct.new(:decl, :outer, keyword_init: true) do - # @implements D[M] - - include ContextUtil - - def context - @context ||= calculate_context(outer + [decl]) - end - end - - attr_reader :name - attr_reader :decls - - def initialize(name:) - @name = name - @decls = [] - end - - def insert(decl:, outer:) - decls << D.new(decl: decl, outer: outer) - @primary = nil - end - - def validate_type_params - unless decls.empty? - hd_decl, *tl_decls = decls - raise unless hd_decl - - hd_params = hd_decl.decl.type_params - - tl_decls.each do |tl_decl| - tl_params = tl_decl.decl.type_params - - unless compatible_params?(hd_params, tl_params) - raise GenericParameterMismatchError.new(name: name, decl: _ = tl_decl.decl) - end - end - end - end - - def compatible_params?(ps1, ps2) - if ps1.size == ps2.size - ps1 == AST::TypeParam.rename(ps2, new_names: ps1.map(&:name)) - end - end - - def type_params - primary.decl.type_params - end - - def primary - raise "Not implemented" - end - end - - class ModuleEntry < MultiEntry - def self_types - decls.flat_map do |d| - d.decl.self_types - end.uniq - end - - def primary - @primary ||= begin - validate_type_params - decls.first or raise("decls cannot be empty") - end - end - end - - class ClassEntry < MultiEntry - def primary - @primary ||= begin - validate_type_params - decls.find {|d| d.decl.super_class } || decls.first or raise("decls cannot be empty") - end - end - end + attr_reader :sources class SingleEntry attr_reader :name - attr_reader :outer + attr_reader :context attr_reader :decl - def initialize(name:, decl:, outer:) + def initialize(name:, decl:, context:) @name = name @decl = decl - @outer = outer - end - - include ContextUtil - - def context - @context ||= calculate_context(outer) + @context = context end end @@ -143,8 +43,7 @@ class GlobalEntry < SingleEntry end def initialize - @signatures = {} - @declarations = [] + @sources = [] @class_decls = {} @interface_decls = {} @@ -156,8 +55,7 @@ def initialize end def initialize_copy(other) - @signatures = other.signatures.dup - @declarations = other.declarations.dup + @sources = other.sources.dup @class_decls = other.class_decls.dup @interface_decls = other.interface_decls.dup @@ -370,7 +268,48 @@ def normalize_module_name?(name) @normalize_module_name_cache[name] = normalized_type_name end - def insert_decl(decl, outer:, namespace:) + def insert_ruby_decl(decl, context: nil, namespace: Namespace.root) + case decl + when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl + class_name = decl.name.with_prefix(namespace) + + if cdecl = constant_entry(class_name) + if cdecl.is_a?(ConstantEntry) || cdecl.is_a?(ModuleAliasEntry) || cdecl.is_a?(ClassAliasEntry) + raise DuplicatedDeclarationError.new(class_name, decl, cdecl.decl) + end + end + + entry = class_decls[class_name] ||= + case decl + when AST::Ruby::Declarations::ClassDecl + ClassEntry.new(class_name) + when AST::Ruby::Declarations::ModuleDecl + ModuleEntry.new(class_name) + end + + case + when decl.is_a?(AST::Ruby::Declarations::ModuleDecl) && entry.is_a?(ModuleEntry) + entry << [context, decl] + when decl.is_a?(AST::Ruby::Declarations::ClassDecl) && entry.is_a?(ClassEntry) + entry << [context, decl] + else + raise DuplicatedDeclarationError.new(class_name, decl, entry.each_decl.first || raise) + end + + decl.each_decl do |decl| + next if decl.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + insert_ruby_decl(decl, context: [context, class_name], namespace: class_name.to_namespace) + end + when AST::Ruby::Declarations::EmbeddedRBSDecl + decl.each_decl do |decl| + insert_decl(decl, context: context, namespace: namespace) + end + else + raise "Unexpected decl: #{decl.class}" + end + end + + def insert_decl(decl, context: nil, namespace: Namespace.root) case decl when AST::Declarations::Class, AST::Declarations::Module name = decl.name.with_prefix(namespace) @@ -384,9 +323,9 @@ def insert_decl(decl, outer:, namespace:) unless class_decls.key?(name) case decl when AST::Declarations::Class - class_decls[name] ||= ClassEntry.new(name: name) + class_decls[name] ||= ClassEntry.new(name) when AST::Declarations::Module - class_decls[name] ||= ModuleEntry.new(name: name) + class_decls[name] ||= ModuleEntry.new(name) end end @@ -394,17 +333,15 @@ def insert_decl(decl, outer:, namespace:) case when decl.is_a?(AST::Declarations::Module) && existing_entry.is_a?(ModuleEntry) - existing_entry.insert(decl: decl, outer: outer) + existing_entry << [context, decl] when decl.is_a?(AST::Declarations::Class) && existing_entry.is_a?(ClassEntry) - existing_entry.insert(decl: decl, outer: outer) + existing_entry << [context, decl] else - raise DuplicatedDeclarationError.new(name, decl, existing_entry.decls[0].decl) + raise DuplicatedDeclarationError.new(name, decl, existing_entry.each_decl.first || raise) end - prefix = outer + [decl] - ns = name.to_namespace decl.each_decl do |d| - insert_decl(d, outer: prefix, namespace: ns) + insert_decl(d, context: [context, name], namespace: name.to_namespace) end when AST::Declarations::Interface @@ -414,7 +351,7 @@ def insert_decl(decl, outer:, namespace:) raise DuplicatedDeclarationError.new(name, decl, interface_entry.decl) end - interface_decls[name] = InterfaceEntry.new(name: name, decl: decl, outer: outer) + interface_decls[name] = InterfaceEntry.new(name: name, decl: decl, context: context) when AST::Declarations::TypeAlias name = decl.name.with_prefix(namespace) @@ -423,7 +360,7 @@ def insert_decl(decl, outer:, namespace:) raise DuplicatedDeclarationError.new(name, decl, entry.decl) end - type_alias_decls[name] = TypeAliasEntry.new(name: name, decl: decl, outer: outer) + type_alias_decls[name] = TypeAliasEntry.new(name: name, decl: decl, context: context) when AST::Declarations::Constant name = decl.name.with_prefix(namespace) @@ -433,18 +370,18 @@ def insert_decl(decl, outer:, namespace:) when ClassAliasEntry, ModuleAliasEntry, ConstantEntry raise DuplicatedDeclarationError.new(name, decl, entry.decl) when ClassEntry, ModuleEntry - raise DuplicatedDeclarationError.new(name, decl, *entry.decls.map(&:decl)) + raise DuplicatedDeclarationError.new(name, decl, *entry.each_decl.to_a) end end - constant_decls[name] = ConstantEntry.new(name: name, decl: decl, outer: outer) + constant_decls[name] = ConstantEntry.new(name: name, decl: decl, context: context) when AST::Declarations::Global if entry = global_decls[decl.name] raise DuplicatedDeclarationError.new(decl.name, decl, entry.decl) end - global_decls[decl.name] = GlobalEntry.new(name: decl.name, decl: decl, outer: outer) + global_decls[decl.name] = GlobalEntry.new(name: decl.name, decl: decl, context: context) when AST::Declarations::ClassAlias, AST::Declarations::ModuleAlias name = decl.new_name.with_prefix(namespace) @@ -454,35 +391,36 @@ def insert_decl(decl, outer:, namespace:) when ClassAliasEntry, ModuleAliasEntry, ConstantEntry raise DuplicatedDeclarationError.new(name, decl, entry.decl) when ClassEntry, ModuleEntry - raise DuplicatedDeclarationError.new(name, decl, *entry.decls.map(&:decl)) + raise DuplicatedDeclarationError.new(name, decl, *entry.each_decl.to_a) end end case decl when AST::Declarations::ClassAlias - class_alias_decls[name] = ClassAliasEntry.new(name: name, decl: decl, outer: outer) + class_alias_decls[name] = ClassAliasEntry.new(name: name, decl: decl, context: context) when AST::Declarations::ModuleAlias - class_alias_decls[name] = ModuleAliasEntry.new(name: name, decl: decl, outer: outer) + class_alias_decls[name] = ModuleAliasEntry.new(name: name, decl: decl, context: context) end end end - def <<(decl) - declarations << decl - insert_decl(decl, outer: [], namespace: Namespace.root) - self - end - - def add_signature(buffer:, directives:, decls:) - signatures[buffer] = [directives, decls] - decls.each do |decl| - self << decl + def add_signature(source) + sources << source + case source + when Source::RBS + source.declarations.each do |decl| + insert_decl(decl) + end + when Source::Ruby + source.declarations.each do |decl| + insert_ruby_decl(decl) + end end end def validate_type_params class_decls.each_value do |decl| - decl.primary + decl.validate_type_params end end @@ -501,7 +439,7 @@ def resolve_signature(resolver, table, dirs, decls, only: nil) if only && !only.member?(decl) decl else - resolve_declaration(resolver, map, decl, outer: [], prefix: Namespace.root) + resolve_declaration(resolver, map, decl, context: nil, prefix: Namespace.root) end end @@ -519,17 +457,76 @@ def resolve_type_names(only: nil) table.known_types.merge(interface_decls.keys) table.compute_children - signatures.each do |buffer, (dirs, decls)| - resolve = dirs.find { _1.is_a?(AST::Directives::ResolveTypeNames) } #: AST::Directives::ResolveTypeNames? + each_rbs_source do |source| + resolve = source.directives.find { _1.is_a?(AST::Directives::ResolveTypeNames) } #: AST::Directives::ResolveTypeNames? + if !resolve || resolve.value - _, decls = resolve_signature(resolver, table, dirs, decls) + map = UseMap.new(table: table) + source.directives.each do |dir| + if dir.is_a?(AST::Directives::Use) + dir.clauses.each do |clause| + map.build_map(clause) + end + end + end + + decls = source.declarations.map do |decl| + if only && !only.member?(decl) + decl + else + resolve_declaration(resolver, map, decl, context: nil, prefix: Namespace.root) + end + end + else + decls = source.declarations end - env.add_signature(buffer: buffer, directives: dirs, decls: decls) + + source = Source::RBS.new(source.buffer, source.directives, decls) + env.add_signature(source) + end + + each_ruby_source do |source| + map = UseMap.new(table: table) + + decls = source.declarations.map do |decl| + if only && !only.member?(decl) + decl + else + resolve_ruby_declaration(resolver, map, decl, context: nil, prefix: Namespace.root) + end + end + + source = Source::Ruby.new(source.buffer, source.prism_result, decls, source.diagnostics) + env.add_signature(source) end env end + def each_rbs_source(&block) + if block + sources.each do |source| + if source.is_a?(Source::RBS) + yield source + end + end + else + enum_for(__method__ || raise) + end + end + + def each_ruby_source(&block) + if block + sources.each do |source| + if source.is_a?(Source::Ruby) + yield source + end + end + else + enum_for(__method__ || raise) + end + end + def resolver_context(*nesting) nesting.inject(nil) {|context, decl| #$ Resolver::context append_context(context, decl) @@ -545,7 +542,105 @@ def append_context(context, decl) end end - def resolve_declaration(resolver, map, decl, outer:, prefix:) + def resolve_ruby_declaration(resolver, map, decl, context:, prefix:) + case decl + when AST::Ruby::Declarations::ClassDecl + class_name = decl.class_name.with_prefix(prefix) + inner_context = [context, class_name] #: Resolver::context + + AST::Ruby::Declarations::ClassDecl.new( + decl.buffer, + decl.node, + location: decl.location, + class_name: class_name, + generics: decl.generics.map_type_name {|name| absolute_type_name(resolver, map, name, context: inner_context) }, + super_node: decl.super_node&.map_type_name {|name| absolute_type_name(resolver, map, name, context: context) }, + super_annotation: decl.super_annotation&.map_type_name {|name| absolute_type_name(resolver, map, name, context: context) }, + class_name_location: decl.class_name_location + ).tap do |resolved| + prefix = class_name.to_namespace + decl.members.each do |member| + case member + when AST::Ruby::Members::Base + resolved.members << resolve_ruby_member(resolver, map, member, context: inner_context) + when AST::Ruby::Declarations::Base + if member.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + resolved.members << resolve_ruby_member(resolver, map, member, context: inner_context) + else + resolved.members << resolve_ruby_declaration(resolver, map, member, context: inner_context, prefix: prefix) + end + end + end + end + + when AST::Ruby::Declarations::ModuleDecl + module_name = decl.module_name.with_prefix(prefix) + inner_context = [context, module_name] #: Resolver::context + + AST::Ruby::Declarations::ModuleDecl.new( + decl.buffer, + decl.node, + location: decl.location, + module_name: module_name, + module_name_location: decl.module_name_location, + generics: decl.generics.map_type_name {|name| absolute_type_name(resolver, map, name, context: inner_context) }, + self_constraints: decl.self_constraints.map { _1.map_type_name {|name| absolute_type_name(resolver, map, name, context: inner_context) } }, + ).tap do |resolved| + prefix = module_name.to_namespace + decl.members.each do |member| + case member + when AST::Ruby::Members::Base + resolved.members << resolve_ruby_member(resolver, map, member, context: inner_context) + when AST::Ruby::Declarations::Base + if member.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + resolved.members << resolve_ruby_member(resolver, map, member, context: inner_context) + else + resolved.members << resolve_ruby_declaration(resolver, map, member, context: inner_context, prefix: prefix) + end + end + end + end + + when AST::Ruby::Declarations::EmbeddedRBSDecl + members = decl.members.map do |member| + case member + when AST::Members::Base + resolve_member(resolver, map, member, context: context) + when AST::Declarations::Base + resolve_declaration(resolver, map, member, context: context, prefix: prefix) + end + end + AST::Ruby::Declarations::EmbeddedRBSDecl.new(decl.buffer,decl.location, members) + end + end + + def resolve_ruby_member(resolver, map, member, context:) + case member + when AST::Ruby::Members::MixinMember + member.map_type_name do |name| + absolute_type_name(resolver, map, name, context: context) + end + when AST::Ruby::Declarations::SingletonClassDecl + resolved = AST::Ruby::Declarations::SingletonClassDecl.new(member.buffer, member.node) + member.members.each do |member| + member = resolve_ruby_member(resolver, map, member, context: context) #: AST::Ruby::Members::t + resolved.members << member + end + resolved + when AST::Ruby::Members::DefMember + member.map_type_name do |name| + absolute_type_name(resolver, map, name, context: context) + end + when AST::Ruby::Members::ClassVariableMember, AST::Ruby::Members::InstanceVariableMember, AST::Ruby::Members::ClassInstanceVariableMember + member.map_type_name do |name| + absolute_type_name(resolver, map, name, context: context) + end + else + member + end + end + + def resolve_declaration(resolver, map, decl, context:, prefix:) if decl.is_a?(AST::Declarations::Global) # @type var decl: AST::Declarations::Global return AST::Declarations::Global.new( @@ -557,14 +652,11 @@ def resolve_declaration(resolver, map, decl, outer:, prefix:) ) end - context = resolver_context(*outer) - case decl when AST::Declarations::Class outer_context = context inner_context = append_context(outer_context, decl) - outer_ = outer + [decl] prefix_ = prefix + decl.name.to_namespace AST::Declarations::Class.new( name: decl.name.with_prefix(prefix), @@ -585,7 +677,7 @@ def resolve_declaration(resolver, map, decl, outer:, prefix:) resolver, map, member, - outer: outer_, + context: inner_context, prefix: prefix_ ) else @@ -601,7 +693,6 @@ def resolve_declaration(resolver, map, decl, outer:, prefix:) outer_context = context inner_context = append_context(outer_context, decl) - outer_ = outer + [decl] prefix_ = prefix + decl.name.to_namespace AST::Declarations::Module.new( name: decl.name.with_prefix(prefix), @@ -622,7 +713,7 @@ def resolve_declaration(resolver, map, decl, outer:, prefix:) resolver, map, member, - outer: outer_, + context: inner_context, prefix: prefix_ ) else @@ -811,20 +902,25 @@ def absolute_type(resolver, map, type, context:) end def inspect - ivars = %i[@declarations @class_decls @class_alias_decls @interface_decls @type_alias_decls @constant_decls @global_decls] - "\#" + ivars = %i[@sources @class_decls @class_alias_decls @interface_decls @type_alias_decls @constant_decls @global_decls] + "\#" end def buffers - signatures.keys + sources.map { _1.buffer } end def unload(buffers) env = Environment.new - signatures.each do |buf, (dirs, decls)| - next if buffers.include?(buf) - env.add_signature(buffer: buf, directives: dirs, decls: decls) + each_rbs_source.each do |source| + next if buffers.include?(source.buffer) + env.add_signature(source) + end + + each_ruby_source.each do |source| + next if buffers.include?(source.buffer) + env.add_signature(source) end env diff --git a/lib/rbs/environment/class_entry.rb b/lib/rbs/environment/class_entry.rb new file mode 100644 index 000000000..121310cf6 --- /dev/null +++ b/lib/rbs/environment/class_entry.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module RBS + class Environment + class ClassEntry + attr_reader :name + + attr_reader :context_decls + + def initialize(name) + @name = name + @context_decls = [] + end + + def <<(context_decl) + context_decls << context_decl + @primary_decl = nil + self + end + + def each_decl(&block) + if block + context_decls.each do |_, decl| + yield decl + end + else + enum_for(__method__ || raise) + end + end + + def empty? + context_decls.empty? + end + + def primary_decl + @primary_decl ||= nil.tap do + # @type break: declaration + + decl = each_decl.find {|decl| decl.super_class } + break decl if decl + + decl = each_decl.first + break decl if decl + end || raise("Cannot find primary declaration for #{name}") + end + + def type_params + validate_type_params + primary_decl.type_params + end + + def validate_type_params + unless context_decls.empty? + first_decl, *rest_decls = each_decl.to_a + first_decl or raise + + first_params = first_decl.type_params + first_names = first_params.map(&:name) + rest_decls.each do |other_decl| + other_params = other_decl.type_params + unless first_names.size == other_params.size && first_params == AST::TypeParam.rename(other_params, new_names: first_names) + raise GenericParameterMismatchError.new(name: name, decl: other_decl) + end + end + end + end + end + end +end diff --git a/lib/rbs/environment/module_entry.rb b/lib/rbs/environment/module_entry.rb new file mode 100644 index 000000000..be466169c --- /dev/null +++ b/lib/rbs/environment/module_entry.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +module RBS + class Environment + class ModuleEntry + attr_reader :name + + attr_reader :context_decls + + def initialize(name) + @name = name + @context_decls = [] + end + + def <<(context_decl) + context_decls << context_decl + self + end + + def each_decl(&block) + if block + context_decls.each do |_, decl| + yield decl + end + else + enum_for(__method__ || raise) + end + end + + def empty? + context_decls.empty? + end + + def primary_decl + each_decl.first or raise + end + + def type_params + validate_type_params + primary_decl.type_params + end + + def self_types + each_decl.flat_map do |decl| + decl.self_types + end.uniq + end + + def validate_type_params + unless context_decls.empty? + first_decl, *rest_decls = each_decl.to_a + first_decl or raise + + first_params = first_decl.type_params + first_names = first_params.map(&:name) + rest_decls.each do |other_decl| + other_params = other_decl.type_params + unless first_names.size == other_params.size && first_params == AST::TypeParam.rename(other_params, new_names: first_names) + raise GenericParameterMismatchError.new(name: name, decl: other_decl) + end + end + end + end + end + end +end diff --git a/lib/rbs/environment_loader.rb b/lib/rbs/environment_loader.rb index cdfde8335..4bcd588f5 100644 --- a/lib/rbs/environment_loader.rb +++ b/lib/rbs/environment_loader.rb @@ -122,7 +122,8 @@ def load(env:) decls.each do |decl| loaded << [decl, path, source] end - env.add_signature(buffer: buffer, directives: dirs, decls: decls) + source = Source::RBS.new(buffer, dirs, decls) + env.add_signature(source) end loaded @@ -161,7 +162,7 @@ def each_signature next if files.include?(path) files << path - buffer = Buffer.new(name: path.to_s, content: path.read(encoding: "UTF-8")) + buffer = Buffer.new(name: path, content: path.read(encoding: "UTF-8")) _, dirs, decls = Parser.parse_signature(buffer) diff --git a/lib/rbs/errors.rb b/lib/rbs/errors.rb index 88ceb69f8..6171b831b 100644 --- a/lib/rbs/errors.rb +++ b/lib/rbs/errors.rb @@ -325,16 +325,24 @@ def type_name class VariableDuplicationError < DefinitionError include DetailedMessageable + attr_reader :member attr_reader :type_name - attr_reader :variable_name - attr_reader :location - def initialize(type_name:, variable_name:, location:) + def initialize(type_name:, member:) + @member = member @type_name = type_name - @variable_name = variable_name - @location = location - super "#{Location.to_string location}: Duplicated #{kind} variable name `#{variable_name}` in `#{type_name}`" + super "#{Location.to_string location}: Duplicated #{kind} variable name `#{member.name}` in `#{type_name}`" + end + + def location + case member + when AST::Members::Base + loc = @member.location or raise + loc[:name] || raise + when AST::Ruby::Members::Base + member.annotation.var_name_location.absolute_location + end end end @@ -371,7 +379,14 @@ class SuperclassMismatchError < DefinitionError def initialize(name:, entry:) @name = name @entry = entry - super "#{Location.to_string entry.primary.decl.location}: Superclass mismatch: #{name}" + loc = + case entry.primary_decl + when AST::Declarations::Class, AST::Declarations::Module + Location.to_string entry.primary_decl.location + when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl + entry.primary_decl.node.location.to_s + end + super "#{loc}: Superclass mismatch: #{name}" end end @@ -411,7 +426,14 @@ class GenericParameterMismatchError < LoadingError def initialize(name:, decl:) @name = name @decl = decl - super "#{Location.to_string decl.location}: Generic parameters mismatch: #{name}" + location = + case decl + when AST::Declarations::Class, AST::Declarations::Module + Location.to_string decl.location + when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl + decl.node.location.to_s + end + super "#{location}: Generic parameters mismatch: #{name}" end end @@ -424,7 +446,18 @@ def initialize(name, *decls) @decls = decls last_decl = decls.last or raise - super "#{Location.to_string last_decl.location}: Duplicated declaration: #{name}" + loc = + case last_decl + when AST::Declarations::Base + Location.to_string last_decl.location + when AST::Ruby::Declarations::Base + if last_decl.is_a?(AST::Ruby::Declarations::RBSDecl) + raise + else + last_decl.node.location.to_s + end + end + super "#{loc}: Duplicated declaration: #{name}" end end @@ -474,7 +507,7 @@ def initialize(type_name:, member:) @type_name = type_name @member = member - super "#{Location.to_string member.location}: Cannot #{mixin_name} a class `#{member.name}` in the definition of `#{type_name}`" + super "#{Location.to_string member.location}: Cannot #{mixin_name} a class `#{member.module_name}` in the definition of `#{type_name}`" end def location @@ -482,7 +515,7 @@ def location end def self.check!(type_name:, env:, member:) - if env.class_decl?(member.name) + if env.class_decl?(member.module_name) raise new(type_name: type_name, member: member) end end diff --git a/lib/rbs/inline/comment_association.rb b/lib/rbs/inline/comment_association.rb new file mode 100644 index 000000000..d4a6362b7 --- /dev/null +++ b/lib/rbs/inline/comment_association.rb @@ -0,0 +1,87 @@ +module RBS + module Inline + class CommentAssociation + attr_reader :blocks, :associated_blocks, :start_line_map, :end_line_map + + def initialize(blocks) + @blocks = blocks.sort_by {|block| block.start_line } + @associated_blocks = Set[].compare_by_identity + + @start_line_map = {} + @end_line_map = {} + + blocks.each do |block| + if block.leading? + end_line_map[block.end_line] = block + else + start_line_map[block.start_line] = block + end + end + end + + def self.build(buffer, result) + blocks = AST::Ruby::CommentBlock.build(buffer, result.comments) + new(blocks) + end + + def leading_block(node) + start_line = node.location.start_line + + if block = end_line_map.fetch(start_line - 1, nil) + unless associated_blocks.include?(block) + associated_blocks << block + block + end + end + end + + def trailing_block(node) + location = + if node.is_a?(Prism::Node) + node.location + else + node + end #: Prism::Location + end_line = location.end_line + if block = start_line_map.fetch(end_line, nil) + unless associated_blocks.include?(block) + associated_blocks << block + block + end + end + end + + def each_enclosed_block(node) + if block_given? + start_line = node.location.start_line + end_line = node.location.end_line + + if start_line+1 < end_line + ((start_line + 1)...end_line).each do |line| + if block = end_line_map.fetch(line, nil) + unless associated_blocks.include?(block) + associated_blocks << block + yield block + end + end + end + end + else + enum_for :each_enclosed_block, node + end + end + + def each_unassociated_block + if block_given? + blocks.each do |block| + unless associated_blocks.include?(block) + yield block + end + end + else + enum_for :each_unassociated_block + end + end + end + end +end diff --git a/lib/rbs/inline_parser.rb b/lib/rbs/inline_parser.rb new file mode 100644 index 000000000..7e1c56422 --- /dev/null +++ b/lib/rbs/inline_parser.rb @@ -0,0 +1,857 @@ +# frozen_string_literal: true + +module RBS + class InlineParser + class Result + attr_reader :buffer + attr_reader :prism_result + attr_reader :declarations + attr_reader :diagnostics + + def initialize(buffer, result) + @buffer = buffer + @prism_result = result + + @declarations = [] + @diagnostics = [] + end + end + + module Diagnostics + class Base + attr_reader :location + attr_reader :message + + def initialize(location, message) + @location = location + @message = message + end + end + + class NonConstantName < Base + end + + class ToplevelSingletonClass < Base + end + + class DeclarationInsideSingletonClass < Base + end + + class NonConstantSuperClass < Base + end + + class NonSelfSingletonClass < Base + end + + class InvalidVisibilityCall < Base + end + + class InvalidMixinCall < Base + end + + class UnusedAnnotation < Base + end + + class AnnotationSyntaxError < Base + end + + class VariableAnnotationInSingletonClassError < Base + end + + class TopLevelAliasError < Base + end + + class MethodNameAliasNonLiteralError < Base + end + + class UnexpectedSingletonClassMemberError < Base + end + + class TopLevelMethodDefinition < Base + end + + class TopLevelMixinError < Base + end + + class AttributeDefinitionError < Base + end + end + + def self.enabled?(result, default:) + result.comments.each do |comment| + if comment.location.start_character_column == 0 + if comment.location.slice == "# rbs_inline: enabled" + return true + end + if comment.location.slice == "# rbs_inline: disabled" + return false + end + end + end + + return default + end + + def self.parse(buffer, prism) + result = Result.new(buffer, prism) + association = Inline::CommentAssociation.build(buffer, prism) + + parser = Parser.new(result, association) + parser.parse() + + result + end + + class Parser < Prism::Visitor + attr_reader :result + attr_reader :decl_contexts + attr_reader :comments + + def buffer + result.buffer + end + + def diagnostics + result.diagnostics + end + + def current_context + decl_contexts.last + end + + def current_context! + current_context or raise "No context" + end + + def initialize(result, comments) + @result = result + @comments = comments + @decl_contexts = [] + @type_params_stack = [] + @method_visibility_stack = [] + end + + def parse() + decl_contexts.clear + visit(result.prism_result.value) + end + + def push_decl_context(context) + decl_contexts.push(context) + yield + ensure + decl_contexts.pop + end + + def insert_class_module_decl(decl) + if current_context + current_context.is_a?(AST::Ruby::Declarations::SingletonClassDecl) and raise + current_context.members << decl + else + result.declarations << decl + end + end + + def visit_class_node(node) + unless class_name = AST::Ruby::Helpers::ConstantHelper.constant_as_type_name(node.constant_path) + diagnostics << Diagnostics::NonConstantName.new( + buffer.rbs_location(node.constant_path.location), + "Class name should be constant" + ) + return + end + class_name_location = buffer.rbs_location(node.constant_path.location) + + if current_context.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + diagnostics << Diagnostics::DeclarationInsideSingletonClass.new( + buffer.rbs_location(node.constant_path.location), + "Class definition inside singleton class definition is ignored" + ) + return + end + + leading_block = comments.leading_block(node) + leading_comments = [] #: Array[AST::Ruby::Annotation::leading_annotation] + leading_block&.each_paragraph([]) do |paragraph| + case paragraph + when Location + when AST::Ruby::CommentBlock::AnnotationSyntaxError + report_annotation_syntax_error(paragraph) + else + leading_comments << paragraph + end + end + + generics, leading_comments = AST::Ruby::Declarations::GenericsTypeParams.build(leading_comments) + super_annotation, leading_comments = AST::Ruby::Declarations::ClassDecl::SuperAnnotation.build(leading_comments) + + push_type_params(generics.type_params) do + if super_node_ = node.superclass + trailing_block = comments.trailing_block(super_node_) + + visit super_node_ + + if super_class_name = AST::Ruby::Helpers::ConstantHelper.constant_as_type_name(super_node_) + super_class_name_location = buffer.rbs_location(super_node_.location) + + open_paren_location = nil #: Location? + type_args = [] #: Array[Types::t] + close_paren_location = nil #: Location? + + if trailing_block + type_param_names = generics.type_params.map(&:name) + if (app = trailing_block.trailing_annotation(type_param_names)).is_a?(AST::Ruby::Annotation::NodeApplication) + open_paren_location = app.prefix_location + type_args = app.types + close_paren_location = app.suffix_location + end + end + + super_node = AST::Ruby::Declarations::ClassDecl::SuperNode.new( + class_name: super_class_name, + class_name_location: super_class_name_location, + location: class_name_location, + type_args: type_args, + open_paren_location: open_paren_location, + close_paren_location: close_paren_location + ) + else + unless super_annotation + diagnostics << Diagnostics::NonConstantSuperClass.new( + buffer.rbs_location(super_node_.location), + "Super class of #{class_name} should be constant" + ) + end + end + end + + if leading_block + leading_comments.each do |annotation| + report_unused_annotation(annotation) + end + end + + decl = AST::Ruby::Declarations::ClassDecl.new( + buffer, + node, + class_name: class_name, + location: buffer.rbs_location(node.location), + class_name_location: class_name_location, + generics: generics, + super_node: super_node, + super_annotation: super_annotation + ) + + insert_class_module_decl(decl) + push_decl_context(decl) do + visit node.body + end + + comments.each_enclosed_block(node) do |block| + block.each_paragraph(current_type_param_names) do |paragraph| + case paragraph + when Location + # skip + when AST::Ruby::CommentBlock::AnnotationSyntaxError + report_annotation_syntax_error(paragraph) + when AST::Ruby::Annotation::IvarTypeAnnotation + name = paragraph.var_name_location.source.to_sym + type = paragraph.type + decl.members << AST::Ruby::Members::InstanceVariableMember.new(buffer, name, type, paragraph) + when AST::Ruby::Annotation::ClassIvarTypeAnnotation + name = paragraph.var_name_location.source.to_sym + type = paragraph.type + decl.members << AST::Ruby::Members::ClassInstanceVariableMember.new(buffer, name, type, paragraph) + when AST::Ruby::Annotation::ClassVarTypeAnnotation + name = paragraph.var_name_location.source.to_sym + type = paragraph.type + decl.members << AST::Ruby::Members::ClassVariableMember.new(buffer, name, type, paragraph) + when AST::Ruby::Annotation::EmbeddedRBSAnnotation + decl.members << AST::Ruby::Declarations::EmbeddedRBSDecl.new( + buffer, + paragraph.location.absolute_location, + paragraph.members + ) + else + report_unused_annotation(paragraph) + end + end + end + + decl.members.sort_by! { _1.location.absolute_location.start_line } + end + end + + def visit_singleton_class_node(node) + unless node.expression.is_a?(Prism::SelfNode) + diagnostics << Diagnostics::NonSelfSingletonClass.new( + buffer.rbs_location(node.expression.location), + "Singleton class should be defined with `self` is ignored" + ) + return + end + unless current_context + diagnostics << Diagnostics::ToplevelSingletonClass.new( + buffer.rbs_location(node.operator_loc, node.expression.location), + "Toplevel singleton class definition is ignored" + ) + return + end + if current_context.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + diagnostics << Diagnostics::DeclarationInsideSingletonClass.new( + buffer.rbs_location(node.operator_loc, node.expression.location), + "Singleton class definition inside another singleton class definition is ignored" + ) + return + end + + decl = AST::Ruby::Declarations::SingletonClassDecl.new(buffer, node) + + push_type_params([]) do + current_context.members << decl + push_decl_context(decl) do + visit node.body + end + + comments.each_enclosed_block(node) do |block| + block.each_paragraph(current_type_param_names) do |paragraph| + case paragraph + when Location + # skip + when AST::Ruby::CommentBlock::AnnotationSyntaxError + report_annotation_syntax_error(paragraph) + when AST::Ruby::Annotation::IvarTypeAnnotation, AST::Ruby::Annotation::ClassIvarTypeAnnotation, AST::Ruby::Annotation::ClassVarTypeAnnotation + diagnostics << Diagnostics::VariableAnnotationInSingletonClassError.new( + paragraph.location, + "Variable type definition cannot be included in singleton class definition", + ) + else + report_unused_annotation(paragraph) + end + end + end + end + end + + def visit_module_node(node) + unless module_name = AST::Ruby::Helpers::ConstantHelper.constant_as_type_name(node.constant_path) + diagnostics << Diagnostics::NonConstantName.new( + buffer.rbs_location(node.constant_path.location), + "Module name should be constant" + ) + return + end + module_name_location = buffer.rbs_location(node.constant_path.location) + + if current_context.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + diagnostics << Diagnostics::DeclarationInsideSingletonClass.new( + module_name_location, + "Module definition inside singleton class definition is ignored" + ) + return + end + + leading_block = comments.leading_block(node) + leading_comments = [] #: Array[AST::Ruby::Annotation::leading_annotation] + leading_block&.each_paragraph([]) do |paragraph| + case paragraph + when Location + when AST::Ruby::CommentBlock::AnnotationSyntaxError + report_annotation_syntax_error(paragraph) + else + leading_comments << paragraph + end + end + + generics, leading_comments = AST::Ruby::Declarations::GenericsTypeParams.build(leading_comments) + push_type_params(generics.type_params) do + self_constraints, leading_comments = AST::Ruby::Declarations::ModuleDecl::SelfConstraint.build(leading_comments) + + if leading_block + leading_comments.each do |comment| + report_unused_annotation(comment) + end + end + + decl = AST::Ruby::Declarations::ModuleDecl.new( + buffer, + node, + location: buffer.rbs_location(node.location), + module_name: module_name, + module_name_location: module_name_location, + generics: generics, + self_constraints: self_constraints + ) + + insert_class_module_decl(decl) + push_decl_context(decl) do + visit node.body + end + + comments.each_enclosed_block(node) do |block| + block.each_paragraph(current_type_param_names) do |paragraph| + case paragraph + when Location + # skip + when AST::Ruby::CommentBlock::AnnotationSyntaxError + report_annotation_syntax_error(paragraph) + when AST::Ruby::Annotation::IvarTypeAnnotation + name = paragraph.var_name_location.source.to_sym + type = paragraph.type + decl.members << AST::Ruby::Members::InstanceVariableMember.new(buffer, name, type, paragraph) + when AST::Ruby::Annotation::ClassIvarTypeAnnotation + name = paragraph.var_name_location.source.to_sym + type = paragraph.type + decl.members << AST::Ruby::Members::ClassInstanceVariableMember.new(buffer, name, type, paragraph) + when AST::Ruby::Annotation::ClassVarTypeAnnotation + name = paragraph.var_name_location.source.to_sym + type = paragraph.type + decl.members << AST::Ruby::Members::ClassVariableMember.new(buffer, name, type, paragraph) + when AST::Ruby::Annotation::EmbeddedRBSAnnotation + decl.members << AST::Ruby::Declarations::EmbeddedRBSDecl.new( + buffer, + paragraph.location.absolute_location, + paragraph.members + ) + else + report_unused_annotation(paragraph) + end + end + end + + decl.members.sort_by! { _1.location.absolute_location.start_line } + end + end + + def visit_def_node(node) + leading_block = comments.leading_block(node) + trailing_block = comments.trailing_block(node.parameters ? node.parameters.location : node.name_loc) + annotations, unused_annots, unused_trailing_annot = AST::Ruby::Members::DefAnnotations.build(leading_block, trailing_block, current_type_param_names) + + pairs = [] #: Array[[AST::Ruby::CommentBlock, AST::Ruby::Annotation::t | AST::Ruby::CommentBlock::AnnotationSyntaxError]] + if leading_block + pairs.concat unused_annots.map { [leading_block, _1] } + end + if trailing_block && unused_trailing_annot + pairs << [trailing_block, unused_trailing_annot] + end + + pairs.each do |block, annot| + case annot + when AST::Ruby::CommentBlock::AnnotationSyntaxError + report_annotation_syntax_error(annot) + else + report_unused_annotation(annot) + end + end + + case context = current_context + when AST::Ruby::Declarations::SingletonClassDecl + if node.receiver + diagnostics << Diagnostics::UnexpectedSingletonClassMemberError.new( + buffer.rbs_location(node.location), + "Singleton class cannot have singleton method definition" + ) + return + end + member = AST::Ruby::Members::DefMember.new(buffer, node, name: node.name, inline_annotations: annotations, visibility: current_method_visibility) + context.members << member + when AST::Ruby::Declarations::ModuleDecl, AST::Ruby::Declarations::ClassDecl + if node.receiver + member = AST::Ruby::Members::DefSingletonMember.new(buffer, node, name: node.name, inline_annotations: annotations, visibility: current_method_visibility) + if member.self? + context.members << member + end + else + member = AST::Ruby::Members::DefMember.new(buffer, node, name: node.name, inline_annotations: annotations, visibility: current_method_visibility) + current_context.members << member + end + else + diagnostics << Diagnostics::TopLevelMethodDefinition.new( + buffer.rbs_location(node.location), + "Method definition outside of class/module definition is ignored" + ) + end + end + + def visit_constant_write_node(node) + decl = AST::Ruby::Declarations::ConstantDecl.new(buffer, node) + + if current_context + unless current_context.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + current_context.members << decl + end + else + result.declarations << decl + end + + visit_child_nodes node + end + + def visit_call_node(node) + case + when member = visibility_member?(node) + current_context!.members << member + when visibility = visibility_modifier?(node) + push_method_visibility(visibility) do + visit_child_nodes(node) + end + when member = mixin_member?(node) + case context = current_context + when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl + context.members << member + when AST::Ruby::Declarations::SingletonClassDecl + diagnostics << Diagnostics::UnexpectedSingletonClassMemberError.new( + buffer.rbs_location(node.location), + "Singleton class cannot have mixin definition" + ) + else + # mixin_member? confirms if the context present + raise + end + when member = attribute_member?(node) + current_context!.members << member + end + end + + def visit_alias_method_node(node) + unless current_context + diagnostics << Diagnostics::TopLevelAliasError.new( + buffer.rbs_location(node.location), + "Alias definition outside of class/module definition is ignored" + ) + return + end + + unless node.old_name.is_a?(Prism::SymbolNode) + diagnostics << Diagnostics::MethodNameAliasNonLiteralError.new( + buffer.rbs_location(node.old_name.location), + "Method alias cannot have non-literal name" + ) + return + end + + unless node.new_name.is_a?(Prism::SymbolNode) + diagnostics << Diagnostics::MethodNameAliasNonLiteralError.new( + buffer.rbs_location(node.new_name.location), + "Method alias cannot have non-literal name" + ) + return + end + + member = AST::Ruby::Members::AliasMember.new(buffer, node) + current_context!.members << member + end + + def visibility_modifier?(node) + if node.name == :private || node.name == :public + if self_call?(node) + unless no_argument?(node) + if current_context + case node.name + when :private + :private + when :public + :public + end + end + end + end + end + end + + def visibility_member?(node) + if node.name == :private || node.name == :public + unless current_context + diagnostics << Diagnostics::InvalidVisibilityCall.new( + buffer.rbs_location(node.message_loc || raise), + "`#{node.name}` call outside of class/module definition is ignored" + ) + return + end + unless self_call?(node) + receiver = node.receiver or raise + diagnostics << Diagnostics::InvalidVisibilityCall.new( + buffer.rbs_location(receiver.location), + "`#{node.name}` call with non-self receiver is ignored" + ) + return + end + + if no_argument?(node) + if node.name == :private + AST::Ruby::Members::PrivateMember.new(buffer, node) + else + AST::Ruby::Members::PublicMember.new(buffer, node) + end + else + args = node.arguments or raise + diagnostics << Diagnostics::InvalidVisibilityCall.new( + buffer.rbs_location(args.location), + "`#{node.name}` call with arguments is ignored" + ) + nil + end + end + end + + def attribute_member?(node) + case node.name + when :attr_reader, :attr_writer, :attr_accessor + unless current_context + diagnostics << Diagnostics::AttributeDefinitionError.new( + buffer.rbs_location(node.message_loc || raise), + "`#{node.name}` call outside of class/module definition is ignored" + ) + return + end + unless self_call?(node) + diagnostics << Diagnostics::AttributeDefinitionError.new( + buffer.rbs_location(node.message_loc || raise), + "`#{node.name}` call with receiver other than `self` is ignored" + ) + return + end + + attr_names = [] #: Array[Symbol] + if node.arguments + node.arguments.child_nodes.each do |arg| + if arg.is_a?(Prism::SymbolNode) + if value = arg.value + attr_names << value.to_sym + end + else + if arg + diagnostics << Diagnostics::AttributeDefinitionError.new( + buffer.rbs_location(arg.location), + "`#{node.name}` call argument other than symbol literal is ignored" + ) + end + end + end + end + + if attr_names.empty? + return + end + + if trailing_block = comments.trailing_block(node) + case annotation = trailing_block.trailing_annotation(current_type_param_names) + when AST::Ruby::Annotation::NodeTypeAssertion + assertion = annotation + end + end + + case node.name + when :attr_reader + AST::Ruby::Members::AttrReaderMember.new(buffer, node, attr_names, assertion, current_method_visibility) + when :attr_accessor + AST::Ruby::Members::AttrAccessorMember.new(buffer, node, attr_names, assertion, current_method_visibility) + when :attr_writer + AST::Ruby::Members::AttrWriterMember.new(buffer, node, attr_names, assertion, current_method_visibility) + end + end + end + + def pop_type_params + @type_params_stack.pop + end + + def push_type_params(params, &block) + @type_params_stack.push(params) + + if block + begin + yield + ensure + pop_type_params + end + end + end + + def current_type_params + @type_params_stack.last || [] + end + + def current_type_param_names + current_type_params.map(&:name) + end + + def current_type_param_names + if context = current_context + case context + when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl + context.type_params.map(&:name) + else + [] + end + else + [] + end + end + + def mixin_member?(node) + case node.name + when :include, :prepend, :extend + unless current_context + diagnostics << Diagnostics::InvalidMixinCall.new( + buffer.rbs_location(node.message_loc || raise), + "`#{node.name}` call outside of class/module definition is ignored" + ) + return + end + unless self_call?(node) + receiver = node.receiver or raise + diagnostics << Diagnostics::InvalidMixinCall.new( + buffer.rbs_location(receiver.location), + "`#{node.name}` call with non-self receiver is ignored" + ) + return + end + + type_param_names = + if node.name == :extend + [] #: Array[Symbol] + else + current_type_param_names + end + + if arg = one_argument?(node) + if const_node = constant_node?(arg) + block = comments.trailing_block(node.location) + + module_name = AST::Ruby::Helpers::ConstantHelper.constant_as_type_name(const_node) or return + location = buffer.rbs_location(node.location) + module_name_location = buffer.rbs_location(const_node.location) + open_paren_location = nil #: Location? + close_paren_location = nil #: Location? + type_args = [] #: Array[Types::t] + + if block + if (application = block.trailing_annotation(type_param_names)).is_a?(AST::Ruby::Annotation::NodeApplication) + open_paren_location = application.prefix_location + close_paren_location = application.suffix_location + type_args = application.types + end + end + + case node.name + when :include + AST::Ruby::Members::IncludeMember.new( + buffer, + node, + location: location, + module_name: module_name, + module_name_location: module_name_location, + type_args: type_args, + open_paren_location: open_paren_location, + close_paren_location: close_paren_location + ) + when :prepend + AST::Ruby::Members::PrependMember.new( + buffer, + node, + location: location, + module_name: module_name, + module_name_location: module_name_location, + type_args: type_args, + open_paren_location: open_paren_location, + close_paren_location: close_paren_location + ) + when :extend + AST::Ruby::Members::ExtendMember.new( + buffer, + node, + location: location, + module_name: module_name, + module_name_location: module_name_location, + type_args: type_args, + open_paren_location: open_paren_location, + close_paren_location: close_paren_location + ) + end + else + diagnostics << Diagnostics::InvalidMixinCall.new( + buffer.rbs_location(arg.location), + "`#{node.name}` call with non-constant argument is ignored" + ) + nil + end + else + diagnostics << Diagnostics::InvalidMixinCall.new( + buffer.rbs_location(node.arguments&.location || node.location), + "`#{node.name}` call without argument/with more than one arguments is ignored" + ) + nil + end + end + end + + def constant_node?(node) + case node + when Prism::ConstantReadNode + node + when Prism::ConstantPathNode + if node.parent + if constant_node?(node.parent) + node + end + else + node + end + end + end + + def no_argument?(node) + return true unless node.arguments + + return false if node.arguments.contains_forwarding? + return false if node.arguments.contains_keywords? + return false if node.arguments.contains_keyword_splat? + return false if node.arguments.contains_splat? + return false if node.arguments.contains_multiple_splats? + return false if node.block + + return node.arguments.arguments.size == 0 + end + + def one_argument?(node) + return unless node.arguments + + return if node.arguments.contains_forwarding? + return if node.arguments.contains_keywords? + return if node.arguments.contains_keyword_splat? + return if node.arguments.contains_splat? + return if node.arguments.contains_multiple_splats? + return if node.block + + if node.arguments.arguments.size == 1 + node.arguments.arguments[0] + end + end + + def self_call?(node) + !node.receiver || node.receiver.is_a?(Prism::SelfNode) + end + + def report_annotation_syntax_error(error) + diagnostics << Diagnostics::AnnotationSyntaxError.new(error.location, "Annotation syntax error: #{error.error.message}") + end + + def report_unused_annotation(annot) + diagnostics << Diagnostics::UnusedAnnotation.new(annot.location, "Unused annotation") + end + + def push_method_visibility(visibility) + @method_visibility_stack.push(visibility) + yield + ensure + @method_visibility_stack.pop + end + + def current_method_visibility + @method_visibility_stack.last + end + end + end +end diff --git a/lib/rbs/location_aux.rb b/lib/rbs/location_aux.rb index de0413686..054561206 100644 --- a/lib/rbs/location_aux.rb +++ b/lib/rbs/location_aux.rb @@ -127,6 +127,17 @@ def key?(name) optional_key?(name) || required_key?(name) end + def +(other) + if other + unless other.buffer == buffer + raise "Cannot concat two locations with different buffers" + end + Location.new(buffer, start_pos, other.end_pos) + else + self + end + end + def optional_key?(name) _optional_keys.include?(name) end @@ -134,5 +145,33 @@ def optional_key?(name) def required_key?(name) _required_keys.include?(name) end + + def absolute_location() + if parent = buffer.parent_buffer + top_buffer = parent.top_buffer + top_start = buffer.absolute_position(start_pos) or raise + top_end = buffer.absolute_position(end_pos) or raise + + Location.new(top_buffer, top_start, top_end).tap do |location| + each_optional_key do |key| + if loc = self[key] + opt_start = buffer.absolute_position(loc.start_pos) or raise + opt_end = buffer.absolute_position(loc.end_pos) or raise + location.add_optional_child(key, opt_start...opt_end) + else + location.add_optional_child(key, nil) + end + end + each_required_key do |key| + loc = self[key] or raise + req_start = buffer.absolute_position(loc.start_pos) or raise + req_end = buffer.absolute_position(loc.end_pos) or raise + location.add_optional_child(key, req_start...req_end) + end + end #: self + else + self + end + end end end diff --git a/lib/rbs/parser_aux.rb b/lib/rbs/parser_aux.rb index ea4ecd82d..76f0e0751 100644 --- a/lib/rbs/parser_aux.rb +++ b/lib/rbs/parser_aux.rb @@ -59,6 +59,16 @@ def self.magic_comment(buf) end end + def self.parse_inline(source, range, variables: []) + buf = buffer(source) + _parse_inline(buf, range.begin || 0, range.end || buf.last_position, variables) + end + + def self.parse_inline_assertion(source, range, variables: []) + buf = buffer(source) + _parse_inline_assertion(buf, range.begin || 0, range.end || buf.last_position, variables) + end + def self.lex(source) buf = buffer(source) list = _lex(buf, buf.last_position) @@ -71,7 +81,7 @@ def self.lex(source) def self.buffer(source) case source when String - Buffer.new(content: source, name: "a.rbs") + Buffer.new(content: source, name: Pathname("a.rbs")) when Buffer source end diff --git a/lib/rbs/prototype/runtime.rb b/lib/rbs/prototype/runtime.rb index 7a8548b0c..1b3b1d9f9 100644 --- a/lib/rbs/prototype/runtime.rb +++ b/lib/rbs/prototype/runtime.rb @@ -50,8 +50,8 @@ def mixin_decls(type_name) type_name_absolute = type_name.absolute! @mixin_decls_cache ||= {} #: Hash[TypeName, Array[AST::Members::Mixin]] @mixin_decls_cache.fetch(type_name_absolute) do - @mixin_decls_cache[type_name_absolute] = @builder.env.class_decls[type_name_absolute].decls.flat_map do |d| - d.decl.members.select { |m| m.kind_of?(AST::Members::Mixin) } + @mixin_decls_cache[type_name_absolute] = @builder.env.class_decls[type_name_absolute].each_decl.flat_map do |decl| + decl.members.select { |m| m.kind_of?(AST::Members::Mixin) } end end end diff --git a/lib/rbs/source.rb b/lib/rbs/source.rb new file mode 100644 index 000000000..4119dd906 --- /dev/null +++ b/lib/rbs/source.rb @@ -0,0 +1,101 @@ +module RBS + module Source + class RBS + attr_reader :buffer + attr_reader :directives + attr_reader :declarations + + def initialize(buffer, directives, declarations) + @buffer = buffer + @directives = directives + @declarations = declarations + end + + def each_type_name(&block) + if block + set = Set[] #: Set[TypeName] + declarations.each do |decl| + each_declaration_type_name(set, decl, &block) + end + else + enum_for :each_type_name + end + end + + def each_declaration_type_name(names, decl, &block) + case decl + when AST::Declarations::Class + decl.each_decl { each_declaration_type_name(names, _1, &block) } + type_name = decl.name + when AST::Declarations::Module + decl.each_decl { each_declaration_type_name(names, _1, &block) } + type_name = decl.name + when AST::Declarations::Interface + type_name = decl.name + when AST::Declarations::TypeAlias + type_name = decl.name + when AST::Declarations::ModuleAlias + type_name = decl.new_name + when AST::Declarations::ClassAlias + type_name = decl.new_name + end + + if type_name + unless names.include?(type_name) + yield type_name + names << type_name + end + end + end + end + + class Ruby + attr_reader :buffer + attr_reader :prism_result + attr_reader :declarations + attr_reader :diagnostics + + def initialize(buffer, prism, declarations, diagnostics) + @buffer = buffer + @prism_result = prism + @declarations = declarations + @diagnostics = diagnostics + end + + def each_type_name(&block) + if block + names = Set[] #: Set[TypeName] + declarations.each do |decl| + each_declaration_type_name(names, decl, &block) + end + else + enum_for :each_type_name + end + end + + def each_declaration_type_name(names, decl, &block) + case decl + when AST::Ruby::Declarations::ClassDecl + decl.each_decl do |d| + next if d.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + each_declaration_type_name(names, d, &block) + end + type_name = decl.class_name + when AST::Ruby::Declarations::ModuleDecl + decl.each_decl do |d| + next if d.is_a?(AST::Ruby::Declarations::SingletonClassDecl) + each_declaration_type_name(names, d, &block) + end + type_name = decl.module_name + end + + if type_name + unless names.include?(type_name) + yield type_name + names << type_name + end + end + end + end + end +end diff --git a/lib/rbs/subtractor.rb b/lib/rbs/subtractor.rb index ed8fa14f1..ed965b98c 100644 --- a/lib/rbs/subtractor.rb +++ b/lib/rbs/subtractor.rb @@ -129,7 +129,7 @@ def call(minuend = @minuend, context: nil) entry = @subtrahend.class_decls[owner] return unless entry - decls = entry.decls.map { |d| d.decl } + decls = entry.each_decl.to_a decls.each { |d| d.members.each { |m| block.call(m) } } end diff --git a/rbs.gemspec b/rbs.gemspec index a95dd6822..0c66dab4a 100644 --- a/rbs.gemspec +++ b/rbs.gemspec @@ -46,4 +46,5 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.required_ruby_version = ">= 3.1" spec.add_dependency "logger" + spec.add_dependency "prism", ">= 1.3.0" end diff --git a/sig/ancestor_builder.rbs b/sig/ancestor_builder.rbs index 5b1d78faa..2f960a015 100644 --- a/sig/ancestor_builder.rbs +++ b/sig/ancestor_builder.rbs @@ -138,6 +138,8 @@ module RBS def validate_super_class!: (TypeName, Environment::ClassEntry) -> void + # Inserts mixin ancestors from the entry + # def mixin_ancestors: (Environment::ClassEntry | Environment::ModuleEntry, TypeName, included_modules: Array[Definition::Ancestor::Instance]?, @@ -146,7 +148,7 @@ module RBS extended_modules: Array[Definition::Ancestor::Instance]?, extended_interfaces: Array[Definition::Ancestor::Instance]?) -> void - def mixin_ancestors0: (AST::Declarations::Class | AST::Declarations::Module | AST::Declarations::Interface, + def mixin_ancestors0: (declaration, TypeName, align_params: Substitution?, included_modules: Array[Definition::Ancestor::Instance]?, @@ -154,6 +156,14 @@ module RBS prepended_modules: Array[Definition::Ancestor::Instance]?, extended_modules: Array[Definition::Ancestor::Instance]?, extended_interfaces: Array[Definition::Ancestor::Instance]?) -> void + type declaration = AST::Ruby::Declarations::ClassDecl | AST::Ruby::Declarations::ModuleDecl + | AST::Declarations::Class | AST::Declarations::Module | AST::Declarations::Interface + + type mixin = AST::Ruby::Members::IncludeMember | AST::Ruby::Members::ExtendMember | AST::Ruby::Members::PrependMember + | AST::Members::Include | AST::Members::Extend | AST::Members::Prepend + + def each_mixin: (declaration) { (mixin) -> void } -> void + | (declaration) -> Enumerator[mixin] # Fill `#source` of instance ancestor if `ancestor.name == name` and its `source` is `nil` # diff --git a/sig/ast/ruby/annotation.rbs b/sig/ast/ruby/annotation.rbs new file mode 100644 index 000000000..d6c98fa94 --- /dev/null +++ b/sig/ast/ruby/annotation.rbs @@ -0,0 +1,497 @@ +module RBS + module AST + module Ruby + module Annotation + type leading_annotation = ColonMethodTypeAnnotation + | MethodTypesAnnotation + | ReturnTypeAnnotation + | ParamTypeAnnotation + | SplatParamTypeAnnotation + | DoubleSplatParamTypeAnnotation + | BlockParamTypeAnnotation + | OverrideAnnotation + | RBSAnnotationAnnotation + | GenericAnnotation + | SkipAnnotation + | ModuleSelfAnnotation + | InheritsAnnotation + | IvarTypeAnnotation + | ClassIvarTypeAnnotation + | ClassVarTypeAnnotation + + type trailing_annotation = NodeTypeAssertion | NodeApplication + + type t = leading_annotation | trailing_annotation + + class Base + attr_reader location: Location + + # Location of `@rbs`, `@rbs!`, or `:` prefix + # + attr_reader prefix_location: Location + + def initialize: (Location location, Location prefix_location) -> void + end + + # `: TYPE` annotation attached to nodes + # + class NodeTypeAssertion < Base + attr_reader type: Types::t + + def initialize: (location: Location, prefix_location: Location, type: Types::t) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `[TYPE, ...]` annotation attached to nodes + # + class NodeApplication < Base + attr_reader types: Array[Types::t] + + attr_reader suffix_location: Location + + def initialize: (location: Location, prefix_location: Location, types: Array[Types::t], suffix_location: Location) -> void + end + + # `@rbs skip` annotation + # + # ``` + # @rbs skip -- Optional comment + # ^^^^ -- prefix_location + # ^^^^ -- skip_location + # ^^^^^^^^^^^^^^^^^^^ -- comment + # ``` + class SkipAnnotation < Base + attr_reader skip_location: Location + + attr_reader comment: Location? + + def initialize: (location: Location, prefix_location: Location, skip_location: Location, comment: Location?) -> void + end + + # `: METHOD-TYPE` annotation in leading comments + class ColonMethodTypeAnnotation < Base + attr_reader annotations: Array[AST::Annotation] + + attr_reader method_type: MethodType + + def initialize: (location: Location, prefix_location: Location, annotations: Array[AST::Annotation], method_type: MethodType) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `@rbs METHOD-TYPEs` annotation in leading comments + # + # ``` + # @rbs () -> void | %a{foo} () -> String + # ^^^^ -- prefix_location + # ^ -- vertical_bar_locations[0] + # ``` + class MethodTypesAnnotation < Base + class Overload + attr_reader annotations: Array[AST::Annotation] + + attr_reader method_type: MethodType + + def initialize: (annotations: Array[AST::Annotation], method_type: MethodType) -> void + end + + attr_reader overloads: Array[Overload] + + attr_reader vertical_bar_locations: Array[Location] + + def initialize: (location: Location, prefix_location: Location, overloads: Array[Overload], vertical_bar_locations: Array[Location]) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `@rbs return: T -- COMMENT` annotation in leading comments + # + # ``` + # @rbs return: String -- Returns a string + # ^^^ -- prefix_location + # ^^^^^^ -- return_location + # ^ -- colon_location + # ^^^^^^^^^^^^^^^^^^^ -- comment + # ``` + class ReturnTypeAnnotation < Base + attr_reader return_location: Location + + attr_reader colon_location: Location + + attr_reader return_type: Types::t + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + return_location: Location, + colon_location: Location, + return_type: Types::t, + comment: Location?, + ) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `@rbs param: T -- COMMENT` annotation in leading comments + # + # ``` + # @rbs x: String -- A string + # ^^^^ -- prefix_location + # ^ -- param_name_location + # ^ -- colon_location + # ^^^^^^^^^^^ -- comment + # ``` + class ParamTypeAnnotation < Base + attr_reader param_name_location: Location + + attr_reader colon_location: Location + + attr_reader type: Types::t + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + param_name_location: Location, + colon_location: Location, + type: Types::t, + comment: Location? + ) -> void + + def param_name: () -> Symbol + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `@rbs *param: TYPE -- COMMENT` annotation in leading comments + # + # ``` + # @rbs *x: String -- A string + # ^^^^ -- prefix_location + # ^ -- operator_location + # ^ -- param_name_location (optional) + # ^ -- colon_location + # ^^^^^^^^^^^ -- comment + # ``` + + class SplatParamTypeAnnotation < Base + attr_reader operator_location: Location + + attr_reader param_name_location: Location? + + attr_reader colon_location: Location + + attr_reader type: Types::t + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + operator_location: Location, + param_name_location: Location?, + colon_location: Location, + type: Types::t, + comment: Location? + ) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `@rbs **param: TYPE -- COMMENT` annotation in leading comments + # + # ``` + # @rbs **x: String -- A string + # ^^^^ -- prefix_location + # ^^ -- operator_location + # ^ -- param_name_location? + # ^ -- colon_location + # ^^^^^^^^^^^ -- comment + # ``` + class DoubleSplatParamTypeAnnotation < Base + attr_reader operator_location: Location + + attr_reader param_name_location: Location? + + attr_reader colon_location: Location + + attr_reader type: Types::t + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + operator_location: Location, + param_name_location: Location?, + colon_location: Location, + type: Types::t, + comment: Location? + ) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `@rbs &block: ? method-type -- COMMENT` annotation in leading comments + # + # ``` + # @rbs &x: () -> void -- Yields something + # ^^^^ -- prefix_location + # ^ -- operator_location + # ^ -- param_name_location + # ^ -- colon_location + # ^^^^^^^^^^^^^^^^^^^ -- comment + # + # @rbs &: ? () -> void -- Yields something + # ^^^^ -- prefix_location + # ^ -- operator_location + # ^ -- colon_location + # ^ -- question_mark_location + # ^^^^^^^^^^^ -- comment + # ``` + # + class BlockParamTypeAnnotation < Base + attr_reader operator_location: Location + + attr_reader param_name_location: Location? + + attr_reader colon_location: Location + + attr_reader question_mark_location: Location? + + attr_reader block: Types::Block + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + operator_location: Location, + param_name_location: Location?, + colon_location: Location, + question_mark_location: Location?, + block: Types::Block, + comment: Location? + ) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `@rbs override` annotation in leading comments + # + # ``` + # @rbs override + # ^^^^ -- prefix_location + # ^^^^^^^^ -- override_location + # ``` + class OverrideAnnotation < Base + attr_reader override_location: Location + + def initialize: (location: Location, prefix_location: Location, override_location: Location) -> void + end + + # `@rbs generic unchecked out A < String = untyped` annotation in leading comments + # + # ``` + # @rbs generic unchecked out A < String = untyped + # ^^^^ -- prefix_location + # ^^^^^^^ -- generic_location + # ^^^^^^^^^ -- unchecked_location + # ^^^ -- variance_location + # ^ -- name_location + # ^ -- upper_bound_operator_location + # ^ -- default_type_operator_location + # ``` + class GenericAnnotation < Base + attr_reader generic_location: Location + + attr_reader unchecked_location: Location? + + attr_reader variance_location: Location? + + attr_reader name_location: Location + + attr_reader upper_bound_operator_location: Location? + + attr_reader upper_bound: Types::t? + + attr_reader default_type_operator_location: Location? + + attr_reader default_type: Types::t? + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + generic_location: Location, + unchecked_location: Location?, + variance_location: Location?, + name_location: Location, + upper_bound_operator_location: Location?, + upper_bound: Types::t?, + default_type_operator_location: Location?, + default_type: Types::t?, + comment: Location? + ) -> void + + def unchecked?: () -> bool + + def variance: () -> TypeParam::variance + + def upper_bound_location: () -> Location? + + def default_type_location: () -> Location? + + def name: () -> Symbol + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + # `@rbs %a{annotation} %a{annotation}` annotation in leading comments + # + class RBSAnnotationAnnotation < Base + attr_reader annotations: Array[AST::Annotation] + + def initialize: (location: Location, prefix_location: Location, annotations: Array[AST::Annotation]) -> void + end + + # `@rbs module-self Foo[Bar] -- comment` annotation in leading comments + # + class ModuleSelfAnnotation < Base + attr_reader module_self_location: Location + + attr_reader type_name: TypeName + + attr_reader type_name_location: Location + + attr_reader open_paren_location: Location? + + attr_reader type_args: Array[Types::t] + + attr_reader close_paren_location: Location? + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + module_self_location: Location, + type_name: TypeName, + type_name_location: Location, + open_paren_location: Location?, + type_args: Array[Types::t], + close_paren_location: Location?, + comment: Location? + ) -> void + end + + # `@rbs inherits Foo[Bar] -- comment` annotation in leading comments + # + class InheritsAnnotation < Base + attr_reader inherits_location: Location + + attr_reader type_name: TypeName + + attr_reader type_name_location: Location + + attr_reader open_paren_location: Location? + + attr_reader type_args: Array[Types::t] + + attr_reader close_paren_location: Location? + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + inherits_location: Location, + type_name: TypeName, + type_name_location: Location, + open_paren_location: Location?, + type_args: Array[Types::t], + close_paren_location: Location?, + comment: Location? + ) -> void + end + + # `@rbs @ivar: T -- comment` annotation in leading comments + class IvarTypeAnnotation < Base + attr_reader var_name_location: Location + + attr_reader colon_location: Location + + attr_reader type: Types::t + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + var_name_location: Location, + colon_location: Location, + type: Types::t, + comment: Location? + ) -> void + end + + class ClassVarTypeAnnotation < Base + attr_reader var_name_location: Location + + attr_reader colon_location: Location + + attr_reader type: Types::t + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + var_name_location: Location, + colon_location: Location, + type: Types::t, + comment: Location? + ) -> void + end + + class ClassIvarTypeAnnotation < Base + attr_reader self_location: Location + + attr_reader dot_location: Location + + attr_reader var_name_location: Location + + attr_reader colon_location: Location + + attr_reader type: Types::t + + attr_reader comment: Location? + + def initialize: ( + location: Location, + prefix_location: Location, + self_location: Location, + dot_location: Location, + var_name_location: Location, + colon_location: Location, + type: Types::t, + comment: Location? + ) -> void + end + + class EmbeddedRBSAnnotation < Base + type member = AST::Declarations::t | AST::Members::t + + attr_reader members: Array[member] + + def initialize: (location: Location, prefix_location: Location, members: Array[member]) -> void + end + end + end + end +end diff --git a/sig/ast/ruby/comment_block.rbs b/sig/ast/ruby/comment_block.rbs new file mode 100644 index 000000000..9f852ab9f --- /dev/null +++ b/sig/ast/ruby/comment_block.rbs @@ -0,0 +1,98 @@ +use Prism::Comment + +module RBS + module AST + module Ruby + class CommentBlock + attr_reader name: Pathname + + # Sub buffer of the contents of the comments + # + attr_reader comment_buffer: Buffer + + attr_reader offsets: Array[ + [ + Comment, + Integer, # -- prefix size + ] + ] + + def initialize: (Buffer source_buffer, Array[Comment]) -> void + + # Build comment block instances + def self.build: (Buffer, Array[Comment]) -> Array[instance] + + # Returns true if the comment block is a *leading* comment, which is attached to the successor node + def leading?: () -> bool + + # Returns true if the comment block is a *trailing* comment, which is attached to the predecessor node + def trailing?: () -> bool + + # The line number of the first comment in the block + def start_line: () -> Integer + + # The line number of the last comment in the block + def end_line: () -> Integer + + # The character index of `#comment_buffer` at the start of the lines + # + def line_starts: () -> Array[Integer] + + # Returns the text content of the comment + def text: (Integer index) -> String + + # Yields paragraph and annotation + # + # A paragraph is a sequence of lines that are separated by annotations. + # An annotation starts with a line starting with `@rbs` or `:`, and may follow lines that has more leading spaces. + # + # ``` + # # Line 1 ^ Paragraph 1 + # # Line 2 | + # # | + # # Line 3 v + # # @rbs ... < Annotation 1 + # # @rbs ... ^ Annotation 2 + # # ... | + # # | + # # ... v + # # ^ Paragraph 2 + # # Line 4 | + # # Line 5 v + # ``` + # + def each_paragraph: (Array[Symbol] variables) { (Location | AST::Ruby::Annotation::leading_annotation | AnnotationSyntaxError) -> void } -> void + | (Array[Symbol] variables) -> Enumerator[Location | AST::Ruby::Annotation::leading_annotation | AnnotationSyntaxError] + + # Returns a trailing annotation if it exists + # + # * Returns `nil` if the block is not a type annotation + # * Returns an annotation if the block has a type annotation + # * Returns AnnotationSyntaxError if the annotation has a syntax error + # + def trailing_annotation: (Array[Symbol] variables) -> (AST::Ruby::Annotation::trailing_annotation | AnnotationSyntaxError | nil) + + class AnnotationSyntaxError + attr_reader location: Location + + attr_reader error: ParsingError + + def initialize: (Location, ParsingError) -> void + end + + private def yield_paragraph: (Integer start_line, Integer current_line, Array[Symbol] variables) { (Location | AST::Ruby::Annotation::leading_annotation | AnnotationSyntaxError) -> void } -> void + + private def yield_annotation: (Integer start_line, Integer end_line, Integer current_line, Array[Symbol] variables) { (Location | AST::Ruby::Annotation::leading_annotation | AnnotationSyntaxError) -> void } -> void + + private def parse_annotation_lines: (Integer start_line, Integer end_line, Array[Symbol] variables) -> (AST::Ruby::Annotation::leading_annotation | AnnotationSyntaxError) + + def comments: () -> Array[Comment] + + def line_location: (Integer start_line, Integer end_line) -> Location + + private def leading_annotation?: (Integer index) -> bool + end + end + end +end + diff --git a/sig/ast/ruby/declarations.rbs b/sig/ast/ruby/declarations.rbs new file mode 100644 index 000000000..c71d2b920 --- /dev/null +++ b/sig/ast/ruby/declarations.rbs @@ -0,0 +1,266 @@ +module RBS + module AST + module Ruby + module Declarations + # Possible top level declarations + type t = ClassDecl | ModuleDecl | ConstantDecl | GlobalDecl | EmbeddedRBSDecl + + class Base + attr_reader buffer: Buffer + + def initialize: (Buffer) -> void + + def constant_as_type_name: (Prism::node?) -> TypeName? + + def rbs_location: (Prism::Location) -> Location + end + + # Returns `AST::TypeParam` objects from given `generic` annotations + # + # Note that `locations` in the returned objects may be `nil`. + # You can use the `type_param_annotations` directly for location aware operations. + # + class GenericsTypeParams + attr_reader annotations: Array[Annotation::GenericAnnotation] + + attr_reader type_params: Array[AST::TypeParam] + + def initialize: (Array[Annotation::GenericAnnotation]) -> void + + def self.build: (Array[Annotation::leading_annotation]) -> [instance, Array[Annotation::leading_annotation]] + + def param_names: () -> Array[Symbol] + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + class ClassDecl < Base + class SuperNode + attr_reader class_name: TypeName + + attr_reader class_name_location: Location + + attr_reader type_args: Array[Types::t] + + attr_reader open_paren_location: Location? + + attr_reader close_paren_location: Location? + + attr_reader location: Location + + def initialize: ( + class_name: TypeName, + type_args: Array[Types::t], + location: Location, + class_name_location: Location, + open_paren_location: Location?, + close_paren_location: Location? + ) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + + alias name class_name + + alias args type_args + end + + class SuperAnnotation + attr_reader class_name: TypeName + + attr_reader type_args: Array[Types::t] + + attr_reader annotation: Annotation::InheritsAnnotation + + def initialize: ( + TypeName, + Array[Types::t], + Annotation::InheritsAnnotation + ) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + + alias name class_name + + alias args type_args + + def self.build: (Array[Annotation::leading_annotation]) -> [instance?, Array[Annotation::leading_annotation]] + + # Returns the location in the source code + # + # Calculated based on `#annotation` and `#block`. + # + def location: () -> Location + end + + type member = Members::t | t | SingletonClassDecl + + attr_reader node: Prism::ClassNode + + attr_reader location: Location + + attr_reader class_name: TypeName + + alias name class_name + + attr_reader class_name_location: Location + + attr_reader generics: GenericsTypeParams + + attr_reader members: Array[member] + + attr_reader super_annotation: SuperAnnotation? + + attr_reader super_node: SuperNode? + + def initialize: ( + Buffer, + Prism::ClassNode, + location: Location, + class_name: TypeName, + class_name_location: Location, + generics: GenericsTypeParams, + super_node: SuperNode?, + super_annotation: SuperAnnotation? + ) -> void + + def type_params: () -> Array[AST::TypeParam] + + # Yields `AST::Ruby::Members` and `AST::Members` object + # + # * `AST::Ruby::Members` can be enumerated through the `#members` attribute directly + # * `AST::Members` can be enumerated through the `EmbeddedRBSDecl` objects + # + def each_member: () { (Members::t | AST::Members::t) -> void } -> void + | () -> Enumerator[Members::t | AST::Members::t] + + def each_decl: () { (t | SingletonClassDecl) -> void } -> void + | () -> Enumerator[t | SingletonClassDecl] + + def super_class: () -> (SuperNode | SuperAnnotation | nil) + end + + # `class < Location + + def initialize: (Buffer, Prism::SingletonClassNode) -> void + end + + class ModuleDecl < Base + class SelfConstraint + attr_reader name: TypeName + + attr_reader args: Array[Types::t] + + attr_reader annotation: Annotation::ModuleSelfAnnotation + + def location: () -> Location + + def initialize: (TypeName, Array[Types::t], Annotation::ModuleSelfAnnotation) -> void + + # Note that type names in `annotation` doesn't update + # + def map_type_name: () { (TypeName) -> TypeName } -> self + + def self.build: (Array[Annotation::leading_annotation]) -> [Array[instance], Array[Annotation::leading_annotation]] + end + + type member = Members::t | t | SingletonClassDecl + + attr_reader node: Prism::ModuleNode + + attr_reader location: Location + + attr_reader module_name: TypeName + + attr_reader module_name_location: Location + + attr_reader generics: GenericsTypeParams + + attr_reader self_constraints: Array[SelfConstraint] + + attr_reader members: Array[member] + + alias name module_name + + def initialize: ( + Buffer, + Prism::ModuleNode, + location: Location, + module_name: TypeName, + module_name_location: Location, + generics: GenericsTypeParams, + self_constraints: Array[SelfConstraint] + ) -> void + + def type_params: () -> Array[AST::TypeParam] + + def self_types: () -> Array[SelfConstraint] + + # Yields `AST::Ruby::Members` and `AST::Members` object + # + # * `AST::Ruby::Members` can be enumerated through the `#members` attribute directly + # * `AST::Members` can be enumerated through the `EmbeddedRBSDecl` objects + # + def each_member: () { (Members::t | AST::Members::t) -> void } -> void + | () -> Enumerator[Members::t | AST::Members::t] + + def each_decl: () { (t | SingletonClassDecl) -> void } -> void + | () -> Enumerator[t | SingletonClassDecl] + end + + class ConstantDecl < Base + attr_reader node: Prism::ConstantWriteNode + + def initialize: (Buffer, Prism::ConstantWriteNode) -> void + + # Returns the type of the constant + def type: () -> Types::t + + def location: () -> Location + end + + class GlobalDecl < Base + attr_reader node: Prism::GlobalVariableWriteNode + + attr_reader location: Location + end + + # `@rbs!` declaration + class EmbeddedRBSDecl < Base + type member = AST::Declarations::t | AST::Members::t + + attr_reader location: Location + + attr_reader members: Array[member] + + def initialize: (Buffer, Location, Array[member]) -> void + + # Enumerates `AST::Declarations` object + def each_decl: () { (AST::Declarations::t) -> void } -> void + | () -> Enumerator[AST::Declarations::t] + + # Enumerates `AST::Members` object + def each_member: () { (AST::Members::t) -> void } -> void + | () -> Enumerator[AST::Members::t] + end + end + end + end +end diff --git a/sig/ast/ruby/helpers.rbs b/sig/ast/ruby/helpers.rbs new file mode 100644 index 000000000..35575cb9b --- /dev/null +++ b/sig/ast/ruby/helpers.rbs @@ -0,0 +1,11 @@ +module RBS + module AST + module Ruby + module Helpers + module ConstantHelper + def self?.constant_as_type_name: (Prism::Node) -> TypeName? + end + end + end + end +end diff --git a/sig/ast/ruby/members.rbs b/sig/ast/ruby/members.rbs new file mode 100644 index 000000000..f3d34c511 --- /dev/null +++ b/sig/ast/ruby/members.rbs @@ -0,0 +1,306 @@ +module RBS + module AST + module Ruby + module Members + class Base + attr_reader buffer: Buffer + + def initialize: (Buffer) -> void + + include Helpers::ConstantHelper + end + + type t = DefMember | DefSingletonMember + | AliasMember + | IncludeMember | ExtendMember | PrependMember + | AttrReaderMember | AttrWriterMember | AttrAccessorMember + | PublicMember | PrivateMember + | InstanceVariableMember | ClassVariableMember | ClassInstanceVariableMember + + class Overload + attr_reader annotations: Array[AST::Annotation] + attr_reader method_type: MethodType + + def initialize: (MethodType, Array[AST::Annotation]) -> void + end + + class DefAnnotations + class DocStyleTypeAnnotations + type return_type_annotation = Annotation::NodeTypeAssertion | Annotation::ReturnTypeAnnotation + + attr_reader return_type_annotation: return_type_annotation? + + attr_reader param_type_annotations: Hash[Symbol, Annotation::ParamTypeAnnotation] + + attr_reader splat_type_annotation: Annotation::SplatParamTypeAnnotation? + + attr_reader kwsplat_type_annotation: Annotation::DoubleSplatParamTypeAnnotation? + + attr_reader block_type_annotation: Annotation::BlockParamTypeAnnotation? + + def initialize: ( + return_type_annotation: return_type_annotation?, + param_type_annotations: Hash[Symbol, Annotation::ParamTypeAnnotation], + splat_type_annotation: Annotation::SplatParamTypeAnnotation?, + kwsplat_type_annotation: Annotation::DoubleSplatParamTypeAnnotation?, + block_type_annotation: Annotation::BlockParamTypeAnnotation? + ) -> void + + def self.empty: () -> DocStyleTypeAnnotations + + def map_type_name: { (TypeName) -> TypeName } -> self + + # In-place update + def update!: ( + ?return_type_annotation: return_type_annotation?, + ?splat_type_annotation: Annotation::SplatParamTypeAnnotation?, + ?kwsplat_type_annotation: Annotation::DoubleSplatParamTypeAnnotation?, + ?block_type_annotation: Annotation::BlockParamTypeAnnotation? + ) -> void + + def construct_method_type: (Prism::ParametersNode?) -> MethodType + + def empty?: () -> bool + end + + type type_annotations = DocStyleTypeAnnotations | Array[Annotation::ColonMethodTypeAnnotation | Annotation::MethodTypesAnnotation] | nil + + attr_reader type_annotations: type_annotations + + attr_reader annotations: Array[Annotation::RBSAnnotationAnnotation] + + attr_reader override: Annotation::OverrideAnnotation? + + def initialize: ( + type_annotations: type_annotations, + annotations: Array[Annotation::RBSAnnotationAnnotation], + override: Annotation::OverrideAnnotation? + ) -> void + + def map_type_name: { (TypeName) -> TypeName } -> self + + # Returns the method type annotations from the comment block + # + # Returns a tuple of `DefAnnotations` object, array of unused leading annotations, and unused trailing annotation. + # + def self.build: (CommentBlock? leading_block, CommentBlock? trailing_block, Array[Symbol]) -> [ + DefAnnotations, + Array[Annotation::leading_annotation | CommentBlock::AnnotationSyntaxError], + Annotation::trailing_annotation | CommentBlock::AnnotationSyntaxError | nil + ] + + # Returns `true` if it doesn't have any annotation + def empty?: () -> bool + end + + type visibility = :public | :private | nil + + class DefMember < Base + attr_reader node: Prism::DefNode + + attr_reader name: Symbol + + attr_reader inline_annotations: DefAnnotations + + attr_reader visibility: visibility + + def initialize: (Buffer, Prism::DefNode, name: Symbol, inline_annotations: DefAnnotations, visibility: visibility) -> void + + # Returns the overload of the method + # + # Note that the `#location` of the objects obtained from this method may be `nil`. + # + # * For types that comes from the source code, the type has `location` + # * For types that are inferred, the type does not have `location` + # * Other intermediate components doesn't have `location` + # + def overloads: () -> Array[Overload] + + def override?: () -> bool + + def annotations: () -> Array[AST::Annotation] + + def map_type_name: () { (TypeName) -> TypeName } -> self + + def location: () -> Location + + def name_location: () -> Location + end + + class DefSingletonMember < Base + attr_reader buffer: Buffer + + attr_reader name: Symbol + + attr_reader node: Prism::DefNode + + attr_reader inline_annotations: DefAnnotations + + attr_reader visibility: visibility + + def initialize: (Buffer, Prism::DefNode, name: Symbol, inline_annotations: DefAnnotations, visibility: visibility) -> void + + def overloads: () -> Array[Overload] + + def override?: () -> bool + + # Returns true if the receiver is `self` + def self?: () -> bool + + def annotations: () -> Array[AST::Annotation] + + def location: () -> Location + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + class AliasMember < Base + attr_reader node: Prism::AliasMethodNode + + def initialize: (Buffer, Prism::AliasMethodNode) -> void + + def location: () -> Location + + def new_name: () -> Symbol + + def old_name: () -> Symbol + end + + # include, extend, and prepend + # + # ```rb + # include Foo #[Integer, String] + # # ^^^ module_name_location + # # ^ open_paran_location + # # ^^^^^^^ (type_args[0]) + # # ^ colon_locations[0] + # # ^^^^^^ (type_args[1]) + # # ^ close_paren_location + # ``` + # + class MixinMember < Base + attr_reader location: Location + + attr_reader module_name: TypeName + + attr_reader module_name_location: Location + + attr_reader open_paren_location: Location? + + attr_reader close_paren_location: Location? + + # Type arguments for mixin + attr_reader type_args: Array[Types::t] + + def initialize: ( + Buffer, + Prism::CallNode, + location: Location, + module_name: TypeName, + type_args: Array[Types::t], + module_name_location: Location, + open_paren_location: Location?, + close_paren_location: Location? + ) -> void + + attr_reader node: Prism::CallNode + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + class IncludeMember < MixinMember + end + + class ExtendMember < MixinMember + end + + class PrependMember < MixinMember + end + + class AttributeMember < Base + attr_reader node: Prism::CallNode + + attr_reader names: Array[Symbol] + + attr_reader annotation: Annotation::NodeTypeAssertion? + + attr_reader visibility: visibility + + def initialize: (Buffer, Prism::CallNode, Array[Symbol] names, Annotation::NodeTypeAssertion?, visibility) -> void + + def map_type_name: () { (TypeName) -> TypeName } -> self + + # Location that covers the attribute definition + def location: () -> Location + + def type: () -> Types::t? + end + + class AttrReaderMember < AttributeMember + end + + class AttrWriterMember < AttributeMember + end + + class AttrAccessorMember < AttributeMember + end + + class VisibilityMember < Base + attr_reader node: Prism::CallNode + + def initialize: (Buffer, Prism::CallNode) -> void + + def location: () -> Location + end + + class PublicMember < VisibilityMember + end + + class PrivateMember < VisibilityMember + end + + class InstanceVariableMember < Base + attr_reader name: Symbol + + attr_reader type: Types::t + + attr_reader annotation: Annotation::IvarTypeAnnotation + + def initialize: (Buffer, Symbol, Types::t, Annotation::IvarTypeAnnotation) -> void + + def location: () -> Location + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + class ClassVariableMember < Base + attr_reader name: Symbol + + attr_reader type: Types::t + + attr_reader annotation: Annotation::ClassVarTypeAnnotation + + def initialize: (Buffer, Symbol, Types::t, Annotation::ClassVarTypeAnnotation) -> void + + def location: () -> Location + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + + class ClassInstanceVariableMember < Base + attr_reader name: Symbol + + attr_reader type: Types::t + + attr_reader annotation: Annotation::ClassIvarTypeAnnotation + + def initialize: (Buffer, Symbol, Types::t, Annotation::ClassIvarTypeAnnotation) -> void + + def location: () -> Location + + def map_type_name: () { (TypeName) -> TypeName } -> self + end + end + end + end +end diff --git a/sig/buffer.rbs b/sig/buffer.rbs index 296ead2e0..8d0b181d9 100644 --- a/sig/buffer.rbs +++ b/sig/buffer.rbs @@ -6,21 +6,45 @@ module RBS type loc = [Integer, Integer] # Name to identify Buffer. - attr_reader name: untyped + attr_reader name: Pathname # The content of the buffer. attr_reader content: String - @lines: Array[String] + attr_reader parent: [Buffer, Array[Range[Integer]]]? - @ranges: Array[Range[Integer]] - - def initialize: (name: untyped name, content: String content) -> void + def initialize: (name: Pathname name, content: String content) -> void + | (content: String content, parent: [Buffer, Array[Range[Integer]]] parent) -> void + # Array of lines of the content, without the EOL + # + # ```rb + # buffer = Buffer.new(name: name, content: "123\nabc") + # buffer.lines # => ["123", "abc"] + # ``` + # + # If the input has EOL at the end of the file, the `lines` has an empty string at the end. + # + # ```rb + # buffer = Buffer.new(name: name, content: "123\nabc\n") + # buffer.lines # => ["123", "abc", ""] + # ``` + # def lines: () -> Array[String] + @ranges: Array[Range[Integer]] + # Array of ranges that stores the ranges of the each line, without the EOL + # + # ```rb + # buffer = Buffer.new(name: name, content: "123\nabc\n") + # buffer.ranges # => [0...3, 4...7, 8...8] + # ``` + # def ranges: () -> Array[Range[Integer]] + # Returns the number of the lines + def line_count: () -> Integer + # Translate position to location. def pos_to_loc: (Integer pos) -> loc @@ -28,5 +52,37 @@ module RBS def loc_to_pos: (loc loc) -> Integer def last_position: () -> Integer + + # Translate `Prism::Location` to `RBS::Location` attached to this buffer + # + # It assumes the `Prism::Location` has a source which is equivalent to `self`. + # + def rbs_location: (Prism::Location) -> Location + | (Prism::Location, Prism::Location) -> Location + + # Construct a buffer from substrings of this buffer. + # + # The returned buffer contains lines from given ranges. + # + # ```rb + # buffer = Buffer.new(name: name, content: < Buffer with content = 1\n34 + # buffer.sub_buffer(lines: [5..7]) # => Raises an error because the range contains newline + # ``` + # + %a{pure} def sub_buffer: (lines: Array[Range[Integer]]) -> Buffer + + %a{pure} def parent_buffer: () -> Buffer? + + %a{pure} def parent_position: (Integer) -> Integer? + + %a{pure} def absolute_position: (Integer) -> Integer? + + %a{pure} def top_buffer: () -> Buffer end end diff --git a/sig/definition.rbs b/sig/definition.rbs index 2e35b1a20..bfb4ac2fc 100644 --- a/sig/definition.rbs +++ b/sig/definition.rbs @@ -21,6 +21,7 @@ module RBS class Method type method_member = AST::Members::MethodDefinition | AST::Members::AttrReader | AST::Members::AttrAccessor | AST::Members::AttrWriter + | AST::Ruby::Members::DefMember | AST::Ruby::Members::DefSingletonMember class TypeDef attr_reader type: MethodType @@ -155,6 +156,8 @@ module RBS | nil # Itself | AST::Members::Include | AST::Members::Extend | AST::Members::Prepend # AST | AST::Declarations::Module::Self + | AST::Ruby::Declarations::ModuleDecl::SelfConstraint + | AST::Ruby::Members::IncludeMember | AST::Ruby::Members::PrependMember attr_reader name: TypeName attr_reader args: Array[Types::t] diff --git a/sig/definition_builder.rbs b/sig/definition_builder.rbs index 4b72fe6f9..32e901832 100644 --- a/sig/definition_builder.rbs +++ b/sig/definition_builder.rbs @@ -103,7 +103,11 @@ module RBS def validate_type_params: (Definition, ancestors: AncestorBuilder::OneAncestors, methods: MethodBuilder::Methods) -> void - def source_location: (Definition::Ancestor::Instance::source, AST::Declarations::t) -> Location[untyped, untyped]? + type declaration = Environment::ModuleEntry::declaration | Environment::ClassEntry::declaration | AST::Declarations::Interface + + # Returns a location to report an error on an ancestor + # + private def source_location: (Definition::Ancestor::Instance::source, declaration) -> Location[untyped, untyped]? def validate_variable: (Definition::Variable) -> void @@ -175,5 +179,40 @@ module RBS # ``` # def tapp_subst: (TypeName, Array[Types::t]) -> Substitution + + type module_decl = Environment::ModuleEntry::declaration | Environment::ClassEntry::declaration + + type instance_variable_member = AST::Members::AttrReader + | AST::Members::AttrWriter + | AST::Members::AttrAccessor + | AST::Members::InstanceVariable + | AST::Members::ClassVariable + | AST::Ruby::Members::AttrReaderMember + | AST::Ruby::Members::AttrWriterMember + | AST::Ruby::Members::AttrAccessorMember + | AST::Ruby::Members::InstanceVariableMember + | AST::Ruby::Members::ClassVariableMember + + type singleton_variable_member = AST::Members::AttrReader + | AST::Members::AttrWriter + | AST::Members::AttrAccessor + | AST::Members::ClassInstanceVariable + | AST::Members::ClassVariable + | AST::Ruby::Members::AttrReaderMember + | AST::Ruby::Members::AttrWriterMember + | AST::Ruby::Members::AttrAccessorMember + | AST::Ruby::Members::ClassInstanceVariableMember + | AST::Ruby::Members::ClassVariableMember + + # Enumerates variable members on instance + # + # * Yields attribute members with `instance` kind + # + def each_instance_variable_member: (module_decl) { (instance_variable_member) -> void } -> void + + # Enumerates variable members on singleton + # + # * Yields attribute members with `singleton` kind + def each_singleton_variable_member: (module_decl) { (singleton_variable_member) -> void } -> void end end diff --git a/sig/environment.rbs b/sig/environment.rbs index 95b2d1fd1..26d28948f 100644 --- a/sig/environment.rbs +++ b/sig/environment.rbs @@ -2,75 +2,14 @@ module RBS class Environment type module_decl = AST::Declarations::Class | AST::Declarations::Module - interface _ModuleOrClass - def name: () -> TypeName - - def type_params: () -> Array[AST::TypeParam] - end - - interface _NamedDecl - def name: () -> TypeName - end - - module ContextUtil - def calculate_context: (Array[_NamedDecl]) -> Resolver::context - end - - # Name of object, it's (multiple) declarations with the outer module declarations - # - class MultiEntry[M < _ModuleOrClass] - class D[M < _ModuleOrClass] - attr_reader decl: M - attr_reader outer: Array[module_decl] - - def initialize: (decl: M, outer: Array[module_decl]) -> void - - include ContextUtil - - @context: Resolver::context - - def context: () -> Resolver::context - end - - attr_reader name: TypeName - attr_reader decls: Array[D[M]] - - @primary: D[M]? - - def initialize: (name: TypeName) -> void - - def insert: (decl: M, outer: Array[module_decl]) -> void - - def validate_type_params: () -> void - - def compatible_params?: (Array[AST::TypeParam], Array[AST::TypeParam]) -> boolish - - def type_params: () -> Array[AST::TypeParam] - - def primary: () -> D[M] - end - - class ModuleEntry < MultiEntry[AST::Declarations::Module] - def self_types: () -> Array[AST::Declarations::Module::Self] - end - - class ClassEntry < MultiEntry[AST::Declarations::Class] - end - # Name of object, it's (single) declaration, and the outer module declarations # class SingleEntry[N, D] attr_reader name: N attr_reader decl: D - attr_reader outer: Array[module_decl] - - def initialize: (name: N, decl: D, outer: Array[module_decl]) -> void - - include ContextUtil + attr_reader context: Resolver::context - @context: Resolver::context - - def context: () -> Resolver::context + def initialize: (name: N, decl: D, context: Resolver::context) -> void end class ModuleAliasEntry < SingleEntry[TypeName, AST::Declarations::ModuleAlias] @@ -91,8 +30,9 @@ module RBS class GlobalEntry < SingleEntry[Symbol, AST::Declarations::Global] end - # Top level declarations - attr_reader declarations: Array[AST::Declarations::t] + # Array of source objects loaded in the environment + # + attr_reader sources: Array[Source::RBS | Source::Ruby] # Class declarations attr_reader class_decls: Hash[TypeName, ModuleEntry | ClassEntry] @@ -111,27 +51,27 @@ module RBS # Global declarations attr_reader global_decls: Hash[Symbol, GlobalEntry] - # A hash from Buffer to it's contents - # - attr_reader signatures: Hash[Buffer, [Array[AST::Directives::t], Array[AST::Declarations::t]]] - def initialize: () -> void def initialize_copy: (Environment) -> void + def each_rbs_source: () { (Source::RBS) -> void } -> void + | () -> Enumerator[Source::RBS] + + def each_ruby_source: () { (Source::Ruby) -> void } -> void + | () -> Enumerator[Source::Ruby] + # Construct an environment from EnvironmentLoader # def self.from_loader: (EnvironmentLoader) -> Environment - def add_signature: (buffer: Buffer, directives: Array[AST::Directives::t], decls: Array[AST::Declarations::t]) -> void - - # Insert a toplevel declaration into the environment - # - def <<: (AST::Declarations::t decl) -> self + def add_signature: (Source::RBS | Source::Ruby) -> void # Insert a declaration into the environment # - private def insert_decl: (AST::Declarations::t, outer: Array[module_decl], namespace: Namespace) -> void + private def insert_decl: (AST::Declarations::t, ?context: Resolver::context, ?namespace: Namespace) -> void + + private def insert_ruby_decl: (AST::Ruby::Declarations::t, ?context: Resolver::context, ?namespace: Namespace) -> void # Resolve all type names in the environment to absolute type names. # Relative type name will be left if absolute type name cannot be found. @@ -139,7 +79,7 @@ module RBS # When `only` is given, it skips other _top-level_ declarations not included in the collection. # This helps running resolution faster in the case of _partial updates_. # - def resolve_type_names: (?only: Set[AST::Declarations::t]?) -> Environment + def resolve_type_names: (?only: Set[AST::Declarations::t | AST::Ruby::Declarations::t]?) -> Environment def resolve_signature: (Resolver::TypeNameResolver, UseMap::Table, Array[AST::Directives::t], Array[AST::Declarations::t], ?only: Set[AST::Declarations::t]?) -> [Array[AST::Directives::t], Array[AST::Declarations::t]] @@ -264,7 +204,7 @@ module RBS def append_context: (Resolver::context, module_decl) -> Resolver::context - def resolve_declaration: (Resolver::TypeNameResolver resolver, UseMap map, AST::Declarations::t decl, outer: Array[module_decl], prefix: Namespace) -> AST::Declarations::t + def resolve_declaration: (Resolver::TypeNameResolver resolver, UseMap map, AST::Declarations::t decl, context: Resolver::context, prefix: Namespace) -> AST::Declarations::t def resolve_member: (Resolver::TypeNameResolver, UseMap map, AST::Members::t, context: Resolver::context) -> AST::Members::t @@ -272,6 +212,10 @@ module RBS def resolve_type_params: (Resolver::TypeNameResolver resolver, UseMap map, Array[AST::TypeParam], context: Resolver::context) -> Array[AST::TypeParam] + def resolve_ruby_declaration: (Resolver::TypeNameResolver resolver, UseMap map, AST::Ruby::Declarations::t decl, context: Resolver::context, prefix: Namespace) -> AST::Ruby::Declarations::t + + def resolve_ruby_member: (Resolver::TypeNameResolver, UseMap map, AST::Ruby::Members::t | AST::Ruby::Declarations::SingletonClassDecl, context: Resolver::context) -> (AST::Ruby::Members::t | AST::Ruby::Declarations::SingletonClassDecl) + def absolute_type: (Resolver::TypeNameResolver, UseMap map, Types::t, context: Resolver::context) -> Types::t def absolute_type_name: (Resolver::TypeNameResolver, UseMap map, TypeName, context: Resolver::context) -> TypeName diff --git a/sig/environment/class_entry.rbs b/sig/environment/class_entry.rbs new file mode 100644 index 000000000..854e191eb --- /dev/null +++ b/sig/environment/class_entry.rbs @@ -0,0 +1,50 @@ +module RBS + class Environment + # Represents a class entry in the environment + # + # ```rb + # entry = ClassEntry.new(TypeName.parse("::String")) + # entry << [nil, declaration] + # entry << [[nil, TypeName.parse("::Kernel")], declaration] + # ``` + class ClassEntry + attr_reader name: TypeName + + type declaration = AST::Declarations::Class | AST::Ruby::Declarations::ClassDecl + + type context_decl = [Resolver::context, declaration] + + attr_reader context_decls: Array[context_decl] + + @primary_decl: declaration? + + def initialize: (TypeName) -> void + + def <<: (context_decl) -> self + + def each_decl: { (declaration) -> void } -> void + | () -> Enumerator[declaration] + + # Returns true if the entry doesn't have any declaration + # + def empty?: () -> bool + + # Find the *primary* declaration of the class + # + # * Returns the first declaration with super class + # * Returns the first declaration if the declaration doesn't have super class + # + %a{pure} def primary_decl: () -> declaration + + # Returns the generics declaration + # + def type_params: () -> Array[AST::TypeParam] + + # Confirms if the type parameters in the declaration are compatible + # + # * Raises `GenericParameterMismatchError` if incompatible declaration is detected. + # + def validate_type_params: () -> void + end + end +end diff --git a/sig/environment/module_entry.rbs b/sig/environment/module_entry.rbs new file mode 100644 index 000000000..b2e6b2c7c --- /dev/null +++ b/sig/environment/module_entry.rbs @@ -0,0 +1,49 @@ +module RBS + class Environment + # Represents a class entry in the environment + # + # ```rb + # entry = ModuleEntry.new(TypeName.parse("::Kernel")) + # entry << [nil, declaration] + # entry << [[nil, TypeName.parse("::Object")], declaration] + # ``` + class ModuleEntry + attr_reader name: TypeName + + type declaration = AST::Declarations::Module | AST::Ruby::Declarations::ModuleDecl + + type context_decl = [Resolver::context, declaration] + + attr_reader context_decls: Array[context_decl] + + def initialize: (TypeName) -> void + + def <<: (context_decl) -> self + + def each_decl: { (declaration) -> void } -> void + | () -> Enumerator[declaration] + + # Returns true if the entry doesn't have any declaration + # + def empty?: () -> bool + + # Returns the first declaration + # + # This method helps using the class with `ClassEntry` objects. + # + def primary_decl: () -> declaration + + # Returns the generics declaration + # + def type_params: () -> Array[AST::TypeParam] + + # Confirms if the type parameters in the declaration are compatible + # + # * Raises `GenericParameterMismatchError` if incompatible declaration is detected. + # + def validate_type_params: () -> void + + def self_types: () -> Array[AST::Declarations::Module::Self | AST::Ruby::Declarations::ModuleDecl::SelfConstraint] + end + end +end diff --git a/sig/errors.rbs b/sig/errors.rbs index 2566567f1..98cd94ecf 100644 --- a/sig/errors.rbs +++ b/sig/errors.rbs @@ -102,6 +102,9 @@ module RBS class NoSelfTypeFoundError < DefinitionError include DetailedMessageable + type self_constraint = AST::Declarations::Module::Self + | AST::Ruby::Declarations::ModuleDecl::SelfConstraint + attr_reader type_name: TypeName attr_reader location: Location[untyped, untyped]? @@ -109,20 +112,23 @@ module RBS # The type name in `self` is automatically normalized # - def self.check!: (AST::Declarations::Module::Self, env: Environment) -> void + def self.check!: (self_constraint, env: Environment) -> void end class NoMixinFoundError < DefinitionError include DetailedMessageable + type member = AST::Members::t + | AST::Ruby::Members::IncludeMember | AST::Ruby::Members::PrependMember + attr_reader type_name: TypeName - attr_reader member: AST::Members::t + attr_reader member: member - def initialize: (type_name: TypeName, member: AST::Members::t) -> void + def initialize: (type_name: TypeName, member: member) -> void def location: () -> Location[untyped, untyped]? - def self.check!: (TypeName, env: Environment, member: AST::Members::t) -> void + def self.check!: (TypeName, env: Environment, member: member) -> void end class DuplicatedMethodDefinitionError < DefinitionError @@ -185,20 +191,34 @@ module RBS class VariableDuplicationError < DefinitionError include DetailedMessageable + type member = AST::Members::InstanceVariable | AST::Members::ClassVariable | AST::Members::ClassInstanceVariable + | AST::Ruby::Members::InstanceVariableMember | AST::Ruby::Members::ClassInstanceVariableMember | AST::Ruby::Members::ClassVariableMember + + attr_reader member: member + attr_reader type_name: TypeName attr_reader variable_name: Symbol attr_reader location: Location[untyped, untyped]? - def initialize: (type_name: TypeName, variable_name: Symbol, location: Location[untyped, untyped]?) -> void + def initialize: (member: member) -> void + def kind: () -> String end class InstanceVariableDuplicationError < VariableDuplicationError + type member = AST::Members::InstanceVariable | AST::Ruby::Members::InstanceVariableMember + def kind: () -> String + + def self.check!: (variables: Hash[Symbol, Definition::Variable], member: member, type_name: TypeName) -> void end class ClassInstanceVariableDuplicationError < VariableDuplicationError + type member = AST::Members::ClassInstanceVariable | AST::Ruby::Members::ClassInstanceVariableMember + def kind: () -> String + + def self.check!: (variables: Hash[Symbol, Definition::Variable], member: member, type_name: TypeName) -> void end # The `alias` member declares an alias from unknown method @@ -224,30 +244,38 @@ module RBS # The *overloading* method definition cannot find *non-overloading* method definition # class InvalidOverloadMethodError < DefinitionError + type overloading = DefinitionBuilder::MethodBuilder::Methods::Definition::overloading + include DetailedMessageable attr_reader type_name: TypeName attr_reader method_name: Symbol attr_reader kind: :instance | :singleton - attr_reader members: Array[AST::Members::MethodDefinition] + attr_reader members: Array[overloading] - def initialize: (type_name: TypeName, method_name: Symbol, kind: :instance | :singleton, members: Array[AST::Members::MethodDefinition]) -> void + def initialize: (type_name: TypeName, method_name: Symbol, kind: :instance | :singleton, members: Array[overloading]) -> void - def location: () -> AST::Members::MethodDefinition::loc? + def location: () -> Location[bot, bot]? end class GenericParameterMismatchError < LoadingError + type decl = AST::Declarations::Class | AST::Declarations::Module + | AST::Ruby::Declarations::ClassDecl | AST::Ruby::Declarations::ModuleDecl + attr_reader name: TypeName - attr_reader decl: AST::Declarations::Class | AST::Declarations::Module + attr_reader decl: decl - def initialize: (name: TypeName, decl: AST::Declarations::Class | AST::Declarations::Module) -> void + def initialize: (name: TypeName, decl: decl) -> void end class DuplicatedDeclarationError < LoadingError + type decl = AST::Declarations::t + | AST::Ruby::Declarations::t + attr_reader name: TypeName | Symbol - attr_reader decls: Array[AST::Declarations::t] + attr_reader decls: Array[decl] - def initialize: (TypeName | Symbol, *AST::Declarations::t) -> void + def initialize: (TypeName | Symbol, *decl) -> void end class InvalidVarianceAnnotationError < DefinitionError @@ -280,6 +308,7 @@ module RBS include DetailedMessageable type member = AST::Members::Include | AST::Members::Prepend | AST::Members::Extend + | AST::Ruby::Members::IncludeMember | AST::Ruby::Members::PrependMember attr_reader type_name: TypeName attr_reader member: member @@ -296,11 +325,15 @@ module RBS # InheritModuleError is raised if a class definition inherits a module (not a class) # class InheritModuleError < DefinitionError + type super_class = AST::Declarations::Class::Super + | AST::Ruby::Declarations::ClassDecl::SuperNode + | AST::Ruby::Declarations::ClassDecl::SuperAnnotation + include DetailedMessageable - attr_reader super_decl: AST::Declarations::Class::Super + attr_reader super_decl: super_class - def initialize: (AST::Declarations::Class::Super) -> void + def initialize: (super_class) -> void def location: () -> Location[untyped, untyped]? @@ -308,7 +341,7 @@ module RBS # # Automatically normalize the name of super. # - def self.check!: (AST::Declarations::Class::Super, env: Environment) -> void + def self.check!: (super_class, env: Environment) -> void end class RecursiveTypeAliasError < BaseError diff --git a/sig/inline/comment_association.rbs b/sig/inline/comment_association.rbs new file mode 100644 index 000000000..c0c87e27e --- /dev/null +++ b/sig/inline/comment_association.rbs @@ -0,0 +1,48 @@ +use RBS::AST::Ruby::CommentBlock + +module RBS + module Inline + # CommentAssociation manages the association between `Prism::Node` and `CommentBlock` + # + class CommentAssociation + attr_reader blocks: Array[CommentBlock] + + attr_reader start_line_map: Hash[Integer, CommentBlock] + + attr_reader end_line_map: Hash[Integer, CommentBlock] + + # CommentBlocks that are already associated to a node, which cannot be associated to another node again + # + attr_reader associated_blocks: Set[CommentBlock] + + def self.build: (Buffer, Prism::Result) -> instance + + def initialize: (Array[CommentBlock]) -> void + + # Returns a CommentBlock that is associated to given node + # + # Update association status. + # + def leading_block: (Prism::Node) -> CommentBlock? + + # Returns a CommentBlock that is associated to given node, or by its location + # + # Update association status. + # + def trailing_block: (Prism::Node) -> CommentBlock? + | (Prism::Location) -> CommentBlock? + + # Yields leading CommentBlocks that is enclosed in the given node + # + # Note that `enclosed_blocks` works only after all of the *leading* blocks inside the node is associated. + # + # Update association status. + # + def each_enclosed_block: (Prism::Node) { (CommentBlock) -> void } -> void + | (Prism::Node) -> Enumerator[CommentBlock] + + def each_unassociated_block: () { (CommentBlock) -> void } -> void + | () -> Enumerator[CommentBlock] + end + end +end diff --git a/sig/inline_parser.rbs b/sig/inline_parser.rbs new file mode 100644 index 000000000..0acfa64ed --- /dev/null +++ b/sig/inline_parser.rbs @@ -0,0 +1,195 @@ +use RBS::AST::Ruby::Members + +module RBS + class InlineParser + class Result + attr_reader buffer: Buffer + attr_reader prism_result: Prism::ParseResult + attr_reader declarations: Array[AST::Ruby::Declarations::t] + attr_reader diagnostics: Array[Diagnostics::t] + + def initialize: (Buffer, Prism::ParseResult) -> void + end + + module Diagnostics + type t = NonConstantName + | ToplevelSingletonClass + | DeclarationInsideSingletonClass + | NonConstantSuperClass + | NonSelfSingletonClass + | InvalidVisibilityCall + | InvalidMixinCall + | UnusedAnnotation + | AnnotationSyntaxError + | VariableAnnotationInSingletonClassError + | TopLevelAliasError + | MethodNameAliasNonLiteralError + | UnexpectedSingletonClassMemberError + | TopLevelMethodDefinition + | TopLevelMixinError + | AttributeDefinitionError + + class Base + attr_reader message: String + + attr_reader location: Location + + def initialize: (Location, String) -> void + end + + # Module/class definition has non-constant name + class NonConstantName < Base + end + + # Singleton class definition at the top level + class ToplevelSingletonClass < Base + end + + # < bool + + def self.parse: (Buffer, Prism::ParseResult) -> Result + + class Parser < Prism::Visitor + type decl_context = AST::Ruby::Declarations::ClassDecl + | AST::Ruby::Declarations::SingletonClassDecl + | AST::Ruby::Declarations::ModuleDecl + + attr_reader result: Result + + attr_reader comments: Inline::CommentAssociation + + def buffer: () -> Buffer + + def diagnostics: () -> Array[Diagnostics::t] + + %a{pure} def current_context: () -> decl_context? + + %a{pure} def current_context!: () -> decl_context + + attr_reader decl_contexts: Array[decl_context] + + def initialize: (Result, Inline::CommentAssociation) -> void + + def parse: () -> void + + def push_decl_context: (decl_context) { () -> void } -> void + + def insert_class_module_decl: (AST::Ruby::Declarations::ClassDecl | AST::Ruby::Declarations::ModuleDecl) -> void + + def visit_class_node: ... + + def visit_singleton_class_node: ... + + def visit_def_node: ... + + def visit_constant_write_node: ... + + def visibility_member?: (Prism::CallNode) -> (Members::PrivateMember | Members::PublicMember | nil) + + def mixin_member?: (Prism::CallNode) -> (Members::IncludeMember | Members::ExtendMember | Members::PrependMember | nil) + + def attribute_member?: (Prism::CallNode) -> (Members::AttrReaderMember | Members::AttrWriterMember | Members::AttrAccessorMember | nil) + + def visibility_modifier?: (Prism::CallNode) -> Members::visibility + + def push_type_params: [T] (Array[AST::TypeParam]) { () -> T } -> T + | (Array[AST::TypeParam]) -> void + + def pop_type_params: () -> void + + @type_params_stack: Array[Array[AST::TypeParam]] + def current_type_params: () -> Array[AST::TypeParam] + + def current_type_param_names: () -> Array[Symbol] + + def push_method_visibility: (AST::Ruby::Members::visibility) { () -> void } -> void + + @method_visibility_stack: Array[AST::Ruby::Members::visibility] + def current_method_visibility: () -> AST::Ruby::Members::visibility + + # Returns true if the receiver is (implicitly/explicitly) a `self` node + def self_call?: (Prism::CallNode) -> bool + + # Returns true if the node doesn't have argument + # + def no_argument?: (Prism::CallNode) -> bool + + # Returns the argument if the call has exactly one argument + # + def one_argument?: (Prism::CallNode) -> Prism::Node? + + # Returns `true` if the node is a constant node + # + # * If the node is a simple constant read node `Foo`, or + # * If the node is a constant path node that parent is a constant node + # + def constant_node?: (Prism::Node) -> (Prism::ConstantReadNode | Prism::ConstantPathNode | nil) + + def report_annotation_syntax_error: (AST::Ruby::CommentBlock::AnnotationSyntaxError) -> void + + def report_unused_annotation: (AST::Ruby::Annotation::t) -> void + end + end +end diff --git a/sig/location.rbs b/sig/location.rbs index f9588ddc7..c23b5d796 100644 --- a/sig/location.rbs +++ b/sig/location.rbs @@ -4,7 +4,7 @@ module RBS # # A location can have _child_ locations. # - class Location[in RequiredChildKeys, in OptionalChildKeys] + class Location[in RequiredChildKeys = bot, in OptionalChildKeys = bot] # The buffer this location points on. attr_reader buffer (): Buffer @@ -97,6 +97,12 @@ module RBS def key?: (Symbol) -> bool + def +: (Location?) -> Location + + # Returns a location in the top buffer + # + def absolute_location: () -> self + private def _add_required_child: (RequiredChildKeys name, Integer start_pos, Integer end_pos) -> void diff --git a/sig/members.rbs b/sig/members.rbs index 2951d3ba2..aba73ed01 100644 --- a/sig/members.rbs +++ b/sig/members.rbs @@ -135,6 +135,8 @@ module RBS def initialize: (name: TypeName, args: Array[Types::t], annotations: Array[Annotation], location: loc?, comment: Comment?) -> void include _HashEqual + + alias module_name name end class Include < Base diff --git a/sig/method_builder.rbs b/sig/method_builder.rbs index e47b625c3..dcd03d6f8 100644 --- a/sig/method_builder.rbs +++ b/sig/method_builder.rbs @@ -8,6 +8,8 @@ module RBS # Set of methods defined in a type. # class Methods + # The type of the receiver of the method + # type instance_type = Types::ClassInstance | Types::ClassSingleton | Types::Interface # Definition of a method in the type @@ -15,14 +17,19 @@ module RBS # The Definition object allows invalid method definition so that more than one _original_ definitions may exist. # class Definition + # AST members that can be *original* method definition type original = AST::Members::MethodDefinition | AST::Members::Alias | AST::Members::AttrAccessor | AST::Members::AttrWriter | AST::Members::AttrReader + | AST::Ruby::Members::DefMember | AST::Ruby::Members::DefSingletonMember + + # AST members that can define overloading methods + type overloading = AST::Members::MethodDefinition | AST::Ruby::Members::DefMember type accessibility = RBS::Definition::accessibility attr_reader name: Symbol attr_reader type: instance_type attr_reader originals: Array[original] - attr_reader overloads: Array[AST::Members::MethodDefinition] + attr_reader overloads: Array[overloading] attr_reader accessibilities: Array[accessibility] def initialize: (name: Symbol, type: instance_type, originals: Array[original], overloads: Array[AST::Members::MethodDefinition], accessibilities: Array[accessibility]) -> void @@ -57,6 +64,10 @@ module RBS end end + type member = AST::Members::t | AST::Ruby::Members::t + + type declaration = Environment::ModuleEntry::declaration | Environment::ClassEntry::declaration + attr_reader env: Environment attr_reader instance_methods: Hash[TypeName, Methods] attr_reader singleton_methods: Hash[TypeName, Methods] @@ -70,14 +81,27 @@ module RBS def build_interface: (TypeName) -> Methods - def build_alias: (Methods, Methods::instance_type, member: AST::Members::Alias) -> void + private def build_alias: (Methods, Methods::instance_type, member: AST::Members::Alias) -> void - def build_attribute: (Methods, Methods::instance_type, member: AST::Members::AttrAccessor | AST::Members::AttrReader | AST::Members::AttrWriter, accessibility: Definition::accessibility) -> void + private def build_attribute: (Methods, Methods::instance_type, member: AST::Members::AttrAccessor | AST::Members::AttrReader | AST::Members::AttrWriter, accessibility: Definition::accessibility) -> void - def build_method: (Methods, Methods::instance_type, member: AST::Members::MethodDefinition, accessibility: Definition::accessibility) -> void + private def build_method: (Methods, Methods::instance_type, member: AST::Members::MethodDefinition, accessibility: Definition::accessibility) -> void - def each_member_with_accessibility: (Array[AST::Members::t | AST::Declarations::t], ?accessibility: Definition::accessibility) { (AST::Members::t | AST::Declarations::t, Definition::accessibility) -> void } -> void + private def build_ruby_method: (Methods, Methods::instance_type, member: AST::Ruby::Members::DefMember | AST::Ruby::Members::DefSingletonMember, accessibility: Definition::accessibility) -> void + # Enumerates members in declaration with current accessibility + # + # * Note that this method doesn't yield accessibility specifier members. + # + private def each_member_with_accessibility: ( + declaration, + ?accessibility: Definition::accessibility + ) { (member, Definition::accessibility, AST::Ruby::Declarations::SingletonClassDecl?) -> void } -> void + + # Returns another MethodBuilder instance with given `env` + # + # Method definitions already computed are kept, but ones associated with type names which is included in `except` set are deleted. + # def update: (env: Environment, except: _Each[TypeName]) -> MethodBuilder end end diff --git a/sig/parser.rbs b/sig/parser.rbs index da9e94692..5cd4efe46 100644 --- a/sig/parser.rbs +++ b/sig/parser.rbs @@ -82,6 +82,10 @@ module RBS KEYWORDS: Hash[String, bot] + def self.parse_inline: (Buffer | String, Range[Integer?], ?variables: Array[Symbol]) -> AST::Ruby::Annotation::leading_annotation + + def self.parse_inline_assertion: (Buffer | String, Range[Integer?], ?variables: Array[Symbol]) -> AST::Ruby::Annotation::trailing_annotation + private def self.buffer: (String | Buffer source) -> Buffer @@ -92,6 +96,10 @@ module RBS def self._parse_signature: (Buffer, Integer start_pos, Integer end_pos) -> [Array[AST::Directives::t], Array[AST::Declarations::t]] + def self._parse_inline: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables) -> AST::Ruby::Annotation::leading_annotation + + def self._parse_inline_assertion: (Buffer, Integer start_pos, Integer end_pos, Array[Symbol] variables) -> AST::Ruby::Annotation::NodeTypeAssertion + def self._lex: (Buffer, Integer end_pos) -> Array[[Symbol, Location[untyped, untyped]]] class LocatedValue diff --git a/sig/source.rbs b/sig/source.rbs new file mode 100644 index 000000000..444fa4863 --- /dev/null +++ b/sig/source.rbs @@ -0,0 +1,46 @@ +module RBS + module Source + class RBS + attr_reader buffer: Buffer + + attr_reader directives: Array[AST::Directives::t] + + attr_reader declarations: Array[AST::Declarations::t] + + def initialize: (Buffer, Array[AST::Directives::t], Array[AST::Declarations::t]) -> void + + # Enumerates defined type names in the source + # + # The order is undefined. Deduplicated per source object. + # + def each_type_name: () { (TypeName) -> void } -> void + | () -> Enumerator[TypeName] + + private def each_declaration_type_name: (Set[TypeName], AST::Declarations::t) { (TypeName) -> void } -> void + end + + class Ruby + attr_reader buffer: Buffer + + attr_reader prism_result: Prism::ParseResult + + attr_reader declarations: Array[AST::Ruby::Declarations::t] + + attr_reader diagnostics: Array[InlineParser::Diagnostics::t] + + def initialize: (Buffer, Prism::ParseResult, Array[AST::Ruby::Declarations::t], Array[InlineParser::Diagnostics::t]) -> void + + def each_type_name: () { (TypeName) -> void } -> void + | () -> Enumerator[TypeName] + + private def each_declaration_type_name: (Set[TypeName], AST::Ruby::Declarations::t) { (TypeName) -> void } -> void + + # Compares the type declaration between `self` and `other` + # + # The comparison is based on the AST structure. + # Differences on Ruby code implementation may be ignored. + # + def ==: (other: Ruby) -> bool + end + end +end diff --git a/src/constants.c b/src/constants.c index dd1e2ee5c..1f93e61b6 100644 --- a/src/constants.c +++ b/src/constants.c @@ -14,6 +14,9 @@ VALUE RBS_AST; VALUE RBS_AST_Declarations; VALUE RBS_AST_Directives; VALUE RBS_AST_Members; +VALUE RBS_AST_Ruby; +VALUE RBS_AST_Ruby_Annotation; +VALUE RBS_AST_Ruby_Annotation_MethodTypesAnnotation; VALUE RBS_Parser; VALUE RBS_Types; VALUE RBS_Types_Bases; @@ -47,6 +50,26 @@ VALUE RBS_AST_Members_MethodDefinition_Overload; VALUE RBS_AST_Members_Prepend; VALUE RBS_AST_Members_Private; VALUE RBS_AST_Members_Public; +VALUE RBS_AST_Ruby_Annotation_BlockParamTypeAnnotation; +VALUE RBS_AST_Ruby_Annotation_ClassIvarTypeAnnotation; +VALUE RBS_AST_Ruby_Annotation_ClassVarTypeAnnotation; +VALUE RBS_AST_Ruby_Annotation_ColonMethodTypeAnnotation; +VALUE RBS_AST_Ruby_Annotation_DoubleSplatParamTypeAnnotation; +VALUE RBS_AST_Ruby_Annotation_EmbeddedRBSAnnotation; +VALUE RBS_AST_Ruby_Annotation_GenericAnnotation; +VALUE RBS_AST_Ruby_Annotation_InheritsAnnotation; +VALUE RBS_AST_Ruby_Annotation_IvarTypeAnnotation; +VALUE RBS_AST_Ruby_Annotation_MethodTypesAnnotation; +VALUE RBS_AST_Ruby_Annotation_MethodTypesAnnotation_Overload; +VALUE RBS_AST_Ruby_Annotation_ModuleSelfAnnotation; +VALUE RBS_AST_Ruby_Annotation_NodeApplication; +VALUE RBS_AST_Ruby_Annotation_NodeTypeAssertion; +VALUE RBS_AST_Ruby_Annotation_OverrideAnnotation; +VALUE RBS_AST_Ruby_Annotation_ParamTypeAnnotation; +VALUE RBS_AST_Ruby_Annotation_RBSAnnotationAnnotation; +VALUE RBS_AST_Ruby_Annotation_ReturnTypeAnnotation; +VALUE RBS_AST_Ruby_Annotation_SkipAnnotation; +VALUE RBS_AST_Ruby_Annotation_SplatParamTypeAnnotation; VALUE RBS_AST_TypeParam; VALUE RBS_MethodType; VALUE RBS_Namespace; @@ -89,6 +112,9 @@ void rbs__init_constants(void) { IMPORT_CONSTANT(RBS_AST_Declarations, RBS_AST, "Declarations"); IMPORT_CONSTANT(RBS_AST_Directives, RBS_AST, "Directives"); IMPORT_CONSTANT(RBS_AST_Members, RBS_AST, "Members"); + IMPORT_CONSTANT(RBS_AST_Ruby, RBS_AST, "Ruby"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation, RBS_AST_Ruby, "Annotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_MethodTypesAnnotation, RBS_AST_Ruby_Annotation, "MethodTypesAnnotation"); IMPORT_CONSTANT(RBS_Types, RBS, "Types"); IMPORT_CONSTANT(RBS_Types_Bases, RBS_Types, "Bases"); @@ -121,6 +147,26 @@ void rbs__init_constants(void) { IMPORT_CONSTANT(RBS_AST_Members_Prepend, RBS_AST_Members, "Prepend"); IMPORT_CONSTANT(RBS_AST_Members_Private, RBS_AST_Members, "Private"); IMPORT_CONSTANT(RBS_AST_Members_Public, RBS_AST_Members, "Public"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_BlockParamTypeAnnotation, RBS_AST_Ruby_Annotation, "BlockParamTypeAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_ClassIvarTypeAnnotation, RBS_AST_Ruby_Annotation, "ClassIvarTypeAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_ClassVarTypeAnnotation, RBS_AST_Ruby_Annotation, "ClassVarTypeAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_ColonMethodTypeAnnotation, RBS_AST_Ruby_Annotation, "ColonMethodTypeAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_DoubleSplatParamTypeAnnotation, RBS_AST_Ruby_Annotation, "DoubleSplatParamTypeAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_EmbeddedRBSAnnotation, RBS_AST_Ruby_Annotation, "EmbeddedRBSAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_GenericAnnotation, RBS_AST_Ruby_Annotation, "GenericAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_InheritsAnnotation, RBS_AST_Ruby_Annotation, "InheritsAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_IvarTypeAnnotation, RBS_AST_Ruby_Annotation, "IvarTypeAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_MethodTypesAnnotation, RBS_AST_Ruby_Annotation, "MethodTypesAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_MethodTypesAnnotation_Overload, RBS_AST_Ruby_Annotation_MethodTypesAnnotation, "Overload"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_ModuleSelfAnnotation, RBS_AST_Ruby_Annotation, "ModuleSelfAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_NodeApplication, RBS_AST_Ruby_Annotation, "NodeApplication"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_NodeTypeAssertion, RBS_AST_Ruby_Annotation, "NodeTypeAssertion"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_OverrideAnnotation, RBS_AST_Ruby_Annotation, "OverrideAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_ParamTypeAnnotation, RBS_AST_Ruby_Annotation, "ParamTypeAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_RBSAnnotationAnnotation, RBS_AST_Ruby_Annotation, "RBSAnnotationAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_ReturnTypeAnnotation, RBS_AST_Ruby_Annotation, "ReturnTypeAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_SkipAnnotation, RBS_AST_Ruby_Annotation, "SkipAnnotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_SplatParamTypeAnnotation, RBS_AST_Ruby_Annotation, "SplatParamTypeAnnotation"); IMPORT_CONSTANT(RBS_AST_TypeParam, RBS_AST, "TypeParam"); IMPORT_CONSTANT(RBS_MethodType, RBS, "MethodType"); IMPORT_CONSTANT(RBS_Namespace, RBS, "Namespace"); diff --git a/src/ruby_objs.c b/src/ruby_objs.c index be3e717f8..bc8b975ae 100644 --- a/src/ruby_objs.c +++ b/src/ruby_objs.c @@ -439,6 +439,319 @@ VALUE rbs_ast_members_public(VALUE location) { ); } +VALUE rbs_ast_ruby_annotation_block_param_type_annotation(VALUE location, VALUE prefix_location, VALUE operator_location, VALUE param_name_location, VALUE colon_location, VALUE question_mark_location, VALUE block, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("operator_location")), operator_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("param_name_location")), param_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("colon_location")), colon_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("question_mark_location")), question_mark_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("block")), block); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_BlockParamTypeAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_class_ivar_type_annotation(VALUE location, VALUE prefix_location, VALUE self_location, VALUE dot_location, VALUE var_name_location, VALUE colon_location, VALUE type, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("self_location")), self_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("dot_location")), dot_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("var_name_location")), var_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("colon_location")), colon_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_ClassIvarTypeAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_class_var_type_annotation(VALUE location, VALUE prefix_location, VALUE var_name_location, VALUE colon_location, VALUE type, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("var_name_location")), var_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("colon_location")), colon_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_ClassVarTypeAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_colon_method_type_annotation(VALUE location, VALUE prefix_location, VALUE annotations, VALUE method_type) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("annotations")), annotations); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("method_type")), method_type); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_ColonMethodTypeAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_double_splat_param_type_annotation(VALUE location, VALUE prefix_location, VALUE operator_location, VALUE param_name_location, VALUE colon_location, VALUE type, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("operator_location")), operator_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("param_name_location")), param_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("colon_location")), colon_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_DoubleSplatParamTypeAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_embedded_rbs_annotation(VALUE location, VALUE prefix_location, VALUE members) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("members")), members); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_EmbeddedRBSAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_generic_annotation(VALUE location, VALUE prefix_location, VALUE generic_location, VALUE unchecked_location, VALUE variance_location, VALUE name_location, VALUE upper_bound_operator_location, VALUE upper_bound, VALUE default_type_operator_location, VALUE default_type, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("generic_location")), generic_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("unchecked_location")), unchecked_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("variance_location")), variance_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("name_location")), name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("upper_bound_operator_location")), upper_bound_operator_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("upper_bound")), upper_bound); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("default_type_operator_location")), default_type_operator_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("default_type")), default_type); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_GenericAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_inherits_annotation(VALUE location, VALUE prefix_location, VALUE inherits_location, VALUE type_name, VALUE type_name_location, VALUE open_paren_location, VALUE type_args, VALUE close_paren_location, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("inherits_location")), inherits_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type_name")), type_name); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type_name_location")), type_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("open_paren_location")), open_paren_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type_args")), type_args); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("close_paren_location")), close_paren_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_InheritsAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_ivar_type_annotation(VALUE location, VALUE prefix_location, VALUE var_name_location, VALUE colon_location, VALUE type, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("var_name_location")), var_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("colon_location")), colon_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_IvarTypeAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_method_types_annotation(VALUE location, VALUE prefix_location, VALUE overloads, VALUE vertical_bar_locations) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("overloads")), overloads); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("vertical_bar_locations")), vertical_bar_locations); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_MethodTypesAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_method_types_annotation_overload(VALUE annotations, VALUE method_type) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("annotations")), annotations); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("method_type")), method_type); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_MethodTypesAnnotation_Overload, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_module_self_annotation(VALUE location, VALUE prefix_location, VALUE module_self_location, VALUE type_name, VALUE type_name_location, VALUE open_paren_location, VALUE type_args, VALUE close_paren_location, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("module_self_location")), module_self_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type_name")), type_name); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type_name_location")), type_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("open_paren_location")), open_paren_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type_args")), type_args); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("close_paren_location")), close_paren_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_ModuleSelfAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_node_application(VALUE location, VALUE prefix_location, VALUE types, VALUE suffix_location) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("types")), types); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("suffix_location")), suffix_location); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_NodeApplication, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_node_type_assertion(VALUE location, VALUE prefix_location, VALUE type) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_NodeTypeAssertion, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_override_annotation(VALUE location, VALUE prefix_location, VALUE override_location) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("override_location")), override_location); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_OverrideAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_param_type_annotation(VALUE location, VALUE prefix_location, VALUE param_name_location, VALUE colon_location, VALUE type, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("param_name_location")), param_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("colon_location")), colon_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_ParamTypeAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_rbs_annotation_annotation(VALUE location, VALUE prefix_location, VALUE annotations) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("annotations")), annotations); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_RBSAnnotationAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_return_type_annotation(VALUE location, VALUE prefix_location, VALUE return_location, VALUE colon_location, VALUE return_type, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("return_location")), return_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("colon_location")), colon_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("return_type")), return_type); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_ReturnTypeAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_skip_annotation(VALUE location, VALUE comment, VALUE prefix_location, VALUE skip_location) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("skip_location")), skip_location); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_SkipAnnotation, + 1, + &_init_kwargs + ); +} + +VALUE rbs_ast_ruby_annotation_splat_param_type_annotation(VALUE location, VALUE prefix_location, VALUE operator_location, VALUE param_name_location, VALUE colon_location, VALUE type, VALUE comment) { + VALUE _init_kwargs = rb_hash_new(); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("location")), location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("prefix_location")), prefix_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("operator_location")), operator_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("param_name_location")), param_name_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("colon_location")), colon_location); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("type")), type); + rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("comment")), comment); + + return CLASS_NEW_INSTANCE( + RBS_AST_Ruby_Annotation_SplatParamTypeAnnotation, + 1, + &_init_kwargs + ); +} + VALUE rbs_ast_type_param(VALUE name, VALUE variance, VALUE upper_bound, VALUE default_type, VALUE unchecked, VALUE location) { VALUE _init_kwargs = rb_hash_new(); rb_hash_aset(_init_kwargs, ID2SYM(rb_intern("name")), name); diff --git a/steep/Gemfile b/steep/Gemfile index 9fc874c55..7eaaa7a69 100644 --- a/steep/Gemfile +++ b/steep/Gemfile @@ -1,4 +1,8 @@ source "https://rubygems.org" -gem "rbs", "~> 3.9.pre" +gem "rbs", "~> 3.9.0" gem "steep", "~> 1.10" +gem "prism", "~> 1.3.0" +gem "ruby-lsp", require: false, group: :development +gem "debug", require: false, group: :development, platforms: :mri +gem "test-unit" diff --git a/steep/Gemfile.lock b/steep/Gemfile.lock index 2d41ded3a..af472aa35 100644 --- a/steep/Gemfile.lock +++ b/steep/Gemfile.lock @@ -21,11 +21,19 @@ GEM concurrent-ruby (1.3.5) connection_pool (2.5.0) csv (3.3.2) + date (3.4.1) + debug (1.10.0) + irb (~> 1.10) + reline (>= 0.3.8) drb (2.2.1) ffi (1.17.1) fileutils (1.7.3) i18n (1.14.7) concurrent-ruby (~> 1.0) + io-console (0.8.0) + irb (1.14.3) + rdoc (>= 4.0.0) + reline (>= 0.4.2) json (2.10.2) language_server-protocol (3.17.0.4) listen (3.9.0) @@ -37,6 +45,11 @@ GEM parser (3.3.7.1) ast (~> 2.4.1) racc + power_assert (2.0.5) + prism (1.3.0) + psych (5.2.3) + date + stringio racc (1.8.1) rainbow (3.1.1) rb-fsevent (0.11.2) @@ -44,7 +57,17 @@ GEM ffi (~> 1.0) rbs (3.9.0) logger + rdoc (6.11.0) + psych (>= 4.0.0) + reline (0.6.0) + io-console (~> 0.5) + ruby-lsp (0.23.6) + language_server-protocol (~> 3.17.0) + prism (>= 1.2, < 2.0) + rbs (>= 3, < 4) + sorbet-runtime (>= 0.5.10782) securerandom (0.4.1) + sorbet-runtime (0.5.11766) steep (1.10.0) activesupport (>= 5.1) concurrent-ruby (>= 1.1.10) @@ -62,9 +85,12 @@ GEM strscan (>= 1.0.0) terminal-table (>= 2, < 5) uri (>= 0.12.0) + stringio (3.1.2) strscan (3.1.2) terminal-table (4.0.0) unicode-display_width (>= 1.1.1, < 4) + test-unit (3.6.7) + power_assert tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (3.1.4) @@ -76,8 +102,12 @@ PLATFORMS ruby DEPENDENCIES - rbs (~> 3.9.pre) + debug + prism (~> 1.3.0) + rbs (~> 3.9.0) + ruby-lsp steep (~> 1.10) + test-unit BUNDLED WITH 2.6.3 diff --git a/templates/include/rbs/constants.h.erb b/templates/include/rbs/constants.h.erb index 7be88cdfa..9e8109d2d 100644 --- a/templates/include/rbs/constants.h.erb +++ b/templates/include/rbs/constants.h.erb @@ -7,6 +7,9 @@ extern VALUE RBS_AST; extern VALUE RBS_AST_Declarations; extern VALUE RBS_AST_Directives; extern VALUE RBS_AST_Members; +extern VALUE RBS_AST_Ruby; +extern VALUE RBS_AST_Ruby_Annotation; +extern VALUE RBS_AST_Ruby_Annotation_MethodTypesAnnotation; extern VALUE RBS_Types; extern VALUE RBS_Types_Bases; extern VALUE RBS_ParsingError; diff --git a/templates/src/constants.c.erb b/templates/src/constants.c.erb index 1a6ff9400..6baf3bd96 100644 --- a/templates/src/constants.c.erb +++ b/templates/src/constants.c.erb @@ -7,6 +7,9 @@ VALUE RBS_AST; VALUE RBS_AST_Declarations; VALUE RBS_AST_Directives; VALUE RBS_AST_Members; +VALUE RBS_AST_Ruby; +VALUE RBS_AST_Ruby_Annotation; +VALUE RBS_AST_Ruby_Annotation_MethodTypesAnnotation; VALUE RBS_Parser; VALUE RBS_Types; VALUE RBS_Types_Bases; @@ -27,6 +30,9 @@ void rbs__init_constants(void) { IMPORT_CONSTANT(RBS_AST_Declarations, RBS_AST, "Declarations"); IMPORT_CONSTANT(RBS_AST_Directives, RBS_AST, "Directives"); IMPORT_CONSTANT(RBS_AST_Members, RBS_AST, "Members"); + IMPORT_CONSTANT(RBS_AST_Ruby, RBS_AST, "Ruby"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation, RBS_AST_Ruby, "Annotation"); + IMPORT_CONSTANT(RBS_AST_Ruby_Annotation_MethodTypesAnnotation, RBS_AST_Ruby_Annotation, "MethodTypesAnnotation"); IMPORT_CONSTANT(RBS_Types, RBS, "Types"); IMPORT_CONSTANT(RBS_Types_Bases, RBS_Types, "Bases"); diff --git a/test/rbs/ancestor_builder_test.rb b/test/rbs/ancestor_builder_test.rb index ae8579c3b..087a62049 100644 --- a/test/rbs/ancestor_builder_test.rb +++ b/test/rbs/ancestor_builder_test.rb @@ -77,6 +77,225 @@ class Hello[X] < Array[Integer] end end + def test_one_ancestors__inline__generic_class + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + # @rbs generic A < Integer + # @rbs generic B = Array[A] + class Foo + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder::AncestorBuilder.new(env: env) + + builder.one_instance_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_equal [:A, :B], a.params + end + + builder.one_singleton_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_nil a.params + end + end + end + end + + def test_one_ancestors__inline__module__generic + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + # @rbs generic A < Integer + # @rbs generic B = Array[A] + module Foo + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder::AncestorBuilder.new(env: env) + + builder.one_instance_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_equal [:A, :B], a.params + end + + builder.one_singleton_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_nil a.params + end + end + end + end + + def test_one_ancestors__inline__module__self + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + # @rbs module-self BasicObject + module Foo + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder::AncestorBuilder.new(env: env) + + builder.one_instance_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_equal( + [ + Ancestor::Instance.new(name: type_name("::BasicObject"), args: [], source: nil) + ], + a.self_types + ) + end + end + end + end + + def test_one_ancestors__inline__class + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + module M + end + + class Foo + # @rbs generic T + module Bar + end + + include M + prepend Bar #[String] + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder::AncestorBuilder.new(env: env) + + builder.one_instance_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_equal [], a.params + + assert_equal( + Ancestor::Instance.new(name: type_name("::Object"), args: [], source: nil), + a.super_class + ) + + assert_equal( + [ + Ancestor::Instance.new(name: type_name("::M"), args: [], source: nil) + ], + a.included_modules + ) + + assert_equal( + [ + Ancestor::Instance.new(name: type_name("::Foo::Bar"), args: [parse_type("::String")], source: nil) + ], + a.prepended_modules + ) + end + + builder.one_singleton_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_nil a.params + + assert_equal( + Ancestor::Singleton.new(name: type_name("::Object")), + a.super_class + ) + end + end + end + end + + def test_one_ancestors__inline__class__inherits_annotation + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + c = BasicObject + + # @rbs inherits BasicObject + class Foo < c + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder::AncestorBuilder.new(env: env) + + builder.one_instance_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_equal [], a.params + + assert_equal( + Ancestor::Instance.new(name: type_name("::BasicObject"), args: [], source: nil), + a.super_class + ) + end + + builder.one_singleton_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_nil a.params + + assert_equal( + Ancestor::Singleton.new(name: type_name("::BasicObject")), + a.super_class + ) + end + end + end + end + + def test_one_ancestors__inline__embedded__include + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + module Bar + end + + class Foo + # @rbs! + # include Bar + # extend Bar + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder::AncestorBuilder.new(env: env) + + builder.one_instance_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_equal [], a.params + + assert_equal( + Ancestor::Instance.new(name: type_name("::Object"), args: [], source: nil), + a.super_class + ) + + assert_equal( + [ + Ancestor::Instance.new(name: type_name("::Bar"), args: [], source: nil) + ], + a.included_modules + ) + end + + builder.one_singleton_ancestors(type_name("::Foo")).tap do |a| + assert_equal type_name("::Foo"), a.type_name + assert_nil a.params + + assert_equal( + Ancestor::Singleton.new(name: type_name("::Object")), + a.super_class + ) + + assert_equal( + [ + Ancestor::Instance.new(name: type_name("::Bar"), args: [], source: nil) + ], + a.extended_modules + ) + end + end + end + end + def test_one_ancestors_module SignatureManager.new(system_builtin: true) do |manager| manager.files[Pathname("foo.rbs")] = < Bar + # + # Bar + RUBY + + block = CommentBlock.new(buffer, comments) + + paragraphs = block.each_paragraph([]).to_a + + paragraphs[0].tap do |paragraph| + assert_instance_of RBS::Location, paragraph + assert_equal ": Foo\n", paragraph.source + end + paragraphs[1].tap do |paragraph| + assert_instance_of RBS::AST::Ruby::Annotation::ColonMethodTypeAnnotation, paragraph + assert_equal ": %a{foo}\n () -> Bar", paragraph.location.source + end + paragraphs[2].tap do |paragraph| + assert_instance_of RBS::Location, paragraph + assert_equal "\nBar", paragraph.source + end + end + + def test_trailing_annotation + buffer, comments = parse_comments(<<~RUBY) + foo #: String + + foo #[String] + + foo #: String[ + + foo # This is some comment + + #: String + RUBY + + blocks = CommentBlock.build(buffer, comments) + + blocks[0].trailing_annotation([]).tap do |annotation| + assert_instance_of RBS::AST::Ruby::Annotation::NodeTypeAssertion, annotation + end + + blocks[1].trailing_annotation([]).tap do |annotation| + assert_instance_of RBS::AST::Ruby::Annotation::NodeApplication, annotation + end + + blocks[2].trailing_annotation([]).tap do |annotation| + assert_instance_of CommentBlock::AnnotationSyntaxError, annotation + end + + blocks[3].trailing_annotation([]).tap do |annotation| + assert_nil annotation + end + + blocks[4].trailing_annotation([]).tap do |annotation| + assert_nil annotation + end + end +end diff --git a/test/rbs/buffer_test.rb b/test/rbs/buffer_test.rb index a3fbc01c6..fe5ddbc87 100644 --- a/test/rbs/buffer_test.rb +++ b/test/rbs/buffer_test.rb @@ -9,8 +9,8 @@ def test_buffer abc CONTENT - assert_equal ["123\n", "abc\n"], buffer.lines - assert_equal [0...4, 4...8], buffer.ranges + assert_equal ["123", "abc", ""], buffer.lines + assert_equal [0...3, 4...7, 8...8], buffer.ranges assert_equal [1, 0], buffer.pos_to_loc(0) assert_equal [1, 1], buffer.pos_to_loc(1) @@ -41,8 +41,8 @@ def test_buffer def test_buffer_with_no_eol buffer = Buffer.new(name: Pathname("foo.rbs"), content: "123\nabc") - assert_equal ["123\n", "abc"], buffer.lines - assert_equal [0...4, 4...8], buffer.ranges + assert_equal ["123", "abc"], buffer.lines + assert_equal [0...3, 4...7], buffer.ranges assert_equal [1, 0], buffer.pos_to_loc(0) assert_equal [1, 1], buffer.pos_to_loc(1) @@ -67,4 +67,25 @@ def test_buffer_with_no_eol assert_equal 7, buffer.last_position end + + def test_sub_buffer + buffer = Buffer.new(name: Pathname("foo.rbs"), content: <<~CONTENT) + 123 + abc + CONTENT + + buffer.sub_buffer(lines: [1...3, 5...7]).tap do |sub_buffer| + assert_equal <<~CONTENT.chomp, sub_buffer.content + 23 + bc + CONTENT + + assert_equal 1, sub_buffer.parent_position(0) + assert_equal 2, sub_buffer.parent_position(1) + assert_equal 3, sub_buffer.parent_position(2) + assert_equal 5, sub_buffer.parent_position(3) + assert_equal 6, sub_buffer.parent_position(4) + assert_equal 7, sub_buffer.parent_position(5) + end + end end diff --git a/test/rbs/definition_builder_test.rb b/test/rbs/definition_builder_test.rb index 9e3bf0e7b..f21d4c539 100644 --- a/test/rbs/definition_builder_test.rb +++ b/test/rbs/definition_builder_test.rb @@ -2323,7 +2323,7 @@ class Foo end DEF RBS::Parser.parse_signature(rbs).tap do |buf, dirs, decls| - env.add_signature(buffer: buf, directives: dirs, decls: decls) + env.add_signature(RBS::Source::RBS.new(buf, dirs, decls)) end definition_builder = RBS::DefinitionBuilder.new(env: env.resolve_type_names) definition_builder.build_instance(RBS::TypeName.parse("::Foo")).tap do |defn| @@ -3150,4 +3150,142 @@ class A end end end + + def test__inline__build_instance__class + SignatureManager.new do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + def puts = "123" #: String + + # @rbs () -> Integer? + def hello + end + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder.new(env: env) + + builder.build_instance(type_name("::Foo")).tap do |definition| + assert_instance_of Definition, definition + assert_equal type_name("::Foo"), definition.type_name + assert_equal parse_type("::Foo"), definition.self_type + assert_equal [], definition.type_params + + assert_operator definition.methods, :key?, :puts + definition.methods[:puts].tap do |method| + assert_method_definition method, ["() -> ::String"], accessibility: :public + assert_instance_of RBS::Definition::Method, method.super_method + end + + assert_operator definition.methods, :key?, :hello + definition.methods[:hello].tap do |method| + assert_method_definition method, ["() -> ::Integer?"], accessibility: :public + assert_nil method.super_method + end + + assert_empty definition.instance_variables + end + end + end + end + + def test__inline__build__class__variables + SignatureManager.new do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + # @rbs @name: String + # @rbs self.@name: Symbol + # @rbs @@name: bool + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder.new(env: env) + + builder.build_instance(type_name("::Foo")).tap do |definition| + assert_instance_of Definition, definition + assert_equal type_name("::Foo"), definition.type_name + assert_equal parse_type("::Foo"), definition.self_type + + definition.instance_variables[:@name].tap do |var| + assert_equal parse_type("::String"), var.type + end + definition.class_variables[:@@name].tap do |var| + assert_equal parse_type("bool"), var.type + end + end + + builder.build_singleton(type_name("::Foo")).tap do |definition| + assert_instance_of Definition, definition + assert_equal type_name("::Foo"), definition.type_name + assert_equal parse_type("singleton(::Foo)"), definition.self_type + + definition.instance_variables[:@name].tap do |var| + assert_equal parse_type("::Symbol"), var.type + end + definition.class_variables[:@@name].tap do |var| + assert_equal parse_type("bool"), var.type + end + end + end + end + end + + def test__inline__build__class__embedded + SignatureManager.new do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + # @rbs! + # @@instance: Foo + # + # def foo: () -> void + # attr_reader bar: untyped + # alias baz foo + # @name: String + # + # def self.foo: () -> String + # attr_reader self.bar: String + # alias self.baz self.foo + # self.@size: Integer + end + RUBY + + manager.build do |env| + builder = DefinitionBuilder.new(env: env) + + builder.build_instance(type_name("::Foo")).tap do |definition| + assert_instance_of Definition, definition + + assert_method_definition definition.methods[:foo], ["() -> void"] + assert_method_definition definition.methods[:bar], ["() -> untyped"] + assert_method_definition definition.methods[:baz], ["() -> void"] + + definition.instance_variables[:@name].tap do |var| + assert_equal parse_type("::String"), var.type + end + + definition.class_variables[:@@instance].tap do |var| + assert_equal parse_type("::Foo"), var.type + end + end + + builder.build_singleton(type_name("::Foo")).tap do |definition| + assert_instance_of Definition, definition + + assert_method_definition definition.methods[:foo], ["() -> ::String"] + assert_method_definition definition.methods[:bar], ["() -> ::String"] + assert_method_definition definition.methods[:baz], ["() -> ::String"] + + definition.instance_variables[:@size].tap do |var| + assert_equal parse_type("::Integer"), var.type + end + + definition.class_variables[:@@instance].tap do |var| + assert_equal parse_type("::Foo"), var.type + end + end + end + end + end end diff --git a/test/rbs/environment_test.rb b/test/rbs/environment_test.rb index 833b55e57..d96c24370 100644 --- a/test/rbs/environment_test.rb +++ b/test/rbs/environment_test.rb @@ -7,29 +7,6 @@ class RBS::EnvironmentTest < Test::Unit::TestCase Namespace = RBS::Namespace InvalidTypeApplicationError = RBS::InvalidTypeApplicationError - def test_entry_context - _, _, decls = RBS::Parser.parse_signature(< bool end RBS - env.add_signature(buffer: buf, directives: dirs, decls: decls) + env.add_signature(RBS::Source::RBS.new(buf, dirs, decls)) env_ = env.resolve_type_names writer = RBS::Writer.new(out: StringIO.new) - writer.write(env_.declarations) + writer.write(env_.each_decl.to_a) assert_equal(< void", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ColonMethodTypeAnnotation, annot + assert_equal ": (untyped) -> void", annot.location.source + assert_equal ":", annot.prefix_location.source + assert_equal "(untyped) -> void", annot.method_type.location.source + end + + Parser.parse_inline(": [T] (T) -> T", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ColonMethodTypeAnnotation, annot + assert_equal ": [T] (T) -> T", annot.location.source + assert_equal ":", annot.prefix_location.source + assert_equal "[T] (T) -> T", annot.method_type.location.source + end + + Parser.parse_inline(": { () -> void } -> top", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ColonMethodTypeAnnotation, annot + assert_equal ": { () -> void } -> top", annot.location.source + assert_equal ":", annot.prefix_location.source + assert_equal "{ () -> void } -> top", annot.method_type.location.source + end + + Parser.parse_inline(": %a{foo} () -> top", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ColonMethodTypeAnnotation, annot + assert_equal ": %a{foo} () -> top", annot.location.source + assert_equal ":", annot.prefix_location.source + assert_equal "() -> top", annot.method_type.location.source + assert_equal ["foo"], annot.annotations.map(&:string) + end + end + + def test_method_types_annotation + Parser.parse_inline("@rbs (untyped) -> void | %a{pure} () -> String?", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::MethodTypesAnnotation, annot + assert_equal "@rbs (untyped) -> void | %a{pure} () -> String?", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + + annot.overloads[0] do |overload| + assert_empty overload.annotations + assert_equal "(untyped) -> void", overload.method_type.location.source + end + + annot.overloads[1] do |overload| + assert_equal ["pure"], overload.annotations.map(&:string) + assert_equal "() -> String?", overload.method_type.location.source + end + + assert_equal ["|"], annot.vertical_bar_locations.map(&:source) + end + + Parser.parse_inline("@rbs %a{pure} () -> String?", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::MethodTypesAnnotation, annot + assert_equal "@rbs %a{pure} () -> String?", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + + annot.overloads[0] do |overload| + assert_equal ["pure"], overload.annotations.map(&:string) + assert_equal "() -> String?", overload.method_type.location.source + end + + assert_empty annot.vertical_bar_locations.map(&:source) + end + end + + def test_return_type_annotation + Parser.parse_inline("@rbs return: String -- returns a string", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ReturnTypeAnnotation, annot + assert_equal "@rbs return: String -- returns a string", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "return", annot.return_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.return_type.location.source + assert_equal "-- returns a string", annot.comment.source + end + end + + def test_param_type_annotation + Parser.parse_inline("@rbs param: String -- a string", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ParamTypeAnnotation, annot + assert_equal "@rbs param: String -- a string", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "param", annot.param_name_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- a string", annot.comment.source + end + end + + def test_splat_param_type_annotation + Parser.parse_inline("@rbs *args: String -- strings", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::SplatParamTypeAnnotation, annot + assert_equal "@rbs *args: String -- strings", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "*", annot.operator_location.source + assert_equal "args", annot.param_name_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- strings", annot.comment.source + end + + Parser.parse_inline("@rbs *: String -- strings", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::SplatParamTypeAnnotation, annot + assert_equal "@rbs *: String -- strings", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "*", annot.operator_location.source + assert_nil annot.param_name_location + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- strings", annot.comment.source + end + end + + def test_double_splat_param_type_annotation + Parser.parse_inline("@rbs **args: String -- strings", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::DoubleSplatParamTypeAnnotation, annot + assert_equal "@rbs **args: String -- strings", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "**", annot.operator_location.source + assert_equal "args", annot.param_name_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- strings", annot.comment.source + end + + Parser.parse_inline("@rbs **: String -- strings", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::DoubleSplatParamTypeAnnotation, annot + assert_equal "@rbs **: String -- strings", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "**", annot.operator_location.source + assert_nil annot.param_name_location + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- strings", annot.comment.source + end + end + + def test_block_param_type_annotation + Parser.parse_inline("@rbs &block: (String) -> void -- block", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::BlockParamTypeAnnotation, annot + assert_equal "@rbs &block: (String) -> void -- block", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "&", annot.operator_location.source + assert_equal "block", annot.param_name_location.source + assert_equal ":", annot.colon_location.source + assert_nil annot.question_mark_location + assert_instance_of Types::Block, annot.block + assert annot.block.required + assert_equal "-- block", annot.comment.source + end + + Parser.parse_inline("@rbs &: ? () [self: instance] -> void -- block", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::BlockParamTypeAnnotation, annot + assert_equal "@rbs &: ? () [self: instance] -> void -- block", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "&", annot.operator_location.source + assert_nil annot.param_name_location + assert_equal ":", annot.colon_location.source + assert_equal "?", annot.question_mark_location.source + assert_instance_of Types::Block, annot.block + refute annot.block.required + assert_equal "-- block", annot.comment.source + end + end + + def test_override_annotation + Parser.parse_inline("@rbs override", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::OverrideAnnotation, annot + assert_equal "@rbs override", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "override", annot.override_location.source + end + end + + def test_generic_annotation + Parser.parse_inline("@rbs generic A", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::GenericAnnotation, annot + assert_equal "@rbs generic A", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "generic", annot.generic_location.source + assert_nil annot.unchecked_location + assert_nil annot.variance_location + assert_equal "A", annot.name_location.source + assert_nil annot.upper_bound_operator_location + assert_nil annot.upper_bound + assert_nil annot.default_type_operator_location + assert_nil annot.default_type + assert_nil annot.comment + end + + Parser.parse_inline("@rbs generic unchecked out A < String = untyped -- hello", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::GenericAnnotation, annot + assert_equal "@rbs generic unchecked out A < String = untyped -- hello", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "generic", annot.generic_location.source + assert_equal "unchecked", annot.unchecked_location.source + assert_equal "out", annot.variance_location.source + assert_equal "A", annot.name_location.source + assert_equal "<", annot.upper_bound_operator_location.source + assert_instance_of RBS::Types::ClassInstance, annot.upper_bound + assert_equal "=", annot.default_type_operator_location.source + assert_instance_of RBS::Types::Bases::Any, annot.default_type + assert_equal "-- hello", annot.comment.source + end + end + + def test_annotation_annotation + Parser.parse_inline("@rbs %a{foo} %a{bar}", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::RBSAnnotationAnnotation, annot + assert_equal "@rbs %a{foo} %a{bar}", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal ["foo", "bar"], annot.annotations.map(&:string) + end + end + + def test_type_assertion + Parser.parse_inline_assertion(": String?", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::NodeTypeAssertion, annot + assert_equal ": String?", annot.location.source + assert_equal ":", annot.prefix_location.source + assert_instance_of RBS::Types::Optional, annot.type + end + end + + def test_type_application + Parser.parse_inline_assertion("[String?, nil]", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::NodeApplication, annot + assert_equal "[String?, nil]", annot.location.source + assert_equal "[", annot.prefix_location.source + assert_instance_of RBS::Types::Optional, annot.types[0] + assert_instance_of RBS::Types::Bases::Nil, annot.types[1] + assert_equal "]", annot.suffix_location.source + end + end + + def test_inherits_annotation + Parser.parse_inline("@rbs inherits Foo", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::InheritsAnnotation, annot + + assert_equal "@rbs inherits Foo", annot.location.source + assert_equal "inherits", annot.inherits_location.source + assert_equal TypeName.parse("Foo"), annot.type_name + assert_equal "Foo", annot.type_name_location.source + assert_nil annot.open_paren_location + assert_empty annot.type_args + assert_nil annot.close_paren_location + assert_nil annot.comment + end + + Parser.parse_inline("@rbs inherits Foo[_Each[untyped], untyped] -- comment", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::InheritsAnnotation, annot + + assert_equal "@rbs inherits Foo[_Each[untyped], untyped] -- comment", annot.location.source + assert_equal "inherits", annot.inherits_location.source + assert_equal TypeName.parse("Foo"), annot.type_name + assert_equal "Foo", annot.type_name_location.source + assert_equal "[", annot.open_paren_location.source + assert_equal "_Each[untyped]", annot.type_args[0].location.source + assert_equal "untyped", annot.type_args[1].location.source + assert_equal "]", annot.close_paren_location.source + assert_equal "-- comment", annot.comment.source + end + end + + def test_class_module_annotation + omit("Implement `@rbs class/@rbs module` annotation") + + Parser.parse_inline("@rbs class Foo[String?, nil]", 0...).tap do |annot| + end + + Parser.parse_inline("@rbs module Foo[String?, nil]", 0...).tap do |annot| + end + end + + def test_module_self_annotation + Parser.parse_inline("@rbs module-self _Foo", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ModuleSelfAnnotation, annot + + assert_equal "@rbs module-self _Foo", annot.location.source + assert_equal "module-self", annot.module_self_location.source + assert_equal TypeName.parse("_Foo"), annot.type_name + assert_equal "_Foo", annot.type_name_location.source + assert_nil annot.open_paren_location + assert_empty annot.type_args + assert_nil annot.close_paren_location + end + + Parser.parse_inline("@rbs module-self Foo[String?, nil] -- comment", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ModuleSelfAnnotation, annot + + assert_equal "@rbs module-self Foo[String?, nil] -- comment", annot.location.source + assert_equal "module-self", annot.module_self_location.source + assert_equal TypeName.parse("Foo"), annot.type_name + assert_equal "Foo", annot.type_name_location.source + assert_equal "[", annot.open_paren_location.source + assert_equal "String?", annot.type_args[0].location.source + assert_equal "nil", annot.type_args[1].location.source + assert_equal "]", annot.close_paren_location.source + assert_equal "-- comment", annot.comment.source + end + end + + def test_instance_variable_annotation + Parser.parse_inline("@rbs @name: String -- name of something", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::IvarTypeAnnotation, annot + + assert_equal "@rbs @name: String -- name of something", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "@name", annot.var_name_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- name of something", annot.comment.source + end + + Parser.parse_inline("@rbs @rbs: String -- name of something", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::IvarTypeAnnotation, annot + + assert_equal "@rbs @rbs: String -- name of something", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "@rbs", annot.var_name_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- name of something", annot.comment.source + end + + Parser.parse_inline("@rbs self.@name: String -- name of something", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ClassIvarTypeAnnotation, annot + + assert_equal "@rbs self.@name: String -- name of something", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "self", annot.self_location.source + assert_equal ".", annot.dot_location.source + assert_equal "@name", annot.var_name_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- name of something", annot.comment.source + end + + Parser.parse_inline("@rbs self.@rbs: String -- name of something", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ClassIvarTypeAnnotation, annot + + assert_equal "@rbs self.@rbs: String -- name of something", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "self", annot.self_location.source + assert_equal ".", annot.dot_location.source + assert_equal "@rbs", annot.var_name_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- name of something", annot.comment.source + end + + Parser.parse_inline("@rbs @@name: String -- name of something", 0...).tap do |annot| + assert_instance_of AST::Ruby::Annotation::ClassVarTypeAnnotation, annot + + assert_equal "@rbs @@name: String -- name of something", annot.location.source + assert_equal "@rbs", annot.prefix_location.source + assert_equal "@@name", annot.var_name_location.source + assert_equal ":", annot.colon_location.source + assert_equal "String", annot.type.location.source + assert_equal "-- name of something", annot.comment.source + end + end + + def test_embbed_syntax + Parser.parse_inline(<<~RBS, 0...).tap do |annot| + @rbs! + interface _Hello + def foo: () -> void + end + + type world = top + RBS + + assert_instance_of AST::Ruby::Annotation::EmbeddedRBSAnnotation, annot + + assert_equal <<~RBS.chomp, annot.location.source + @rbs! + interface _Hello + def foo: () -> void + end + + type world = top + RBS + + assert_equal "@rbs!", annot.prefix_location.source + assert_equal [AST::Declarations::Interface, AST::Declarations::TypeAlias], annot.members.map(&:class) + end + end + + def test_use + omit("Implement `@rbs use` annotation") + Parser.parse_inline_uses(<<~RBS, 0...).tap do |uses| + use Foo, Bar::*, Baz as B + RBS + end + end +end diff --git a/test/rbs/inline_parser_test.rb b/test/rbs/inline_parser_test.rb new file mode 100644 index 000000000..3cc4bed51 --- /dev/null +++ b/test/rbs/inline_parser_test.rb @@ -0,0 +1,915 @@ +require "test_helper" + +class RBS::InlineParserTest < Test::Unit::TestCase + def parse_ruby(source) + [ + RBS::Buffer.new(name: Pathname("test.rb"), content: source), + Prism.parse(source) + ] + end + + def test_parse + buffer, result = parse_ruby(<<~RUBY) + class Foo + def foo = 123 + end + + module Bar + def self.bar = 123 + end + + class < void"], defn.overloads.map(&:method_type).map(&:to_s) + end + end + + def test_parse__method_definition__return_type_assertion + buffer, result = parse_ruby(<<~RUBY) + class Foo + def foo() #: Integer + end + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].members[0].tap do |defn| + assert_equal [[]], defn.overloads.map { _1.annotations.map(&:string) } + assert_equal ["() -> Integer"], defn.overloads.map(&:method_type).map(&:to_s) + end + end + + def test_parse__method_definition__method_types + buffer, result = parse_ruby(<<~RUBY) + class Foo + # Hello World + # + #: () -> Integer + #: %a{pure} () -> String? + # @rbs () -> Symbol + # | %a{pure} () -> bool? + # @rbs %a{pure} () -> Array[String] + def foo() + end + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].members[0].tap do |defn| + assert_equal [[], ["pure"], [], ["pure"], ["pure"]], defn.overloads.map { _1.annotations.map(&:string) } + assert_equal ["() -> Integer", "() -> String?", "() -> Symbol", "() -> bool?", "() -> Array[String]"], defn.overloads.map(&:method_type).map(&:to_s) + end + end + + def test_parse__method_definition__block + buffer, result = parse_ruby(<<~RUBY) + class Foo + # @rbs &block: ? (String) -> void -- Block + def foo(&block) + end + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].members[0].tap do |defn| + assert_equal [[]], defn.overloads.map { _1.annotations.map(&:string) } + assert_equal ["() ?{ (String) -> void } -> untyped"], defn.overloads.map(&:method_type).map(&:to_s) + end + end + + def test_unused_annotations + buffer, result = parse_ruby(<<~RUBY) + class Foo + # @rbs () -> void + # @rbs x: String + # @rbs return: untyped + # @rbs %a{pure} + # @rbs y: ( + def foo(x) + end + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].members[0].tap do |defn| + assert_equal [[]], defn.overloads.map { _1.annotations.map(&:string) } + assert_equal ["() -> void"], defn.overloads.map(&:method_type).map(&:to_s) + end + + assert_equal ["@rbs x: String", "@rbs return: untyped", "@rbs y: ("], ret.diagnostics.map { _1.location.source } + end + + def test_mixin__type_args + buffer, result = parse_ruby(<<~RUBY) + class Foo + include Bar #[Integer, String] + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].members[0].tap do |mixin| + assert_equal ["Integer", "String"], mixin.type_args.map(&:to_s) + end + end + + def test_class_decl__generic + buffer, result = parse_ruby(<<~RUBY) + # @rbs generic A -- type parameter of A + # @rbs generic out B < Integer = untyped + class Foo + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].tap do |klass| + assert_equal "A", klass.generics.type_params[0].to_s + assert_equal "out B < Integer = untyped", klass.generics.type_params[1].to_s + end + end + + def test_class_decl__super_annotation + buffer, result = parse_ruby(<<~RUBY) + # @rbs inherits BasicObject -- inherits super class + class Foo + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].tap do |klass| + assert_instance_of RBS::AST::Ruby::Declarations::ClassDecl::SuperAnnotation, klass.super_annotation + assert_instance_of RBS::AST::Ruby::Declarations::ClassDecl::SuperAnnotation, klass.super_class + + assert_equal "BasicObject", klass.super_annotation.name.to_s + assert_empty klass.super_annotation.args + end + end + + def test_module_decl__generic + buffer, result = parse_ruby(<<~RUBY) + # @rbs generic A -- type parameter of A + # @rbs generic out B < Integer = untyped + module Foo + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].tap do |mod| + assert_instance_of RBS::AST::Ruby::Declarations::ModuleDecl, mod + assert_equal "A", mod.generics.type_params[0].to_s + assert_equal "out B < Integer = untyped", mod.generics.type_params[1].to_s + end + end + + def test_module_decl__self_constraints + buffer, result = parse_ruby(<<~RUBY) + # @rbs module-self BasicObject -- type parameter of A + module Foo + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].tap do |mod| + assert_instance_of RBS::AST::Ruby::Declarations::ModuleDecl, mod + + mod.self_constraints[0].tap do |constraint| + assert_instance_of RBS::AST::Ruby::Declarations::ModuleDecl::SelfConstraint, constraint + assert_equal "BasicObject", constraint.name.to_s + assert_equal [], constraint.args + end + end + end + + def test_ivar_annotation + buffer, result = parse_ruby(<<~RUBY) + module Foo + # @rbs @name: String -- name of something + + def foo = nil + + # @rbs self.@name: String -- name of something else + # @rbs @@name: String -- one more name + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].tap do |mod| + assert_instance_of RBS::AST::Ruby::Declarations::ModuleDecl, mod + + mod.members[0].tap do |ivar| + assert_instance_of RBS::AST::Ruby::Members::InstanceVariableMember, ivar + assert_equal :@name, ivar.name + assert_equal "String", ivar.type.to_s + assert_equal "@rbs @name: String -- name of something", ivar.location.source + end + + mod.members[2].tap do |ivar| + assert_instance_of RBS::AST::Ruby::Members::ClassInstanceVariableMember, ivar + assert_equal :@name, ivar.name + assert_equal "String", ivar.type.to_s + assert_equal "@rbs self.@name: String -- name of something else", ivar.location.source + end + + mod.members[3].tap do |ivar| + assert_instance_of RBS::AST::Ruby::Members::ClassVariableMember, ivar + assert_equal :@@name, ivar.name + assert_equal "String", ivar.type.to_s + assert_equal "@rbs @@name: String -- one more name", ivar.location.source + end + end + end + + def test_ivar_annotation__singleton_class + buffer, result = parse_ruby(<<~RUBY) + module Foo + class < Integer + private def foo = 123 + + public def self.baz = 123 #: Integer + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].tap do |decl| + decl.members[0].tap do |member| + assert_instance_of RBS::AST::Ruby::Members::DefMember, member + assert_equal :private, member.visibility + end + + decl.members[1].tap do |member| + assert_instance_of RBS::AST::Ruby::Members::DefSingletonMember, member + assert_equal :public, member.visibility + end + end + end + + def test_parse__attribute + buffer, result = parse_ruby(<<~RUBY) + class Foo + attr_reader :foo + + attr_writer :bar #: String + + attr_accessor(:baz1, :baz2, "baz3") #: Integer + end + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + ret.declarations[0].tap do |decl| + decl.members[0].tap do |member| + assert_instance_of RBS::AST::Ruby::Members::AttrReaderMember, member + assert_equal [:foo], member.names + assert_nil member.type + end + + decl.members[1].tap do |member| + assert_instance_of RBS::AST::Ruby::Members::AttrWriterMember, member + assert_equal [:bar], member.names + assert_equal "String", member.type.to_s + end + + decl.members[2].tap do |member| + assert_instance_of RBS::AST::Ruby::Members::AttrAccessorMember, member + assert_equal [:baz1, :baz2], member.names + assert_equal "Integer", member.type.to_s + end + end + end + + def test_error__attribute + buffer, result = parse_ruby(<<~RUBY) + class Foo + Kernel.attr_reader :foo + + attr_writer "foo" #: String + end + + attr_accessor :hello + RUBY + + ret = RBS::InlineParser.parse(buffer, result) + + assert_any!(ret.diagnostics) do + assert_instance_of RBS::InlineParser::Diagnostics::AttributeDefinitionError, _1 + assert_equal "`attr_reader` call with receiver other than `self` is ignored", _1.message + assert_equal "attr_reader", _1.location.source + end + + assert_any!(ret.diagnostics) do + assert_instance_of RBS::InlineParser::Diagnostics::AttributeDefinitionError, _1 + assert_equal "`attr_writer` call argument other than symbol literal is ignored", _1.message + assert_equal '"foo"', _1.location.source + end + + assert_any!(ret.diagnostics) do + assert_instance_of RBS::InlineParser::Diagnostics::AttributeDefinitionError, _1 + assert_equal "`attr_accessor` call outside of class/module definition is ignored", _1.message + assert_equal "attr_accessor", _1.location.source + end + + ret.declarations[0].tap do |decl| + assert_empty decl.members + end + end +end diff --git a/test/rbs/method_builder_test.rb b/test/rbs/method_builder_test.rb index ce8af3e37..e81723144 100644 --- a/test/rbs/method_builder_test.rb +++ b/test/rbs/method_builder_test.rb @@ -50,6 +50,217 @@ def hello: () -> Integer end end + def test___inline__instance__def + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + def hello = 123 + end + RUBY + + manager.build do |env| + builder = MethodBuilder.new(env: env) + + builder.build_instance(type_name("::Foo")).tap do |methods| + assert_equal parse_type("::Foo"), methods.type + + methods.methods[:hello].tap do |hello| + assert_instance_of MethodBuilder::Methods::Definition, hello + + assert_instance_of AST::Ruby::Members::DefMember, hello.original + assert_equal [parse_method_type("(?) -> untyped")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :public, hello.accessibility + end + end + end + end + end + + def test___inline__singleton__def + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + def self.hello = 123 + end + RUBY + + manager.build do |env| + builder = MethodBuilder.new(env: env) + + builder.build_singleton(type_name("::Foo")).tap do |methods| + assert_equal parse_type("singleton(::Foo)"), methods.type + + methods.methods[:hello].tap do |hello| + assert_instance_of MethodBuilder::Methods::Definition, hello + + assert_instance_of AST::Ruby::Members::DefSingletonMember, hello.original + assert_equal [parse_method_type("(?) -> untyped")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :public, hello.accessibility + end + end + end + end + end + + def test__inline__embedded__instance_def + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + # @rbs! + # def hello: () -> Integer + end + RUBY + + manager.build do |env| + builder = MethodBuilder.new(env: env) + + builder.build_instance(type_name("::Foo")).tap do |methods| + assert_equal parse_type("::Foo"), methods.type + + methods.methods[:hello].tap do |hello| + assert_instance_of MethodBuilder::Methods::Definition, hello + + assert_instance_of AST::Members::MethodDefinition, hello.original + assert_equal [parse_method_type("() -> ::Integer")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :public, hello.accessibility + end + end + end + end + end + + def test__inline__embedded__singleton_def + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + # @rbs! + # def self.hello: () -> Integer + end + RUBY + + manager.build do |env| + builder = MethodBuilder.new(env: env) + + builder.build_singleton(type_name("::Foo")).tap do |methods| + assert_equal parse_type("singleton(::Foo)"), methods.type + + methods.methods[:hello].tap do |hello| + assert_instance_of MethodBuilder::Methods::Definition, hello + + assert_instance_of AST::Members::MethodDefinition, hello.original + assert_equal [parse_method_type("() -> ::Integer")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :public, hello.accessibility + end + end + end + end + end + + def test__inline__embedded__singleton + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + class < untyped")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :public, hello.accessibility + end + end + end + end + end + + def test__inline__embedded__instance_accessibility + SignatureManager.new(system_builtin: true) do |manager| + manager.ruby_files["foo.rb"] = <<~RUBY + class Foo + private + + def m1 = 123 + + # @rbs! + # def m2: () -> Integer + # public + # def m3: () -> Integer + + def m4 = 123 + end + RUBY + + manager.build do |env| + builder = MethodBuilder.new(env: env) + + builder.build_instance(type_name("::Foo")).tap do |methods| + assert_equal parse_type("::Foo"), methods.type + + methods.methods[:m1].tap do |hello| + assert_instance_of MethodBuilder::Methods::Definition, hello + + assert_instance_of AST::Ruby::Members::DefMember, hello.original + assert_equal [parse_method_type("() -> untyped")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :private, hello.accessibility + end + + methods.methods[:m2].tap do |hello| + assert_instance_of MethodBuilder::Methods::Definition, hello + + assert_instance_of AST::Members::MethodDefinition, hello.original + assert_equal [parse_method_type("() -> ::Integer")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :private, hello.accessibility + end + + methods.methods[:m3].tap do |hello| + assert_instance_of MethodBuilder::Methods::Definition, hello + + assert_instance_of AST::Members::MethodDefinition, hello.original + assert_equal [parse_method_type("() -> ::Integer")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :public, hello.accessibility + end + + methods.methods[:m4].tap do |hello| + assert_instance_of MethodBuilder::Methods::Definition, hello + + assert_instance_of AST::Ruby::Members::DefMember, hello.original + assert_equal [parse_method_type("() -> untyped")], hello.original.overloads.map(&:method_type) + assert_empty hello.overloads + + assert_equal :public, hello.accessibility + end + end + end + end + end + def test_instance_attributes SignatureManager.new(system_builtin: true) do |manager| manager.files[Pathname("foo.rbs")] = < void } -> void end end end + + def test_keyword_keyword + Parser.parse_signature(<<~RBS) + class Foo + def inherits: (inherits: top inherits) -> void + + def return: (return: top return) -> void + + def override: (override: top override) -> void + + def generic: (generic: top generic) -> void + + def skip: (skip: top skip) -> void + + alias inherits skip + + @rbs: untyped + + self.@rbs: untyped + end + RBS + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index f64292d22..1c316fae9 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -78,9 +78,11 @@ def silence_warnings class SignatureManager attr_reader :files attr_reader :system_builtin + attr_reader :ruby_files def initialize(system_builtin: false) @files = {} + @ruby_files = {} @system_builtin = system_builtin files[Pathname("builtin.rbs")] = BUILTINS unless system_builtin @@ -179,7 +181,19 @@ def build loader = RBS::EnvironmentLoader.new(core_root: root) loader.add(path: tmppath) - yield RBS::Environment.from_loader(loader).resolve_type_names, tmppath + env = RBS::Environment.from_loader(loader) + + ruby_files.each do |name, content| + buffer = RBS::Buffer.new(name: Pathname(name), content: content) + prism = Prism.parse(content, filepath: name) + + result = RBS::InlineParser.parse(buffer, prism) + source = RBS::Source::Ruby.new(buffer, prism, result.declarations, result.diagnostics) + + env.add_signature(source) + end + + yield env.resolve_type_names, tmppath end end end