Skip to content

Commit

Permalink
Feature: Add support for @returns and @errors doc comment tags (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
timotheeguerin authored Sep 25, 2023
1 parent 8415d52 commit 9a2a1bf
Show file tree
Hide file tree
Showing 16 changed files with 554 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/compiler",
"comment": "Add support for `@returns` and `@errors` doc comment tags. `@returns`(or `@returnsDoc` decorator) can be used to describe the success return types of an operation. `@errors`(or `@errorsDoc` decorator) can be used to describe the error return types of an operation.",
"type": "none"
}
],
"packageName": "@typespec/compiler"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/http",
"comment": "Add support for `@returns` and `@errors` doc comment tags.",
"type": "none"
}
],
"packageName": "@typespec/http"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@typespec/openapi3",
"comment": "Add support for `@returns` and `@errors` doc comment tags.",
"type": "none"
}
],
"packageName": "@typespec/openapi3"
}
52 changes: 52 additions & 0 deletions docs/standard-library/built-in-decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,32 @@ message: string;
```


### `@errorsDoc` {#@errorsDoc}

Attach a documentation string to describe the error return types of an operation.
If an operation returns a union of success and errors it only describe the errors. See `@errorsDoc` for success documentation.

```typespec
@errorsDoc(doc: valueof string)
```

#### Target

`Operation`

#### Parameters
| Name | Type | Description |
|------|------|-------------|
| doc | `valueof scalar string` | Documentation string |

#### Examples

```typespec
@errorsDoc("Returns doc")
op get(): Pet | NotFound;
```


### `@format` {#@format}

Specify a known data format hint for this string type. For example `uuid`, `uri`, etc.
Expand Down Expand Up @@ -631,6 +657,32 @@ expireAt: int32;
```


### `@returnsDoc` {#@returnsDoc}

Attach a documentation string to describe the successful return types of an operation.
If an operation returns a union of success and errors it only describe the success. See `@errorsDoc` for error documentation.

```typespec
@returnsDoc(doc: valueof string)
```

#### Target

`Operation`

#### Parameters
| Name | Type | Description |
|------|------|-------------|
| doc | `valueof scalar string` | Documentation string |

#### Examples

```typespec
@returnsDoc("Returns doc")
op get(): Pet | NotFound;
```


### `@returnTypeVisibility` {#@returnTypeVisibility}

Sets which visibilities apply to the return type for the given operation.
Expand Down
26 changes: 26 additions & 0 deletions packages/compiler/lib/decorators.tsp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,32 @@ extern dec summary(target: unknown, summary: valueof string);
*/
extern dec doc(target: unknown, doc: valueof string, formatArgs?: {});

/**
* Attach a documentation string to describe the successful return types of an operation.
* If an operation returns a union of success and errors it only describe the success. See `@errorsDoc` for error documentation.
* @param doc Documentation string
*
* @example
* ```typespec
* @returnsDoc("Returns doc")
* op get(): Pet | NotFound;
* ```
*/
extern dec returnsDoc(target: Operation, doc: valueof string);

/**
* Attach a documentation string to describe the error return types of an operation.
* If an operation returns a union of success and errors it only describe the errors. See `@errorsDoc` for success documentation.
* @param doc Documentation string
*
* @example
* ```typespec
* @errorsDoc("Returns doc")
* op get(): Pet | NotFound;
* ```
*/
extern dec errorsDoc(target: Operation, doc: valueof string);

