Zack Williams | e940c7a | 2019-08-21 14:25:39 -0700 | [diff] [blame^] | 1 | package protoparse |
| 2 | |
| 3 | import ( |
| 4 | "bytes" |
| 5 | "errors" |
| 6 | "fmt" |
| 7 | "io" |
| 8 | "io/ioutil" |
| 9 | "math" |
| 10 | "os" |
| 11 | "path/filepath" |
| 12 | "sort" |
| 13 | "strings" |
| 14 | |
| 15 | "github.com/golang/protobuf/proto" |
| 16 | dpb "github.com/golang/protobuf/protoc-gen-go/descriptor" |
| 17 | |
| 18 | "github.com/jhump/protoreflect/desc" |
| 19 | "github.com/jhump/protoreflect/desc/internal" |
| 20 | ) |
| 21 | |
| 22 | //go:generate goyacc -o proto.y.go -p proto proto.y |
| 23 | |
| 24 | var errNoImportPathsForAbsoluteFilePath = errors.New("must specify at least one import path if any absolute file paths are given") |
| 25 | |
| 26 | func init() { |
| 27 | protoErrorVerbose = true |
| 28 | |
| 29 | // fix up the generated "token name" array so that error messages are nicer |
| 30 | setTokenName(_STRING_LIT, "string literal") |
| 31 | setTokenName(_INT_LIT, "int literal") |
| 32 | setTokenName(_FLOAT_LIT, "float literal") |
| 33 | setTokenName(_NAME, "identifier") |
| 34 | setTokenName(_FQNAME, "fully-qualified name") |
| 35 | setTokenName(_TYPENAME, "type name") |
| 36 | setTokenName(_ERROR, "error") |
| 37 | // for keywords, just show the keyword itself wrapped in quotes |
| 38 | for str, i := range keywords { |
| 39 | setTokenName(i, fmt.Sprintf(`"%s"`, str)) |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | func setTokenName(token int, text string) { |
| 44 | // NB: this is based on logic in generated parse code that translates the |
| 45 | // int returned from the lexer into an internal token number. |
| 46 | var intern int |
| 47 | if token < len(protoTok1) { |
| 48 | intern = protoTok1[token] |
| 49 | } else { |
| 50 | if token >= protoPrivate { |
| 51 | if token < protoPrivate+len(protoTok2) { |
| 52 | intern = protoTok2[token-protoPrivate] |
| 53 | } |
| 54 | } |
| 55 | if intern == 0 { |
| 56 | for i := 0; i+1 < len(protoTok3); i += 2 { |
| 57 | if protoTok3[i] == token { |
| 58 | intern = protoTok3[i+1] |
| 59 | break |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | if intern >= 1 && intern-1 < len(protoToknames) { |
| 66 | protoToknames[intern-1] = text |
| 67 | return |
| 68 | } |
| 69 | |
| 70 | panic(fmt.Sprintf("Unknown token value: %d", token)) |
| 71 | } |
| 72 | |
| 73 | // FileAccessor is an abstraction for opening proto source files. It takes the |
| 74 | // name of the file to open and returns either the input reader or an error. |
| 75 | type FileAccessor func(filename string) (io.ReadCloser, error) |
| 76 | |
| 77 | // FileContentsFromMap returns a FileAccessor that uses the given map of file |
| 78 | // contents. This allows proto source files to be constructed in memory and |
| 79 | // easily supplied to a parser. The map keys are the paths to the proto source |
| 80 | // files, and the values are the actual proto source contents. |
| 81 | func FileContentsFromMap(files map[string]string) FileAccessor { |
| 82 | return func(filename string) (io.ReadCloser, error) { |
| 83 | contents, ok := files[filename] |
| 84 | if !ok { |
| 85 | return nil, os.ErrNotExist |
| 86 | } |
| 87 | return ioutil.NopCloser(strings.NewReader(contents)), nil |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | // ResolveFilenames tries to resolve fileNames into paths that are relative to |
| 92 | // directories in the given importPaths. The returned slice has the results in |
| 93 | // the same order as they are supplied in fileNames. |
| 94 | // |
| 95 | // The resulting names should be suitable for passing to Parser.ParseFiles. |
| 96 | // |
| 97 | // If importPaths is empty and any path is absolute, this returns error. |
| 98 | // If importPaths is empty and all paths are relative, this returns the original fileNames. |
| 99 | func ResolveFilenames(importPaths []string, fileNames ...string) ([]string, error) { |
| 100 | if len(importPaths) == 0 { |
| 101 | if containsAbsFilePath(fileNames) { |
| 102 | // We have to do this as otherwise parseProtoFiles can result in duplicate symbols. |
| 103 | // For example, assume we import "foo/bar/bar.proto" in a file "/home/alice/dev/foo/bar/baz.proto" |
| 104 | // as we call ParseFiles("/home/alice/dev/foo/bar/bar.proto","/home/alice/dev/foo/bar/baz.proto") |
| 105 | // with "/home/alice/dev" as our current directory. Due to the recursive nature of parseProtoFiles, |
| 106 | // it will discover the import "foo/bar/bar.proto" in the input file, and call parse on this, |
| 107 | // adding "foo/bar/bar.proto" to the parsed results, as well as "/home/alice/dev/foo/bar/bar.proto" |
| 108 | // from the input file list. This will result in a |
| 109 | // 'duplicate symbol SYMBOL: already defined as field in "/home/alice/dev/foo/bar/bar.proto' |
| 110 | // error being returned from ParseFiles. |
| 111 | return nil, errNoImportPathsForAbsoluteFilePath |
| 112 | } |
| 113 | return fileNames, nil |
| 114 | } |
| 115 | absImportPaths, err := absoluteFilePaths(importPaths) |
| 116 | if err != nil { |
| 117 | return nil, err |
| 118 | } |
| 119 | absFileNames, err := absoluteFilePaths(fileNames) |
| 120 | if err != nil { |
| 121 | return nil, err |
| 122 | } |
| 123 | resolvedFileNames := make([]string, 0, len(fileNames)) |
| 124 | for _, absFileName := range absFileNames { |
| 125 | resolvedFileName, err := resolveAbsFilename(absImportPaths, absFileName) |
| 126 | if err != nil { |
| 127 | return nil, err |
| 128 | } |
| 129 | resolvedFileNames = append(resolvedFileNames, resolvedFileName) |
| 130 | } |
| 131 | return resolvedFileNames, nil |
| 132 | } |
| 133 | |
| 134 | // Parser parses proto source into descriptors. |
| 135 | type Parser struct { |
| 136 | // The paths used to search for dependencies that are referenced in import |
| 137 | // statements in proto source files. If no import paths are provided then |
| 138 | // "." (current directory) is assumed to be the only import path. |
| 139 | // |
| 140 | // This setting is only used during ParseFiles operations. Since calls to |
| 141 | // ParseFilesButDoNotLink do not link, there is no need to load and parse |
| 142 | // dependencies. |
| 143 | ImportPaths []string |
| 144 | |
| 145 | // If true, the supplied file names/paths need not necessarily match how the |
| 146 | // files are referenced in import statements. The parser will attempt to |
| 147 | // match import statements to supplied paths, "guessing" the import paths |
| 148 | // for the files. Note that this inference is not perfect and link errors |
| 149 | // could result. It works best when all proto files are organized such that |
| 150 | // a single import path can be inferred (e.g. all files under a single tree |
| 151 | // with import statements all being relative to the root of this tree). |
| 152 | InferImportPaths bool |
| 153 | |
| 154 | // Used to create a reader for a given filename, when loading proto source |
| 155 | // file contents. If unset, os.Open is used. If ImportPaths is also empty |
| 156 | // then relative paths are will be relative to the process's current working |
| 157 | // directory. |
| 158 | Accessor FileAccessor |
| 159 | |
| 160 | // If true, the resulting file descriptors will retain source code info, |
| 161 | // that maps elements to their location in the source files as well as |
| 162 | // includes comments found during parsing (and attributed to elements of |
| 163 | // the source file). |
| 164 | IncludeSourceCodeInfo bool |
| 165 | |
| 166 | // If true, the results from ParseFilesButDoNotLink will be passed through |
| 167 | // some additional validations. But only constraints that do not require |
| 168 | // linking can be checked. These include proto2 vs. proto3 language features, |
| 169 | // looking for incorrect usage of reserved names or tags, and ensuring that |
| 170 | // fields have unique tags and that enum values have unique numbers (unless |
| 171 | // the enum allows aliases). |
| 172 | ValidateUnlinkedFiles bool |
| 173 | |
| 174 | // If true, the results from ParseFilesButDoNotLink will have options |
| 175 | // interpreted. Any uninterpretable options (including any custom options or |
| 176 | // options that refer to message and enum types, which can only be |
| 177 | // interpreted after linking) will be left in uninterpreted_options. Also, |
| 178 | // the "default" pseudo-option for fields can only be interpreted for scalar |
| 179 | // fields, excluding enums. (Interpreting default values for enum fields |
| 180 | // requires resolving enum names, which requires linking.) |
| 181 | InterpretOptionsInUnlinkedFiles bool |
| 182 | } |
| 183 | |
| 184 | // ParseFiles parses the named files into descriptors. The returned slice has |
| 185 | // the same number of entries as the give filenames, in the same order. So the |
| 186 | // first returned descriptor corresponds to the first given name, and so on. |
| 187 | // |
| 188 | // All dependencies for all specified files (including transitive dependencies) |
| 189 | // must be accessible via the parser's Accessor or a link error will occur. The |
| 190 | // exception to this rule is that files can import standard Google-provided |
| 191 | // files -- e.g. google/protobuf/*.proto -- without needing to supply sources |
| 192 | // for these files. Like protoc, this parser has a built-in version of these |
| 193 | // files it can use if they aren't explicitly supplied. |
| 194 | func (p Parser) ParseFiles(filenames ...string) ([]*desc.FileDescriptor, error) { |
| 195 | accessor := p.Accessor |
| 196 | if accessor == nil { |
| 197 | accessor = func(name string) (io.ReadCloser, error) { |
| 198 | return os.Open(name) |
| 199 | } |
| 200 | } |
| 201 | paths := p.ImportPaths |
| 202 | if len(paths) > 0 { |
| 203 | acc := accessor |
| 204 | accessor = func(name string) (io.ReadCloser, error) { |
| 205 | var ret error |
| 206 | for _, path := range paths { |
| 207 | f, err := acc(filepath.Join(path, name)) |
| 208 | if err != nil { |
| 209 | if ret == nil { |
| 210 | ret = err |
| 211 | } |
| 212 | continue |
| 213 | } |
| 214 | return f, nil |
| 215 | } |
| 216 | return nil, ret |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | protos := map[string]*parseResult{} |
| 221 | err := parseProtoFiles(accessor, filenames, true, true, protos) |
| 222 | if err != nil { |
| 223 | return nil, err |
| 224 | } |
| 225 | if p.InferImportPaths { |
| 226 | protos = fixupFilenames(protos) |
| 227 | } |
| 228 | linkedProtos, err := newLinker(protos).linkFiles() |
| 229 | if err != nil { |
| 230 | return nil, err |
| 231 | } |
| 232 | if p.IncludeSourceCodeInfo { |
| 233 | for name, fd := range linkedProtos { |
| 234 | pr := protos[name] |
| 235 | fd.AsFileDescriptorProto().SourceCodeInfo = pr.generateSourceCodeInfo() |
| 236 | internal.RecomputeSourceInfo(fd) |
| 237 | } |
| 238 | } |
| 239 | fds := make([]*desc.FileDescriptor, len(filenames)) |
| 240 | for i, name := range filenames { |
| 241 | fd := linkedProtos[name] |
| 242 | fds[i] = fd |
| 243 | } |
| 244 | return fds, nil |
| 245 | } |
| 246 | |
| 247 | // ParseFilesButDoNotLink parses the named files into descriptor protos. The |
| 248 | // results are just protos, not fully-linked descriptors. It is possible that |
| 249 | // descriptors are invalid and still be returned in parsed form without error |
| 250 | // due to the fact that the linking step is skipped (and thus many validation |
| 251 | // steps omitted). |
| 252 | // |
| 253 | // There are a few side effects to not linking the descriptors: |
| 254 | // 1. No options will be interpreted. Options can refer to extensions or have |
| 255 | // message and enum types. Without linking, these extension and type |
| 256 | // references are not resolved, so the options may not be interpretable. |
| 257 | // So all options will appear in UninterpretedOption fields of the various |
| 258 | // descriptor options messages. |
| 259 | // 2. Type references will not be resolved. This means that the actual type |
| 260 | // names in the descriptors may be unqualified and even relative to the |
| 261 | // scope in which the type reference appears. This goes for fields that |
| 262 | // have message and enum types. It also applies to methods and their |
| 263 | // references to request and response message types. |
| 264 | // 3. Enum fields are not known. Until a field's type reference is resolved |
| 265 | // (during linking), it is not known whether the type refers to a message |
| 266 | // or an enum. So all fields with such type references have their Type set |
| 267 | // to TYPE_MESSAGE. |
| 268 | // |
| 269 | // This method will still validate the syntax of parsed files. If the parser's |
| 270 | // ValidateUnlinkedFiles field is true, additional checks, beyond syntax will |
| 271 | // also be performed. |
| 272 | func (p Parser) ParseFilesButDoNotLink(filenames ...string) ([]*dpb.FileDescriptorProto, error) { |
| 273 | accessor := p.Accessor |
| 274 | if accessor == nil { |
| 275 | accessor = func(name string) (io.ReadCloser, error) { |
| 276 | return os.Open(name) |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | protos := map[string]*parseResult{} |
| 281 | err := parseProtoFiles(accessor, filenames, false, p.ValidateUnlinkedFiles, protos) |
| 282 | if err != nil { |
| 283 | return nil, err |
| 284 | } |
| 285 | if p.InferImportPaths { |
| 286 | protos = fixupFilenames(protos) |
| 287 | } |
| 288 | fds := make([]*dpb.FileDescriptorProto, len(filenames)) |
| 289 | for i, name := range filenames { |
| 290 | pr := protos[name] |
| 291 | fd := pr.fd |
| 292 | if p.InterpretOptionsInUnlinkedFiles { |
| 293 | pr.lenient = true |
| 294 | interpretFileOptions(pr, poorFileDescriptorish{FileDescriptorProto: fd}) |
| 295 | } |
| 296 | if p.IncludeSourceCodeInfo { |
| 297 | fd.SourceCodeInfo = pr.generateSourceCodeInfo() |
| 298 | } |
| 299 | fds[i] = fd |
| 300 | } |
| 301 | return fds, nil |
| 302 | } |
| 303 | |
| 304 | func containsAbsFilePath(filePaths []string) bool { |
| 305 | for _, filePath := range filePaths { |
| 306 | if filepath.IsAbs(filePath) { |
| 307 | return true |
| 308 | } |
| 309 | } |
| 310 | return false |
| 311 | } |
| 312 | |
| 313 | func absoluteFilePaths(filePaths []string) ([]string, error) { |
| 314 | absFilePaths := make([]string, 0, len(filePaths)) |
| 315 | for _, filePath := range filePaths { |
| 316 | absFilePath, err := filepath.Abs(filePath) |
| 317 | if err != nil { |
| 318 | return nil, err |
| 319 | } |
| 320 | absFilePaths = append(absFilePaths, absFilePath) |
| 321 | } |
| 322 | return absFilePaths, nil |
| 323 | } |
| 324 | |
| 325 | func resolveAbsFilename(absImportPaths []string, absFileName string) (string, error) { |
| 326 | for _, absImportPath := range absImportPaths { |
| 327 | if isDescendant(absImportPath, absFileName) { |
| 328 | resolvedPath, err := filepath.Rel(absImportPath, absFileName) |
| 329 | if err != nil { |
| 330 | return "", err |
| 331 | } |
| 332 | return resolvedPath, nil |
| 333 | } |
| 334 | } |
| 335 | return "", fmt.Errorf("%s does not reside in any import path", absFileName) |
| 336 | } |
| 337 | |
| 338 | // isDescendant returns true if file is a descendant of dir. |
| 339 | func isDescendant(dir, file string) bool { |
| 340 | dir = filepath.Clean(dir) |
| 341 | cur := file |
| 342 | for { |
| 343 | d := filepath.Dir(cur) |
| 344 | if d == dir { |
| 345 | return true |
| 346 | } |
| 347 | if d == "." || d == cur { |
| 348 | // we've run out of path elements |
| 349 | return false |
| 350 | } |
| 351 | cur = d |
| 352 | } |
| 353 | } |
| 354 | |
| 355 | func fixupFilenames(protos map[string]*parseResult) map[string]*parseResult { |
| 356 | // In the event that the given filenames (keys in the supplied map) do not |
| 357 | // match the actual paths used in 'import' statements in the files, we try |
| 358 | // to revise names in the protos so that they will match and be linkable. |
| 359 | revisedProtos := map[string]*parseResult{} |
| 360 | |
| 361 | protoPaths := map[string]struct{}{} |
| 362 | // TODO: this is O(n^2) but could likely be O(n) with a clever data structure (prefix tree that is indexed backwards?) |
| 363 | importCandidates := map[string]map[string]struct{}{} |
| 364 | candidatesAvailable := map[string]struct{}{} |
| 365 | for name := range protos { |
| 366 | candidatesAvailable[name] = struct{}{} |
| 367 | for _, f := range protos { |
| 368 | for _, imp := range f.fd.Dependency { |
| 369 | if strings.HasSuffix(name, imp) { |
| 370 | candidates := importCandidates[imp] |
| 371 | if candidates == nil { |
| 372 | candidates = map[string]struct{}{} |
| 373 | importCandidates[imp] = candidates |
| 374 | } |
| 375 | candidates[name] = struct{}{} |
| 376 | } |
| 377 | } |
| 378 | } |
| 379 | } |
| 380 | for imp, candidates := range importCandidates { |
| 381 | // if we found multiple possible candidates, use the one that is an exact match |
| 382 | // if it exists, and otherwise, guess that it's the shortest path (fewest elements) |
| 383 | var best string |
| 384 | for c := range candidates { |
| 385 | if _, ok := candidatesAvailable[c]; !ok { |
| 386 | // already used this candidate and re-written its filename accordingly |
| 387 | continue |
| 388 | } |
| 389 | if c == imp { |
| 390 | // exact match! |
| 391 | best = c |
| 392 | break |
| 393 | } |
| 394 | if best == "" { |
| 395 | best = c |
| 396 | } else { |
| 397 | // HACK: we can't actually tell which files is supposed to match |
| 398 | // this import, so arbitrarily pick the "shorter" one (fewest |
| 399 | // path elements) or, on a tie, the lexically earlier one |
| 400 | minLen := strings.Count(best, string(filepath.Separator)) |
| 401 | cLen := strings.Count(c, string(filepath.Separator)) |
| 402 | if cLen < minLen || (cLen == minLen && c < best) { |
| 403 | best = c |
| 404 | } |
| 405 | } |
| 406 | } |
| 407 | if best != "" { |
| 408 | prefix := best[:len(best)-len(imp)] |
| 409 | if len(prefix) > 0 { |
| 410 | protoPaths[prefix] = struct{}{} |
| 411 | } |
| 412 | f := protos[best] |
| 413 | f.fd.Name = proto.String(imp) |
| 414 | revisedProtos[imp] = f |
| 415 | delete(candidatesAvailable, best) |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | if len(candidatesAvailable) == 0 { |
| 420 | return revisedProtos |
| 421 | } |
| 422 | |
| 423 | if len(protoPaths) == 0 { |
| 424 | for c := range candidatesAvailable { |
| 425 | revisedProtos[c] = protos[c] |
| 426 | } |
| 427 | return revisedProtos |
| 428 | } |
| 429 | |
| 430 | // Any remaining candidates are entry-points (not imported by others), so |
| 431 | // the best bet to "fixing" their file name is to see if they're in one of |
| 432 | // the proto paths we found, and if so strip that prefix. |
| 433 | protoPathStrs := make([]string, len(protoPaths)) |
| 434 | i := 0 |
| 435 | for p := range protoPaths { |
| 436 | protoPathStrs[i] = p |
| 437 | i++ |
| 438 | } |
| 439 | sort.Strings(protoPathStrs) |
| 440 | // we look at paths in reverse order, so we'll use a longer proto path if |
| 441 | // there is more than one match |
| 442 | for c := range candidatesAvailable { |
| 443 | var imp string |
| 444 | for i := len(protoPathStrs) - 1; i >= 0; i-- { |
| 445 | p := protoPathStrs[i] |
| 446 | if strings.HasPrefix(c, p) { |
| 447 | imp = c[len(p):] |
| 448 | break |
| 449 | } |
| 450 | } |
| 451 | if imp != "" { |
| 452 | f := protos[c] |
| 453 | f.fd.Name = proto.String(imp) |
| 454 | revisedProtos[imp] = f |
| 455 | } else { |
| 456 | revisedProtos[c] = protos[c] |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | return revisedProtos |
| 461 | } |
| 462 | |
| 463 | func parseProtoFiles(acc FileAccessor, filenames []string, recursive, validate bool, parsed map[string]*parseResult) error { |
| 464 | for _, name := range filenames { |
| 465 | if _, ok := parsed[name]; ok { |
| 466 | continue |
| 467 | } |
| 468 | in, err := acc(name) |
| 469 | if err != nil { |
| 470 | if d, ok := standardImports[name]; ok { |
| 471 | parsed[name] = &parseResult{fd: d} |
| 472 | continue |
| 473 | } |
| 474 | return err |
| 475 | } |
| 476 | func() { |
| 477 | defer in.Close() |
| 478 | parsed[name], err = parseProto(name, in, validate) |
| 479 | }() |
| 480 | if err != nil { |
| 481 | return err |
| 482 | } |
| 483 | if recursive { |
| 484 | err = parseProtoFiles(acc, parsed[name].fd.Dependency, true, validate, parsed) |
| 485 | if err != nil { |
| 486 | return fmt.Errorf("failed to load imports for %q: %s", name, err) |
| 487 | } |
| 488 | } |
| 489 | } |
| 490 | return nil |
| 491 | } |
| 492 | |
| 493 | type parseResult struct { |
| 494 | // the parsed file descriptor |
| 495 | fd *dpb.FileDescriptorProto |
| 496 | |
| 497 | // if set to true, enables lenient interpretation of options, where |
| 498 | // unrecognized options will be left uninterpreted instead of resulting in a |
| 499 | // link error |
| 500 | lenient bool |
| 501 | |
| 502 | // a map of elements in the descriptor to nodes in the AST |
| 503 | // (for extracting position information when validating the descriptor) |
| 504 | nodes map[proto.Message]node |
| 505 | |
| 506 | // a map of uninterpreted option AST nodes to their relative path |
| 507 | // in the resulting options message |
| 508 | interpretedOptions map[*optionNode][]int32 |
| 509 | } |
| 510 | |
| 511 | func (r *parseResult) getFileNode(f *dpb.FileDescriptorProto) fileDecl { |
| 512 | if r.nodes == nil { |
| 513 | return noSourceNode{pos: unknownPos(f.GetName())} |
| 514 | } |
| 515 | return r.nodes[f].(fileDecl) |
| 516 | } |
| 517 | |
| 518 | func (r *parseResult) getOptionNode(o *dpb.UninterpretedOption) optionDecl { |
| 519 | if r.nodes == nil { |
| 520 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 521 | } |
| 522 | return r.nodes[o].(optionDecl) |
| 523 | } |
| 524 | |
| 525 | func (r *parseResult) getOptionNamePartNode(o *dpb.UninterpretedOption_NamePart) node { |
| 526 | if r.nodes == nil { |
| 527 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 528 | } |
| 529 | return r.nodes[o] |
| 530 | } |
| 531 | |
| 532 | func (r *parseResult) getMessageNode(m *dpb.DescriptorProto) msgDecl { |
| 533 | if r.nodes == nil { |
| 534 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 535 | } |
| 536 | return r.nodes[m].(msgDecl) |
| 537 | } |
| 538 | |
| 539 | func (r *parseResult) getFieldNode(f *dpb.FieldDescriptorProto) fieldDecl { |
| 540 | if r.nodes == nil { |
| 541 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 542 | } |
| 543 | return r.nodes[f].(fieldDecl) |
| 544 | } |
| 545 | |
| 546 | func (r *parseResult) getOneOfNode(o *dpb.OneofDescriptorProto) node { |
| 547 | if r.nodes == nil { |
| 548 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 549 | } |
| 550 | return r.nodes[o] |
| 551 | } |
| 552 | |
| 553 | func (r *parseResult) getExtensionRangeNode(e *dpb.DescriptorProto_ExtensionRange) rangeDecl { |
| 554 | if r.nodes == nil { |
| 555 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 556 | } |
| 557 | return r.nodes[e].(rangeDecl) |
| 558 | } |
| 559 | |
| 560 | func (r *parseResult) getMessageReservedRangeNode(rr *dpb.DescriptorProto_ReservedRange) rangeDecl { |
| 561 | if r.nodes == nil { |
| 562 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 563 | } |
| 564 | return r.nodes[rr].(rangeDecl) |
| 565 | } |
| 566 | |
| 567 | func (r *parseResult) getEnumNode(e *dpb.EnumDescriptorProto) node { |
| 568 | if r.nodes == nil { |
| 569 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 570 | } |
| 571 | return r.nodes[e] |
| 572 | } |
| 573 | |
| 574 | func (r *parseResult) getEnumValueNode(e *dpb.EnumValueDescriptorProto) enumValueDecl { |
| 575 | if r.nodes == nil { |
| 576 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 577 | } |
| 578 | return r.nodes[e].(enumValueDecl) |
| 579 | } |
| 580 | |
| 581 | func (r *parseResult) getEnumReservedRangeNode(rr *dpb.EnumDescriptorProto_EnumReservedRange) rangeDecl { |
| 582 | if r.nodes == nil { |
| 583 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 584 | } |
| 585 | return r.nodes[rr].(rangeDecl) |
| 586 | } |
| 587 | |
| 588 | func (r *parseResult) getServiceNode(s *dpb.ServiceDescriptorProto) node { |
| 589 | if r.nodes == nil { |
| 590 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 591 | } |
| 592 | return r.nodes[s] |
| 593 | } |
| 594 | |
| 595 | func (r *parseResult) getMethodNode(m *dpb.MethodDescriptorProto) methodDecl { |
| 596 | if r.nodes == nil { |
| 597 | return noSourceNode{pos: unknownPos(r.fd.GetName())} |
| 598 | } |
| 599 | return r.nodes[m].(methodDecl) |
| 600 | } |
| 601 | |
| 602 | func (r *parseResult) putFileNode(f *dpb.FileDescriptorProto, n *fileNode) { |
| 603 | r.nodes[f] = n |
| 604 | } |
| 605 | |
| 606 | func (r *parseResult) putOptionNode(o *dpb.UninterpretedOption, n *optionNode) { |
| 607 | r.nodes[o] = n |
| 608 | } |
| 609 | |
| 610 | func (r *parseResult) putOptionNamePartNode(o *dpb.UninterpretedOption_NamePart, n *optionNamePartNode) { |
| 611 | r.nodes[o] = n |
| 612 | } |
| 613 | |
| 614 | func (r *parseResult) putMessageNode(m *dpb.DescriptorProto, n msgDecl) { |
| 615 | r.nodes[m] = n |
| 616 | } |
| 617 | |
| 618 | func (r *parseResult) putFieldNode(f *dpb.FieldDescriptorProto, n fieldDecl) { |
| 619 | r.nodes[f] = n |
| 620 | } |
| 621 | |
| 622 | func (r *parseResult) putOneOfNode(o *dpb.OneofDescriptorProto, n *oneOfNode) { |
| 623 | r.nodes[o] = n |
| 624 | } |
| 625 | |
| 626 | func (r *parseResult) putExtensionRangeNode(e *dpb.DescriptorProto_ExtensionRange, n *rangeNode) { |
| 627 | r.nodes[e] = n |
| 628 | } |
| 629 | |
| 630 | func (r *parseResult) putMessageReservedRangeNode(rr *dpb.DescriptorProto_ReservedRange, n *rangeNode) { |
| 631 | r.nodes[rr] = n |
| 632 | } |
| 633 | |
| 634 | func (r *parseResult) putEnumNode(e *dpb.EnumDescriptorProto, n *enumNode) { |
| 635 | r.nodes[e] = n |
| 636 | } |
| 637 | |
| 638 | func (r *parseResult) putEnumValueNode(e *dpb.EnumValueDescriptorProto, n *enumValueNode) { |
| 639 | r.nodes[e] = n |
| 640 | } |
| 641 | |
| 642 | func (r *parseResult) putEnumReservedRangeNode(rr *dpb.EnumDescriptorProto_EnumReservedRange, n *rangeNode) { |
| 643 | r.nodes[rr] = n |
| 644 | } |
| 645 | |
| 646 | func (r *parseResult) putServiceNode(s *dpb.ServiceDescriptorProto, n *serviceNode) { |
| 647 | r.nodes[s] = n |
| 648 | } |
| 649 | |
| 650 | func (r *parseResult) putMethodNode(m *dpb.MethodDescriptorProto, n *methodNode) { |
| 651 | r.nodes[m] = n |
| 652 | } |
| 653 | |
| 654 | func parseProto(filename string, r io.Reader, validate bool) (*parseResult, error) { |
| 655 | lx := newLexer(r) |
| 656 | lx.filename = filename |
| 657 | protoParse(lx) |
| 658 | if lx.err != nil { |
| 659 | if _, ok := lx.err.(ErrorWithSourcePos); ok { |
| 660 | return nil, lx.err |
| 661 | } else { |
| 662 | return nil, ErrorWithSourcePos{Pos: lx.prev(), Underlying: lx.err} |
| 663 | } |
| 664 | } |
| 665 | // parser will not return an error if input is empty, so we |
| 666 | // need to also check if the result is non-nil |
| 667 | if lx.res == nil { |
| 668 | return nil, ErrorWithSourcePos{Pos: lx.prev(), Underlying: errors.New("input is empty")} |
| 669 | } |
| 670 | |
| 671 | res, err := createParseResult(filename, lx.res) |
| 672 | if err != nil { |
| 673 | return nil, err |
| 674 | } |
| 675 | if validate { |
| 676 | if err := basicValidate(res); err != nil { |
| 677 | return nil, err |
| 678 | } |
| 679 | } |
| 680 | return res, nil |
| 681 | } |
| 682 | |
| 683 | func createParseResult(filename string, file *fileNode) (*parseResult, error) { |
| 684 | res := &parseResult{ |
| 685 | nodes: map[proto.Message]node{}, |
| 686 | interpretedOptions: map[*optionNode][]int32{}, |
| 687 | } |
| 688 | err := res.createFileDescriptor(filename, file) |
| 689 | return res, err |
| 690 | } |
| 691 | |
| 692 | func (r *parseResult) createFileDescriptor(filename string, file *fileNode) error { |
| 693 | fd := &dpb.FileDescriptorProto{Name: proto.String(filename)} |
| 694 | r.putFileNode(fd, file) |
| 695 | |
| 696 | isProto3 := false |
| 697 | if file.syntax != nil { |
| 698 | isProto3 = file.syntax.syntax.val == "proto3" |
| 699 | // proto2 is the default, so no need to set unless proto3 |
| 700 | if isProto3 { |
| 701 | fd.Syntax = proto.String(file.syntax.syntax.val) |
| 702 | } |
| 703 | } |
| 704 | |
| 705 | for _, decl := range file.decls { |
| 706 | if decl.enum != nil { |
| 707 | fd.EnumType = append(fd.EnumType, r.asEnumDescriptor(decl.enum)) |
| 708 | } else if decl.extend != nil { |
| 709 | r.addExtensions(decl.extend, &fd.Extension, &fd.MessageType, isProto3) |
| 710 | } else if decl.imp != nil { |
| 711 | file.imports = append(file.imports, decl.imp) |
| 712 | index := len(fd.Dependency) |
| 713 | fd.Dependency = append(fd.Dependency, decl.imp.name.val) |
| 714 | if decl.imp.public { |
| 715 | fd.PublicDependency = append(fd.PublicDependency, int32(index)) |
| 716 | } else if decl.imp.weak { |
| 717 | fd.WeakDependency = append(fd.WeakDependency, int32(index)) |
| 718 | } |
| 719 | } else if decl.message != nil { |
| 720 | fd.MessageType = append(fd.MessageType, r.asMessageDescriptor(decl.message, isProto3)) |
| 721 | } else if decl.option != nil { |
| 722 | if fd.Options == nil { |
| 723 | fd.Options = &dpb.FileOptions{} |
| 724 | } |
| 725 | fd.Options.UninterpretedOption = append(fd.Options.UninterpretedOption, r.asUninterpretedOption(decl.option)) |
| 726 | } else if decl.service != nil { |
| 727 | fd.Service = append(fd.Service, r.asServiceDescriptor(decl.service)) |
| 728 | } else if decl.pkg != nil { |
| 729 | if fd.Package != nil { |
| 730 | return ErrorWithSourcePos{Pos: decl.pkg.start(), Underlying: errors.New("files should have only one package declaration")} |
| 731 | } |
| 732 | file.pkg = decl.pkg |
| 733 | fd.Package = proto.String(decl.pkg.name.val) |
| 734 | } |
| 735 | } |
| 736 | r.fd = fd |
| 737 | return nil |
| 738 | } |
| 739 | |
| 740 | func (r *parseResult) asUninterpretedOptions(nodes []*optionNode) []*dpb.UninterpretedOption { |
| 741 | opts := make([]*dpb.UninterpretedOption, len(nodes)) |
| 742 | for i, n := range nodes { |
| 743 | opts[i] = r.asUninterpretedOption(n) |
| 744 | } |
| 745 | return opts |
| 746 | } |
| 747 | |
| 748 | func (r *parseResult) asUninterpretedOption(node *optionNode) *dpb.UninterpretedOption { |
| 749 | opt := &dpb.UninterpretedOption{Name: r.asUninterpretedOptionName(node.name.parts)} |
| 750 | r.putOptionNode(opt, node) |
| 751 | |
| 752 | switch val := node.val.value().(type) { |
| 753 | case bool: |
| 754 | if val { |
| 755 | opt.IdentifierValue = proto.String("true") |
| 756 | } else { |
| 757 | opt.IdentifierValue = proto.String("false") |
| 758 | } |
| 759 | case int64: |
| 760 | opt.NegativeIntValue = proto.Int64(val) |
| 761 | case uint64: |
| 762 | opt.PositiveIntValue = proto.Uint64(val) |
| 763 | case float64: |
| 764 | opt.DoubleValue = proto.Float64(val) |
| 765 | case string: |
| 766 | opt.StringValue = []byte(val) |
| 767 | case identifier: |
| 768 | opt.IdentifierValue = proto.String(string(val)) |
| 769 | case []*aggregateEntryNode: |
| 770 | var buf bytes.Buffer |
| 771 | aggToString(val, &buf) |
| 772 | aggStr := buf.String() |
| 773 | opt.AggregateValue = proto.String(aggStr) |
| 774 | } |
| 775 | return opt |
| 776 | } |
| 777 | |
| 778 | func (r *parseResult) asUninterpretedOptionName(parts []*optionNamePartNode) []*dpb.UninterpretedOption_NamePart { |
| 779 | ret := make([]*dpb.UninterpretedOption_NamePart, len(parts)) |
| 780 | for i, part := range parts { |
| 781 | txt := part.text.val |
| 782 | if !part.isExtension { |
| 783 | txt = part.text.val[part.offset : part.offset+part.length] |
| 784 | } |
| 785 | np := &dpb.UninterpretedOption_NamePart{ |
| 786 | NamePart: proto.String(txt), |
| 787 | IsExtension: proto.Bool(part.isExtension), |
| 788 | } |
| 789 | r.putOptionNamePartNode(np, part) |
| 790 | ret[i] = np |
| 791 | } |
| 792 | return ret |
| 793 | } |
| 794 | |
| 795 | func (r *parseResult) addExtensions(ext *extendNode, flds *[]*dpb.FieldDescriptorProto, msgs *[]*dpb.DescriptorProto, isProto3 bool) { |
| 796 | extendee := ext.extendee.val |
| 797 | for _, decl := range ext.decls { |
| 798 | if decl.field != nil { |
| 799 | decl.field.extendee = ext |
| 800 | fd := r.asFieldDescriptor(decl.field) |
| 801 | fd.Extendee = proto.String(extendee) |
| 802 | *flds = append(*flds, fd) |
| 803 | } else if decl.group != nil { |
| 804 | decl.group.extendee = ext |
| 805 | fd, md := r.asGroupDescriptors(decl.group, isProto3) |
| 806 | fd.Extendee = proto.String(extendee) |
| 807 | *flds = append(*flds, fd) |
| 808 | *msgs = append(*msgs, md) |
| 809 | } |
| 810 | } |
| 811 | } |
| 812 | |
| 813 | func asLabel(lbl *labelNode) *dpb.FieldDescriptorProto_Label { |
| 814 | if lbl == nil { |
| 815 | return nil |
| 816 | } |
| 817 | switch { |
| 818 | case lbl.repeated: |
| 819 | return dpb.FieldDescriptorProto_LABEL_REPEATED.Enum() |
| 820 | case lbl.required: |
| 821 | return dpb.FieldDescriptorProto_LABEL_REQUIRED.Enum() |
| 822 | default: |
| 823 | return dpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum() |
| 824 | } |
| 825 | } |
| 826 | |
| 827 | func (r *parseResult) asFieldDescriptor(node *fieldNode) *dpb.FieldDescriptorProto { |
| 828 | fd := newFieldDescriptor(node.name.val, node.fldType.val, int32(node.tag.val), asLabel(node.label)) |
| 829 | r.putFieldNode(fd, node) |
| 830 | if len(node.options) > 0 { |
| 831 | fd.Options = &dpb.FieldOptions{UninterpretedOption: r.asUninterpretedOptions(node.options)} |
| 832 | } |
| 833 | return fd |
| 834 | } |
| 835 | |
| 836 | func newFieldDescriptor(name string, fieldType string, tag int32, lbl *dpb.FieldDescriptorProto_Label) *dpb.FieldDescriptorProto { |
| 837 | fd := &dpb.FieldDescriptorProto{ |
| 838 | Name: proto.String(name), |
| 839 | JsonName: proto.String(internal.JsonName(name)), |
| 840 | Number: proto.Int32(tag), |
| 841 | Label: lbl, |
| 842 | } |
| 843 | switch fieldType { |
| 844 | case "double": |
| 845 | fd.Type = dpb.FieldDescriptorProto_TYPE_DOUBLE.Enum() |
| 846 | case "float": |
| 847 | fd.Type = dpb.FieldDescriptorProto_TYPE_FLOAT.Enum() |
| 848 | case "int32": |
| 849 | fd.Type = dpb.FieldDescriptorProto_TYPE_INT32.Enum() |
| 850 | case "int64": |
| 851 | fd.Type = dpb.FieldDescriptorProto_TYPE_INT64.Enum() |
| 852 | case "uint32": |
| 853 | fd.Type = dpb.FieldDescriptorProto_TYPE_UINT32.Enum() |
| 854 | case "uint64": |
| 855 | fd.Type = dpb.FieldDescriptorProto_TYPE_UINT64.Enum() |
| 856 | case "sint32": |
| 857 | fd.Type = dpb.FieldDescriptorProto_TYPE_SINT32.Enum() |
| 858 | case "sint64": |
| 859 | fd.Type = dpb.FieldDescriptorProto_TYPE_SINT64.Enum() |
| 860 | case "fixed32": |
| 861 | fd.Type = dpb.FieldDescriptorProto_TYPE_FIXED32.Enum() |
| 862 | case "fixed64": |
| 863 | fd.Type = dpb.FieldDescriptorProto_TYPE_FIXED64.Enum() |
| 864 | case "sfixed32": |
| 865 | fd.Type = dpb.FieldDescriptorProto_TYPE_SFIXED32.Enum() |
| 866 | case "sfixed64": |
| 867 | fd.Type = dpb.FieldDescriptorProto_TYPE_SFIXED64.Enum() |
| 868 | case "bool": |
| 869 | fd.Type = dpb.FieldDescriptorProto_TYPE_BOOL.Enum() |
| 870 | case "string": |
| 871 | fd.Type = dpb.FieldDescriptorProto_TYPE_STRING.Enum() |
| 872 | case "bytes": |
| 873 | fd.Type = dpb.FieldDescriptorProto_TYPE_BYTES.Enum() |
| 874 | default: |
| 875 | // NB: we don't have enough info to determine whether this is an enum or a message type, |
| 876 | // so we'll change it to enum later once we can ascertain if it's an enum reference |
| 877 | fd.Type = dpb.FieldDescriptorProto_TYPE_MESSAGE.Enum() |
| 878 | fd.TypeName = proto.String(fieldType) |
| 879 | } |
| 880 | return fd |
| 881 | } |
| 882 | |
| 883 | func (r *parseResult) asGroupDescriptors(group *groupNode, isProto3 bool) (*dpb.FieldDescriptorProto, *dpb.DescriptorProto) { |
| 884 | fieldName := strings.ToLower(group.name.val) |
| 885 | fd := &dpb.FieldDescriptorProto{ |
| 886 | Name: proto.String(fieldName), |
| 887 | JsonName: proto.String(internal.JsonName(fieldName)), |
| 888 | Number: proto.Int32(int32(group.tag.val)), |
| 889 | Label: asLabel(group.label), |
| 890 | Type: dpb.FieldDescriptorProto_TYPE_GROUP.Enum(), |
| 891 | TypeName: proto.String(group.name.val), |
| 892 | } |
| 893 | r.putFieldNode(fd, group) |
| 894 | md := &dpb.DescriptorProto{Name: proto.String(group.name.val)} |
| 895 | r.putMessageNode(md, group) |
| 896 | r.addMessageDecls(md, &group.reserved, group.decls, isProto3) |
| 897 | return fd, md |
| 898 | } |
| 899 | |
| 900 | func (r *parseResult) asMapDescriptors(mapField *mapFieldNode, isProto3 bool) (*dpb.FieldDescriptorProto, *dpb.DescriptorProto) { |
| 901 | var lbl *dpb.FieldDescriptorProto_Label |
| 902 | if !isProto3 { |
| 903 | lbl = dpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum() |
| 904 | } |
| 905 | keyFd := newFieldDescriptor("key", mapField.keyType.val, 1, lbl) |
| 906 | r.putFieldNode(keyFd, mapField.keyField()) |
| 907 | valFd := newFieldDescriptor("value", mapField.valueType.val, 2, lbl) |
| 908 | r.putFieldNode(valFd, mapField.valueField()) |
| 909 | entryName := internal.InitCap(internal.JsonName(mapField.name.val)) + "Entry" |
| 910 | fd := newFieldDescriptor(mapField.name.val, entryName, int32(mapField.tag.val), dpb.FieldDescriptorProto_LABEL_REPEATED.Enum()) |
| 911 | if len(mapField.options) > 0 { |
| 912 | fd.Options = &dpb.FieldOptions{UninterpretedOption: r.asUninterpretedOptions(mapField.options)} |
| 913 | } |
| 914 | r.putFieldNode(fd, mapField) |
| 915 | md := &dpb.DescriptorProto{ |
| 916 | Name: proto.String(entryName), |
| 917 | Options: &dpb.MessageOptions{MapEntry: proto.Bool(true)}, |
| 918 | Field: []*dpb.FieldDescriptorProto{keyFd, valFd}, |
| 919 | } |
| 920 | r.putMessageNode(md, mapField) |
| 921 | return fd, md |
| 922 | } |
| 923 | |
| 924 | func (r *parseResult) asExtensionRanges(node *extensionRangeNode) []*dpb.DescriptorProto_ExtensionRange { |
| 925 | opts := r.asUninterpretedOptions(node.options) |
| 926 | ers := make([]*dpb.DescriptorProto_ExtensionRange, len(node.ranges)) |
| 927 | for i, rng := range node.ranges { |
| 928 | er := &dpb.DescriptorProto_ExtensionRange{ |
| 929 | Start: proto.Int32(rng.st), |
| 930 | End: proto.Int32(rng.en + 1), |
| 931 | } |
| 932 | if len(opts) > 0 { |
| 933 | er.Options = &dpb.ExtensionRangeOptions{UninterpretedOption: opts} |
| 934 | } |
| 935 | r.putExtensionRangeNode(er, rng) |
| 936 | ers[i] = er |
| 937 | } |
| 938 | return ers |
| 939 | } |
| 940 | |
| 941 | func (r *parseResult) asEnumValue(ev *enumValueNode) *dpb.EnumValueDescriptorProto { |
| 942 | var num int32 |
| 943 | if ev.numberP != nil { |
| 944 | num = int32(ev.numberP.val) |
| 945 | } else { |
| 946 | num = int32(ev.numberN.val) |
| 947 | } |
| 948 | evd := &dpb.EnumValueDescriptorProto{Name: proto.String(ev.name.val), Number: proto.Int32(num)} |
| 949 | r.putEnumValueNode(evd, ev) |
| 950 | if len(ev.options) > 0 { |
| 951 | evd.Options = &dpb.EnumValueOptions{UninterpretedOption: r.asUninterpretedOptions(ev.options)} |
| 952 | } |
| 953 | return evd |
| 954 | } |
| 955 | |
| 956 | func (r *parseResult) asMethodDescriptor(node *methodNode) *dpb.MethodDescriptorProto { |
| 957 | md := &dpb.MethodDescriptorProto{ |
| 958 | Name: proto.String(node.name.val), |
| 959 | InputType: proto.String(node.input.msgType.val), |
| 960 | OutputType: proto.String(node.output.msgType.val), |
| 961 | } |
| 962 | r.putMethodNode(md, node) |
| 963 | if node.input.streamKeyword != nil { |
| 964 | md.ClientStreaming = proto.Bool(true) |
| 965 | } |
| 966 | if node.output.streamKeyword != nil { |
| 967 | md.ServerStreaming = proto.Bool(true) |
| 968 | } |
| 969 | // protoc always adds a MethodOptions if there are brackets |
| 970 | // We have a non-nil node.options if there are brackets |
| 971 | // We do the same to match protoc as closely as possible |
| 972 | // https://github.com/protocolbuffers/protobuf/blob/0c3f43a6190b77f1f68b7425d1b7e1a8257a8d0c/src/google/protobuf/compiler/parser.cc#L2152 |
| 973 | if node.options != nil { |
| 974 | md.Options = &dpb.MethodOptions{UninterpretedOption: r.asUninterpretedOptions(node.options)} |
| 975 | } |
| 976 | return md |
| 977 | } |
| 978 | |
| 979 | func (r *parseResult) asEnumDescriptor(en *enumNode) *dpb.EnumDescriptorProto { |
| 980 | ed := &dpb.EnumDescriptorProto{Name: proto.String(en.name.val)} |
| 981 | r.putEnumNode(ed, en) |
| 982 | for _, decl := range en.decls { |
| 983 | if decl.option != nil { |
| 984 | if ed.Options == nil { |
| 985 | ed.Options = &dpb.EnumOptions{} |
| 986 | } |
| 987 | ed.Options.UninterpretedOption = append(ed.Options.UninterpretedOption, r.asUninterpretedOption(decl.option)) |
| 988 | } else if decl.value != nil { |
| 989 | ed.Value = append(ed.Value, r.asEnumValue(decl.value)) |
| 990 | } else if decl.reserved != nil { |
| 991 | for _, n := range decl.reserved.names { |
| 992 | en.reserved = append(en.reserved, n) |
| 993 | ed.ReservedName = append(ed.ReservedName, n.val) |
| 994 | } |
| 995 | for _, rng := range decl.reserved.ranges { |
| 996 | ed.ReservedRange = append(ed.ReservedRange, r.asEnumReservedRange(rng)) |
| 997 | } |
| 998 | } |
| 999 | } |
| 1000 | return ed |
| 1001 | } |
| 1002 | |
| 1003 | func (r *parseResult) asEnumReservedRange(rng *rangeNode) *dpb.EnumDescriptorProto_EnumReservedRange { |
| 1004 | rr := &dpb.EnumDescriptorProto_EnumReservedRange{ |
| 1005 | Start: proto.Int32(rng.st), |
| 1006 | End: proto.Int32(rng.en), |
| 1007 | } |
| 1008 | r.putEnumReservedRangeNode(rr, rng) |
| 1009 | return rr |
| 1010 | } |
| 1011 | |
| 1012 | func (r *parseResult) asMessageDescriptor(node *messageNode, isProto3 bool) *dpb.DescriptorProto { |
| 1013 | msgd := &dpb.DescriptorProto{Name: proto.String(node.name.val)} |
| 1014 | r.putMessageNode(msgd, node) |
| 1015 | r.addMessageDecls(msgd, &node.reserved, node.decls, isProto3) |
| 1016 | return msgd |
| 1017 | } |
| 1018 | |
| 1019 | func (r *parseResult) addMessageDecls(msgd *dpb.DescriptorProto, reservedNames *[]*stringLiteralNode, decls []*messageElement, isProto3 bool) { |
| 1020 | for _, decl := range decls { |
| 1021 | if decl.enum != nil { |
| 1022 | msgd.EnumType = append(msgd.EnumType, r.asEnumDescriptor(decl.enum)) |
| 1023 | } else if decl.extend != nil { |
| 1024 | r.addExtensions(decl.extend, &msgd.Extension, &msgd.NestedType, isProto3) |
| 1025 | } else if decl.extensionRange != nil { |
| 1026 | msgd.ExtensionRange = append(msgd.ExtensionRange, r.asExtensionRanges(decl.extensionRange)...) |
| 1027 | } else if decl.field != nil { |
| 1028 | msgd.Field = append(msgd.Field, r.asFieldDescriptor(decl.field)) |
| 1029 | } else if decl.mapField != nil { |
| 1030 | fd, md := r.asMapDescriptors(decl.mapField, isProto3) |
| 1031 | msgd.Field = append(msgd.Field, fd) |
| 1032 | msgd.NestedType = append(msgd.NestedType, md) |
| 1033 | } else if decl.group != nil { |
| 1034 | fd, md := r.asGroupDescriptors(decl.group, isProto3) |
| 1035 | msgd.Field = append(msgd.Field, fd) |
| 1036 | msgd.NestedType = append(msgd.NestedType, md) |
| 1037 | } else if decl.oneOf != nil { |
| 1038 | oodIndex := len(msgd.OneofDecl) |
| 1039 | ood := &dpb.OneofDescriptorProto{Name: proto.String(decl.oneOf.name.val)} |
| 1040 | r.putOneOfNode(ood, decl.oneOf) |
| 1041 | msgd.OneofDecl = append(msgd.OneofDecl, ood) |
| 1042 | for _, oodecl := range decl.oneOf.decls { |
| 1043 | if oodecl.option != nil { |
| 1044 | if ood.Options == nil { |
| 1045 | ood.Options = &dpb.OneofOptions{} |
| 1046 | } |
| 1047 | ood.Options.UninterpretedOption = append(ood.Options.UninterpretedOption, r.asUninterpretedOption(oodecl.option)) |
| 1048 | } else if oodecl.field != nil { |
| 1049 | fd := r.asFieldDescriptor(oodecl.field) |
| 1050 | fd.OneofIndex = proto.Int32(int32(oodIndex)) |
| 1051 | msgd.Field = append(msgd.Field, fd) |
| 1052 | } |
| 1053 | } |
| 1054 | } else if decl.option != nil { |
| 1055 | if msgd.Options == nil { |
| 1056 | msgd.Options = &dpb.MessageOptions{} |
| 1057 | } |
| 1058 | msgd.Options.UninterpretedOption = append(msgd.Options.UninterpretedOption, r.asUninterpretedOption(decl.option)) |
| 1059 | } else if decl.nested != nil { |
| 1060 | msgd.NestedType = append(msgd.NestedType, r.asMessageDescriptor(decl.nested, isProto3)) |
| 1061 | } else if decl.reserved != nil { |
| 1062 | for _, n := range decl.reserved.names { |
| 1063 | *reservedNames = append(*reservedNames, n) |
| 1064 | msgd.ReservedName = append(msgd.ReservedName, n.val) |
| 1065 | } |
| 1066 | for _, rng := range decl.reserved.ranges { |
| 1067 | msgd.ReservedRange = append(msgd.ReservedRange, r.asMessageReservedRange(rng)) |
| 1068 | } |
| 1069 | } |
| 1070 | } |
| 1071 | } |
| 1072 | |
| 1073 | func (r *parseResult) asMessageReservedRange(rng *rangeNode) *dpb.DescriptorProto_ReservedRange { |
| 1074 | rr := &dpb.DescriptorProto_ReservedRange{ |
| 1075 | Start: proto.Int32(rng.st), |
| 1076 | End: proto.Int32(rng.en + 1), |
| 1077 | } |
| 1078 | r.putMessageReservedRangeNode(rr, rng) |
| 1079 | return rr |
| 1080 | } |
| 1081 | |
| 1082 | func (r *parseResult) asServiceDescriptor(svc *serviceNode) *dpb.ServiceDescriptorProto { |
| 1083 | sd := &dpb.ServiceDescriptorProto{Name: proto.String(svc.name.val)} |
| 1084 | r.putServiceNode(sd, svc) |
| 1085 | for _, decl := range svc.decls { |
| 1086 | if decl.option != nil { |
| 1087 | if sd.Options == nil { |
| 1088 | sd.Options = &dpb.ServiceOptions{} |
| 1089 | } |
| 1090 | sd.Options.UninterpretedOption = append(sd.Options.UninterpretedOption, r.asUninterpretedOption(decl.option)) |
| 1091 | } else if decl.rpc != nil { |
| 1092 | sd.Method = append(sd.Method, r.asMethodDescriptor(decl.rpc)) |
| 1093 | } |
| 1094 | } |
| 1095 | return sd |
| 1096 | } |
| 1097 | |
| 1098 | func toNameParts(ident *identNode, offset int) []*optionNamePartNode { |
| 1099 | parts := strings.Split(ident.val[offset:], ".") |
| 1100 | ret := make([]*optionNamePartNode, len(parts)) |
| 1101 | for i, p := range parts { |
| 1102 | ret[i] = &optionNamePartNode{text: ident, offset: offset, length: len(p)} |
| 1103 | ret[i].setRange(ident, ident) |
| 1104 | offset += len(p) + 1 |
| 1105 | } |
| 1106 | return ret |
| 1107 | } |
| 1108 | |
| 1109 | func checkUint64InInt32Range(lex protoLexer, pos *SourcePos, v uint64) { |
| 1110 | if v > math.MaxInt32 { |
| 1111 | lexError(lex, pos, fmt.Sprintf("constant %d is out of range for int32 (%d to %d)", v, math.MinInt32, math.MaxInt32)) |
| 1112 | } |
| 1113 | } |
| 1114 | |
| 1115 | func checkInt64InInt32Range(lex protoLexer, pos *SourcePos, v int64) { |
| 1116 | if v > math.MaxInt32 || v < math.MinInt32 { |
| 1117 | lexError(lex, pos, fmt.Sprintf("constant %d is out of range for int32 (%d to %d)", v, math.MinInt32, math.MaxInt32)) |
| 1118 | } |
| 1119 | } |
| 1120 | |
| 1121 | func checkTag(lex protoLexer, pos *SourcePos, v uint64) { |
| 1122 | if v > internal.MaxTag { |
| 1123 | lexError(lex, pos, fmt.Sprintf("tag number %d is higher than max allowed tag number (%d)", v, internal.MaxTag)) |
| 1124 | } else if v >= internal.SpecialReservedStart && v <= internal.SpecialReservedEnd { |
| 1125 | lexError(lex, pos, fmt.Sprintf("tag number %d is in disallowed reserved range %d-%d", v, internal.SpecialReservedStart, internal.SpecialReservedEnd)) |
| 1126 | } |
| 1127 | } |
| 1128 | |
| 1129 | func aggToString(agg []*aggregateEntryNode, buf *bytes.Buffer) { |
| 1130 | buf.WriteString("{") |
| 1131 | for _, a := range agg { |
| 1132 | buf.WriteString(" ") |
| 1133 | buf.WriteString(a.name.value()) |
| 1134 | if v, ok := a.val.(*aggregateLiteralNode); ok { |
| 1135 | aggToString(v.elements, buf) |
| 1136 | } else { |
| 1137 | buf.WriteString(": ") |
| 1138 | elementToString(a.val.value(), buf) |
| 1139 | } |
| 1140 | } |
| 1141 | buf.WriteString(" }") |
| 1142 | } |
| 1143 | |
| 1144 | func elementToString(v interface{}, buf *bytes.Buffer) { |
| 1145 | switch v := v.(type) { |
| 1146 | case bool, int64, uint64, identifier: |
| 1147 | fmt.Fprintf(buf, "%v", v) |
| 1148 | case float64: |
| 1149 | if math.IsInf(v, 1) { |
| 1150 | buf.WriteString(": inf") |
| 1151 | } else if math.IsInf(v, -1) { |
| 1152 | buf.WriteString(": -inf") |
| 1153 | } else if math.IsNaN(v) { |
| 1154 | buf.WriteString(": nan") |
| 1155 | } else { |
| 1156 | fmt.Fprintf(buf, ": %v", v) |
| 1157 | } |
| 1158 | case string: |
| 1159 | buf.WriteRune('"') |
| 1160 | writeEscapedBytes(buf, []byte(v)) |
| 1161 | buf.WriteRune('"') |
| 1162 | case []valueNode: |
| 1163 | buf.WriteString(": [") |
| 1164 | first := true |
| 1165 | for _, e := range v { |
| 1166 | if first { |
| 1167 | first = false |
| 1168 | } else { |
| 1169 | buf.WriteString(", ") |
| 1170 | } |
| 1171 | elementToString(e.value(), buf) |
| 1172 | } |
| 1173 | buf.WriteString("]") |
| 1174 | case []*aggregateEntryNode: |
| 1175 | aggToString(v, buf) |
| 1176 | } |
| 1177 | } |
| 1178 | |
| 1179 | func writeEscapedBytes(buf *bytes.Buffer, b []byte) { |
| 1180 | for _, c := range b { |
| 1181 | switch c { |
| 1182 | case '\n': |
| 1183 | buf.WriteString("\\n") |
| 1184 | case '\r': |
| 1185 | buf.WriteString("\\r") |
| 1186 | case '\t': |
| 1187 | buf.WriteString("\\t") |
| 1188 | case '"': |
| 1189 | buf.WriteString("\\\"") |
| 1190 | case '\'': |
| 1191 | buf.WriteString("\\'") |
| 1192 | case '\\': |
| 1193 | buf.WriteString("\\\\") |
| 1194 | default: |
| 1195 | if c >= 0x20 && c <= 0x7f && c != '"' && c != '\\' { |
| 1196 | // simple printable characters |
| 1197 | buf.WriteByte(c) |
| 1198 | } else { |
| 1199 | // use octal escape for all other values |
| 1200 | buf.WriteRune('\\') |
| 1201 | buf.WriteByte('0' + ((c >> 6) & 0x7)) |
| 1202 | buf.WriteByte('0' + ((c >> 3) & 0x7)) |
| 1203 | buf.WriteByte('0' + (c & 0x7)) |
| 1204 | } |
| 1205 | } |
| 1206 | } |
| 1207 | } |
| 1208 | |
| 1209 | func basicValidate(res *parseResult) error { |
| 1210 | fd := res.fd |
| 1211 | isProto3 := fd.GetSyntax() == "proto3" |
| 1212 | |
| 1213 | for _, md := range fd.MessageType { |
| 1214 | if err := validateMessage(res, isProto3, "", md); err != nil { |
| 1215 | return err |
| 1216 | } |
| 1217 | } |
| 1218 | |
| 1219 | for _, ed := range fd.EnumType { |
| 1220 | if err := validateEnum(res, isProto3, "", ed); err != nil { |
| 1221 | return err |
| 1222 | } |
| 1223 | } |
| 1224 | |
| 1225 | for _, fld := range fd.Extension { |
| 1226 | if err := validateField(res, isProto3, "", fld); err != nil { |
| 1227 | return err |
| 1228 | } |
| 1229 | } |
| 1230 | return nil |
| 1231 | } |
| 1232 | |
| 1233 | func validateMessage(res *parseResult, isProto3 bool, prefix string, md *dpb.DescriptorProto) error { |
| 1234 | nextPrefix := md.GetName() + "." |
| 1235 | |
| 1236 | for _, fld := range md.Field { |
| 1237 | if err := validateField(res, isProto3, nextPrefix, fld); err != nil { |
| 1238 | return err |
| 1239 | } |
| 1240 | } |
| 1241 | for _, fld := range md.Extension { |
| 1242 | if err := validateField(res, isProto3, nextPrefix, fld); err != nil { |
| 1243 | return err |
| 1244 | } |
| 1245 | } |
| 1246 | for _, ed := range md.EnumType { |
| 1247 | if err := validateEnum(res, isProto3, nextPrefix, ed); err != nil { |
| 1248 | return err |
| 1249 | } |
| 1250 | } |
| 1251 | for _, nmd := range md.NestedType { |
| 1252 | if err := validateMessage(res, isProto3, nextPrefix, nmd); err != nil { |
| 1253 | return err |
| 1254 | } |
| 1255 | } |
| 1256 | |
| 1257 | scope := fmt.Sprintf("message %s%s", prefix, md.GetName()) |
| 1258 | |
| 1259 | if isProto3 && len(md.ExtensionRange) > 0 { |
| 1260 | n := res.getExtensionRangeNode(md.ExtensionRange[0]) |
| 1261 | return ErrorWithSourcePos{Pos: n.start(), Underlying: fmt.Errorf("%s: extension ranges are not allowed in proto3", scope)} |
| 1262 | } |
| 1263 | |
| 1264 | if index, err := findOption(res, scope, md.Options.GetUninterpretedOption(), "map_entry"); err != nil { |
| 1265 | return err |
| 1266 | } else if index >= 0 { |
| 1267 | opt := md.Options.UninterpretedOption[index] |
| 1268 | optn := res.getOptionNode(opt) |
| 1269 | md.Options.UninterpretedOption = removeOption(md.Options.UninterpretedOption, index) |
| 1270 | valid := false |
| 1271 | if opt.IdentifierValue != nil { |
| 1272 | if opt.GetIdentifierValue() == "true" { |
| 1273 | return ErrorWithSourcePos{Pos: optn.getValue().start(), Underlying: fmt.Errorf("%s: map_entry option should not be set explicitly; use map type instead", scope)} |
| 1274 | } else if opt.GetIdentifierValue() == "false" { |
| 1275 | md.Options.MapEntry = proto.Bool(false) |
| 1276 | valid = true |
| 1277 | } |
| 1278 | } |
| 1279 | if !valid { |
| 1280 | return ErrorWithSourcePos{Pos: optn.getValue().start(), Underlying: fmt.Errorf("%s: expecting bool value for map_entry option", scope)} |
| 1281 | } |
| 1282 | } |
| 1283 | |
| 1284 | // reserved ranges should not overlap |
| 1285 | rsvd := make(tagRanges, len(md.ReservedRange)) |
| 1286 | for i, r := range md.ReservedRange { |
| 1287 | n := res.getMessageReservedRangeNode(r) |
| 1288 | rsvd[i] = tagRange{start: r.GetStart(), end: r.GetEnd(), node: n} |
| 1289 | |
| 1290 | } |
| 1291 | sort.Sort(rsvd) |
| 1292 | for i := 1; i < len(rsvd); i++ { |
| 1293 | if rsvd[i].start < rsvd[i-1].end { |
| 1294 | return ErrorWithSourcePos{Pos: rsvd[i].node.start(), Underlying: fmt.Errorf("%s: reserved ranges overlap: %d to %d and %d to %d", scope, rsvd[i-1].start, rsvd[i-1].end-1, rsvd[i].start, rsvd[i].end-1)} |
| 1295 | } |
| 1296 | } |
| 1297 | |
| 1298 | // extensions ranges should not overlap |
| 1299 | exts := make(tagRanges, len(md.ExtensionRange)) |
| 1300 | for i, r := range md.ExtensionRange { |
| 1301 | n := res.getExtensionRangeNode(r) |
| 1302 | exts[i] = tagRange{start: r.GetStart(), end: r.GetEnd(), node: n} |
| 1303 | } |
| 1304 | sort.Sort(exts) |
| 1305 | for i := 1; i < len(exts); i++ { |
| 1306 | if exts[i].start < exts[i-1].end { |
| 1307 | return ErrorWithSourcePos{Pos: exts[i].node.start(), Underlying: fmt.Errorf("%s: extension ranges overlap: %d to %d and %d to %d", scope, exts[i-1].start, exts[i-1].end-1, exts[i].start, exts[i].end-1)} |
| 1308 | } |
| 1309 | } |
| 1310 | |
| 1311 | // see if any extension range overlaps any reserved range |
| 1312 | var i, j int // i indexes rsvd; j indexes exts |
| 1313 | for i < len(rsvd) && j < len(exts) { |
| 1314 | if rsvd[i].start >= exts[j].start && rsvd[i].start < exts[j].end || |
| 1315 | exts[j].start >= rsvd[i].start && exts[j].start < rsvd[i].end { |
| 1316 | |
| 1317 | var pos *SourcePos |
| 1318 | if rsvd[i].start >= exts[j].start && rsvd[i].start < exts[j].end { |
| 1319 | pos = rsvd[i].node.start() |
| 1320 | } else { |
| 1321 | pos = exts[j].node.start() |
| 1322 | } |
| 1323 | // ranges overlap |
| 1324 | return ErrorWithSourcePos{Pos: pos, Underlying: fmt.Errorf("%s: extension range %d to %d overlaps reserved range %d to %d", scope, exts[j].start, exts[j].end-1, rsvd[i].start, rsvd[i].end-1)} |
| 1325 | } |
| 1326 | if rsvd[i].start < exts[j].start { |
| 1327 | i++ |
| 1328 | } else { |
| 1329 | j++ |
| 1330 | } |
| 1331 | } |
| 1332 | |
| 1333 | // now, check that fields don't re-use tags and don't try to use extension |
| 1334 | // or reserved ranges or reserved names |
| 1335 | rsvdNames := map[string]struct{}{} |
| 1336 | for _, n := range md.ReservedName { |
| 1337 | rsvdNames[n] = struct{}{} |
| 1338 | } |
| 1339 | fieldTags := map[int32]string{} |
| 1340 | for _, fld := range md.Field { |
| 1341 | fn := res.getFieldNode(fld) |
| 1342 | if _, ok := rsvdNames[fld.GetName()]; ok { |
| 1343 | return ErrorWithSourcePos{Pos: fn.fieldName().start(), Underlying: fmt.Errorf("%s: field %s is using a reserved name", scope, fld.GetName())} |
| 1344 | } |
| 1345 | if existing := fieldTags[fld.GetNumber()]; existing != "" { |
| 1346 | return ErrorWithSourcePos{Pos: fn.fieldTag().start(), Underlying: fmt.Errorf("%s: fields %s and %s both have the same tag %d", scope, existing, fld.GetName(), fld.GetNumber())} |
| 1347 | } |
| 1348 | fieldTags[fld.GetNumber()] = fld.GetName() |
| 1349 | // check reserved ranges |
| 1350 | r := sort.Search(len(rsvd), func(index int) bool { return rsvd[index].end > fld.GetNumber() }) |
| 1351 | if r < len(rsvd) && rsvd[r].start <= fld.GetNumber() { |
| 1352 | return ErrorWithSourcePos{Pos: fn.fieldTag().start(), Underlying: fmt.Errorf("%s: field %s is using tag %d which is in reserved range %d to %d", scope, fld.GetName(), fld.GetNumber(), rsvd[r].start, rsvd[r].end-1)} |
| 1353 | } |
| 1354 | // and check extension ranges |
| 1355 | e := sort.Search(len(exts), func(index int) bool { return exts[index].end > fld.GetNumber() }) |
| 1356 | if e < len(exts) && exts[e].start <= fld.GetNumber() { |
| 1357 | return ErrorWithSourcePos{Pos: fn.fieldTag().start(), Underlying: fmt.Errorf("%s: field %s is using tag %d which is in extension range %d to %d", scope, fld.GetName(), fld.GetNumber(), exts[e].start, exts[e].end-1)} |
| 1358 | } |
| 1359 | } |
| 1360 | |
| 1361 | return nil |
| 1362 | } |
| 1363 | |
| 1364 | func validateEnum(res *parseResult, isProto3 bool, prefix string, ed *dpb.EnumDescriptorProto) error { |
| 1365 | scope := fmt.Sprintf("enum %s%s", prefix, ed.GetName()) |
| 1366 | |
| 1367 | if index, err := findOption(res, scope, ed.Options.GetUninterpretedOption(), "allow_alias"); err != nil { |
| 1368 | return err |
| 1369 | } else if index >= 0 { |
| 1370 | opt := ed.Options.UninterpretedOption[index] |
| 1371 | ed.Options.UninterpretedOption = removeOption(ed.Options.UninterpretedOption, index) |
| 1372 | valid := false |
| 1373 | if opt.IdentifierValue != nil { |
| 1374 | if opt.GetIdentifierValue() == "true" { |
| 1375 | ed.Options.AllowAlias = proto.Bool(true) |
| 1376 | valid = true |
| 1377 | } else if opt.GetIdentifierValue() == "false" { |
| 1378 | ed.Options.AllowAlias = proto.Bool(false) |
| 1379 | valid = true |
| 1380 | } |
| 1381 | } |
| 1382 | if !valid { |
| 1383 | optNode := res.getOptionNode(opt) |
| 1384 | return ErrorWithSourcePos{Pos: optNode.getValue().start(), Underlying: fmt.Errorf("%s: expecting bool value for allow_alias option", scope)} |
| 1385 | } |
| 1386 | } |
| 1387 | |
| 1388 | if isProto3 && ed.Value[0].GetNumber() != 0 { |
| 1389 | evNode := res.getEnumValueNode(ed.Value[0]) |
| 1390 | return ErrorWithSourcePos{Pos: evNode.getNumber().start(), Underlying: fmt.Errorf("%s: proto3 requires that first value in enum have numeric value of 0", scope)} |
| 1391 | } |
| 1392 | |
| 1393 | if !ed.Options.GetAllowAlias() { |
| 1394 | // make sure all value numbers are distinct |
| 1395 | vals := map[int32]string{} |
| 1396 | for _, evd := range ed.Value { |
| 1397 | if existing := vals[evd.GetNumber()]; existing != "" { |
| 1398 | evNode := res.getEnumValueNode(evd) |
| 1399 | return ErrorWithSourcePos{Pos: evNode.getNumber().start(), Underlying: fmt.Errorf("%s: values %s and %s both have the same numeric value %d; use allow_alias option if intentional", scope, existing, evd.GetName(), evd.GetNumber())} |
| 1400 | } |
| 1401 | vals[evd.GetNumber()] = evd.GetName() |
| 1402 | } |
| 1403 | } |
| 1404 | |
| 1405 | // reserved ranges should not overlap |
| 1406 | rsvd := make(tagRanges, len(ed.ReservedRange)) |
| 1407 | for i, r := range ed.ReservedRange { |
| 1408 | n := res.getEnumReservedRangeNode(r) |
| 1409 | rsvd[i] = tagRange{start: r.GetStart(), end: r.GetEnd(), node: n} |
| 1410 | } |
| 1411 | sort.Sort(rsvd) |
| 1412 | for i := 1; i < len(rsvd); i++ { |
| 1413 | if rsvd[i].start <= rsvd[i-1].end { |
| 1414 | return ErrorWithSourcePos{Pos: rsvd[i].node.start(), Underlying: fmt.Errorf("%s: reserved ranges overlap: %d to %d and %d to %d", scope, rsvd[i-1].start, rsvd[i-1].end, rsvd[i].start, rsvd[i].end)} |
| 1415 | } |
| 1416 | } |
| 1417 | |
| 1418 | // now, check that fields don't re-use tags and don't try to use extension |
| 1419 | // or reserved ranges or reserved names |
| 1420 | rsvdNames := map[string]struct{}{} |
| 1421 | for _, n := range ed.ReservedName { |
| 1422 | rsvdNames[n] = struct{}{} |
| 1423 | } |
| 1424 | for _, ev := range ed.Value { |
| 1425 | evn := res.getEnumValueNode(ev) |
| 1426 | if _, ok := rsvdNames[ev.GetName()]; ok { |
| 1427 | return ErrorWithSourcePos{Pos: evn.getName().start(), Underlying: fmt.Errorf("%s: value %s is using a reserved name", scope, ev.GetName())} |
| 1428 | } |
| 1429 | // check reserved ranges |
| 1430 | r := sort.Search(len(rsvd), func(index int) bool { return rsvd[index].end >= ev.GetNumber() }) |
| 1431 | if r < len(rsvd) && rsvd[r].start <= ev.GetNumber() { |
| 1432 | return ErrorWithSourcePos{Pos: evn.getNumber().start(), Underlying: fmt.Errorf("%s: value %s is using number %d which is in reserved range %d to %d", scope, ev.GetName(), ev.GetNumber(), rsvd[r].start, rsvd[r].end)} |
| 1433 | } |
| 1434 | } |
| 1435 | |
| 1436 | return nil |
| 1437 | } |
| 1438 | |
| 1439 | func validateField(res *parseResult, isProto3 bool, prefix string, fld *dpb.FieldDescriptorProto) error { |
| 1440 | scope := fmt.Sprintf("field %s%s", prefix, fld.GetName()) |
| 1441 | |
| 1442 | node := res.getFieldNode(fld) |
| 1443 | if isProto3 { |
| 1444 | if fld.GetType() == dpb.FieldDescriptorProto_TYPE_GROUP { |
| 1445 | n := node.(*groupNode) |
| 1446 | return ErrorWithSourcePos{Pos: n.groupKeyword.start(), Underlying: fmt.Errorf("%s: groups are not allowed in proto3", scope)} |
| 1447 | } |
| 1448 | if fld.Label != nil && fld.GetLabel() != dpb.FieldDescriptorProto_LABEL_REPEATED { |
| 1449 | return ErrorWithSourcePos{Pos: node.fieldLabel().start(), Underlying: fmt.Errorf("%s: field has label %v, but proto3 should omit labels other than 'repeated'", scope, fld.GetLabel())} |
| 1450 | } |
| 1451 | if index, err := findOption(res, scope, fld.Options.GetUninterpretedOption(), "default"); err != nil { |
| 1452 | return err |
| 1453 | } else if index >= 0 { |
| 1454 | optNode := res.getOptionNode(fld.Options.GetUninterpretedOption()[index]) |
| 1455 | return ErrorWithSourcePos{Pos: optNode.getName().start(), Underlying: fmt.Errorf("%s: default values are not allowed in proto3", scope)} |
| 1456 | } |
| 1457 | } else { |
| 1458 | if fld.Label == nil && fld.OneofIndex == nil { |
| 1459 | return ErrorWithSourcePos{Pos: node.fieldName().start(), Underlying: fmt.Errorf("%s: field has no label, but proto2 must indicate 'optional' or 'required'", scope)} |
| 1460 | } |
| 1461 | if fld.GetExtendee() != "" && fld.Label != nil && fld.GetLabel() == dpb.FieldDescriptorProto_LABEL_REQUIRED { |
| 1462 | return ErrorWithSourcePos{Pos: node.fieldLabel().start(), Underlying: fmt.Errorf("%s: extension fields cannot be 'required'", scope)} |
| 1463 | } |
| 1464 | } |
| 1465 | |
| 1466 | // finally, set any missing label to optional |
| 1467 | if fld.Label == nil { |
| 1468 | fld.Label = dpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum() |
| 1469 | } |
| 1470 | return nil |
| 1471 | } |
| 1472 | |
| 1473 | func findOption(res *parseResult, scope string, opts []*dpb.UninterpretedOption, name string) (int, error) { |
| 1474 | found := -1 |
| 1475 | for i, opt := range opts { |
| 1476 | if len(opt.Name) != 1 { |
| 1477 | continue |
| 1478 | } |
| 1479 | if opt.Name[0].GetIsExtension() || opt.Name[0].GetNamePart() != name { |
| 1480 | continue |
| 1481 | } |
| 1482 | if found >= 0 { |
| 1483 | optNode := res.getOptionNode(opt) |
| 1484 | return -1, ErrorWithSourcePos{Pos: optNode.getName().start(), Underlying: fmt.Errorf("%s: option %s cannot be defined more than once", scope, name)} |
| 1485 | } |
| 1486 | found = i |
| 1487 | } |
| 1488 | return found, nil |
| 1489 | } |
| 1490 | |
| 1491 | func removeOption(uo []*dpb.UninterpretedOption, indexToRemove int) []*dpb.UninterpretedOption { |
| 1492 | if indexToRemove == 0 { |
| 1493 | return uo[1:] |
| 1494 | } else if int(indexToRemove) == len(uo)-1 { |
| 1495 | return uo[:len(uo)-1] |
| 1496 | } else { |
| 1497 | return append(uo[:indexToRemove], uo[indexToRemove+1:]...) |
| 1498 | } |
| 1499 | } |
| 1500 | |
| 1501 | type tagRange struct { |
| 1502 | start int32 |
| 1503 | end int32 |
| 1504 | node rangeDecl |
| 1505 | } |
| 1506 | |
| 1507 | type tagRanges []tagRange |
| 1508 | |
| 1509 | func (r tagRanges) Len() int { |
| 1510 | return len(r) |
| 1511 | } |
| 1512 | |
| 1513 | func (r tagRanges) Less(i, j int) bool { |
| 1514 | return r[i].start < r[j].start || |
| 1515 | (r[i].start == r[j].start && r[i].end < r[j].end) |
| 1516 | } |
| 1517 | |
| 1518 | func (r tagRanges) Swap(i, j int) { |
| 1519 | r[i], r[j] = r[j], r[i] |
| 1520 | } |