Skip to content

Commit ac05a66

Browse files
committed
[Diagnostics] Introduce "Educational Notes" for diagnostics
Educational notes are small pieces of documentation which explain a concept relevant to some diagnostic message. If -enable-descriptive-diagnostics is passed, they will be printed after a diagnostic message if available. Educational notes can be found at /usr/share/doc/diagnostics in a toolchain, and are associated with specific compiler diagnostics in EducationalNotes.def.
1 parent 9931aab commit ac05a66

File tree

14 files changed

+121
-1
lines changed

14 files changed

+121
-1
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1129,6 +1129,8 @@ endif()
11291129

11301130
add_subdirectory(utils)
11311131

1132+
add_subdirectory(userdocs)
1133+
11321134
if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
11331135
if(SWIFT_BUILD_PERF_TESTSUITE)
11341136
add_subdirectory(benchmark)

include/swift/AST/DiagnosticConsumer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ struct DiagnosticInfo {
5757
/// DiagnosticInfo of notes which are children of this diagnostic, if any
5858
ArrayRef<DiagnosticInfo *> ChildDiagnosticInfo;
5959

60+
/// Paths to "educational note" diagnostic documentation in the toolchain.
61+
ArrayRef<std::string> EducationalNotePaths;
62+
6063
/// Represents a fix-it, a replacement of one range of text with another.
6164
class FixIt {
6265
CharSourceRange Range;

include/swift/AST/DiagnosticEngine.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,9 @@ namespace swift {
673673
/// Use descriptive diagnostic style when available.
674674
bool useDescriptiveDiagnostics = false;
675675

676+
/// Path to diagnostic documentation directory.
677+
std::string diagnosticDocumentationPath = "";
678+
676679
friend class InFlightDiagnostic;
677680
friend class DiagnosticTransaction;
678681
friend class CompoundDiagnosticTransaction;
@@ -723,6 +726,13 @@ namespace swift {
723726
return useDescriptiveDiagnostics;
724727
}
725728

729+
void setDiagnosticDocumentationPath(std::string path) {
730+
diagnosticDocumentationPath = path;
731+
}
732+
StringRef getDiagnosticDocumentationPath() {
733+
return diagnosticDocumentationPath;
734+
}
735+
726736
void ignoreDiagnostic(DiagID id) {
727737
state.setDiagnosticBehavior(id, DiagnosticState::Behavior::Ignore);
728738
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===-- EducationalNotes.def - Diagnostic Documentation Content -*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This file associates diagnostics with relevant educational notes which
14+
// explain important concepts.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef EDUCATIONAL_NOTES
19+
# error Must define EDUCATIONAL_NOTES
20+
#endif
21+
22+
// EDUCATIONAL_NOTES(DIAG_ID, EDUCATIONAL_NOTE_FILENAMES...)
23+
24+
EDUCATIONAL_NOTES(non_nominal_no_initializers, "nominal-types.md")
25+
EDUCATIONAL_NOTES(non_nominal_extension, "nominal-types.md")
26+
EDUCATIONAL_NOTES(associated_type_witness_conform_impossible,
27+
"nominal-types.md")
28+
29+
#undef EDUCATIONAL_NOTES

include/swift/Basic/DiagnosticOptions.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class DiagnosticOptions {
5959
/// Descriptive diagnostic output is not intended to be machine-readable.
6060
bool EnableDescriptiveDiagnostics = false;
6161

62+
std::string DiagnosticDocumentationPath = "";
63+
6264
/// Return a hash code of any components from these options that should
6365
/// contribute to a Swift Bridging PCH hash.
6466
llvm::hash_code getPCHHashComponents() const {

include/swift/Option/FrontendOptions.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ def show_diagnostics_after_fatal : Flag<["-"], "show-diagnostics-after-fatal">,
115115

116116
def enable_descriptive_diagnostics : Flag<["-"], "enable-descriptive-diagnostics">,
117117
HelpText<"Show descriptive diagnostic information, if available.">;
118+
119+
def diagnostic_documentation_path
120+
: Separate<["-"], "diagnostic-documentation-path">, MetaVarName<"<path>">,
121+
HelpText<"Path to diagnostic documentation resources">;
118122

119123
def enable_swiftcall : Flag<["-"], "enable-swiftcall">,
120124
HelpText<"Enable the use of LLVM swiftcall support">;

lib/AST/DiagnosticEngine.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,18 @@ static constexpr const char *const fixItStrings[] = {
116116
"<not a fix-it>",
117117
};
118118

119+
#define EDUCATIONAL_NOTES(DIAG, ...) \
120+
static constexpr const char *const DIAG##_educationalNotes[] = {__VA_ARGS__, \
121+
nullptr};
122+
#include "swift/AST/EducationalNotes.def"
123+
124+
static constexpr const char *const *educationalNotes[LocalDiagID::NumDiags]{
125+
[LocalDiagID::invalid_diagnostic] = {},
126+
#define EDUCATIONAL_NOTES(DIAG, ...) \
127+
[LocalDiagID::DIAG] = DIAG##_educationalNotes,
128+
#include "swift/AST/EducationalNotes.def"
129+
};
130+
119131
DiagnosticState::DiagnosticState() {
120132
// Initialize our per-diagnostic state to default
121133
perDiagnosticBehavior.resize(LocalDiagID::NumDiags, Behavior::Unspecified);
@@ -951,6 +963,19 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
951963
}
952964
}
953965
info->ChildDiagnosticInfo = childInfoPtrs;
966+
967+
SmallVector<std::string, 1> educationalNotePaths;
968+
if (useDescriptiveDiagnostics) {
969+
auto associatedNotes = educationalNotes[(uint32_t)diagnostic.getID()];
970+
while (associatedNotes && *associatedNotes) {
971+
SmallString<128> notePath(getDiagnosticDocumentationPath());
972+
llvm::sys::path::append(notePath, *associatedNotes);
973+
educationalNotePaths.push_back(notePath.str());
974+
associatedNotes++;
975+
}
976+
info->EducationalNotePaths = educationalNotePaths;
977+
}
978+
954979
for (auto &consumer : Consumers) {
955980
consumer->handleDiagnostic(SourceMgr, *info);
956981
}

lib/Frontend/CompilerInvocation.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,13 @@ void CompilerInvocation::setMainExecutablePath(StringRef Path) {
4444
llvm::sys::path::remove_filename(LibPath); // Remove /bin
4545
llvm::sys::path::append(LibPath, "lib", "swift");
4646
setRuntimeResourcePath(LibPath.str());
47+
48+
llvm::SmallString<128> DiagnosticDocsPath(Path);
49+
llvm::sys::path::remove_filename(DiagnosticDocsPath); // Remove /swift
50+
llvm::sys::path::remove_filename(DiagnosticDocsPath); // Remove /bin
51+
llvm::sys::path::append(DiagnosticDocsPath, "share", "doc", "swift",
52+
"diagnostics");
53+
DiagnosticOpts.DiagnosticDocumentationPath = DiagnosticDocsPath.str();
4754
}
4855

4956
/// If we haven't explicitly passed -prebuilt-module-cache-path, set it to
@@ -682,7 +689,9 @@ static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args,
682689
Opts.PrintDiagnosticNames |= Args.hasArg(OPT_debug_diagnostic_names);
683690
Opts.EnableDescriptiveDiagnostics |=
684691
Args.hasArg(OPT_enable_descriptive_diagnostics);
685-
692+
if (Arg *A = Args.getLastArg(OPT_diagnostic_documentation_path)) {
693+
Opts.DiagnosticDocumentationPath = A->getValue();
694+
}
686695
assert(!(Opts.WarningsAsErrors && Opts.SuppressWarnings) &&
687696
"conflicting arguments; should have been caught by driver");
688697

lib/Frontend/Frontend.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,8 @@ void CompilerInstance::setUpDiagnosticOptions() {
319319
if (Invocation.getDiagnosticOptions().EnableDescriptiveDiagnostics) {
320320
Diagnostics.setUseDescriptiveDiagnostics(true);
321321
}
322+
Diagnostics.setDiagnosticDocumentationPath(
323+
Invocation.getDiagnosticOptions().DiagnosticDocumentationPath);
322324
}
323325

324326
// The ordering of ModuleLoaders is important!

lib/Frontend/PrintingDiagnosticConsumer.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ void PrintingDiagnosticConsumer::handleDiagnostic(SourceManager &SM,
6969
return;
7070

7171
printDiagnostic(SM, Info);
72+
for (auto path : Info.EducationalNotePaths) {
73+
auto buffer = llvm::MemoryBuffer::getFile(path);
74+
if (buffer) {
75+
Stream << buffer->get()->getBuffer() << "\n";
76+
}
77+
}
7278

7379
for (auto ChildInfo : Info.ChildDiagnosticInfo) {
7480
printDiagnostic(SM, *ChildInfo);

0 commit comments

Comments
 (0)