@@ -21,9 +21,10 @@ import (
2121const IgnoreText = "coverage-ignore"
2222
2323type Config struct {
24- Profiles []string
25- ExcludePaths []string
26- SourceDir string
24+ Profiles []string
25+ ExcludePaths []string
26+ SourceDir string
27+ ForceAnnotationComment bool
2728}
2829
2930func GenerateCoverageStats (cfg Config ) ([]Stats , error ) {
@@ -52,7 +53,7 @@ func GenerateCoverageStats(cfg Config) ([]Stats, error) {
5253 continue // this file is excluded
5354 }
5455
55- s , err := coverageForFile (profile , fi )
56+ s , err := coverageForFile (profile , fi , cfg . ForceAnnotationComment )
5657 if err != nil {
5758 return nil , err
5859 }
@@ -78,7 +79,7 @@ func GenerateCoverageStats(cfg Config) ([]Stats, error) {
7879 return fileStats , nil
7980}
8081
81- func coverageForFile (profile * cover.Profile , fi fileInfo ) (Stats , error ) {
82+ func coverageForFile (profile * cover.Profile , fi fileInfo , forceComment bool ) (Stats , error ) {
8283 source , err := os .ReadFile (fi .path )
8384 if err != nil { // coverage-ignore
8485 return Stats {}, fmt .Errorf ("failed reading file source [%s]: %w" , fi .path , err )
@@ -89,14 +90,19 @@ func coverageForFile(profile *cover.Profile, fi fileInfo) (Stats, error) {
8990 return Stats {}, err
9091 }
9192
92- annotations , err := findAnnotations (source )
93+ annotations , withoutComment , err := findAnnotations (source , forceComment )
9394 if err != nil { // coverage-ignore
9495 return Stats {}, err
9596 }
9697
9798 s := sumCoverage (profile , funcs , blocks , annotations )
9899 s .Name = fi .name
99100
101+ s .AnnotationsWithoutComments = make ([]int , len (withoutComment ))
102+ for i , extent := range withoutComment {
103+ s .AnnotationsWithoutComments [i ] = extent .StartLine
104+ }
105+
100106 return s , nil
101107}
102108
@@ -251,23 +257,43 @@ func findFilePathMatchingSearch(files *[]fileInfo, search string) string {
251257 return path
252258}
253259
254- func findAnnotations (source []byte ) ([]extent , error ) {
260+ // findAnnotations finds coverage-ignore annotations and checks for explanations
261+ func findAnnotations (source []byte , forceComment bool ) ([]extent , []extent , error ) {
255262 fset := token .NewFileSet ()
256263
257264 node , err := parser .ParseFile (fset , "" , source , parser .ParseComments )
258265 if err != nil {
259- return nil , fmt .Errorf ("can't parse comments: %w" , err )
266+ return nil , nil , fmt .Errorf ("can't parse comments: %w" , err )
260267 }
261268
262- var res []extent
269+ var validAnnotations []extent
270+
271+ var annotationsWithoutComment []extent
263272
264273 for _ , c := range node .Comments {
265- if strings .Contains (c .Text (), IgnoreText ) {
266- res = append (res , newExtent (fset , c ))
274+ if ! strings .Contains (c .Text (), IgnoreText ) {
275+ continue // does not have annotation continue to next comment
276+ }
277+
278+ if forceComment {
279+ if hasComment (c .Text ()) {
280+ validAnnotations = append (validAnnotations , newExtent (fset , c ))
281+ } else {
282+ annotationsWithoutComment = append (annotationsWithoutComment , newExtent (fset , c ))
283+ }
284+ } else {
285+ validAnnotations = append (validAnnotations , newExtent (fset , c ))
267286 }
268287 }
269288
270- return res , nil
289+ return validAnnotations , annotationsWithoutComment , nil
290+ }
291+
292+ // hasComment checks if the coverage-ignore annotation has an explanation comment
293+ func hasComment (text string ) bool {
294+ // coverage-ignore should be followed by additional text to be considered an explanation
295+ trimmedComment := strings .TrimSpace (text )
296+ return len (trimmedComment ) > len (IgnoreText )
271297}
272298
273299func findFuncsAndBlocks (source []byte ) ([]extent , []extent , error ) {
0 commit comments