diff --git a/build/psalm/OcpSinceChecker.php b/build/psalm/OcpSinceChecker.php index 9323b07feb7a6..964693d216e7f 100644 --- a/build/psalm/OcpSinceChecker.php +++ b/build/psalm/OcpSinceChecker.php @@ -140,6 +140,9 @@ private static function checkClassComment(ClassLike $stmt, FileSource $statement ) ); } + foreach ($parsedDocblock->tags['since'] ?? [] as $since) { + self::checkSinceIsReleasable(trim($since), $statementsSource, $stmt); + } if (isset($parsedDocblock->tags['depreacted'])) { IssueBuffer::maybeAdd( @@ -184,6 +187,9 @@ private static function checkStatementComment(Stmt $stmt, FileSource $statements ) ); } + foreach ($parsedDocblock->tags['since'] ?? [] as $since) { + self::checkSinceIsReleasable(trim($since), $statementsSource, $stmt); + } if (isset($parsedDocblock->tags['depreacted'])) { IssueBuffer::maybeAdd( @@ -213,9 +219,46 @@ private static function checkAttributeHasValidSinceVersion(\PhpParser\Node\Attri new CodeLocation($statementsSource, $stmt) ) ); + } else { + self::checkSinceIsReleasable($arg->value->value, $statementsSource, $stmt); } } } } } + + /** + * A since version must not be newer than the next patch release of the + * current branch, so backports with an unadjusted since fail + */ + private static function checkSinceIsReleasable(string $since, FileSource $statementsSource, \PhpParser\Node $stmt): void { + // Backports may list multiple versions: "33.0.1, 32.0.7" + if (!preg_match('/^\d+(\.\d+)*(\s*,\s*\d+(\.\d+)*)*/', $since, $list)) { + return; + } + + $maxVersion = self::getMaxSinceVersion(); + foreach (preg_split('/\s*,\s*/', $list[0]) as $version) { + if (version_compare($version, $maxVersion, '<=')) { + return; + } + } + + IssueBuffer::maybeAdd( + new InvalidDocblock( + 'Since version ' . $list[0] . ' is newer than the next release ' . $maxVersion . ' of this branch, please adjust it to the version the change is released in.', + new CodeLocation($statementsSource, $stmt) + ) + ); + } + + private static function getMaxSinceVersion(): string { + static $maxVersion = null; + if ($maxVersion === null) { + require __DIR__ . '/../../version.php'; + /** @var array{0: int, 1: int, 2: int, 3: int} $OC_Version */ + $maxVersion = $OC_Version[0] . '.' . $OC_Version[1] . '.' . ($OC_Version[2] + 1); + } + return $maxVersion; + } } diff --git a/lib/public/Authentication/IProvideUserSecretBackend.php b/lib/public/Authentication/IProvideUserSecretBackend.php index 04873f710bf47..2096e106f8481 100644 --- a/lib/public/Authentication/IProvideUserSecretBackend.php +++ b/lib/public/Authentication/IProvideUserSecretBackend.php @@ -27,7 +27,7 @@ interface IProvideUserSecretBackend { * secret but is unable to do so. * * @since 23.0.0 - * @since 35.0.0 The returns value is now optional. + * @since 34.0.2 The returns value is now optional. */ public function getCurrentUserSecret(): ?string; } diff --git a/lib/public/Calendar/CalendarImportOptions.php b/lib/public/Calendar/CalendarImportOptions.php index c302d916de717..45052409421d2 100644 --- a/lib/public/Calendar/CalendarImportOptions.php +++ b/lib/public/Calendar/CalendarImportOptions.php @@ -158,7 +158,7 @@ public function setValidate(int $value): void { /** * Gets whether to include object counts as the first yielded value * - * @since 35.0.0 + * @since 34.0.5 */ public function getCounts(): bool { return $this->counts; @@ -167,7 +167,7 @@ public function getCounts(): bool { /** * Sets whether to include object counts as the first yielded value * - * @since 35.0.0 + * @since 34.0.5 */ public function setCounts(bool $counts): void { $this->counts = $counts; diff --git a/lib/public/Calendar/Events/CalendarObjectUpdatedEvent.php b/lib/public/Calendar/Events/CalendarObjectUpdatedEvent.php index 0b4153df91f90..c6766c939ff6b 100644 --- a/lib/public/Calendar/Events/CalendarObjectUpdatedEvent.php +++ b/lib/public/Calendar/Events/CalendarObjectUpdatedEvent.php @@ -36,7 +36,7 @@ public function __construct( * Returns the object data as it was before the update. * * @return array - * @since 35.0.0 + * @since 34.0.0 */ public function getOldObjectData(): array { return $this->oldObjectData; diff --git a/lib/public/Calendar/Room/IRoomMetadata.php b/lib/public/Calendar/Room/IRoomMetadata.php index ebd69ae8f6652..0f52f60c615e9 100644 --- a/lib/public/Calendar/Room/IRoomMetadata.php +++ b/lib/public/Calendar/Room/IRoomMetadata.php @@ -43,7 +43,7 @@ interface IRoomMetadata { * building from BUILDING_ADDRESS, which only works for backends that * happen to put the building name first. * - * @since 35.0.0 + * @since 34.0.4 */ public const BUILDING_NAME = '{http://nextcloud.com/ns}room-building-name'; diff --git a/lib/public/Contacts/ContactsImportOptions.php b/lib/public/Contacts/ContactsImportOptions.php index 1543a4dad1df0..6bf3322618b20 100644 --- a/lib/public/Contacts/ContactsImportOptions.php +++ b/lib/public/Contacts/ContactsImportOptions.php @@ -13,29 +13,29 @@ /** * Contacts Import Options * - * @since 35.0.0 + * @since 34.0.5 */ final class ContactsImportOptions { /** - * @since 35.0.0 + * @since 34.0.5 */ public const FORMATS = ['vcf', 'jcf', 'xcf']; /** - * @since 35.0.0 + * @since 34.0.5 */ public const VALIDATE_NONE = 0; /** - * @since 35.0.0 + * @since 34.0.5 */ public const VALIDATE_SKIP = 1; /** - * @since 35.0.0 + * @since 34.0.5 */ public const VALIDATE_FAIL = 2; /** - * @since 35.0.0 + * @since 34.0.5 */ public const VALIDATE_OPTIONS = [ self::VALIDATE_NONE, @@ -43,15 +43,15 @@ final class ContactsImportOptions { self::VALIDATE_FAIL, ]; /** - * @since 35.0.0 + * @since 34.0.5 */ public const ERROR_CONTINUE = 0; /** - * @since 35.0.0 + * @since 34.0.5 */ public const ERROR_FAIL = 1; /** - * @since 35.0.0 + * @since 34.0.5 */ public const ERROR_OPTIONS = [ self::ERROR_CONTINUE, @@ -69,7 +69,7 @@ final class ContactsImportOptions { * Gets the import format * * @return 'vcf'|'jcf'|'xcf' (defaults to vcf) - * @since 35.0.0 + * @since 34.0.5 */ public function getFormat(): string { return $this->format; @@ -79,7 +79,7 @@ public function getFormat(): string { * Sets the import format * * @param 'vcf'|'jcf'|'xcf' $value - * @since 35.0.0 + * @since 34.0.5 */ public function setFormat(string $value): void { if (!in_array($value, self::FORMATS, true)) { @@ -91,7 +91,7 @@ public function setFormat(string $value): void { /** * Gets whether to supersede existing objects * - * @since 35.0.0 + * @since 34.0.5 */ public function getSupersede(): bool { return $this->supersede; @@ -100,7 +100,7 @@ public function getSupersede(): bool { /** * Sets whether to supersede existing objects * - * @since 35.0.0 + * @since 34.0.5 */ public function setSupersede(bool $supersede): void { $this->supersede = $supersede; @@ -110,7 +110,7 @@ public function setSupersede(bool $supersede): void { * Gets how to handle object errors * * @return int 0 - continue, 1 - fail - * @since 35.0.0 + * @since 34.0.5 */ public function getErrors(): int { return $this->errors; @@ -122,7 +122,7 @@ public function getErrors(): int { * @param int $value 0 - continue, 1 - fail * * @template $value of self::ERROR_* - * @since 35.0.0 + * @since 34.0.5 */ public function setErrors(int $value): void { if (!in_array($value, self::ERROR_OPTIONS, true)) { @@ -135,7 +135,7 @@ public function setErrors(int $value): void { * Gets how to handle object validation * * @return int 0 - no validation, 1 - validate and skip on issue, 2 - validate and fail on issue - * @since 35.0.0 + * @since 34.0.5 */ public function getValidate(): int { return $this->validate; @@ -147,7 +147,7 @@ public function getValidate(): int { * @param int $value 0 - no validation, 1 - validate and skip on issue, 2 - validate and fail on issue * * @template $value of self::VALIDATE_* - * @since 35.0.0 + * @since 34.0.5 */ public function setValidate(int $value): void { if (!in_array($value, self::VALIDATE_OPTIONS, true)) { @@ -159,7 +159,7 @@ public function setValidate(int $value): void { /** * Gets whether to include object counts as the first yielded value * - * @since 35.0.0 + * @since 34.0.5 */ public function getCounts(): bool { return $this->counts; @@ -168,7 +168,7 @@ public function getCounts(): bool { /** * Sets whether to include object counts as the first yielded value * - * @since 35.0.0 + * @since 34.0.5 */ public function setCounts(bool $counts): void { $this->counts = $counts; diff --git a/lib/public/DB/QueryBuilder/IFunctionBuilder.php b/lib/public/DB/QueryBuilder/IFunctionBuilder.php index 22521e9197c77..ff2a2ef1fa955 100644 --- a/lib/public/DB/QueryBuilder/IFunctionBuilder.php +++ b/lib/public/DB/QueryBuilder/IFunctionBuilder.php @@ -68,7 +68,7 @@ public function substring($input, $start, $length = null): IQueryFunction; * @param string|ILiteral|IParameter|IQueryFunction $pattern The pattern to match and return * * @return IQueryFunction - * @since 36.0.0 + * @since 34.0.5 */ public function regexSubstring($input, $pattern): IQueryFunction; diff --git a/lib/public/Defaults.php b/lib/public/Defaults.php index ecc8e98eee04a..258db268a9d9f 100644 --- a/lib/public/Defaults.php +++ b/lib/public/Defaults.php @@ -177,7 +177,7 @@ public function getLogo(bool $useSvg = true): string { * Raw logo image data (raster) for embedding directly into emails * * @return array{content: string, mimeType: string}|null null when unavailable - * @since 35.0.0 + * @since 34.0.4 */ public function getLogoImage(): ?array { return $this->defaults->getLogoImage(); diff --git a/lib/public/FilesMetadata/IFilesMetadataManager.php b/lib/public/FilesMetadata/IFilesMetadataManager.php index ce8a3e73e0ee6..6f94a29440b85 100644 --- a/lib/public/FilesMetadata/IFilesMetadataManager.php +++ b/lib/public/FilesMetadata/IFilesMetadataManager.php @@ -76,7 +76,7 @@ public function getMetadata(int $fileId, bool $generate = false): IFilesMetadata * * @return IFilesMetadata * @throws FilesMetadataNotFoundException if not found - * @since 36.0.0 + * @since 34.0.6 */ public function getMetadataForNode(Node $node, bool $generate = false): IFilesMetadata; diff --git a/lib/public/FilesMetadata/Model/IFilesMetadata.php b/lib/public/FilesMetadata/Model/IFilesMetadata.php index 556af8de72d16..79cd9cfe30be7 100644 --- a/lib/public/FilesMetadata/Model/IFilesMetadata.php +++ b/lib/public/FilesMetadata/Model/IFilesMetadata.php @@ -49,7 +49,7 @@ public function getFileId(): int; * returns the storage id linked to this metadata * * @return int - * @since 36.0.0 + * @since 34.0.6 */ public function getStorageId(): int; diff --git a/lib/public/IAddressBookWritable.php b/lib/public/IAddressBookWritable.php index 35f44f15a054f..7e9a19f5d4f3f 100644 --- a/lib/public/IAddressBookWritable.php +++ b/lib/public/IAddressBookWritable.php @@ -12,13 +12,13 @@ /** * IAddressBook Interface extension for checking if the address book is writable * - * @since 35.0.0 + * @since 34.0.5 */ interface IAddressBookWritable extends IAddressBook { /** * Indicates whether the address book can be modified * - * @since 35.0.0 + * @since 34.0.5 * * @return bool */ diff --git a/lib/public/IUserManager.php b/lib/public/IUserManager.php index 477ce10f2984d..161953c8eb058 100644 --- a/lib/public/IUserManager.php +++ b/lib/public/IUserManager.php @@ -290,7 +290,7 @@ public function getAvatarUrlDark(string $userId, int $size): string; * * @param \OCP\Federation\ICloudId $federatedUserId A cloud ID of the federated user * @return IUser - * @since 35.0.0 + * @since 34.0.3 */ public function getFederatedUser(\OCP\Federation\ICloudId $cloudId): IUser; } diff --git a/lib/public/OCM/Events/LocalOCMDiscoveryEvent.php b/lib/public/OCM/Events/LocalOCMDiscoveryEvent.php index 8c4b7cb38d8ac..13ec2dcf966bc 100644 --- a/lib/public/OCM/Events/LocalOCMDiscoveryEvent.php +++ b/lib/public/OCM/Events/LocalOCMDiscoveryEvent.php @@ -60,7 +60,7 @@ public function registerResourceType(string $name, array $shareTypes, array $pro * * @return IOCMProvider * - * @since 35.0.0 + * @since 34.0.4 */ public function getProvider(): IOCMProvider { return $this->provider; diff --git a/lib/public/OCM/Events/OCMNotificationReceivedEvent.php b/lib/public/OCM/Events/OCMNotificationReceivedEvent.php index 517ddf65b0922..e6224d3c7986e 100644 --- a/lib/public/OCM/Events/OCMNotificationReceivedEvent.php +++ b/lib/public/OCM/Events/OCMNotificationReceivedEvent.php @@ -13,12 +13,12 @@ use OCP\Federation\ICloudFederationNotification; /** - * @since 35.0.0 + * @since 34.0.1 */ -#[Listenable(since: '35.0.0')] +#[Listenable(since: '34.0.1')] class OCMNotificationReceivedEvent extends Event { /** - * @since 35.0.0 + * @since 34.0.1 */ public function __construct( private readonly ICloudFederationNotification $notification, @@ -27,7 +27,7 @@ public function __construct( } /** - * @since 35.0.0 + * @since 34.0.1 */ public function getNotification(): ICloudFederationNotification { return $this->notification; diff --git a/lib/public/Security/Ip/IRemoteAddress.php b/lib/public/Security/Ip/IRemoteAddress.php index 45c636dfba4ad..a5c7295a5f50a 100644 --- a/lib/public/Security/Ip/IRemoteAddress.php +++ b/lib/public/Security/Ip/IRemoteAddress.php @@ -22,7 +22,7 @@ public function allowsAdminActions(): bool; /** * Check if the current remote address is allowed to bypass the password confirmation. - * @since 35.0.0 + * @since 34.0.3 */ public function allowsBypassPasswordConfirmation(): bool; } diff --git a/lib/public/TaskProcessing/IManager.php b/lib/public/TaskProcessing/IManager.php index 9abdce4a252b1..9060dfb4ee40f 100644 --- a/lib/public/TaskProcessing/IManager.php +++ b/lib/public/TaskProcessing/IManager.php @@ -186,7 +186,7 @@ public function getNextScheduledTasks(array $taskTypeIds = [], array $taskIdsToI * @param list $taskTypeIds When non-empty, only tasks of these task type IDs are considered. * @return Task|null The claimed task (status RUNNING), or null if nothing could be claimed. * @throws Exception If the query failed - * @since 35.0.0 + * @since 34.0.1 */ public function claimNextScheduledTask(array $taskTypeIds = []): ?Task; @@ -284,7 +284,7 @@ public function setTaskStatus(Task $task, int $status): void; * @return int The count of matching tasks * @throws Exception If the query failed * @since 34.0.0 - * @since 36.0.0 - parameter $status became optional, parameters $scheduleAfter and $minPickupDelay were added + * @since 34.0.5 - parameter $status became optional, parameters $scheduleAfter and $minPickupDelay were added */ public function countTasks( ?int $status = null, array $taskTypeIds = [], ?int $scheduleAfter = null, ?int $minPickupDelay = null,