Skip to content

Commit 9a2a1bf

Browse files
Feature: Add support for @returns and @errors doc comment tags (microsoft#2436)
fix microsoft#2384
1 parent 8415d52 commit 9a2a1bf

16 files changed

+554
-23
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@typespec/compiler",
5+
"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.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@typespec/compiler"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@typespec/http",
5+
"comment": "Add support for `@returns` and `@errors` doc comment tags.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@typespec/http"
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"changes": [
3+
{
4+
"packageName": "@typespec/openapi3",
5+
"comment": "Add support for `@returns` and `@errors` doc comment tags.",
6+
"type": "none"
7+
}
8+
],
9+
"packageName": "@typespec/openapi3"
10+
}

docs/standard-library/built-in-decorators.md

+52
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,32 @@ message: string;
158158
```
159159

160160

161+
### `@errorsDoc` {#@errorsDoc}
162+
163+
Attach a documentation string to describe the error return types of an operation.
164+
If an operation returns a union of success and errors it only describe the errors. See `@errorsDoc` for success documentation.
165+
166+
```typespec
167+
@errorsDoc(doc: valueof string)
168+
```
169+
170+
#### Target
171+
172+
`Operation`
173+
174+
#### Parameters
175+
| Name | Type | Description |
176+
|------|------|-------------|
177+
| doc | `valueof scalar string` | Documentation string |
178+
179+
#### Examples
180+
181+
```typespec
182+
@errorsDoc("Returns doc")
183+
op get(): Pet | NotFound;
184+
```
185+
186+
161187
### `@format` {#@format}
162188

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

633659

660+
### `@returnsDoc` {#@returnsDoc}
661+
662+
Attach a documentation string to describe the successful return types of an operation.
663+
If an operation returns a union of success and errors it only describe the success. See `@errorsDoc` for error documentation.
664+
665+
```typespec
666+
@returnsDoc(doc: valueof string)
667+
```
668+
669+
#### Target
670+
671+
`Operation`
672+
673+
#### Parameters
674+
| Name | Type | Description |
675+
|------|------|-------------|
676+
| doc | `valueof scalar string` | Documentation string |
677+
678+
#### Examples
679+
680+
```typespec
681+
@returnsDoc("Returns doc")
682+
op get(): Pet | NotFound;
683+
```
684+
685+
634686
### `@returnTypeVisibility` {#@returnTypeVisibility}
635687

636688
Sets which visibilities apply to the return type for the given operation.

packages/compiler/lib/decorators.tsp

+26
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,32 @@ extern dec summary(target: unknown, summary: valueof string);
2929
*/
3030
extern dec doc(target: unknown, doc: valueof string, formatArgs?: {});
3131

32+
/**
33+
* Attach a documentation string to describe the successful return types of an operation.
34+
* If an operation returns a union of success and errors it only describe the success. See `@errorsDoc` for error documentation.
35+
* @param doc Documentation string
36+
*
37+
* @example
38+
* ```typespec
39+
* @returnsDoc("Returns doc")
40+
* op get(): Pet | NotFound;
41+
* ```
42+
*/
43+
extern dec returnsDoc(target: Operation, doc: valueof string);
44+
45+
/**
46+
* Attach a documentation string to describe the error return types of an operation.
47+
* If an operation returns a union of success and errors it only describe the errors. See `@errorsDoc` for success documentation.
48+
* @param doc Documentation string
49+
*
50+
* @example
51+
* ```typespec
52+
* @errorsDoc("Returns doc")
53+
* op get(): Pet | NotFound;
54+
* ```
55+
*/
56+
extern dec errorsDoc(target: Operation, doc: valueof string);
57+
3258
/**
3359
* Mark this type as deprecated.
3460
*

packages/compiler/src/core/checker.ts

+45-8
Original file line numberDiff line numberDiff line change
@@ -3179,10 +3179,7 @@ export function createChecker(program: Program): Checker {
31793179
) {
31803180
const doc = extractParamDoc(prop.parent.parent.parent, type.name);
31813181
if (doc) {
3182-
type.decorators.unshift({
3183-
decorator: $docFromComment,
3184-
args: [{ value: createLiteralType(doc), jsValue: doc }],
3185-
});
3182+
type.decorators.unshift(createDocFromCommentDecorator("self", doc));
31863183
}
31873184
}
31883185
finishType(type);
@@ -3193,6 +3190,16 @@ export function createChecker(program: Program): Checker {
31933190
return type;
31943191
}
31953192

3193+
function createDocFromCommentDecorator(key: "self" | "returns" | "errors", doc: string) {
3194+
return {
3195+
decorator: $docFromComment,
3196+
args: [
3197+
{ value: createLiteralType(key), jsValue: key },
3198+
{ value: createLiteralType(doc), jsValue: doc },
3199+
],
3200+
};
3201+
}
3202+
31963203
function isValueType(type: Type): boolean {
31973204
if (type === nullType) {
31983205
return true;
@@ -3439,10 +3446,16 @@ export function createChecker(program: Program): Checker {
34393446
// Doc comment should always be the first decorator in case an explicit @doc must override it.
34403447
const docComment = extractMainDoc(targetType);
34413448
if (docComment) {
3442-
decorators.unshift({
3443-
decorator: $docFromComment,
3444-
args: [{ value: createLiteralType(docComment), jsValue: docComment }],
3445-
});
3449+
decorators.unshift(createDocFromCommentDecorator("self", docComment));
3450+
}
3451+
if (targetType.kind === "Operation") {
3452+
const returnTypesDocs = extractReturnsDocs(targetType);
3453+
if (returnTypesDocs.returns) {
3454+
decorators.unshift(createDocFromCommentDecorator("returns", returnTypesDocs.returns));
3455+
}
3456+
if (returnTypesDocs.errors) {
3457+
decorators.unshift(createDocFromCommentDecorator("errors", returnTypesDocs.errors));
3458+
}
34463459
}
34473460
return decorators;
34483461
}
@@ -5827,6 +5840,30 @@ function extractMainDoc(type: Type): string | undefined {
58275840
return trimmed === "" ? undefined : trimmed;
58285841
}
58295842

5843+
function extractReturnsDocs(type: Type): {
5844+
returns: string | undefined;
5845+
errors: string | undefined;
5846+
} {
5847+
const result: { returns: string | undefined; errors: string | undefined } = {
5848+
returns: undefined,
5849+
errors: undefined,
5850+
};
5851+
if (type.node?.docs === undefined) {
5852+
return result;
5853+
}
5854+
for (const doc of type.node.docs) {
5855+
for (const tag of doc.tags) {
5856+
if (tag.kind === SyntaxKind.DocReturnsTag) {
5857+
result.returns = getDocContent(tag.content);
5858+
}
5859+
if (tag.kind === SyntaxKind.DocErrorsTag) {
5860+
result.errors = getDocContent(tag.content);
5861+
}
5862+
}
5863+
}
5864+
return result;
5865+
}
5866+
58305867
function extractParamDoc(node: OperationStatementNode, paramName: string): string | undefined {
58315868
if (node.docs === undefined) {
58325869
return undefined;

packages/compiler/src/core/parser.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
DirectiveArgument,
2828
DirectiveExpressionNode,
2929
DocContent,
30+
DocErrorsTagNode,
3031
DocNode,
3132
DocParamTagNode,
3233
DocReturnsTagNode,
@@ -2399,7 +2400,7 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
23992400
}
24002401

24012402
type ParamLikeTag = DocTemplateTagNode | DocParamTagNode;
2402-
type SimpleTag = DocReturnsTagNode | DocUnknownTagNode;
2403+
type SimpleTag = DocReturnsTagNode | DocErrorsTagNode | DocUnknownTagNode;
24032404

24042405
function parseDocTag(): DocTag {
24052406
const pos = tokenPos();
@@ -2413,6 +2414,8 @@ function createParser(code: string | SourceFile, options: ParseOptions = {}): Pa
24132414
case "return":
24142415
case "returns":
24152416
return parseDocSimpleTag(pos, tagName, SyntaxKind.DocReturnsTag);
2417+
case "errors":
2418+
return parseDocSimpleTag(pos, tagName, SyntaxKind.DocErrorsTag);
24162419
default:
24172420
return parseDocSimpleTag(pos, tagName, SyntaxKind.DocUnknownTag);
24182421
}
@@ -3127,6 +3130,7 @@ export function visitChildren<T>(node: Node, cb: NodeCallback<T>): T | undefined
31273130
visitNode(cb, node.tagName) || visitNode(cb, node.paramName) || visitEach(cb, node.content)
31283131
);
31293132
case SyntaxKind.DocReturnsTag:
3133+
case SyntaxKind.DocErrorsTag:
31303134
case SyntaxKind.DocUnknownTag:
31313135
return visitNode(cb, node.tagName) || visitEach(cb, node.content);
31323136

packages/compiler/src/core/types.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ export enum SyntaxKind {
752752
DocText,
753753
DocParamTag,
754754
DocReturnsTag,
755+
DocErrorsTag,
755756
DocTemplateTag,
756757
DocUnknownTag,
757758
Projection,
@@ -1558,7 +1559,12 @@ export interface DocTagBaseNode extends BaseNode {
15581559
readonly content: readonly DocContent[];
15591560
}
15601561

1561-
export type DocTag = DocReturnsTagNode | DocParamTagNode | DocTemplateTagNode | DocUnknownTagNode;
1562+
export type DocTag =
1563+
| DocReturnsTagNode
1564+
| DocErrorsTagNode
1565+
| DocParamTagNode
1566+
| DocTemplateTagNode
1567+
| DocUnknownTagNode;
15621568
export type DocContent = DocTextNode;
15631569

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

1579+
export interface DocErrorsTagNode extends DocTagBaseNode {
1580+
readonly kind: SyntaxKind.DocErrorsTag;
1581+
}
1582+
15731583
export interface DocParamTagNode extends DocTagBaseNode {
15741584
readonly kind: SyntaxKind.DocParamTag;
15751585
readonly paramName: IdentifierNode;

packages/compiler/src/formatter/print/printer.ts

+1
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ export function printNode(
347347
case SyntaxKind.DocParamTag:
348348
case SyntaxKind.DocTemplateTag:
349349
case SyntaxKind.DocReturnsTag:
350+
case SyntaxKind.DocErrorsTag:
350351
case SyntaxKind.DocUnknownTag:
351352
// https://github.com/microsoft/typespec/issues/1319 Tracks pretty-printing doc comments.
352353
compilerAssert(

0 commit comments

Comments
 (0)