diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index f34be120d292b..145c7d18dd013 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -1,10 +1,10 @@
 use std::collections::hash_map::Entry;
 use std::collections::BTreeMap;
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::Symbol;
-use serde::ser::{Serialize, SerializeStruct, Serializer};
+use serde::ser::{Serialize, SerializeSeq, SerializeStruct, Serializer};
 
 use crate::clean;
 use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
@@ -78,9 +78,9 @@ pub(crate) fn build_index<'tcx>(
             map: &mut FxHashMap<F, usize>,
             itemid: F,
             lastpathid: &mut usize,
-            crate_paths: &mut Vec<(ItemType, Symbol)>,
+            crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
             item_type: ItemType,
-            path: Symbol,
+            path: &[Symbol],
         ) {
             match map.entry(itemid) {
                 Entry::Occupied(entry) => ty.id = Some(RenderTypeId::Index(*entry.get())),
@@ -88,7 +88,7 @@ pub(crate) fn build_index<'tcx>(
                     let pathid = *lastpathid;
                     entry.insert(pathid);
                     *lastpathid += 1;
-                    crate_paths.push((item_type, path));
+                    crate_paths.push((item_type, path.to_vec()));
                     ty.id = Some(RenderTypeId::Index(pathid));
                 }
             }
@@ -100,7 +100,7 @@ pub(crate) fn build_index<'tcx>(
             itemid_to_pathid: &mut FxHashMap<ItemId, usize>,
             primitives: &mut FxHashMap<Symbol, usize>,
             lastpathid: &mut usize,
-            crate_paths: &mut Vec<(ItemType, Symbol)>,
+            crate_paths: &mut Vec<(ItemType, Vec<Symbol>)>,
         ) {
             if let Some(generics) = &mut ty.generics {
                 for item in generics {
@@ -131,7 +131,7 @@ pub(crate) fn build_index<'tcx>(
                             lastpathid,
                             crate_paths,
                             item_type,
-                            *fqp.last().unwrap(),
+                            fqp,
                         );
                     } else {
                         ty.id = None;
@@ -146,7 +146,7 @@ pub(crate) fn build_index<'tcx>(
                         lastpathid,
                         crate_paths,
                         ItemType::Primitive,
-                        sym,
+                        &[sym],
                     );
                 }
                 RenderTypeId::Index(_) => {}
@@ -191,7 +191,7 @@ pub(crate) fn build_index<'tcx>(
                         lastpathid += 1;
 
                         if let Some(&(ref fqp, short)) = paths.get(&defid) {
-                            crate_paths.push((short, *fqp.last().unwrap()));
+                            crate_paths.push((short, fqp.clone()));
                             Some(pathid)
                         } else {
                             None
@@ -213,118 +213,163 @@ pub(crate) fn build_index<'tcx>(
     struct CrateData<'a> {
         doc: String,
         items: Vec<&'a IndexItem>,
-        paths: Vec<(ItemType, Symbol)>,
+        paths: Vec<(ItemType, Vec<Symbol>)>,
         // The String is alias name and the vec is the list of the elements with this alias.
         //
         // To be noted: the `usize` elements are indexes to `items`.
         aliases: &'a BTreeMap<String, Vec<usize>>,
     }
 
