diff --git a/scripts/symbols/import_project_symbols.php b/scripts/symbols/import_project_symbols.php
index 8aef684a40..4ff2a3f2cd 100755
--- a/scripts/symbols/import_project_symbols.php
+++ b/scripts/symbols/import_project_symbols.php
@@ -1,180 +1,172 @@
 #!/usr/bin/env php
 <?php
 
 /*
  * Copyright 2012 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 $root = dirname(dirname(dirname(__FILE__)));
 require_once $root.'/scripts/__init_script__.php';
 
 $args = new PhutilArgumentParser($argv);
 $args->setSynopsis(<<<EOSYNOPSIS
 **import_project_symbols.php** [__options__] __project_name__ < symbols
 
   Import project symbols (symbols are read from stdin).
 EOSYNOPSIS
   );
 $args->parseStandardArguments();
 $args->parse(
   array(
-    array(
-      'name'      => 'ignore-duplicates',
-      'help'      => 'Ignore duplicate symbols, choosing one at random. By '.
-                     'default, this script throws if given duplicate '.
-                     'symbols.',
-    ),
     array(
       'name'      => 'more',
       'wildcard'  => true,
     ),
   ));
 
-$ignore_duplicates = $args->getArg('ignore-duplicates');
 $more = $args->getArg('more');
 if (count($more) !== 1) {
   $args->printHelpAndExit();
 }
 
 $project_name = head($more);
 $project = id(new PhabricatorRepositoryArcanistProject())->loadOneWhere(
   'name = %s',
   $project_name);
 
 if (!$project) {
   // TODO: Provide a less silly way to do this explicitly, or just do it right
   // here.
   echo "Project '{$project_name}' is unknown. Upload a diff to implicitly ".
        "create it.\n";
   exit(1);
 }
 
 echo "Parsing input from stdin...\n";
 $input = file_get_contents('php://stdin');
 $input = trim($input);
 $input = explode("\n", $input);
 
-$map = array();
 $symbols = array();
 foreach ($input as $key => $line) {
   $line_no = $key + 1;
   $matches = null;
-  $ok = preg_match('/^([^ ]+) ([^ ]+) ([^ ]+) (\d+) (.*)$/', $line, $matches);
+  $ok = preg_match(
+    '/^((?P<context>[^ ]+)? )?(?P<name>[^ ]+) (?P<type>[^ ]+) '.
+    '(?P<lang>[^ ]+) (?P<line>\d+) (?P<path>.*)$/',
+    $line,
+    $matches);
   if (!$ok) {
     throw new Exception(
-      "Line #{$line_no} of input is invalid. Expected five space-delimited ".
-      "fields: symbol name, symbol type, symbol language, line number, path. ".
+      "Line #{$line_no} of input is invalid. Expected five or six ".
+      "space-delimited fields: maybe symbol context, symbol name, symbol ".
+      "type, symbol language, line number, path. ".
       "For example:\n\n".
       "idx function php 13 /path/to/some/file.php\n\n".
       "Actual line was:\n\n".
       "{$line}");
   }
-  list($all, $name, $type, $lang, $line_number, $path) = $matches;
-
-  if (isset($map[$name][$type][$lang])) {
-    if ($ignore_duplicates) {
-      echo "Ignoring duplicate definition of '{$name}' on line {$line_no}.\n";
-    } else {
-      $previous = $map[$name][$type][$lang] + 1;
-      throw new Exception(
-        "Line #{$line_no} of input is invalid. It specifies a duplicate ".
-        "symbol (same name, language, and type) which has already been ".
-        "defined elsewhere. You must preprocess the symbol list to remove ".
-        "duplicates and choose exactly one master definition for each ".
-        "symbol, or specify --ignore-duplicates. This symbol was previously ".
-        "defined on line #{$previous}.\n\n".
-        "Line #{$line_no}:\n".
-        $line."\n\n".
-        "Line #{$previous}:\n".
-        $input[$previous - 1]);
-    }
-  } else {
-    $map[$name][$type][$lang] = $key;
+  if (empty($matches['context'])) {
+    $matches['context'] = '';
+  }
+  $context     = $matches['context'];
+  $name        = $matches['name'];
+  $type        = $matches['type'];
+  $lang        = $matches['lang'];
+  $line_number = $matches['line'];
+  $path        = $matches['path'];
+
+  if (strlen($context) > 128) {
+    throw new Exception(
+      "Symbol context '{$context}' defined on line #{$line_no} is too long, ".
+      "maximum symbol context length is 128 characters.");
   }
 
   if (strlen($name) > 128) {
     throw new Exception(
       "Symbol name '{$name}' defined on line #{$line_no} is too long, maximum ".
       "symbol name length is 128 characters.");
   }
 
   if (strlen($type) > 12) {
     throw new Exception(
       "Symbol type '{$type}' defined on line #{$line_no} is too long, maximum ".
       "symbol type length is 12 characters.");
   }
 
   if (strlen($lang) > 32) {
     throw new Exception(
       "Symbol language '{$lang}' defined on line #{$line_no} is too long, ".
       "maximum symbol language length is 32 characters.");
   }
 
   if (!strlen($path) || $path[0] != 0) {
     throw new Exception(
       "Path '{$path}' defined on line #{$line_no} is invalid. Paths should be ".
       "begin with '/' and specify a path from the root of the project, like ".
       "'/src/utils/utils.php'.");
   }
 
   $symbols[] = array(
+    'ctxt' => $context,
     'name' => $name,
     'type' => $type,
     'lang' => $lang,
     'line' => $line_number,
     'path' => $path,
   );
 }
 
 echo "Looking up path IDs...\n";
 $path_map = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(
   ipull($symbols, 'path'));
 
 $symbol = new PhabricatorRepositorySymbol();
 $conn_w = $symbol->establishConnection('w');
 
 echo "Preparing queries...\n";
 $sql = array();
 foreach ($symbols as $dict) {
   $sql[] = qsprintf(
     $conn_w,
-    '(%d, %s, %s, %s, %d, %d)',
+    '(%d, %s, %s, %s, %s, %d, %d)',
     $project->getID(),
+    $dict['ctxt'],
     $dict['name'],
     $dict['type'],
     $dict['lang'],
     $dict['line'],
     $path_map[$dict['path']]);
 }
 
 echo "Purging old symbols...\n";
 queryfx(
   $conn_w,
   'DELETE FROM %T WHERE arcanistProjectID = %d',
   $symbol->getTableName(),
   $project->getID());
 
 echo "Loading ".number_format(count($sql))." symbols...\n";
 foreach (array_chunk($sql, 128) as $chunk) {
   queryfx(
     $conn_w,
     'INSERT INTO %T
-      (arcanistProjectID, symbolName, symbolType, symbolLanguage, lineNumber,
-        pathID) VALUES %Q',
+      (arcanistProjectID, symbolContext, symbolName, symbolType,
+        symbolLanguage, lineNumber, pathID) VALUES %Q',
     $symbol->getTableName(),
     implode(', ', $chunk));
 }
 
 echo "Done.\n";
diff --git a/src/applications/diffusion/controller/DiffusionSymbolController.php b/src/applications/diffusion/controller/DiffusionSymbolController.php
index 42caa0aea6..74de67ead0 100644
--- a/src/applications/diffusion/controller/DiffusionSymbolController.php
+++ b/src/applications/diffusion/controller/DiffusionSymbolController.php
@@ -1,165 +1,172 @@
 <?php
 
 /*
  * Copyright 2012 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 final class DiffusionSymbolController extends DiffusionController {
 
   private $name;
 
   public function willProcessRequest(array $data) {
     $this->name = $data['name'];
   }
 
   public function processRequest() {
     $request = $this->getRequest();
     $user = $request->getUser();
 
     $query = new DiffusionSymbolQuery();
     $query->setName($this->name);
 
+    if ($request->getStr('context') !== null) {
+      $query->setContext($request->getStr('context'));
+    }
+
     if ($request->getStr('type')) {
       $query->setType($request->getStr('type'));
     }
 
     if ($request->getStr('lang')) {
       $query->setLanguage($request->getStr('lang'));
     }
 
     if ($request->getStr('projects')) {
       $phids = $request->getStr('projects');
       $phids = explode(',', $phids);
       $phids = array_filter($phids);
 
       if ($phids) {
         $projects = id(new PhabricatorRepositoryArcanistProject())
           ->loadAllWhere(
             'phid IN (%Ls)',
             $phids);
         $projects = mpull($projects, 'getID');
         if ($projects) {
           $query->setProjectIDs($projects);
         }
       }
     }
 
     $query->needPaths(true);
     $query->needArcanistProjects(true);
     $query->needRepositories(true);
 
     $symbols = $query->execute();
 
     // For PHP builtins, jump to php.net documentation.
     if ($request->getBool('jump') && count($symbols) == 0) {
       if ($request->getStr('lang') == 'php') {
         switch ($request->getStr('type')) {
           case 'function':
             $functions = get_defined_functions();
             if (in_array($this->name, $functions['internal'])) {
               return id(new AphrontRedirectResponse())
                 ->setURI('http://www.php.net/function.'.$this->name);
             }
             break;
           case 'class':
             if (class_exists($this->name, false) ||
                 interface_exists($this->name, false)) {
               if (id(new ReflectionClass($this->name))->isInternal()) {
                 return id(new AphrontRedirectResponse())
                   ->setURI('http://www.php.net/class.'.$this->name);
               }
             }
             break;
         }
       }
     }
 
     $rows = array();
     foreach ($symbols as $symbol) {
       $project = $symbol->getArcanistProject();
       if ($project) {
         $project_name = $project->getName();
       } else {
         $project_name = '-';
       }
 
       $file = phutil_escape_html($symbol->getPath());
       $line = phutil_escape_html($symbol->getLineNumber());
 
       $repo = $symbol->getRepository();
       if ($repo) {
         $href = $symbol->getURI();
 
         if ($request->getBool('jump') && count($symbols) == 1) {
           // If this is a clickthrough from Differential, just jump them
           // straight to the target if we got a single hit.
           return id(new AphrontRedirectResponse())->setURI($href);
         }
 
         $location = phutil_render_tag(
           'a',
           array(
             'href' => $href,
           ),
           phutil_escape_html($file.':'.$line));
       } else if ($file) {
         $location = phutil_escape_html($file.':'.$line);
       } else {
         $location = '?';
       }
 
       $rows[] = array(
         phutil_escape_html($symbol->getSymbolType()),
+        phutil_escape_html($symbol->getSymbolContext()),
         phutil_escape_html($symbol->getSymbolName()),
         phutil_escape_html($symbol->getSymbolLanguage()),
         phutil_escape_html($project_name),
         $location,
       );
     }
 
     $table = new AphrontTableView($rows);
     $table->setHeaders(
       array(
         'Type',
+        'Context',
         'Name',
         'Language',
         'Project',
         'File',
       ));
     $table->setColumnClasses(
       array(
+        '',
         '',
         'pri',
         '',
         '',
         '',
       ));
     $table->setNoDataString(
       "No matching symbol could be found in any indexed project.");
 
     $panel = new AphrontPanelView();
     $panel->setHeader('Similar Symbols');
     $panel->appendChild($table);
 
     return $this->buildStandardPageResponse(
       array(
         $panel,
       ),
       array(
         'title' => 'Find Symbol',
       ));
   }
 
 }
diff --git a/src/applications/diffusion/query/DiffusionSymbolQuery.php b/src/applications/diffusion/query/DiffusionSymbolQuery.php
index 25b6e49a1a..c8af76bdc8 100644
--- a/src/applications/diffusion/query/DiffusionSymbolQuery.php
+++ b/src/applications/diffusion/query/DiffusionSymbolQuery.php
@@ -1,278 +1,295 @@
 <?php
 
 /*
  * Copyright 2012 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 /**
  * Query symbol information (class and function names and location), returning
  * a list of matching @{class:PhabricatorRepositorySymbol} objects and possibly
  * attached data.
  *
  * @task config   Configuring the Query
  * @task exec     Executing the Query
  * @task internal Internals
  *
  * @group diffusion
  */
 final class DiffusionSymbolQuery extends PhabricatorOffsetPagedQuery {
 
+  private $context;
   private $namePrefix;
   private $name;
 
   private $projectIDs;
   private $language;
   private $type;
 
   private $needPaths;
   private $needArcanistProject;
   private $needRepositories;
 
 
 /* -(  Configuring the Query  )---------------------------------------------- */
 
 
+  /**
+   * @task config
+   */
+  public function setContext($context) {
+    $this->context = $context;
+    return $this;
+  }
+
+
   /**
    * @task config
    */
   public function setName($name) {
     $this->name = $name;
     return $this;
   }
 
 
   /**
    * @task config
    */
   public function setNamePrefix($name_prefix) {
     $this->namePrefix = $name_prefix;
     return $this;
   }
 
 
   /**
    * @task config
    */
   public function setProjectIDs(array $project_ids) {
     $this->projectIDs = $project_ids;
     return $this;
   }
 
 
   /**
    * @task config
    */
   public function setLanguage($language) {
     $this->language = $language;
     return $this;
   }
 
 
   /**
    * @task config
    */
   public function setType($type) {
     $this->type = $type;
     return $this;
   }
 
 
   /**
    * @task config
    */
   public function needPaths($need_paths) {
     $this->needPaths = $need_paths;
     return $this;
   }
 
 
   /**
    * @task config
    */
   public function needArcanistProjects($need_arcanist_projects) {
     $this->needArcanistProjects = $need_arcanist_projects;
     return $this;
   }
 
 
   /**
    * @task config
    */
   public function needRepositories($need_repositories) {
     $this->needRepositories = $need_repositories;
     return $this;
   }
 
 
 /* -(  Executing the Query  )------------------------------------------------ */
 
 
   /**
    * @task exec
    */
   public function execute() {
     if ($this->name && $this->namePrefix) {
       throw new Exception(
         "You can not set both a name and a name prefix!");
     } else if (!$this->name && !$this->namePrefix) {
       throw new Exception(
         "You must set a name or a name prefix!");
     }
 
     $symbol = new PhabricatorRepositorySymbol();
     $conn_r = $symbol->establishConnection('r');
 
     $data = queryfx_all(
       $conn_r,
       'SELECT * FROM %T %Q %Q %Q',
       $symbol->getTableName(),
       $this->buildWhereClause($conn_r),
       $this->buildOrderClause($conn_r),
       $this->buildLimitClause($conn_r));
 
     $symbols = $symbol->loadAllFromArray($data);
 
     if ($symbols) {
       if ($this->needPaths) {
         $this->loadPaths($symbols);
       }
       if ($this->needArcanistProjects || $this->needRepositories) {
         $this->loadArcanistProjects($symbols);
       }
       if ($this->needRepositories) {
         $this->loadRepositories($symbols);
       }
 
     }
 
     return $symbols;
   }
 
 
 /* -(  Internals  )---------------------------------------------------------- */
 
 
   /**
    * @task internal
    */
   private function buildOrderClause($conn_r) {
     return qsprintf(
       $conn_r,
       'ORDER BY symbolName ASC');
   }
 
 
   /**
    * @task internal
    */
   private function buildWhereClause($conn_r) {
     $where = array();
 
+    if (isset($this->context)) {
+      $where[] = qsprintf(
+        $conn_r,
+        'symbolContext = %s',
+        $this->context);
+    }
+
     if ($this->name) {
       $where[] = qsprintf(
         $conn_r,
         'symbolName = %s',
         $this->name);
     }
 
     if ($this->namePrefix) {
       $where[] = qsprintf(
         $conn_r,
         'symbolName LIKE %>',
         $this->namePrefix);
     }
 
     if ($this->projectIDs) {
       $where[] = qsprintf(
         $conn_r,
         'arcanistProjectID IN (%Ld)',
         $this->projectIDs);
     }
 
     if ($this->language) {
       $where[] = qsprintf(
         $conn_r,
         'symbolLanguage = %s',
         $this->language);
     }
 
     return $this->formatWhereClause($where);
   }
 
 
   /**
    * @task internal
    */
   private function loadPaths(array $symbols) {
     assert_instances_of($symbols, 'PhabricatorRepositorySymbol');
     $path_map = queryfx_all(
       id(new PhabricatorRepository())->establishConnection('r'),
       'SELECT * FROM %T WHERE id IN (%Ld)',
       PhabricatorRepository::TABLE_PATH,
       mpull($symbols, 'getPathID'));
     $path_map = ipull($path_map, 'path', 'id');
     foreach ($symbols as $symbol) {
       $symbol->attachPath(idx($path_map, $symbol->getPathID()));
     }
   }
 
 
   /**
    * @task internal
    */
   private function loadArcanistProjects(array $symbols) {
     assert_instances_of($symbols, 'PhabricatorRepositorySymbol');
     $projects = id(new PhabricatorRepositoryArcanistProject())->loadAllWhere(
       'id IN (%Ld)',
       mpull($symbols, 'getArcanistProjectID'));
     foreach ($symbols as $symbol) {
       $project = idx($projects, $symbol->getArcanistProjectID());
       $symbol->attachArcanistProject($project);
     }
   }
 
 
   /**
    * @task internal
    */
   private function loadRepositories(array $symbols) {
     assert_instances_of($symbols, 'PhabricatorRepositorySymbol');
 
     $projects = mpull($symbols, 'getArcanistProject');
     $projects = array_filter($projects);
 
     $repo_ids = mpull($projects, 'getRepositoryID');
     $repo_ids = array_filter($repo_ids);
 
     if ($repo_ids) {
       $repos = id(new PhabricatorRepository())->loadAllWhere(
         'id IN (%Ld)',
         $repo_ids);
     } else {
       $repos = array();
     }
 
     foreach ($symbols as $symbol) {
       $proj = $symbol->getArcanistProject();
       if ($proj) {
         $symbol->attachRepository(idx($repos, $proj->getRepositoryID()));
       } else {
         $symbol->attachRepository(null);
       }
     }
   }
 
 
 }