1 /* 2 Copyright 2020 Google LLC 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 https://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Warnings about visibility of .bzl files 18 19 package warn 20 21 import ( 22 "fmt" 23 "regexp" 24 "strings" 25 26 "github.com/bazelbuild/buildtools/build" 27 ) 28 29 var internalDirectory = regexp.MustCompile("/(internal|private)[/:]") 30 31 func bzlVisibilityWarning(f *build.File) []*LinterFinding { 32 var findings []*LinterFinding 33 34 if f.WorkspaceRoot == "" { 35 // Empty workspace root means buildifier doesn't know the location of 36 // the file relative to the workspace directory and can't warn about .bzl 37 // file visibility correctly. 38 return findings 39 } 40 41 for _, stmt := range f.Stmt { 42 load, ok := stmt.(*build.LoadStmt) 43 if !ok || load.Module == nil { 44 continue 45 } 46 47 // A load statement may use a fully qualified module name (including a 48 // repository name). Buildifier should check if the repository name refers 49 // to the current repository, but because it doesn't know the current 50 // repository name it's better to assume that it matches the repository name 51 // in the load statement: this way it may miss some usages of private .bzl 52 // files that aren't supposed to be visible, but won't show false-positive 53 // warnings in case the private file is actually allowed to be used. 54 module := load.Module.Value 55 if strings.HasPrefix(module, "@") { 56 if chunks := strings.SplitN(module, "//", 2); len(chunks) == 2 { 57 module = "//" + chunks[1] 58 } 59 } 60 61 path := f.CanonicalPath() // Canonical name of the file 62 chunks := internalDirectory.Split(module, 2) 63 if len(chunks) < 2 { 64 continue 65 } 66 67 if strings.HasPrefix(path, chunks[0]) || 68 strings.HasPrefix(strings.Replace(path, "/javatests/", "/java/", 1), chunks[0]) { 69 continue 70 } 71 72 findings = append(findings, makeLinterFinding( 73 load.Module, 74 fmt.Sprintf("Module %q can only be loaded from files located inside %q, not from %q.", load.Module.Value, chunks[0], path))) 75 } 76 77 return findings 78 } 79