mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Diagnostics] Eliminate educational notes in favor of diagnostic groups
We've been converging the implementations of educational notes and diagnostic groups, where both provide category information in diagnostics (e.g., `[#StrictMemorySafety]`) and corresponding short-form documentation files. The diagnostic group model is more useful in a few ways: * It provides warnings-as-errors control for warnings in the group * It is easier to associate a diagnostic with a group with GROUPED_ERROR/GROUPED_WARNING than it is to have a separate diagnostic ID -> mapping. * It is easier to see our progress on diagnostic-group coverage * It provides an easy name to use for diagnostic purposes. Collapse the educational-notes infrastructure into diagnostic groups, migrating all of the existing educational notes into new groups. Simplify the code paths that dealt with multiple educational notes to have a single, possibly-missing "category documentation URL", which is how we're treating this.
This commit is contained in:
@@ -84,34 +84,35 @@ The correct spelling of this feature is "fix-it" rather than "fixit". In [camelc
|
||||
|
||||
[camelcased]: https://en.wikipedia.org/wiki/Camel_case
|
||||
|
||||
### Educational Notes ###
|
||||
### Diagnostic Groups ###
|
||||
|
||||
Educational notes are short-form documentation attached to a diagnostic which explain relevant language concepts. They are intended to further Swift's goal of progressive disclosure by providing a learning resource at the point of use when encountering an error message for the first time. In very limited circumstances, they also allow the main diagnostic message to use precise terminology (e.g. nominal types) which would otherwise be too unfriendly for beginners.
|
||||
Diagnostic groups collect some number of diagnostics together under a common group name with short-form documentation to help explain relevant language concepts. For warnings, diagnostic groups also provide a way to control whether the warnings within that group are escalated to errors or not wit compiler flags such as `-Werror <groupname>` and `-Wwarning <groupname>`. They are intended to further Swift's goal of progressive disclosure by providing a learning resource at the point of use when encountering an error message for the first time. In very limited circumstances, they also allow the main diagnostic message to use precise terminology (e.g. nominal types) which would otherwise be too unfriendly for beginners.
|
||||
|
||||
When outputting diagnostics on the command line, educational notes will be printed after the main diagnostic body if enabled using the `-print-educational-notes` driver option. When presented in an IDE, it's expected they will be collapsed under a disclosure arrow, info button, or similar to avoid cluttering output.
|
||||
When outputting diagnostics on the command line, diagnostic group names will be printed as `[#GroupName]` at the end of the main diagnostic, with a footnote that links to the corresponding documentation. When presented in an IDE, it's expected they will be collapsed under a disclosure arrow, info button, or similar to avoid cluttering output.
|
||||
|
||||
Educational notes should:
|
||||
- Explain a single language concept. This makes them easy to reuse across related diagnostics and helps keep them clear, concise, and easy to understand.
|
||||
Diagnostic group documentation should:
|
||||
- Cover a single language concept. This helps keep them clear, concise, and easy to understand.
|
||||
- Be written in unabbreviated English. These are longer-form messages compared to the main diagnostic, so there's no need to omit needless words and punctuation.
|
||||
- Not generally exceed 3-4 paragraphs. Educational notes should be clear and easily digestible. Messages which are too long also have the potential to create UX issues on the command line.
|
||||
- Be accessible. Educational notes should be beginner friendly and avoid assuming unnecessary prior knowledge. The goal is not only to help users understand what a diagnostic is telling them, but also to turn errors and warnings into "teachable moments".
|
||||
- Not generally exceed 3-4 paragraphs. Diagnostic group documentation should be clear and easily digestible. Messages which are too long also have the potential to create UX issues on the command line.
|
||||
- Be accessible. Diagnostic group documentation should be beginner friendly and avoid assuming unnecessary prior knowledge. The goal is not only to help users understand what a diagnostic is telling them, but also to turn errors and warnings into "teachable moments".
|
||||
- Include references to relevant chapters of _The Swift Programming Language_.
|
||||
- Be written in Markdown, but avoid excessive markup which negatively impacts the terminal UX.
|
||||
|
||||
### Quick-Start Guide for Contributing New Educational Notes ###
|
||||
### Quick-Start Guide for Contributing New Diagnostic Groups ###
|
||||
|
||||
Adding new educational notes is a great way to get familiar with the process of contributing to Swift, while also making a big impact!
|
||||
Adding new diagnostic groups is a great way to get familiar with the process of contributing to Swift, while also making a big impact!
|
||||
|
||||
To add a new educational note:
|
||||
To add a new diagnostics group:
|
||||
1. Follow the [directions in the README](https://github.com/swiftlang/swift#getting-sources-for-swift-and-related-projects) to checkout the Swift sources locally. Being able to build the Swift compiler is recommended, but not required, when contributing a new note.
|
||||
2. Identify a diagnostic to write an educational note for. To associate an educational note with a diagnostic name, you'll need to know its internal identifier. The easiest way to do this is to write a small program which triggers the diagnostic, and run it using the `-debug-diagnostic-names` compiler flag. This flag will cause the internal diagnostic identifier to be printed after the diagnostic message in square brackets.
|
||||
2. Identify a diagnostic to collect into a group. To find the diagnostic, you'll need to know its internal identifier. The easiest way to do this is to write a small program which triggers the diagnostic, and run it using the `-debug-diagnostic-names` compiler flag. This flag will cause the internal diagnostic identifier to be printed after the diagnostic message in square brackets.
|
||||
3. Find any closely related diagnostics. Sometimes, what appears to be one diagnostic from a user's perspective may have multiple variations internally. After determining a diagnostic's internal identifier, run a search for it in the compiler source. You should find:
|
||||
- An entry in a `Diagnostics*.def` file describing the diagnostic. If there are any closely related diagnostics the note should also be attached to, they can usually be found nearby.
|
||||
- Each point in the compiler source where the diagnostic is emitted. This can be helpful in determining the exact circumstances which cause it to be emitted.
|
||||
4. Add a new Markdown file in the `userdocs/diagnostics/` directory in the swift repository containing the contents of the note. When writing a note, keep the writing guidelines from the section above in mind. The existing notes in the directory are another useful guide.
|
||||
5. Associate the note with the appropriate diagnostics in `EducationalNotes.def`. An entry like `EDUCATIONAL_NOTES(property_wrapper_failable_init, "property-wrapper-requirements.md")` will associate the note with filename `property-wrapper-requirements.md` with the diagnostic having an internal identifier of `property_wrapper_failable_init`.
|
||||
6. If possible, rebuild the compiler and try recompiling your test program with `-print-educational-notes`. Your new note should appear after the diagnostic in the terminal.
|
||||
7. That's it! The new note is now ready to be submitted as a pull request on GitHub.
|
||||
4. Add a new Markdown file in the `userdocs/diagnostics/` directory in the swift repository containing the documentation. When writing a note, keep the writing guidelines from the section above in mind. The existing notes in the directory are another useful guide.
|
||||
5. Create a new entry in `DiagnosticGroups.def` that provides the name for your new diagnostic group along with the name of the file you added in step (4).
|
||||
6. Find each diagnostic you want to make part of this group in the various `Diagnostics*.def` files. For each diagnostic, replace the `ERROR` or `WARNING` with `GROUPED_ERROR` or `GROUPED_WARNING`, respectively, and put the diagnostic group name after the string literal for the diagnostic message.
|
||||
7. If possible, rebuild the compiler and try recompiling your test program. Your new diagnostic group should appear as `[#<group name>]` at the end of the diagnostic, with a link to the diagnostic file.
|
||||
8. That's it! The new diagnostic group is now ready to be submitted as a pull request on GitHub.
|
||||
|
||||
If you run into any issues or have questions while following the steps above, feel free to post a question on the Swift forums or open a work-in-progress pull request on GitHub.
|
||||
|
||||
@@ -167,4 +168,4 @@ An expected diagnostic is denoted by a comment which begins with `expected-error
|
||||
|
||||
* If two (or more) expected fix-its are juxtaposed with nothing (or whitespace) between them, then both must be present for the verifier to match. If two (or more) expected fix-its have `||` between them, then one of them must be present for the verifier to match. `||` binds more tightly than juxtaposition: `{{1-1=a}} {{2-2=b}} || {{2-2=c}} {{3-3=d}} {{none}}` will only match if there is either a set of three fix-its that insert `a`, `b`, and `d`, or a set of three fix-its that insert `a`, `c`, and `d`. (Without the `{{none}}`, it would also permit all four fix-its, but only because one of the four would be unmatched and ignored.)
|
||||
|
||||
- (Optional) Expected educational notes. These appear as a comma separated list after the expected message, enclosed in double curly braces and prefixed by 'educational-notes='. For example, `{{educational-notes=some-note,some-other-note}}` will verify the educational notes with filenames `some-note` and `some-other-note` appear. Do not include the file extension when specifying note names.
|
||||
- (Optional) Expected documentation file. These is the name of the documentation file, enclosed in double curly braces and prefixed by 'documentation-file='. For example, `{{documentation-file=some-file}}` will verify the documentation group with filename `some-note` appears. Do not include the file extension when specifying the name.
|
||||
|
||||
@@ -334,11 +334,11 @@ provide greater clarity to contributors wanting to add new documentation.
|
||||
It might also be valuable to introduce the tips in context, and have the
|
||||
explanation link to all the different tips.
|
||||
- [Diagnostics.md](/docs/Diagnostics.md):
|
||||
Describes how to write diagnostic messages and associated educational notes.
|
||||
Describes how to write diagnostic messages and associated documentation.
|
||||
TODO: Consider splitting into how-tos and recommended practices.
|
||||
For example, we could have a how-to guide on adding a new diagnostic,
|
||||
and have a recommended practices page which explains the writing style
|
||||
for diagnostics and educational notes.
|
||||
for diagnostics and diagnostic groups.
|
||||
- [HowSwiftImportsCAPIs.md](/docs/HowSwiftImportsCAPIs.md):
|
||||
Contains a thorough description of the mapping between C/ObjC entities and
|
||||
Swift entities.
|
||||
|
||||
@@ -733,7 +733,7 @@ func baz() {
|
||||
Additionally, if they are of a tuple or struct type, their stored members
|
||||
without observers may also be passed inout as non-ephemeral pointers.
|
||||
|
||||
For more details, see the educational note on
|
||||
For more details, see the diagnostic group
|
||||
[temporary pointer usage](/userdocs/diagnostics/temporary-pointers.md).
|
||||
|
||||
## `@_nonoverride`
|
||||
|
||||
@@ -59,8 +59,8 @@ struct DiagnosticInfo {
|
||||
/// DiagnosticInfo of notes which are children of this diagnostic, if any
|
||||
ArrayRef<DiagnosticInfo *> ChildDiagnosticInfo;
|
||||
|
||||
/// Paths to "educational note" diagnostic documentation in the toolchain.
|
||||
ArrayRef<std::string> EducationalNotePaths;
|
||||
/// Path for category documentation.
|
||||
std::string CategoryDocumentationURL;
|
||||
|
||||
/// Represents a fix-it, a replacement of one range of text with another.
|
||||
class FixIt {
|
||||
|
||||
@@ -22,13 +22,30 @@
|
||||
|
||||
GROUP(no_group, "")
|
||||
|
||||
GROUP(DeprecatedDeclaration, "deprecated-declaration.md")
|
||||
GROUP(StrictMemorySafety, "strict-memory-safety.md")
|
||||
GROUP(UnknownWarningGroup, "unknown-warning-group.md")
|
||||
GROUP(PreconcurrencyImport, "preconcurrency-import.md")
|
||||
GROUP(ActorIsolatedCall, "actor-isolated-call.md")
|
||||
GROUP(AsyncCallerExecution, "async-caller-execution.md")
|
||||
GROUP(StrictLanguageFeatures, "strict-language-features.md")
|
||||
GROUP(ConformanceIsolation, "conformance-isolation.md")
|
||||
GROUP(DeprecatedDeclaration, "deprecated-declaration.md")
|
||||
GROUP(DynamicCallable, "dynamic-callable-requirements.md")
|
||||
GROUP(ErrorInFutureSwiftVersion, "error-in-future-swift-version.md")
|
||||
GROUP(ExistentialAny, "existential-any.md")
|
||||
GROUP(ExistentialMemberAccess, "existential-member-access-limitations.md")
|
||||
GROUP(MultipleInheritance, "multiple-inheritance.md")
|
||||
GROUP(MutableGlobalVariable, "mutable-global-variable.md")
|
||||
GROUP(NominalTypes, "nominal-types.md")
|
||||
GROUP(OpaqueTypeInference, "opaque-type-inference.md")
|
||||
GROUP(PreconcurrencyImport, "preconcurrency-import.md")
|
||||
GROUP(PropertyWrappers, "property-wrapper-requirements.md")
|
||||
GROUP(ProtocolTypeNonConformance, "protocol-type-non-conformance.md")
|
||||
GROUP(ResultBuilderMethods, "result-builder-methods.md")
|
||||
GROUP(SendableClosureCaptures, "sendable-closure-captures.md")
|
||||
GROUP(SendingRisksDataRace, "sending-risks-data-race.md")
|
||||
GROUP(StrictLanguageFeatures, "strict-language-features.md")
|
||||
GROUP(StrictMemorySafety, "strict-memory-safety.md")
|
||||
GROUP(StringInterpolationConformance, "string-interpolation-conformance.md")
|
||||
GROUP(TemporaryPointers, "temporary-pointers.md")
|
||||
GROUP(TrailingClosureMatching, "trailing-closure-matching.md")
|
||||
GROUP(UnknownWarningGroup, "unknown-warning-group.md")
|
||||
|
||||
#define UNDEFINE_DIAGNOSTIC_GROUPS_MACROS
|
||||
#include "swift/AST/DefineDiagnosticGroupsMacros.h"
|
||||
|
||||
@@ -49,7 +49,7 @@ NOTE(brace_stmt_suggest_do,none,
|
||||
|
||||
// This does not have a group because warnings of this kind inherit the group
|
||||
// from the wrapped error.
|
||||
WARNING(error_in_swift_lang_mode,none,
|
||||
GROUPED_WARNING(error_in_swift_lang_mode,ErrorInFutureSwiftVersion,none,
|
||||
"%0; this is an error in the Swift %1 language mode",
|
||||
(DiagnosticInfo *, unsigned))
|
||||
|
||||
|
||||
@@ -970,10 +970,11 @@ NOTE(regionbasedisolation_type_is_non_sendable, none,
|
||||
//===
|
||||
// Use After Send Emitter
|
||||
|
||||
ERROR(regionbasedisolation_named_send_yields_race, none,
|
||||
GROUPED_ERROR(regionbasedisolation_named_send_yields_race,
|
||||
SendingRisksDataRace, none,
|
||||
"sending %0 risks causing data races",
|
||||
(Identifier))
|
||||
ERROR(regionbasedisolation_type_send_yields_race, none,
|
||||
GROUPED_ERROR(regionbasedisolation_type_send_yields_race, SendingRisksDataRace, none,
|
||||
"sending value of non-Sendable type %0 risks causing data races",
|
||||
(Type))
|
||||
NOTE(regionbasedisolation_type_use_after_send, none,
|
||||
|
||||
@@ -148,7 +148,7 @@ ERROR(could_not_use_instance_member_on_type,none,
|
||||
"%select{| of type %2}3 cannot be used on"
|
||||
"%select{| instance of nested}3 type %0",
|
||||
(Type, DeclNameRef, Type, bool))
|
||||
ERROR(could_not_use_member_on_existential,none,
|
||||
GROUPED_ERROR(could_not_use_member_on_existential,ExistentialMemberAccess,none,
|
||||
"member %1 cannot be used on value of type %0; consider using a"
|
||||
" generic constraint instead",
|
||||
(Type, DeclNameRef))
|
||||
@@ -447,7 +447,7 @@ ERROR(cannot_convert_closure_result_global_actor,none,
|
||||
ERROR(cannot_convert_global_actor_mismatch_element,none,
|
||||
"cannot convert value actor-isolated to %0 to expected element type actor-isolated to %1",
|
||||
(Type,Type))
|
||||
ERROR(cannot_convert_global_actor_mismatch_dict_value,none,
|
||||
GROUPED_ERROR(cannot_convert_global_actor_mismatch_dict_value,TemporaryPointers,none,
|
||||
"cannot convert value actor-isolated to %0 to expected dictionary value type actor-isolated to %1",
|
||||
(Type,Type))
|
||||
ERROR(cannot_convert_global_actor_mismatch_tuple_element,none,
|
||||
@@ -458,14 +458,14 @@ ERROR(ternary_expr_cases_global_actor_mismatch,none,
|
||||
(Type, Type))
|
||||
|
||||
// @_nonEphemeral conversion diagnostics
|
||||
ERROR(cannot_pass_type_to_non_ephemeral,none,
|
||||
GROUPED_ERROR(cannot_pass_type_to_non_ephemeral,TemporaryPointers,none,
|
||||
"cannot pass %0 to parameter; argument %1 must be a pointer that "
|
||||
"outlives the call%select{| to %2}2",
|
||||
(Type, StringRef, const ValueDecl *))
|
||||
ERROR(cannot_use_inout_non_ephemeral,none,
|
||||
GROUPED_ERROR(cannot_use_inout_non_ephemeral,TemporaryPointers,none,
|
||||
"cannot use inout expression here; argument %0 must be a pointer that "
|
||||
"outlives the call%select{| to %1}1", (StringRef, const ValueDecl *))
|
||||
ERROR(cannot_construct_dangling_pointer,none,
|
||||
GROUPED_ERROR(cannot_construct_dangling_pointer,TemporaryPointers,none,
|
||||
"initialization of %0 results in a dangling %select{|buffer }1pointer",
|
||||
(Type, unsigned))
|
||||
NOTE(ephemeral_pointer_argument_conversion_note,none,
|
||||
@@ -1647,7 +1647,8 @@ ERROR(extra_trailing_closure_in_call,none,
|
||||
ERROR(closure_bad_param,none,
|
||||
"%select{|trailing }1closure passed to parameter of type %0 that does not "
|
||||
"accept a closure", (Type, bool))
|
||||
WARNING(unlabeled_trailing_closure_deprecated,Deprecation,
|
||||
GROUPED_WARNING(unlabeled_trailing_closure_deprecated,
|
||||
TrailingClosureMatching,Deprecation,
|
||||
"backward matching of the unlabeled trailing closure is deprecated; label the argument with %0 to suppress this warning",
|
||||
(Identifier))
|
||||
NOTE(candidate_with_extraneous_args_closure,none,
|
||||
@@ -1675,11 +1676,11 @@ NOTE(archetype_declared_in_type,none,
|
||||
NOTE(unbound_generic_parameter_explicit_fix,none,
|
||||
"explicitly specify the generic arguments to fix this issue", ())
|
||||
|
||||
ERROR(invalid_dynamic_callable_type,none,
|
||||
GROUPED_ERROR(invalid_dynamic_callable_type,DynamicCallable,none,
|
||||
"@dynamicCallable attribute requires %0 to have either a valid "
|
||||
"'dynamicallyCall(withArguments:)' method or "
|
||||
"'dynamicallyCall(withKeywordArguments:)' method", (Type))
|
||||
ERROR(missing_dynamic_callable_kwargs_method,none,
|
||||
GROUPED_ERROR(missing_dynamic_callable_kwargs_method,DynamicCallable,none,
|
||||
"@dynamicCallable type %0 cannot be applied with keyword arguments; "
|
||||
"missing 'dynamicCall(withKeywordArguments:)' method", (Type))
|
||||
|
||||
@@ -2629,7 +2630,7 @@ ERROR(inverse_type_not_invertible,none,
|
||||
"type %0 cannot be suppressed", (Type))
|
||||
|
||||
// Extensions
|
||||
ERROR(non_nominal_extension,none,
|
||||
GROUPED_ERROR(non_nominal_extension,NominalTypes,none,
|
||||
"non-nominal type %0 cannot be extended", (Type))
|
||||
WARNING(composition_in_extended_type,none,
|
||||
"extending a protocol composition is not supported; extending %0 "
|
||||
@@ -2721,7 +2722,7 @@ ERROR(type_cannot_conform_to_nsobject,none,
|
||||
|
||||
ERROR(use_of_equal_instead_of_equality,none,
|
||||
"use of '=' in a boolean context, did you mean '=='?", ())
|
||||
ERROR(type_cannot_conform, none,
|
||||
GROUPED_ERROR(type_cannot_conform, ProtocolTypeNonConformance, none,
|
||||
"type %0 cannot conform to %1",
|
||||
(Type, Type))
|
||||
NOTE(only_concrete_types_conform_to_protocols,none,
|
||||
@@ -3260,16 +3261,18 @@ NOTE(req_near_match_access,none,
|
||||
"warning", (DeclName, AccessLevel))
|
||||
|
||||
// appendInterpolation methods
|
||||
ERROR(missing_append_interpolation,none,
|
||||
GROUPED_ERROR(missing_append_interpolation,StringInterpolationConformance,none,
|
||||
"type conforming to 'StringInterpolationProtocol' does not implement "
|
||||
"a valid 'appendInterpolation' method", ())
|
||||
WARNING(append_interpolation_static,none,
|
||||
GROUPED_WARNING(append_interpolation_static,StringInterpolationConformance,none,
|
||||
"'appendInterpolation' method will never be used because it is static",
|
||||
())
|
||||
WARNING(append_interpolation_void_or_discardable,none,
|
||||
GROUPED_WARNING(append_interpolation_void_or_discardable,
|
||||
StringInterpolationConformance,none,
|
||||
"'appendInterpolation' method does not return 'Void' or have a "
|
||||
"discardable result", ())
|
||||
WARNING(append_interpolation_access_control,none,
|
||||
GROUPED_WARNING(append_interpolation_access_control,
|
||||
StringInterpolationConformance,none,
|
||||
"'appendInterpolation' method is %select{private|fileprivate|internal|package|"
|
||||
"public|open}0, but %1 is %select{private|fileprivate|internal|package|public|"
|
||||
"open}2",
|
||||
@@ -3589,7 +3592,7 @@ ERROR(inheritance_from_protocol_with_superclass,none,
|
||||
WARNING(anyobject_class_inheritance_deprecated,Deprecation,
|
||||
"using 'class' keyword to define a class-constrained protocol is deprecated; "
|
||||
"use 'AnyObject' instead", ())
|
||||
ERROR(multiple_inheritance,none,
|
||||
GROUPED_ERROR(multiple_inheritance,MultipleInheritance,none,
|
||||
"multiple inheritance from classes %0 and %1", (Type, Type))
|
||||
ERROR(inheritance_from_non_protocol_or_class,none,
|
||||
"inheritance from non-protocol, non-class type %0", (Type))
|
||||
@@ -5182,29 +5185,32 @@ WARNING(trailing_closure_requires_parens,none,
|
||||
" statement; pass as a parenthesized argument to silence this warning",
|
||||
())
|
||||
|
||||
ERROR(opaque_type_no_underlying_type_candidates,none,
|
||||
GROUPED_ERROR(opaque_type_no_underlying_type_candidates,OpaqueTypeInference,none,
|
||||
"function declares an opaque return type, but has no return statements "
|
||||
"in its body from which to infer an underlying type", ())
|
||||
NOTE(opaque_type_missing_return_last_expr_note,none,
|
||||
"did you mean to return the last expression?", ())
|
||||
ERROR(opaque_type_mismatched_underlying_type_candidates,none,
|
||||
GROUPED_ERROR(opaque_type_mismatched_underlying_type_candidates,
|
||||
OpaqueTypeInference,none,
|
||||
"function declares an opaque return type %0, but the return statements "
|
||||
"in its body do not have matching underlying types", (TypeRepr *))
|
||||
ERROR(opaque_type_mismatched_underlying_type_candidates_named,none,
|
||||
GROUPED_ERROR(opaque_type_mismatched_underlying_type_candidates_named,
|
||||
OpaqueTypeInference,none,
|
||||
"function declares an opaque return type %0, but the return statements "
|
||||
"in its body do not have matching underlying types", (Identifier))
|
||||
NOTE(opaque_type_underlying_type_candidate_here,none,
|
||||
"return statement has underlying type %0", (Type))
|
||||
ERROR(opaque_type_self_referential_underlying_type,none,
|
||||
GROUPED_ERROR(opaque_type_self_referential_underlying_type,
|
||||
OpaqueTypeInference,none,
|
||||
"function opaque return type was inferred as %0, which defines the "
|
||||
"opaque type in terms of itself", (Type))
|
||||
ERROR(opaque_type_cannot_contain_dynamic_self,none,
|
||||
GROUPED_ERROR(opaque_type_cannot_contain_dynamic_self,OpaqueTypeInference,none,
|
||||
"function with opaque return type cannot return "
|
||||
"the covariant 'Self' type of a class", ())
|
||||
ERROR(opaque_type_var_no_init,none,
|
||||
GROUPED_ERROR(opaque_type_var_no_init,OpaqueTypeInference,none,
|
||||
"property declares an opaque return type, but has no initializer "
|
||||
"expression from which to infer an underlying type", ())
|
||||
ERROR(opaque_type_var_no_underlying_type,none,
|
||||
GROUPED_ERROR(opaque_type_var_no_underlying_type,OpaqueTypeInference,none,
|
||||
"property declares an opaque return type, but cannot infer the "
|
||||
"underlying type from its initializer expression", ())
|
||||
|
||||
@@ -5584,10 +5590,10 @@ ERROR(actor_isolated_inout_state,none,
|
||||
ERROR(actor_isolated_mutating_func,none,
|
||||
"cannot call mutating async function %0 on actor-isolated %kind1",
|
||||
(DeclName, const ValueDecl *))
|
||||
ERROR(actor_isolated_call,none,
|
||||
GROUPED_ERROR(actor_isolated_call,ActorIsolatedCall,none,
|
||||
"call to %0 function in a synchronous %1 context",
|
||||
(ActorIsolation, ActorIsolation))
|
||||
ERROR(actor_isolated_call_decl,none,
|
||||
GROUPED_ERROR(actor_isolated_call_decl,ActorIsolatedCall,none,
|
||||
"call to %0 %kind1 in a synchronous %2 context",
|
||||
(ActorIsolation, const ValueDecl *, ActorIsolation))
|
||||
ERROR(actor_isolated_partial_apply,none,
|
||||
@@ -5611,15 +5617,15 @@ ERROR(sendable_isolated_sync_function,none,
|
||||
ERROR(nonsendable_instance_method,none,
|
||||
"instance method of non-Sendable type %0 cannot be marked as '@Sendable'",
|
||||
(Type))
|
||||
ERROR(concurrent_access_of_local_capture,none,
|
||||
GROUPED_ERROR(concurrent_access_of_local_capture,SendableClosureCaptures,none,
|
||||
"%select{mutation of|reference to}0 captured %kind1 in "
|
||||
"concurrently-executing code",
|
||||
(bool, const ValueDecl *))
|
||||
ERROR(concurrent_access_of_inout_param,none,
|
||||
GROUPED_ERROR(concurrent_access_of_inout_param,SendableClosureCaptures,none,
|
||||
"mutable capture of 'inout' parameter %0 is not allowed in "
|
||||
"concurrently-executing code",
|
||||
(DeclName))
|
||||
ERROR(non_sendable_capture,none,
|
||||
GROUPED_ERROR(non_sendable_capture,SendableClosureCaptures,none,
|
||||
"capture of %1 with non-sendable type %0 in a '@Sendable' "
|
||||
"%select{local function|closure}2",
|
||||
(Type, DeclName, bool))
|
||||
@@ -5694,10 +5700,10 @@ NOTE(actor_mutable_state,none,
|
||||
ERROR(shared_mutable_state_access,none,
|
||||
"reference to %kind0 is not concurrency-safe because it involves shared "
|
||||
"mutable state", (const ValueDecl *))
|
||||
ERROR(shared_mutable_state_decl,none,
|
||||
GROUPED_ERROR(shared_mutable_state_decl,MutableGlobalVariable,none,
|
||||
"%kind0 is not concurrency-safe because it is nonisolated global shared "
|
||||
"mutable state", (const ValueDecl *))
|
||||
ERROR(shared_immutable_state_decl,none,
|
||||
GROUPED_ERROR(shared_immutable_state_decl,MutableGlobalVariable,none,
|
||||
"%kind1 is not concurrency-safe because non-'Sendable' type %0 may have "
|
||||
"shared mutable state",
|
||||
(Type, const ValueDecl *))
|
||||
@@ -5714,11 +5720,11 @@ ERROR(actor_cannot_conform_to_global_actor_protocol,none,
|
||||
(Type, Type))
|
||||
NOTE(protocol_isolated_to_global_actor_here,none,
|
||||
"%0 is isolated to global actor %1 here", (Type, Type))
|
||||
ERROR(conformance_mismatched_isolation,none,
|
||||
GROUPED_ERROR(conformance_mismatched_isolation,ConformanceIsolation,none,
|
||||
"conformance of %0 to %kind1 involves isolation mismatches "
|
||||
"and can cause data races",
|
||||
(Type, const ValueDecl *))
|
||||
ERROR(conformance_mismatched_isolation_common,none,
|
||||
GROUPED_ERROR(conformance_mismatched_isolation_common,ConformanceIsolation,none,
|
||||
"conformance of %0 to %kind1 crosses into %2 code and can cause "
|
||||
"data races", (Type, const ValueDecl *, ActorIsolation))
|
||||
NOTE(note_make_conformance_preconcurrency,none,
|
||||
@@ -7329,31 +7335,31 @@ WARNING(executor_enqueue_unused_implementation, none,
|
||||
//------------------------------------------------------------------------------
|
||||
// MARK: property wrapper diagnostics
|
||||
//------------------------------------------------------------------------------
|
||||
ERROR(property_wrapper_no_value_property, none,
|
||||
GROUPED_ERROR(property_wrapper_no_value_property,PropertyWrappers,none,
|
||||
"property wrapper type %0 does not contain a non-static property "
|
||||
"named %1", (Type, Identifier))
|
||||
ERROR(property_wrapper_ambiguous_value_property, none,
|
||||
GROUPED_ERROR(property_wrapper_ambiguous_value_property,PropertyWrappers,none,
|
||||
"property wrapper type %0 has multiple non-static properties "
|
||||
"named %1", (Type, Identifier))
|
||||
ERROR(property_wrapper_wrong_initial_value_init, none,
|
||||
GROUPED_ERROR(property_wrapper_wrong_initial_value_init,PropertyWrappers,none,
|
||||
"%0 parameter type (%1) must be the same as its "
|
||||
"'wrappedValue' property type (%2) or an @autoclosure thereof",
|
||||
(DeclName, Type, Type))
|
||||
ERROR(property_wrapper_failable_init, none,
|
||||
GROUPED_ERROR(property_wrapper_failable_init,PropertyWrappers,none,
|
||||
"property wrapper initializer %0 cannot be failable", (DeclName))
|
||||
ERROR(property_wrapper_type_requirement_not_accessible,none,
|
||||
GROUPED_ERROR(property_wrapper_type_requirement_not_accessible,PropertyWrappers,none,
|
||||
"%select{private|fileprivate|internal|package|public|open}0 %kind1 cannot have "
|
||||
"more restrictive access than its enclosing property wrapper type %2 "
|
||||
"(which is %select{private|fileprivate|internal|package|public|open}3)",
|
||||
(AccessLevel, const ValueDecl *, Type, AccessLevel))
|
||||
ERROR(property_wrapper_ambiguous_enclosing_self_subscript, none,
|
||||
GROUPED_ERROR(property_wrapper_ambiguous_enclosing_self_subscript,PropertyWrappers,none,
|
||||
"property wrapper type %0 has multiple enclosing-self subscripts %1",
|
||||
(Type, DeclName))
|
||||
ERROR(property_wrapper_dynamic_self_type, none,
|
||||
GROUPED_ERROR(property_wrapper_dynamic_self_type,PropertyWrappers,none,
|
||||
"property wrapper %select{wrapped value|projected value}0 cannot have "
|
||||
"dynamic Self type", (bool))
|
||||
|
||||
ERROR(property_wrapper_var_must_be_static, none,
|
||||
GROUPED_ERROR(property_wrapper_var_must_be_static,PropertyWrappers,none,
|
||||
"property %0, must be static because property wrapper %1 can only "
|
||||
"be applied to static properties",
|
||||
(Identifier, Type))
|
||||
@@ -7505,15 +7511,18 @@ NOTE(result_builder_infer_add_return, none,
|
||||
NOTE(result_builder_infer_pick_specific, none,
|
||||
"apply result builder %0 (inferred from %select{protocol|dynamic replacement of}1 %2)",
|
||||
(Type, unsigned, DeclName))
|
||||
WARNING(result_builder_missing_limited_availability, none,
|
||||
GROUPED_WARNING(result_builder_missing_limited_availability,
|
||||
ResultBuilderMethods, none,
|
||||
"result builder %0 does not implement 'buildLimitedAvailability'; "
|
||||
"this code may crash on earlier versions of the OS",
|
||||
(Type))
|
||||
ERROR(result_builder_static_buildblock_or_buildpartialblock, none,
|
||||
GROUPED_ERROR(result_builder_static_buildblock_or_buildpartialblock,
|
||||
ResultBuilderMethods,none,
|
||||
"result builder must provide at least one static 'buildBlock' "
|
||||
"method, or both 'buildPartialBlock(first:)' and "
|
||||
"'buildPartialBlock(accumulated:next:)'", ())
|
||||
ERROR(result_builder_missing_available_buildpartialblock, none,
|
||||
GROUPED_ERROR(result_builder_missing_available_buildpartialblock,
|
||||
ResultBuilderMethods, none,
|
||||
"result builder %0 does not implement any 'buildBlock' or a combination "
|
||||
"of 'buildPartialBlock(first:)' and "
|
||||
"'buildPartialBlock(accumulated:next:)' with sufficient availability for "
|
||||
|
||||
@@ -1,115 +0,0 @@
|
||||
//===-- EducationalNotes.def - Diagnostic Documentation Content -*- C++ -*-===//
|
||||
//
|
||||
// This source file is part of the Swift.org open source project
|
||||
//
|
||||
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
|
||||
// Licensed under Apache License v2.0 with Runtime Library Exception
|
||||
//
|
||||
// See https://swift.org/LICENSE.txt for license information
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file associates diagnostics with relevant educational notes which
|
||||
// explain important concepts.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef EDUCATIONAL_NOTES
|
||||
# error Must define EDUCATIONAL_NOTES
|
||||
#endif
|
||||
|
||||
// EDUCATIONAL_NOTES(DIAG_ID, EDUCATIONAL_NOTE_FILENAMES...)
|
||||
|
||||
EDUCATIONAL_NOTES(could_not_use_member_on_existential,
|
||||
"existential-member-access-limitations.md")
|
||||
|
||||
EDUCATIONAL_NOTES(cannot_pass_type_to_non_ephemeral, "temporary-pointers.md")
|
||||
EDUCATIONAL_NOTES(cannot_use_inout_non_ephemeral,
|
||||
"temporary-pointers.md")
|
||||
EDUCATIONAL_NOTES(cannot_construct_dangling_pointer, "temporary-pointers.md")
|
||||
|
||||
EDUCATIONAL_NOTES(non_nominal_extension, "nominal-types.md")
|
||||
EDUCATIONAL_NOTES(associated_type_witness_conform_impossible,
|
||||
"nominal-types.md")
|
||||
|
||||
EDUCATIONAL_NOTES(invalid_dynamic_callable_type,
|
||||
"dynamic-callable-requirements.md")
|
||||
EDUCATIONAL_NOTES(missing_dynamic_callable_kwargs_method,
|
||||
"dynamic-callable-requirements.md")
|
||||
|
||||
EDUCATIONAL_NOTES(property_wrapper_no_value_property,
|
||||
"property-wrapper-requirements.md")
|
||||
EDUCATIONAL_NOTES(property_wrapper_wrong_initial_value_init,
|
||||
"property-wrapper-requirements.md")
|
||||
EDUCATIONAL_NOTES(property_wrapper_failable_init,
|
||||
"property-wrapper-requirements.md")
|
||||
EDUCATIONAL_NOTES(property_wrapper_type_requirement_not_accessible,
|
||||
"property-wrapper-requirements.md")
|
||||
|
||||
EDUCATIONAL_NOTES(opaque_type_var_no_init, "opaque-type-inference.md")
|
||||
EDUCATIONAL_NOTES(opaque_type_no_underlying_type_candidates,
|
||||
"opaque-type-inference.md")
|
||||
EDUCATIONAL_NOTES(opaque_type_mismatched_underlying_type_candidates,
|
||||
"opaque-type-inference.md")
|
||||
EDUCATIONAL_NOTES(opaque_type_self_referential_underlying_type,
|
||||
"opaque-type-inference.md")
|
||||
EDUCATIONAL_NOTES(opaque_type_var_no_underlying_type,
|
||||
"opaque-type-inference.md")
|
||||
|
||||
|
||||
EDUCATIONAL_NOTES(missing_append_interpolation,
|
||||
"string-interpolation-conformance.md")
|
||||
EDUCATIONAL_NOTES(append_interpolation_static,
|
||||
"string-interpolation-conformance.md")
|
||||
EDUCATIONAL_NOTES(append_interpolation_void_or_discardable,
|
||||
"string-interpolation-conformance.md")
|
||||
EDUCATIONAL_NOTES(type_cannot_conform, "protocol-type-non-conformance.md")
|
||||
|
||||
EDUCATIONAL_NOTES(unlabeled_trailing_closure_deprecated,
|
||||
"trailing-closure-matching.md")
|
||||
|
||||
EDUCATIONAL_NOTES(result_builder_static_buildblock_or_buildpartialblock,
|
||||
"result-builder-methods.md")
|
||||
EDUCATIONAL_NOTES(result_builder_missing_limited_availability,
|
||||
"result-builder-methods.md")
|
||||
EDUCATIONAL_NOTES(result_builder_missing_build_optional,
|
||||
"result-builder-methods.md")
|
||||
EDUCATIONAL_NOTES(result_builder_missing_build_either,
|
||||
"result-builder-methods.md")
|
||||
EDUCATIONAL_NOTES(result_builder_missing_build_array,
|
||||
"result-builder-methods.md")
|
||||
|
||||
EDUCATIONAL_NOTES(multiple_inheritance,
|
||||
"multiple-inheritance.md")
|
||||
|
||||
EDUCATIONAL_NOTES(regionbasedisolation_named_send_yields_race,
|
||||
"sending-risks-data-race.md")
|
||||
EDUCATIONAL_NOTES(regionbasedisolation_type_send_yields_race,
|
||||
"sending-risks-data-race.md")
|
||||
EDUCATIONAL_NOTES(regionbasedisolation_typed_tns_passed_sending_closure,
|
||||
"sending-closure-risks-data-race.md")
|
||||
EDUCATIONAL_NOTES(shared_mutable_state_decl,
|
||||
"mutable-global-variable.md")
|
||||
EDUCATIONAL_NOTES(shared_immutable_state_decl,
|
||||
"mutable-global-variable.md")
|
||||
EDUCATIONAL_NOTES(non_sendable_capture,
|
||||
"sendable-closure-captures.md")
|
||||
EDUCATIONAL_NOTES(concurrent_access_of_local_capture,
|
||||
"sendable-closure-captures.md")
|
||||
EDUCATIONAL_NOTES(concurrent_access_of_inout_param,
|
||||
"sendable-closure-captures.md")
|
||||
EDUCATIONAL_NOTES(actor_isolated_call,
|
||||
"actor-isolated-call.md")
|
||||
EDUCATIONAL_NOTES(actor_isolated_call_decl,
|
||||
"actor-isolated-call.md")
|
||||
|
||||
EDUCATIONAL_NOTES(error_in_swift_lang_mode,
|
||||
"error-in-future-swift-version.md")
|
||||
|
||||
EDUCATIONAL_NOTES(conformance_mismatched_isolation,
|
||||
"conformance-isolation.md")
|
||||
EDUCATIONAL_NOTES(conformance_mismatched_isolation_common,
|
||||
"conformance-isolation.md")
|
||||
|
||||
#undef EDUCATIONAL_NOTES
|
||||
@@ -71,18 +71,17 @@ struct CapturedDiagnosticInfo {
|
||||
unsigned Line;
|
||||
unsigned Column;
|
||||
SmallVector<CapturedFixItInfo, 2> FixIts;
|
||||
SmallVector<std::string, 1> EducationalNotes;
|
||||
std::string CategoryDocFile;
|
||||
|
||||
CapturedDiagnosticInfo(llvm::SmallString<128> Message,
|
||||
std::optional<unsigned> SourceBufferID,
|
||||
DiagnosticKind Classification, SourceLoc Loc,
|
||||
unsigned Line, unsigned Column,
|
||||
SmallVector<CapturedFixItInfo, 2> FixIts,
|
||||
SmallVector<std::string, 1> EducationalNotes)
|
||||
const std::string &categoryDocFile)
|
||||
: Message(Message), SourceBufferID(SourceBufferID),
|
||||
Classification(Classification), Loc(Loc), Line(Line), Column(Column),
|
||||
FixIts(FixIts), EducationalNotes(EducationalNotes) {
|
||||
std::sort(EducationalNotes.begin(), EducationalNotes.end());
|
||||
FixIts(FixIts), CategoryDocFile(categoryDocFile) {
|
||||
}
|
||||
};
|
||||
/// This class implements support for -verify mode in the compiler. It
|
||||
|
||||
@@ -63,9 +63,7 @@ static void addQueueDiagnostic(void *queuedDiagnostics,
|
||||
highlightRanges.push_back(range);
|
||||
}
|
||||
|
||||
StringRef documentationPath;
|
||||
if (info.EducationalNotePaths.size() > 0)
|
||||
documentationPath = info.EducationalNotePaths[0];
|
||||
StringRef documentationPath = info.CategoryDocumentationURL;
|
||||
|
||||
SmallVector<BridgedFixIt, 2> fixIts;
|
||||
for (const auto &fixIt : info.FixIts) {
|
||||
|
||||
@@ -152,32 +152,6 @@ static constexpr const char *const fixItStrings[] = {
|
||||
"<not a fix-it>",
|
||||
};
|
||||
|
||||
#define EDUCATIONAL_NOTES(DIAG, ...) \
|
||||
static constexpr const char *const DIAG##_educationalNotes[] = {__VA_ARGS__, \
|
||||
nullptr};
|
||||
#include "swift/AST/EducationalNotes.def"
|
||||
|
||||
// NOTE: sadly, while GCC and Clang support array designators in C++, they are
|
||||
// not part of the standard at the moment, so Visual C++ doesn't support them.
|
||||
// This construct allows us to provide a constexpr array initialized to empty
|
||||
// values except in the cases that EducationalNotes.def are provided, similar to
|
||||
// what the C array would have looked like.
|
||||
template<int N>
|
||||
struct EducationalNotes {
|
||||
constexpr EducationalNotes() : value() {
|
||||
for (auto i = 0; i < N; ++i) value[i] = {};
|
||||
#define EDUCATIONAL_NOTES(DIAG, ...) \
|
||||
value[static_cast<std::underlying_type_t<DiagID>>(DiagID::DIAG)] = \
|
||||
DIAG##_educationalNotes;
|
||||
#include "swift/AST/EducationalNotes.def"
|
||||
}
|
||||
const char *const *value[N];
|
||||
};
|
||||
|
||||
static constexpr EducationalNotes<NumDiagIDs> _EducationalNotes =
|
||||
EducationalNotes<NumDiagIDs>();
|
||||
static constexpr auto educationalNotes = _EducationalNotes.value;
|
||||
|
||||
DiagnosticState::DiagnosticState() {
|
||||
// Initialize our ignored diagnostics to defaults
|
||||
ignoredDiagnostics.reserve(NumDiagIDs);
|
||||
@@ -1403,7 +1377,6 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic,
|
||||
|
||||
auto groupID = diagnostic.getGroupID();
|
||||
StringRef Category;
|
||||
const char * const *associatedNotes = nullptr;
|
||||
if (isAPIDigesterBreakageDiagnostic(diagnostic.getID()))
|
||||
Category = "api-digester-breaking-change";
|
||||
else if (isNoUsageDiagnostic(diagnostic.getID()))
|
||||
@@ -1412,10 +1385,6 @@ DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic,
|
||||
Category = getDiagGroupInfoByID(groupID).name;
|
||||
else if (isDeprecationDiagnostic(diagnostic.getID()))
|
||||
Category = "deprecation";
|
||||
else if ((associatedNotes = educationalNotes[(uint32_t)diagnostic.getID()]) &&
|
||||
*associatedNotes) {
|
||||
Category = llvm::sys::path::stem(*associatedNotes);
|
||||
}
|
||||
|
||||
auto fixIts = diagnostic.getFixIts();
|
||||
if (loc.isValid()) {
|
||||
@@ -1589,28 +1558,19 @@ void DiagnosticEngine::emitDiagnostic(const Diagnostic &diagnostic) {
|
||||
}
|
||||
info->ChildDiagnosticInfo = childInfoPtrs;
|
||||
|
||||
SmallVector<std::string, 1> educationalNotePaths;
|
||||
auto associatedNotes = educationalNotes[(uint32_t)diagnostic.getID()];
|
||||
while (associatedNotes && *associatedNotes) {
|
||||
SmallString<128> notePath(getDiagnosticDocumentationPath());
|
||||
llvm::sys::path::append(notePath, *associatedNotes);
|
||||
educationalNotePaths.push_back(notePath.str().str());
|
||||
++associatedNotes;
|
||||
}
|
||||
|
||||
// Capture information about the diagnostic group along with the remaining
|
||||
// educational notes.
|
||||
// Capture information about the diagnostic group and its documentation
|
||||
// URL.
|
||||
auto groupID = diagnostic.getGroupID();
|
||||
if (groupID != DiagGroupID::no_group) {
|
||||
const auto &diagGroup = getDiagGroupInfoByID(groupID);
|
||||
|
||||
SmallString<128> docPath(getDiagnosticDocumentationPath());
|
||||
llvm::sys::path::append(docPath, diagGroup.documentationFile);
|
||||
educationalNotePaths.push_back(docPath.str().str());
|
||||
|
||||
std::string docURL(getDiagnosticDocumentationPath());
|
||||
if (!docURL.empty() && docURL.back() != '/')
|
||||
docURL += "/";
|
||||
docURL += diagGroup.documentationFile;
|
||||
info->CategoryDocumentationURL = std::move(docURL);
|
||||
}
|
||||
|
||||
info->EducationalNotePaths = educationalNotePaths;
|
||||
|
||||
for (auto &consumer : Consumers) {
|
||||
consumer->handleDiagnostic(SourceMgr, *info);
|
||||
}
|
||||
|
||||
@@ -469,6 +469,9 @@ DiagnosticSerializer::convertDiagnosticInfo(SourceManager &SM,
|
||||
return Serialized;
|
||||
};
|
||||
|
||||
std::vector<std::string> educationalNotes;
|
||||
if (!Info.CategoryDocumentationURL.empty())
|
||||
educationalNotes.push_back(Info.CategoryDocumentationURL);
|
||||
return {(uint32_t)Info.ID,
|
||||
convertSourceLoc(SM, Info.Loc),
|
||||
(uint8_t)Info.Kind,
|
||||
@@ -476,8 +479,7 @@ DiagnosticSerializer::convertDiagnosticInfo(SourceManager &SM,
|
||||
Info.Category.str(),
|
||||
convertSourceLoc(SM, Info.BufferIndirectlyCausingDiagnostic),
|
||||
convertDiagnosticInfoArray(Info.ChildDiagnosticInfo),
|
||||
std::vector<std::string>(Info.EducationalNotePaths.begin(),
|
||||
Info.EducationalNotePaths.end()),
|
||||
educationalNotes,
|
||||
convertSourceRangeArray(Info.Ranges),
|
||||
convertFixItArray(Info.FixIts),
|
||||
Info.IsChildNote};
|
||||
@@ -623,7 +625,8 @@ llvm::Error DiagnosticSerializer::deserializeDiagnosticInfo(
|
||||
Ranges,
|
||||
FixIts,
|
||||
Info.IsChildNote};
|
||||
DeserializedInfo.EducationalNotePaths = Info.EducationalNotePaths;
|
||||
if (Info.EducationalNotePaths.size() == 1)
|
||||
DeserializedInfo.CategoryDocumentationURL = Info.EducationalNotePaths[0];
|
||||
return callback(DeserializedInfo);
|
||||
}
|
||||
|
||||
|
||||
@@ -175,7 +175,7 @@ CapturedFixItInfo::getLineColumnRange(SourceManager &SM) const {
|
||||
namespace {
|
||||
|
||||
static constexpr StringLiteral fixitExpectationNoneString("none");
|
||||
static constexpr StringLiteral educationalNotesSpecifier("educational-notes=");
|
||||
static constexpr StringLiteral categoryDocFileSpecifier("documentation-file=");
|
||||
|
||||
struct ExpectedDiagnosticInfo {
|
||||
// This specifies the full range of the "expected-foo {{}}" specifier.
|
||||
@@ -209,16 +209,16 @@ struct ExpectedDiagnosticInfo {
|
||||
// Loc of {{none}}
|
||||
const char *noneMarkerStartLoc = nullptr;
|
||||
|
||||
/// Represents a specifier of the form '{{educational-notes=note1,note2}}'.
|
||||
struct ExpectedEducationalNotes {
|
||||
/// Represents a specifier of the form '{{documentation-file=note1}}'.
|
||||
struct ExpectedDocumentationFile {
|
||||
const char *StartLoc, *EndLoc; // The loc of the {{ and }}'s.
|
||||
llvm::SmallVector<StringRef, 1> Names; // Names of expected notes.
|
||||
StringRef Name; // Name of expected documentation file.
|
||||
|
||||
ExpectedEducationalNotes(const char *StartLoc, const char *EndLoc,
|
||||
llvm::SmallVector<StringRef, 1> Names)
|
||||
: StartLoc(StartLoc), EndLoc(EndLoc), Names(Names) {}
|
||||
ExpectedDocumentationFile(const char *StartLoc, const char *EndLoc,
|
||||
StringRef Name)
|
||||
: StartLoc(StartLoc), EndLoc(EndLoc), Name(Name) {}
|
||||
};
|
||||
std::optional<ExpectedEducationalNotes> EducationalNotes;
|
||||
std::optional<ExpectedDocumentationFile> DocumentationFile;
|
||||
|
||||
ExpectedDiagnosticInfo(const char *ExpectedStart,
|
||||
const char *ClassificationStart,
|
||||
@@ -243,15 +243,12 @@ static std::string getDiagKindString(DiagnosticKind Kind) {
|
||||
llvm_unreachable("Unhandled DiagKind in switch.");
|
||||
}
|
||||
|
||||
/// Render the verifier syntax for a given set of educational notes.
|
||||
/// Render the verifier syntax for a given documentation file.
|
||||
static std::string
|
||||
renderEducationalNotes(llvm::SmallVectorImpl<std::string> &EducationalNotes) {
|
||||
renderDocumentationFile(const std::string &documentationFile) {
|
||||
std::string Result;
|
||||
llvm::raw_string_ostream OS(Result);
|
||||
OS << "{{" << educationalNotesSpecifier;
|
||||
interleave(EducationalNotes, [&](const auto &Note) { OS << Note; },
|
||||
[&] { OS << ','; });
|
||||
OS << "}}";
|
||||
OS << "{{" << categoryDocFileSpecifier << documentationFile << "}}";
|
||||
return OS.str();
|
||||
}
|
||||
|
||||
@@ -810,30 +807,23 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
|
||||
startNewAlternatives = true;
|
||||
}
|
||||
|
||||
// If this check starts with 'educational-notes=', check for one or more
|
||||
// educational notes instead of a fix-it.
|
||||
if (CheckStr.starts_with(educationalNotesSpecifier)) {
|
||||
if (Expected.EducationalNotes.has_value()) {
|
||||
// If this check starts with 'documentation-file=', check for a
|
||||
// documentation file name instead of a fix-it.
|
||||
if (CheckStr.starts_with(categoryDocFileSpecifier)) {
|
||||
if (Expected.DocumentationFile.has_value()) {
|
||||
addError(CheckStr.data(),
|
||||
"each verified diagnostic may only have one "
|
||||
"{{educational-notes=<#notes#>}} declaration");
|
||||
"{{documentation-file=<#notes#>}} declaration");
|
||||
continue;
|
||||
}
|
||||
StringRef NotesStr = CheckStr.substr(
|
||||
educationalNotesSpecifier.size()); // Trim 'educational-notes='.
|
||||
llvm::SmallVector<StringRef, 1> names;
|
||||
// Note names are comma-separated.
|
||||
std::pair<StringRef, StringRef> split;
|
||||
do {
|
||||
split = NotesStr.split(',');
|
||||
names.push_back(split.first);
|
||||
NotesStr = split.second;
|
||||
} while (!NotesStr.empty());
|
||||
Expected.EducationalNotes.emplace(OpenLoc, CloseLoc, names);
|
||||
|
||||
// Trim 'documentation-file='.
|
||||
StringRef name = CheckStr.substr(categoryDocFileSpecifier.size());
|
||||
Expected.DocumentationFile = { OpenLoc, CloseLoc, name };
|
||||
continue;
|
||||
}
|
||||
|
||||
// This wasn't an educational notes specifier, so it must be a fix-it.
|
||||
// This wasn't a documentation file specifier, so it must be a fix-it.
|
||||
// Special case for specifying no fixits should appear.
|
||||
if (CheckStr == fixitExpectationNoneString) {
|
||||
if (Expected.noneMarkerStartLoc) {
|
||||
@@ -1066,29 +1056,27 @@ DiagnosticVerifier::Result DiagnosticVerifier::verifyFile(unsigned BufferID) {
|
||||
replEndLoc, actualFixits);
|
||||
}
|
||||
|
||||
if (auto expectedNotes = expected.EducationalNotes) {
|
||||
// Verify educational notes
|
||||
for (auto &foundName : FoundDiagnostic.EducationalNotes) {
|
||||
llvm::erase_if(expectedNotes->Names,
|
||||
[&](StringRef item) { return item == foundName; });
|
||||
}
|
||||
if (auto expectedDocFile = expected.DocumentationFile) {
|
||||
// Verify diagnostic file.
|
||||
if (FoundDiagnostic.CategoryDocFile == expectedDocFile->Name)
|
||||
expectedDocFile = std::nullopt;
|
||||
|
||||
if (!expectedNotes->Names.empty()) {
|
||||
if (FoundDiagnostic.EducationalNotes.empty()) {
|
||||
addError(expectedNotes->StartLoc,
|
||||
"expected educational note(s) not seen");
|
||||
if (expectedDocFile) {
|
||||
if (FoundDiagnostic.CategoryDocFile.empty()) {
|
||||
addError(expectedDocFile->StartLoc,
|
||||
"expected documentation file not seen");
|
||||
} else {
|
||||
// If we had an incorrect expected note, render it and produce a fixit
|
||||
// of our own.
|
||||
// If we had an incorrect expected document file, render it and
|
||||
// produce a fixit of our own.
|
||||
auto actual =
|
||||
renderEducationalNotes(FoundDiagnostic.EducationalNotes);
|
||||
auto replStartLoc = SMLoc::getFromPointer(expectedNotes->StartLoc);
|
||||
auto replEndLoc = SMLoc::getFromPointer(expectedNotes->EndLoc);
|
||||
renderDocumentationFile(FoundDiagnostic.CategoryDocFile);
|
||||
auto replStartLoc = SMLoc::getFromPointer(expectedDocFile->StartLoc);
|
||||
auto replEndLoc = SMLoc::getFromPointer(expectedDocFile->EndLoc);
|
||||
|
||||
llvm::SMFixIt fix(llvm::SMRange(replStartLoc, replEndLoc), actual);
|
||||
addError(expectedNotes->StartLoc,
|
||||
"expected educational note(s) not seen; actual educational "
|
||||
"note(s): " + actual, fix);
|
||||
addError(expectedDocFile->StartLoc,
|
||||
"expected documentation file not seen; actual documentation "
|
||||
"file: " + actual, fix);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1281,11 +1269,6 @@ void DiagnosticVerifier::handleDiagnostic(SourceManager &SM,
|
||||
fixIts.emplace_back(SM, fixIt);
|
||||
}
|
||||
|
||||
llvm::SmallVector<std::string, 1> eduNotes;
|
||||
for (auto ¬ePath : Info.EducationalNotePaths) {
|
||||
eduNotes.push_back(llvm::sys::path::stem(notePath).str());
|
||||
}
|
||||
|
||||
llvm::SmallString<128> message;
|
||||
{
|
||||
llvm::raw_svector_ostream Out(message);
|
||||
@@ -1296,7 +1279,8 @@ void DiagnosticVerifier::handleDiagnostic(SourceManager &SM,
|
||||
DiagLoc loc(SM, this->SM, Info.Loc);
|
||||
CapturedDiagnostics.emplace_back(message, loc.bufferID, Info.Kind,
|
||||
loc.sourceLoc, loc.line, loc.column, fixIts,
|
||||
eduNotes);
|
||||
llvm::sys::path::stem(
|
||||
Info.CategoryDocumentationURL).str());
|
||||
}
|
||||
|
||||
/// Once all diagnostics have been captured, perform verification.
|
||||
|
||||
@@ -196,13 +196,12 @@ private:
|
||||
// Record identifier for the category.
|
||||
unsigned getEmitCategory(StringRef Category, StringRef CategoryURL);
|
||||
|
||||
/// Emit a flag record that contains a semi-colon separated
|
||||
/// list of all of the educational notes associated with the
|
||||
/// diagnostic or `0` if there are no notes.
|
||||
/// Emit a flag record that contains the documentation URL associated with
|
||||
/// a diagnostic or `0` if there is none.
|
||||
///
|
||||
/// \returns a flag record identifier that could be embedded in
|
||||
/// other records.
|
||||
unsigned emitEducationalNotes(const DiagnosticInfo &info);
|
||||
unsigned emitDocumentationURL(const DiagnosticInfo &info);
|
||||
|
||||
/// Add a source location to a record.
|
||||
void addLocToRecord(SourceLoc Loc,
|
||||
@@ -335,19 +334,11 @@ unsigned SerializedDiagnosticConsumer::getEmitCategory(
|
||||
}
|
||||
|
||||
unsigned
|
||||
SerializedDiagnosticConsumer::emitEducationalNotes(const DiagnosticInfo &Info) {
|
||||
if (Info.EducationalNotePaths.empty())
|
||||
SerializedDiagnosticConsumer::emitDocumentationURL(const DiagnosticInfo &Info) {
|
||||
if (Info.CategoryDocumentationURL.empty())
|
||||
return 0;
|
||||
|
||||
SmallString<32> scratch;
|
||||
interleave(
|
||||
Info.EducationalNotePaths,
|
||||
[&scratch](const auto ¬ePath) { scratch += notePath; },
|
||||
[&scratch] { scratch += ';'; });
|
||||
|
||||
StringRef paths = scratch.str();
|
||||
|
||||
unsigned &recordID = State->Flags[paths];
|
||||
unsigned &recordID = State->Flags[Info.CategoryDocumentationURL];
|
||||
if (recordID)
|
||||
return recordID;
|
||||
|
||||
@@ -356,9 +347,9 @@ SerializedDiagnosticConsumer::emitEducationalNotes(const DiagnosticInfo &Info) {
|
||||
RecordData Record;
|
||||
Record.push_back(RECORD_DIAG_FLAG);
|
||||
Record.push_back(recordID);
|
||||
Record.push_back(paths.size());
|
||||
Record.push_back(Info.CategoryDocumentationURL.size());
|
||||
State->Stream.EmitRecordWithBlob(State->Abbrevs.get(RECORD_DIAG_FLAG), Record,
|
||||
paths);
|
||||
Info.CategoryDocumentationURL);
|
||||
return recordID;
|
||||
}
|
||||
|
||||
@@ -610,21 +601,18 @@ emitDiagnosticMessage(SourceManager &SM,
|
||||
|
||||
// Emit the category.
|
||||
if (!Info.Category.empty()) {
|
||||
std::string categoryURL;
|
||||
if (!Info.EducationalNotePaths.empty()) {
|
||||
categoryURL = Info.EducationalNotePaths[0];
|
||||
}
|
||||
Record.push_back(getEmitCategory(Info.Category, categoryURL));
|
||||
Record.push_back(
|
||||
getEmitCategory(Info.Category, Info.CategoryDocumentationURL));
|
||||
} else {
|
||||
Record.push_back(0);
|
||||
}
|
||||
|
||||
// Use "flags" slot to emit a semi-colon separated list of
|
||||
// educational notes. If there are no notes associated with
|
||||
// this diagnostic `0` placeholder would be emitted instead.
|
||||
// FIXME: This is a bit of a kludge. We're moving toward putting the
|
||||
// educational note into the category field instead.
|
||||
Record.push_back(emitEducationalNotes(Info));
|
||||
// Use "flags" slot to emit the category documentation URL. If there is not
|
||||
// such URL, the `0` placeholder would be emitted instead.
|
||||
// FIXME: This is a kludge. The category documentation URL is part of the
|
||||
// category description now, and we will switch back to using the flag field
|
||||
// as intended once clients have had a chance to adopt the new place.
|
||||
Record.push_back(emitDocumentationURL(Info));
|
||||
|
||||
// Emit the message.
|
||||
Record.push_back(Text.size());
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
// Has a lot of invalid 'appendInterpolation' methods
|
||||
public struct BadStringInterpolation: StringInterpolationProtocol {
|
||||
// expected-error@-1{{type conforming to 'StringInterpolationProtocol' does not implement a valid 'appendInterpolation' method}} {{educational-notes=string-interpolation-conformance}}
|
||||
// expected-error@-1{{type conforming to 'StringInterpolationProtocol' does not implement a valid 'appendInterpolation' method}} {{documentation-file=string-interpolation-conformance}}
|
||||
|
||||
public init(literalCapacity: Int, interpolationCount: Int) {}
|
||||
public mutating func appendLiteral(_: String) {}
|
||||
|
||||
public static func appendInterpolation(static: ()) {
|
||||
// expected-warning@-1{{'appendInterpolation' method will never be used because it is static}} {{10-17=}} {{educational-notes=string-interpolation-conformance}}
|
||||
// expected-warning@-1{{'appendInterpolation' method will never be used because it is static}} {{10-17=}} {{documentation-file=string-interpolation-conformance}}
|
||||
}
|
||||
|
||||
private func appendInterpolation(private: ()) {
|
||||
@@ -20,7 +20,7 @@ public struct BadStringInterpolation: StringInterpolationProtocol {
|
||||
}
|
||||
|
||||
public func appendInterpolation(intResult: ()) -> Int {
|
||||
// expected-warning@-1{{'appendInterpolation' method does not return 'Void' or have a discardable result}} {{10-10=@discardableResult }} {{educational-notes=string-interpolation-conformance}}
|
||||
// expected-warning@-1{{'appendInterpolation' method does not return 'Void' or have a discardable result}} {{10-10=@discardableResult }} {{documentation-file=string-interpolation-conformance}}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,11 +23,11 @@ var optionalArr: [Int8]?
|
||||
// We cannot use array-to-pointer and string-to-pointer conversions with
|
||||
// non-ephemeral parameters.
|
||||
|
||||
takesMutableRaw(&arr, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} {{educational-notes=temporary-pointers}}
|
||||
takesMutableRaw(&arr, 5) // expected-error {{cannot use inout expression here; argument #1 must be a pointer that outlives the call to 'takesMutableRaw'}} {{documentation-file=temporary-pointers}}
|
||||
// expected-note@-1 {{implicit argument conversion from '[Int8]' to 'UnsafeMutableRawPointer' produces a pointer valid only for the duration of the call to 'takesMutableRaw'}}
|
||||
// expected-note@-2 {{use the 'withUnsafeMutableBytes' method on Array in order to explicitly convert argument to buffer pointer valid for a defined scope}}
|
||||
|
||||
takesConst(str, 5) // expected-error {{cannot pass 'String' to parameter; argument #1 must be a pointer that outlives the call to 'takesConst'}} {{educational-notes=temporary-pointers}}
|
||||
takesConst(str, 5) // expected-error {{cannot pass 'String' to parameter; argument #1 must be a pointer that outlives the call to 'takesConst'}} {{documentation-file=temporary-pointers}}
|
||||
// expected-note@-1 {{implicit argument conversion from 'String' to 'UnsafePointer<Int8>' produces a pointer valid only for the duration of the call to 'takesConst'}}
|
||||
// expected-note@-2 {{use the 'withCString' method on String in order to explicitly convert argument to pointer valid for a defined scope}}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ func testFunction(a: CallableReturningFunction) {
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// Arguments' type may not be variadic.
|
||||
// expected-error @+1 {{@dynamicCallable attribute requires 'Invalid1' to have either a valid 'dynamicallyCall(withArguments:)' method or 'dynamicallyCall(withKeywordArguments:)' method}} {{educational-notes=dynamic-callable-requirements}}
|
||||
// expected-error @+1 {{@dynamicCallable attribute requires 'Invalid1' to have either a valid 'dynamicallyCall(withArguments:)' method or 'dynamicallyCall(withKeywordArguments:)' method}} {{documentation-file=dynamic-callable-requirements}}
|
||||
@dynamicCallable
|
||||
struct Invalid1 {
|
||||
func dynamicallyCall(withArguments arguments: [Int]...) -> Int {
|
||||
@@ -107,7 +107,7 @@ struct Invalid1 {
|
||||
}
|
||||
|
||||
// Keyword arguments' key type must be ExpressibleByStringLiteral.
|
||||
// expected-error @+1 {{@dynamicCallable attribute requires 'Invalid2' to have either a valid 'dynamicallyCall(withArguments:)' method or 'dynamicallyCall(withKeywordArguments:)' method}} {{educational-notes=dynamic-callable-requirements}}
|
||||
// expected-error @+1 {{@dynamicCallable attribute requires 'Invalid2' to have either a valid 'dynamicallyCall(withArguments:)' method or 'dynamicallyCall(withKeywordArguments:)' method}} {{documentation-file=dynamic-callable-requirements}}
|
||||
@dynamicCallable
|
||||
struct Invalid2 {
|
||||
func dynamicallyCall(
|
||||
@@ -128,8 +128,8 @@ protocol NoKeywordProtocol {
|
||||
}
|
||||
|
||||
func testInvalidKeywordCall(x: NoKeyword, y: NoKeywordProtocol & AnyObject) {
|
||||
x(a: 1, b: 2) // expected-error {{@dynamicCallable type 'NoKeyword' cannot be applied with keyword arguments; missing 'dynamicCall(withKeywordArguments:)' method}} {{educational-notes=dynamic-callable-requirements}}
|
||||
y(a: 1, b: 2) // expected-error {{@dynamicCallable type 'any NoKeywordProtocol & AnyObject' cannot be applied with keyword arguments; missing 'dynamicCall(withKeywordArguments:)' method}} {{educational-notes=dynamic-callable-requirements}}
|
||||
x(a: 1, b: 2) // expected-error {{@dynamicCallable type 'NoKeyword' cannot be applied with keyword arguments; missing 'dynamicCall(withKeywordArguments:)' method}} {{documentation-file=dynamic-callable-requirements}}
|
||||
y(a: 1, b: 2) // expected-error {{@dynamicCallable type 'any NoKeywordProtocol & AnyObject' cannot be applied with keyword arguments; missing 'dynamicCall(withKeywordArguments:)' method}} {{documentation-file=dynamic-callable-requirements}}
|
||||
}
|
||||
|
||||
// expected-error @+1 {{'@dynamicCallable' attribute cannot be applied to this declaration}}
|
||||
|
||||
@@ -58,7 +58,7 @@ struct WrapperAcceptingAutoclosure<T> {
|
||||
|
||||
@propertyWrapper
|
||||
struct MissingValue<T> { }
|
||||
// expected-error@-1{{property wrapper type 'MissingValue' does not contain a non-static property named 'wrappedValue'}} {{educational-notes=property-wrapper-requirements}}{{25-25=\nvar wrappedValue: <#Value#>}}
|
||||
// expected-error@-1{{property wrapper type 'MissingValue' does not contain a non-static property named 'wrappedValue'}} {{documentation-file=property-wrapper-requirements}}{{25-25=\nvar wrappedValue: <#Value#>}}
|
||||
|
||||
@propertyWrapper
|
||||
struct StaticValue {
|
||||
@@ -76,7 +76,7 @@ protocol CannotBeAWrapper {
|
||||
|
||||
@propertyWrapper
|
||||
struct NonVisibleValueWrapper<Value> {
|
||||
private var wrappedValue: Value // expected-error{{private property 'wrappedValue' cannot have more restrictive access than its enclosing property wrapper type 'NonVisibleValueWrapper' (which is internal)}} {{educational-notes=property-wrapper-requirements}}
|
||||
private var wrappedValue: Value // expected-error{{private property 'wrappedValue' cannot have more restrictive access than its enclosing property wrapper type 'NonVisibleValueWrapper' (which is internal)}} {{documentation-file=property-wrapper-requirements}}
|
||||
}
|
||||
|
||||
@propertyWrapper
|
||||
@@ -92,7 +92,7 @@ struct NonVisibleInitWrapper<Value> {
|
||||
struct InitialValueTypeMismatch<Value> {
|
||||
var wrappedValue: Value // expected-note{{'wrappedValue' declared here}}
|
||||
|
||||
init(wrappedValue initialValue: Value?) { // expected-error{{'init(wrappedValue:)' parameter type ('Value?') must be the same as its 'wrappedValue' property type ('Value') or an @autoclosure thereof}} {{educational-notes=property-wrapper-requirements}}
|
||||
init(wrappedValue initialValue: Value?) { // expected-error{{'init(wrappedValue:)' parameter type ('Value?') must be the same as its 'wrappedValue' property type ('Value') or an @autoclosure thereof}} {{documentation-file=property-wrapper-requirements}}
|
||||
self.wrappedValue = initialValue!
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ struct MultipleInitialValues<Value> {
|
||||
struct InitialValueFailable<Value> {
|
||||
var wrappedValue: Value
|
||||
|
||||
init?(wrappedValue initialValue: Value) { // expected-error{{property wrapper initializer 'init(wrappedValue:)' cannot be failable}} {{educational-notes=property-wrapper-requirements}}
|
||||
init?(wrappedValue initialValue: Value) { // expected-error{{property wrapper initializer 'init(wrappedValue:)' cannot be failable}} {{documentation-file=property-wrapper-requirements}}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
|
||||
typealias Fn = () -> ()
|
||||
extension Fn {}
|
||||
// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Fn' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] [nominal-types]
|
||||
// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Fn' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] [NominalTypes]
|
||||
|
||||
|
||||
// Shares the flag record with `Fn`
|
||||
typealias Dup = () -> ()
|
||||
extension Dup {}
|
||||
// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Dup' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] [nominal-types]
|
||||
// CHECK: [[@LINE-1]]:1: error: non-nominal type 'Dup' (aka '() -> ()') cannot be extended [{{.*}}nominal-types.md] [NominalTypes]
|
||||
|
||||
do {
|
||||
func noNote(_: Int) {}
|
||||
|
||||
@@ -34,14 +34,14 @@ func b() {
|
||||
// CHECK-WARNINGS-AS-ERRORS: unexpected error produced: initialization of immutable value 'c' was never used
|
||||
|
||||
typealias Crap = () -> ()
|
||||
extension Crap {} // expected-error {{non-nominal type 'Crap' (aka '() -> ()') cannot be extended}} {{educational-notes=foo-bar-baz}}
|
||||
// CHECK: error: expected educational note(s) not seen; actual educational note(s): {{[{][{]}}educational-notes=nominal-types{{[}][}]}}
|
||||
extension Crap {} // expected-error {{non-nominal type 'Crap' (aka '() -> ()') cannot be extended}} {{documentation-file=foo-bar-baz}}
|
||||
// CHECK: error: expected documentation file not seen; actual documentation file: {{[{][{]}}documentation-file=nominal-types{{[}][}]}}
|
||||
|
||||
extension Crap {} // expected-error {{non-nominal type 'Crap' (aka '() -> ()') cannot be extended}} {{educational-notes=nominal-types}} {{educational-notes=nominal-types}}
|
||||
// CHECK: error: each verified diagnostic may only have one {{[{][{]}}educational-notes=<#notes#>{{[}][}]}} declaration
|
||||
extension Crap {} // expected-error {{non-nominal type 'Crap' (aka '() -> ()') cannot be extended}} {{documentation-file=nominal-types}} {{documentation-file=nominal-types}}
|
||||
// CHECK: error: each verified diagnostic may only have one {{[{][{]}}documentation-file=<#notes#>{{[}][}]}} declaration
|
||||
|
||||
extension Crap {} // expected-error {{non-nominal type 'Crap' (aka '() -> ()') cannot be extended}} {{educational-notes=nominal-types,foo-bar-baz}}
|
||||
// CHECK: error: expected educational note(s) not seen; actual educational note(s): {{[{][{]}}educational-notes=nominal-types{{[}][}]}}
|
||||
extension Crap {} // expected-error {{non-nominal type 'Crap' (aka '() -> ()') cannot be extended}} {{documentation-file=nominal-types,foo-bar-baz}}
|
||||
// CHECK: error: expected documentation file not seen; actual documentation file: {{[{][{]}}documentation-file=nominal-types{{[}][}]}}
|
||||
|
||||
// Verify the serialized diags have the right magic at the top.
|
||||
// CHECK-SERIALIZED: DIA
|
||||
|
||||
@@ -9,7 +9,7 @@ func unsafePointerInitEphemeralConversions() {
|
||||
var optionalArr: [Int]? = [0]
|
||||
var c: C?
|
||||
|
||||
_ = UnsafePointer(&foo) // expected-warning {{initialization of 'UnsafePointer<Int>' results in a dangling pointer}} {{educational-notes=temporary-pointers}}
|
||||
_ = UnsafePointer(&foo) // expected-warning {{initialization of 'UnsafePointer<Int>' results in a dangling pointer}} {{documentation-file=temporary-pointers}}
|
||||
// expected-note@-1 {{implicit argument conversion from 'Int' to 'UnsafePointer<Int>' produces a pointer valid only for the duration of the call to 'init(_:)'}}
|
||||
// expected-note@-2 {{use 'withUnsafePointer' in order to explicitly convert argument to pointer valid for a defined scope}}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class C {}
|
||||
class D: C, P, Q { func paul() {}; func priscilla() {}; func quinn() {}; func d() {} }
|
||||
|
||||
let property: some P = 1
|
||||
let deflessLet: some P // expected-error{{has no initializer}} {{educational-notes=opaque-type-inference}}
|
||||
let deflessLet: some P // expected-error{{has no initializer}} {{documentation-file=opaque-type-inference}}
|
||||
var deflessVar: some P // expected-error{{has no initializer}}
|
||||
|
||||
struct GenericProperty<T: P> {
|
||||
@@ -173,13 +173,13 @@ func recursion(x: Int) -> some P {
|
||||
return recursion(x: x - 1)
|
||||
}
|
||||
|
||||
func noReturnStmts() -> some P {} // expected-error {{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}} {{educational-notes=opaque-type-inference}}
|
||||
func noReturnStmts() -> some P {} // expected-error {{function declares an opaque return type, but has no return statements in its body from which to infer an underlying type}} {{documentation-file=opaque-type-inference}}
|
||||
|
||||
func returnUninhabited() -> some P { // expected-note {{opaque return type declared here}}
|
||||
fatalError() // expected-error{{return type of global function 'returnUninhabited()' requires that 'Never' conform to 'P'}}
|
||||
}
|
||||
|
||||
func mismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> some P { // expected-error{{do not have matching underlying types}} {{educational-notes=opaque-type-inference}}
|
||||
func mismatchedReturnTypes(_ x: Bool, _ y: Int, _ z: String) -> some P { // expected-error{{do not have matching underlying types}} {{documentation-file=opaque-type-inference}}
|
||||
if x {
|
||||
return y // expected-note{{underlying type 'Int'}}
|
||||
} else {
|
||||
@@ -209,7 +209,7 @@ func jan() -> some P {
|
||||
return [marcia(), marcia(), marcia()]
|
||||
}
|
||||
func marcia() -> some P {
|
||||
return [marcia(), marcia(), marcia()] // expected-error{{defines the opaque type in terms of itself}} {{educational-notes=opaque-type-inference}}
|
||||
return [marcia(), marcia(), marcia()] // expected-error{{defines the opaque type in terms of itself}} {{documentation-file=opaque-type-inference}}
|
||||
}
|
||||
|
||||
protocol R {
|
||||
|
||||
@@ -180,8 +180,8 @@ void EditorDiagConsumer::handleDiagnostic(SourceManager &SM,
|
||||
}
|
||||
SKInfo.Description = Text.str();
|
||||
|
||||
for (auto notePath : Info.EducationalNotePaths)
|
||||
SKInfo.EducationalNotePaths.push_back(notePath);
|
||||
if (!Info.CategoryDocumentationURL.empty())
|
||||
SKInfo.EducationalNotePaths.push_back(Info.CategoryDocumentationURL);
|
||||
|
||||
std::optional<unsigned> BufferIDOpt;
|
||||
if (Info.Loc.isValid()) {
|
||||
|
||||
Reference in New Issue
Block a user