Skip to content

Commit 6a44443

Browse files
[Diag] Refactor DiagnosticEngine to use YAML files - Reopen (swiftlang#32483)
Introduce localization support for diagnostics via file-per-language store in `YAML` format. Add `LocalizationFormat` interface and its implementation `YAMLLocalizationProducer`. Augment `DiagnosticEngine` to support localization when applicable.
1 parent e38d103 commit 6a44443

File tree

8 files changed

+263
-5
lines changed

8 files changed

+263
-5
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: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
#ifndef SWIFT_BASIC_DIAGNOSTICENGINE_H
1919
#define SWIFT_BASIC_DIAGNOSTICENGINE_H
2020

21-
#include "swift/AST/TypeLoc.h"
2221
#include "swift/AST/DeclNameLoc.h"
2322
#include "swift/AST/DiagnosticConsumer.h"
23+
#include "swift/AST/LocalizationFormat.h"
24+
#include "swift/AST/TypeLoc.h"
2425
#include "llvm/ADT/StringSet.h"
2526
#include "llvm/Support/Allocator.h"
2627
#include "llvm/Support/VersionTuple.h"
@@ -629,7 +630,7 @@ namespace swift {
629630
DiagnosticState(DiagnosticState &&) = default;
630631
DiagnosticState &operator=(DiagnosticState &&) = default;
631632
};
632-
633+
633634
/// Class responsible for formatting diagnostics and presenting them
634635
/// to the user.
635636
class DiagnosticEngine {
@@ -663,6 +664,10 @@ namespace swift {
663664
/// but rather stored until all transactions complete.
664665
llvm::StringSet<llvm::BumpPtrAllocator &> TransactionStrings;
665666

667+
/// Diagnostic producer to handle the logic behind retrieving a localized
668+
/// diagnostic message.
669+
std::unique_ptr<diag::LocalizationProducer> localization;
670+
666671
/// The number of open diagnostic transactions. Diagnostics are only
667672
/// emitted once all transactions have closed.
668673
unsigned TransactionCount = 0;
@@ -734,6 +739,12 @@ namespace swift {
734739
return diagnosticDocumentationPath;
735740
}
736741

742+
void setLocalization(std::string locale, std::string path) {
743+
if (!locale.empty() && !path.empty())
744+
localization =
745+
std::make_unique<diag::YAMLLocalizationProducer>(locale, path);
746+
}
747+
737748
void ignoreDiagnostic(DiagID id) {
738749
state.setDiagnosticBehavior(id, DiagnosticState::Behavior::Ignore);
739750
}
@@ -955,8 +966,7 @@ namespace swift {
955966
void emitTentativeDiagnostics();
956967

957968
public:
958-
static const char *diagnosticStringFor(const DiagID id,
959-
bool printDiagnosticName);
969+
const char *diagnosticStringFor(const DiagID id, bool printDiagnosticName);
960970

961971
/// If there is no clear .dia file for a diagnostic, put it in the one
962972
/// corresponding to the SourceLoc given here.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//===--- LocalizationFormat.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 format for localized diagnostic messages.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_LOCALIZATIONFORMAT_H
19+
#define SWIFT_LOCALIZATIONFORMAT_H
20+
21+
#include "llvm/ADT/StringRef.h"
22+
#include "llvm/Support/YAMLParser.h"
23+
#include "llvm/Support/YAMLTraits.h"
24+
#include <string>
25+
#include <type_traits>
26+
27+
namespace swift {
28+
enum class DiagID : uint32_t;
29+
30+
namespace diag {
31+
class LocalizationProducer {
32+
public:
33+
/// If the message isn't available/localized in the current `yaml` file,
34+
/// return the fallback default message.
35+
virtual llvm::StringRef getMessageOr(swift::DiagID id,
36+
llvm::StringRef defaultMessage) const {
37+
return defaultMessage;
38+
}
39+
40+
virtual ~LocalizationProducer() {}
41+
};
42+
43+
class YAMLLocalizationProducer final : public LocalizationProducer {
44+
public:
45+
std::vector<std::string> diagnostics;
46+
explicit YAMLLocalizationProducer(std::string locale, std::string path);
47+
llvm::StringRef getMessageOr(swift::DiagID id,
48+
llvm::StringRef defaultMessage) const override;
49+
};
50+
51+
class LocalizationInput : public llvm::yaml::Input {
52+
using Input::Input;
53+
54+
/// Read diagnostics in the YAML file iteratively
55+
template <typename T, typename Context>
56+
friend typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value,
57+
void>::type
58+
readYAML(llvm::yaml::IO &io, T &Seq, bool, Context &Ctx);
59+
60+
template <typename T>
61+
friend typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value,
62+
LocalizationInput &>::type
63+
operator>>(LocalizationInput &yin, T &diagnostics);
64+
};
65+
66+
} // namespace diag
67+
} // namespace swift
68+
69+
#endif

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 the English 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/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 the French language.
14+
# Each diagnostic is described in the following format:
15+
# - id: "<diagnostic-id>"
16+
# msg: "<diagnostic-message>"
17+
#
18+
#===----------------------------------------------------------------------===#
19+

lib/AST/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ add_swift_host_library(swiftAST STATIC
5656
IndexSubset.cpp
5757
InlinableText.cpp
5858
LayoutConstraint.cpp
59+
LocalizationFormat.cpp
5960
Module.cpp
6061
ModuleDependencies.cpp
6162
ModuleLoader.cpp

lib/AST/DiagnosticEngine.cpp

Lines changed: 9 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/LocalizationFormat.h"
2324
#include "swift/AST/Module.h"
2425
#include "swift/AST/Pattern.h"
2526
#include "swift/AST/PrintOptions.h"
@@ -1011,10 +1012,17 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
10111012

10121013
const char *DiagnosticEngine::diagnosticStringFor(const DiagID id,
10131014
bool printDiagnosticName) {
1015+
// TODO: Print diagnostic names from `localization`.
10141016
if (printDiagnosticName) {
10151017
return debugDiagnosticStrings[(unsigned)id];
10161018
}
1017-
return diagnosticStrings[(unsigned)id];
1019+
auto defaultMessage = diagnosticStrings[(unsigned)id];
1020+
if (localization) {
1021+
auto localizedMessage =
1022+
localization.get()->getMessageOr(id, defaultMessage);
1023+
return localizedMessage.data();
1024+
}
1025+
return defaultMessage;
10181026
}
10191027

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

lib/AST/LocalizationFormat.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
//===--- LocalizationFormat.cpp - 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 implements the format for localized diagnostic messages.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#include "swift/AST/LocalizationFormat.h"
19+
#include "llvm/ADT/SmallString.h"
20+
#include "llvm/ADT/StringRef.h"
21+
#include "llvm/Support/CommandLine.h"
22+
#include "llvm/Support/MemoryBuffer.h"
23+
#include "llvm/Support/YAMLParser.h"
24+
#include "llvm/Support/YAMLTraits.h"
25+
#include <string>
26+
#include <type_traits>
27+
28+
namespace {
29+
enum LocalDiagID : uint32_t {
30+
#define DIAG(KIND, ID, Options, Text, Signature) ID,
31+
#include "swift/AST/DiagnosticsAll.def"
32+
NumDiags
33+
};
34+
35+
struct DiagnosticNode {
36+
uint32_t id;
37+
std::string msg;
38+
};
39+
} // namespace
40+
41+
namespace llvm {
42+
namespace yaml {
43+
44+
template <> struct ScalarEnumerationTraits<LocalDiagID> {
45+
static void enumeration(IO &io, LocalDiagID &value) {
46+
#define DIAG(KIND, ID, Options, Text, Signature) \
47+
io.enumCase(value, #ID, LocalDiagID::ID);
48+
#include "swift/AST/DiagnosticsAll.def"
49+
}
50+
};
51+
52+
template <> struct MappingTraits<DiagnosticNode> {
53+
static void mapping(IO &io, DiagnosticNode &node) {
54+
LocalDiagID diagID;
55+
io.mapRequired("id", diagID);
56+
io.mapRequired("msg", node.msg);
57+
node.id = static_cast<uint32_t>(diagID);
58+
}
59+
};
60+
61+
} // namespace yaml
62+
} // namespace llvm
63+
64+
namespace swift {
65+
namespace diag {
66+
67+
YAMLLocalizationProducer::YAMLLocalizationProducer(std::string locale,
68+
std::string path) {
69+
llvm::SmallString<128> DiagnosticsFilePath(path);
70+
llvm::sys::path::append(DiagnosticsFilePath, locale);
71+
llvm::sys::path::replace_extension(DiagnosticsFilePath, ".yaml");
72+
auto FileBufOrErr = llvm::MemoryBuffer::getFileOrSTDIN(DiagnosticsFilePath);
73+
// Absence of localizations shouldn't crash the compiler.
74+
if (!FileBufOrErr)
75+
return;
76+
llvm::MemoryBuffer *document = FileBufOrErr->get();
77+
diag::LocalizationInput yin(document->getBuffer());
78+
yin >> diagnostics;
79+
}
80+
81+
llvm::StringRef
82+
YAMLLocalizationProducer::getMessageOr(swift::DiagID id,
83+
llvm::StringRef defaultMessage) const {
84+
if (diagnostics.empty())
85+
return defaultMessage;
86+
const std::string &diagnosticMessage = diagnostics[(unsigned)id];
87+
if (diagnosticMessage.empty())
88+
return defaultMessage;
89+
return diagnosticMessage;
90+
}
91+
92+
template <typename T, typename Context>
93+
typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value, void>::type
94+
readYAML(llvm::yaml::IO &io, T &Seq, bool, Context &Ctx) {
95+
unsigned count = io.beginSequence();
96+
if (count)
97+
Seq.resize(LocalDiagID::NumDiags);
98+
for (unsigned i = 0; i < count; ++i) {
99+
void *SaveInfo;
100+
if (io.preflightElement(i, SaveInfo)) {
101+
DiagnosticNode current;
102+
yamlize(io, current, true, Ctx);
103+
io.postflightElement(SaveInfo);
104+
// YAML file isn't guaranteed to have diagnostics in order of their
105+
// declaration in `.def` files, to accommodate that we need to leave
106+
// holes in diagnostic array for diagnostics which haven't yet been
107+
// localized and for the ones that have `DiagnosticNode::id`
108+
// indicates their position.
109+
Seq[static_cast<unsigned>(current.id)] = std::move(current.msg);
110+
}
111+
}
112+
io.endSequence();
113+
}
114+
115+
template <typename T>
116+
typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value,
117+
LocalizationInput &>::type
118+
operator>>(LocalizationInput &yin, T &diagnostics) {
119+
llvm::yaml::EmptyContext Ctx;
120+
if (yin.setCurrentDocument()) {
121+
// If YAML file's format doesn't match the current format in
122+
// DiagnosticMessageFormat, will throw an error.
123+
readYAML(yin, diagnostics, true, Ctx);
124+
}
125+
return yin;
126+
}
127+
128+
} // namespace diag
129+
} // namespace swift

0 commit comments

Comments
 (0)