Module: RDL::Annotate
- Included in:
- ActiveRecord::AutosaveAssociation, ActiveRecord::Base, ActiveRecord::Calculations, ActiveRecord::Core::ClassMethods, ActiveRecord::Delegation, ActiveRecord::FinderMethods, ActiveRecord::Persistence, ActiveRecord::QueryMethods::WhereChain, ActiveRecord::Querying, ActiveRecord::Relation::QueryMethods, ActiveRecord::Scoping::Named::ClassMethods, ActiveRecord::Suppressor, ActiveRecord::Transactions, ActiveRecord_Relation, JoinTable, RDL, SeqIdent, SeqQualIdent, Sequel, Sequel::Mysql2::Database, SequelDB, Table
- Defined in:
- lib/rdl/wrap.rb,
lib/rdl_disable.rb
Instance Method Summary collapse
-
#attr_accessor_type(*args) ⇒ Object
In the following three methods [+ args +] is a sequence of symbol, typ.
- #attr_reader_type(*args) ⇒ Object (also: #attr_type)
- #attr_writer_type(*args) ⇒ Object
-
#post(*args) ⇒ Object
Add a postcondition to a method.
-
#pre(*args) ⇒ Object
klass
-
may be Class, Symbol, or String [
method
] may be Symbol or String [contract
] must be a Contract [wrap
] indicates whether the contract should be enforced (true) or just recorded (false) [+ version +] is a rubygems version requirement string (or array of such requirement strings) if the current Ruby version does not satisfy the version, the type call will be ignored.
-
#rdl_alias(*args) ⇒ Object
Aliases contracts for meth_old and meth_new.
- #readd_comp_types ⇒ Object
-
#type(*args) ⇒ Object
- + klass +
-
may be Class, Symbol, or String [+ method ] may be Symbol or String [ type ] may be Type or String [ wrap ] indicates whether the type should be enforced (true) or just recorded (false) [ typecheck ] indicates a method that should be statically type checked, as follows if :call, indicates method should be typechecked when called if :now, indicates method should be typechecked immediately if other-symbol, indicates method should be typechecked when rdl_do_typecheck(other-symbol) is called [ version +] is a rubygems version requirement string (or array of such requirement strings) if the current Ruby version does not satisfy the version, the type call will be ignored.
-
#type_params(*args) ⇒ Object
- + klass +
-
is the class whose type parameters to set; self if omitted [
params
] is an array of symbols or strings that are the parameters of this (generic) type [variance
] is an array of the corresponding variances, :+ for covariant, :- for contravariant, and :~ for invariant.
-
#var_type(*args) ⇒ Object
- + klass +
-
is the class containing the variable; self if omitted; ignored for local and global variables [+ var ] is a symbol or string containing the name of the variable [ typ +] is a string containing the type.
Instance Method Details
#attr_accessor_type(*args) ⇒ Object
In the following three methods
- + args +
-
is a sequence of symbol, typ. attr_reader is called for each symbol,
and var_type is called to assign the immediately following type to the attribute named after that symbol. Note these three methods are duplicated in RDLAnnotate
405 406 407 408 409 410 411 412 413 |
# File 'lib/rdl/wrap.rb', line 405 def attr_accessor_type(*args) args.each_slice(2) { |name, typ| attr_accessor name var_type ("@" + name.to_s), typ type name, "() -> #{typ}" type name.to_s + "=", "(#{typ}) -> #{typ}" } nil end |
#attr_reader_type(*args) ⇒ Object Also known as: attr_type
415 416 417 418 419 420 421 422 |
# File 'lib/rdl/wrap.rb', line 415 def attr_reader_type(*args) args.each_slice(2) { |name, typ| attr_reader name var_type ("@" + name.to_s), typ type name, "() -> #{typ}" } nil end |
#attr_writer_type(*args) ⇒ Object
426 427 428 429 430 431 432 433 |
# File 'lib/rdl/wrap.rb', line 426 def attr_writer_type(*args) args.each_slice(2) { |name, typ| attr_writer name var_type ("@" + name.to_s), typ type name.to_s + "=", "(#{typ}) -> #{typ}" } nil end |
#post(*args) ⇒ Object
Add a postcondition to a method. Same possible invocations as pre.
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 |
# File 'lib/rdl/wrap.rb', line 294 def post(*args, wrap: RDL::Config.instance.post_defaults[:wrap], version: nil, &blk) return if version && !(Gem::Requirement.new(version).satisfied_by? Gem.ruby_version) klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Postcondition", *args, &blk) if meth RDL::Globals.info.add(klass, meth, :post, contract) if wrap if RDL::Util.method_defined?(klass, meth) || meth == :initialize RDL::Wrap.wrap(klass, meth) else RDL::Globals.to_wrap << [klass, meth] end end else RDL::Globals.deferred << [klass, :post, contract, {wrap: wrap}] end nil end |
#pre(*args) ⇒ Object
klass
-
may be Class, Symbol, or String
method
-
may be Symbol or String
contract
-
must be a Contract
wrap
-
indicates whether the contract should be enforced (true) or just recorded (false)
- + version +
-
is a rubygems version requirement string (or array of such requirement strings)
if the current Ruby version does not satisfy the version, the type call will be ignored
Add a precondition to a method. Possible invocations: pre(klass, meth, contract) pre(klass, meth) { block } = pre(klass, meth, FlatContract.new { block }) pre(meth, contract) = pre(self, meth, contract) pre(meth) { block } = pre(self, meth, FlatContract.new { block }) pre(contract) = pre(self, next method, contract) pre { block } = pre(self, next method, FlatContract.new { block })
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 |
# File 'lib/rdl/wrap.rb', line 275 def pre(*args, wrap: RDL::Config.instance.pre_defaults[:wrap], version: nil, &blk) return if version && !(Gem::Requirement.new(version).satisfied_by? Gem.ruby_version) klass, meth, contract = RDL::Wrap.process_pre_post_args(self, "Precondition", *args, &blk) if meth RDL::Globals.info.add(klass, meth, :pre, contract) if wrap if RDL::Util.method_defined?(klass, meth) || meth == :initialize # there is always an initialize RDL::Wrap.wrap(klass, meth) else RDL::Globals.to_wrap << [klass, meth] end end else RDL::Globals.deferred << [klass, :pre, contract, {wrap: wrap}] end nil end |
#rdl_alias(*args) ⇒ Object
Aliases contracts for meth_old and meth_new. Currently, this must be called for any aliases or they will not be wrapped with contracts. Only creates aliases in the current class.
438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
# File 'lib/rdl/wrap.rb', line 438 def rdl_alias(klass=self, new_name, old_name) klass = klass.to_s klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main") RDL::Globals.aliases[klass] = {} unless RDL::Globals.aliases[klass] if RDL::Globals.aliases[klass][new_name] raise RuntimeError, "Tried to alias #{new_name}, already aliased to #{RDL::Globals.aliases[klass][new_name]}" end RDL::Globals.aliases[klass][new_name] = old_name if Module.const_defined?(klass) && RDL::Util.to_class(klass).method_defined?(new_name) RDL::Wrap.wrap(klass, new_name) else RDL::Globals.to_wrap << [klass, old_name] end nil end |
#readd_comp_types ⇒ Object
383 384 385 |
# File 'lib/rdl/wrap.rb', line 383 def readd_comp_types RDL::Globals.dep_types.each { |klass, meth, t| RDL::Globals.info.add(klass, meth, :type, t) unless meth.nil? } end |
#type(*args) ⇒ Object
- + klass +
-
may be Class, Symbol, or String
- + method +
-
may be Symbol or String
- + type +
-
may be Type or String
- + wrap +
-
indicates whether the type should be enforced (true) or just recorded (false)
- + typecheck +
-
indicates a method that should be statically type checked, as follows
if :call, indicates method should be typechecked when called
if :now, indicates method should be typechecked immediately
if other-symbol, indicates method should be typechecked when rdl_do_typecheck(other-symbol) is called
- + version +
-
is a rubygems version requirement string (or array of such requirement strings)
if the current Ruby version does not satisfy the version, the type call will be ignored
Set a method’s type. Possible invocations: type(klass, meth, type) type(meth, type) type(type)
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
# File 'lib/rdl/wrap.rb', line 327 def type(*args, wrap: RDL::Config.instance.type_defaults[:wrap], typecheck: RDL::Config.instance.type_defaults[:typecheck], version: nil, effect: nil) return if version && !(Gem::Requirement.new(version).satisfied_by? Gem.ruby_version) klass, meth, type = begin RDL::Wrap.process_type_args(self, *args) rescue Racc::ParseError => err # Remove enough backtrace to only include actual source line # Warning: Adjust the -5 below if the code (or this comment) changes bt = err.backtrace bt.shift until bt[0] =~ /^#{__FILE__}:#{__LINE__-5}/ bt.shift # remove RDL::Globals.contract_switch.off call bt.shift # remove type call itself err.set_backtrace bt raise err end effect[0] = :- if effect && effect[0] == :~ ## For now, treating pure-ish :~ as :-, since we realized it doesn't actually affect termination checking. typs = type.args + [type.ret] if type.block block_type = type.block.is_a?(RDL::Type::OptionalType) ? type.block.type : type.block typs = typs + block_type.args + [block_type.ret] end RDL::Globals.dep_types << [klass, meth, type] if typs.any? { |t| t.is_a?(RDL::Type::ComputedType) || (t.is_a?(RDL::Type::BoundArgType) && t.type.is_a?(RDL::Type::ComputedType)) } if meth # It turns out Ruby core/stdlib don't always follow this convention... # if (meth.to_s[-1] == "?") && (type.ret != RDL::Globals.types[:bool]) # warn "#{RDL::Util.pp_klass_method(klass, meth)}: methods that end in ? should have return type %bool" # end RDL::Globals.info.add(klass, meth, :type, type) RDL::Globals.info.add(klass, meth, :effect, effect) unless RDL::Globals.info.set(klass, meth, :typecheck, typecheck) raise RuntimeError, "Inconsistent typecheck flag on #{RDL::Util.pp_klass_method(klass, meth)}" end if wrap || typecheck if RDL::Util.method_defined?(klass, meth) || meth == :initialize RDL::Globals.info.set(klass, meth, :source_location, RDL::Util.to_class(klass).instance_method(meth).source_location) if typecheck == :now RDL::Typecheck.typecheck(klass, meth) elsif typecheck && (typecheck != :call) RDL::Globals.to_typecheck[typecheck] = Set.new unless RDL::Globals.to_typecheck[typecheck] RDL::Globals.to_typecheck[typecheck].add([klass, meth]) end RDL::Wrap.wrap(klass, meth) if wrap else RDL::Globals.to_wrap << [klass, meth] if wrap if (typecheck && typecheck != :call) RDL::Globals.to_typecheck[typecheck] = Set.new unless RDL::Globals.to_typecheck[typecheck] RDL::Globals.to_typecheck[typecheck].add([klass, meth]) end end end else RDL::Globals.deferred << [klass, :type, type, {wrap: wrap, typecheck: typecheck, effect: effect}] end nil end |
#type_params(*args) ⇒ Object
- + klass +
-
is the class whose type parameters to set; self if omitted
params
-
is an array of symbols or strings that are the
parameters of this (generic) type
variance
-
is an array of the corresponding variances, :+ for
covariant, :- for contravariant, and :~ for invariant. If omitted, all parameters are assumed to be invariant
all
-
should be a symbol naming an all? method that behaves like Array#all?, and that accepts
a block that takes arguments in the same order as the type parameters
blk
-
is for advanced use only. If present, [
all
] must be
nil. Whenever an instance of this class is instantiated!, the block will be passed an array typs corresponding to the type parameters of the class, and the block should return true if and only if self is a member of self.class<typs>.
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 |
# File 'lib/rdl/wrap.rb', line 469 def type_params(klass=self, params, all, variance: nil, &blk) raise RuntimeError, "Empty type parameters not allowed" if params.empty? klass = "Object" if (klass.is_a? Object) && (klass.to_s == "main") klass = klass.to_s if RDL::Globals.type_params[klass] raise RuntimeError, "#{klass} already has type parameters #{RDL::Globals.type_params[klass]}" end params = params.map { |v| raise RuntimeError, "Type parameter #{v.inspect} is not symbol or string" unless v.class == String || v.class == Symbol v.to_sym } raise RuntimeError, "Duplicate type parameters not allowed" unless params.uniq.size == params.size raise RuntimeError, "Expecting #{params.size} variance annotations, got #{variance.size}" if variance && params.size != variance.size raise RuntimeError, "Only :+, +-, and :~ are allowed variance annotations" unless (not variance) || variance.all? { |v| [:+, :-, :~].member? v } raise RuntimeError, "Can't pass both all and a block" if all && blk raise RuntimeError, "all must be a symbol" unless (not all) || (all.instance_of? Symbol) chk = all || blk raise RuntimeError, "At least one of {all, blk} required" unless chk variance = params.map { |p| :~ } unless variance # default to invariant RDL::Globals.type_params[klass] = [params, variance, chk] nil end |
#var_type(*args) ⇒ Object
- + klass +
-
is the class containing the variable; self if omitted; ignored for local and global variables
- + var +
-
is a symbol or string containing the name of the variable
- + typ +
-
is a string containing the type
390 391 392 393 394 395 396 397 398 |
# File 'lib/rdl/wrap.rb', line 390 def var_type(klass=self, var, typ) raise RuntimeError, "Variable cannot begin with capital" if var.to_s =~ /^[A-Z]/ return if var.to_s =~ /^[a-z]/ # local variables handled specially, inside type checker klass = RDL::Util::GLOBAL_NAME if var.to_s =~ /^\$/ unless RDL::Globals.info.set(klass, var, :type, RDL::Globals.parser.scan_str("#T #{typ}")) raise RuntimeError, "Type already declared for #{var}" end nil end |