Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ We have contextual limitations on some types:

These contextual limitation is introduced at RBS 3.3.
The parser accepts those types even if it doesn't satisfy contextual limitation, but warning is reported with `rbs validate` command.
We plan to change the parser to reject those types if it breaks the contextual limitations in next release -- `3.4`.
Since RBS 4.0, it is reported during parsing.

#### Limitations on `void` types

Expand Down
5 changes: 3 additions & 2 deletions ext/rbs_extension/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "rbs_string_bridging.h"

#include "ruby/vm.h"
#include "rbs/parser.h"

/**
* Raises `RBS::ParsingError` or `RuntimeError` on `tok` with message constructed with given `fmt`.
Expand Down Expand Up @@ -101,7 +102,7 @@ static VALUE parse_type_try(VALUE a) {
}

rbs_node_t *type;
rbs_parse_type(parser, &type);
rbs_parse_type(parser, &type, SkipValidation);

raise_error_if_any(parser, arg->buffer);

Expand Down Expand Up @@ -187,7 +188,7 @@ static VALUE parse_method_type_try(VALUE a) {
}

rbs_method_type_t *method_type = NULL;
rbs_parse_method_type(parser, &method_type);
rbs_parse_method_type(parser, &method_type, SkipValidation);

raise_error_if_any(parser, arg->buffer);

Expand Down
20 changes: 18 additions & 2 deletions include/rbs/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,24 @@ rbs_ast_comment_t *rbs_parser_get_comment(rbs_parser_t *parser, int subject_line

void rbs_parser_set_error(rbs_parser_t *parser, rbs_token_t tok, bool syntax_error, const char *fmt, ...) RBS_ATTRIBUTE_FORMAT(4, 5);

bool rbs_parse_type(rbs_parser_t *parser, rbs_node_t **type);
bool rbs_parse_method_type(rbs_parser_t *parser, rbs_method_type_t **method_type);
/**
* rbs_type_parsing_option_t represents the validation rules for type parsing.
* It controls whether certain types are allowed in specific contexts.
* */
typedef struct {
bool no_void; /* If true, `void` type is not allowed.*/
bool no_void_allowed_here; /* If true, `void` type is not allowed, but it's allowed in one depth.*/
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no_void_allowed_here looks a bit too cryptic to me. Wondering if we can have a better name...

But, I'm wondering if we can merge the two void options. (I know it was from the original validation code, so I mean the original implementation was bad...)

  • Parsing function return types allows direct void types,
  • Parsing generics arguments allows direct void types,
  • Anything else prohibits void types (right?)

I'm assuming we can implement this with only one flag.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with you, but merging the two void options would likely result in an implementation that is no simpler—or potentially more complex—than the current one. Here are the two ideas I’ve come up with:

  • Option 1: Make no_void a type that can represent three states instead of just a bool.
  • Option 2: Add a member to the parser to retain context during parsing.

Neither of these seems like a particularly good idea.

bool no_self; /* If true, `self` type is not allowed.*/
bool no_classish; /* If true, `class` or `instance` types are not allowed.*/
} rbs_type_parsing_option_t;

/**
* SkipValidation is a rbs_type_parsing_option_t that allows all types.
* */
extern const rbs_type_parsing_option_t SkipValidation;

bool rbs_parse_type(rbs_parser_t *parser, rbs_node_t **type, rbs_type_parsing_option_t validation);
bool rbs_parse_method_type(rbs_parser_t *parser, rbs_method_type_t **method_type, rbs_type_parsing_option_t validation);
bool rbs_parse_signature(rbs_parser_t *parser, rbs_signature_t **signature);

bool rbs_parse_type_params(rbs_parser_t *parser, bool module_type_params, rbs_node_list_t **params);
Expand Down
100 changes: 5 additions & 95 deletions lib/rbs/cli/validate.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,18 @@ module RBS
class CLI
class Validate
class Errors
def initialize(limit:, exit_error:)
def initialize(limit:)
@limit = limit
@exit_error = exit_error
@errors = []
@has_syntax_error = false
end

def add(error)
if error.instance_of?(WillSyntaxError)
RBS.logger.warn(build_message(error))
@has_syntax_error = true
else
@errors << error
end
@errors << error
finish if @limit == 1
end

