             }
             linters += files.map { visitor.linter(forFile: $0, configuration: newConfig) }
         }
-        if visitor.parallel {
-            let collectedLinters = linters.parallelCompactMap(transform: collect)
-            DispatchQueue.concurrentPerform(iterations: fileCount) { index in
-                let linter = collectedLinters[index]
-                visit(linter)
-            }
-        } else {
-            let collectedLinters = linters.compactMap(collect)
-            collectedLinters.forEach(visit)
-        }
-        return .success(linters.compactMap({ $0.file }))
+        return linters
+    }
+
+    private func collect(linters: [Linter],
+                         visitor: LintableFilesVisitor,
+                         storage: RuleStorage) -> [CollectedLinter] {
+        var collected = 0
+        let collect = { (linter: Linter) -> CollectedLinter? in
+            let skipFile = visitor.shouldSkipFile(atPath: linter.file.path)
+            if !visitor.quiet, let filename = linter.file.path?.bridge().lastPathComponent {
+                let increment = {
+                    collected += 1
+                    if skipFile {
+                        queuedPrintError("""
+                            Skipping '\(filename)' (\(collected)/\(linters.count)) \
+                            because its compiler arguments could not be found
+                            """)
+                    } else {
+                        queuedPrintError("Collecting '\(filename)' (\(collected)/\(linters.count))")
+                    }
+                }
+                if visitor.parallel {
+                    indexIncrementerQueue.sync(execute: increment)
+                } else {
+                    increment()
+                }
+            }
+
+            guard !skipFile else {
+                return nil
+            }
+
+            return autoreleasepool {
+                linter.collect(into: storage)
+            }
+        }
+        return visitor.parallel ? linters.parallelCompactMap(transform: collect) : linters.compactMap(collect)
+    }
+
+    private func visit(linters: [CollectedLinter],
+                       visitor: LintableFilesVisitor,
+                       storage: RuleStorage) -> [File] {
+        var visited = 0
+        let visit = { (linter: CollectedLinter) -> File in
+            if !visitor.quiet, let filename = linter.file.path?.bridge().lastPathComponent {
+                let increment = {
+                    visited += 1
+                    queuedPrintError("\(visitor.action) '\(filename)' (\(visited)/\(linters.count))")
+                }
+                if visitor.parallel {
+                    indexIncrementerQueue.sync(execute: increment)
+                } else {
+                    increment()
+                }
+            }
+
+            autoreleasepool {
+                visitor.block(linter)
+            }
+            return linter.file
+        }
+        return visitor.parallel ? linters.parallelMap(transform: visit) : linters.map(visit)
     }
 
     fileprivate func getFiles(with visitor: LintableFilesVisitor) -> Result<[File], CommandantError<()>> {

        -> Result<[File], CommandantError<()>> {
            return getFiles(with: visitor)
                .flatMap { groupFiles($0, visitor: visitor) }
                .flatMap { visit(filesPerConfiguration: $0, visitor: visitor, storage: storage) }
    }

    private func groupFiles(_ files: [File],

        return .success(groupedFiles)
    }

    private func linters(for filesPerConfiguration: [Configuration: [File]],
                         visitor: LintableFilesVisitor) -> [Linter] {
        let fileCount = filesPerConfiguration.reduce(0) { $0 + $1.value.count }

        var linters = [Linter]()
        linters.reserveCapacity(fileCount)
        for (config, files) in filesPerConfiguration {
