Skip to content

Commit c685d38

Browse files
Refactor DiagnosticEngine to use YAML files
1 parent eae52e1 commit c685d38

File tree

8 files changed

+257
-2
lines changed

8 files changed

+257
-2
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: 13 additions & 2 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
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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/Support/YAMLParser.h"
22+
#include "llvm/Support/YAMLTraits.h"
23+
24+
namespace {
25+
enum LocalDiagID : uint32_t;
26+
}
27+
28+
namespace swift {
29+
namespace diag {
30+
31+
struct DiagnosticNode {
32+
LocalDiagID id;
33+
std::string msg;
34+
};
35+
36+
class LocalizationProducer {
37+
public:
38+
/// If the message isn't available/localized in the current `yaml` file,
39+
/// return the fallback default message.
40+
virtual llvm::StringRef getMessageOr(LocalDiagID id,
41+
llvm::StringRef defaultMessage) const {
42+
return defaultMessage;
43+
}
44+
45+
virtual ~LocalizationProducer() {}
46+
};
47+
48+
class YAMLLocalizationProducer final : public LocalizationProducer {
49+
public:
50+
std::vector<std::string> diagnostics;
51+
explicit YAMLLocalizationProducer(std::string locale, std::string path);
52+
llvm::StringRef getMessageOr(LocalDiagID id,
53+
llvm::StringRef defaultMessage) const override;
54+
};
55+
56+
class LocalizationInput : public llvm::yaml::Input {
57+
using Input::Input;
58+
59+
/// Read diagnostics in the YAML file iteratively
60+
template <typename T, typename Context>
61+
friend typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value,
62+
void>::type
63+
readYAML(llvm::yaml::IO &io, T &Seq, bool, Context &Ctx);
64+
65+
template <typename T>
66+
friend typename std::enable_if<llvm::yaml::has_SequenceTraits<T>::value,
67+
LocalizationInput &>::type
68+
operator>>(LocalizationInput &yin, T &diagnostics);
69+
};
70+
71+
} // namespace diag
72+
} // namespace swift
73+
74+
#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: 8 additions & 0 deletions
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,9 +1012,16 @@ 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
}
1019+
auto defaultMessage = diagnosticStrings[(unsigned)id];
1020+
if (localization) {
1021+
const std::string &localizedMessage =
1022+
localization.get()->getMessageOr((LocalDiagID)id, defaultMessage);
1023+
return localizedMessage.c_str();
1024+
}
10171025
return diagnosticStrings[(unsigned)id];
10181026
}
10191027

lib/AST/LocalizationFormat.cpp

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

0 commit comments

Comments
 (0)