@@ -549,7 +549,7 @@ func (p *Package) extractEmbedLines(addError func(error)) {
549549 }
550550
551551 // Look for //go:embed comments.
552- var allPatterns []string
552+ var allPatterns []embedPattern
553553 for _ , comment := range doc .List {
554554 if comment .Text != "//go:embed" && ! strings .HasPrefix (comment .Text , "//go:embed " ) {
555555 continue
@@ -577,21 +577,7 @@ func (p *Package) extractEmbedLines(addError func(error)) {
577577 })
578578 continue
579579 }
580- for _ , pattern := range patterns {
581- // Check that the pattern is well-formed.
582- // It must be valid: the Go toolchain has already
583- // checked for invalid patterns. But let's check
584- // anyway to be sure.
585- if _ , err := path .Match (pattern , "" ); err != nil {
586- addError (types.Error {
587- Fset : p .program .fset ,
588- Pos : comment .Pos (),
589- Msg : "invalid pattern syntax" ,
590- })
591- continue
592- }
593- allPatterns = append (allPatterns , pattern )
594- }
580+ allPatterns = append (allPatterns , patterns ... )
595581 }
596582
597583 if len (allPatterns ) != 0 {
@@ -624,8 +610,8 @@ func (p *Package) extractEmbedLines(addError func(error)) {
624610 // Match all //go:embed patterns against the embed files
625611 // provided by `go list`.
626612 for _ , name := range p .EmbedFiles {
627- for _ , pattern := range allPatterns {
628- if matchPattern ( pattern , name ) {
613+ for _ , ep := range allPatterns {
614+ if ep . Match ( name ) {
629615 p .EmbedGlobals [globalName ] = append (p .EmbedGlobals [globalName ], & EmbedFile {
630616 Name : name ,
631617 NeedsData : byteSlice ,
@@ -641,13 +627,37 @@ func (p *Package) extractEmbedLines(addError func(error)) {
641627 }
642628}
643629
644- // matchPattern returns true if (and only if) the given pattern would match the
645- // filename. The pattern could also match a parent directory of name, in which
646- // case hidden files do not match.
647- func matchPattern (pattern , name string ) bool {
630+ // embedPattern represents a valid //go:embed pattern.
631+ // See: https://pkg.go.dev/embed#hdr-Directives
632+ type embedPattern struct {
633+ pattern string // glob pattern without "all:" prefix
634+ includeHidden bool // true if "all:" prefix was present
635+ }
636+
637+ // newEmbedPattern parses and validates a //go:embed pattern.
638+ // The "all:" prefix (if present) is stripped and reflected in includeHidden.
639+ // Returns an error if the pattern syntax is invalid.
640+ func newEmbedPattern (s string ) (embedPattern , error ) {
641+ ep := embedPattern {pattern : s }
642+ if strings .HasPrefix (s , "all:" ) {
643+ ep .pattern = s [4 :]
644+ ep .includeHidden = true
645+ }
646+ if _ , err := path .Match (ep .pattern , "" ); err != nil {
647+ return embedPattern {}, err
648+ }
649+ return ep , nil
650+ }
651+
652+ // Match returns true if (and only if) the pattern matches the given filename.
653+ // The pattern could also match a parent directory of name, in which case hidden
654+ // files do not match (unless includeHidden is true).
655+ func (ep embedPattern ) Match (name string ) bool {
656+ pattern := ep .pattern
657+ includeHidden := ep .includeHidden
658+
648659 // Match this file.
649- matched , _ := path .Match (pattern , name )
650- if matched {
660+ if matched , _ := path .Match (pattern , name ); matched {
651661 return true
652662 }
653663
@@ -661,17 +671,20 @@ func matchPattern(pattern, name string) bool {
661671 dir = path .Clean (dir )
662672 if matched , _ := path .Match (pattern , dir ); matched {
663673 // Pattern matches the directory.
664- suffix := name [len (dir ):]
665- if strings .Contains (suffix , "/_" ) || strings .Contains (suffix , "/." ) {
666- // Pattern matches a hidden file.
667- // Hidden files are included when listed directly as a
668- // pattern, but not when they are part of a directory tree.
669- // Source:
670- // > If a pattern names a directory, all files in the
671- // > subtree rooted at that directory are embedded
672- // > (recursively), except that files with names beginning
673- // > with ‘.’ or ‘_’ are excluded.
674- return false
674+ if ! includeHidden {
675+ suffix := name [len (dir ):]
676+ if strings .Contains (suffix , "/_" ) || strings .Contains (suffix , "/." ) {
677+ // Pattern matches a hidden file.
678+ // Hidden files are included when listed directly as a
679+ // pattern, or when the "all:" prefix is used, but not
680+ // when they are part of a directory tree without "all:".
681+ // Source:
682+ // > If a pattern names a directory, all files in the
683+ // > subtree rooted at that directory are embedded
684+ // > (recursively), except that files with names beginning
685+ // > with ‘.’ or ‘_’ are excluded.
686+ return false
687+ }
675688 }
676689 return true
677690 }
@@ -680,11 +693,12 @@ func matchPattern(pattern, name string) bool {
680693
681694// parseGoEmbed is like strings.Fields but for a //go:embed line. It parses
682695// regular fields and quoted fields (that may contain spaces).
683- func (p * Package ) parseGoEmbed (args string , pos token.Pos ) (patterns []string , err error ) {
696+ func (p * Package ) parseGoEmbed (args string , pos token.Pos ) (patterns []embedPattern , err error ) {
684697 args = strings .TrimSpace (args )
685698 initialLen := len (args )
686699 for args != "" {
687700 patternPos := pos + token .Pos (initialLen - len (args ))
701+ var pattern string
688702 switch args [0 ] {
689703 case '`' , '"' , '\\' :
690704 // Parse the next pattern using the Go scanner.
@@ -703,8 +717,7 @@ func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, e
703717 Msg : "invalid quoted string in //go:embed" ,
704718 }
705719 }
706- pattern := constant .StringVal (constant .MakeFromLiteral (lit , tok , 0 ))
707- patterns = append (patterns , pattern )
720+ pattern = constant .StringVal (constant .MakeFromLiteral (lit , tok , 0 ))
708721 args = strings .TrimLeftFunc (args [len (lit ):], unicode .IsSpace )
709722 default :
710723 // The value is just a regular value.
@@ -713,17 +726,18 @@ func (p *Package) parseGoEmbed(args string, pos token.Pos) (patterns []string, e
713726 if index < 0 {
714727 index = len (args )
715728 }
716- pattern := args [:index ]
717- patterns = append (patterns , pattern )
729+ pattern = args [:index ]
718730 args = strings .TrimLeftFunc (args [len (pattern ):], unicode .IsSpace )
719731 }
720- if _ , err := path .Match (patterns [len (patterns )- 1 ], "" ); err != nil {
732+ ep , err := newEmbedPattern (pattern )
733+ if err != nil {
721734 return nil , types.Error {
722735 Fset : p .program .fset ,
723736 Pos : patternPos ,
724737 Msg : "invalid pattern syntax" ,
725738 }
726739 }
740+ patterns = append (patterns , ep )
727741 }
728742 return patterns , nil
729743}
0 commit comments