Skip to content

Commit 94f1d2c

Browse files
authored
Merge pull request swiftlang#26612 from owenv/formatted_fixits
[Diagnostics][NFCI] Introduce Structured fix-its
2 parents e42dd5a + 68e6065 commit 94f1d2c

15 files changed

+207
-54
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: 82 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+
/// Represents a fix-it defined with a format string and optional
323+
/// DiagnosticArguments. The template parameters allow the
324+
/// 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 {
@@ -333,7 +347,7 @@ namespace swift {
333347
Diagnostic(Diag<ArgTypes...> ID,
334348
typename detail::PassArgument<ArgTypes>::type... VArgs)
335349
: ID(ID.ID) {
336-
DiagnosticArgument DiagArgs[] = {
350+
DiagnosticArgument DiagArgs[] = {
337351
DiagnosticArgument(0), std::move(VArgs)...
338352
};
339353
Args.append(DiagArgs + 1, DiagArgs + 1 + sizeof...(VArgs));
@@ -425,25 +439,70 @@ 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+
DiagnosticArgument DiagArgs[] = { std::move(VArgs)... };
451+
return fixItReplace(R, fixItStringFor(fixIt.ID), DiagArgs);
452+
}
453+
454+
/// Add a character-based replacement fix-it to the currently-active
455+
/// diagnostic.
456+
template <typename... ArgTypes>
457+
InFlightDiagnostic &
458+
fixItReplaceChars(SourceLoc Start, SourceLoc End,
459+
StructuredFixIt<ArgTypes...> fixIt,
460+
typename detail::PassArgument<ArgTypes>::type... VArgs) {
461+
DiagnosticArgument DiagArgs[] = { std::move(VArgs)... };
462+
return fixItReplaceChars(Start, End, fixItStringFor(fixIt.ID), DiagArgs);
463+
}
464+
465+
/// Add an insertion fix-it to the currently-active diagnostic.
466+
template <typename... ArgTypes>
467+
InFlightDiagnostic &
468+
fixItInsert(SourceLoc L, StructuredFixIt<ArgTypes...> fixIt,
469+
typename detail::PassArgument<ArgTypes>::type... VArgs) {
470+
DiagnosticArgument DiagArgs[] = { std::move(VArgs)... };
471+
return fixItReplaceChars(L, L, fixItStringFor(fixIt.ID), DiagArgs);
472+
}
473+
474+
/// Add an insertion fix-it to the currently-active diagnostic. The
475+
/// text is inserted immediately *after* the token specified.
476+
template <typename... ArgTypes>
477+
InFlightDiagnostic &
478+
fixItInsertAfter(SourceLoc L, StructuredFixIt<ArgTypes...> fixIt,
479+
typename detail::PassArgument<ArgTypes>::type... VArgs) {
480+
DiagnosticArgument DiagArgs[] = { std::move(VArgs)... };
481+
return fixItInsertAfter(L, fixItStringFor(fixIt.ID), DiagArgs);
482+
}
483+
428484
/// Add a token-based replacement fix-it to the currently-active
429485
/// diagnostic.
430486
InFlightDiagnostic &fixItReplace(SourceRange R, StringRef Str);
431487

432488
/// Add a character-based replacement fix-it to the currently-active
433489
/// diagnostic.
434490
InFlightDiagnostic &fixItReplaceChars(SourceLoc Start, SourceLoc End,
435-
StringRef Str);
491+
StringRef Str) {
492+
return fixItReplaceChars(Start, End, "%0", {Str});
493+
}
436494

437495
/// Add an insertion fix-it to the currently-active diagnostic.
438496
InFlightDiagnostic &fixItInsert(SourceLoc L, StringRef Str) {
439-
return fixItReplaceChars(L, L, Str);
497+
return fixItReplaceChars(L, L, "%0", {Str});
440498
}
441499

442500
/// Add an insertion fix-it to the currently-active diagnostic. The
443501
/// text is inserted immediately *after* the token specified.
444-
///
445-
InFlightDiagnostic &fixItInsertAfter(SourceLoc L, StringRef);
446-
502+
InFlightDiagnostic &fixItInsertAfter(SourceLoc L, StringRef Str) {
503+
return fixItInsertAfter(L, "%0", {Str});
504+
}
505+
447506
/// Add a token-based removal fix-it to the currently-active
448507
/// diagnostic.
449508
InFlightDiagnostic &fixItRemove(SourceRange R);
@@ -457,6 +516,22 @@ namespace swift {
457516
/// Add two replacement fix-it exchanging source ranges to the
458517
/// currently-active diagnostic.
459518
InFlightDiagnostic &fixItExchange(SourceRange R1, SourceRange R2);
519+
520+
private:
521+
InFlightDiagnostic &fixItReplace(SourceRange R, StringRef FormatString,
522+
ArrayRef<DiagnosticArgument> Args);
523+
524+
InFlightDiagnostic &fixItReplaceChars(SourceLoc Start, SourceLoc End,
525+
StringRef FormatString,
526+
ArrayRef<DiagnosticArgument> Args);
527+
528+
InFlightDiagnostic &fixItInsert(SourceLoc L, StringRef FormatString,
529+
ArrayRef<DiagnosticArgument> Args) {
530+
return fixItReplaceChars(L, L, FormatString, Args);
531+
}
532+
533+
InFlightDiagnostic &fixItInsertAfter(SourceLoc L, StringRef FormatString,
534+
ArrayRef<DiagnosticArgument> Args);
460535
};
461536

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

include/swift/AST/DiagnosticsAll.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
DIAG(REMARK,ID,Options,Text,Signature)
3939
#endif
4040

41+
#ifndef FIXIT
42+
# define FIXIT(ID, Text, Signature)
43+
#endif
44+
4145
#define DIAG_NO_UNDEF
4246

4347
#include "DiagnosticsCommon.def"
@@ -60,3 +64,4 @@
6064
#undef WARNING
6165
#undef ERROR
6266
#undef REMARK
67+
#undef FIXIT

include/swift/AST/DiagnosticsCommon.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@
4141
DIAG(REMARK,ID,Options,Text,Signature)
4242
#endif
4343

44+
#ifndef FIXIT
45+
# define FIXIT(ID, Text, Signature)
46+
#endif
47+
4448
ERROR(invalid_diagnostic,none,
4549
"INTERNAL ERROR: this diagnostic should not be produced", ())
4650

@@ -166,4 +170,5 @@ NOTE(kind_declname_declared_here,none,
166170
# undef NOTE
167171
# undef WARNING
168172
# undef ERROR
173+
# undef FIXIT
169174
#endif

include/swift/AST/DiagnosticsCommon.h

Lines changed: 15 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

@@ -48,8 +58,10 @@ namespace swift {
4858
// Declare common diagnostics objects with their appropriate types.
4959
#define DIAG(KIND,ID,Options,Text,Signature) \
5060
extern detail::DiagWithArguments<void Signature>::type ID;
61+
#define FIXIT(ID, Text, Signature) \
62+
extern detail::StructuredFixItWithArguments<void Signature>::type ID;
5163
#include "DiagnosticsCommon.def"
52-
}
53-
}
64+
} // end namespace diag
65+
} // end namespace swift
5466

