Skip to content

DiagnosticEngine: Support TypeAttribute diagnostic arguments #80473

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions docs/Diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ Clang also has a kind of diagnostic called a "remark", which represents informat
- Normal: "cannot call 'super.init' outside of an initializer"
- Better: "'super.init' cannot be called outside of an initializer"

- When referring to attributes by name, use *either* "the 'foo' attribute" or "'@foo'", rather than "the '@foo' attribute".
- When referring to attributes by name, use *either* `attribute 'foo'` or
`'@foo'`, rather than `attribute '@foo'`.

- Match the tone and phrasing of other diagnostics. Some common phrases:

Expand Down Expand Up @@ -130,7 +131,10 @@ If you run into any issues or have questions while following the steps above, fe

- `%%` - Emits a literal percent sign.

There are several format specifiers that are specific to `Decl` parameters:
The following subsections describe format specifiers that are specific to
diagnostic arguments of a given type.

#### `Decl`

- `%kind0` - Prefixes the declaration's name with its descriptive decl kind (e.g. `instance method 'foo(x:)'`).

Expand All @@ -140,6 +144,11 @@ There are several format specifiers that are specific to `Decl` parameters:

- `%kindbase0` - Combines `kind` and `base` (e.g. `instance method 'foo'`).

#### `TypeAttribute`

- `%kind0` - Replaced with `attribute 'foo'`, whereas `%0` is replaced with
`'@foo'`.

Note: If your diagnostic could apply to accessors, be careful how you format the declaration's name; accessors have an empty name, so you need to display their accessor kind and the name of their storage decl instead. Inserting the name with a `Decl *` parameter will handle these complications automatically; if you want to use `DeclName` or `Identifier` instead, you'll probably need a separate version of the diagnostic for accessors.

### Diagnostic Verifier ###
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -3781,6 +3781,10 @@ class alignas(1 << AttrAlignInBits) TypeAttribute
TypeAttrKind getKind() const {
return TypeAttrKind(Bits.TypeAttribute.Kind);
}

/// - Note: Do not call this directly when emitting a diagnostic. Instead,
/// define the diagnostic to accept a `const TypeAttribute *` and use the
/// appropriate format specifier.
const char *getAttrName() const {
return getAttrName(getKind());
}
Expand Down
213 changes: 213 additions & 0 deletions include/swift/AST/DiagnosticArgument.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
//===--- DiagnosticArgument.h -----------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_AST_DIAGNOSTIC_ARGUMENT_H
#define SWIFT_AST_DIAGNOSTIC_ARGUMENT_H

#include "swift/AST/ActorIsolation.h"
#include "swift/AST/AvailabilityDomain.h"
#include "swift/AST/DiagnosticConsumer.h"
#include "swift/AST/Identifier.h"
#include "swift/AST/LayoutConstraint.h"
#include "swift/AST/TypeLoc.h"
#include "swift/Basic/Version.h"
#include "llvm/Support/VersionTuple.h"

namespace clang {
class NamedDecl;
class Type;
} // namespace clang

