Skip to content

Commit dd3d2cf

Browse files
committed
[Diagnostics][NFC] Introduce Structured fix-its
These are defined with macros like errors/warnings/notes, and make use of format strings and diagnostic arguments. The intent is to leverage diagnostic arguments in the future to disambiguate ambiguously spelled types. Ported a few miscellaneous fix-its to the new system
1 parent b8de310 commit dd3d2cf

File tree

9 files changed

+223
-44
lines changed

9 files changed

+223
-44
lines changed

include/swift/AST/DiagnosticConsumer.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ struct DiagnosticInfo {
4949
std::string Text;
5050

5151
public:
52-
FixIt(CharSourceRange R, StringRef Str)
53-
: Range(R), Text(Str) {}
52+
FixIt(CharSourceRange R, StringRef Str, ArrayRef<DiagnosticArgument> Args);
5453

5554
CharSourceRange getRange() const { return Range; }
5655
StringRef getText() const { return Text; }

include/swift/AST/DiagnosticEngine.h

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -309,8 +309,22 @@ namespace swift {
309309
: OpeningQuotationMark("'"), ClosingQuotationMark("'"),
310310
AKAFormatString("'%s' (aka '%s')"),
311311
OpaqueResultFormatString("'%s' (%s of '%s')") {}
312+
313+
/// When formatting fix-it arguments, don't include quotes or other
314+
/// additions which would result in invalid code.
315+
static DiagnosticFormatOptions formatForFixits() {
316+
return DiagnosticFormatOptions("", "", "%s", "%s");
317+
}
312318
};
313-
319+
320+
enum class FixItID : uint32_t;
321+
322+
/// StructuredFixIt - Represents a fix-it defined in FixIts.def with a format
323+
/// string and optional DiagnosticArguments. The template parameters
324+
/// allow the fixIt... methods on InflightDiagnostic to infer their own
325+
/// template params.
326+
template <typename... ArgTypes> struct StructuredFixIt { FixItID ID; };
327+
314328
/// Diagnostic - This is a specific instance of a diagnostic along with all of
315329
/// the DiagnosticArguments that it requires.
316330
class Diagnostic {
@@ -425,25 +439,89 @@ namespace swift {
425439
/// Add a character-based range to the currently-active diagnostic.
426440
InFlightDiagnostic &highlightChars(SourceLoc Start, SourceLoc End);
427441

442+
static const char *fixItStringFor(const FixItID id);
443+
444+
/// Add a token-based replacement fix-it to the currently-active
445+
/// diagnostic.
446+
template <typename... ArgTypes>
447+
InFlightDiagnostic &
448+
fixItReplace(SourceRange R, StructuredFixIt<ArgTypes...> fixIt,
449+
typename detail::PassArgument<ArgTypes>::type... VArgs) {
450+
SmallVector<DiagnosticArgument, 3> Args;
451+
DiagnosticArgument DiagArgs[] = {
452+
DiagnosticArgument(0), std::move(VArgs)...
453+
};
454+
Args.append(DiagArgs + 1, DiagArgs + 1 + sizeof...(VArgs));
455+
return fixItReplace(R, fixItStringFor(fixIt.ID), std::move(Args));
456+
}
457+
458+
/// Add a character-based replacement fix-it to the currently-active
459+
/// diagnostic.
460+
template <typename... ArgTypes>
461+
InFlightDiagnostic &
462+
fixItReplaceChars(SourceLoc Start, SourceLoc End,
463+
StructuredFixIt<ArgTypes...> fixIt,
464+
typename detail::PassArgument<ArgTypes>::type... VArgs) {
465+
SmallVector<DiagnosticArgument, 3> Args;
466+
DiagnosticArgument DiagArgs[] = {
467+
DiagnosticArgument(0), std::move(VArgs)...
468+
};
469+
Args.append(DiagArgs + 1, DiagArgs + 1 + sizeof...(VArgs));
470+
return fixItReplaceChars(Start, End, fixItStringFor(fixIt.ID),
471+
std::move(Args));
472+
}
473+
474+
/// Add an insertion fix-it to the currently-active diagnostic.
475+
template <typename... ArgTypes>
476+
InFlightDiagnostic &
477+
fixItInsert(SourceLoc L, StructuredFixIt<ArgTypes...> fixIt,
478+
typename detail::PassArgument<ArgTypes>::type... VArgs) {
479+
SmallVector<DiagnosticArgument, 3> Args;
480+
DiagnosticArgument DiagArgs[] = {
481+
DiagnosticArgument(0), std::move(VArgs)...
482+
};
483+
Args.append(DiagArgs + 1, DiagArgs + 1 + sizeof...(VArgs));
484+
return fixItReplaceChars(L, L, fixItStringFor(fixIt.ID), std::move(Args));
485+
}
486+
487+
/// Add an insertion fix-it to the currently-active diagnostic. The
488+
/// text is inserted immediately *after* the token specified.
489+
template <typename... ArgTypes>
490+
InFlightDiagnostic &
491+
fixItInsertAfter(SourceLoc L, StructuredFixIt<ArgTypes...> fixIt,
492+
typename detail::PassArgument<ArgTypes>::type... VArgs) {
493+
SmallVector<DiagnosticArgument, 3> Args;
494+
DiagnosticArgument DiagArgs[] = {
495+
DiagnosticArgument(0), std::move(VArgs)...
496+
};
497+
Args.append(DiagArgs + 1, DiagArgs + 1 + sizeof...(VArgs));
498+
return fixItInsertAfter(L, fixItStringFor(fixIt.ID), std::move(Args));
499+
}
500+
428501
/// Add a token-based replacement fix-it to the currently-active
429502
/// diagnostic.
430-
InFlightDiagnostic &fixItReplace(SourceRange R, StringRef Str);
503+
InFlightDiagnostic &fixItReplace(SourceRange R, StringRef Str) {
504+
return fixItReplace(R, Str, {});
505+
}
431506

432507
/// Add a character-based replacement fix-it to the currently-active
433508
/// diagnostic.
434509
InFlightDiagnostic &fixItReplaceChars(SourceLoc Start, SourceLoc End,
435-
StringRef Str);
510+
StringRef Str) {
511+
return fixItReplaceChars(Start, End, Str, {});
512+
}
436513

437514
/// Add an insertion fix-it to the currently-active diagnostic.
438515
InFlightDiagnostic &fixItInsert(SourceLoc L, StringRef Str) {
439-
return fixItReplaceChars(L, L, Str);
516+
return fixItReplaceChars(L, L, Str, {});
440517
}
441518

442519
/// Add an insertion fix-it to the currently-active diagnostic. The
443520
/// text is inserted immediately *after* the token specified.
444-
///
445-
InFlightDiagnostic &fixItInsertAfter(SourceLoc L, StringRef);
446-
521+
InFlightDiagnostic &fixItInsertAfter(SourceLoc L, StringRef Str) {
522+
return fixItInsertAfter(L, Str, {});
523+
}
524+
447525
/// Add a token-based removal fix-it to the currently-active
448526
/// diagnostic.
449527
InFlightDiagnostic &fixItRemove(SourceRange R);
@@ -457,6 +535,22 @@ namespace swift {
457535
/// Add two replacement fix-it exchanging source ranges to the
458536
/// currently-active diagnostic.
459537
InFlightDiagnostic &fixItExchange(SourceRange R1, SourceRange R2);
538+
539+
private:
540+
InFlightDiagnostic &fixItReplace(SourceRange R, StringRef Str,
541+
ArrayRef<DiagnosticArgument> Args);
542+
543+
InFlightDiagnostic &fixItReplaceChars(SourceLoc Start, SourceLoc End,
544+
StringRef Str,
545+
ArrayRef<DiagnosticArgument> Args);
546+
547+
InFlightDiagnostic &fixItInsert(SourceLoc L, StringRef Str,
548+
ArrayRef<DiagnosticArgument> Args) {
549+
return fixItReplaceChars(L, L, Str, Args);
550+
}
551+
552+
InFlightDiagnostic &fixItInsertAfter(SourceLoc L, StringRef,
553+
ArrayRef<DiagnosticArgument> Args);
460554
};
461555

462556
/// Class to track, map, and remap diagnostic severity and fatality

include/swift/AST/DiagnosticsCommon.h

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,24 @@ namespace swift {
2828
struct Diag;
2929

3030
namespace detail {
31+
// These templates are used to help extract the type arguments of the
32+
// DIAG/ERROR/WARNING/NOTE/REMARK/FIXIT macros.
3133
template<typename T>
3234
struct DiagWithArguments;
3335

3436
template<typename ...ArgTypes>
3537
struct DiagWithArguments<void(ArgTypes...)> {
3638
typedef Diag<ArgTypes...> type;
3739
};
38-
}
40+
41+
template <typename T>
42+
struct StructuredFixItWithArguments;
43+
44+
template <typename... ArgTypes>
45+
struct StructuredFixItWithArguments<void(ArgTypes...)> {
46+
typedef StructuredFixIt<ArgTypes...> type;
47+
};
48+
} // end namespace detail
3949

4050
enum class StaticSpellingKind : uint8_t;
4151

@@ -49,7 +59,14 @@ namespace swift {
4959
#define DIAG(KIND,ID,Options,Text,Signature) \
5060
extern detail::DiagWithArguments<void Signature>::type ID;
5161
#include "DiagnosticsCommon.def"
52-
}
53-
}
62+
} // end namespace diag
63+
64+
namespace fixIt {
65+
// Declare structured fix-it objects with the appropriate type.
66+
#define FIXIT(ID, Text, Signature) \
67+
extern detail::StructuredFixItWithArguments<void Signature>::type ID;
68+
#include "FixIts.def"
69+
} // end namespace fixIt
70+
} // end namespace swift
5471

