File: Tracer.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (138 lines) | stat: -rw-r--r-- 4,481 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//===----------------------------- Tracer.swift ---------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import class TSCBasic.DiagnosticsEngine

extension ModuleDependencyGraph {

/// Trace dependencies through the graph
  struct Tracer {
    typealias Graph = ModuleDependencyGraph

    let startingPoints: DirectlyInvalidatedNodeSet
    let graph: ModuleDependencyGraph

    private(set) var tracedUses = TransitivelyInvalidatedNodeArray()

    /// Record the paths taking so that  -driver-show-incremental can explain why things are recompiled
    /// If tracing dependencies, holds a vector used to hold the current path
    /// def - use/def - use/def - ...
    private var currentPathIfTracing: [Node]?

    private let diagnosticEngine: DiagnosticsEngine
  }
}

// MARK:- Tracing
extension ModuleDependencyGraph.Tracer {

  /// Find all uses of `defs` that have not already been traced.
  ///
  /// - Parameters:
  ///   - defNodes: Nodes for changed declarations
  ///   - graph: The graph hosting the nodes
  ///   - diagnosticEngine: The complaint department
  /// - Returns: all uses of the changed nodes that have not already been traced. These represent
  /// heretofore-unschedule compilations that are now required.
  static func collectPreviouslyUntracedNodesUsing(
    defNodes: DirectlyInvalidatedNodeSet,
    in graph: ModuleDependencyGraph,
    diagnosticEngine: DiagnosticsEngine
  ) -> Self {
    var tracer = Self(collectingUsesOf: defNodes,
                      in: graph,
                      diagnosticEngine: diagnosticEngine)
    tracer.collectPreviouslyUntracedDependents()
    return tracer
  }

  private init(collectingUsesOf defs: DirectlyInvalidatedNodeSet,
               in graph: ModuleDependencyGraph,
               diagnosticEngine: DiagnosticsEngine) {
    self.graph = graph
    self.startingPoints = defs
    self.currentPathIfTracing = graph.info.reporter != nil ? [] : nil
    self.diagnosticEngine = diagnosticEngine
  }

  private mutating func collectPreviouslyUntracedDependents() {
    for n in startingPoints {
      collectNextPreviouslyUntracedDependent(of: n)
    }
  }

  private mutating func collectNextPreviouslyUntracedDependent(
    of definition: ModuleDependencyGraph.Node
  ) {
    guard definition.isUntraced else { return }
    definition.setTraced()

    tracedUses.append(definition)

    // If this node is merely used, but not defined anywhere, nothing else
    // can possibly depend upon it
    if case .unknown = definition.definitionLocation { return }

    let pathLengthAfterArrival = traceArrival(at: definition);

    // If this use also provides something, follow it
    for use in graph.nodeFinder.uses(of: definition) {
      collectNextPreviouslyUntracedDependent(of: use)
    }
    traceDeparture(pathLengthAfterArrival);
  }



  private mutating func traceArrival(at visitedNode: ModuleDependencyGraph.Node
  ) -> Int {
    guard var currentPath = currentPathIfTracing else {
      return 0
    }
    currentPath.append(visitedNode)
    currentPathIfTracing = currentPath

    printPath(currentPath)

    return currentPath.count
  }


  private mutating func traceDeparture(_ pathLengthAfterArrival: Int) {
    guard var currentPath = currentPathIfTracing else { return }
    assert(pathLengthAfterArrival == currentPath.count,
           "Path must be maintained throughout recursive visits.")
    currentPath.removeLast()
    currentPathIfTracing = currentPath
  }

  private func printPath(_ path: [Graph.Node]) {
    guard path.first?.definitionLocation != path.last?.definitionLocation
    else {
      return
    }
    graph.info.reporter?.report(
      [
        "Traced:",
        path.compactMap { node in
          guard case let .known(source) = node.definitionLocation else {
            return nil
          }
          return source.typedFile.type == .swift
          ? "\(node.key.description(in: graph)) in \(source.file.basename)"
          : "\(node.key.description(in: graph))"
        }
        .joined(separator: " -> ")
      ].joined(separator: " ")
    )
  }
}