Files
swift-mirror/lib/AST/DocComment.cpp
Jordan Rose 537954fb93 [AST] Rename several DeclContext methods to be clearer and shorter (#18798)
- getAsDeclOrDeclExtensionContext -> getAsDecl

This is basically the same as a dyn_cast, so it should use a 'getAs'
name like TypeBase does.

- getAsNominalTypeOrNominalTypeExtensionContext -> getSelfNominalTypeDecl
- getAsClassOrClassExtensionContext -> getSelfClassDecl
- getAsEnumOrEnumExtensionContext -> getSelfEnumDecl
- getAsStructOrStructExtensionContext -> getSelfStructDecl
- getAsProtocolOrProtocolExtensionContext -> getSelfProtocolDecl
- getAsTypeOrTypeExtensionContext -> getSelfTypeDecl (private)

These do /not/ return some form of 'this'; instead, they get the
extended types when 'this' is an extension. They started off life with
'is' names, which makes sense, but changed to this at some point.  The
names I went with match up with getSelfInterfaceType and
getSelfTypeInContext, even though strictly speaking they're closer to
what getDeclaredInterfaceType does. But it didn't seem right to claim
that an extension "declares" the ClassDecl here.

- getAsProtocolExtensionContext -> getExtendedProtocolDecl

Like the above, this didn't return the ExtensionDecl; it returned its
extended type.

This entire commit is a mechanical change: find-and-replace, followed
by manual reformatted but no code changes.
2018-08-17 14:05:24 -07:00

460 lines
14 KiB
C++

//===--- DocComment.cpp - Extraction of doc comments ----------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file implements extraction of documentation comments from a Swift
/// Markup AST tree.
///
//===----------------------------------------------------------------------===//
#include "swift/AST/Comment.h"
#include "swift/AST/Decl.h"
#include "swift/AST/Types.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/RawComment.h"
#include "swift/Markup/Markup.h"
using namespace swift;
void *DocComment::operator new(size_t Bytes, swift::markup::MarkupContext &MC,
unsigned Alignment) {
return MC.allocate(Bytes, Alignment);
}
Optional<swift::markup::ParamField *> extractParamOutlineItem(
swift::markup::MarkupContext &MC,
swift::markup::MarkupASTNode *Node) {
auto Item = dyn_cast<swift::markup::Item>(Node);
if (!Item)
return None;
auto Children = Item->getChildren();
if (Children.empty())
return None;
auto FirstChild = Children.front();
auto FirstParagraph = dyn_cast<swift::markup::Paragraph>(FirstChild);
if (!FirstParagraph)
return None;
auto FirstParagraphChildren = FirstParagraph->getChildren();
if (FirstParagraphChildren.empty())
return None;
auto ParagraphText =
dyn_cast<swift::markup::Text>(FirstParagraphChildren.front());
if (!ParagraphText)
return None;
StringRef Name;
StringRef Remainder;
std::tie(Name, Remainder) = ParagraphText->getLiteralContent().split(':');
Name = Name.rtrim();
if (Name.empty())
return None;
ParagraphText->setLiteralContent(Remainder.ltrim());
return swift::markup::ParamField::create(MC, Name, Children);
}
bool extractParameterOutline(
swift::markup::MarkupContext &MC, swift::markup::List *L,
SmallVectorImpl<swift::markup::ParamField *> &ParamFields) {
SmallVector<swift::markup::MarkupASTNode *, 8> NormalItems;
auto Children = L->getChildren();
if (Children.empty())
return false;
for (auto Child : Children) {
auto I = dyn_cast<swift::markup::Item>(Child);
if (!I) {
NormalItems.push_back(Child);
continue;
}
auto ItemChildren = I->getChildren();
if (ItemChildren.empty()) {
NormalItems.push_back(Child);
continue;
}
auto FirstChild = ItemChildren.front();
auto FirstParagraph = dyn_cast<swift::markup::Paragraph>(FirstChild);
if (!FirstParagraph) {
NormalItems.push_back(Child);
continue;
}
auto FirstParagraphChildren = FirstParagraph->getChildren();
if (FirstParagraphChildren.empty()) {
NormalItems.push_back(Child);
continue;
}
auto HeadingText
= dyn_cast<swift::markup::Text>(FirstParagraphChildren.front());
if (!HeadingText) {
NormalItems.push_back(Child);
continue;
}
auto HeadingContent = HeadingText->getLiteralContent();
if (!HeadingContent.rtrim().equals_lower("parameters:")) {
NormalItems.push_back(Child);
continue;
}
auto Rest = ArrayRef<swift::markup::MarkupASTNode *>(
ItemChildren.begin() + 1, ItemChildren.end());
if (Rest.empty()) {
NormalItems.push_back(Child);
continue;
}
for (auto Child : Rest) {
auto SubList = dyn_cast<swift::markup::List>(Child);
if (!SubList)
continue;
for (auto SubListChild : SubList->getChildren()) {
auto Param = extractParamOutlineItem(MC, SubListChild);
if (Param.hasValue()) {
ParamFields.push_back(Param.getValue());
}
}
}
}
if (NormalItems.size() != Children.size()) {
L->setChildren(NormalItems);
}
return NormalItems.empty();
}
bool extractSeparatedParams(
swift::markup::MarkupContext &MC, swift::markup::List *L,
SmallVectorImpl<swift::markup::ParamField *> &ParamFields) {
SmallVector<swift::markup::MarkupASTNode *, 8> NormalItems;
auto Children = L->getChildren();
for (auto Child : Children) {
auto I = dyn_cast<swift::markup::Item>(Child);
if (!I) {
NormalItems.push_back(Child);
continue;
}
auto ItemChildren = I->getChildren();
if (ItemChildren.empty()) {
NormalItems.push_back(Child);
continue;
}
auto FirstChild = ItemChildren.front();
auto FirstParagraph = dyn_cast<swift::markup::Paragraph>(FirstChild);
if (!FirstParagraph) {
NormalItems.push_back(Child);
continue;
}
auto FirstParagraphChildren = FirstParagraph->getChildren();
if (FirstParagraphChildren.empty()) {
NormalItems.push_back(Child);
continue;
}
auto ParagraphText
= dyn_cast<swift::markup::Text>(FirstParagraphChildren.front());
if (!ParagraphText) {
NormalItems.push_back(Child);
continue;
}
StringRef ParameterPrefix("parameter ");
auto ParagraphContent = ParagraphText->getLiteralContent();
auto PotentialMatch = ParagraphContent.substr(0, ParameterPrefix.size());
if (!PotentialMatch.startswith_lower(ParameterPrefix)) {
NormalItems.push_back(Child);
continue;
}
ParagraphContent = ParagraphContent.substr(ParameterPrefix.size());
ParagraphText->setLiteralContent(ParagraphContent.ltrim());
auto ParamField = extractParamOutlineItem(MC, I);
if (ParamField.hasValue())
ParamFields.push_back(ParamField.getValue());
else
NormalItems.push_back(Child);
}
if (NormalItems.size() != Children.size())
L->setChildren(NormalItems);
return NormalItems.empty();
}
bool extractSimpleField(
swift::markup::MarkupContext &MC, swift::markup::List *L,
swift::markup::CommentParts &Parts,
SmallVectorImpl<const swift::markup::MarkupASTNode *> &BodyNodes) {
auto Children = L->getChildren();
SmallVector<swift::markup::MarkupASTNode *, 8> NormalItems;
for (auto Child : Children) {
auto I = dyn_cast<swift::markup::Item>(Child);
if (!I) {
NormalItems.push_back(Child);
continue;
}
auto ItemChildren = I->getChildren();
if (ItemChildren.empty()) {
NormalItems.push_back(Child);
continue;
}
auto FirstParagraph
= dyn_cast<swift::markup::Paragraph>(ItemChildren.front());
if (!FirstParagraph) {
NormalItems.push_back(Child);
continue;
}
auto ParagraphChildren = FirstParagraph->getChildren();
if (ParagraphChildren.empty()) {
NormalItems.push_back(Child);
continue;
}
auto ParagraphText
= dyn_cast<swift::markup::Text>(ParagraphChildren.front());
if (!ParagraphText) {
NormalItems.push_back(Child);
continue;
}
StringRef Tag;
StringRef Remainder;
std::tie(Tag, Remainder) = ParagraphText->getLiteralContent().split(':');
Tag = Tag.ltrim().rtrim();
Remainder = Remainder.ltrim();
if (!swift::markup::isAFieldTag(Tag)) {
NormalItems.push_back(Child);
continue;
}
ParagraphText->setLiteralContent(Remainder);
auto Field = swift::markup::createSimpleField(MC, Tag, ItemChildren);
if (auto RF = dyn_cast<swift::markup::ReturnsField>(Field)) {
Parts.ReturnsField = RF;
} else if (auto TF = dyn_cast<swift::markup::ThrowsField>(Field)) {
Parts.ThrowsField = TF;
} else if (auto TF = dyn_cast<swift::markup::TagField>(Field)) {
llvm::SmallString<64> Scratch;
llvm::raw_svector_ostream OS(Scratch);
printInlinesUnder(TF, OS);
Parts.Tags.insert(MC.allocateCopy(OS.str()));
} else if (auto LKF = dyn_cast<markup::LocalizationKeyField>(Field)) {
Parts.LocalizationKeyField = LKF;
} else {
BodyNodes.push_back(Field);
}
}
if (NormalItems.size() != Children.size())
L->setChildren(NormalItems);
return NormalItems.empty();
}
swift::markup::CommentParts
swift::extractCommentParts(swift::markup::MarkupContext &MC,
swift::markup::MarkupASTNode *Node) {
swift::markup::CommentParts Parts;
auto Children = Node->getChildren();
if (Children.empty())
return Parts;
auto FirstParagraph
= dyn_cast<swift::markup::Paragraph>(Node->getChildren().front());
if (FirstParagraph)
Parts.Brief = FirstParagraph;
SmallVector<const swift::markup::MarkupASTNode *, 4> BodyNodes;
SmallVector<swift::markup::ParamField *, 8> ParamFields;
// Look for special top-level lists
size_t StartOffset = FirstParagraph == nullptr ? 0 : 1;
for (auto C = Children.begin() + StartOffset; C != Children.end(); ++C) {
if (auto L = dyn_cast<swift::markup::List>(*C)) {
// Could be one of the following:
// 1. A parameter outline:
// - Parameters:
// - x: ...
// - y: ...
// 2. An exploded parameter list:
// - parameter x: ...
// - parameter y: ...
// 3. Some other simple field, including "returns" (see SimpleFields.def)
auto ListNowEmpty = extractParameterOutline(MC, L, ParamFields);
ListNowEmpty |= extractSeparatedParams(MC, L, ParamFields);
ListNowEmpty |= extractSimpleField(MC, L, Parts, BodyNodes);
if (ListNowEmpty)
continue; // This drops the empty list from the doc comment body.
}
BodyNodes.push_back(*C);
}
// Copy BodyNodes and ParamFields into the MarkupContext.
Parts.BodyNodes = MC.allocateCopy(llvm::makeArrayRef(BodyNodes));
Parts.ParamFields = MC.allocateCopy(llvm::makeArrayRef(ParamFields));
for (auto Param : Parts.ParamFields) {
auto ParamParts = extractCommentParts(MC, Param);
Param->setParts(ParamParts);
}
return Parts;
}
Optional<DocComment *>
swift::getSingleDocComment(swift::markup::MarkupContext &MC, const Decl *D) {
PrettyStackTraceDecl StackTrace("parsing comment for", D);
auto RC = D->getRawComment();
if (RC.isEmpty())
return None;
swift::markup::LineList LL = MC.getLineList(RC);
auto *Doc = swift::markup::parseDocument(MC, LL);
auto Parts = extractCommentParts(MC, Doc);
return new (MC) DocComment(D, Doc, Parts);
}
static Optional<DocComment *>
getAnyBaseClassDocComment(swift::markup::MarkupContext &MC,
const ClassDecl *CD,
const Decl *D) {
RawComment RC;
if (const auto *VD = dyn_cast<ValueDecl>(D)) {
const auto *BaseDecl = VD->getOverriddenDecl();
while (BaseDecl) {
RC = BaseDecl->getRawComment();
if (!RC.isEmpty()) {
swift::markup::LineList LL = MC.getLineList(RC);
auto *Doc = swift::markup::parseDocument(MC, LL);
auto Parts = extractCommentParts(MC, Doc);
SmallString<48> RawCascadeText;
llvm::raw_svector_ostream OS(RawCascadeText);
OS << "This documentation comment was inherited from ";
auto *Text = swift::markup::Text::create(MC, MC.allocateCopy(OS.str()));
auto BaseClass = BaseDecl->getDeclContext()->getSelfClassDecl();
auto *BaseClassMonospace =
swift::markup::Code::create(MC,
MC.allocateCopy(BaseClass->getNameStr()));
auto *Period = swift::markup::Text::create(MC, ".");
auto *Para = swift::markup::Paragraph::create(MC, {
Text, BaseClassMonospace, Period
});
auto CascadeNote = swift::markup::NoteField::create(MC, {Para});
SmallVector<const swift::markup::MarkupASTNode *, 8> BodyNodes {
Parts.BodyNodes.begin(),
Parts.BodyNodes.end()
};
BodyNodes.push_back(CascadeNote);
Parts.BodyNodes = MC.allocateCopy(llvm::makeArrayRef(BodyNodes));
return new (MC) DocComment(D, Doc, Parts);
}
BaseDecl = BaseDecl->getOverriddenDecl();
}
}
return None;
}
static Optional<DocComment *>
getProtocolRequirementDocComment(swift::markup::MarkupContext &MC,
const ProtocolDecl *ProtoExt,
const Decl *D) {
auto getSingleRequirementWithNonemptyDoc = [](const ProtocolDecl *P,
const ValueDecl *VD)
-> const ValueDecl * {
SmallVector<ValueDecl *, 2> Members;
P->lookupQualified(const_cast<ProtocolDecl *>(P),
VD->getFullName(),
NLOptions::NL_ProtocolMembers,
Members);
SmallVector<const ValueDecl *, 1> ProtocolRequirements;
for (auto Member : Members)
if (isa<ProtocolDecl>(Member->getDeclContext()) &&
Member->isProtocolRequirement())
ProtocolRequirements.push_back(Member);
if (ProtocolRequirements.size() == 1) {
auto Requirement = ProtocolRequirements.front();
if (!Requirement->getRawComment().isEmpty())
return Requirement;
}
return nullptr;
};
if (const auto *VD = dyn_cast<ValueDecl>(D)) {
SmallVector<const ValueDecl *, 4> RequirementsWithDocs;
if (auto Requirement = getSingleRequirementWithNonemptyDoc(ProtoExt, VD))
RequirementsWithDocs.push_back(Requirement);
if (RequirementsWithDocs.size() == 1)
return getSingleDocComment(MC, RequirementsWithDocs.front());
}
return None;
}
Optional<DocComment *>
swift::getCascadingDocComment(swift::markup::MarkupContext &MC, const Decl *D) {
auto Doc = getSingleDocComment(MC, D);
if (Doc.hasValue())
return Doc;
// If this refers to a class member, check to see if any
// base classes have a doc comment and cascade it to here.
if (const auto *CD = D->getDeclContext()->getSelfClassDecl())
if (auto BaseClassDoc = getAnyBaseClassDocComment(MC, CD, D))
return BaseClassDoc;
if (const auto *PE = D->getDeclContext()->getExtendedProtocolDecl())
if (auto ReqDoc = getProtocolRequirementDocComment(MC, PE, D))
return ReqDoc;
return None;
}