Skip to content

Commit 044e97b

Browse files
authored
Merge pull request #82663 from bnbarham/generate-doc-index
[userdocs] Automatically generate doc indices
2 parents b0739a5 + 5ee9133 commit 044e97b

File tree

7 files changed

+290
-14
lines changed

7 files changed

+290
-14
lines changed

userdocs/diagnostics/compilation-caching.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
# Compilation Caching
1+
# Compilation caching (CompilationCaching)
2+
3+
Errors and warnings related to the compiler's CAS compilation caching.
4+
5+
6+
## Overview
27

38
Compilation caching allows Swift compiler to reuse previously compiled outputs if the identical
49
compilation has been done before.
Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Diagnostic descriptions
22

3+
<!-- This file is auto-generated via `swift swift/utils/generate-doc-index.swift` -->
4+
35
Detailed explanations for various compiler diagnostics.
46

57

@@ -15,23 +17,23 @@ presented specially within your IDE of choice. See below for the full list of th
1517

1618
## Topics
1719

18-
- <doc:actor-isolated-call>
19-
- <doc:conformance-isolation>
2020
- <doc:dynamic-callable-requirements>
21-
- <doc:error-in-future-swift-version>
22-
- <doc:existential-member-access-limitations>
21+
- <doc:trailing-closure-matching>
22+
- <doc:actor-isolated-call>
23+
- <doc:sendable-closure-captures>
24+
- <doc:string-interpolation-conformance>
2325
- <doc:isolated-conformances>
26+
- <doc:error-in-future-swift-version>
2427
- <doc:multiple-inheritance>
25-
- <doc:mutable-global-variable>
2628
- <doc:nominal-types>
27-
- <doc:opaque-type-inference>
2829
- <doc:property-wrapper-requirements>
2930
- <doc:protocol-type-non-conformance>
31+
- <doc:conformance-isolation>
3032
- <doc:result-builder-methods>
31-
- <doc:sendable-closure-captures>
32-
- <doc:sending-closure-risks-data-race>
3333
- <doc:sendable-metatypes>
34+
- <doc:sending-closure-risks-data-race>
3435
- <doc:sending-risks-data-race>
35-
- <doc:string-interpolation-conformance>
3636
- <doc:temporary-pointers>
37-
- <doc:trailing-closure-matching>
37+
- <doc:opaque-type-inference>
38+
- <doc:mutable-global-variable>
39+
- <doc:existential-member-access-limitations>

userdocs/diagnostics/diagnostic-groups.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Diagnostic groups
22

3+
<!-- This file is auto-generated via `swift swift/utils/generate-doc-index.swift` -->
4+
35
Diagnostic groups allow controlling the behavior of warnings in a more precise manner.
46

57

@@ -25,10 +27,13 @@ Or upgrade all warnings except deprecated declaration to errors:
2527

2628
## Topics
2729

28-
- <doc:clang-declaration-import>
30+
- <doc:compilation-caching>
2931
- <doc:deprecated-declaration>
32+
- <doc:implementation-only-deprecated>
3033
- <doc:preconcurrency-import>
34+
- <doc:clang-declaration-import>
35+
- <doc:missing-module-on-known-paths>
3136
- <doc:strict-language-features>
3237
- <doc:strict-memory-safety>
3338
- <doc:unknown-warning-group>
34-
39+
- <doc:availability-unrecognized-name>

userdocs/diagnostics/missing-module-on-known-paths.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
# Missing Module On Known Path From A Dependency Note (`MissingModuleOnKnownPaths`)
1+
# Missing module on known path from a dependency (MissingModuleOnKnownPaths)
22

3+
Notes related to information about a missing module dependency.
4+
5+
6+
## Overview
37

48
This diagnostic group covers notes related to displaying information about a missing module dependency which the compiler is able to locate as present on a search path found in a loaded Swift binary module, but which is not specified to the current compilation.
59

