Skip to content

Commit ad977aa

Browse files
authored
feat(graindoc)!: Allow @since and @returns once per export (#1946)
1 parent 5401c75 commit ad977aa

File tree

5 files changed

+92
-38
lines changed

5 files changed

+92
-38
lines changed

compiler/graindoc/docblock.re

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ exception
8080
exception MissingLabeledParamType({name: string});
8181
exception MissingUnlabeledParamType({idx: int});
8282
exception MissingReturnType;
83+
exception AttributeAppearsMultipleTimes({attr: string});
8384
exception
8485
InvalidAttribute({
8586
name: string,
@@ -115,6 +116,10 @@ let () =
115116
| MissingReturnType =>
116117
let msg = "Unable to find a return type. Please file an issue!";
117118
Some(msg);
119+
| AttributeAppearsMultipleTimes({attr}) =>
120+
let msg =
121+
Printf.sprintf("Attribute @%s is only allowed to appear once.", attr);
122+
Some(msg);
118123
| InvalidAttribute({name, attr}) =>
119124
let msg = Printf.sprintf("Invalid attribute @%s on %s", attr, name);
120125
Some(msg);
@@ -292,16 +297,18 @@ let for_value_description =
292297
examples,
293298
)
294299
| Since({attr_version}) =>
295-
// TODO(#787): Should we fail if more than one `@since` attribute?
296-
(
297-
deprecations,
298-
Some({since_version: attr_version}),
299-
history,
300-
params,
301-
returns,
302-
throws,
303-
examples,
304-
)
300+
switch (since) {
301+
| Some(_) => raise(AttributeAppearsMultipleTimes({attr: "since"}))
302+
| None => (
303+
deprecations,
304+
Some({since_version: attr_version}),
305+
history,
306+
params,
307+
returns,
308+
throws,
309+
examples,
310+
)
311+
}
305312
| History({attr_version: history_version, attr_desc: history_msg}) => (
306313
deprecations,
307314
since,
@@ -347,20 +354,25 @@ let for_value_description =
347354
examples,
348355
);
349356
| Returns({attr_desc: returns_msg}) =>
350-
let returns_type =
351-
switch (return_type) {
352-
| Some(typ) => Printtyp.string_of_type_sch(typ)
353-
| None => raise(MissingReturnType)
354-
};
355-
(
356-
deprecations,
357-
since,
358-
history,
359-
params,
360-
Some({returns_msg, returns_type}),
361-
throws,
362-
examples,
363-
);
357+
switch (returns) {
358+
| Some(_) =>
359+
raise(AttributeAppearsMultipleTimes({attr: "returns"}))
360+
| None =>
361+
let returns_type =
362+
switch (return_type) {
363+
| Some(typ) => Printtyp.string_of_type_sch(typ)
364+
| None => raise(MissingReturnType)
365+
};
366+
(
367+
deprecations,
368+
since,
369+
history,
370+
params,
371+
Some({returns_msg, returns_type}),
372+
throws,
373+
examples,
374+
);
375+
}
364376
| Throws({attr_type: throw_type, attr_desc: throw_msg}) => (
365377
deprecations,
366378
since,
@@ -427,13 +439,15 @@ let for_type_declaration =
427439
examples,
428440
)
429441
| Since({attr_version}) =>
430-
// TODO(#787): Should we fail if more than one `@since` attribute?
431-
(
432-
deprecations,
433-
Some({since_version: attr_version}),
434-
history,
435-
examples,
436-
)
442+
switch (since) {
443+
| Some(_) => raise(AttributeAppearsMultipleTimes({attr: "since"}))
444+
| None => (
445+
deprecations,
446+
Some({since_version: attr_version}),
447+
history,
448+
examples,
449+
)
450+
}
437451
| History({attr_version: history_version, attr_desc: history_msg}) => (
438452
deprecations,
439453
since,
@@ -554,13 +568,15 @@ and for_signature_items =
554568
examples,
555569
)
556570
| Since({attr_version}) =>
557-
// TODO(#787): Should we fail if more than one `@since` attribute?
558-
(
559-
deprecations,
560-
Some({since_version: attr_version}),
561-
history,
562-
examples,
563-
)
571+
switch (since) {
572+
| Some(_) => raise(AttributeAppearsMultipleTimes({attr: "since"}))
573+
| None => (
574+
deprecations,
575+
Some({since_version: attr_version}),
576+
history,
577+
examples,
578+
)
579+
}
564580
| History({attr_version: history_version, attr_desc: history_msg}) => (
565581
deprecations,
566582
since,
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module SingleReturn
2+
3+
/**
4+
* @returns first return
5+
* @returns second return
6+
*/
7+
provide let test = () => 1
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module SingleSince
2+
3+
/**
4+
* @since v1.0.0
5+
* @since v1.0.0
6+
*/
7+
provide let test = () => print("t")

compiler/test/runner.re

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,3 +487,14 @@ let makeGrainDocRunner = (test, name, filename, arguments) => {
487487
},
488488
);
489489
};
490+
491+
let makeGrainDocErrorRunner = (test, name, filename, expected, arguments) => {
492+
test(
493+
name,
494+
({expect}) => {
495+
let infile = gaindoc_in_file(filename);
496+
let (result, _) = doc(infile, arguments);
497+
expect.string(result).toMatch(expected);
498+
},
499+
);
500+
};

compiler/test/suites/graindoc.re

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe("graindoc", ({test, testSkip}) => {
99
Sys.backend_type == Other("js_of_ocaml") ? testSkip : test;
1010

1111
let assertGrianDocOutput = makeGrainDocRunner(test_or_skip);
12+
let assertGrainDocError = makeGrainDocErrorRunner(test_or_skip);
1213
();
1314
assertGrianDocOutput("noDoc", "noDoc", [||]);
1415
assertGrianDocOutput(
@@ -18,4 +19,16 @@ describe("graindoc", ({test, testSkip}) => {
1819
);
1920
assertGrianDocOutput("since", "since", [|"--current-version=v0.2.0"|]);
2021
assertGrianDocOutput("example", "example", [|"--current-version=v0.2.0"|]);
22+
assertGrainDocError(
23+
"singleSince",
24+
"singleSince",
25+
"Attribute @since is only allowed to appear once.",
26+
[|"--current-version=v0.2.0"|],
27+
);
28+
assertGrainDocError(
29+
"singleReturn",
30+
"singleReturn",
31+
"Attribute @returns is only allowed to appear once.",
32+
[|"--current-version=v0.2.0"|],
33+
);
2134
});

0 commit comments

Comments
 (0)