5567
#endif

include/swift/AST/DiagnosticsParse.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
DIAG(NOTE,ID,Options,Text,Signature)
3737
#endif
3838

39+
#ifndef FIXIT
40+
# define FIXIT(ID, Text, Signature)
41+
#endif
42+
3943
//==============================================================================
4044
// MARK: Lexing and Parsing diagnostics
4145
//==============================================================================
@@ -1638,4 +1642,5 @@ ERROR(unknown_syntax_entity, PointsToFirstBadToken,
16381642
# undef NOTE
16391643
# undef WARNING
16401644
# undef ERROR
1645+
# undef FIXIT
16411646
#endif

include/swift/AST/DiagnosticsParse.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ namespace swift {
2525
// Declare common diagnostics objects with their appropriate types.
2626
#define DIAG(KIND,ID,Options,Text,Signature) \
2727
extern detail::DiagWithArguments<void Signature>::type ID;
28+
#define FIXIT(ID,Text,Signature) \
29+
extern detail::StructuredFixItWithArguments<void Signature>::type ID;
2830
#include "DiagnosticsParse.def"
2931
}
3032
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
DIAG(NOTE,ID,Options,Text,Signature)
3838
#endif
3939

40+
#ifndef FIXIT
41+
# define FIXIT(ID, Text, Signature)
42+
#endif
43+
4044
NOTE(decl_declared_here,none,
4145
"%0 declared here", (DeclName))
4246
NOTE(kind_declared_here,none,
@@ -145,6 +149,8 @@ ERROR(could_not_use_member_on_existential,none,
145149
"member %1 cannot be used on value of protocol type %0; use a generic"
146150
" constraint instead",
147151
(Type, DeclName))
152+
FIXIT(replace_with_type,"%0",(Type))
153+
FIXIT(insert_type_qualification,"%0.",(Type))
148154

149155
ERROR(candidate_inaccessible,none,
150156
"%0 is inaccessible due to "
@@ -297,6 +303,9 @@ ERROR(cannot_infer_closure_type,none,
297303
ERROR(cannot_infer_closure_result_type,none,
298304
"unable to infer complex closure return type; "
299305
"add explicit type to disambiguate", ())
306+
FIXIT(insert_closure_return_type,
307+
"%select{| () }1-> %0 %select{|in }1",
308+
(Type, bool))
300309

301310
ERROR(incorrect_explicit_closure_result,none,
302311
"declared closure result %0 is incompatible with contextual type %1",
@@ -344,6 +353,9 @@ ERROR(cannot_throw_error_code,none,
344353
"thrown error code type %0 does not conform to 'Error'; construct an %1 "
345354
"instance", (Type, Type))
346355

356+
FIXIT(insert_type_coercion,
357+
" %select{as!|as}0 %1",(bool, Type))
358+
347359
ERROR(bad_yield_count,none,
348360
"expected %0 yield value(s)", (unsigned))
349361

@@ -4183,6 +4195,9 @@ NOTE(availability_guard_with_version_check, none,
41834195

41844196
NOTE(availability_add_attribute, none,
41854197
"add @available attribute to enclosing %0", (DescriptiveDeclKind))
4198+
FIXIT(insert_available_attr,
4199+
"@available(%0 %1, *)\n%2",
4200+
(StringRef, StringRef, StringRef))
41864201

41874202
ERROR(availability_accessor_only_version_newer, none,
41884203
"%select{getter|setter}0 for %1 is only available in %2 %3"
@@ -4600,4 +4615,5 @@ ERROR(function_builder_arguments, none,
46004615
# undef NOTE
46014616
# undef WARNING
46024617
# undef ERROR
4618+
# undef FIXIT
46034619
#endif

include/swift/AST/DiagnosticsSema.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ namespace swift {
3535
// Declare common diagnostics objects with their appropriate types.
3636
#define DIAG(KIND,ID,Options,Text,Signature) \
3737
extern detail::DiagWithArguments<void Signature>::type ID;
38+
#define FIXIT(ID,Text,Signature) \
39+
extern detail::StructuredFixItWithArguments<void Signature>::type ID;
3840
#include "DiagnosticsSema.def"
3941
}
4042
}

lib/AST/DiagnosticConsumer.cpp

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

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

32+
DiagnosticInfo::FixIt::FixIt(CharSourceRange R, StringRef Str,
33+
ArrayRef<DiagnosticArgument> Args) : Range(R) {
34+
// FIXME: Defer text formatting to later in the pipeline.
35+
llvm::raw_string_ostream OS(Text);
36+
DiagnosticEngine::formatDiagnosticText(OS, Str, Args,
37+
DiagnosticFormatOptions::
38+
formatForFixIts());
39+
}
40+
3241
llvm::SMLoc DiagnosticConsumer::getRawLoc(SourceLoc loc) {
3342
return loc.Value;
3443
}

0 commit comments

Comments
 (0)