ObservePackage observes all the annotations and assertions computed locally about the current package. The assertions are sorted based on whether they are already known to trigger without reliance on annotation sites, such as `x` in `x = nil; x.f`, which will generate `SingleAssertionFailure`s, whet
(pkgFullTriggers []annotation.FullTrigger)
| 181 | // observeImplication. Before all assertions are sorted and handled thus, the annotations read for |
| 182 | // the package are iterated over and observed via calls to observeSiteExplanation as a <Val>BecauseAnnotation. |
| 183 | func (e *Engine) ObservePackage(pkgFullTriggers []annotation.FullTrigger) { |
| 184 | // As Step 1, we do a pre-analysis of "guard missing" triggers to verify if their dereferences are always nil-safe, |
| 185 | // and hence can be deleted to not report a false positive error. Specifically, this analyis of "always safe" paths |
| 186 | // is focussed on the rich check effect functions, namely error returning functions and ok-returning functions. |
| 187 | // The process is to find all guard missing triggers reaching a function return site, and then check if all the return triggers |
| 188 | // to that function site are non-nil. If so, we can safely delete all the guard-missing triggers for this function site. |
| 189 | triggersToBeDeleted := make(map[int]bool) |
| 190 | mapSiteGuardMissing, mapSiteReturn := e.mapGuardMissingAndReturnToFuncSite(pkgFullTriggers) |
| 191 | for site, guardMissingIndices := range mapSiteGuardMissing { |
| 192 | if returnIndices, ok := mapSiteReturn[site]; ok { |
| 193 | // Check if all the return triggers to this function site are non-nil. |
| 194 | nonnilCnt := 0 |
| 195 | for _, index := range returnIndices { |
| 196 | returnTrigger := pkgFullTriggers[index] |
| 197 | if returnTrigger.Producer.Annotation.Kind() != annotation.Never { |
| 198 | // break early if we find a potentially nilable trigger |
| 199 | break |
| 200 | } |
| 201 | nonnilCnt++ |
| 202 | } |
| 203 | |
| 204 | if nonnilCnt == len(returnIndices) { |
| 205 | // If all return triggers are non-nil, then we can safely delete all the guard-missing triggers |
| 206 | // for this function site. |
| 207 | for _, index := range guardMissingIndices { |
| 208 | triggersToBeDeleted[index] = true |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | } |
| 213 | // Add all placeholder UseAsReturnForAlwaysSafePath triggers to triggersToBeDeleted |
| 214 | for _, indices := range mapSiteReturn { |
| 215 | for _, index := range indices { |
| 216 | triggersToBeDeleted[index] = true |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | // Filter out the triggers that are to be deleted. |
| 221 | pkgFullTriggers = slices.DeleteFunc(pkgFullTriggers, func(t annotation.FullTrigger) bool { |
| 222 | index := slices.Index(pkgFullTriggers, t) |
| 223 | return triggersToBeDeleted[index] |
| 224 | }) |
| 225 | |
| 226 | // Separate out triggers with UseAsNonErrorRetDependentOnErrorRetNilability consumer from other triggers. |
| 227 | // This is needed since whether UseAsNonErrorRetDependentOnErrorRetNilability triggers should be fired |
| 228 | // is dependent on their corresponding UseAsErrorRetWithNilabilityUnknown triggers. By this separation, |
| 229 | // we can process all other triggers, including UseAsErrorRetWithNilabilityUnknown, first, and once |
| 230 | // their nilability status is known, then filter out the unnecessary UseAsNonErrorRetDependentOnErrorRetNilability |
| 231 | // triggers, and run the pkg inference process again only for the remainder triggers. |
| 232 | // Steps 2--4 below depict this approach in more detail. |
| 233 | var ( |
| 234 | nonErrRetTriggers []annotation.FullTrigger |
| 235 | // In most cases all triggers will be stored in otherTriggers, so we set a proper capacity. |
| 236 | otherTriggers = make([]annotation.FullTrigger, 0, len(pkgFullTriggers)) |
| 237 | ) |
| 238 | |
| 239 | for _, t := range pkgFullTriggers { |
| 240 | if _, ok := t.Consumer.Annotation.(*annotation.UseAsNonErrorRetDependentOnErrorRetNilability); ok { |
no test coverage detected