+    struct Paths {
+        ty: ItemType,
+        name: Symbol,
+        path: Option<usize>,
+    }
+
+    impl Serialize for Paths {
+        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+        where
+            S: Serializer,
+        {
+            let mut seq = serializer.serialize_seq(None)?;
+            seq.serialize_element(&self.ty)?;
+            seq.serialize_element(self.name.as_str())?;
+            if let Some(ref path) = self.path {
+                seq.serialize_element(path)?;
+            }
+            seq.end()
+        }
+    }
+
     impl<'a> Serialize for CrateData<'a> {
         fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
         where
             S: Serializer,
         {
+            let mut extra_paths = FxHashMap::default();
+            // We need to keep the order of insertion, hence why we use an `IndexMap`. Then we will
+            // insert these "extra paths" (which are paths of items from external crates) into the
+            // `full_paths` list at the end.
+            let mut revert_extra_paths = FxIndexMap::default();
+            let mut mod_paths = FxHashMap::default();
+            for (index, item) in self.items.iter().enumerate() {
+                if item.path.is_empty() {
+                    continue;
+                }
+                mod_paths.insert(&item.path, index);
+            }
+            let mut paths = Vec::with_capacity(self.paths.len());
+            for (ty, path) in &self.paths {
+                if path.len() < 2 {
+                    paths.push(Paths { ty: *ty, name: path[0], path: None });
+                    continue;
+                }
+                let full_path = join_with_double_colon(&path[..path.len() - 1]);
+                if let Some(index) = mod_paths.get(&full_path) {
+                    paths.push(Paths { ty: *ty, name: *path.last().unwrap(), path: Some(*index) });
+                    continue;
+                }
+                // It means it comes from an external crate so the item and its path will be
+                // stored into another array.
+                //
+                // `index` is put after the last `mod_paths`
+                let index = extra_paths.len() + self.items.len();
+                if !revert_extra_paths.contains_key(&index) {
+                    revert_extra_paths.insert(index, full_path.clone());
+                }
+                match extra_paths.entry(full_path) {
+                    Entry::Occupied(entry) => {
+                        paths.push(Paths {
+                            ty: *ty,
+                            name: *path.last().unwrap(),
+                            path: Some(*entry.get()),
+                        });
+                    }
+                    Entry::Vacant(entry) => {
+                        entry.insert(index);
+                        paths.push(Paths {
+                            ty: *ty,
+                            name: *path.last().unwrap(),
+                            path: Some(index),
+                        });
+                    }
+                }
+            }
+
+            let mut names = Vec::with_capacity(self.items.len());
+            let mut types = String::with_capacity(self.items.len());
+            let mut full_paths = Vec::with_capacity(self.items.len());
+            let mut descriptions = Vec::with_capacity(self.items.len());
+            let mut parents = Vec::with_capacity(self.items.len());
+            let mut functions = Vec::with_capacity(self.items.len());
+            let mut deprecated = Vec::with_capacity(self.items.len());
+
+            for (index, item) in self.items.iter().enumerate() {
+                let n = item.ty as u8;
+                let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
+                assert!(c <= 'z', "item types must fit within ASCII printables");
+                types.push(c);
+
+                assert_eq!(
+                    item.parent.is_some(),
+                    item.parent_idx.is_some(),
+                    "`{}` is missing idx",
+                    item.name
+                );
+                // 0 is a sentinel, everything else is one-indexed
+                parents.push(item.parent_idx.map(|x| x + 1).unwrap_or(0));
+
+                names.push(item.name.as_str());
+                descriptions.push(&item.desc);
+
+                if !item.path.is_empty() {
+                    full_paths.push((index, &item.path));
+                }
+
+                // Fake option to get `0` out as a sentinel instead of `null`.
+                // We want to use `0` because it's three less bytes.
+                enum FunctionOption<'a> {
+                    Function(&'a IndexItemFunctionType),
+                    None,
+                }
+                impl<'a> Serialize for FunctionOption<'a> {
+                    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+                    where
+                        S: Serializer,
+                    {
+                        match self {
+                            FunctionOption::None => 0.serialize(serializer),
+                            FunctionOption::Function(ty) => ty.serialize(serializer),
+                        }
+                    }
+                }
+                functions.push(match &item.search_type {
+                    Some(ty) => FunctionOption::Function(ty),
+                    None => FunctionOption::None,
+                });
+
+                if item.deprecation.is_some() {
+                    deprecated.push(index);
+                }
+            }
+
+            for (index, path) in &revert_extra_paths {
+                full_paths.push((*index, path));
+            }
+
             let has_aliases = !self.aliases.is_empty();
             let mut crate_data =
                 serializer.serialize_struct("CrateData", if has_aliases { 9 } else { 8 })?;
             crate_data.serialize_field("doc", &self.doc)?;
-            crate_data.serialize_field(
-                "t",
-                &self
-                    .items
-                    .iter()
-                    .map(|item| {
-                        let n = item.ty as u8;
-                        let c = char::try_from(n + b'A').expect("item types must fit in ASCII");
-                        assert!(c <= 'z', "item types must fit within ASCII printables");
-                        c
-                    })
-                    .collect::<String>(),
-            )?;
-            crate_data.serialize_field(
-                "n",
-                &self.items.iter().map(|item| item.name.as_str()).collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "q",
-                &self
-                    .items
-                    .iter()
-                    .enumerate()
-                    // Serialize as an array of item indices and full paths
-                    .filter_map(
-                        |(index, item)| {
-                            if item.path.is_empty() { None } else { Some((index, &item.path)) }
-                        },
-                    )
-                    .collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "d",
-                &self.items.iter().map(|item| &item.desc).collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "i",
-                &self
-                    .items
-                    .iter()
-                    .map(|item| {
-                        assert_eq!(
-                            item.parent.is_some(),
-                            item.parent_idx.is_some(),
-                            "`{}` is missing idx",
-                            item.name
-                        );
-                        // 0 is a sentinel, everything else is one-indexed
-                        item.parent_idx.map(|x| x + 1).unwrap_or(0)
-                    })
-                    .collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "f",
-                &self
-                    .items
-                    .iter()
-                    .map(|item| {
-                        // Fake option to get `0` out as a sentinel instead of `null`.
-                        // We want to use `0` because it's three less bytes.
-                        enum FunctionOption<'a> {
-                            Function(&'a IndexItemFunctionType),
-                            None,
-                        }
-                        impl<'a> Serialize for FunctionOption<'a> {
-                            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-                            where
-                                S: Serializer,
-                            {
-                                match self {
-                                    FunctionOption::None => 0.serialize(serializer),
-                                    FunctionOption::Function(ty) => ty.serialize(serializer),
-                                }
-                            }
-                        }
-                        match &item.search_type {
-                            Some(ty) => FunctionOption::Function(ty),
-                            None => FunctionOption::None,
-                        }
-                    })
-                    .collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "c",
-                &self
-                    .items
-                    .iter()
-                    .enumerate()
-                    // Serialize as an array of deprecated item indices
-                    .filter_map(|(index, item)| item.deprecation.map(|_| index))
-                    .collect::<Vec<_>>(),
-            )?;
-            crate_data.serialize_field(
-                "p",
-                &self.paths.iter().map(|(it, s)| (it, s.as_str())).collect::<Vec<_>>(),
-            )?;
+            crate_data.serialize_field("t", &types)?;
+            crate_data.serialize_field("n", &names)?;
+            // Serialize as an array of item indices and full paths
+            crate_data.serialize_field("q", &full_paths)?;
+            crate_data.serialize_field("d", &descriptions)?;
+            crate_data.serialize_field("i", &parents)?;
+            crate_data.serialize_field("f", &functions)?;
+            crate_data.serialize_field("c", &deprecated)?;
+            crate_data.serialize_field("p", &paths)?;
             if has_aliases {
                 crate_data.serialize_field("a", &self.aliases)?;
             }
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 42088e7355440..449168c460bed 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -263,7 +263,6 @@ function initSearch(rawSearchIndex) {
      * @returns {integer}
      */
     function buildTypeMapIndex(name) {
-
         if (name === "" || name === null) {
             return -1;
         }
@@ -1380,7 +1379,7 @@ function initSearch(rawSearchIndex) {
              * @type Map<integer, QueryElement[]>
              */
             const queryElemSet = new Map();
-            const addQueryElemToQueryElemSet = function addQueryElemToQueryElemSet(queryElem) {
+            const addQueryElemToQueryElemSet = queryElem => {
                 let currentQueryElemList;
                 if (queryElemSet.has(queryElem.id)) {
                     currentQueryElemList = queryElemSet.get(queryElem.id);
@@ -1397,7 +1396,7 @@ function initSearch(rawSearchIndex) {
              * @type Map<integer, FunctionType[]>
              */
             const fnTypeSet = new Map();
-            const addFnTypeToFnTypeSet = function addFnTypeToFnTypeSet(fnType) {
+            const addFnTypeToFnTypeSet = fnType => {
                 // Pure generic, or an item that's not matched by any query elems.
                 // Try [unboxing] it.
                 //
@@ -1463,6 +1462,32 @@ function initSearch(rawSearchIndex) {
                     if (!typePassesFilter(queryElem.typeFilter, fnType.ty)) {
                         continue;
                     }
+                    const queryElemPathLength = queryElem.pathWithoutLast.length;
+                    // If the query element is a path (it contains `::`), we need to check if this
+                    // path is compatible with the target type.
+                    if (queryElemPathLength > 0) {
+                        const fnTypePath = fnType.path !== undefined && fnType.path !== null ?
+                            fnType.path.split("::") : [];
+                        // If the path provided in the query element is longer than this type,
+                        // no need to check it since it won't match in any case.
+                        if (queryElemPathLength > fnTypePath.length) {
+                            continue;
+                        }
+                        let i = 0;
+                        for (const path of fnTypePath) {
+                            if (path === queryElem.pathWithoutLast[i]) {
+                                i += 1;
+                                if (i >= queryElemPathLength) {
+                                    break;
+                                }
+                            }
+                        }
+                        if (i < queryElemPathLength) {
+                            // If we didn't find all parts of the path of the query element inside
+                            // the fn type, then it's not the right one.
+                            continue;
+                        }
+                    }
                     if (queryElem.generics.length === 0 || checkGenerics(fnType, queryElem)) {
                         currentFnTypeList.splice(i, 1);
                         const result = doHandleQueryElemList(currentFnTypeList, queryElemList);
@@ -1863,14 +1888,14 @@ function initSearch(rawSearchIndex) {
              * @param {QueryElement} elem
              */
             function convertNameToId(elem) {
-                if (typeNameIdMap.has(elem.name)) {
-                    elem.id = typeNameIdMap.get(elem.name);
+                if (typeNameIdMap.has(elem.pathLast)) {
+                    elem.id = typeNameIdMap.get(elem.pathLast);
                 } else if (!parsedQuery.literalSearch) {
                     let match = -1;
                     let matchDist = maxEditDistance + 1;
                     let matchName = "";
                     for (const [name, id] of typeNameIdMap) {
-                        const dist = editDistance(name, elem.name, maxEditDistance);
+                        const dist = editDistance(name, elem.pathLast, maxEditDistance);
                         if (dist <= matchDist && dist <= maxEditDistance) {
                             if (dist === matchDist && matchName > name) {
                                 continue;
@@ -2385,12 +2410,20 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                     lowercasePaths
                 );
             }
+            // `0` is used as a sentinel because it's fewer bytes than `null`
+            if (pathIndex === 0) {
+                return {
+                    id: -1,
+                    ty: null,
+                    path: null,
+                    generics: generics,
+                };
+            }
+            const item = lowercasePaths[pathIndex - 1];
             return {
-                // `0` is used as a sentinel because it's fewer bytes than `null`
-                id: pathIndex === 0
-                    ? -1
-                    : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
-                ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
+                id: buildTypeMapIndex(item.name),
+                ty: item.ty,
+                path: item.path,
                 generics: generics,
             };
         });
@@ -2422,13 +2455,22 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         let inputs, output;
         if (typeof functionSearchType[INPUTS_DATA] === "number") {
             const pathIndex = functionSearchType[INPUTS_DATA];
-            inputs = [{
-                id: pathIndex === 0
-                    ? -1
-                    : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
-                ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
-                generics: [],
-            }];
+            if (pathIndex === 0) {
+                inputs = [{
+                    id: -1,
+                    ty: null,
+                    path: null,
+                    generics: [],
+                }];
+            } else {
+                const item = lowercasePaths[pathIndex - 1];
+                inputs = [{
+                    id: buildTypeMapIndex(item.name),
+                    ty: item.ty,
+                    path: item.path,
+                    generics: [],
+                }];
+            }
         } else {
             inputs = buildItemSearchTypeAll(
                 functionSearchType[INPUTS_DATA],
@@ -2438,13 +2480,22 @@ ${item.displayPath}<span class="${type}">${name}</span>\
         if (functionSearchType.length > 1) {
             if (typeof functionSearchType[OUTPUT_DATA] === "number") {
                 const pathIndex = functionSearchType[OUTPUT_DATA];
-                output = [{
-                    id: pathIndex === 0
-                        ? -1
-                        : buildTypeMapIndex(lowercasePaths[pathIndex - 1].name),
-                    ty: pathIndex === 0 ? null : lowercasePaths[pathIndex - 1].ty,
-                    generics: [],
-                }];
+                if (pathIndex === 0) {
+                    output = [{
+                        id: -1,
+                        ty: null,
+                        path: null,
+                        generics: [],
+                    }];
+                } else {
+                    const item = lowercasePaths[pathIndex - 1];
+                    output = [{
+                        id: buildTypeMapIndex(item.name),
+                        ty: item.ty,
+                        path: item.path,
+                        generics: [],
+                    }];
+                }
             } else {
                 output = buildItemSearchTypeAll(
                     functionSearchType[OUTPUT_DATA],
@@ -2577,9 +2628,19 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             // convert `rawPaths` entries into object form
             // generate normalizedPaths for function search mode
             let len = paths.length;
+            let lastPath = itemPaths.get(0);
             for (let i = 0; i < len; ++i) {
-                lowercasePaths.push({ty: paths[i][0], name: paths[i][1].toLowerCase()});
-                paths[i] = {ty: paths[i][0], name: paths[i][1]};
+                const elem = paths[i];
+                const ty = elem[0];
+                const name = elem[1];
+                let path = null;
+                if (elem.length > 2) {
+                    path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath;
+                    lastPath = path;
+                }
+
+                lowercasePaths.push({ty: ty, name: name.toLowerCase(), path: path});
+                paths[i] = {ty: ty, name: name, path: path};
             }
 
             // convert `item*` into an object form, and construct word indices.
@@ -2589,8 +2650,8 @@ ${item.displayPath}<span class="${type}">${name}</span>\
             // operation that is cached for the life of the page state so that
             // all other search operations have access to this cached data for
             // faster analysis operations
+            lastPath = "";
             len = itemTypes.length;
-            let lastPath = "";
             for (let i = 0; i < len; ++i) {
                 let word = "";
                 // This object should have exactly the same set of fields as the "crateRow"
@@ -2599,11 +2660,12 @@ ${item.displayPath}<span class="${type}">${name}</span>\
                     word = itemNames[i].toLowerCase();
                 }
                 searchWords.push(word);
+                const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath;
                 const row = {
                     crate: crate,
                     ty: itemTypes.charCodeAt(i) - charA,
                     name: itemNames[i],
-                    path: itemPaths.has(i) ? itemPaths.get(i) : lastPath,
+                    path: path,
                     desc: itemDescs[i],
                     parent: itemParentIdxs[i] > 0 ? paths[itemParentIdxs[i] - 1] : undefined,
                     type: buildFunctionSearchType(
diff --git a/tests/rustdoc-js-std/full-path-function.js b/tests/rustdoc-js-std/full-path-function.js
new file mode 100644
index 0000000000000..ac157b3aadf8e
--- /dev/null
+++ b/tests/rustdoc-js-std/full-path-function.js
@@ -0,0 +1,7 @@
+const EXPECTED = {
+    'query': 'vec::vec -> usize',
+    'others': [
+        { 'path': 'std::vec::Vec', 'name': 'len' },
+        { 'path': 'std::vec::Vec', 'name': 'capacity' },
+    ],
+};
diff --git a/tests/rustdoc-js/full-path-function.js b/tests/rustdoc-js/full-path-function.js
new file mode 100644
index 0000000000000..48be51b156fde
--- /dev/null
+++ b/tests/rustdoc-js/full-path-function.js
@@ -0,0 +1,43 @@
+// exact-check
+
+const EXPECTED = [
+    {
+        'query': 'sac -> usize',
+        'others': [
+            { 'path': 'full_path_function::b::Sac', 'name': 'bar' },
+            { 'path': 'full_path_function::b::Sac', 'name': 'len' },
+            { 'path': 'full_path_function::sac::Sac', 'name': 'len' },
+        ],
+    },
+    {
+        'query': 'b::sac -> usize',
+        'others': [
+            { 'path': 'full_path_function::b::Sac', 'name': 'bar' },
+            { 'path': 'full_path_function::b::Sac', 'name': 'len' },
+        ],
+    },
+    {
+        'query': 'b::sac -> u32',
+        'others': [
+            { 'path': 'full_path_function::b::Sac', 'name': 'bar2' },
+        ],
+    },
+    {
+        'query': 'string::string -> u32',
+        'others': [
+            { 'path': 'full_path_function::b::Sac', 'name': 'string' },
+        ],
+    },
+    {
+        'query': 'alloc::string::string -> u32',
+        'others': [
+            { 'path': 'full_path_function::b::Sac', 'name': 'string' },
+        ],
+    },
+    {
+        'query': 'alloc::string -> u32',
+        'others': [
+            { 'path': 'full_path_function::b::Sac', 'name': 'string' },
+        ],
+    },
+];
diff --git a/tests/rustdoc-js/full-path-function.rs b/tests/rustdoc-js/full-path-function.rs
new file mode 100644
index 0000000000000..8dcc3f2b69d84
--- /dev/null
+++ b/tests/rustdoc-js/full-path-function.rs
@@ -0,0 +1,17 @@
+pub mod sac {
+    pub struct Sac;
+
+    impl Sac {
+        pub fn len(&self) -> usize { 0 }
+    }
+}
+
+pub mod b {
+    pub struct Sac;
+    impl Sac {
+        pub fn len(&self) -> usize { 0 }
+        pub fn bar(&self, w: u32) -> usize { 0 }
+        pub fn bar2(&self, w: u32) -> u32 { 0 }
+        pub fn string(w: String) -> u32 { 0 }
+    }
+}