Using @return never documentation on always-throw-function

This helps phan to detect unreachable code and also impossible types
after the functions.
It helps phan to avoid false positives for array keys
when the keys are checked before

Bug: T240141
Change-Id: I895f70e82b3053a46cd44135b15437e6f82a07b2
This commit is contained in:
Umherirrender 2021-09-04 03:42:33 +02:00
parent 07b499fbcf
commit 44fd53fee3
42 changed files with 86 additions and 12 deletions

View file

@ -130,9 +130,6 @@ $cfg['globals_type_map'] = array_merge( $cfg['globals_type_map'], [
'wgExtraNamespaces' => 'string[]',
] );
// TODO Use @return never annotations - T240141
$cfg['plugins'] = array_diff( $cfg['plugins'], [ 'AddNeverReturnTypePlugin' ] );
// Include a local config file if it exists
if ( file_exists( __DIR__ . '/local-config.php' ) ) {
require __DIR__ . '/local-config.php';

View file

@ -61,6 +61,7 @@ class HeaderParserBase {
*
* @param string $message
* @throws HeaderParserError
* @return never
*/
protected function error( $message ) {
throw new HeaderParserError( "$message at {$this->pos}" );

View file

@ -70,9 +70,15 @@ class SlotRecord {
public static function newWithSuppressedContent( SlotRecord $slot ) {
$row = $slot->row;
return new SlotRecord( $row, static function () {
throw new SuppressedDataException( 'Content suppressed!' );
} );
return new SlotRecord(
$row,
/**
* @return never
*/
static function () {
throw new SuppressedDataException( 'Content suppressed!' );
}
);
}
/**

View file

@ -51,6 +51,9 @@ if ( $IP === false ) {
$IP = dirname( __DIR__ );
}
/**
* @return never
*/
function wfWebStartNoLocalSettings() {
# LocalSettings.php is the per-site customization file. If it does not exist
# the wiki installer needs to be launched or the generated file uploaded to

View file

@ -1374,6 +1374,7 @@ abstract class ApiBase extends ContextSource {
* @param array|null $data See ApiErrorFormatter::addError()
* @param int|null $httpCode HTTP error code to use
* @throws ApiUsageException always
* @return never
*/
public function dieWithError( $msg, $code = null, $data = null, $httpCode = null ) {
throw ApiUsageException::newWithMessage( $this, $msg, $code, $data, $httpCode );
@ -1432,6 +1433,7 @@ abstract class ApiBase extends ContextSource {
* @since 1.29 Accepts a StatusValue
* @param StatusValue $status
* @throws ApiUsageException always
* @return never
*/
public function dieStatus( StatusValue $status ) {
if ( $status->isGood() ) {
@ -1569,6 +1571,7 @@ abstract class ApiBase extends ContextSource {
* @param string $method Method or function name
* @param string $message Error message
* @throws MWException always
* @return never
*/
protected static function dieDebug( $method, $message ) {
throw new MWException( "Internal error in $method: $message" );

View file

@ -332,6 +332,7 @@ class ApiParamValidator {
* @param ApiBase $module
* @param ValidationException $ex
* @throws ApiUsageException always
* @return never
*/
private function convertValidationException( ApiBase $module, ValidationException $ex ) {
$mv = $ex->getFailureMessage();

View file

@ -40,6 +40,7 @@ abstract class AbstractPrimaryAuthenticationProvider extends AbstractAuthenticat
* @return AuthenticationResponse|void
*/
public function continuePrimaryAuthentication( array $reqs ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new \BadMethodCallException( __METHOD__ . ' is not implemented.' );
}
@ -109,6 +110,7 @@ abstract class AbstractPrimaryAuthenticationProvider extends AbstractAuthenticat
* @stable to override
*/
public function continuePrimaryAccountCreation( $user, $creator, array $reqs ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new \BadMethodCallException( __METHOD__ . ' is not implemented.' );
}
@ -147,6 +149,7 @@ abstract class AbstractPrimaryAuthenticationProvider extends AbstractAuthenticat
* @stable to override
*/
public function beginPrimaryAccountLink( $user, array $reqs ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
if ( $this->accountCreationType() === self::TYPE_LINK ) {
throw new \BadMethodCallException( __METHOD__ . ' is not implemented.' );
} else {
@ -161,6 +164,7 @@ abstract class AbstractPrimaryAuthenticationProvider extends AbstractAuthenticat
* @stable to override
*/
public function continuePrimaryAccountLink( $user, array $reqs ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new \BadMethodCallException( __METHOD__ . ' is not implemented.' );
}

View file

@ -42,7 +42,10 @@ use MediaWiki\Page\PageReference;
*/
abstract class CacheKeyHelper {
/** Private constructor to defy instantiation. */
/**
* Private constructor to defy instantiation.
* @return never
*/
private function __construct() {
// we should never even get here...
throw new LogicException( 'Should not instantiate ' . __CLASS__ );

View file

@ -350,6 +350,7 @@ class WikitextContent extends TextContent {
* @throws MWException
*/
protected function getHtml() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException(
"getHtml() not implemented for wikitext. "
. "Use getParserOutput()->getText()."

View file

@ -259,6 +259,7 @@ abstract class MWLBFactory {
/**
* @param string $prefix Table prefix
* @param string $dbType Database type
* @return never
*/
private static function reportIfPrefixSet( $prefix, $dbType ) {
$e = new UnexpectedValueException(
@ -272,6 +273,7 @@ abstract class MWLBFactory {
/**
* @param string $srvDB Server config database
* @param string $ldDB Local DB domain database
* @return never
*/
private static function reportMismatchedDBs( $srvDB, $ldDB ) {
$e = new UnexpectedValueException(
@ -289,6 +291,7 @@ abstract class MWLBFactory {
/**
* @param string $srvTP Server config table prefix
* @param string $ldTP Local DB domain database
* @return never
*/
private static function reportMismatchedPrefixes( $srvTP, $ldTP ) {
$e = new UnexpectedValueException(

View file

@ -162,6 +162,7 @@ class TextSlotDiffRenderer extends SlotDiffRenderer {
/**
* @param Status $status
* @throws FatalError
* @return never
*/
$error = static function ( $status ) {
throw new FatalError( $status->getWikiText() );

View file

@ -159,6 +159,9 @@ class MWExceptionHandler {
// Make sure we don't claim success on exit for CLI scripts (T177414)
if ( wfIsCLI() ) {
register_shutdown_function(
/**
* @return never
*/
static function () {
exit( 255 );
}

View file

@ -35,6 +35,7 @@ class ExternalStoreHttp extends ExternalStoreMedium {
}
public function store( $location, $data ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( "ExternalStoreHttp is read-only and does not support store()." );
}

View file

@ -1968,7 +1968,7 @@ class FileRepo {
* Throw an exception if this repo is read-only by design.
* This does not and should not check getReadOnlyReason().
*
* @return void
* @return void|never
* @throws MWException
*/
protected function assertWritableRepo() {

View file

@ -594,6 +594,7 @@ class ForeignAPIRepo extends FileRepo {
* @throws MWException
*/
public function enumFiles( $callback ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( 'enumFiles is not supported by ' . static::class );
}

View file

@ -4,6 +4,7 @@
*/
class TempFileRepo extends FileRepo {
public function getTempRepo() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( "Cannot get a temp repo from a temp repo." );
}
}

View file

@ -1962,6 +1962,7 @@ abstract class File implements IDBAccessObject, MediaHandlerState {
/**
* @throws MWException
* @return never
*/
protected function readOnlyError() {
throw new MWException( static::class . ': write operations are not supported' );

View file

@ -25,7 +25,6 @@ use MediaWiki\MediaWikiServices;
use MediaWiki\User\UserIdentity;
use Wikimedia\Rdbms\DBUnexpectedError;
// @phan-file-suppress PhanTypeMissingReturn false positives
/**
* Foreign file with an accessible MediaWiki database
*

View file

@ -149,6 +149,7 @@ class HTMLMultiSelectField extends HTMLFormField implements HTMLNestedFilterable
* @throws MWException
*/
public function getOptionsOOUI() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
// Sections make this difficult. See getInputOOUI().
throw new MWException( 'HTMLMultiSelectField#getOptionsOOUI() is not supported' );
}

View file

@ -32,6 +32,7 @@ class MwHttpRequestToResponseInterfaceAdapter implements ResponseInterface {
}
public function getProtocolVersion(): void {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
// This is not accessible via MWHttpRequest, but it is set in its protected `respVersion` property.
// If this is ever needed, it can get exposed in MWHttpRequest.
throw new LogicException( __METHOD__ . ' is not implemented' );
@ -91,6 +92,10 @@ class MwHttpRequestToResponseInterfaceAdapter implements ResponseInterface {
return ''; // not exposed through MWHttpRequest, unlikely to ever be useful
}
/**
* @param string $method
* @return never
*/
private function throwExceptionForBuilderMethod( string $method ): void {
throw new LogicException( "Builder method $method is not supported." );
}

View file

@ -11,6 +11,7 @@ trait NonSerializableTrait {
/**
* @throws LogicException always
* @return never
*/
public function __sleep() {
throw new LogicException( 'Instances of ' . get_class( $this ) . ' are not serializable!' );

View file

@ -143,6 +143,7 @@ class UploadedFileStream implements StreamInterface {
}
public function write( $string ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
$this->checkOpen();
throw new RuntimeException( 'Stream is read-only' );
}

View file

@ -284,10 +284,12 @@ abstract class QuorumLockManager extends LockManager {
abstract protected function releaseAllLocks();
final protected function doLock( array $paths, $type ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
}
final protected function doUnlock( array $paths, $type ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
}
}

View file

@ -233,6 +233,11 @@ class MSCompoundFileReader {
return $this->readOffset( $this->sectorOffset( $sectorId ), 1 << $this->header['sector_shift'] );
}
/**
* @param string $message
* @param int $code
* @return never
*/
private function error( $message, $code ) {
throw new RuntimeException( $message, $code );
}

View file

@ -144,6 +144,7 @@ class DBConnRef implements IDatabase {
}
public function setLBInfo( $nameOrArray, $value = null ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
// Disallow things that might confuse the LoadBalancer tracking
throw $this->getDomainChangeException();
}
@ -284,6 +285,7 @@ class DBConnRef implements IDatabase {
}
public function close( $fname = __METHOD__, $owner = null ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new DBUnexpectedError( $this->conn, 'Cannot close shared connection.' );
}
@ -450,11 +452,13 @@ class DBConnRef implements IDatabase {
}
public function selectDB( $db ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
// Disallow things that might confuse the LoadBalancer tracking
throw $this->getDomainChangeException();
}
public function selectDomain( $domain ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
// Disallow things that might confuse the LoadBalancer tracking
throw $this->getDomainChangeException();
}

View file

@ -6034,6 +6034,7 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware
* Called by serialize. Throw an exception when DB connection is serialized.
* This causes problems on some database engines because the connection is
* not restored on unserialize.
* @return never
*/
public function __sleep() {
throw new RuntimeException( 'Database serialization may cause problems, since ' .

View file

@ -46,6 +46,7 @@ class FakeResultWrapper extends ResultWrapper {
}
protected function doGetFieldNames() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new RuntimeException( __METHOD__ . ' is unimplemented' );
}
}

View file

@ -70,6 +70,7 @@ class LBFactorySingle extends LBFactory {
}
public function newMainLB( $domain = false, $owner = null ): ILoadBalancer {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new BadMethodCallException( "Method is not supported." );
}
@ -78,10 +79,12 @@ class LBFactorySingle extends LBFactory {
}
public function newExternalLB( $cluster, $owner = null ): ILoadBalancer {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new BadMethodCallException( "Method is not supported." );
}
public function getExternalLB( $cluster ): ILoadBalancer {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new BadMethodCallException( "Method is not supported." );
}

View file

@ -1494,6 +1494,7 @@ class LoadBalancer implements ILoadBalancer {
/**
* @throws DBConnectionError
* @return never
*/
private function reportConnectionError() {
$conn = $this->errorConnection; // the connection which caused the error

View file

@ -37,11 +37,13 @@ use Wikimedia\Rdbms\IDatabase;
class RCDatabaseLogEntry extends DatabaseLogEntry {
public static function newFromId( $id, IDatabase $db ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
// Make the LSP violation explicit to prevent sneaky failures
throw new LogicException( 'Not implemented!' );
}
public static function getSelectQueryData() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
// Make the LSP violation explicit to prevent sneaky failures
throw new LogicException( 'Not implemented!' );
}

View file

@ -68,14 +68,17 @@ class PPNode_Hash_Array implements PPNode {
}
public function splitArg() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
public function splitExt() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
public function splitHeading() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
}

View file

@ -85,14 +85,17 @@ class PPNode_Hash_Attr implements PPNode {
}
public function splitArg() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
public function splitExt() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
public function splitHeading() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
}

View file

@ -81,14 +81,17 @@ class PPNode_Hash_Text implements PPNode {
}
public function splitArg() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
public function splitExt() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
public function splitHeading() {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new MWException( __METHOD__ . ': not supported' );
}
}

View file

@ -218,6 +218,7 @@ abstract class QueryPage extends SpecialPage {
* This is strongly deprecated; getQueryInfo() should be overridden instead.
* @throws MWException
* @return string
* @suppress PhanPluginNeverReturnMethod
*/
protected function getSQL() {
/* Implement getQueryInfo() instead */

View file

@ -341,6 +341,7 @@ class SpecialPage implements MessageLocalizer {
* Output an error message telling the user what access level they have to have
* @stable to override
* @throws PermissionsError
* @return never
*/
protected function displayRestrictionError() {
throw new PermissionsError( $this->mRestriction );
@ -351,7 +352,7 @@ class SpecialPage implements MessageLocalizer {
*
* @stable to override
* @since 1.19
* @return void
* @return void|never
* @throws PermissionsError
*/
public function checkPermissions() {
@ -364,7 +365,7 @@ class SpecialPage implements MessageLocalizer {
* If the wiki is currently in readonly mode, throws a ReadOnlyError
*
* @since 1.19
* @return void
* @return void|never
* @throws ReadOnlyError
*/
public function checkReadOnly() {

View file

@ -378,6 +378,7 @@ class SpecialMediaStatistics extends QueryPage {
* @param stdClass $result Result row
* @return bool|string|void
* @throws MWException
* @suppress PhanPluginNeverReturnMethod
*/
public function formatResult( $skin, $result ) {
throw new MWException( "unimplemented" );

View file

@ -73,6 +73,7 @@ class RemexMungerData {
public $nonblankNodeCount = 0;
public function __set( $name, $value ) {
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
throw new \Exception( "Cannot set property \"$name\"" );
}

View file

@ -185,6 +185,7 @@ class ZipDirectoryReader {
* @param mixed $code
* @param string $debugMessage
* @throws ZipDirectoryReaderError
* @return never
*/
private function error( $code, $debugMessage ) {
wfDebug( __CLASS__ . ": Fatal error: $debugMessage" );

View file

@ -27,6 +27,7 @@ use Wikimedia\Rdbms\DBReadOnlyError;
/**
* @internal
* @since 1.31
* @phan-file-suppress PhanPluginNeverReturnMethod
*/
class NoWriteWatchedItemStore implements WatchedItemStoreInterface {

View file

@ -21,7 +21,6 @@ class BenchmarkCommentFormatter extends Benchmarker {
if ( !isset( $result['query']['recentchanges'] ) ) {
$this->fatalError( "Invalid JSON data" );
}
// @phan-suppress-next-line PhanTypeArraySuspiciousNullable T240141
$entries = $result['query']['recentchanges'];
$inputs = [];
foreach ( $entries as $entry ) {

View file

@ -484,6 +484,7 @@ abstract class Maintenance {
* @param string $msg Error message
* @param int $exitCode PHP exit status. Should be in range 1-254.
* @since 1.31
* @return never
*/
protected function fatalError( $msg, $exitCode = 1 ) {
$this->error( $msg );

View file

@ -54,6 +54,9 @@ class PPFuzzTester {
*/
private static $currentTest = false;
/**
* @return void|never
*/
public function execute() {
if ( !file_exists( 'results' ) ) {
mkdir( 'results' );