github.com/googleapis/api-linter@v1.65.2/locations/locations.go (about) 1 // Copyright 2019 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package locations provides functions to get the location of a particular part 16 // of a descriptor, allowing Problems to be attached to just a descriptor's 17 // name, type, etc.. This allows for better auto-replacement functionality in 18 // code review tools. 19 // 20 // All functions in this package accept a descriptor and return a 21 // protobuf SourceCodeInfo_Location object, which can be passed directly 22 // to the Location property on Problem. 23 package locations 24 25 import ( 26 "github.com/jhump/protoreflect/desc" 27 dpb "google.golang.org/protobuf/types/descriptorpb" 28 ) 29 30 // pathLocation returns the precise location for a given descriptor and path. 31 // It combines the path of the descriptor itself with any path provided appended. 32 func pathLocation(d desc.Descriptor, path ...int) *dpb.SourceCodeInfo_Location { 33 fullPath := d.GetSourceInfo().GetPath() 34 for _, i := range path { 35 fullPath = append(fullPath, int32(i)) 36 } 37 return sourceInfoRegistry.sourceInfo(d.GetFile()).findLocation(fullPath) 38 } 39 40 type sourceInfo map[string]*dpb.SourceCodeInfo_Location 41 42 // findLocation returns the Location for a given path. 43 func (si sourceInfo) findLocation(path []int32) *dpb.SourceCodeInfo_Location { 44 // If the path exists in the source info registry, return that object. 45 if loc, ok := si[strPath(path)]; ok { 46 return loc 47 } 48 49 // We could not find the path; return nil. 50 return nil 51 } 52 53 // The source map registry is a singleton that computes a source map for 54 // any file descriptor that it is given, but then caches it to avoid computing 55 // the source map for the same file descriptors over and over. 56 type sourceInfoRegistryType map[*desc.FileDescriptor]sourceInfo 57 58 // Each location has a path defined as an []int32, but we can not 59 // use slices as keys, so compile them into a string. 60 func strPath(segments []int32) (p string) { 61 for i, segment := range segments { 62 if i > 0 { 63 p += "," 64 } 65 p += string(segment) 66 } 67 return 68 } 69 70 // sourceInfo compiles the source info object for a given file descriptor. 71 // It also caches this into a registry, so subsequent calls using the same 72 // descriptor will return the same object. 73 func (sir sourceInfoRegistryType) sourceInfo(fd *desc.FileDescriptor) sourceInfo { 74 answer, ok := sir[fd] 75 if !ok { 76 answer = sourceInfo{} 77 78 // This file descriptor does not yet have a source info map. 79 // Compile one. 80 for _, loc := range fd.AsFileDescriptorProto().GetSourceCodeInfo().GetLocation() { 81 answer[strPath(loc.Path)] = loc 82 } 83 84 // Now that we calculated all of this, cache it on the registry so it 85 // does not need to be calculated again. 86 sir[fd] = answer 87 } 88 return answer 89 } 90 91 var sourceInfoRegistry = sourceInfoRegistryType{}