|
20 | 20 | #include "swift/AST/ASTPrinter.h"
|
21 | 21 | #include "swift/AST/Decl.h"
|
22 | 22 | #include "swift/AST/DiagnosticSuppression.h"
|
| 23 | +#include "swift/AST/DiagnosticMessageFormat.h" |
23 | 24 | #include "swift/AST/Module.h"
|
24 | 25 | #include "swift/AST/Pattern.h"
|
25 | 26 | #include "swift/AST/PrintOptions.h"
|
|
32 | 33 | #include "llvm/ADT/Twine.h"
|
33 | 34 | #include "llvm/Support/CommandLine.h"
|
34 | 35 | #include "llvm/Support/Format.h"
|
| 36 | +#include "llvm/Support/YAMLParser.h" |
| 37 | +#include "llvm/Support/YAMLTraits.h" |
35 | 38 | #include "llvm/Support/raw_ostream.h"
|
36 | 39 |
|
37 | 40 | using namespace swift;
|
@@ -140,6 +143,52 @@ struct EducationalNotes {
|
140 | 143 | static constexpr EducationalNotes<LocalDiagID::NumDiags> _EducationalNotes = EducationalNotes<LocalDiagID::NumDiags>();
|
141 | 144 | static constexpr auto educationalNotes = _EducationalNotes.value;
|
142 | 145 |
|
| 146 | +class LocalizationInput : public llvm::yaml::Input { |
| 147 | + using Input::Input; |
| 148 | + |
| 149 | + /// Read diagnostics in the YAML file iteratively |
| 150 | + template <typename T, typename Context> |
| 151 | + static typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value, |
| 152 | + void>::type |
| 153 | + readYAML(IO &io, T &Seq, bool, Context &Ctx) { |
| 154 | + unsigned count = io.beginSequence(); |
| 155 | + |
| 156 | + // Resize Diags from YAML file to be the same size |
| 157 | + // as diagnosticStrings from def files. |
| 158 | + Seq.resize(LocalDiagID::NumDiags); |
| 159 | + for (unsigned i = 0; i < count; ++i) { |
| 160 | + void *SaveInfo; |
| 161 | + if (io.preflightElement(i, SaveInfo)) { |
| 162 | + DiagnosticNode current; |
| 163 | + yamlize(io, current, true, Ctx); |
| 164 | + io.postflightElement(SaveInfo); |
| 165 | + // YAML file isn't guaranteed to have diagnostics in order of their |
| 166 | + // declaration in `.def` files, to accommodate that we need to leave |
| 167 | + // holes in diagnostic array for diagnostics which haven't yet been |
| 168 | + // localized and for the ones that have `DiagnosticNode::id` |
| 169 | + // indicates their position. |
| 170 | + Seq[static_cast<unsigned>(current.id)] = std::move(current.msg); |
| 171 | + } |
| 172 | + } |
| 173 | + io.endSequence(); |
| 174 | + } |
| 175 | + |
| 176 | + template <typename T> |
| 177 | + inline friend |
| 178 | + typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value, |
| 179 | + LocalizationInput &>::type |
| 180 | + operator>>(LocalizationInput &yin, T &diagnostics) { |
| 181 | + llvm::yaml::EmptyContext Ctx; |
| 182 | + if (yin.setCurrentDocument()) { |
| 183 | + // If YAML file's format doesn't match the current format in |
| 184 | + // DiagnosticMessageFormat, will throw an error. |
| 185 | + readYAML(yin, diagnostics, true, Ctx); |
| 186 | + } |
| 187 | + |
| 188 | + return yin; |
| 189 | + } |
| 190 | +}; |
| 191 | + |
143 | 192 | DiagnosticState::DiagnosticState() {
|
144 | 193 | // Initialize our per-diagnostic state to default
|
145 | 194 | perDiagnosticBehavior.resize(LocalDiagID::NumDiags, Behavior::Unspecified);
|
@@ -311,6 +360,28 @@ void Diagnostic::addChildNote(Diagnostic &&D) {
|
311 | 360 | ChildNotes.push_back(std::move(D));
|
312 | 361 | }
|
313 | 362 |
|
| 363 | +YAMLLocalizationProducer::YAMLLocalizationProducer(std::string locale, |
| 364 | + std::string path) { |
| 365 | + llvm::SmallString<128> DiagnosticsFilePath(path); |
| 366 | + llvm::sys::path::append(DiagnosticsFilePath, locale); |
| 367 | + llvm::sys::path::replace_extension(DiagnosticsFilePath, ".yaml"); |
| 368 | + auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(DiagnosticsFilePath); |
| 369 | + if (!FileBufOrErr) |
| 370 | + llvm_unreachable("Failed to read yaml file"); |
| 371 | + llvm::MemoryBuffer *document = FileBufOrErr->get(); |
| 372 | + LocalizationInput yin(document->getBuffer()); |
| 373 | + yin >> diagnostics; |
| 374 | +} |
| 375 | + |
| 376 | +std::string |
| 377 | +YAMLLocalizationProducer::getMessageOr(DiagID id, |
| 378 | + std::string defaultMessage) const { |
| 379 | + std::string diagnosticMessage = diagnostics[(unsigned)id]; |
| 380 | + if (diagnosticMessage.empty()) |
| 381 | + return defaultMessage; |
| 382 | + return diagnosticMessage; |
| 383 | +} |
| 384 | + |
314 | 385 | bool DiagnosticEngine::isDiagnosticPointsToFirstBadToken(DiagID ID) const {
|
315 | 386 | return storedDiagnosticInfos[(unsigned) ID].pointsToFirstBadToken;
|
316 | 387 | }
|
@@ -1011,10 +1082,17 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
|
1011 | 1082 |
|
1012 | 1083 | const char *DiagnosticEngine::diagnosticStringFor(const DiagID id,
|
1013 | 1084 | bool printDiagnosticName) {
|
| 1085 | + // TODO: Print diagnostic names from `localization`. |
1014 | 1086 | if (printDiagnosticName) {
|
1015 | 1087 | return debugDiagnosticStrings[(unsigned)id];
|
1016 | 1088 | }
|
1017 |
| - return diagnosticStrings[(unsigned)id]; |
| 1089 | + auto defaultMessage = diagnosticStrings[(unsigned)id]; |
| 1090 | + if (localization) { |
| 1091 | + std::string localizedMessage = |
| 1092 | + localization.get()->getMessageOr(id, defaultMessage); |
| 1093 | + return localizedMessage.c_str(); |
| 1094 | + } |
| 1095 | + return defaultMessage; |
1018 | 1096 | }
|
1019 | 1097 |
|
1020 | 1098 | const char *InFlightDiagnostic::fixItStringFor(const FixItID id) {
|
|
0 commit comments