inferContracts infers function contracts for a function if it has no contracts written. It returns a list of inferred contracts, which may be empty if no contract is inferred but is never nil.
(fn *ssa.Function)
| 31 | // returns a list of inferred contracts, which may be empty if no contract is inferred but is never |
| 32 | // nil. |
| 33 | func inferContracts(fn *ssa.Function) Contracts { |
| 34 | nilnessTableSetByBB := make(map[*ssa.BasicBlock]nilnessTableSet) |
| 35 | retInstrs := getReturnInstrs(fn) // TODO: Consider *ssa.Panic |
| 36 | // No need of an expensive dataflow analysis if we can derive contracts from the return |
| 37 | // instructions directly. |
| 38 | if ctrs := deriveContracts(retInstrs, fn, nilnessTableSetByBB); len(ctrs) != 0 { |
| 39 | return ctrs |
| 40 | } |
| 41 | |
| 42 | // Add the entry block to the queue. |
| 43 | // TODO: visit fn.Recover. |
| 44 | var queue []*ssa.BasicBlock |
| 45 | queue = append(queue, fn.Blocks[0]) |
| 46 | seen := make([]bool, len(fn.Blocks)) // seen[i] means we have visited block i at least once. |
| 47 | |
| 48 | // Visit every block in the queue. |
| 49 | for len(queue) > 0 { |
| 50 | b := queue[0] |
| 51 | queue = queue[1:] |
| 52 | |
| 53 | // Create a nilnessTableSet for this block if it doesn't exist. |
| 54 | if _, ok := nilnessTableSetByBB[b]; !ok { |
| 55 | nilnessTableSetByBB[b] = newNilnessTableSet() |
| 56 | } |
| 57 | |
| 58 | // Propagate nilnessTables from predecessors. Union every predecessor's nilnessTableSet |
| 59 | // into this block's nilnessTableSet. |
| 60 | |
| 61 | // Map each predecessor to the nilnessTables propagated from it. |
| 62 | nilnessTablesUnderPred := make(map[*ssa.BasicBlock]nilnessTableSet) |
| 63 | for _, pred := range b.Preds { |
| 64 | var nilnessTableSetOfPred nilnessTableSet |
| 65 | if !seen[pred.Index] { |
| 66 | // Skip any predecessor that has not been visited yet. |
| 67 | continue |
| 68 | } |
| 69 | if r, ok := nilnessTableSetByBB[pred]; ok && len(r) != 0 { |
| 70 | nilnessTableSetOfPred = r |
| 71 | } else { |
| 72 | nilnessTableSetOfPred = newNilnessTableSet() |
| 73 | nilnessTableSetOfPred, _ = add(nilnessTableSetOfPred, nilnessTable{}) |
| 74 | } |
| 75 | nilnessTableSetUnderThisPred := newNilnessTableSet() |
| 76 | for _, table := range nilnessTableSetOfPred { |
| 77 | // Copy all the existing values. |
| 78 | nTable := table.copy() |
| 79 | // Learn new nilness from the branch if any. |
| 80 | lTable, ok := learnNilness(b, pred, table) |
| 81 | if !ok { |
| 82 | // conflict found when extending this table, we should drop this table. |
| 83 | continue |
| 84 | } |
| 85 | nTable.addAll(lTable) |
| 86 | nilnessTableSetUnderThisPred, _ = add(nilnessTableSetUnderThisPred, nTable) |
| 87 | } |
| 88 | nilnessTablesUnderPred[pred] = nilnessTableSetUnderThisPred |
| 89 | } |
| 90 |
no test coverage detected