observeImplication augments the inferred map with a new implication discovered as the result of an assertion. In particular, we note that all assertions discovered as FullTriggers by the assertions or affiliations analyzer are of the form `nilable X -> nilable Y`, so this method just takes the `prod
( producerSite, consumerSite primitiveSite, assertion primitiveFullTrigger, )
| 498 | // - If both sites are undetermined (i.e. both are under-constrained nodes in the implication graph) |
| 499 | // then we simply ensure that this assertion is present as en edge between them. |
| 500 | func (e *Engine) observeImplication( |
| 501 | producerSite, |
| 502 | consumerSite primitiveSite, |
| 503 | assertion primitiveFullTrigger, |
| 504 | ) { |
| 505 | // When we observe an implication between the producer site (PS) and consumer site (CS), we |
| 506 | // check their existing values in the inferred map (denoted as P and C) and behave accordingly: |
| 507 | // * If either P or C is determined, the other site will be determined. Note that we do not |
| 508 | // need to continue processing after either case since we are handling the same implication |
| 509 | // (i.e., no need to handle P => C and then C => P again). This also ensures that we do not |
| 510 | // report duplicate errors when P and C are both determined and have conflicting nilabilities. |
| 511 | // Specifically: |
| 512 | // * P is nilable => C must be nilable |
| 513 | // * P is nonnil => this implication does not yield more information which can be safely discarded |
| 514 | // * C is nilable => this implication does not yield more information which can be safely discarded |
| 515 | // * C is nonnil => P must be nonnil |
| 516 | // |
| 517 | // * If _both_ P and C are "undetermined or does not exist", we should create an implication |
| 518 | // edge between PS and CS. |
| 519 | |
| 520 | // Nilable (true) producer => Nilable (true) consumer. We do not care about "ok" here since |
| 521 | // the "ok" in the type assertion below implies this "ok == true". |
| 522 | producer, _ := e.inferredMap.Load(producerSite) |
| 523 | if v, ok := producer.(*DeterminedVal); ok { |
| 524 | if v.Bool.Val() { |
| 525 | e.observeSiteExplanation(consumerSite, TrueBecauseDeepConstraint{ |
| 526 | InternalAssertion: assertion, |
| 527 | DeeperExplanation: v.Bool, |
| 528 | }) |
| 529 | } |
| 530 | return |
| 531 | } |
| 532 | |
| 533 | // Nonnil (false) consumer => Nonnil (false) producer. We do not care about "ok" here since |
| 534 | // the "ok" in the type assertion below implies this "ok == true". |
| 535 | consumer, _ := e.inferredMap.Load(consumerSite) |
| 536 | if v, ok := consumer.(*DeterminedVal); ok { |
| 537 | if !v.Bool.Val() { |
| 538 | e.observeSiteExplanation(producerSite, FalseBecauseDeepConstraint{ |
| 539 | InternalAssertion: assertion, |
| 540 | DeeperExplanation: v.Bool, |
| 541 | }) |
| 542 | } |
| 543 | return |
| 544 | } |
| 545 | |
| 546 | // If we reach here, it means that the existing values for the producer and consumer are |
| 547 | // undetermined (or non-existent), so we can simply add an implication edge in the graph. |
| 548 | e.inferredMap.StoreImplication(producerSite, consumerSite, assertion) |
| 549 | } |
| 550 | |
| 551 | // GobRegister must be called in an `init` function before attempting to run any procedure that can |
| 552 | // deal with InferredAnnotationMaps as Facts. If not, gob encoding/decoding will be unable to handle |
no test coverage detected