@@ -15,4 +19,5 @@ The Swift compiler would emit a module-not-found error and a note to inform the
1519
error: Compilation search paths unable to resolve module dependency: 'Bar' [#MissingModuleOnKnownPaths]
1620
note: 'Bar' can be found using a search path that was specified when building module 'Foo' ('<Search Path>'). This search path was not specified on the current compilation.
1721
```
22+
1823
Some prior versions of the Swift compiler erroneously inherited search paths from loaded binary Swift modules and used them to resolve other, subsequently-encountered module dependencies. All search paths required to resolve direct and transitive module dependencies must be explicitly specified on the compiler invocation which will encounter these dependencies.

userdocs/diagnostics/upcoming-language-features.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Upcoming language features
22

3+
<!-- This file is auto-generated via `swift swift/utils/generate-doc-index.swift` -->
4+
35
Upcoming language features enable new (but potentially source breaking) functionality that be
46
enabled by default in an upcoming language mode.
57

utils/generate-doc-index.swift

Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
#!/usr/bin/env swift -enable-upcoming-feature BareSlashRegexLiterals
2+
3+
import Foundation
4+
5+
let usage = """
6+
./\(CommandLine.arguments[0]) <swift-source-directory> [output-directory]
7+
8+
Generates index files for diagnostics notes, groups, and upcoming features.
9+
"""
10+
11+
let docsDir = "userdocs/diagnostics"
12+
let topLevelFileName = "diagnostics.md"
13+
14+
let notesDocFileName = "diagnostic-descriptions.md"
15+
let notesHeader = """
16+
# Diagnostic descriptions
17+
18+
<!-- This file is auto-generated via `swift swift/utils/generate-doc-index.swift` -->
19+
20+
Detailed explanations for various compiler diagnostics.
21+
22+
23+
## Overview
24+
25+
Swift diagnostics are classified into errors and warnings. Warnings can only be silenced in an
26+
intentional manner, e.g., adding `_ =` for an unused function result.
27+
28+
Some diagnostics have more detailed explanations available. These include a `[#Name]` inline and
29+
reference to this documentation at the end of the compiler output on the command line, or is
30+
presented specially within your IDE of choice. See below for the full list of these notes.
31+
32+
33+
## Topics
34+
35+
36+
"""
37+
38+
let groupsDocFileName = "diagnostic-groups.md"
39+
let groupsHeader = """
40+
# Diagnostic groups
41+
42+
<!-- This file is auto-generated via `swift swift/utils/generate-doc-index.swift` -->
43+
44+
Diagnostic groups allow controlling the behavior of warnings in a more precise manner.
45+
46+
47+
## Overview
48+
49+
Diagnostic groups collect some number of diagnostics together under a common group name. This allows
50+
for extra documentation to help explain relevant language concepts, as well as the ability to
51+
control the behavior of warnings in a more precise manner:
52+
- `-Werror <group>` - upgrades warnings in the specified group to errors
53+
- `-Wwarning <group>` - indicates that warnings in the specified group should remain warnings, even
54+
if they were previously upgraded to errors
55+
56+
As a concrete example, to upgrade deprecated declaration warnings to errors:
57+
```sh
58+
-Werror DeprecatedDeclaration
59+
```
60+
61+
Or upgrade all warnings except deprecated declaration to errors:
62+
```sh
63+
-warnings-as-errors -Wwarning DeprecatedDeclaration
64+
```
65+
66+
67+
## Topics
68+
69+
70+
"""
71+
72+
let featuresDocFileName = "upcoming-language-features.md"
73+
let featuresHeader = """
74+
# Upcoming language features
75+
76+
<!-- This file is auto-generated via `swift swift/utils/generate-doc-index.swift` -->
77+
78+
Upcoming language features enable new (but potentially source breaking) functionality that be
79+
enabled by default in an upcoming language mode.
80+
81+
82+
## Overview
83+
84+
Upcoming language features allow the incremental adoption of language features that would otherwise
85+
only be available in a new language mode, without having to fully migrate to that mode. They can be
86+
enabled on the command line with `-enable-upcoming-feature <feature>`.
87+
88+
Some upcoming features have an additional "migration" mode, where the compiler will emit warnings
89+
with fix-its to help migrate to that mode. This can be enabled with `-enable-upcoming-feature
90+
<feature>:migrate`.
91+
92+
93+
## Topics
94+
95+
96+
"""
97+
98+
let groupsFileName = "include/swift/AST/DiagnosticGroups.def"
99+
let groupRegex = /GROUP\(([a-zA-Z]+), ".+"\)/
100+
101+
let featuresFileName = "include/swift/Basic/Features.def"
102+
let featuresRegex = /UPCOMING_FEATURE\(([a-zA-Z]+), .+\)/
103+
104+
let nameRegex = /# .+ \((?<name>[a-zA-Z]+)\)/
105+
106+
var args = CommandLine.arguments.dropFirst()
107+
if args.count != 1 && args.count != 2 {
108+
print(usage)
109+
exit(2)
110+
}
111+
112+
let swiftSourceDir = args.removeFirst()
113+
let outputDir: String
114+
if !args.isEmpty {
115+
outputDir = args.removeFirst()
116+
} else {
117+
outputDir = "\(swiftSourceDir)/\(docsDir)"
118+
}
119+
120+
let generator = GenerateUserDocs(swiftSourceDir: swiftSourceDir, outputDir: outputDir)
121+
do {
122+
try generator.generateIndex()
123+
} catch {
124+
print("error: \(error)")
125+
exit(1)
126+
}
127+
128+
struct GenerateUserDocs {
129+
let swiftSourceDir: String
130+
let outputDir: String
131+
132+
func generateIndex() throws {
133+
let notesHandle = try createIndex(name: notesDocFileName, header: notesHeader)
134+
defer { try? notesHandle.close() }
135+
136+
let groupsHandle = try createIndex(name: groupsDocFileName, header: groupsHeader)
137+
defer { try? groupsHandle.close() }
138+
139+
let featuresHandle = try createIndex(name: featuresDocFileName, header: featuresHeader)
140+
defer { try? featuresHandle.close() }
141+
142+
let docs = try retrieveDocs().sorted { a, b in
143+
return a.title < b.title
144+
}
145+
146+
for doc in docs {
147+
let handle: FileHandle
148+
switch doc.kind {
149+
case .note:
150+
handle = notesHandle
151+
case .group:
152+
handle = groupsHandle
153+
case .feature:
154+
handle = featuresHandle
155+
}
156+
157+
let ref = "- <doc:\(doc.name.dropLast(3))>\n"
158+
try handle.write(contentsOf: ref.data(using: .utf8)!)
159+
}
160+
}
161+
162+
func createIndex(name: String, header: String) throws -> FileHandle {
163+
let path = "\(outputDir)/\(name)"
164+
165+
if FileManager.default.fileExists(atPath: path) {
166+
try FileManager.default.removeItem(atPath: path)
167+
}
168+
FileManager.default.createFile(atPath: path, contents: nil)
169+
170+
let handle = try FileHandle(forWritingTo: URL(filePath: path))
171+
try handle.write(contentsOf: header.data(using: .utf8)!)
172+
return handle
173+
}
174+
175+
func matches(in fileName: String, with regex: Regex<(Substring, Substring)>) throws -> Set<String> {
176+
let file = try String(contentsOfFile: "\(swiftSourceDir)/\(fileName)", encoding: .utf8)
177+
178+
var matches: Set<String> = []
179+
for line in file.components(separatedBy: .newlines) {
180+
if let match = try? regex.firstMatch(in: line) {
181+
matches.insert(String(match.1))
182+
}
183+
}
184+
185+
return matches
186+
}
187+
188+
func retrieveDocs() throws -> [UserDoc] {
189+
let groups = try matches(in: groupsFileName, with: groupRegex)
190+
let features = try matches(in: featuresFileName, with: featuresRegex)
191+
192+
var docs: [UserDoc] = []
193+
194+
let files = try FileManager.default.contentsOfDirectory(atPath: "\(swiftSourceDir)/\(docsDir)")
195+
for name in files {
196+
if !name.hasSuffix(".md")
197+
|| name.hasSuffix(topLevelFileName)
198+
|| name.hasSuffix(notesDocFileName)
199+
|| name.hasSuffix(groupsDocFileName)
200+
|| name.hasSuffix(featuresDocFileName) {
201+
continue
202+
}
203+
204+
let file = try String(contentsOfFile: "\(swiftSourceDir)/\(docsDir)/\(name)", encoding: .utf8)
205+
206+
if let match = try? nameRegex.prefixMatch(in: file) {
207+
let groupName = String(match.name)
208+
209+
let kind: UserDoc.Kind
210+
if features.contains(groupName) {
211+
kind = .feature
212+
} else if groups.contains(groupName) {
213+
kind = .group
214+
} else {
215+
kind = .note
216+
}
217+
218+
docs.append(UserDoc(name: name, title: String(match.0), kind: kind))
219+
} else {
220+
if let newlineIndex = file.firstIndex(of: "\n") {
221+
docs.append(UserDoc(name: name, title: String(file[..<newlineIndex]), kind: .note))
222+
} else {
223+
docs.append(UserDoc(name: name, title: file, kind: .note))
224+
}
225+
}
226+
}
227+
228+
return docs
229+
}
230+
}
231+
232+
struct UserDoc {
233+
enum Kind {
234+
case note
235+
case group
236+
case feature
237+
}
238+
239+
let name: String
240+
let title: String
241+
let kind: Kind
242+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# REQUIRES: swift_swift_parser
2+
# REQUIRES: target-same-as-host
3+
# REQUIRES: swift_feature_BareSlashRegexLiterals
4+
5+
# RUN: %empty-directory(%t)
6+
7+
# Check that the generated doc indices match those in the repo. Run
8+
# `./utils/generate-doc-index.swift <swift-source-directory>` to regenerate.
9+
10+
# RUN: %host-build-swift -swift-version 5 -enable-upcoming-feature BareSlashRegexLiterals %swift_src_root/utils/generate-doc-index.swift -o %t/generate-doc-index
11+
# RUN: %t/generate-doc-index %swift_src_root %t
12+
13+
# RUN: diff -u %swift_src_root/userdocs/diagnostics/diagnostic-descriptions.md %t/diagnostic-descriptions.md
14+
# RUN: diff -u %swift_src_root/userdocs/diagnostics/diagnostic-groups.md %t/diagnostic-groups.md
15+
# RUN: diff -u %swift_src_root/userdocs/diagnostics/upcoming-language-features.md %t/upcoming-language-features.md

0 commit comments

Comments
 (0)