public function testStripUtilities()
 {
     // Variable stripping.
     $this->assertEqual('stuff', ArcanistXHPASTLintNamingHook::stripPHPVariable('stuff'));
     $this->assertEqual('stuff', ArcanistXHPASTLintNamingHook::stripPHPVariable('$stuff'));
     // Function/method stripping.
     $this->assertEqual('construct', ArcanistXHPASTLintNamingHook::stripPHPFunction('construct'));
     $this->assertEqual('construct', ArcanistXHPASTLintNamingHook::stripPHPFunction('__construct'));
 }
 protected function lintNamingConventions($root)
 {
     // We're going to build up a list of <type, name, token, error> tuples
     // and then try to instantiate a hook class which has the opportunity to
     // override us.
     $names = array();
     $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
     foreach ($classes as $class) {
         $name_token = $class->getChildByIndex(1);
         $name_string = $name_token->getConcreteString();
         $names[] = array('class', $name_string, $name_token, ArcanistXHPASTLintNamingHook::isUpperCamelCase($name_string) ? null : 'Follow naming conventions: classes should be named using ' . 'UpperCamelCase.');
     }
     $ifaces = $root->selectDescendantsOfType('n_INTERFACE_DECLARATION');
     foreach ($ifaces as $iface) {
         $name_token = $iface->getChildByIndex(1);
         $name_string = $name_token->getConcreteString();
         $names[] = array('interface', $name_string, $name_token, ArcanistXHPASTLintNamingHook::isUpperCamelCase($name_string) ? null : 'Follow naming conventions: interfaces should be named using ' . 'UpperCamelCase.');
     }
     $functions = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION');
     foreach ($functions as $function) {
         $name_token = $function->getChildByIndex(2);
         if ($name_token->getTypeName() == 'n_EMPTY') {
             // Unnamed closure.
             continue;
         }
         $name_string = $name_token->getConcreteString();
         $names[] = array('function', $name_string, $name_token, ArcanistXHPASTLintNamingHook::isLowercaseWithUnderscores(ArcanistXHPASTLintNamingHook::stripPHPFunction($name_string)) ? null : 'Follow naming conventions: functions should be named using ' . 'lowercase_with_underscores.');
     }
     $methods = $root->selectDescendantsOfType('n_METHOD_DECLARATION');
     foreach ($methods as $method) {
         $name_token = $method->getChildByIndex(2);
         $name_string = $name_token->getConcreteString();
         $names[] = array('method', $name_string, $name_token, ArcanistXHPASTLintNamingHook::isLowerCamelCase(ArcanistXHPASTLintNamingHook::stripPHPFunction($name_string)) ? null : 'Follow naming conventions: methods should be named using ' . 'lowerCamelCase.');
     }
     $param_tokens = array();
     $params = $root->selectDescendantsOfType('n_DECLARATION_PARAMETER_LIST');
     foreach ($params as $param_list) {
         foreach ($param_list->getChildren() as $param) {
             $name_token = $param->getChildByIndex(1);
             if ($name_token->getTypeName() == 'n_VARIABLE_REFERENCE') {
                 $name_token = $name_token->getChildOfType(0, 'n_VARIABLE');
             }
             $param_tokens[$name_token->getID()] = true;
             $name_string = $name_token->getConcreteString();
             $names[] = array('parameter', $name_string, $name_token, ArcanistXHPASTLintNamingHook::isLowercaseWithUnderscores(ArcanistXHPASTLintNamingHook::stripPHPVariable($name_string)) ? null : 'Follow naming conventions: parameters should be named using ' . 'lowercase_with_underscores.');
         }
     }
     $constants = $root->selectDescendantsOfType('n_CLASS_CONSTANT_DECLARATION_LIST');
     foreach ($constants as $constant_list) {
         foreach ($constant_list->getChildren() as $constant) {
             $name_token = $constant->getChildByIndex(0);
             $name_string = $name_token->getConcreteString();
             $names[] = array('constant', $name_string, $name_token, ArcanistXHPASTLintNamingHook::isUppercaseWithUnderscores($name_string) ? null : 'Follow naming conventions: class constants should be named ' . 'using UPPERCASE_WITH_UNDERSCORES.');
         }
     }
     $member_tokens = array();
     $props = $root->selectDescendantsOfType('n_CLASS_MEMBER_DECLARATION_LIST');
     foreach ($props as $prop_list) {
         foreach ($prop_list->getChildren() as $token_id => $prop) {
             if ($prop->getTypeName() == 'n_CLASS_MEMBER_MODIFIER_LIST') {
                 continue;
             }
             $name_token = $prop->getChildByIndex(0);
             $member_tokens[$name_token->getID()] = true;
             $name_string = $name_token->getConcreteString();
             $names[] = array('member', $name_string, $name_token, ArcanistXHPASTLintNamingHook::isLowerCamelCase(ArcanistXHPASTLintNamingHook::stripPHPVariable($name_string)) ? null : 'Follow naming conventions: class properties should be named ' . 'using lowerCamelCase.');
         }
     }
     $superglobal_map = array_fill_keys($this->getSuperGlobalNames(), true);
     $fdefs = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION');
     $mdefs = $root->selectDescendantsOfType('n_METHOD_DECLARATION');
     $defs = $fdefs->add($mdefs);
     foreach ($defs as $def) {
         $globals = $def->selectDescendantsOfType('n_GLOBAL_DECLARATION_LIST');
         $globals = $globals->selectDescendantsOfType('n_VARIABLE');
         $globals_map = array();
         foreach ($globals as $global) {
             $global_string = $global->getConcreteString();
             $globals_map[$global_string] = true;
             $names[] = array('global', $global_string, $global, null);
         }
         // Exclude access of static properties, since lint will be raised at
         // their declaration if they're invalid and they may not conform to
         // variable rules. This is slightly overbroad (includes the entire
         // rhs of a "Class::..." token) to cover cases like "Class:$x[0]". These
         // varaibles are simply made exempt from naming conventions.
         $exclude_tokens = array();
         $statics = $def->selectDescendantsOfType('n_CLASS_STATIC_ACCESS');
         foreach ($statics as $static) {
             $rhs = $static->getChildByIndex(1);
             $rhs_vars = $def->selectDescendantsOfType('n_VARIABLE');
             foreach ($rhs_vars as $var) {
                 $exclude_tokens[$var->getID()] = true;
             }
         }
         $vars = $def->selectDescendantsOfType('n_VARIABLE');
         foreach ($vars as $token_id => $var) {
             if (isset($member_tokens[$token_id])) {
                 continue;
             }
             if (isset($param_tokens[$token_id])) {
                 continue;
             }
             if (isset($exclude_tokens[$token_id])) {
                 continue;
             }
             $var_string = $var->getConcreteString();
             // Awkward artifact of "$o->{$x}".
             $var_string = trim($var_string, '{}');
             if (isset($superglobal_map[$var_string])) {
                 continue;
             }
             if (isset($globals_map[$var_string])) {
                 continue;
             }
             $names[] = array('variable', $var_string, $var, ArcanistXHPASTLintNamingHook::isLowercaseWithUnderscores(ArcanistXHPASTLintNamingHook::stripPHPVariable($var_string)) ? null : 'Follow naming conventions: variables should be named using ' . 'lowercase_with_underscores.');
         }
     }
     $engine = $this->getEngine();
     $working_copy = $engine->getWorkingCopy();
     if ($working_copy) {
         // If a naming hook is configured, give it a chance to override the
         // default results for all the symbol names.
         $hook_class = $working_copy->getConfig('lint.xhpast.naminghook');
         if ($hook_class) {
             $hook_obj = newv($hook_class, array());
             foreach ($names as $k => $name_attrs) {
                 list($type, $name, $token, $default) = $name_attrs;
                 $result = $hook_obj->lintSymbolName($type, $name, $default);
                 $names[$k][3] = $result;
             }
         }
     }
     // Raise anything we're left with.
     foreach ($names as $k => $name_attrs) {
         list($type, $name, $token, $result) = $name_attrs;
         if ($result) {
             $this->raiseLintAtNode($token, self::LINT_NAMING_CONVENTIONS, $result);
         }
     }
 }