Skip to content

Commit f674b47

Browse files
introduce a @_documentation(...) attribute to influence SymbolGraphGen (#60242)
* add @_documentation(...) attribute to influence SymbolGraphGen rdar://79049241
1 parent 8e81a80 commit f674b47

24 files changed

+442
-10
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,27 @@ extension Text {
121121
}
122122
```
123123

124+
## `@_documentation(metadata: ...)`
125+
126+
Adds "documentation metadata" to the symbol. The identifier in the attribute is
127+
added to the symbol graph in the `"metadata"` field of the symbol. This can be
128+
used to add an arbitrary grouping or other indicator to symbols for use in
129+
documentation.
130+
131+
## `@_documentation(visibility: ...)`
132+
133+
Forces the symbol to be treated as the given access level when checking
134+
visibility. This can be used to, for example, force a symbol with an underscored
135+
name to appear in `public` symbol graphs, or treat an otherwise-`public` symbol
136+
as being `internal` or `private` for the purposes of documentation, to hide it
137+
from `public` docs.
138+
139+
This can also be applied to `@_exported import` statements to only include the
140+
imported symbols in symbol graphs with the given minimum access level. For
141+
example, applying `@_documentation(visibility: internal)` to an `@_exported
142+
import` statement will hide the imported symbols from `public` symbol graphs and
143+
documentation, but show them on `internal` symbol graphs and documentation.
144+
124145
## `@_dynamicReplacement(for: targetFunc(label:))`
125146

126147
Marks a function as the dynamic replacement for another `dynamic` function.

include/swift/AST/Attr.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,11 @@ SIMPLE_DECL_ATTR(_spiOnly, SPIOnly,
773773
ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove,
774774
135)
775775

776+
DECL_ATTR(_documentation, Documentation,
777+
OnAnyDecl | UserInaccessible |
778+
APIBreakingToAdd | APIStableToRemove | ABIStableToAdd | ABIStableToRemove,
779+
136)
780+
776781
// If you're adding a new underscored attribute here, please document it in
777782
// docs/ReferenceGuides/UnderscoredAttributes.md.
778783

include/swift/AST/Attr.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2244,6 +2244,27 @@ class ExposeAttr : public DeclAttribute {
22442244
}
22452245
};
22462246

2247+
/// The `@_documentation(...)` attribute, used to override a symbol's visibility
2248+
/// in symbol graphs, and/or adding arbitrary metadata to it.
2249+
class DocumentationAttr: public DeclAttribute {
2250+
public:
2251+
DocumentationAttr(SourceLoc AtLoc, SourceRange Range,
2252+
StringRef Metadata, Optional<AccessLevel> Visibility,
2253+
bool Implicit)
2254+
: DeclAttribute(DAK_Documentation, AtLoc, Range, Implicit),
2255+
Metadata(Metadata), Visibility(Visibility) {}
2256+
2257+
DocumentationAttr(StringRef Metadata, Optional<AccessLevel> Visibility, bool Implicit)
2258+
: DocumentationAttr(SourceLoc(), SourceRange(), Metadata, Visibility, Implicit) {}
2259+
2260+
const StringRef Metadata;
2261+
const Optional<AccessLevel> Visibility;
2262+
2263+
static bool classof(const DeclAttribute *DA) {
2264+
return DA->getKind() == DAK_Documentation;
2265+
}
2266+
};
2267+
22472268
/// Attributes that may be applied to declarations.
22482269
class DeclAttributes {
22492270
/// Linked list of declaration attributes.

include/swift/AST/DiagnosticsParse.def

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1763,6 +1763,28 @@ WARNING(warn_attr_unsafe_removed,none,
17631763
"'%0' attribute has been removed in favor of @preconcurrency",
17641764
(StringRef))
17651765

1766+
// _documentation
1767+
ERROR(documentation_attr_expected_argument,none,
1768+
"@_documentation attribute expected 'visibility' or 'metadata' argument",
1769+
())
1770+
ERROR(documentation_attr_unknown_argument,none,
1771+
"unknown argument '%0', expected 'visibility' or 'metadata'",
1772+
(StringRef))
1773+
ERROR(documentation_attr_expected_access_level,none,
1774+
"@_documentation attribute's 'visibility' argument expected an access level",
1775+
())
1776+
ERROR(documentation_attr_unknown_access_level,none,
1777+
"unknown visibility '%0', expected an access level keyword",
1778+
(StringRef))
1779+
ERROR(documentation_attr_duplicate_visibility,none,
1780+
"cannot give more than one visibility to the same item", ())
1781+
ERROR(documentation_attr_metadata_expected_text,none,
1782+
"@_documentation attribute's 'metadata' argument expected an identifier or "
1783+
"quoted string",
1784+
())
1785+
ERROR(documentation_attr_duplicate_metadata,none,
1786+
"cannot give more than one metadata argument to the same item", ())
1787+
17661788
//------------------------------------------------------------------------------
17671789
// MARK: Generics parsing diagnostics
17681790
//------------------------------------------------------------------------------

include/swift/AST/Import.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#ifndef SWIFT_IMPORT_H
2020
#define SWIFT_IMPORT_H
2121

22+
#include "swift/AST/AttrKind.h"
2223
#include "swift/AST/Identifier.h"
2324
#include "swift/Basic/Located.h"
2425
#include "swift/Basic/OptionSet.h"
@@ -579,13 +580,18 @@ struct AttributedImport {
579580
/// is the source range covering the annotation.
580581
SourceRange preconcurrencyRange;
581582

583+
/// If the import declaration has a `@_documentation(visibility: <access>)`
584+
/// attribute, this is the given access level.
585+
Optional<AccessLevel> docVisibility;
586+
582587
AttributedImport(ModuleInfo module, SourceLoc importLoc = SourceLoc(),
583588
ImportOptions options = ImportOptions(),
584589
StringRef filename = {}, ArrayRef<Identifier> spiGroups = {},
585-
SourceRange preconcurrencyRange = {})
590+
SourceRange preconcurrencyRange = {},
591+
Optional<AccessLevel> docVisibility = None)
586592
: module(module), importLoc(importLoc), options(options),
587593
sourceFileArg(filename), spiGroups(spiGroups),
588-
preconcurrencyRange(preconcurrencyRange) {
594+
preconcurrencyRange(preconcurrencyRange), docVisibility(docVisibility) {
589595
assert(!(options.contains(ImportFlags::Exported) &&
590596
options.contains(ImportFlags::ImplementationOnly)) ||
591597
options.contains(ImportFlags::Reserved));
@@ -595,14 +601,15 @@ struct AttributedImport {
595601
AttributedImport(ModuleInfo module, AttributedImport<OtherModuleInfo> other)
596602
: AttributedImport(module, other.importLoc, other.options,
597603
other.sourceFileArg, other.spiGroups,
598-
other.preconcurrencyRange) { }
604+
other.preconcurrencyRange, other.docVisibility) { }
599605

600606
friend bool operator==(const AttributedImport<ModuleInfo> &lhs,
601607
const AttributedImport<ModuleInfo> &rhs) {
602608
return lhs.module == rhs.module &&
603609
lhs.options.toRaw() == rhs.options.toRaw() &&
604610
lhs.sourceFileArg == rhs.sourceFileArg &&
605-
lhs.spiGroups == rhs.spiGroups;
611+
lhs.spiGroups == rhs.spiGroups &&
612+
lhs.docVisibility == rhs.docVisibility;
606613
}
607614

608615
AttributedImport<ImportedModule> getLoaded(ModuleDecl *loadedModule) const {
@@ -754,14 +761,14 @@ struct DenseMapInfo<swift::AttributedImport<ModuleInfo>> {
754761
SourceLocDMI::getEmptyKey(),
755762
ImportOptionsDMI::getEmptyKey(),
756763
StringRefDMI::getEmptyKey(),
757-
{});
764+
{}, {}, None);
758765
}
759766
static inline AttributedImport getTombstoneKey() {
760767
return AttributedImport(ModuleInfoDMI::getTombstoneKey(),
761768
SourceLocDMI::getEmptyKey(),
762769
ImportOptionsDMI::getTombstoneKey(),
763770
StringRefDMI::getTombstoneKey(),
764-
{});
771+
{}, {}, None);
765772
}
766773
static inline unsigned getHashValue(const AttributedImport &import) {
767774
return detail::combineHashValue(
@@ -775,7 +782,8 @@ struct DenseMapInfo<swift::AttributedImport<ModuleInfo>> {
775782
return ModuleInfoDMI::isEqual(a.module, b.module) &&
776783
ImportOptionsDMI::isEqual(a.options, b.options) &&
777784
StringRefDMI::isEqual(a.sourceFileArg, b.sourceFileArg) &&
778-
a.spiGroups == b.spiGroups;
785+
a.spiGroups == b.spiGroups &&
786+
a.docVisibility == b.docVisibility;
779787
}
780788
};
781789
}

include/swift/AST/Module.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,8 @@ inline SourceLoc extractNearestSourceLoc(const ModuleDecl *mod) {
959959
/// Collects modules that this module imports via `@_exported import`.
960960
void collectParsedExportedImports(const ModuleDecl *M,
961961
SmallPtrSetImpl<ModuleDecl *> &Imports,
962-
llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> &QualifiedImports);
962+
llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> &QualifiedImports,
963+
llvm::function_ref<bool(AttributedImport<ImportedModule>)> includeImport = nullptr);
963964

964965
} // end namespace swift
965966

include/swift/Parse/Parser.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,14 @@ class Parser {
11451145
bool parseBackDeployAttribute(DeclAttributes &Attributes, StringRef AttrName,
11461146
SourceLoc AtLoc, SourceLoc Loc);
11471147

1148+
/// Parse the @_documentation attribute.
1149+
ParserResult<DocumentationAttr> parseDocumentationAttribute(SourceLoc AtLoc,
1150+
SourceLoc Loc);
1151+
1152+
/// Parse a single argument from a @_documentation attribute.
1153+
bool parseDocumentationAttributeArgument(Optional<StringRef> &Metadata,
1154+
Optional<AccessLevel> &Visibility);
1155+
11481156
/// Parse a specific attribute.
11491157
ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
11501158
PatternBindingInitializer *&initContext,
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===--- DocumentationCategory.h - Accessors for @_documentation ----------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_SYMBOLGRAPHGEN_DOCUMENTATIONCATEGORY_H
14+
#define SWIFT_SYMBOLGRAPHGEN_DOCUMENTATIONCATEGORY_H
15+
16+
#include "swift/AST/Decl.h"
17+
18+
#include "llvm/Support/Compiler.h"
19+
20+
namespace swift {
21+
namespace symbolgraphgen {
22+
23+
LLVM_ATTRIBUTE_USED
24+
static StringRef documentationMetadataForDecl(const Decl *D) {
25+
if (!D) return {};
26+
27+
if (const auto *DC = D->getAttrs().getAttribute<DocumentationAttr>()) {
28+
return DC->Metadata;
29+
}
30+
31+
return {};
32+
}
33+
34+
LLVM_ATTRIBUTE_USED
35+
static Optional<AccessLevel> documentationVisibilityForDecl(const Decl *D) {
36+
if (!D) return None;
37+
38+
if (const auto *DC = D->getAttrs().getAttribute<DocumentationAttr>()) {
39+
return DC->Visibility;
40+
}
41+
42+
return None;
43+
}
44+
45+
} // namespace symbolgraphgen
46+
} // namespace swift
47+
48+
#endif // SWIFT_SYMBOLGRAPHGEN_DOCUMENTATIONCATEGORY_H

lib/AST/Attr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,6 +1450,8 @@ StringRef DeclAttribute::getAttrName() const {
14501450
return "_backDeploy";
14511451
case DAK_Expose:
14521452
return "_expose";
1453+
case DAK_Documentation:
1454+
return "_documentation";
14531455
}
14541456
llvm_unreachable("bad DeclAttrKind");
14551457
}

lib/AST/Module.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -794,12 +794,14 @@ bool ModuleDecl::shouldCollectDisplayDecls() const {
794794

795795
void swift::collectParsedExportedImports(const ModuleDecl *M,
796796
SmallPtrSetImpl<ModuleDecl *> &Imports,
797-
llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> &QualifiedImports) {
797+
llvm::SmallDenseMap<ModuleDecl *, SmallPtrSet<Decl *, 4>, 4> &QualifiedImports,
798+
llvm::function_ref<bool(AttributedImport<ImportedModule>)> includeImport) {
798799
for (const FileUnit *file : M->getFiles()) {
799800
if (const SourceFile *source = dyn_cast<SourceFile>(file)) {
800801
if (source->hasImports()) {
801802
for (auto import : source->getImports()) {
802803
if (import.options.contains(ImportFlags::Exported) &&
804+
(!includeImport || includeImport(import)) &&
803805
import.module.importedModule->shouldCollectDisplayDecls()) {
804806
auto *TheModule = import.module.importedModule;
805807

0 commit comments

Comments
 (0)