def finish
if @errors.empty?
if @exit_error && @has_syntax_error
exit 1
else
# success
end
else
unless @errors.empty?
@errors.each do |error|
RBS.logger.error(build_message(error))
end
Expand All @@ -53,7 +40,6 @@ def initialize(args:, options:)
@env = Environment.from_loader(loader).resolve_type_names
@builder = DefinitionBuilder.new(env: @env)
@validator = Validator.new(env: @env)
exit_error = false
limit = nil #: Integer?
OptionParser.new do |opts|
opts.banner = <<EOU
Expand All @@ -70,14 +56,14 @@ def initialize(args:, options:)
RBS.print_warning { "`--silent` option is deprecated because it's silent by default. You can use --log-level option of rbs command to display more information." }
end
opts.on("--[no-]exit-error-on-syntax-error", "exit(1) if syntax error is detected") {|bool|
exit_error = bool
RBS.print_warning { "`--[no-]exit-error-on-syntax-error` option is deprecated because it's built in during parsing." }
}
opts.on("--fail-fast", "Exit immediately as soon as a validation error is found.") do |arg|
limit = 1
end
end.parse!(args)

@errors = Errors.new(limit: limit, exit_error: exit_error)
@errors = Errors.new(limit: limit)
end

def run
Expand Down Expand Up @@ -112,9 +98,6 @@ def validate_class_module_definition
entry.each_decl do |decl|
if super_class = decl.super_class
super_class.args.each do |arg|
void_type_context_validator(arg, true)
no_self_type_validator(arg)
no_classish_type_validator(arg)
@validator.validate_type(arg, context: nil)
end
end
Expand All @@ -123,9 +106,6 @@ def validate_class_module_definition
entry.each_decl do |decl|
decl.self_types.each do |self_type|
self_type.args.each do |arg|
void_type_context_validator(arg, true)
no_self_type_validator(arg)
no_classish_type_validator(arg)
@validator.validate_type(arg, context: nil)
end

Expand Down Expand Up @@ -153,9 +133,6 @@ def validate_class_module_definition

d.type_params.each do |param|
if ub = param.upper_bound_type
void_type_context_validator(ub)
no_self_type_validator(ub)
no_classish_type_validator(ub)
@validator.validate_type(ub, context: nil)
end

Expand All @@ -167,9 +144,6 @@ def validate_class_module_definition
end

if dt = param.default_type
void_type_context_validator(dt, true)
no_self_type_validator(dt)
no_classish_type_validator(dt)
@validator.validate_type(dt, context: nil)
end
end
Expand All @@ -183,18 +157,7 @@ def validate_class_module_definition
case member
when AST::Members::MethodDefinition
@validator.validate_method_definition(member, type_name: name)
member.overloads.each do |ov|
void_type_context_validator(ov.method_type)
end
when AST::Members::Attribute
void_type_context_validator(member.type)
when AST::Members::Mixin
member.args.each do |arg|
no_self_type_validator(arg)
unless arg.is_a?(Types::Bases::Void)
void_type_context_validator(arg, true)
end
end
params =
if member.name.class?
module_decl = @env.normalized_module_entry(member.name) or raise
Expand All @@ -206,10 +169,6 @@ def validate_class_module_definition
InvalidTypeApplicationError.check!(type_name: member.name, params: params, args: member.args, location: member.location)
when AST::Members::Var
@validator.validate_variable(member)
void_type_context_validator(member.type)
if member.is_a?(AST::Members::ClassVariable)
no_self_type_validator(member.type)
end
end
end
else
Expand Down Expand Up @@ -245,9 +204,6 @@ def validate_interface

decl.decl.type_params.each do |param|
if ub = param.upper_bound_type
void_type_context_validator(ub)
no_self_type_validator(ub)
no_classish_type_validator(ub)
@validator.validate_type(ub, context: nil)
end

Expand All @@ -259,9 +215,6 @@ def validate_interface
end

