Skip to content

Commit 3340612

Browse files
committed
Add support for generating enumsynopsis for Enums
1 parent c7797fc commit 3340612

File tree

4 files changed

+198
-40
lines changed

4 files changed

+198
-40
lines changed

build/gen_stub.php

Lines changed: 131 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2217,10 +2217,10 @@ public function enterNode(Node $expr)
22172217
$isUnknownConstValue = false;
22182218

22192219
$evaluator = new ConstExprEvaluator(
2220-
static function (Expr $expr) use ($allConstInfos, &$isUnknownConstValue) {
2221-
// $expr is a ConstFetch with a name of a C macro here
2220+
function (Expr $expr) use ($allConstInfos, &$isUnknownConstValue) {
2221+
// $expr is a ConstFetch with a name of a C macro here. Class constants are not supported yet
22222222
if (!$expr instanceof Expr\ConstFetch) {
2223-
throw new Exception($this->getVariableTypeName() . " " . $this->name->__toString() . " has an unsupported value");
2223+
throw new Exception("Cannot evaluate expression");
22242224
}
22252225

22262226
$constName = $expr->name->__toString();
@@ -2379,7 +2379,7 @@ abstract protected function getFieldSynopsisDefaultLinkend(): string;
23792379
abstract protected function getFieldSynopsisName(): string;
23802380

23812381
/** @param array<string, ConstInfo> $allConstInfos */
2382-
abstract protected function getFieldSynopsisValueString(array $allConstInfos): ?string;
2382+
abstract public function getFieldSynopsisValueString(array $allConstInfos): ?string;
23832383

23842384
abstract public function discardInfoForOldPhpVersions(?int $minimumPhpVersionIdCompatibility): void;
23852385

@@ -2604,7 +2604,7 @@ protected function getFieldSynopsisName(): string
26042604
}
26052605

26062606
/** @param array<string, ConstInfo> $allConstInfos */
2607-
protected function getFieldSynopsisValueString(array $allConstInfos): ?string
2607+
public function getFieldSynopsisValueString(array $allConstInfos): ?string
26082608
{
26092609
$value = EvaluatedValue::createFromExpression($this->value, null, $this->cValue, $allConstInfos);
26102610
if ($value->isUnknownConstValue) {
@@ -2939,7 +2939,7 @@ protected function getFieldSynopsisName(): string
29392939
}
29402940

29412941
/** @param array<string, ConstInfo> $allConstInfos */
2942-
protected function getFieldSynopsisValueString(array $allConstInfos): ?string
2942+
public function getFieldSynopsisValueString(array $allConstInfos): ?string
29432943
{
29442944
return $this->defaultValueString;
29452945
}
@@ -3061,10 +3061,12 @@ public function __clone()
30613061
class EnumCaseInfo {
30623062
public string $name;
30633063
public ?Expr $value;
3064+
public ?string $valueString;
30643065

3065-
public function __construct(string $name, ?Expr $value) {
3066+
public function __construct(string $name, ?Expr $value, ?string $valueString) {
30663067
$this->name = $name;
30673068
$this->value = $value;
3069+
$this->valueString = $valueString;
30683070
}
30693071

30703072
/** @param array<string, ConstInfo> $allConstInfos */
@@ -3082,6 +3084,51 @@ public function getDeclaration(array $allConstInfos): string {
30823084

30833085
return $code;
30843086
}
3087+
3088+
/** @param array<string, ConstInfo> $allConstInfos */
3089+
public function getEnumItemElement(DOMDocument $doc, array $allConstInfos): DOMElement
3090+
{
3091+
$enumItemElement = $doc->createElement("enumitem");
3092+
3093+
$enumItemElement->appendChild(new DOMText("\n "));
3094+
$enumIdentifierElement = $doc->createElement("enumidentifier");
3095+
$enumIdentifierElement->appendChild(new DOMText($this->name));
3096+
$enumItemElement->appendChild($enumIdentifierElement);
3097+
3098+
$valueString = $this->getEnumValueString($allConstInfos);
3099+
if ($valueString) {
3100+
$enumValueElement = $doc->createElement("enumvalue");
3101+
$enumItemElement->appendChild(new DOMText("\n "));
3102+
$initializerElement = $doc->createElement("initializer", $valueString);
3103+
$enumValueElement->appendChild($initializerElement);
3104+
$enumItemElement->appendChild($enumValueElement);
3105+
}
3106+
3107+
$enumItemElement->appendChild(new DOMText("\n "));
3108+
3109+
return $enumItemElement;
3110+
}
3111+
3112+
/** @param array<string, ConstInfo> $allConstInfos */
3113+
protected function getEnumValueString(array $allConstInfos): ?string
3114+
{
3115+
if ($this->value === null) {
3116+
return null;
3117+
};
3118+
3119+
$value = EvaluatedValue::createFromExpression($this->value, null, null, $allConstInfos);
3120+
if ($value->isUnknownConstValue) {
3121+
return null;
3122+
}
3123+
3124+
if ($value->originatingConsts) {
3125+
return implode("\n", array_map(function (ConstInfo $const) use ($allConstInfos) {
3126+
return $const->getFieldSynopsisValueString($allConstInfos);
3127+
}, $value->originatingConsts));
3128+
}
3129+
3130+
return $this->valueString;
3131+
}
30853132
}
30863133

30873134
class AttributeInfo {
@@ -3468,17 +3515,36 @@ public function getClassSynopsisDocument(array $classMap, array $allConstInfos):
34683515
* @param array<string, ConstInfo> $allConstInfos
34693516
*/
34703517
public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array $allConstInfos): ?DOMElement {
3518+
$isEnum = $this->type === "enum";
3519+
3520+
$classSynopsis = $doc->createElement($isEnum ? "enumsynopsis" : "classsynopsis");
3521+
$synopsisInfoName = $isEnum ? "synopsisinfo" : "classsynopsisinfo";
34713522

3472-
$classSynopsis = $doc->createElement("classsynopsis");
3473-
$classSynopsis->setAttribute("class", $this->type === "interface" ? "interface" : "class");
3523+
if (!$isEnum) {
3524+
$classSynopsis->setAttribute("class", $this->type);
3525+
}
34743526

34753527
$exceptionOverride = $this->type === "class" && $this->isException($classMap) ? "exception" : null;
3528+
$classSynopsis->appendChild(new DOMText("\n "));
3529+
34763530
$ooElement = self::createOoElement($doc, $this, $exceptionOverride, true, null, 4);
3477-
if (!$ooElement) {
3478-
return null;
3531+
if ($ooElement) {
3532+
$classSynopsis->appendChild($ooElement);
3533+
}
3534+
3535+
if ($isEnum) {
3536+
$enumNameElement = $doc->createElement("enumname");
3537+
$enumNameElement->appendChild(new DOMText($this->name->toString()));
3538+
$classSynopsis->appendChild($enumNameElement);
3539+
3540+
if ($this->enumBackingType) {
3541+
$classSynopsis->appendChild(new DOMText("\n "));
3542+
$enumNameElement = $doc->createElement("modifier");
3543+
$enumNameElement->setAttribute("role", "enum_backing_type");
3544+
$enumNameElement->appendChild(new DOMText($this->enumBackingType->name));
3545+
$classSynopsis->appendChild($enumNameElement);
3546+
}
34793547
}
3480-
$classSynopsis->appendChild(new DOMText("\n "));
3481-
$classSynopsis->appendChild($ooElement);
34823548

34833549
foreach ($this->extends as $k => $parent) {
34843550
$parentInfo = $classMap[$parent->toString()] ?? null;
@@ -3502,6 +3568,11 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array
35023568
$classSynopsis->appendChild($ooElement);
35033569
}
35043570

3571+
// Enums implicitly implement either UnitEnum or BackEnum. This way inherited methods can be displayed.
3572+
if ($isEnum) {
3573+
$this->implements[] = new Name\FullyQualified($this->enumBackingType ? "BackedEnum" : "UnitEnum");
3574+
}
3575+
35053576
foreach ($this->implements as $k => $interface) {
35063577
$interfaceInfo = $classMap[$interface->toString()] ?? null;
35073578
if (!$interfaceInfo) {
@@ -3535,13 +3606,14 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array
35353606
$doc,
35363607
$classSynopsis,
35373608
$parentsWithInheritedConstants,
3609+
$synopsisInfoName,
35383610
"&Constants;",
35393611
"&InheritedConstants;"
35403612
);
35413613

35423614
if (!empty($this->constInfos)) {
35433615
$classSynopsis->appendChild(new DOMText("\n\n "));
3544-
$classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Constants;");
3616+
$classSynopsisInfo = $doc->createElement($synopsisInfoName, "&Constants;");
35453617
$classSynopsisInfo->setAttribute("role", "comment");
35463618
$classSynopsis->appendChild($classSynopsisInfo);
35473619

@@ -3552,9 +3624,22 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array
35523624
}
35533625
}
35543626

3627+
if (!empty($this->enumCaseInfos)) {
3628+
$classSynopsis->appendChild(new DOMText("\n\n "));
3629+
$classSynopsisInfo = $doc->createElement($synopsisInfoName, "&EnumCases;");
3630+
$classSynopsisInfo->setAttribute("role", "comment");
3631+
$classSynopsis->appendChild($classSynopsisInfo);
3632+
3633+
foreach ($this->enumCaseInfos as $enumCaseInfo) {
3634+
$classSynopsis->appendChild(new DOMText("\n "));
3635+
$enumItemElement = $enumCaseInfo->getEnumItemElement($doc, $allConstInfos);
3636+
$classSynopsis->appendChild($enumItemElement);
3637+
}
3638+
}
3639+
35553640
if (!empty($this->propertyInfos)) {
35563641
$classSynopsis->appendChild(new DOMText("\n\n "));
3557-
$classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Properties;");
3642+
$classSynopsisInfo = $doc->createElement($synopsisInfoName, "&Properties;");
35583643
$classSynopsisInfo->setAttribute("role", "comment");
35593644
$classSynopsis->appendChild($classSynopsisInfo);
35603645

@@ -3569,13 +3654,14 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array
35693654
$doc,
35703655
$classSynopsis,
35713656
$parentsWithInheritedProperties,
3657+
$synopsisInfoName,
35723658
"&Properties;",
35733659
"&InheritedProperties;"
35743660
);
35753661

35763662
if (!empty($this->funcInfos)) {
35773663
$classSynopsis->appendChild(new DOMText("\n\n "));
3578-
$classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&Methods;");
3664+
$classSynopsisInfo = $doc->createElement($synopsisInfoName, "&Methods;");
35793665
$classSynopsisInfo->setAttribute("role", "comment");
35803666
$classSynopsis->appendChild($classSynopsisInfo);
35813667

@@ -3612,7 +3698,7 @@ public function getClassSynopsisElement(DOMDocument $doc, array $classMap, array
36123698

36133699
if (!empty($parentsWithInheritedMethods)) {
36143700
$classSynopsis->appendChild(new DOMText("\n\n "));
3615-
$classSynopsisInfo = $doc->createElement("classsynopsisinfo", "&InheritedMethods;");
3701+
$classSynopsisInfo = $doc->createElement($synopsisInfoName, "&InheritedMethods;");
36163702
$classSynopsisInfo->setAttribute("role", "comment");
36173703
$classSynopsis->appendChild($classSynopsisInfo);
36183704

@@ -3650,8 +3736,7 @@ private static function createOoElement(
36503736
): ?DOMElement {
36513737
$indentation = str_repeat(" ", $indentationLevel);
36523738

3653-
if ($classInfo->type !== "class" && $classInfo->type !== "interface") {
3654-
echo "Class synopsis generation is not implemented for " . $classInfo->type . "\n";
3739+
if ($classInfo->type === "enum") {
36553740
return null;
36563741
}
36573742

@@ -3745,6 +3830,8 @@ private function collectInheritedMembers(
37453830
);
37463831
}
37473832

3833+
$isEnum = $this->type === "enum";
3834+
37483835
foreach ($this->implements as $parent) {
37493836
$parentInfo = $classMap[$parent->toString()] ?? null;
37503837
if (!$parentInfo) {
@@ -3758,13 +3845,25 @@ private function collectInheritedMembers(
37583845
$unusedParentsWithInheritedProperties = [];
37593846
$unusedParentsWithInheritedMethods = [];
37603847

3761-
$parentInfo->collectInheritedMembers(
3762-
$parentsWithInheritedConstants,
3763-
$unusedParentsWithInheritedProperties,
3764-
$unusedParentsWithInheritedMethods,
3765-
$hasConstructor,
3766-
$classMap
3767-
);
3848+
if ($isEnum) {
3849+
$parentInfo->collectInheritedMembers(
3850+
$parentsWithInheritedConstants,
3851+
$unusedParentsWithInheritedProperties,
3852+
// We only want to collect inherited methods in case of enums
3853+
$parentsWithInheritedMethods,
3854+
$hasConstructor,
3855+
$classMap
3856+
);
3857+
} else {
3858+
$parentInfo->collectInheritedMembers(
3859+
$parentsWithInheritedConstants,
3860+
$unusedParentsWithInheritedProperties,
3861+
// We only want to collect inherited methods in case of enums
3862+
$unusedParentsWithInheritedMethods,
3863+
$hasConstructor,
3864+
$classMap
3865+
);
3866+
}
37683867
}
37693868
}
37703869

@@ -3886,14 +3985,14 @@ public function __clone()
38863985
/**
38873986
* @param Name[] $parents
38883987
*/
3889-
private function appendInheritedMemberSectionToClassSynopsis(DOMDocument $doc, DOMElement $classSynopsis, array $parents, string $label, string $inheritedLabel): void
3988+
private function appendInheritedMemberSectionToClassSynopsis(DOMDocument $doc, DOMElement $classSynopsis, array $parents, string $synopsisInfoName, string $label, string $inheritedLabel): void
38903989
{
38913990
if (empty($parents)) {
38923991
return;
38933992
}
38943993

38953994
$classSynopsis->appendChild(new DOMText("\n\n "));
3896-
$classSynopsisInfo = $doc->createElement("classsynopsisinfo", "$inheritedLabel");
3995+
$classSynopsisInfo = $doc->createElement($synopsisInfoName, "$inheritedLabel");
38973996
$classSynopsisInfo->setAttribute("role", "comment");
38983997
$classSynopsis->appendChild($classSynopsisInfo);
38993998

@@ -4738,7 +4837,10 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
47384837
);
47394838
} else if ($classStmt instanceof Stmt\EnumCase) {
47404839
$enumCaseInfos[] = new EnumCaseInfo(
4741-
$classStmt->name->toString(), $classStmt->expr);
4840+
$classStmt->name->toString(),
4841+
$classStmt->expr,
4842+
$prettyPrinter->prettyPrintExpr($classStmt->expr)
4843+
);
47424844
} else {
47434845
throw new Exception("Not implemented {$classStmt->getType()}");
47444846
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?xml version="1.0"?>
2+
<enumsynopsis>
3+
<enumname>Random\IntervalBoundary</enumname>
4+
<modifier role="enum_backing_type">int</modifier>
5+
6+
<oointerface>
7+
<modifier>implements</modifier>
8+
<interfacename>BackedEnum</interfacename>
9+
</oointerface>
10+
11+
<synopsisinfo role="comment">&Constants;</synopsisinfo>
12+
<fieldsynopsis>
13+
<modifier>const</modifier>
14+
<type>int</type>
15+
<varname linkend="random-intervalboundary.constants.closedc">Random\IntervalBoundary::ClosedC</varname>
16+
<initializer>2</initializer>
17+
</fieldsynopsis>
18+
19+
<synopsisinfo role="comment">&EnumCases;</synopsisinfo>
20+
<enumitem>
21+
<enumidentifier>ClosedOpen</enumidentifier>
22+
<enumvalue><initializer>1</initializer></enumvalue>
23+
</enumitem>
24+
<enumitem>
25+
<enumidentifier>ClosedClosed</enumidentifier>
26+
<enumvalue><initializer>2</initializer></enumvalue>
27+
</enumitem>
28+
<enumitem>
29+
<enumidentifier>OpenClosed</enumidentifier>
30+
<enumvalue><initializer>3</initializer></enumvalue>
31+
</enumitem>
32+
<enumitem>
33+
<enumidentifier>OpenOpen</enumidentifier>
34+
<enumvalue><initializer>4</initializer></enumvalue>
35+
</enumitem>
36+
37+
<synopsisinfo role="comment">&InheritedMethods;</synopsisinfo>
38+
<xi:include xpointer="xmlns(db=http://docbook.org/ns/docbook) xpointer(id('class.unitenum')/db:refentry/db:refsect1[@role='description']/descendant::db:methodsynopsis[@role='UnitEnum'])">
39+
<xi:fallback/>
40+
</xi:include>
41+
</enumsynopsis>

ext/random/random.stub.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,11 +155,12 @@ public function __serialize(): array {}
155155
public function __unserialize(array $data): void {}
156156
}
157157

158-
enum IntervalBoundary {
159-
case ClosedOpen;
160-
case ClosedClosed;
161-
case OpenClosed;
162-
case OpenOpen;
158+
enum IntervalBoundary: int {
159+
const int ClosedC = 2;
160+
case ClosedOpen = 1;
161+
case ClosedClosed = IntervalBoundary::ClosedC;
162+
case OpenClosed = 3;
163+
case OpenOpen = 4;
163164
}
164165

165166
/**

0 commit comments

Comments
 (0)