github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/tools/checklocks/facts.go (about)

     1  // Copyright 2020 The gVisor Authors.
     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  //     http://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 checklocks
    16  
    17  import (
    18  	"fmt"
    19  	"go/ast"
    20  	"go/token"
    21  	"go/types"
    22  	"regexp"
    23  	"strings"
    24  
    25  	"golang.org/x/tools/go/ssa"
    26  )
    27  
    28  // atomicAlignment is saved per type.
    29  //
    30  // This represents the alignment required for the type, which may
    31  // be implied and imposed by other types within the aggregate type.
    32  type atomicAlignment int
    33  
    34  // AFact implements analysis.Fact.AFact.
    35  func (*atomicAlignment) AFact() {}
    36  
    37  // atomicDisposition is saved per field.
    38  //
    39  // This represents how the field must be accessed. It must either
    40  // be non-atomic (default), atomic or ignored.
    41  type atomicDisposition int
    42  
    43  const (
    44  	atomicDisallow atomicDisposition = iota
    45  	atomicIgnore
    46  	atomicRequired
    47  )
    48  
    49  // fieldList is a simple list of fields, used in two types below.
    50  //
    51  // Note that the integers in this list refer to one of two things:
    52  // - A positive integer refers to a field index in a struct.
    53  // - A negative integer refers to a field index in a struct, where
    54  //   that field is a pointer and must be subsequently resolved.
    55  type fieldList []int
    56  
    57  // resolvedValue is an ssa.Value with additional fields.
    58  //
    59  // This can be resolved to a string as part of a lock state.
    60  type resolvedValue struct {
    61  	value     ssa.Value
    62  	valid     bool
    63  	fieldList []int
    64  }
    65  
    66  // findExtract finds a relevant extract. This must exist within the referrers
    67  // to the call object. If this doesn't then the object which is locked is never
    68  // consumed, and we should consider this a bug.
    69  func findExtract(v ssa.Value, index int) (ssa.Value, bool) {
    70  	if refs := v.Referrers(); refs != nil {
    71  		for _, inst := range *refs {
    72  			if x, ok := inst.(*ssa.Extract); ok && x.Tuple == v && x.Index == index {
    73  				return inst.(ssa.Value), true
    74  			}
    75  		}
    76  	}
    77  	return nil, false
    78  }
    79  
    80  // resolve resolves the given field list.
    81  func (fl fieldList) resolve(v ssa.Value) (rv resolvedValue) {
    82  	return resolvedValue{
    83  		value:     v,
    84  		fieldList: fl,
    85  		valid:     true,
    86  	}
    87  }
    88  
    89  // valueAsString returns a string representing this value.
    90  //
    91  // This must align with how the string is generated in valueAsString.
    92  func (rv resolvedValue) valueAsString(ls *lockState) string {
    93  	typ := rv.value.Type()
    94  	s := ls.valueAsString(rv.value)
    95  	for i, fieldNumber := range rv.fieldList {
    96  		switch {
    97  		case fieldNumber > 0:
    98  			field, ok := findField(typ, fieldNumber-1)
    99  			if !ok {
   100  				// This can't be resolved, return for debugging.
   101  				return fmt.Sprintf("{%s+%v}", s, rv.fieldList[i:])
   102  			}
   103  			s = fmt.Sprintf("&(%s.%s)", s, field.Name())
   104  			typ = field.Type()
   105  		case fieldNumber < 1:
   106  			field, ok := findField(typ, (-fieldNumber)-1)
   107  			if !ok {
   108  				// See above.
   109  				return fmt.Sprintf("{%s+%v}", s, rv.fieldList[i:])
   110  			}
   111  			s = fmt.Sprintf("*(&(%s.%s))", s, field.Name())
   112  			typ = field.Type()
   113  		}
   114  	}
   115  	return s
   116  }
   117  
   118  // lockFieldFacts apply on every struct field.
   119  type lockFieldFacts struct {
   120  	// IsMutex is true if the field is of type sync.Mutex.
   121  	IsMutex bool
   122  
   123  	// IsRWMutex is true if the field is of type sync.RWMutex.
   124  	IsRWMutex bool
   125  
   126  	// IsPointer indicates if the field is a pointer.
   127  	IsPointer bool
   128  
   129  	// FieldNumber is the number of this field in the struct.
   130  	FieldNumber int
   131  }
   132  
   133  // AFact implements analysis.Fact.AFact.
   134  func (*lockFieldFacts) AFact() {}
   135  
   136  // lockGuardFacts contains guard information.
   137  type lockGuardFacts struct {
   138  	// GuardedBy is the set of locks that are guarding this field. The key
   139  	// is the original annotation value, and the field list is the object
   140  	// traversal path.
   141  	GuardedBy map[string]fieldList
   142  
   143  	// AtomicDisposition is the disposition for this field. Note that this
   144  	// can affect the interpretation of the GuardedBy field above, see the
   145  	// relevant comment.
   146  	AtomicDisposition atomicDisposition
   147  }
   148  
   149  // AFact implements analysis.Fact.AFact.
   150  func (*lockGuardFacts) AFact() {}
   151  
   152  // functionGuard is used by lockFunctionFacts, below.
   153  type functionGuard struct {
   154  	// ParameterNumber is the index of the object that contains the
   155  	// guarding mutex. From this parameter, a walk is performed
   156  	// subsequently using the resolve method.
   157  	//
   158  	// Note that is ParameterNumber is beyond the size of parameters, then
   159  	// it may return to a return value. This applies only for the Acquires
   160  	// relation below.
   161  	ParameterNumber int
   162  
   163  	// NeedsExtract is used in the case of a return value, and indicates
   164  	// that the field must be extracted from a tuple.
   165  	NeedsExtract bool
   166  
   167  	// FieldList is the traversal path to the object.
   168  	FieldList fieldList
   169  }
   170  
   171  // resolveReturn resolves a return value.
   172  //
   173  // Precondition: rv is either an ssa.Value, or an *ssa.Return.
   174  func (fg *functionGuard) resolveReturn(rv interface{}, args int) resolvedValue {
   175  	if rv == nil {
   176  		// For defers and other objects, this may be nil. This is
   177  		// handled in state.go in the actual lock checking logic.
   178  		return resolvedValue{
   179  			value: nil,
   180  			valid: false,
   181  		}
   182  	}
   183  	index := fg.ParameterNumber - args
   184  	// If this is a *ssa.Return object, i.e. we are analyzing the function
   185  	// and not the call site, then we can just pull the result directly.
   186  	if r, ok := rv.(*ssa.Return); ok {
   187  		return fg.FieldList.resolve(r.Results[index])
   188  	}
   189  	if fg.NeedsExtract {
   190  		// Resolve on the extracted field, this is necessary if the
   191  		// type here is not an explicit return. Note that rv must be an
   192  		// ssa.Value, since it is not an *ssa.Return.
   193  		v, ok := findExtract(rv.(ssa.Value), index)
   194  		if !ok {
   195  			return resolvedValue{
   196  				value: v,
   197  				valid: false,
   198  			}
   199  		}
   200  		return fg.FieldList.resolve(v)
   201  	}
   202  	if index != 0 {
   203  		// This should not happen, NeedsExtract should always be set.
   204  		panic("NeedsExtract is false, but return value index is non-zero")
   205  	}
   206  	// Resolve on the single return.
   207  	return fg.FieldList.resolve(rv.(ssa.Value))
   208  }
   209  
   210  // resolveStatic returns an ssa.Value representing the given field.
   211  //
   212  // Precondition: per resolveReturn.
   213  func (fg *functionGuard) resolveStatic(fn *ssa.Function, rv interface{}) resolvedValue {
   214  	if fg.ParameterNumber >= len(fn.Params) {
   215  		return fg.resolveReturn(rv, len(fn.Params))
   216  	}
   217  	return fg.FieldList.resolve(fn.Params[fg.ParameterNumber])
   218  }
   219  
   220  // resolveCall returns an ssa.Value representing the given field.
   221  func (fg *functionGuard) resolveCall(args []ssa.Value, rv ssa.Value) resolvedValue {
   222  	if fg.ParameterNumber >= len(args) {
   223  		return fg.resolveReturn(rv, len(args))
   224  	}
   225  	return fg.FieldList.resolve(args[fg.ParameterNumber])
   226  }
   227  
   228  // lockFunctionFacts apply on every method.
   229  type lockFunctionFacts struct {
   230  	// HeldOnEntry tracks the names and number of parameter (including receiver)
   231  	// lockFuncfields that guard calls to this function.
   232  	//
   233  	// The key is the name specified in the checklocks annotation. e.g given
   234  	// the following code:
   235  	//
   236  	// ```
   237  	// type A struct {
   238  	//	mu sync.Mutex
   239  	//	a int
   240  	// }
   241  	//
   242  	// // +checklocks:a.mu
   243  	// func xyz(a *A) {..}
   244  	// ```
   245  	//
   246  	// '`+checklocks:a.mu' will result in an entry in this map as shown below.
   247  	// HeldOnEntry: {"a.mu" => {ParameterNumber: 0, FieldNumbers: {0}}
   248  	//
   249  	// Unlikely lockFieldFacts, there is no atomic interpretation.
   250  	HeldOnEntry map[string]functionGuard
   251  
   252  	// HeldOnExit tracks the locks that are expected to be held on exit.
   253  	HeldOnExit map[string]functionGuard
   254  
   255  	// Ignore means this function has local analysis ignores.
   256  	//
   257  	// This is not used outside the local package.
   258  	Ignore bool
   259  }
   260  
   261  // AFact implements analysis.Fact.AFact.
   262  func (*lockFunctionFacts) AFact() {}
   263  
   264  // checkGuard validates the guardName.
   265  func (lff *lockFunctionFacts) checkGuard(pc *passContext, d *ast.FuncDecl, guardName string, allowReturn bool) (functionGuard, bool) {
   266  	if _, ok := lff.HeldOnEntry[guardName]; ok {
   267  		pc.maybeFail(d.Pos(), "annotation %s specified more than once, already required", guardName)
   268  		return functionGuard{}, false
   269  	}
   270  	if _, ok := lff.HeldOnExit[guardName]; ok {
   271  		pc.maybeFail(d.Pos(), "annotation %s specified more than once, already acquired", guardName)
   272  		return functionGuard{}, false
   273  	}
   274  	fg, ok := pc.findFunctionGuard(d, guardName, allowReturn)
   275  	return fg, ok
   276  }
   277  
   278  // addGuardedBy adds a field to both HeldOnEntry and HeldOnExit.
   279  func (lff *lockFunctionFacts) addGuardedBy(pc *passContext, d *ast.FuncDecl, guardName string) {
   280  	if fg, ok := lff.checkGuard(pc, d, guardName, false /* allowReturn */); ok {
   281  		if lff.HeldOnEntry == nil {
   282  			lff.HeldOnEntry = make(map[string]functionGuard)
   283  		}
   284  		if lff.HeldOnExit == nil {
   285  			lff.HeldOnExit = make(map[string]functionGuard)
   286  		}
   287  		lff.HeldOnEntry[guardName] = fg
   288  		lff.HeldOnExit[guardName] = fg
   289  	}
   290  }
   291  
   292  // addAcquires adds a field to HeldOnExit.
   293  func (lff *lockFunctionFacts) addAcquires(pc *passContext, d *ast.FuncDecl, guardName string) {
   294  	if fg, ok := lff.checkGuard(pc, d, guardName, true /* allowReturn */); ok {
   295  		if lff.HeldOnExit == nil {
   296  			lff.HeldOnExit = make(map[string]functionGuard)
   297  		}
   298  		lff.HeldOnExit[guardName] = fg
   299  	}
   300  }
   301  
   302  // addReleases adds a field to HeldOnEntry.
   303  func (lff *lockFunctionFacts) addReleases(pc *passContext, d *ast.FuncDecl, guardName string) {
   304  	if fg, ok := lff.checkGuard(pc, d, guardName, false /* allowReturn */); ok {
   305  		if lff.HeldOnEntry == nil {
   306  			lff.HeldOnEntry = make(map[string]functionGuard)
   307  		}
   308  		lff.HeldOnEntry[guardName] = fg
   309  	}
   310  }
   311  
   312  // fieldListFor returns the fieldList for the given object.
   313  func (pc *passContext) fieldListFor(pos token.Pos, fieldObj types.Object, index int, fieldName string, checkMutex bool) (int, bool) {
   314  	var lff lockFieldFacts
   315  	if !pc.pass.ImportObjectFact(fieldObj, &lff) {
   316  		// This should not happen: we export facts for all fields.
   317  		panic(fmt.Sprintf("no lockFieldFacts available for field %s", fieldName))
   318  	}
   319  	// Check that it is indeed a mutex.
   320  	if checkMutex && !lff.IsMutex && !lff.IsRWMutex {
   321  		pc.maybeFail(pos, "field %s is not a mutex or an rwmutex", fieldName)
   322  		return 0, false
   323  	}
   324  	// Return the resolution path.
   325  	if lff.IsPointer {
   326  		return -(index + 1), true
   327  	}
   328  	return (index + 1), true
   329  }
   330  
   331  // resolveOneField resolves a field in a single struct.
   332  func (pc *passContext) resolveOneField(pos token.Pos, structType *types.Struct, fieldName string, checkMutex bool) (fl fieldList, fieldObj types.Object, ok bool) {
   333  	// Scan to match the next field.
   334  	for i := 0; i < structType.NumFields(); i++ {
   335  		fieldObj := structType.Field(i)
   336  		if fieldObj.Name() != fieldName {
   337  			continue
   338  		}
   339  		flOne, ok := pc.fieldListFor(pos, fieldObj, i, fieldName, checkMutex)
   340  		if !ok {
   341  			return nil, nil, false
   342  		}
   343  		fl = append(fl, flOne)
   344  		return fl, fieldObj, true
   345  	}
   346  	// Is this an embed?
   347  	for i := 0; i < structType.NumFields(); i++ {
   348  		fieldObj := structType.Field(i)
   349  		if !fieldObj.Embedded() {
   350  			continue
   351  		}
   352  		// Is this an embedded struct?
   353  		structType, ok := resolveStruct(fieldObj.Type())
   354  		if !ok {
   355  			continue
   356  		}
   357  		// Need to check that there is a resolution path. If there is
   358  		// no resolution path that's not a failure: we just continue
   359  		// scanning the next embed to find a match.
   360  		flEmbed, okEmbed := pc.fieldListFor(pos, fieldObj, i, fieldName, false)
   361  		flCont, fieldObjCont, okCont := pc.resolveOneField(pos, structType, fieldName, checkMutex)
   362  		if okEmbed && okCont {
   363  			fl = append(fl, flEmbed)
   364  			fl = append(fl, flCont...)
   365  			return fl, fieldObjCont, true
   366  		}
   367  	}
   368  	pc.maybeFail(pos, "field %s does not exist", fieldName)
   369  	return nil, nil, false
   370  }
   371  
   372  // resolveField resolves a set of fields given a string, such a 'a.b.c'.
   373  //
   374  // Note that this checks that the final element is a mutex of some kind, and
   375  // will fail appropriately.
   376  func (pc *passContext) resolveField(pos token.Pos, structType *types.Struct, parts []string) (fl fieldList, ok bool) {
   377  	for partNumber, fieldName := range parts {
   378  		flOne, fieldObj, ok := pc.resolveOneField(pos, structType, fieldName, partNumber >= len(parts)-1 /* checkMutex */)
   379  		if !ok {
   380  			// Error already reported.
   381  			return nil, false
   382  		}
   383  		fl = append(fl, flOne...)
   384  		if partNumber < len(parts)-1 {
   385  			// Traverse to the next type.
   386  			structType, ok = resolveStruct(fieldObj.Type())
   387  			if !ok {
   388  				pc.maybeFail(pos, "invalid intermediate field %s", fieldName)
   389  				return fl, false
   390  			}
   391  		}
   392  	}
   393  	return fl, true
   394  }
   395  
   396  var (
   397  	mutexRE   = regexp.MustCompile("((.*/)|^)sync.(CrossGoroutineMutex|Mutex)")
   398  	rwMutexRE = regexp.MustCompile("((.*/)|^)sync.(CrossGoroutineRWMutex|RWMutex)")
   399  )
   400  
   401  // exportLockFieldFacts finds all struct fields that are mutexes, and ensures
   402  // that they are annotated approperly.
   403  //
   404  // This information is consumed subsequently by exportLockGuardFacts, and this
   405  // function must be called first on all structures.
   406  func (pc *passContext) exportLockFieldFacts(ts *ast.TypeSpec, ss *ast.StructType) {
   407  	structType := pc.pass.TypesInfo.TypeOf(ts.Name).Underlying().(*types.Struct)
   408  	for i := range ss.Fields.List {
   409  		lff := &lockFieldFacts{
   410  			FieldNumber: i,
   411  		}
   412  		// We use HasSuffix below because fieldType can be fully
   413  		// qualified with the package name eg for the gvisor sync
   414  		// package mutex fields have the type:
   415  		//	"<package path>/sync/sync.Mutex"
   416  		fieldObj := structType.Field(i)
   417  		s := fieldObj.Type().String()
   418  		switch {
   419  		case mutexRE.MatchString(s):
   420  			lff.IsMutex = true
   421  		case rwMutexRE.MatchString(s):
   422  			lff.IsRWMutex = true
   423  		}
   424  		// Save whether this is a pointer.
   425  		_, lff.IsPointer = fieldObj.Type().Underlying().(*types.Pointer)
   426  		// We must always export the lockFieldFacts, since traversal
   427  		// can take place along any object in the struct.
   428  		pc.pass.ExportObjectFact(fieldObj, lff)
   429  	}
   430  }
   431  
   432  // exportLockGuardFacts finds all relevant guard information for structures.
   433  //
   434  // This function requires exportLockFieldFacts be called first on all
   435  // structures.
   436  func (pc *passContext) exportLockGuardFacts(ts *ast.TypeSpec, ss *ast.StructType) {
   437  	structType := pc.pass.TypesInfo.TypeOf(ts.Name).Underlying().(*types.Struct)
   438  	for i, field := range ss.Fields.List {
   439  		if field.Doc == nil {
   440  			continue
   441  		}
   442  		var (
   443  			lff lockFieldFacts
   444  			lgf lockGuardFacts
   445  		)
   446  		pc.pass.ImportObjectFact(structType.Field(i), &lff)
   447  		fieldObj := structType.Field(i)
   448  		for _, l := range field.Doc.List {
   449  			pc.extractAnnotations(l.Text, map[string]func(string){
   450  				checkAtomicAnnotation: func(string) {
   451  					switch lgf.AtomicDisposition {
   452  					case atomicRequired:
   453  						pc.maybeFail(fieldObj.Pos(), "annotation is redundant, already atomic required")
   454  					case atomicIgnore:
   455  						pc.maybeFail(fieldObj.Pos(), "annotation is contradictory, already atomic ignored")
   456  					}
   457  					lgf.AtomicDisposition = atomicRequired
   458  				},
   459  				checkLocksIgnore: func(string) {
   460  					switch lgf.AtomicDisposition {
   461  					case atomicIgnore:
   462  						pc.maybeFail(fieldObj.Pos(), "annotation is redundant, already atomic ignored")
   463  					case atomicRequired:
   464  						pc.maybeFail(fieldObj.Pos(), "annotation is contradictory, already atomic required")
   465  					}
   466  					lgf.AtomicDisposition = atomicIgnore
   467  				},
   468  				checkLocksAnnotation: func(guardName string) {
   469  					// Check for a duplicate annotation.
   470  					if _, ok := lgf.GuardedBy[guardName]; ok {
   471  						pc.maybeFail(fieldObj.Pos(), "annotation %s specified more than once", guardName)
   472  						return
   473  					}
   474  					fl, ok := pc.resolveField(fieldObj.Pos(), structType, strings.Split(guardName, "."))
   475  					if ok {
   476  						// If we successfully resolved
   477  						// the field, then save it.
   478  						if lgf.GuardedBy == nil {
   479  							lgf.GuardedBy = make(map[string]fieldList)
   480  						}
   481  						lgf.GuardedBy[guardName] = fl
   482  					}
   483  				},
   484  			})
   485  		}
   486  		// Save only if there is something meaningful.
   487  		if len(lgf.GuardedBy) > 0 || lgf.AtomicDisposition != atomicDisallow {
   488  			pc.pass.ExportObjectFact(structType.Field(i), &lgf)
   489  		}
   490  	}
   491  }
   492  
   493  // countFields gives an accurate field count, according for unnamed arguments
   494  // and return values and the compact identifier format.
   495  func countFields(fl []*ast.Field) (count int) {
   496  	for _, field := range fl {
   497  		if len(field.Names) == 0 {
   498  			count++
   499  			continue
   500  		}
   501  		count += len(field.Names)
   502  	}
   503  	return
   504  }
   505  
   506  // matchFieldList attempts to match the given field.
   507  func (pc *passContext) matchFieldList(pos token.Pos, fl []*ast.Field, guardName string) (functionGuard, bool) {
   508  	parts := strings.Split(guardName, ".")
   509  	parameterName := parts[0]
   510  	parameterNumber := 0
   511  	for _, field := range fl {
   512  		// See countFields, above.
   513  		if len(field.Names) == 0 {
   514  			parameterNumber++
   515  			continue
   516  		}
   517  		for _, name := range field.Names {
   518  			if name.Name != parameterName {
   519  				parameterNumber++
   520  				continue
   521  			}
   522  			ptrType, ok := pc.pass.TypesInfo.TypeOf(field.Type).Underlying().(*types.Pointer)
   523  			if !ok {
   524  				// Since mutexes cannot be copied we only care
   525  				// about parameters that are pointer types when
   526  				// checking for guards.
   527  				pc.maybeFail(pos, "parameter name %s does not refer to a pointer type", parameterName)
   528  				return functionGuard{}, false
   529  			}
   530  			structType, ok := ptrType.Elem().Underlying().(*types.Struct)
   531  			if !ok {
   532  				// Fields can only be in named structures.
   533  				pc.maybeFail(pos, "parameter name %s does not refer to a pointer to a struct", parameterName)
   534  				return functionGuard{}, false
   535  			}
   536  			fg := functionGuard{
   537  				ParameterNumber: parameterNumber,
   538  			}
   539  			fl, ok := pc.resolveField(pos, structType, parts[1:])
   540  			fg.FieldList = fl
   541  			return fg, ok // If ok is false, already failed.
   542  		}
   543  	}
   544  	return functionGuard{}, false
   545  }
   546  
   547  // findFunctionGuard identifies the parameter number and field number for a
   548  // particular string of the 'a.b'.
   549  //
   550  // This function will report any errors directly.
   551  func (pc *passContext) findFunctionGuard(d *ast.FuncDecl, guardName string, allowReturn bool) (functionGuard, bool) {
   552  	var (
   553  		parameterList []*ast.Field
   554  		returnList    []*ast.Field
   555  	)
   556  	if d.Recv != nil {
   557  		parameterList = append(parameterList, d.Recv.List...)
   558  	}
   559  	if d.Type.Params != nil {
   560  		parameterList = append(parameterList, d.Type.Params.List...)
   561  	}
   562  	if fg, ok := pc.matchFieldList(d.Pos(), parameterList, guardName); ok {
   563  		return fg, ok
   564  	}
   565  	if allowReturn {
   566  		if d.Type.Results != nil {
   567  			returnList = append(returnList, d.Type.Results.List...)
   568  		}
   569  		if fg, ok := pc.matchFieldList(d.Pos(), returnList, guardName); ok {
   570  			// Fix this up to apply to the return value, as noted
   571  			// in fg.ParameterNumber. For the ssa analysis, we must
   572  			// record whether this has multiple results, since
   573  			// *ssa.Call indicates: "The Call instruction yields
   574  			// the function result if there is exactly one.
   575  			// Otherwise it returns a tuple, the components of
   576  			// which are accessed via Extract."
   577  			fg.ParameterNumber += countFields(parameterList)
   578  			fg.NeedsExtract = countFields(returnList) > 1
   579  			return fg, ok
   580  		}
   581  	}
   582  	// We never saw a matching parameter.
   583  	pc.maybeFail(d.Pos(), "annotation %s does not have a matching parameter", guardName)
   584  	return functionGuard{}, false
   585  }
   586  
   587  // exportFunctionFacts exports relevant function findings.
   588  func (pc *passContext) exportFunctionFacts(d *ast.FuncDecl) {
   589  	if d.Doc == nil || d.Doc.List == nil {
   590  		return
   591  	}
   592  	var lff lockFunctionFacts
   593  	for _, l := range d.Doc.List {
   594  		pc.extractAnnotations(l.Text, map[string]func(string){
   595  			checkLocksIgnore: func(string) {
   596  				// Note that this applies to all atomic
   597  				// analysis as well. There is no provided way
   598  				// to selectively ignore only lock analysis or
   599  				// atomic analysis, as we expect this use to be
   600  				// extremely rare.
   601  				lff.Ignore = true
   602  			},
   603  			checkLocksAnnotation: func(guardName string) { lff.addGuardedBy(pc, d, guardName) },
   604  			checkLocksAcquires:   func(guardName string) { lff.addAcquires(pc, d, guardName) },
   605  			checkLocksReleases:   func(guardName string) { lff.addReleases(pc, d, guardName) },
   606  		})
   607  	}
   608  
   609  	// Export the function facts if there is anything to save.
   610  	if lff.Ignore || len(lff.HeldOnEntry) > 0 || len(lff.HeldOnExit) > 0 {
   611  		funcObj := pc.pass.TypesInfo.Defs[d.Name].(*types.Func)
   612  		pc.pass.ExportObjectFact(funcObj, &lff)
   613  	}
   614  }