Skip to content

Commit

Permalink
__Deprecated('...') user attribute results in runtime warning
Browse files Browse the repository at this point in the history
Summary: Depending on whether the function in question is in
 systemlib, E_DEPRECATED or E_USER_DEPRECATED is used. Subtleties:

 * For `__Memoize` functions, each call to the function results in the warning.
 * For `__Native` functions, the function body is changed to emit the warning
   and then emit `NativeImpl` (ereg is marked __Deprecated to test this).
 * For trait methods, the name of the using class appears in the warning, rather than that of the trait: if suboptimal, this is at least consistent.

Reviewed By: @jano

Differential Revision: D1853244
  • Loading branch information
Eugene Letuchy authored and hhvm-bot committed Feb 18, 2015
1 parent c2254e3 commit fd94fd1
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 2 deletions.
59 changes: 58 additions & 1 deletion hphp/compiler/analysis/emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6436,6 +6436,8 @@ void EmitterVisitor::bindUserAttributes(MethodStatementPtr meth,
}

const StaticString s_Void("HH\\void");
const char* attr_Deprecated = "__Deprecated";
const StaticString s_attr_Deprecated(attr_Deprecated);

void EmitterVisitor::bindNativeFunc(MethodStatementPtr meth,
FuncEmitter *fe) {
Expand Down Expand Up @@ -6546,6 +6548,11 @@ void EmitterVisitor::bindNativeFunc(MethodStatementPtr meth,
Label topOfBody(e);

Offset base = m_ue.bcPos();

if (meth->getFunctionScope()->userAttributes().count(attr_Deprecated)) {
emitDeprecationWarning(e, meth);
}

fe->setBuiltinFunc(bif, nif, attributes, base);
fillFuncEmitterParams(fe, meth->getParams(), true);
e.NativeImpl();
Expand Down Expand Up @@ -6739,6 +6746,49 @@ void EmitterVisitor::emitMethodPrologue(Emitter& e, MethodStatementPtr meth) {
}
}

const StaticString
s_trigger_error("trigger_error"),
s_is_deprecated("deprecated function");

void EmitterVisitor::emitDeprecationWarning(Emitter& e,
MethodStatementPtr meth) {
auto funcScope = meth->getFunctionScope();

auto userAttributes DEBUG_ONLY = funcScope->userAttributes();
assert(userAttributes.find(attr_Deprecated) != userAttributes.end());

// Include the message from <<__Deprecated('<message>')>> in the warning
auto deprArgs = funcScope->getUserAttributeStringParams(attr_Deprecated);
auto deprMessage = deprArgs.empty()
? s_is_deprecated.data()
: deprArgs.front();

{ // preface the message with the name of the offending function
auto funcName = funcScope->getOriginalName();
BlockScopeRawPtr b = funcScope->getOuterScope();
if (b && b->is(BlockScope::ClassScope)) {
ClassScopePtr clsScope = dynamic_pointer_cast<ClassScope>(b);
if (clsScope->isTrait()) {
e.Self();
e.NameA();
e.String(makeStaticString("::" + funcName + ": " + deprMessage));
e.Concat();
} else {
e.String(makeStaticString(
clsScope->getOriginalName() + "::" + funcName
+ ": " + deprMessage));
}
} else {
e.String(makeStaticString(funcName + ": " + deprMessage));
}
}

e.Int((funcScope->isSystem() || funcScope->isNative())
? k_E_DEPRECATED : k_E_USER_DEPRECATED);
e.FCallBuiltin(2, 2, s_trigger_error.get());
emitPop(e);
}

void EmitterVisitor::emitMethod(MethodStatementPtr meth) {
auto region = createRegion(meth, Region::Kind::FuncBody);
enterRegion(region);
Expand All @@ -6748,6 +6798,10 @@ void EmitterVisitor::emitMethod(MethodStatementPtr meth) {
Label topOfBody(e);
emitMethodPrologue(e, meth);

if (meth->getFunctionScope()->userAttributes().count(attr_Deprecated)) {
emitDeprecationWarning(e, meth);
}

// emit code to create generator object
if (m_curFunc->isGenerator) {
e.CreateCont();
Expand Down Expand Up @@ -7393,7 +7447,9 @@ Func* EmitterVisitor::canEmitBuiltinCall(const std::string& name,
f->isMethod() ||
(f->numParams() > Native::maxFCallBuiltinArgs()) ||
(numParams > f->numParams()) ||
f->hasVariadicCaptureParam()) return nullptr;
f->hasVariadicCaptureParam() ||
(f->userAttributes().count(
LowStringPtr(s_attr_Deprecated.get())))) return nullptr;

if ((f->returnType() == KindOfDouble) &&
!Native::allowFCallBuiltinDoubles()) return nullptr;
Expand Down Expand Up @@ -7547,6 +7603,7 @@ void EmitterVisitor::emitFuncCall(Emitter& e, FunctionCallPtr node,
bool byRef = fcallBuiltin->byRef(i);
emitBuiltinCallArg(e, (*params)[i], i, byRef);
}

if (fcallBuiltin->methInfo()) {
// IDL style
for (; i < fcallBuiltin->numParams(); i++) {
Expand Down
1 change: 1 addition & 0 deletions hphp/compiler/analysis/emitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ class EmitterVisitor {
ExpressionListPtr params,
bool coerce_params = false);
void emitMethodPrologue(Emitter& e, MethodStatementPtr meth);
void emitDeprecationWarning(Emitter& e, MethodStatementPtr meth);
void emitMethod(MethodStatementPtr meth);
void emitMemoizeProp(Emitter& e, MethodStatementPtr meth, Id localID,
const std::vector<Id>& paramIDs, uint numParams);
Expand Down
1 change: 1 addition & 0 deletions hphp/compiler/analysis/function_scope.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,7 @@ void FunctionScope::setOverriding(TypePtr returnType,

std::vector<std::string> FunctionScope::getUserAttributeStringParams(
const std::string& key) {

std::vector<std::string> ret;
auto native = m_userAttributes.find(key);
if (native == m_userAttributes.end()) {
Expand Down
2 changes: 1 addition & 1 deletion hphp/runtime/ext/pcre/ext_pcre.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ function ereg_replace(string $pattern,
* error occurred. If the optional parameter regs was not passed or the
* length of the matched string is 0, this function returns 1.
*/
<<__Native>>
<<__Native, __Deprecated('As of PHP 5.3.0, preg_match is suggested instead')>>
function ereg(string $pattern,
string $string,
?array &$regs = null): mixed;
Expand Down
2 changes: 2 additions & 0 deletions hphp/test/slow/ext_preg/ext_preg.php.expectf
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ bool(true)
bool(true)
bool(true)
bool(true)

Deprecated: ereg: As of PHP 5.3.0, preg_match is suggested instead in %s/test/slow/ext_preg/ext_preg.php on line 312
bool(true)
bool(true)
bool(true)
Expand Down
95 changes: 95 additions & 0 deletions hphp/test/slow/lang/deprecated.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?hh

function err_handler($errno, $errstr, $file, $line) {
if ($errno === E_DEPRECATED) {
echo 'E_DEPRECATED', ': ', $errstr, ' for ', $file, ':', $line, "\n";
return true;
} else if ($errno === E_USER_DEPRECATED) {
echo 'E_USER_DEPRECATED', ': ', $errstr, ' for ', $file, ':', $line, "\n";
return true;
}
throw new Exception($errstr);
}
error_reporting(-1);
set_error_handler('err_handler');

trait Tr {
<<__Deprecated('message')>>
public function meth() { echo __METHOD__, "\n"; }
<<__Deprecated('message')>>
public static function stMeth() { echo __METHOD__, "\n"; }
<<__Deprecated('message')>>
public static function __callStatic($name, $args) { echo __METHOD__, "\n"; }
<<__Deprecated('message')>>
public function __call($name, $args) { echo __METHOD__, "\n"; }
}

class C {
<<__Deprecated('message')>>
public function meth() { echo __METHOD__, "\n"; }
<<__Deprecated('message')>>
public static function stMeth() { echo __METHOD__, "\n"; }
<<__Deprecated('message')>>
public static function __callStatic($name, $args) { echo __METHOD__, "\n"; }
<<__Deprecated('message')>>
public function __call($name, $args) { echo __METHOD__, "\n"; }
}

class C_T {
use Tr;
}

<<__Deprecated('message')>>
function f() { echo __METHOD__, "\n"; }
<<__Deprecated('message')>>
function f_gen() { yield null; echo __METHOD__, "\n"; }
<<__Memoize, __Deprecated('message')>>
function fMem() {
echo __METHOD__, "\n";
}

function basic() {
echo '= ', __FUNCTION__, " =", "\n";
f();
C::stMeth();
C::viaCallStatic();
$inst = new C();
$inst->meth();
$inst->viaCall();
f_gen();
echo "\n";
}

function memoized() {
echo '= ', __FUNCTION__, " =", "\n";
fMem();
fMem();
echo "\n";
}

function builtin() {
echo '= ', __FUNCTION__, " =", "\n";
ereg('foobar', 'foo');
echo "\n";
}

function via_trait() {
echo '= ', __FUNCTION__, " =", "\n";
// f();
C_T::stMeth();
C_T::viaCallStatic();
$inst = new C_T();
$inst->meth();
$inst->viaCall();
echo "\n";
}

function main() {
basic();
via_trait();
memoized();
builtin();
echo 'Done', "\n";
}

main();
33 changes: 33 additions & 0 deletions hphp/test/slow/lang/deprecated.php.expectf
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
= basic =
E_USER_DEPRECATED: f: message for %s/lang/deprecated.php:43
f
E_USER_DEPRECATED: C::stMeth: message for %s/lang/deprecated.php:31
C::stMeth
E_USER_DEPRECATED: C::__callStatic: message for %s/lang/deprecated.php:33
C::__callStatic
E_USER_DEPRECATED: C::meth: message for %s/lang/deprecated.php:29
C::meth
E_USER_DEPRECATED: C::__call: message for %s/lang/deprecated.php:35
C::__call
E_USER_DEPRECATED: f_gen: message for %s/lang/deprecated.php:45

= via_trait =
E_USER_DEPRECATED: C_T::stMeth: message for %s/lang/deprecated.php:20
Tr::stMeth
E_USER_DEPRECATED: C_T::__callStatic: message for %s/lang/deprecated.php:22
Tr::__callStatic
E_USER_DEPRECATED: C_T::meth: message for %s/lang/deprecated.php:18
Tr::meth
E_USER_DEPRECATED: C_T::__call: message for %s/lang/deprecated.php:24
Tr::__call

= memoized =
E_USER_DEPRECATED: fMem: message for %s/lang/deprecated.php:49
fMem
E_USER_DEPRECATED: fMem: message for %s/lang/deprecated.php:49
fMem

= builtin =
E_DEPRECATED: ereg: As of PHP 5.3.0, preg_match is suggested instead for %s/lang/deprecated.php:72

Done

0 comments on commit fd94fd1

Please sign in to comment.