5572
#endif

include/swift/AST/FixIts.def

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//===--- FixIts.def - Fix-Its Text ------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2019 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 fix-its which can be emitted alongside a diagnostic.
14+
// Each fix-it is described by a unique identifier and text, and is
15+
// followed by a signature describing the diagnostic argument kinds.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#if (!(defined(FIXIT)))
20+
# error Must define FIXIT
21+
#endif
22+
23+
FIXIT(insert_type_coercion, " %select{as!|as}0 %1", (bool, Type))
24+
25+
FIXIT(replace_with_type, "%0", (Type))
26+
FIXIT(insert_type_qualification, "%0.", (Type))
27+
28+
FIXIT(insert_closure_return_type, "%select{| () }1-> %0 %select{|in }1", (Type, bool))
29+
30+
#ifndef DIAG_NO_UNDEF
31+
# undef FIXIT
32+
#endif

lib/AST/DiagnosticConsumer.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@ using namespace swift;
2929

3030
DiagnosticConsumer::~DiagnosticConsumer() = default;
3131

32+
DiagnosticInfo::FixIt::FixIt(CharSourceRange R, StringRef Str,
33+
ArrayRef<DiagnosticArgument> Args): Range(R) {
34+
if (!Args.empty()) {
35+
// FIXME: Defer text formatting to later in the pipeline.
36+
SmallString<16> Buffer;
37+
llvm::raw_svector_ostream OS(Buffer);
38+
DiagnosticEngine::formatDiagnosticText(OS, Str, Args,
39+
DiagnosticFormatOptions::
40+
formatForFixits());
41+
Text = OS.str();
42+
} else {
43+
Text = Str;
44+
}
45+
}
46+
3247
llvm::SMLoc DiagnosticConsumer::getRawLoc(SourceLoc loc) {
3348
return loc.Value;
3449
}

