Skip to content

[Diagnostics] Add educational notes into serialized diagnostics #70391

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions lib/Frontend/SerializedDiagnosticConsumer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,8 @@ struct SharedState : llvm::RefCountedBase<SharedState> {
/// The collection of categories used.
llvm::DenseMap<const char *, unsigned> Categories;

using DiagFlagsTy =
llvm::DenseMap<const void *, std::pair<unsigned, StringRef>>;

/// Map for uniquing strings.
DiagFlagsTy DiagFlags;
/// The collection of flags used.
llvm::StringMap<unsigned> Flags;

/// Whether we have already started emission of any DIAG blocks. Once
/// this becomes \c true, we never close a DIAG block until we know that we're
Expand Down Expand Up @@ -198,6 +195,14 @@ class SerializedDiagnosticConsumer : public DiagnosticConsumer {
// Record identifier for the category.
unsigned getEmitCategory(StringRef Category);

/// Emit a flag record that contains a semi-colon separated
/// list of all of the educational notes associated with the
/// diagnostic or `0` if there are no notes.
///
/// \returns a flag record identifier that could be embedded in
/// other records.
unsigned emitEducationalNotes(const DiagnosticInfo &info);

/// Add a source location to a record.
void addLocToRecord(SourceLoc Loc,
SourceManager &SM,
Expand Down Expand Up @@ -319,6 +324,34 @@ unsigned SerializedDiagnosticConsumer::getEmitCategory(StringRef Category) {
return entry;
}

unsigned
SerializedDiagnosticConsumer::emitEducationalNotes(const DiagnosticInfo &Info) {
if (Info.EducationalNotePaths.empty())
return 0;

SmallString<32> scratch;
interleave(
Info.EducationalNotePaths,
[&scratch](const auto &notePath) { scratch += notePath; },
[&scratch] { scratch += ';'; });

StringRef paths = scratch.str();

unsigned &recordID = State->Flags[paths];
if (recordID)
return recordID;

recordID = State->Flags.size();

RecordData Record;
Record.push_back(RECORD_DIAG_FLAG);
Record.push_back(recordID);
Record.push_back(paths.size());
State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DIAG_FLAG), Record,
paths.data());
return recordID;
}

void SerializedDiagnosticConsumer::addLocToRecord(SourceLoc Loc,
SourceManager &SM,
StringRef Filename,
Expand Down Expand Up @@ -572,8 +605,10 @@ emitDiagnosticMessage(SourceManager &SM,
Record.push_back(0);
}

// FIXME: Swift diagnostics currently have no flags.
Record.push_back(0);
// Use "flags" slot to emit a semi-colon separated list of
// educational notes. If there are no notes associated with
// this diagnostic `0` placeholder would be emitted instead.
Record.push_back(emitEducationalNotes(Info));

// Emit the message.
Record.push_back(Text.size());
Expand Down
22 changes: 22 additions & 0 deletions test/diagnostics/educational_notes_serialization.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// not %target-swift-frontend -no-color-diagnostics -print-educational-notes -diagnostic-documentation-path %S/test-docs/ -typecheck %s

// RUN: %empty-directory(%t)
// RUN: not %target-swift-frontend -typecheck -no-color-diagnostics -print-educational-notes -diagnostic-documentation-path %S/test-docs/ -serialize-diagnostics-path %t/serialized.dia %s
// RUN: c-index-test -read-diagnostics %t/serialized.dia > %t/serialized.txt 2>&1
// RUN: %FileCheck %s < %t/serialized.txt

typealias Fn = () -> ()
extension Fn {}
// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Fn' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] []


// Shares the flag record with `Fn`
typealias Dup = () -> ()
extension Dup {}
// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Dup' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] []

do {
func noNote(_: Int) {}
noNote("Hello")
// CHECK: [[@LINE-1]]:10: error: cannot convert value of type 'String' to expected argument type 'Int' [] []
}