protostar 0.1.0.beta1
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/CHANGELOG.md +60 -0
- data/LICENSE.txt +21 -0
- data/README.md +647 -0
- data/THIRD_PARTY_NOTICES.md +61 -0
- data/exe/protoc-gen-protostar +7 -0
- data/exe/protostar +7 -0
- data/lib/protostar/builder/generator.rb +71 -0
- data/lib/protostar/builder/sink.rb +35 -0
- data/lib/protostar/builder.rb +965 -0
- data/lib/protostar/cli.rb +267 -0
- data/lib/protostar/codec/decode_generator.rb +199 -0
- data/lib/protostar/codec/decoder.rb +296 -0
- data/lib/protostar/codec/encode_generator.rb +224 -0
- data/lib/protostar/codec/encoder.rb +121 -0
- data/lib/protostar/codec/sizer.rb +91 -0
- data/lib/protostar/codec.rb +15 -0
- data/lib/protostar/compiler/ast.rb +193 -0
- data/lib/protostar/compiler/emitters/descriptor_set.rb +57 -0
- data/lib/protostar/compiler/emitters/google_ruby.rb +103 -0
- data/lib/protostar/compiler/emitters/grpc.rb +175 -0
- data/lib/protostar/compiler/emitters/protostar_rbs.rb +252 -0
- data/lib/protostar/compiler/emitters/rbs.rb +394 -0
- data/lib/protostar/compiler/emitters/ruby.rb +503 -0
- data/lib/protostar/compiler/emitters.rb +268 -0
- data/lib/protostar/compiler/include/LICENSE +32 -0
- data/lib/protostar/compiler/include/google/protobuf/any.proto +106 -0
- data/lib/protostar/compiler/include/google/protobuf/api.proto +229 -0
- data/lib/protostar/compiler/include/google/protobuf/compiler/plugin.proto +180 -0
- data/lib/protostar/compiler/include/google/protobuf/cpp_features.proto +87 -0
- data/lib/protostar/compiler/include/google/protobuf/descriptor.proto +1532 -0
- data/lib/protostar/compiler/include/google/protobuf/duration.proto +115 -0
- data/lib/protostar/compiler/include/google/protobuf/empty.proto +51 -0
- data/lib/protostar/compiler/include/google/protobuf/field_mask.proto +243 -0
- data/lib/protostar/compiler/include/google/protobuf/java_features.proto +132 -0
- data/lib/protostar/compiler/include/google/protobuf/source_context.proto +48 -0
- data/lib/protostar/compiler/include/google/protobuf/struct.proto +111 -0
- data/lib/protostar/compiler/include/google/protobuf/timestamp.proto +145 -0
- data/lib/protostar/compiler/include/google/protobuf/type.proto +217 -0
- data/lib/protostar/compiler/include/google/protobuf/wrappers.proto +157 -0
- data/lib/protostar/compiler/lexer.rb +659 -0
- data/lib/protostar/compiler/linker.rb +894 -0
- data/lib/protostar/compiler/option_interpreter.rb +363 -0
- data/lib/protostar/compiler/parser.rb +1696 -0
- data/lib/protostar/compiler/retention.rb +104 -0
- data/lib/protostar/compiler/run.rb +184 -0
- data/lib/protostar/compiler/source_info.rb +161 -0
- data/lib/protostar/compiler/symbol_table.rb +223 -0
- data/lib/protostar/compiler/text_format.rb +340 -0
- data/lib/protostar/compiler/validator.rb +944 -0
- data/lib/protostar/compiler.rb +304 -0
- data/lib/protostar/diagnostics/codes.rb +1307 -0
- data/lib/protostar/diagnostics/formats.rb +64 -0
- data/lib/protostar/diagnostics/renderer.rb +240 -0
- data/lib/protostar/diagnostics/source.rb +153 -0
- data/lib/protostar/diagnostics.rb +117 -0
- data/lib/protostar/dsl.rb +367 -0
- data/lib/protostar/enum.rb +70 -0
- data/lib/protostar/errors.rb +23 -0
- data/lib/protostar/google/protobuf/any_pb.rb +90 -0
- data/lib/protostar/google/protobuf/api_pb.rb +199 -0
- data/lib/protostar/google/protobuf/compiler/plugin_pb.rb +168 -0
- data/lib/protostar/google/protobuf/descriptor_pb.rb +1239 -0
- data/lib/protostar/google/protobuf/duration_pb.rb +99 -0
- data/lib/protostar/google/protobuf/empty_pb.rb +36 -0
- data/lib/protostar/google/protobuf/field_mask_pb.rb +228 -0
- data/lib/protostar/google/protobuf/source_context_pb.rb +33 -0
- data/lib/protostar/google/protobuf/struct_pb.rb +91 -0
- data/lib/protostar/google/protobuf/timestamp_pb.rb +129 -0
- data/lib/protostar/google/protobuf/type_pb.rb +202 -0
- data/lib/protostar/google/protobuf/wrappers_pb.rb +125 -0
- data/lib/protostar/google_protobuf_shim.rb +24 -0
- data/lib/protostar/json/encoder.rb +340 -0
- data/lib/protostar/json/errors.rb +34 -0
- data/lib/protostar/json/parser.rb +437 -0
- data/lib/protostar/json/path.rb +39 -0
- data/lib/protostar/json.rb +95 -0
- data/lib/protostar/lint/command.rb +197 -0
- data/lib/protostar/lint/config.rb +98 -0
- data/lib/protostar/lint/context.rb +126 -0
- data/lib/protostar/lint/directives.rb +56 -0
- data/lib/protostar/lint/fixer.rb +165 -0
- data/lib/protostar/lint/naming.rb +54 -0
- data/lib/protostar/lint/rule.rb +120 -0
- data/lib/protostar/lint/rules/comment_enum.rb +31 -0
- data/lib/protostar/lint/rules/comment_enum_value.rb +32 -0
- data/lib/protostar/lint/rules/comment_field.rb +33 -0
- data/lib/protostar/lint/rules/comment_message.rb +31 -0
- data/lib/protostar/lint/rules/comment_rpc.rb +31 -0
- data/lib/protostar/lint/rules/comment_service.rb +31 -0
- data/lib/protostar/lint/rules/edition_recommended.rb +31 -0
- data/lib/protostar/lint/rules/enum_first_value_zero.rb +34 -0
- data/lib/protostar/lint/rules/enum_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/enum_value_prefix.rb +38 -0
- data/lib/protostar/lint/rules/enum_value_upper_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/enum_zero_value_suffix.rb +41 -0
- data/lib/protostar/lint/rules/field_lower_snake_case.rb +34 -0
- data/lib/protostar/lint/rules/field_not_required.rb +39 -0
- data/lib/protostar/lint/rules/field_number_reuse_risk.rb +45 -0
- data/lib/protostar/lint/rules/field_numbers_hot_range.rb +44 -0
- data/lib/protostar/lint/rules/file_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/import_no_public.rb +29 -0
- data/lib/protostar/lint/rules/import_no_weak.rb +28 -0
- data/lib/protostar/lint/rules/import_used.rb +33 -0
- data/lib/protostar/lint/rules/message_pascal_case.rb +27 -0
- data/lib/protostar/lint/rules/no_deprecated_features.rb +50 -0
- data/lib/protostar/lint/rules/no_groups.rb +34 -0
- data/lib/protostar/lint/rules/oneof_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/package_defined.rb +28 -0
- data/lib/protostar/lint/rules/package_directory_match.rb +33 -0
- data/lib/protostar/lint/rules/package_lower_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/package_same_directory.rb +39 -0
- data/lib/protostar/lint/rules/package_version_suffix.rb +31 -0
- data/lib/protostar/lint/rules/reserved_names_with_numbers.rb +44 -0
- data/lib/protostar/lint/rules/rpc_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/rpc_request_response_unique.rb +40 -0
- data/lib/protostar/lint/rules/rpc_request_standard_name.rb +32 -0
- data/lib/protostar/lint/rules/rpc_response_standard_name.rb +31 -0
- data/lib/protostar/lint/rules/service_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/service_suffix.rb +28 -0
- data/lib/protostar/lint/rules/syntax_specified.rb +35 -0
- data/lib/protostar/lint/runner.rb +135 -0
- data/lib/protostar/lint.rb +122 -0
- data/lib/protostar/message/accessors.rb +86 -0
- data/lib/protostar/message/coercion.rb +171 -0
- data/lib/protostar/message/field_info.rb +145 -0
- data/lib/protostar/message/reflection.rb +149 -0
- data/lib/protostar/message.rb +572 -0
- data/lib/protostar/plugin.rb +94 -0
- data/lib/protostar/schema/default_value.rb +113 -0
- data/lib/protostar/schema/descriptor.rb +41 -0
- data/lib/protostar/schema/draft/copy.rb +88 -0
- data/lib/protostar/schema/draft/resolution.rb +177 -0
- data/lib/protostar/schema/draft.rb +779 -0
- data/lib/protostar/schema/enum.rb +106 -0
- data/lib/protostar/schema/extension_range.rb +37 -0
- data/lib/protostar/schema/features.rb +153 -0
- data/lib/protostar/schema/field.rb +201 -0
- data/lib/protostar/schema/file.rb +217 -0
- data/lib/protostar/schema/freezer.rb +61 -0
- data/lib/protostar/schema/message.rb +104 -0
- data/lib/protostar/schema/oneof.rb +38 -0
- data/lib/protostar/schema/options.rb +367 -0
- data/lib/protostar/schema/pool.rb +179 -0
- data/lib/protostar/schema/proto.rb +576 -0
- data/lib/protostar/schema/resolver.rb +118 -0
- data/lib/protostar/schema/service.rb +89 -0
- data/lib/protostar/schema/source_info.rb +53 -0
- data/lib/protostar/schema.rb +65 -0
- data/lib/protostar/short_names/any.rb +7 -0
- data/lib/protostar/short_names/duration.rb +7 -0
- data/lib/protostar/short_names/empty.rb +7 -0
- data/lib/protostar/short_names/field_mask.rb +7 -0
- data/lib/protostar/short_names/struct.rb +9 -0
- data/lib/protostar/short_names/timestamp.rb +7 -0
- data/lib/protostar/short_names/wrappers.rb +15 -0
- data/lib/protostar/text/extensions.rb +114 -0
- data/lib/protostar/text/parser.rb +439 -0
- data/lib/protostar/text/printer.rb +304 -0
- data/lib/protostar/text/tokenizer.rb +116 -0
- data/lib/protostar/text.rb +67 -0
- data/lib/protostar/types.rb +152 -0
- data/lib/protostar/unknown_field.rb +58 -0
- data/lib/protostar/version.rb +5 -0
- data/lib/protostar/view/generator.rb +298 -0
- data/lib/protostar/view/map.rb +194 -0
- data/lib/protostar/view/repeated.rb +143 -0
- data/lib/protostar/view/scan.rb +183 -0
- data/lib/protostar/view.rb +283 -0
- data/lib/protostar/well_known/any.rb +52 -0
- data/lib/protostar/well_known/duration.rb +53 -0
- data/lib/protostar/well_known/list_value.rb +63 -0
- data/lib/protostar/well_known/struct.rb +55 -0
- data/lib/protostar/well_known/timestamp.rb +64 -0
- data/lib/protostar/well_known/value.rb +60 -0
- data/lib/protostar/well_known.rb +38 -0
- data/lib/protostar/wire/io_source.rb +40 -0
- data/lib/protostar/wire/reader.rb +254 -0
- data/lib/protostar/wire/writer.rb +201 -0
- data/lib/protostar/wire.rb +96 -0
- data/lib/protostar.rb +95 -0
- data/shims/google-protobuf/lib/google/protobuf/any_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/api_pb.rb +22 -0
- data/shims/google-protobuf/lib/google/protobuf/compiler/plugin_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pb.rb +72 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pool.rb +164 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptors.rb +553 -0
- data/shims/google-protobuf/lib/google/protobuf/duration_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/empty_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/field_mask_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/convert.rb +180 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/extensions.rb +154 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/json.rb +104 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/well_known.rb +140 -0
- data/shims/google-protobuf/lib/google/protobuf/map.rb +216 -0
- data/shims/google-protobuf/lib/google/protobuf/message.rb +544 -0
- data/shims/google-protobuf/lib/google/protobuf/message_exts.rb +41 -0
- data/shims/google-protobuf/lib/google/protobuf/plugin_pb.rb +4 -0
- data/shims/google-protobuf/lib/google/protobuf/repeated_field.rb +366 -0
- data/shims/google-protobuf/lib/google/protobuf/source_context_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/struct_pb.rb +20 -0
- data/shims/google-protobuf/lib/google/protobuf/timestamp_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/type_pb.rb +27 -0
- data/shims/google-protobuf/lib/google/protobuf/well_known_types.rb +37 -0
- data/shims/google-protobuf/lib/google/protobuf/wrappers_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf.rb +107 -0
- data/shims/google-protobuf/lib/google/protobuf_ffi.rb +5 -0
- data/shims/google-protobuf/lib/google/protobuf_native.rb +5 -0
- data/sig/protostar/google/protobuf/any_pb.rbs +77 -0
- data/sig/protostar/google/protobuf/api_pb.rbs +242 -0
- data/sig/protostar/google/protobuf/compiler/plugin_pb.rbs +264 -0
- data/sig/protostar/google/protobuf/descriptor_pb.rbs +2220 -0
- data/sig/protostar/google/protobuf/duration_pb.rbs +82 -0
- data/sig/protostar/google/protobuf/empty_pb.rbs +18 -0
- data/sig/protostar/google/protobuf/field_mask_pb.rbs +170 -0
- data/sig/protostar/google/protobuf/source_context_pb.rbs +24 -0
- data/sig/protostar/google/protobuf/struct_pb.rbs +135 -0
- data/sig/protostar/google/protobuf/timestamp_pb.rbs +102 -0
- data/sig/protostar/google/protobuf/type_pb.rbs +304 -0
- data/sig/protostar/google/protobuf/wrappers_pb.rbs +169 -0
- data/sig/protostar/lint.rbs +216 -0
- data/sig/protostar.rbs +326 -0
- metadata +366 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
class Compiler
|
|
5
|
+
# protoc's StripSourceRetentionOptions (`compiler/retention.cc`): a copy
|
|
6
|
+
# of a file without the options declared `retention = RETENTION_SOURCE`,
|
|
7
|
+
# without options messages left empty by that, and without the source
|
|
8
|
+
# locations under anything removed. protoc does this when it writes a
|
|
9
|
+
# descriptor set or embeds descriptors in generated code; its pool keeps
|
|
10
|
+
# the options for validating later files.
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
module Retention
|
|
14
|
+
# descriptor.proto field numbers of each descriptor's `options`
|
|
15
|
+
OPTIONS_FIELD = {
|
|
16
|
+
Schema::File => 8, Schema::Message => 7, Schema::Field => 8, Schema::Oneof => 2, Schema::Enum => 3,
|
|
17
|
+
Schema::EnumValue => 3, Schema::Service => 3, Schema::Method => 4, Schema::ExtensionRange => 3
|
|
18
|
+
}.freeze
|
|
19
|
+
|
|
20
|
+
# @param file [Schema::File]
|
|
21
|
+
# @param pool [Schema::Pool] holds the stripped copies of the file's imports
|
|
22
|
+
# @param options_type [#call] options message full name to its {Schema::Message}
|
|
23
|
+
# @return [Schema::File] the stripped copy, not added to the pool
|
|
24
|
+
def self.strip(file, pool, options_type)
|
|
25
|
+
stripped = []
|
|
26
|
+
kept = {}.compare_by_identity
|
|
27
|
+
source_paths(file).each do |descriptor, path|
|
|
28
|
+
options = descriptor.options
|
|
29
|
+
next unless options.declared?
|
|
30
|
+
path += [OPTIONS_FIELD.fetch(descriptor.class)]
|
|
31
|
+
value = strip_value(options, options_type.call(options.type_name), path, stripped)
|
|
32
|
+
if !options.empty? && value.empty?
|
|
33
|
+
stripped << path
|
|
34
|
+
next
|
|
35
|
+
end
|
|
36
|
+
kept[descriptor] = value
|
|
37
|
+
end
|
|
38
|
+
info = file.source_info
|
|
39
|
+
if info && !stripped.empty?
|
|
40
|
+
info = Schema::SourceInfo.new(info.locations.reject { |loc| stripped.any? { |s| loc.path[0, s.size] == s } })
|
|
41
|
+
end
|
|
42
|
+
Schema::Draft.copy(file, pool: pool, source_info: info) { |descriptor| kept[descriptor] }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def self.strip_value(value, type, path, stripped)
|
|
46
|
+
fields = {}
|
|
47
|
+
value.fields.each do |name, v|
|
|
48
|
+
field = type&.fields_by_name&.[](name.to_s)
|
|
49
|
+
result = field ? strip_field(field, v, path, stripped) : v
|
|
50
|
+
fields[name] = result unless result.equal?(DROP)
|
|
51
|
+
end
|
|
52
|
+
extensions = {}
|
|
53
|
+
value.extensions.each do |field, v|
|
|
54
|
+
result = strip_field(field, v, path, stripped)
|
|
55
|
+
extensions[field] = result unless result.equal?(DROP)
|
|
56
|
+
end
|
|
57
|
+
Schema::MessageValue.new(fields, extensions, value.unknown_fields)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
DROP = Object.new.freeze
|
|
61
|
+
|
|
62
|
+
def self.strip_field(field, value, path, stripped)
|
|
63
|
+
here = path + [field.number]
|
|
64
|
+
if field.options.retention == :RETENTION_SOURCE
|
|
65
|
+
stripped << here
|
|
66
|
+
return DROP
|
|
67
|
+
end
|
|
68
|
+
return value unless field.type == :message || field.type == :group
|
|
69
|
+
if field.repeated?
|
|
70
|
+
value.each_with_index.map { |v, i| strip_value(v, field.message_type, here + [i], stripped) }
|
|
71
|
+
else
|
|
72
|
+
strip_value(value, field.message_type, here, stripped)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Each descriptor's path in its FileDescriptorProto, as source locations spell it
|
|
77
|
+
def self.source_paths(file)
|
|
78
|
+
paths = {file => []}.compare_by_identity
|
|
79
|
+
visit = lambda do |m, path|
|
|
80
|
+
paths[m] = path
|
|
81
|
+
m.fields.each_with_index { |f, i| paths[f] = path + [2, i] }
|
|
82
|
+
m.nested_messages.each_with_index { |n, i| visit.call(n, path + [3, i]) }
|
|
83
|
+
m.nested_enums.each_with_index { |e, i| enum_paths(paths, e, path + [4, i]) }
|
|
84
|
+
m.extension_range_descriptors.each_with_index { |r, i| paths[r] = path + [5, i] }
|
|
85
|
+
m.extensions.each_with_index { |f, i| paths[f] = path + [6, i] }
|
|
86
|
+
m.oneofs.each_with_index { |o, i| paths[o] = path + [8, i] }
|
|
87
|
+
end
|
|
88
|
+
file.messages.each_with_index { |m, i| visit.call(m, [4, i]) }
|
|
89
|
+
file.enums.each_with_index { |e, i| enum_paths(paths, e, [5, i]) }
|
|
90
|
+
file.services.each_with_index do |s, i|
|
|
91
|
+
paths[s] = [6, i]
|
|
92
|
+
s.rpcs.each_with_index { |m, j| paths[m] = [6, i, 2, j] }
|
|
93
|
+
end
|
|
94
|
+
file.extensions.each_with_index { |f, i| paths[f] = [7, i] }
|
|
95
|
+
paths
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.enum_paths(paths, enum, path)
|
|
99
|
+
paths[enum] = path
|
|
100
|
+
enum.values.each_with_index { |v, i| paths[v] = path + [2, i] }
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
class Compiler
|
|
5
|
+
# One {Compiler#compile} call: loads the named files and their imports
|
|
6
|
+
# depth first, as protoc's importer does (a file is parsed, then its
|
|
7
|
+
# imports are built, then the file itself), and collects diagnostics in
|
|
8
|
+
# protoc's order.
|
|
9
|
+
#
|
|
10
|
+
# @api private
|
|
11
|
+
class Run
|
|
12
|
+
# @return [Array<Diagnostic>]
|
|
13
|
+
attr_reader :diagnostics
|
|
14
|
+
|
|
15
|
+
# @param compiler [Compiler]
|
|
16
|
+
# @param names [Array<String>] the files asked for
|
|
17
|
+
# @param source_info [Boolean]
|
|
18
|
+
# @param sources [Hash{String => String}] file contents to use instead of reading include paths
|
|
19
|
+
# @param protoc_cli [Boolean] behave like protoc's command line (see {Linker#initialize});
|
|
20
|
+
# false behaves like a bare DescriptorPool, which loads no imports itself
|
|
21
|
+
# @param all_direct [Boolean] check every file loaded as if it had been named, imports
|
|
22
|
+
# included (unused imports, deprecated features), as `protostar lint` wants
|
|
23
|
+
def initialize(compiler, names, source_info, sources: {}, protoc_cli: true, all_direct: false)
|
|
24
|
+
@compiler = compiler
|
|
25
|
+
@pool = compiler.pool
|
|
26
|
+
@names = names
|
|
27
|
+
@source_info = source_info
|
|
28
|
+
@sources = sources
|
|
29
|
+
@protoc_cli = protoc_cli
|
|
30
|
+
@all_direct = all_direct
|
|
31
|
+
@diagnostics = []
|
|
32
|
+
@pending = []
|
|
33
|
+
@done = {}
|
|
34
|
+
@failed = false
|
|
35
|
+
@order = []
|
|
36
|
+
@listed = {}
|
|
37
|
+
@linkers = {}
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def failed?
|
|
41
|
+
@failed || @diagnostics.any?(&:error?)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [Linker, nil] the linker of a file this run built
|
|
45
|
+
def linker(name)
|
|
46
|
+
@linkers[name]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [Array<Schema::File>]
|
|
50
|
+
def call
|
|
51
|
+
direct = []
|
|
52
|
+
@names.each do |name|
|
|
53
|
+
file = load(name, direct: true)
|
|
54
|
+
if file.nil?
|
|
55
|
+
@failed = true
|
|
56
|
+
break
|
|
57
|
+
end
|
|
58
|
+
direct << @linkers[name] if @linkers[name]
|
|
59
|
+
end
|
|
60
|
+
Validator.cli_checks(direct, self) if @protoc_cli && !failed?
|
|
61
|
+
@order
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Records a diagnostic with a 0-based position (nil for none)
|
|
65
|
+
# @param spans [Array<Diagnostics::Span>] where the renderer underlines, when known
|
|
66
|
+
def report(file, position, message, severity = :error, spans: [])
|
|
67
|
+
line, column = position
|
|
68
|
+
line = nil if line && line < 0
|
|
69
|
+
path = @compiler.disk_path(file) if file && !@sources.key?(file) && @protoc_cli
|
|
70
|
+
@diagnostics << Diagnostic.new(file, line && line + 1, line && column + 1, message, severity, path: path,
|
|
71
|
+
spans: line ? spans : [])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# The problem's own span, as long as the source location that starts
|
|
77
|
+
# where it is reported, then the places it relates to
|
|
78
|
+
def spans(ast, problem, position)
|
|
79
|
+
return [] unless position
|
|
80
|
+
out = [span(ast, position, nil, true)]
|
|
81
|
+
problem.related.each do |node, kind, label|
|
|
82
|
+
at = node.position(kind)
|
|
83
|
+
out << span(ast, at, label, false) if at
|
|
84
|
+
end
|
|
85
|
+
out
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def span(ast, position, label, primary)
|
|
89
|
+
line, column = position
|
|
90
|
+
end_line, end_column = SourceInfo.extent(ast.locations, line, column)
|
|
91
|
+
Diagnostics::Span.new(line + 1, column + 1, end_line && end_line + 1, end_column && end_column + 1,
|
|
92
|
+
label: label, primary: primary)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# An extension number another file already uses is a warning the linker
|
|
96
|
+
# reports as a diagnostic, so the pool is asked not to print it too
|
|
97
|
+
def add_to_pool(name, file)
|
|
98
|
+
@pool.add(file, warn: false)
|
|
99
|
+
rescue Schema::Error => e
|
|
100
|
+
report(name, nil, e.message)
|
|
101
|
+
nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def list(file)
|
|
105
|
+
return if @listed[file.name]
|
|
106
|
+
file.dependency_files.each { |dep| list(dep) }
|
|
107
|
+
@listed[file.name] = true
|
|
108
|
+
@order << file
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def read(name)
|
|
112
|
+
return @sources[name] if @sources.key?(name)
|
|
113
|
+
return nil unless @protoc_cli
|
|
114
|
+
path = @compiler.resolve(name)
|
|
115
|
+
path && ::File.binread(path)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @return [Schema::File, nil]
|
|
119
|
+
def load(name, direct: false)
|
|
120
|
+
return @done[name] if @done.key?(name)
|
|
121
|
+
if direct && !@protoc_cli && @pool.include?(name)
|
|
122
|
+
ast = Parser.new(read(name).to_s).parse(name)
|
|
123
|
+
report(name, ast.position(:other), "A file with this name is already in the pool.")
|
|
124
|
+
return @done[name] = nil
|
|
125
|
+
end
|
|
126
|
+
if (existing = @pool.file(name))
|
|
127
|
+
list(existing)
|
|
128
|
+
return @done[name] = existing
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
if (from = @pending.index { |n, _| n == name })
|
|
132
|
+
chain = @pending[from..].map(&:first) + [name]
|
|
133
|
+
ast = @pending[from][1]
|
|
134
|
+
import = (from < @pending.size - 1) ? @pending[from + 1][0] : name
|
|
135
|
+
report(name, ast.import_positions[import], "File recursively imports itself: #{chain.join(" -> ")}")
|
|
136
|
+
return nil
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
source = read(name)
|
|
140
|
+
unless source
|
|
141
|
+
report(name, nil, "File not found.") if @protoc_cli
|
|
142
|
+
return @done[name] = nil
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
parser = Parser.new(source)
|
|
146
|
+
ast = parser.parse(name)
|
|
147
|
+
parser.errors.each { |line, column, message| report(name, [line, column], message) }
|
|
148
|
+
parser.warnings.each { |line, column, message| report(name, [line, column], message, :warning) }
|
|
149
|
+
return @done[name] = nil unless parser.errors.empty?
|
|
150
|
+
|
|
151
|
+
@pending << [name, ast]
|
|
152
|
+
missing = []
|
|
153
|
+
(ast.dependency + ast.option_dependency).each do |dep|
|
|
154
|
+
missing << dep if load(dep).nil?
|
|
155
|
+
end
|
|
156
|
+
@pending.pop
|
|
157
|
+
# protoc's BuildFile gives up quietly on a file that imports itself,
|
|
158
|
+
# once the recursion has been reported
|
|
159
|
+
return @done[name] = nil if ast.dependency.include?(name)
|
|
160
|
+
|
|
161
|
+
linker = Linker.new(ast, pool: @pool, missing: missing, direct: (direct || @all_direct) && @protoc_cli,
|
|
162
|
+
options_fallback: ->(type) { @compiler.builtin_options_type(type) }, source_info: @source_info,
|
|
163
|
+
protoc_cli: @protoc_cli)
|
|
164
|
+
linker.link
|
|
165
|
+
Validator.new(linker).validate
|
|
166
|
+
linker.problems.each do |problem|
|
|
167
|
+
position = if problem.location == :import
|
|
168
|
+
ast.import_positions[problem.import]
|
|
169
|
+
else
|
|
170
|
+
problem.node&.position(problem.location)
|
|
171
|
+
end
|
|
172
|
+
report(name, position, problem.message, problem.severity, spans: spans(ast, problem, position))
|
|
173
|
+
end
|
|
174
|
+
file = linker.errors? ? nil : linker.file
|
|
175
|
+
file = add_to_pool(name, file) if file
|
|
176
|
+
if file
|
|
177
|
+
@linkers[name] = linker
|
|
178
|
+
list(file)
|
|
179
|
+
end
|
|
180
|
+
@done[name] = file
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
class Compiler
|
|
5
|
+
# Builds `SourceCodeInfo` the way protoc does: the parser opens a
|
|
6
|
+
# {Recorder} for every element as it starts reading it, so locations come
|
|
7
|
+
# out in pre-order with protoc's paths and spans, and comments are
|
|
8
|
+
# attached at the end of each declaration. After option interpretation
|
|
9
|
+
# {.rewrite_option_paths} moves option locations from
|
|
10
|
+
# `uninterpreted_option` paths to the paths of the options they set.
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
module SourceInfo
|
|
14
|
+
# A `SourceCodeInfo.Location` under construction
|
|
15
|
+
class Location
|
|
16
|
+
attr_accessor :path, :span, :leading_comments, :trailing_comments, :leading_detached_comments
|
|
17
|
+
|
|
18
|
+
def initialize(path, span)
|
|
19
|
+
@path = path
|
|
20
|
+
@span = span
|
|
21
|
+
@leading_comments = nil
|
|
22
|
+
@trailing_comments = nil
|
|
23
|
+
@leading_detached_comments = []
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def dup_with_path(path)
|
|
27
|
+
copy = Location.new(path, @span.dup)
|
|
28
|
+
copy.leading_comments = @leading_comments
|
|
29
|
+
copy.trailing_comments = @trailing_comments
|
|
30
|
+
copy.leading_detached_comments = @leading_detached_comments.dup
|
|
31
|
+
copy
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @return [Schema::SourceInfo::Location] the finished location, comments as UTF-8
|
|
35
|
+
def to_schema
|
|
36
|
+
Schema::SourceInfo::Location.new(path: @path, span: @span, leading_comments: utf8(@leading_comments),
|
|
37
|
+
trailing_comments: utf8(@trailing_comments),
|
|
38
|
+
leading_detached_comments: @leading_detached_comments.map { |c| utf8(c) })
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def utf8(text)
|
|
44
|
+
text&.dup&.force_encoding(Encoding::UTF_8)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# protoc's Parser::LocationRecorder. It starts at the current token when
|
|
49
|
+
# created and, unless ended explicitly, ends at the previous token when
|
|
50
|
+
# {#finish}ed.
|
|
51
|
+
class Recorder
|
|
52
|
+
# @return [Location]
|
|
53
|
+
attr_reader :location
|
|
54
|
+
# @return [Array<Location>] where the location was added
|
|
55
|
+
attr_reader :target
|
|
56
|
+
|
|
57
|
+
# @param lexer [Lexer]
|
|
58
|
+
# @param path [Array<Integer>] the parent's path plus this element's components
|
|
59
|
+
# @param target [Array<Location>]
|
|
60
|
+
def initialize(lexer, path, target)
|
|
61
|
+
@lexer = lexer
|
|
62
|
+
@target = target
|
|
63
|
+
token = lexer.current
|
|
64
|
+
@location = Location.new(path, [token.line, token.column])
|
|
65
|
+
target << @location
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [Recorder] a child location, its path extended by `components`
|
|
69
|
+
def child(*components, target: @target)
|
|
70
|
+
Recorder.new(@lexer, @location.path + components, target)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def add_path(component)
|
|
74
|
+
@location.path << component
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def path_size
|
|
78
|
+
@location.path.size
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def start_at(token)
|
|
82
|
+
@location.span[0] = token.line
|
|
83
|
+
@location.span[1] = token.column
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def start_at_recorder(other)
|
|
87
|
+
@location.span[0] = other.location.span[0]
|
|
88
|
+
@location.span[1] = other.location.span[1]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def end_at(token)
|
|
92
|
+
@location.span << token.line if token.line != @location.span[0]
|
|
93
|
+
@location.span << token.end_column
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# The LocationRecorder destructor
|
|
97
|
+
def finish
|
|
98
|
+
end_at(@lexer.previous) if @location.span.size <= 2
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Records where this element starts, for diagnostics against `node`
|
|
102
|
+
def record_legacy(node, kind)
|
|
103
|
+
node.positions[kind] = [@location.span[0], @location.span[1]]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# @return [Array(Integer, Integer)]
|
|
107
|
+
def start
|
|
108
|
+
[@location.span[0], @location.span[1]]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def attach_comments(leading, trailing, detached)
|
|
112
|
+
@location.leading_comments = leading.dup unless leading.empty?
|
|
113
|
+
@location.trailing_comments = trailing.dup unless trailing.empty?
|
|
114
|
+
detached.each { |c| @location.leading_detached_comments << c }
|
|
115
|
+
detached.clear
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Where the shortest location starting at a position ends: the extent of
|
|
120
|
+
# the element whose name, number or type is reported there
|
|
121
|
+
# @param locations [Array<Location>]
|
|
122
|
+
# @return [Array(Integer, Integer), nil] 0-based line and column just past the end
|
|
123
|
+
def self.extent(locations, line, column)
|
|
124
|
+
best = nil
|
|
125
|
+
locations.each do |loc|
|
|
126
|
+
span = loc.span
|
|
127
|
+
next unless span[0] == line && span[1] == column && span.size >= 3
|
|
128
|
+
finish = (span.size == 4) ? [span[2], span[3]] : [span[0], span[2]]
|
|
129
|
+
best = finish if best.nil? || (finish <=> best) < 0
|
|
130
|
+
end
|
|
131
|
+
best
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# OptionInterpreter::UpdateSourceCodeInfo: the location of each
|
|
135
|
+
# interpreted option takes the path of the option it set, and the
|
|
136
|
+
# locations nested under it (name parts, values) are dropped
|
|
137
|
+
# @param locations [Array<Location>]
|
|
138
|
+
# @param interpreted [Hash{Array<Integer> => Array<Integer>}] uninterpreted_option path to option path
|
|
139
|
+
# @return [Array<Location>]
|
|
140
|
+
def self.rewrite_option_paths(locations, interpreted)
|
|
141
|
+
return locations if interpreted.empty?
|
|
142
|
+
out = []
|
|
143
|
+
match_src = nil
|
|
144
|
+
locations.each do |loc|
|
|
145
|
+
if match_src
|
|
146
|
+
next if loc.path.size >= match_src.size && loc.path[0, match_src.size] == match_src
|
|
147
|
+
match_src = nil
|
|
148
|
+
end
|
|
149
|
+
dest = interpreted[loc.path]
|
|
150
|
+
if dest.nil?
|
|
151
|
+
out << loc
|
|
152
|
+
next
|
|
153
|
+
end
|
|
154
|
+
match_src = loc.path
|
|
155
|
+
out << loc.dup_with_path(dest.dup)
|
|
156
|
+
end
|
|
157
|
+
out
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
class Compiler
|
|
5
|
+
# The names visible while one file is linked: its own symbols, drafted
|
|
6
|
+
# as the linker meets them, and everything in the pool. Lookups follow
|
|
7
|
+
# protoc's DescriptorBuilder (FindSymbol and LookupSymbolNoPlaceholder):
|
|
8
|
+
# the same first-component scoping rule as {Schema::Resolver}, with a
|
|
9
|
+
# symbol in another file visible only through a direct import or a chain
|
|
10
|
+
# of `import public`, and with the failure explanations protoc gives.
|
|
11
|
+
#
|
|
12
|
+
# @api private
|
|
13
|
+
class SymbolTable
|
|
14
|
+
# A named thing
|
|
15
|
+
class Symbol
|
|
16
|
+
# @return [Symbol] :message, :enum, :enum_value, :field, :oneof, :service, :method or :package
|
|
17
|
+
attr_reader :kind
|
|
18
|
+
# @return [String]
|
|
19
|
+
attr_reader :full_name
|
|
20
|
+
# @return [String] the file defining it
|
|
21
|
+
attr_reader :file_name
|
|
22
|
+
# @return [Schema::Descriptor, nil] the descriptor (drafted, for the file being
|
|
23
|
+
# linked); nil for a package
|
|
24
|
+
attr_reader :object
|
|
25
|
+
|
|
26
|
+
def initialize(kind:, full_name:, file_name:, object:, local: false)
|
|
27
|
+
@kind = kind
|
|
28
|
+
@full_name = full_name
|
|
29
|
+
@file_name = file_name
|
|
30
|
+
@object = object
|
|
31
|
+
@local = local
|
|
32
|
+
freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def type?
|
|
36
|
+
@kind == :message || @kind == :enum
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def aggregate?
|
|
40
|
+
@kind == :message || @kind == :package || @kind == :enum || @kind == :service
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [Boolean] defined by the file being linked
|
|
44
|
+
def local?
|
|
45
|
+
@local
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
SCHEMA_KINDS = {
|
|
50
|
+
Schema::Message => :message, Schema::Enum => :enum, Schema::EnumValue => :enum_value,
|
|
51
|
+
Schema::Field => :field, Schema::Oneof => :oneof, Schema::Service => :service, Schema::Method => :method
|
|
52
|
+
}.freeze
|
|
53
|
+
|
|
54
|
+
# @return [String, nil] set when the last failed lookup found the name in a file that is not imported
|
|
55
|
+
attr_reader :undeclared_file, :undeclared_name
|
|
56
|
+
# @return [String, nil] set when the last failed lookup resolved a compound name to a missing symbol
|
|
57
|
+
attr_reader :unresolved_name
|
|
58
|
+
# @return [Array<String>] direct imports no lookup has used yet
|
|
59
|
+
attr_reader :unused
|
|
60
|
+
|
|
61
|
+
# @param file_name [String] the file being linked
|
|
62
|
+
# @param package [String]
|
|
63
|
+
# @param pool [Schema::Pool]
|
|
64
|
+
# @param dependencies [Array<Schema::File, nil>] direct imports, nil where missing
|
|
65
|
+
# @param option_dependencies [Array<Schema::File, nil>] `import option` files
|
|
66
|
+
# @param unused [Array<Schema::File>] imports to report when never used
|
|
67
|
+
def initialize(file_name, package, pool, dependencies, option_dependencies, unused)
|
|
68
|
+
@file_name = file_name
|
|
69
|
+
@package = package
|
|
70
|
+
@pool = pool
|
|
71
|
+
@local = {}
|
|
72
|
+
@visible = {}
|
|
73
|
+
dependencies.each { |dep| record_public(dep, @visible) }
|
|
74
|
+
@option_visible = {}
|
|
75
|
+
option_dependencies.each { |dep| record_public(dep, @option_visible) }
|
|
76
|
+
@unused = unused.map(&:name)
|
|
77
|
+
@packages = {}
|
|
78
|
+
pool.files.each { |f| add_package_names(f.package, f.name) }
|
|
79
|
+
@local_packages = []
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @return [Hash{String => Symbol}] the file's own symbols
|
|
83
|
+
attr_reader :local
|
|
84
|
+
|
|
85
|
+
# Registers a symbol of the file being linked
|
|
86
|
+
# @param descriptor [Schema::Descriptor] as drafted
|
|
87
|
+
# @return [Symbol, nil] the existing symbol when the name is taken
|
|
88
|
+
def add(kind, full_name, descriptor)
|
|
89
|
+
existing = find_any(full_name)
|
|
90
|
+
return existing if existing
|
|
91
|
+
@local[full_name] = Symbol.new(kind: kind, full_name: full_name, file_name: @file_name, object: descriptor,
|
|
92
|
+
local: true)
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Registers the file's package and its parents, as AddPackage does
|
|
97
|
+
# @return [Symbol, nil] a non-package symbol already using one of the names
|
|
98
|
+
def add_package(package)
|
|
99
|
+
existing = find_any(package)
|
|
100
|
+
if existing.nil?
|
|
101
|
+
@packages[package] ||= @file_name
|
|
102
|
+
@local_packages << package
|
|
103
|
+
cut = package.rindex(".")
|
|
104
|
+
return add_package(package[0, cut]) if cut
|
|
105
|
+
elsif existing.kind != :package
|
|
106
|
+
return existing
|
|
107
|
+
end
|
|
108
|
+
nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Any symbol by full name, ignoring visibility
|
|
112
|
+
# @return [Symbol, nil]
|
|
113
|
+
def find_any(full_name)
|
|
114
|
+
if (sym = @local[full_name])
|
|
115
|
+
return sym
|
|
116
|
+
end
|
|
117
|
+
if (descriptor = @pool.find_symbol(full_name))
|
|
118
|
+
return Symbol.new(kind: SCHEMA_KINDS.fetch(descriptor.class), full_name: full_name,
|
|
119
|
+
file_name: descriptor.file.name, object: descriptor)
|
|
120
|
+
end
|
|
121
|
+
if (owner = @packages[full_name])
|
|
122
|
+
return Symbol.new(kind: :package, full_name: full_name, file_name: owner, object: nil)
|
|
123
|
+
end
|
|
124
|
+
nil
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# FindSymbolNotEnforcingDeps: any symbol, marking its file's import as used
|
|
128
|
+
def find_not_enforcing_deps(full_name)
|
|
129
|
+
sym = find_any(full_name)
|
|
130
|
+
if sym && sym.kind != :package && (@visible.key?(sym.file_name) || @option_visible.key?(sym.file_name))
|
|
131
|
+
@unused.delete(sym.file_name)
|
|
132
|
+
end
|
|
133
|
+
sym
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# FindSymbol: a symbol visible from the file being linked, or nil (and
|
|
137
|
+
# {#undeclared_file} set) when it lives in a file that is not imported
|
|
138
|
+
def find(full_name)
|
|
139
|
+
sym = find_not_enforcing_deps(full_name)
|
|
140
|
+
return nil if sym.nil?
|
|
141
|
+
return sym if sym.file_name == @file_name || @visible.key?(sym.file_name)
|
|
142
|
+
return sym if @option_visible.key?(sym.file_name) && sym.kind == :field
|
|
143
|
+
if sym.kind == :package
|
|
144
|
+
return sym if in_package?(@package, full_name)
|
|
145
|
+
return sym if @visible.each_value.any? { |f| in_package?(f.package, full_name) }
|
|
146
|
+
return sym if @option_visible.each_value.any? { |f| in_package?(f.package, full_name) }
|
|
147
|
+
end
|
|
148
|
+
@undeclared_file = sym.file_name
|
|
149
|
+
@undeclared_name = full_name
|
|
150
|
+
nil
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# LookupSymbolNoPlaceholder
|
|
154
|
+
# @param name [String] as written, maybe with a leading dot
|
|
155
|
+
# @param relative_to [String] full name of the element doing the lookup
|
|
156
|
+
# @param types_only [Boolean] skip simple names that are not types
|
|
157
|
+
# @return [Symbol, nil]
|
|
158
|
+
def lookup(name, relative_to, types_only: false)
|
|
159
|
+
@undeclared_file = nil
|
|
160
|
+
@undeclared_name = nil
|
|
161
|
+
@unresolved_name = nil
|
|
162
|
+
return find(name[1..]) if name.start_with?(".")
|
|
163
|
+
|
|
164
|
+
dot = name.index(".")
|
|
165
|
+
first = dot ? name[0, dot] : name
|
|
166
|
+
scope = relative_to.dup
|
|
167
|
+
loop do
|
|
168
|
+
cut = scope.rindex(".")
|
|
169
|
+
return find(name) if cut.nil?
|
|
170
|
+
scope = scope[0, cut]
|
|
171
|
+
candidate = "#{scope}.#{first}"
|
|
172
|
+
sym = find(candidate)
|
|
173
|
+
next if sym.nil?
|
|
174
|
+
if dot
|
|
175
|
+
next unless sym.aggregate?
|
|
176
|
+
full = "#{scope}.#{name}"
|
|
177
|
+
found = find(full)
|
|
178
|
+
@unresolved_name = full if found.nil?
|
|
179
|
+
return found
|
|
180
|
+
end
|
|
181
|
+
return sym unless types_only && !sym.type?
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# AddNotDefinedError's messages for the last failed {#lookup}
|
|
186
|
+
# @return [Array<String>]
|
|
187
|
+
def not_defined_messages(name)
|
|
188
|
+
if @undeclared_file.nil? && @unresolved_name.nil?
|
|
189
|
+
return ["\"#{name}\" is not defined."]
|
|
190
|
+
end
|
|
191
|
+
messages = []
|
|
192
|
+
if @undeclared_file
|
|
193
|
+
messages << "\"#{@undeclared_name}\" seems to be defined in \"#{@undeclared_file}\", which is not " \
|
|
194
|
+
"imported by \"#{@file_name}\". To use it here, please add the necessary import."
|
|
195
|
+
end
|
|
196
|
+
if @unresolved_name
|
|
197
|
+
messages << "\"#{name}\" is resolved to \"#{@unresolved_name}\", which is not defined. The innermost " \
|
|
198
|
+
"scope is searched first in name resolution. Consider using a leading '.'(i.e., \".#{name}\") to start " \
|
|
199
|
+
"from the outermost scope."
|
|
200
|
+
end
|
|
201
|
+
messages
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
private
|
|
205
|
+
|
|
206
|
+
def record_public(file, into)
|
|
207
|
+
return if file.nil? || into.key?(file.name)
|
|
208
|
+
into[file.name] = file
|
|
209
|
+
file.public_dependencies.each { |i| record_public(file.dependency_files[i], into) }
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def add_package_names(package, file_name)
|
|
213
|
+
return if package.empty?
|
|
214
|
+
parts = package.split(".")
|
|
215
|
+
parts.size.downto(1) { |n| @packages[parts.first(n).join(".")] ||= file_name }
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def in_package?(package, name)
|
|
219
|
+
package == name || (package.start_with?(name) && package.getbyte(name.bytesize) == 46) # "."
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|