lib/AST/DiagnosticEngine.cpp

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ static constexpr const char *const debugDiagnosticStrings[] = {
108108
"<not a diagnostic>",
109109
};
110110

111+
static constexpr const char *const fixItStrings[] = {
112+
#define FIXIT(ID, Text, Signature) Text,
113+
#include "swift/AST/FixIts.def"
114+
"<not a fix-it>",
115+
};
116+
111117
DiagnosticState::DiagnosticState() {
112118
// Initialize our per-diagnostic state to default
113119
perDiagnosticBehavior.resize(LocalDiagID::NumDiags, Behavior::Unspecified);
@@ -162,10 +168,11 @@ InFlightDiagnostic &InFlightDiagnostic::highlightChars(SourceLoc Start,
162168
/// Add an insertion fix-it to the currently-active diagnostic. The
163169
/// text is inserted immediately *after* the token specified.
164170
///
165-
InFlightDiagnostic &InFlightDiagnostic::fixItInsertAfter(SourceLoc L,
166-
StringRef Str) {
171+
InFlightDiagnostic &
172+
InFlightDiagnostic::fixItInsertAfter(SourceLoc L, StringRef Str,
173+
ArrayRef<DiagnosticArgument> Args) {
167174
L = Lexer::getLocForEndOfToken(Engine->SourceMgr, L);
168-
return fixItInsert(L, Str);
175+
return fixItInsert(L, Str, std::move(Args));
169176
}
170177

171178
/// Add a token-based removal fix-it to the currently-active
@@ -189,13 +196,13 @@ InFlightDiagnostic &InFlightDiagnostic::fixItRemove(SourceRange R) {
189196
charRange = CharSourceRange(charRange.getStart(),
190197
charRange.getByteLength()+1);
191198
}
192-
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(charRange, {}));
199+
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(charRange, {}, {}));
193200
return *this;
194201
}
195202

