Skip to content
This repository was archived by the owner on Jun 1, 2024. It is now read-only.

Add aliasses for noreturn functions with nothing #109

Merged
merged 4 commits into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 48 additions & 8 deletions src/Framework/HackTest.hack
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,8 @@ class HackTest {
(function(\ReflectionMethod): bool) $method_filter,
(function(ProgressEvent): Awaitable<void>) $progress_callback,
): Awaitable<void> {
$progress = new _Private\Progress(
$progress_callback,
$this->filename,
static::class,
);
$progress =
new _Private\Progress($progress_callback, $this->filename, static::class);

await static::beforeFirstTestAsync();
await using new _Private\OnScopeExitAsync(
Expand Down Expand Up @@ -262,14 +259,58 @@ class HackTest {
}
}

/**
* Preferred over markTestSkipped(), because the typechecker considers
* code past markTestSkipped() to be unreachable. This triggers inference
* bugs when dealing with coeffects @see tests/ShowCoeffectViolationTest.php.
* If you ever want to make your test not be skipped anymore, the code will
* be reachable again, which may reveal previously hidden errors introduced
* by code changes made whilst the test was skipped.
*
* You can use `return static::markAsSkipped('skipping');` to match the old
* `static::markTestSkipped('skipping')` behavior.
*/
public static final function markAsSkipped(string $message): nothing {
self::markTestSkipped($message);
}

/**
* Code below this invocation will become unreachable for the typechecker.
* This reduces the strength of typechecking and may raise baseless type errors.
* @see markAsSkipped()
*/
public static final function markTestSkipped(string $message): noreturn {
throw new SkippedTestException($message);
}

/**
* Preferred over markTestIncomplete(), @see markAsSkipped() for rationale.
*/
public static final function markAsIncomplete(string $message): nothing {
self::markTestIncomplete($message);
}

/**
* Code below this invocation will become unreachable for the typechecker.
* This reduces the strength of typechecking and may raise baseless type errors.
* @see markAsIncomplete()
*/
public static function markTestIncomplete(string $message): noreturn {
throw new SkippedTestException($message);
}

/**
* Preferred over fail(), @see markAsSkipped() for rationale.
*/
public static final function markAsFailed(string $message = ''): nothing {
self::fail($message);
}

/**
* Code below this invocation will become unreachable for the typechecker.
* This reduces the strength of typechecking and may raise baseless type errors.
* @see markAsFailed()
*/
public static final function fail(string $message = ''): noreturn {
throw new \RuntimeException($message);
}
Expand All @@ -281,9 +322,8 @@ class HackTest {
): void {
$this->expectedException = $exception;
$this->expectedExceptionMessage = $exception_message;
$this->expectedExceptionCode = static::computeExpectedExceptionCode(
$exception_code,
);
$this->expectedExceptionCode =
static::computeExpectedExceptionCode($exception_code);
}

private function clearExpectedException(): void {
Expand Down
55 changes: 55 additions & 0 deletions tests/dirty/ShowCoeffectViolationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?hh // strict
/*
* Copyright (c) 2004-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

use namespace Facebook\HackTest;
use function Facebook\FBExpect\expect;

final class ShowCoeffectViolationTest extends HackTest\HackTest {
/*
Typing[4323] A where type constraint is violated here [1]
-> This is the method with where type constraints [2]
-> Expected a function that requires the capability set {AccessGlobals, IO, ImplicitPolicyLocal, RxLocal, System, WriteProperty} [3]
-> But got a function that requires nothing [4]

tests/SomeTest.hack:7:23
5 | public function testShowCoeffectViolation(): void {
6 | self::markTestSkipped('This test is pointless');
[1] 7 | expect(() ==> 3)->toThrow(\Exception::class);
8 | }
9 | }

vendor/facebook/fbexpect/src/ExpectObj.hack:569:19
567 | * the awaitable will be awaited.
568 | *
[2] 569 | public function toThrow<TException as \Throwable, TRet>(
570 | classname<TException> $exception_class,
571 | ?string $expected_exception_message = null,
572 | ?string $msg = null,
573 | mixed ...$args
[3] 574 | ): TException where T = (function(): TRet) {
575 | $msg = \vsprintf($msg ?? '', $args);
576 | $exception = $this->tryCallReturnException($exception_class);

.:0:0
[4] 0 | No source found

1 error found.
*/
public function testShowCoeffectViolation(): void {
self::markTestSkipped('This test is pointless');
/* HH_FIXME[4323] The error shown above. */
expect(() ==> 3)->toThrow(\Exception::class);
}

public function testWithoutCoeffectViolation(): void {
self::markAsSkipped('This test is pointless');
expect(() ==> 3)->toThrow(\Exception::class);
}
}