typeguard 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +8 -0
- data/.rubocop.yml +25 -0
- data/.rubocop_todo.yml +89 -0
- data/Gemfile +16 -0
- data/LICENSE +21 -0
- data/README.md +42 -0
- data/Rakefile +12 -0
- data/lib/typeguard/configuration.rb +57 -0
- data/lib/typeguard/metrics.rb +73 -0
- data/lib/typeguard/resolver.rb +88 -0
- data/lib/typeguard/type_model/builder/rbs_builder.rb +99 -0
- data/lib/typeguard/type_model/builder/yard_builder.rb +237 -0
- data/lib/typeguard/type_model/builder.rb +20 -0
- data/lib/typeguard/type_model/definitions.rb +15 -0
- data/lib/typeguard/type_model/mapper/rbs_mapper.rb +84 -0
- data/lib/typeguard/type_model/mapper/yard_mapper.rb +71 -0
- data/lib/typeguard/types.rb +177 -0
- data/lib/typeguard/validator.rb +83 -0
- data/lib/typeguard/version.rb +5 -0
- data/lib/typeguard/wrapper.rb +101 -0
- data/lib/typeguard.rb +11 -0
- data/sig/typeguard/bin/example.rbs +77 -0
- metadata +118 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Typeguard
|
4
|
+
module Validation
|
5
|
+
class Wrapper
|
6
|
+
include Typeguard::TypeModel::Definitions
|
7
|
+
|
8
|
+
def initialize(definitions, config)
|
9
|
+
@definitions = definitions
|
10
|
+
@config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def wrap!
|
14
|
+
@definitions.each { |definition| wrap_definition(definition) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def wrap_definition(definition)
|
18
|
+
case definition
|
19
|
+
when ModuleDefinition, ClassDefinition
|
20
|
+
mod = Object.const_get(definition.name)
|
21
|
+
definition.children.each do |child|
|
22
|
+
if child.is_a?(MethodDefinition)
|
23
|
+
wrap_method(mod, child)
|
24
|
+
else
|
25
|
+
# Wrap nested modules
|
26
|
+
wrap_definition(child)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
when MethodDefinition
|
30
|
+
# Method defined in root, as a private instance method of Object
|
31
|
+
wrap_method(Object, definition)
|
32
|
+
else raise "Unexpected definition for '#{definition}'"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def wrap_method(mod, sig)
|
37
|
+
return if unsafe_method?(sig)
|
38
|
+
|
39
|
+
target = sig.scope == :class ? mod.singleton_class : mod
|
40
|
+
method_name = sig.name
|
41
|
+
original_method = target.instance_method(method_name)
|
42
|
+
check_arity(mod, sig, original_method)
|
43
|
+
actual_visibility = check_visibility(target, mod, sig, method_name)
|
44
|
+
Validator.exhaustive_path(mod, original_method, sig)
|
45
|
+
target.send(actual_visibility, method_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def unsafe_method?(sig)
|
49
|
+
# It is unsafe to redefine these methods because
|
50
|
+
# the return type is not always the same: assignment
|
51
|
+
# vs method call
|
52
|
+
sig.name == :initialize || sig.name.to_s =~ /=$/
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_arity(mod, sig, original_method)
|
56
|
+
expected_arity = sig.parameters.size
|
57
|
+
actual_arity = original_method.parameters.count
|
58
|
+
return if expected_arity == actual_arity
|
59
|
+
|
60
|
+
log = Metrics.report(mod, sig, :unexpected_arity, expected_arity, actual_arity)
|
61
|
+
raise Metrics.format_log(log) if @config.raise_on_unexpected_arity
|
62
|
+
end
|
63
|
+
|
64
|
+
def check_visibility(target, mod, sig, method_name)
|
65
|
+
expected_visibility = sig.visibility
|
66
|
+
actual_visibility =
|
67
|
+
if target.public_instance_methods(false).include?(method_name)
|
68
|
+
:public
|
69
|
+
elsif target.protected_instance_methods(false).include?(method_name)
|
70
|
+
:protected
|
71
|
+
elsif target.private_instance_methods(false).include?(method_name)
|
72
|
+
:private
|
73
|
+
else
|
74
|
+
:public
|
75
|
+
end
|
76
|
+
|
77
|
+
error = false
|
78
|
+
unless expected_visibility.nil? || expected_visibility == actual_visibility
|
79
|
+
if expected_visibility == :public && actual_visibility == :private
|
80
|
+
if sig.name == :initialize
|
81
|
+
# Initialize is private by default, ignore
|
82
|
+
elsif mod == Object
|
83
|
+
# Methods on Object (root) are private by default, ignore
|
84
|
+
else
|
85
|
+
error = true
|
86
|
+
end
|
87
|
+
else
|
88
|
+
error = true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
if error
|
93
|
+
log = Metrics.report(mod, sig, :unexpected_visibility, expected_visibility, actual_visibility)
|
94
|
+
raise Metrics.format_log(log) if @config.raise_on_unexpected_visibility
|
95
|
+
end
|
96
|
+
|
97
|
+
actual_visibility
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
data/lib/typeguard.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'typeguard/type_model/definitions'
|
4
|
+
require_relative 'typeguard/type_model/builder'
|
5
|
+
require_relative 'typeguard/version'
|
6
|
+
require_relative 'typeguard/configuration'
|
7
|
+
require_relative 'typeguard/resolver'
|
8
|
+
require_relative 'typeguard/metrics'
|
9
|
+
require_relative 'typeguard/types'
|
10
|
+
require_relative 'typeguard/validator'
|
11
|
+
require_relative 'typeguard/wrapper'
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Custom constant
|
2
|
+
class Foo
|
3
|
+
end
|
4
|
+
|
5
|
+
# Nested modules and classes
|
6
|
+
module One
|
7
|
+
class OneClass
|
8
|
+
end
|
9
|
+
|
10
|
+
module Two
|
11
|
+
class TwoClass
|
12
|
+
end
|
13
|
+
|
14
|
+
module Three
|
15
|
+
# Nested class
|
16
|
+
class ThreeClass
|
17
|
+
# _@param_ `one`
|
18
|
+
#
|
19
|
+
# _@param_ `two`
|
20
|
+
def nested_fn: (One::OneClass one, One::Two::TwoClass two) -> [One::OneClass, One::Two::TwoClass, One::Two::Three::ThreeClass]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Yardoc validation example
|
27
|
+
class Example
|
28
|
+
# _@return_ — a regular type
|
29
|
+
def regular0: () -> Object
|
30
|
+
|
31
|
+
# _@return_ — Ruby literal 'true'
|
32
|
+
def literal0: () -> bool
|
33
|
+
|
34
|
+
# sord duck - #push looks like a duck type, replacing with untyped
|
35
|
+
# _@return_ — duck-type: responds to :push
|
36
|
+
def duck0: () -> untyped
|
37
|
+
|
38
|
+
# _@param_ `a` — Integer to double
|
39
|
+
#
|
40
|
+
# _@return_ — double of a
|
41
|
+
def double1: (Integer a) -> Integer
|
42
|
+
|
43
|
+
# _@param_ `a`
|
44
|
+
#
|
45
|
+
# _@param_ `b`
|
46
|
+
#
|
47
|
+
# _@return_ — generic array of numbers
|
48
|
+
def generic_array2: (Integer a, Integer b) -> ::Array[(Integer | Float)]
|
49
|
+
|
50
|
+
# _@param_ `a`
|
51
|
+
#
|
52
|
+
# _@return_ — fixed array with order Symbol -> Integer
|
53
|
+
def fixed_array1: (Integer a) -> [Symbol, Integer]
|
54
|
+
|
55
|
+
# sord warn - Invalid hash, must have exactly two types: "Hash{Symbol => Integer, Float}".
|
56
|
+
# _@param_ `a`
|
57
|
+
#
|
58
|
+
# _@return_ — special hash syntax with 1 key and 2 value types
|
59
|
+
def special_hash1: (Integer a) -> untyped
|
60
|
+
|
61
|
+
# sord duck - #push looks like a duck type, replacing with untyped
|
62
|
+
# sord warn - Invalid hash, must have exactly two types: "Hash{Symbol, String => Boolean, Array<Array(Foo, #push, Set<Integer>)>}".
|
63
|
+
# _@param_ `a`
|
64
|
+
#
|
65
|
+
# _@return_ — complex types
|
66
|
+
def complex1: (Integer a) -> untyped
|
67
|
+
|
68
|
+
# sord warn - Invalid hash, must have exactly two types: "{Symbol => Integer, Symbol}".
|
69
|
+
# _@param_ `a`
|
70
|
+
#
|
71
|
+
# _@return_ — shorthands for collections
|
72
|
+
def shorthands1: (Integer a) -> [[Integer, Float], ::Array[Symbol], untyped]
|
73
|
+
|
74
|
+
def nested_modules0: () -> [One::OneClass, One::Two::TwoClass, One::Two::Three::ThreeClass]
|
75
|
+
|
76
|
+
end
|
77
|
+
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: typeguard
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tesorion
|
8
|
+
bindir: exe
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: dry-configurable
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rbs
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: yard
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: zeitwerk
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 2.6.0
|
61
|
+
type: :runtime
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 2.6.0
|
68
|
+
email:
|
69
|
+
- QmanageDevelopment@tesorion.nl
|
70
|
+
executables: []
|
71
|
+
extensions: []
|
72
|
+
extra_rdoc_files: []
|
73
|
+
files:
|
74
|
+
- ".editorconfig"
|
75
|
+
- ".rubocop.yml"
|
76
|
+
- ".rubocop_todo.yml"
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- lib/typeguard.rb
|
82
|
+
- lib/typeguard/configuration.rb
|
83
|
+
- lib/typeguard/metrics.rb
|
84
|
+
- lib/typeguard/resolver.rb
|
85
|
+
- lib/typeguard/type_model/builder.rb
|
86
|
+
- lib/typeguard/type_model/builder/rbs_builder.rb
|
87
|
+
- lib/typeguard/type_model/builder/yard_builder.rb
|
88
|
+
- lib/typeguard/type_model/definitions.rb
|
89
|
+
- lib/typeguard/type_model/mapper/rbs_mapper.rb
|
90
|
+
- lib/typeguard/type_model/mapper/yard_mapper.rb
|
91
|
+
- lib/typeguard/types.rb
|
92
|
+
- lib/typeguard/validator.rb
|
93
|
+
- lib/typeguard/version.rb
|
94
|
+
- lib/typeguard/wrapper.rb
|
95
|
+
- sig/typeguard/bin/example.rbs
|
96
|
+
homepage: https://github.com/tesorion/typeguard
|
97
|
+
licenses:
|
98
|
+
- MIT
|
99
|
+
metadata:
|
100
|
+
homepage_uri: https://github.com/tesorion/typeguard
|
101
|
+
rdoc_options: []
|
102
|
+
require_paths:
|
103
|
+
- lib
|
104
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: '3.1'
|
109
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
requirements: []
|
115
|
+
rubygems_version: 3.6.8
|
116
|
+
specification_version: 4
|
117
|
+
summary: Validate YARD and RBS type signatures
|
118
|
+
test_files: []
|