Skip to content

Commit 4e29883

Browse files
Refactor DiagnosticEngine to use YAML files
1 parent bded632 commit 4e29883

File tree

6 files changed

+198
-3
lines changed

6 files changed

+198
-3
lines changed

include/swift/AST/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
swift_install_in_component(DIRECTORY diagnostics
2+
DESTINATION "share/swift/"
3+
COMPONENT compiler)

include/swift/AST/DiagnosticEngine.h

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ namespace swift {
5858
DiagID ID;
5959
};
6060

61+
struct DiagnosticNode {
62+
DiagID id;
63+
std::string msg;
64+
};
65+
6166
namespace detail {
6267
/// Describes how to pass a diagnostic argument of the given type.
6368
///
@@ -629,6 +634,26 @@ namespace swift {
629634
DiagnosticState(DiagnosticState &&) = default;
630635
DiagnosticState &operator=(DiagnosticState &&) = default;
631636
};
637+
638+
class LocalizationProducer {
639+
public:
640+
/// If the message isn't available/localized in the current `yaml` file,
641+
/// return the fallback default message.
642+
virtual std::string getMessageOr(DiagID id,
643+
std::string defaultMessage) const {
644+
return defaultMessage;
645+
}
646+
647+
virtual ~LocalizationProducer() { }
648+
};
649+
650+
class YAMLLocalizationProducer final : public LocalizationProducer {
651+
public:
652+
std::vector<DiagnosticNode> diagnostics;
653+
explicit YAMLLocalizationProducer(std::string locale, std::string path);
654+
std::string getMessageOr(DiagID id,
655+
std::string defaultMessage) const override;
656+
};
632657

633658
/// Class responsible for formatting diagnostics and presenting them
634659
/// to the user.
@@ -663,6 +688,9 @@ namespace swift {
663688
/// but rather stored until all transactions complete.
664689
llvm::StringSet<llvm::BumpPtrAllocator &> TransactionStrings;
665690

691+
/// Diagnostic producer to handle the logic behind retriving a localized diagnostic message.
692+
std::unique_ptr<LocalizationProducer> localization;
693+
666694
/// The number of open diagnostic transactions. Diagnostics are only
667695
/// emitted once all transactions have closed.
668696
unsigned TransactionCount = 0;
@@ -734,6 +762,11 @@ namespace swift {
734762
return diagnosticDocumentationPath;
735763
}
736764

765+
void setLocalization(std::string locale, std::string path) {
766+
if (!locale.empty() && !path.empty())
767+
localization = std::make_unique<YAMLLocalizationProducer>(locale, path);
768+
}
769+
737770
void ignoreDiagnostic(DiagID id) {
738771
state.setDiagnosticBehavior(id, DiagnosticState::Behavior::Ignore);
739772
}
@@ -955,8 +988,8 @@ namespace swift {
955988
void emitTentativeDiagnostics();
956989

957990
public:
958-
static const char *diagnosticStringFor(const DiagID id,
959-
bool printDiagnosticName);
991+
const char *diagnosticStringFor(const DiagID id,
992+
bool printDiagnosticName);
960993

961994
/// If there is no clear .dia file for a diagnostic, put it in the one
962995
/// corresponding to the SourceLoc given here.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===--- DiagnosticMessageFormat.h - YAML format for Diagnostic Messages ---*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2020 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 defines the YAML format for diagnostic messages.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#include "llvm/Support/YAMLTraits.h"
18+
#include "DiagnosticList.cpp"
19+
20+
namespace llvm {
21+
namespace yaml {
22+
23+
template<>
24+
struct ScalarEnumerationTraits<swift::DiagID> {
25+
static void enumeration(IO &io, swift::DiagID &value) {
26+
#define DIAG(KIND,ID,Options,Text,Signature) io.enumCase(value, #ID, swift::DiagID::ID);
27+
#include "swift/AST/DiagnosticsAll.def"
28+
}
29+
};
30+
31+
template <>
32+
struct MappingTraits<swift::DiagnosticNode> {
33+
static void mapping(IO &io, swift::DiagnosticNode &node) {
34+
io.mapRequired("id", node.id);
35+
io.mapRequired("msg", node.msg);
36+
}
37+
};
38+
39+
}
40+
}
41+
42+
LLVM_YAML_IS_SEQUENCE_VECTOR(swift::DiagnosticNode)

include/swift/AST/Diagnostics/fr.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#===--- fr.yaml - Localized diagnostic messages for French ---*- YAML -*-===#
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2020 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 defines the diagnostic messages for French language.
14+
# Each diagnostic is described in the following format:
15+
# - id: <diagnostic-id>
16+
# msg: <diagnostic-message>
17+
#
18+
#===----------------------------------------------------------------------===#
19+

include/swift/AST/diagnostics/en.yaml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#===--- en.yaml - Localized diagnostic messages for English ---*- YAML -*-===#
2+
#
3+
# This source file is part of the Swift.org open source project
4+
#
5+
# Copyright (c) 2014 - 2020 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 defines the diagnostic messages for English language.
14+
# Each diagnostic is described in the following format:
15+
# - id: <diagnostic-id>
16+
# msg: <diagnostic-message>
17+
#
18+
#===----------------------------------------------------------------------===#
19+

lib/AST/DiagnosticEngine.cpp

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "swift/AST/ASTPrinter.h"
2121
#include "swift/AST/Decl.h"
2222
#include "swift/AST/DiagnosticSuppression.h"
23+
#include "swift/AST/DiagnosticMessageFormat.h"
2324
#include "swift/AST/Module.h"
2425
#include "swift/AST/Pattern.h"
2526
#include "swift/AST/PrintOptions.h"
@@ -33,6 +34,8 @@
3334
#include "llvm/Support/CommandLine.h"
3435
#include "llvm/Support/Format.h"
3536
#include "llvm/Support/raw_ostream.h"
37+
#include "llvm/Support/YAMLParser.h"
38+
#include "llvm/Support/YAMLTraits.h"
3639

3740
using namespace swift;
3841

@@ -140,6 +143,53 @@ struct EducationalNotes {
140143
static constexpr EducationalNotes<LocalDiagID::NumDiags> _EducationalNotes = EducationalNotes<LocalDiagID::NumDiags>();
141144
static constexpr auto educationalNotes = _EducationalNotes.value;
142145

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
152+
typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value, 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+
166+
// YAML file isn't guaranteed to have diagnostics in order of their
167+
// declaration in `.def` files, to accommodate that we need to leave
168+
// holes in diagnostic array for diagnostics which haven't yet been
169+
// localized and for the ones that
170+
// have `DiagnosticNode::id` indicates their position.
171+
Seq[static_cast<unsigned>(current.id)] = std::move(current);
172+
}
173+
}
174+
io.endSequence();
175+
}
176+
177+
template <typename T>
178+
inline friend
179+
typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value,
180+
LocalizationInput &>::type
181+
operator>> (LocalizationInput &yin, T &diagnostics) {
182+
llvm::yaml::EmptyContext Ctx;
183+
if (yin.setCurrentDocument()) {
184+
// If YAML file's format doesn't match the current format in DiagnosticMessageFormat,
185+
// will through an error.
186+
readYAML(yin, diagnostics, true, Ctx);
187+
}
188+
189+
return yin;
190+
}
191+
};
192+
143193
DiagnosticState::DiagnosticState() {
144194
// Initialize our per-diagnostic state to default
145195
perDiagnosticBehavior.resize(LocalDiagID::NumDiags, Behavior::Unspecified);
@@ -311,6 +361,29 @@ void Diagnostic::addChildNote(Diagnostic &&D) {
311361
ChildNotes.push_back(std::move(D));
312362
}
313363

364+
YAMLLocalizationProducer::YAMLLocalizationProducer(std::string locale,
365+
std::string path) {
366+
llvm::SmallString<128> DiagnosticsFilePath(path);
367+
llvm::sys::path::append(DiagnosticsFilePath, locale);
368+
llvm::sys::path::replace_extension(DiagnosticsFilePath, ".yaml");
369+
auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(DiagnosticsFilePath);
370+
if (!FileBufOrErr)
371+
llvm_unreachable("Failed to read yaml file");
372+
373+
llvm::MemoryBuffer *document = FileBufOrErr->get();
374+
LocalizationInput yin(document->getBuffer());
375+
yin >> diagnostics;
376+
}
377+
378+
std::string
379+
YAMLLocalizationProducer::getMessageOr(DiagID id,
380+
std::string defaultMessage) const {
381+
std::string diagnosticMessage = diagnostics[(unsigned)id].msg;
382+
if (diagnosticMessage.empty())
383+
return defaultMessage;
384+
return diagnosticMessage;
385+
}
386+
314387
bool DiagnosticEngine::isDiagnosticPointsToFirstBadToken(DiagID ID) const {
315388
return storedDiagnosticInfos[(unsigned) ID].pointsToFirstBadToken;
316389
}
@@ -1011,10 +1084,16 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
10111084

10121085
const char *DiagnosticEngine::diagnosticStringFor(const DiagID id,
10131086
bool printDiagnosticName) {
1087+
// TODO: Print diagnostic names from `localization`.
10141088
if (printDiagnosticName) {
10151089
return debugDiagnosticStrings[(unsigned)id];
10161090
}
1017-
return diagnosticStrings[(unsigned)id];
1091+
auto defaultMessage = diagnosticStrings[(unsigned)id];
1092+
if (localization) {
1093+
std::string localizedMessage = localization.get()->getMessageOr(id, defaultMessage);
1094+
return localizedMessage.c_str();
1095+
}
1096+
return defaultMessage;
10181097
}
10191098

10201099
const char *InFlightDiagnostic::fixItStringFor(const FixItID id) {

0 commit comments

Comments
 (0)