namespace swift {

class Decl;
class DeclAttribute;
class TypeAttribute;
class TypeRepr;

enum class DescriptivePatternKind : uint8_t;
enum class DescriptiveDeclKind : uint8_t;
enum class ReferenceOwnership : uint8_t;
enum class SelfAccessKind : uint8_t;
enum class StaticSpellingKind : uint8_t;
enum class StmtKind;

/// A family of wrapper types for compiler data types that forces its
/// underlying data to be formatted with full qualification.
///
/// So far, this is only useful for \c Type, hence the SFINAE'ing.
template <typename T, typename = void>
struct FullyQualified {};

template <typename T>
struct FullyQualified<
T, typename std::enable_if<std::is_convertible<T, Type>::value>::type> {
Type t;

public:
FullyQualified(T t) : t(t) {};

Type getType() const { return t; }
};

struct WitnessType {
Type t;

WitnessType(Type t) : t(t) {}

Type getType() { return t; }
};

/// Describes the kind of diagnostic argument we're storing.
enum class DiagnosticArgumentKind {
String,
Integer,
Unsigned,
Identifier,
ObjCSelector,
Decl,
Type,
TypeRepr,
FullyQualifiedType,
WitnessType,
DescriptivePatternKind,
SelfAccessKind,
ReferenceOwnership,
StaticSpellingKind,
DescriptiveDeclKind,
DescriptiveStmtKind,
DeclAttribute,
TypeAttribute,
AvailabilityDomain,
AvailabilityRange,
VersionTuple,
LayoutConstraint,
ActorIsolation,
IsolationSource,
Diagnostic,
ClangDecl,
ClangType,
};

/// Variant type that holds a single diagnostic argument of a known
/// type.
///
/// All diagnostic arguments are converted to an instance of this class.
class DiagnosticArgument {
DiagnosticArgumentKind Kind;
union {
int IntegerVal;
unsigned UnsignedVal;
StringRef StringVal;
DeclNameRef IdentifierVal;
ObjCSelector ObjCSelectorVal;
const Decl *TheDecl;
Type TypeVal;
TypeRepr *TyR;
FullyQualified<Type> FullyQualifiedTypeVal;
WitnessType WitnessTypeVal;
DescriptivePatternKind DescriptivePatternKindVal;
SelfAccessKind SelfAccessKindVal;
ReferenceOwnership ReferenceOwnershipVal;
StaticSpellingKind StaticSpellingKindVal;
DescriptiveDeclKind DescriptiveDeclKindVal;
StmtKind DescriptiveStmtKindVal;
const DeclAttribute *DeclAttributeVal;
const TypeAttribute *TypeAttributeVal;
AvailabilityDomain AvailabilityDomainVal;
AvailabilityRange AvailabilityRangeVal;
llvm::VersionTuple VersionVal;
LayoutConstraint LayoutConstraintVal;
ActorIsolation ActorIsolationVal;
IsolationSource IsolationSourceVal;
DiagnosticInfo *DiagnosticVal;
const clang::NamedDecl *ClangDecl;
const clang::Type *ClangType;
};

public:
DiagnosticArgument(StringRef S);
DiagnosticArgument(int I);
DiagnosticArgument(unsigned I);
DiagnosticArgument(DeclNameRef R);
DiagnosticArgument(DeclName D);
DiagnosticArgument(DeclBaseName D);
DiagnosticArgument(Identifier I);
DiagnosticArgument(ObjCSelector S);
DiagnosticArgument(const Decl *VD);
DiagnosticArgument(Type T);
DiagnosticArgument(TypeRepr *T);
DiagnosticArgument(FullyQualified<Type> FQT);
DiagnosticArgument(WitnessType WT);
DiagnosticArgument(const TypeLoc &TL);
DiagnosticArgument(DescriptivePatternKind DPK);
DiagnosticArgument(ReferenceOwnership RO);
DiagnosticArgument(SelfAccessKind SAK);
DiagnosticArgument(StaticSpellingKind SSK);
DiagnosticArgument(DescriptiveDeclKind DDK);
DiagnosticArgument(StmtKind SK);
DiagnosticArgument(const DeclAttribute *attr);
DiagnosticArgument(const TypeAttribute *attr);
DiagnosticArgument(const AvailabilityDomain domain);
DiagnosticArgument(const AvailabilityRange &range);
DiagnosticArgument(llvm::VersionTuple version);
DiagnosticArgument(LayoutConstraint L);
DiagnosticArgument(ActorIsolation AI);
DiagnosticArgument(IsolationSource IS);
DiagnosticArgument(DiagnosticInfo *D);
DiagnosticArgument(const clang::NamedDecl *ND);
DiagnosticArgument(const clang::Type *Ty);

/// Initializes a diagnostic argument using the underlying type of the
/// given enum.
template <
typename EnumType,
typename std::enable_if<std::is_enum<EnumType>::value>::type * = nullptr>
DiagnosticArgument(EnumType value)
: DiagnosticArgument(
static_cast<typename std::underlying_type<EnumType>::type>(value)) {
}

DiagnosticArgumentKind getKind() const;

StringRef getAsString() const;
int getAsInteger() const;
unsigned getAsUnsigned() const;
DeclNameRef getAsIdentifier() const;
ObjCSelector getAsObjCSelector() const;
const Decl *getAsDecl() const;
Type getAsType() const;
TypeRepr *getAsTypeRepr() const;
FullyQualified<Type> getAsFullyQualifiedType() const;
WitnessType getAsWitnessType() const;
DescriptivePatternKind getAsDescriptivePatternKind() const;
ReferenceOwnership getAsReferenceOwnership() const;
SelfAccessKind getAsSelfAccessKind() const;
StaticSpellingKind getAsStaticSpellingKind() const;
DescriptiveDeclKind getAsDescriptiveDeclKind() const;
StmtKind getAsDescriptiveStmtKind() const;
const DeclAttribute *getAsDeclAttribute() const;
const TypeAttribute *getAsTypeAttribute() const;
const AvailabilityDomain getAsAvailabilityDomain() const;
const AvailabilityRange getAsAvailabilityRange() const;
llvm::VersionTuple getAsVersionTuple() const;
LayoutConstraint getAsLayoutConstraint() const;
ActorIsolation getAsActorIsolation() const;
IsolationSource getAsIsolationSource() const;
DiagnosticInfo *getAsDiagnostic() const;
const clang::NamedDecl *getAsClangDecl() const;
const clang::Type *getAsClangType() const;
};

} // namespace swift

#endif /* SWIFT_AST_DIAGNOSTIC_ARGUMENT_H */
Loading