if dt = param.default_type
void_type_context_validator(dt, true)
no_self_type_validator(dt)
no_classish_type_validator(dt)
@validator.validate_type(dt, context: nil)
end
end
Expand All @@ -272,10 +225,6 @@ def validate_interface
case member
when AST::Members::MethodDefinition
@validator.validate_method_definition(member, type_name: name)
member.overloads.each do |ov|
void_type_context_validator(ov.method_type)
no_classish_type_validator(ov.method_type)
end
end
end
rescue BaseError => error
Expand All @@ -288,9 +237,6 @@ def validate_constant
RBS.logger.info "Validating constant: `#{name}`..."
@validator.validate_type const.decl.type, context: const.context
@builder.ensure_namespace!(name.namespace, location: const.decl.location)
no_self_type_validator(const.decl.type)
no_classish_type_validator(const.decl.type)
void_type_context_validator(const.decl.type)
rescue BaseError => error
@errors.add(error)
end
Expand All @@ -300,9 +246,6 @@ def validate_global
@env.global_decls.each do |name, global|
RBS.logger.info "Validating global: `#{name}`..."
@validator.validate_type global.decl.type, context: nil
no_self_type_validator(global.decl.type)
no_classish_type_validator(global.decl.type)
void_type_context_validator(global.decl.type)
rescue BaseError => error
@errors.add(error)
end
Expand All @@ -325,9 +268,6 @@ def validate_type_alias

decl.decl.type_params.each do |param|
if ub = param.upper_bound_type
void_type_context_validator(ub)
no_self_type_validator(ub)
no_classish_type_validator(ub)
@validator.validate_type(ub, context: nil)
end

Expand All @@ -339,45 +279,15 @@ def validate_type_alias
end

if dt = param.default_type
void_type_context_validator(dt, true)
no_self_type_validator(dt)
no_classish_type_validator(dt)
@validator.validate_type(dt, context: nil)
end
end

TypeParamDefaultReferenceError.check!(decl.decl.type_params)

no_self_type_validator(decl.decl.type)
no_classish_type_validator(decl.decl.type)
void_type_context_validator(decl.decl.type)
rescue BaseError => error
@errors.add(error)
end
end

private

def no_self_type_validator(type)
if type.has_self_type?
@errors.add WillSyntaxError.new("`self` type is not allowed in this context", location: type.location)
end
end

def no_classish_type_validator(type)
if type.has_classish_type?
@errors.add WillSyntaxError.new("`instance` or `class` type is not allowed in this context", location: type.location)
end
end

def void_type_context_validator(type, allowed_here = false)
if allowed_here
return if type.is_a?(Types::Bases::Void)
end
if type.with_nonreturn_void?
@errors.add WillSyntaxError.new("`void` type is only allowed in return type or generics parameter", location: type.location)
end
end
end
end
end
11 changes: 0 additions & 11 deletions lib/rbs/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -591,17 +591,6 @@ def location
end
end

class WillSyntaxError < DefinitionError
include DetailedMessageable

attr_reader :location

def initialize(message, location:)
super "#{Location.to_string(location)}: #{message}"
@location = location
end
end

class TypeParamDefaultReferenceError < DefinitionError
include DetailedMessageable

Expand Down
4 changes: 2 additions & 2 deletions lib/rbs/prototype/rb.rb
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,8 @@ def process(node, decls:, comments:, context:)
end

value_node = node.children.last
type = if value_node.nil?
# Give up type prediction when node is MASGN.
type = if value_node.nil? || value_node.type == :SELF
# Give up type prediction when node is MASGN or SELF.
Types::Bases::Any.new(location: nil)
else
literal_to_type(value_node)
Expand Down
4 changes: 1 addition & 3 deletions sig/cli/validate.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ module RBS
class Validate
class Errors
@limit: Integer?
@exit_error: boolish
@has_syntax_error: bool
@errors: Array[BaseError]

def initialize: (limit: Integer?, exit_error: boolish) -> void
def initialize: (limit: Integer?) -> void

def add: (BaseError) -> void

Expand Down
8 changes: 0 additions & 8 deletions sig/errors.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -387,14 +387,6 @@ module RBS
def location: () -> AST::Declarations::AliasDecl::loc?
end

class WillSyntaxError < BaseError
include RBS::DetailedMessageable

def initialize: (String message, location: Location[untyped, untyped]?) -> void

attr_reader location: Location[untyped, untyped]?
end

class TypeParamDefaultReferenceError < BaseError
include DetailedMessageable

Expand Down
Loading