diff --git a/lib/Frontend/SerializedDiagnosticConsumer.cpp b/lib/Frontend/SerializedDiagnosticConsumer.cpp index b2ab0eb2f16c3..34f923d881891 100644 --- a/lib/Frontend/SerializedDiagnosticConsumer.cpp +++ b/lib/Frontend/SerializedDiagnosticConsumer.cpp @@ -87,11 +87,8 @@ struct SharedState : llvm::RefCountedBase { /// The collection of categories used. llvm::DenseMap Categories; - using DiagFlagsTy = - llvm::DenseMap>; - - /// Map for uniquing strings. - DiagFlagsTy DiagFlags; + /// The collection of flags used. + llvm::StringMap 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 @@ -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, @@ -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 ¬ePath) { 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, @@ -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()); diff --git a/test/diagnostics/educational_notes_serialization.swift b/test/diagnostics/educational_notes_serialization.swift new file mode 100644 index 0000000000000..0e36c54622c5f --- /dev/null +++ b/test/diagnostics/educational_notes_serialization.swift @@ -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' [] [] +}