Skip to content

Commit a332156

Browse files
authored
Merge pull request #32239 from HassanElDesouky/GSoC-DiagEngineRefactor
[GSoC] Refactor DiagnosticEngine to use `YAML` files
2 parents 0e94708 + 123af81 commit a332156

File tree

6 files changed

+197
-4
lines changed

6 files changed

+197
-4
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 & 3 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,7 +634,26 @@ namespace swift {
629634
DiagnosticState(DiagnosticState &&) = default;
630635
DiagnosticState &operator=(DiagnosticState &&) = default;
631636
};
632-
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<std::string> diagnostics;
653+
explicit YAMLLocalizationProducer(std::string locale, std::string path);
654+
std::string getMessageOr(DiagID id,
655+
std::string defaultMessage) const override;
656+
};
633657
/// Class responsible for formatting diagnostics and presenting them
634658
/// to the user.
635659
class DiagnosticEngine {
@@ -663,6 +687,10 @@ namespace swift {
663687
/// but rather stored until all transactions complete.
664688
llvm::StringSet<llvm::BumpPtrAllocator &> TransactionStrings;
665689

690+
/// Diagnostic producer to handle the logic behind retriving a localized
691+
/// 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,7 @@ 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, bool printDiagnosticName);
960992

961993
/// If there is no clear .dia file for a diagnostic, put it in the one
962994
/// 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 ---*-
2+
//C++ -*-===//
3+
//
4+
// This source file is part of the Swift.org open source project
5+
//
6+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
7+
// Licensed under Apache License v2.0 with Runtime Library Exception
8+
//
9+
// See https://swift.org/LICENSE.txt for license information
10+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
11+
//
12+
//===----------------------------------------------------------------------===//
13+
//
14+
// This file defines the YAML format for diagnostic messages.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#include "DiagnosticList.cpp"
19+
#include "llvm/Support/YAMLTraits.h"
20+
21+
namespace llvm {
22+
namespace yaml {
23+
24+
template <> struct ScalarEnumerationTraits<swift::DiagID> {
25+
static void enumeration(IO &io, swift::DiagID &value) {
26+
#define DIAG(KIND, ID, Options, Text, Signature) \
27+
io.enumCase(value, #ID, swift::DiagID::ID);
28+
#include "swift/AST/DiagnosticsAll.def"
29+
}
30+
};
31+
32+
template <> 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+
} // namespace yaml
40+
} // namespace llvm
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: 79 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"
@@ -32,6 +33,8 @@
3233
#include "llvm/ADT/Twine.h"
3334
#include "llvm/Support/CommandLine.h"
3435
#include "llvm/Support/Format.h"
36+
#include "llvm/Support/YAMLParser.h"
37+
#include "llvm/Support/YAMLTraits.h"
3538
#include "llvm/Support/raw_ostream.h"
3639

3740
using namespace swift;
@@ -140,6 +143,52 @@ 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 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+
143192
DiagnosticState::DiagnosticState() {
144193
// Initialize our per-diagnostic state to default
145194
perDiagnosticBehavior.resize(LocalDiagID::NumDiags, Behavior::Unspecified);
@@ -311,6 +360,28 @@ void Diagnostic::addChildNote(Diagnostic &&D) {
311360
ChildNotes.push_back(std::move(D));
312361
}
313362

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+
314385
bool DiagnosticEngine::isDiagnosticPointsToFirstBadToken(DiagID ID) const {
315386
return storedDiagnosticInfos[(unsigned) ID].pointsToFirstBadToken;
316387
}
@@ -1011,10 +1082,17 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
10111082

10121083
const char *DiagnosticEngine::diagnosticStringFor(const DiagID id,
10131084
bool printDiagnosticName) {
1085+
// TODO: Print diagnostic names from `localization`.
10141086
if (printDiagnosticName) {
10151087
return debugDiagnosticStrings[(unsigned)id];
10161088
}
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;
10181096
}
10191097

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

0 commit comments

Comments
 (0)