From b652d7a4c52e38694d95dca3d857779288ee471f Mon Sep 17 00:00:00 2001 From: Mark Williams Date: Tue, 25 Jun 2019 07:30:27 -0700 Subject: [PATCH] Fix AttrNoOverride* on classes Summary: We weren't checking for uniqueness before setting the magic method no-override flags, which could in theory result in setting them incorrectly. eg if there's a single class B which extends A, with two definitions of A, and a class C which defines a magic getter, and extends B, but which is incompatible with one of the As, we'll incorrectly set the no-override bit on B because *one* possible instantiation of B can't have a magic getter. I couldn't come up with code that behaves incorrectly, but foo; } <<__EntryPoint>> function main() { if (false) { include "bug.inc"; } var_dump(f(new C)); } with bug.inc: closureMap; for (auto& c : unit.classes) { + auto const attrsToRemove = + AttrNoOverride | + AttrNoOverrideMagicGet | + AttrNoOverrideMagicSet | + AttrNoOverrideMagicIsset | + AttrNoOverrideMagicUnset; + attribute_setter(c->attrs, false, attrsToRemove); + if (c->attrs & AttrEnum) { index.enums.insert({c->name, c.get()}); } @@ -2946,14 +2954,13 @@ void find_mocked_classes(IndexData& index) { void mark_no_override_classes(IndexData& index) { for (auto& cinfo : index.allClassInfos) { - auto const set = [&] { - if (!(cinfo->cls->attrs & AttrUnique) || - cinfo->cls->attrs & AttrInterface) { - return false; - } - return cinfo->subclassList.size() == 1; - }(); - attribute_setter(cinfo->cls->attrs, set, AttrNoOverride); + // We cleared all the NoOverride flags while building the + // index. Set them as necessary. + if (!(cinfo->cls->attrs & AttrUnique)) continue; + if (!(cinfo->cls->attrs & AttrInterface) && + cinfo->subclassList.size() == 1) { + attribute_setter(cinfo->cls->attrs, true, AttrNoOverride); + } for (auto& kv : magicMethodMap) { if (kv.second.attrBit == AttrNone) continue; -- 2.11.4.GIT