196-
197-
InFlightDiagnostic &InFlightDiagnostic::fixItReplace(SourceRange R,
198-
StringRef Str) {
203+
InFlightDiagnostic &
204+
InFlightDiagnostic::fixItReplace(SourceRange R, StringRef Str,
205+
ArrayRef<DiagnosticArgument> Args) {
199206
if (Str.empty())
200207
return fixItRemove(R);
201208

@@ -216,17 +223,20 @@ InFlightDiagnostic &InFlightDiagnostic::fixItReplace(SourceRange R,
216223
Str = Str.drop_front();
217224
}
218225

219-
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(charRange, Str));
226+
Engine->getActiveDiagnostic().addFixIt(
227+
Diagnostic::FixIt(charRange, Str, std::move(Args)));
220228
return *this;
221229
}
222230

223-
InFlightDiagnostic &InFlightDiagnostic::fixItReplaceChars(SourceLoc Start,
224-
SourceLoc End,
225-
StringRef Str) {
231+
InFlightDiagnostic &
232+
InFlightDiagnostic::fixItReplaceChars(SourceLoc Start, SourceLoc End,
233+
StringRef Str,
234+
ArrayRef<DiagnosticArgument> Args) {
226235
assert(IsActive && "Cannot modify an inactive diagnostic");
227236
if (Engine && Start.isValid())
228-
Engine->getActiveDiagnostic().addFixIt(Diagnostic::FixIt(
229-
toCharSourceRange(Engine->SourceMgr, Start, End), Str));
237+
Engine->getActiveDiagnostic().addFixIt(
238+
Diagnostic::FixIt(toCharSourceRange(Engine->SourceMgr, Start, End), Str,
239+
std::move(Args)));
230240
return *this;
231241
}
232242

@@ -242,10 +252,10 @@ InFlightDiagnostic &InFlightDiagnostic::fixItExchange(SourceRange R1,
242252
auto text1 = SM.extractText(charRange1);
243253
auto text2 = SM.extractText(charRange2);
244254

245-
Engine->getActiveDiagnostic()
246-
.addFixIt(Diagnostic::FixIt(charRange1, text2));
247-
Engine->getActiveDiagnostic()
248-
.addFixIt(Diagnostic::FixIt(charRange2, text1));
255+
Engine->getActiveDiagnostic().addFixIt(
256+
Diagnostic::FixIt(charRange1, text2, {}));
257+
Engine->getActiveDiagnostic().addFixIt(
258+
Diagnostic::FixIt(charRange2, text1, {}));
249259
return *this;
250260
}
251261

@@ -917,6 +927,10 @@ const char *DiagnosticEngine::diagnosticStringFor(const DiagID id,
917927
return diagnosticStrings[(unsigned)id];
918928
}
919929

930+
const char *InFlightDiagnostic::fixItStringFor(const FixItID id) {
931+
return fixItStrings[(unsigned)id];
932+
}
933+
920934
void DiagnosticEngine::setBufferIndirectlyCausingDiagnosticToInput(
921935
SourceLoc loc) {
922936
// If in the future, nested BufferIndirectlyCausingDiagnosticRAII need be

lib/AST/DiagnosticList.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ enum class swift::DiagID : uint32_t {
2424
static_assert(static_cast<uint32_t>(swift::DiagID::invalid_diagnostic) == 0,
2525
"0 is not the invalid diagnostic ID");
2626

27+
enum class swift::FixItID : uint32_t {
28+
#define FIXIT(ID, Text, Signature) ID,
29+
#include "swift/AST/FixIts.def"
30+
};
31+
2732
// Define all of the diagnostic objects and initialize them with their
2833
// diagnostic IDs.
2934
namespace swift {
@@ -32,4 +37,10 @@ namespace swift {
3237
detail::DiagWithArguments<void Signature>::type ID = { DiagID::ID };
3338
#include "swift/AST/DiagnosticsAll.def"
3439
} // end namespace diag
40+
41+
namespace fixIt {
42+
#define FIXIT(ID, Text, Signature) \
43+
detail::StructuredFixItWithArguments<void Signature>::type ID = {FixItID::ID};
44+
#include "swift/AST/FixIts.def"
45+
} // end namespace fixIt
3546
} // end namespace swift

0 commit comments

Comments
 (0)