/**
* Mark this type as deprecated.
*
Expand Down
53 changes: 45 additions & 8 deletions packages/compiler/src/core/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3179,10 +3179,7 @@ export function createChecker(program: Program): Checker {
) {
const doc = extractParamDoc(prop.parent.parent.parent, type.name);
if (doc) {
type.decorators.unshift({
decorator: $docFromComment,
args: [{ value: createLiteralType(doc), jsValue: doc }],
});
type.decorators.unshift(createDocFromCommentDecorator("self", doc));
}
}
finishType(type);
Expand All @@ -3193,6 +3190,16 @@ export function createChecker(program: Program): Checker {
return type;
}

function createDocFromCommentDecorator(key: "self" | "returns" | "errors", doc: string) {
return {
decorator: $docFromComment,
args: [
{ value: createLiteralType(key), jsValue: key },
{ value: createLiteralType(doc), jsValue: doc },
],
};
}

function isValueType(type: Type): boolean {
if (type === nullType) {
return true;
Expand Down Expand Up @@ -3439,10 +3446,16 @@ export function createChecker(program: Program): Checker {
// Doc comment should always be the first decorator in case an explicit @doc must override it.
const docComment = extractMainDoc(targetType);
if (docComment) {
decorators.unshift({
decorator: $docFromComment,
args: [{ value: createLiteralType(docComment), jsValue: docComment }],
});
decorators.unshift(createDocFromCommentDecorator("self", docComment));
}
if (targetType.kind === "Operation") {
const returnTypesDocs = extractReturnsDocs(targetType);
if (returnTypesDocs.returns) {
decorators.unshift(createDocFromCommentDecorator("returns", returnTypesDocs.returns));
}
if (returnTypesDocs.errors) {
decorators.unshift(createDocFromCommentDecorator("errors", returnTypesDocs.errors));
}
}
return decorators;
}
Expand Down Expand Up @@ -5827,6 +5840,30 @@ function extractMainDoc(type: Type): string | undefined {
return trimmed === "" ? undefined : trimmed;
}

function extractReturnsDocs(type: Type): {
returns: string | undefined;
errors: string | undefined;
} {
const result: { returns: string | undefined; errors: string | undefined } = {
returns: undefined,
errors: undefined,
};
if (type.node?.docs === undefined) {
return result;
}
for (const doc of type.node.docs) {
for (const tag of doc.tags) {
if (tag.kind === SyntaxKind.DocReturnsTag) {
result.returns = getDocContent(tag.content);
}
if (tag.kind === SyntaxKind.DocErrorsTag) {
result.errors = getDocContent(tag.content);
}
}
}
return result;
}

function extractParamDoc(node: OperationStatementNode, paramName: string): string | undefined {
if (node.docs === undefined) {
return undefined;
Expand Down
6 changes: 5 additions & 1 deletion packages/compiler/src/core/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
DirectiveArgument,
DirectiveExpressionNode,
DocContent,
DocErrorsTagNode,
DocNode,
DocParamTagNode,
DocReturnsTagNode,
Expand Down Expand Up @@ -2399,7 +2400,7 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
}

type ParamLikeTag = DocTemplateTagNode | DocParamTagNode;
type SimpleTag = DocReturnsTagNode | DocUnknownTagNode;
type SimpleTag = DocReturnsTagNode | DocErrorsTagNode | DocUnknownTagNode;

function parseDocTag(): DocTag {
const pos = tokenPos();
Expand All @@ -2413,6 +2414,8 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
case "return":
case "returns":
return parseDocSimpleTag(pos, tagName, SyntaxKind.DocReturnsTag);
case "errors":
return parseDocSimpleTag(pos, tagName, SyntaxKind.DocErrorsTag);
default:
return parseDocSimpleTag(pos, tagName, SyntaxKind.DocUnknownTag);
}
Expand Down Expand Up @@ -3127,6 +3130,7 @@ export function visitChildren<T>(node: Node, cb: NodeCallback<T>): T | undefined
visitNode(cb, node.tagName) || visitNode(cb, node.paramName) || visitEach(cb, node.content)
);
case SyntaxKind.DocReturnsTag:
case SyntaxKind.DocErrorsTag:
case SyntaxKind.DocUnknownTag:
return visitNode(cb, node.tagName) || visitEach(cb, node.content);

Expand Down
12 changes: 11 additions & 1 deletion packages/compiler/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,7 @@ export enum SyntaxKind {
DocText,
DocParamTag,
DocReturnsTag,
DocErrorsTag,
DocTemplateTag,
DocUnknownTag,
Projection,
Expand Down Expand Up @@ -1558,7 +1559,12 @@ export interface DocTagBaseNode extends BaseNode {
readonly content: readonly DocContent[];
}

export type DocTag = DocReturnsTagNode | DocParamTagNode | DocTemplateTagNode | DocUnknownTagNode;
export type DocTag =
| DocReturnsTagNode
| DocErrorsTagNode
| DocParamTagNode
| DocTemplateTagNode
| DocUnknownTagNode;
export type DocContent = DocTextNode;

export interface DocTextNode extends BaseNode {
Expand All @@ -1570,6 +1576,10 @@ export interface DocReturnsTagNode extends DocTagBaseNode {
readonly kind: SyntaxKind.DocReturnsTag;
}

export interface DocErrorsTagNode extends DocTagBaseNode {
readonly kind: SyntaxKind.DocErrorsTag;
}

export interface DocParamTagNode extends DocTagBaseNode {
readonly kind: SyntaxKind.DocParamTag;
readonly paramName: IdentifierNode;
Expand Down
1 change: 1 addition & 0 deletions packages/compiler/src/formatter/print/printer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ export function printNode(
case SyntaxKind.DocParamTag:
case SyntaxKind.DocTemplateTag:
case SyntaxKind.DocReturnsTag:
case SyntaxKind.DocErrorsTag:
case SyntaxKind.DocUnknownTag:
// https://github.com/microsoft/typespec/issues/1319 Tracks pretty-printing doc comments.
compilerAssert(
Expand Down
Loading

0 comments on commit 9a2a1bf

Please sign in to comment.