about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2019-03-26 22:26:36 +0100
committerGitHub <noreply@github.com>2019-03-26 22:26:36 +0100
commitf131f042c2de84e330ec006f8f70780efccc4dca (patch)
treead89427be4c0567f33f8d509a848985aedbc63ec /src
parentfbd34efb32b9efb574899e4335bdc8c6525ac27e (diff)
parentbefe9cac910f6cf1f98b9c145bcad80be659106a (diff)
downloadrust-f131f042c2de84e330ec006f8f70780efccc4dca.tar.gz
rust-f131f042c2de84e330ec006f8f70780efccc4dca.zip
Rollup merge of #59004 - GuillaumeGomez:generics-handling, r=QuietMisdreavus
[rustdoc] Improve "in parameters" search and search more generally

Fixes #58230.

r? @QuietMisdreavus
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/collect.rs68
-rw-r--r--src/librustc_typeck/lib.rs2
-rw-r--r--src/librustdoc/clean/inline.rs11
-rw-r--r--src/librustdoc/clean/mod.rs195
-rw-r--r--src/librustdoc/html/render.rs42
-rw-r--r--src/librustdoc/html/static/main.js30
6 files changed, 308 insertions, 40 deletions
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 06450424776..8960c9acafa 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -1134,13 +1134,33 @@ fn report_assoc_ty_on_inherent_impl<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, span:
 }
 
 fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> {
+    checked_type_of(tcx, def_id, true).unwrap()
+}
+
+/// Same as [`type_of`] but returns [`Option`] instead of failing.
+///
+/// If you want to fail anyway, you can set the `fail` parameter to true, but in this case,
+/// you'd better just call [`type_of`] directly.
+pub fn checked_type_of<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    def_id: DefId,
+    fail: bool,
+) -> Option<Ty<'tcx>> {
     use rustc::hir::*;
 
-    let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap();
+    let hir_id = match tcx.hir().as_local_hir_id(def_id) {
+        Some(hir_id) => hir_id,
+        None => {
+            if !fail {
+                return None;
+            }
+            bug!("invalid node");
+        }
+    };
 
     let icx = ItemCtxt::new(tcx, def_id);
 
-    match tcx.hir().get_by_hir_id(hir_id) {
+    Some(match tcx.hir().get_by_hir_id(hir_id) {
         Node::TraitItem(item) => match item.node {
             TraitItemKind::Method(..) => {
                 let substs = InternalSubsts::identity_for_item(tcx, def_id);
@@ -1148,6 +1168,9 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> {
             }
             TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => icx.to_ty(ty),
             TraitItemKind::Type(_, None) => {
+                if !fail {
+                    return None;
+                }
                 span_bug!(item.span, "associated type missing default");
             }
         },
@@ -1229,6 +1252,9 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> {
                 | ItemKind::GlobalAsm(..)
                 | ItemKind::ExternCrate(..)
                 | ItemKind::Use(..) => {
+                    if !fail {
+                        return None;
+                    }
                     span_bug!(
                         item.span,
                         "compute_type_of_item: unexpected item type: {:?}",
@@ -1267,7 +1293,7 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> {
             ..
         }) => {
             if gen.is_some() {
-                return tcx.typeck_tables_of(def_id).node_type(hir_id);
+                return Some(tcx.typeck_tables_of(def_id).node_type(hir_id));
             }
 
             let substs = ty::ClosureSubsts {
@@ -1345,6 +1371,9 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> {
                             }
                             // Sanity check to make sure everything is as expected.
                             if !found_const {
+                                if !fail {
+                                    return None;
+                                }
                                 bug!("no arg matching AnonConst in path")
                             }
                             match path.def {
@@ -1360,24 +1389,37 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> {
                                     for param in &generics.params {
                                         if let ty::GenericParamDefKind::Const = param.kind {
                                             if param_index == arg_index {
-                                                return tcx.type_of(param.def_id);
+                                                return Some(tcx.type_of(param.def_id));
                                             }
                                             param_index += 1;
                                         }
                                     }
                                     // This is no generic parameter associated with the arg. This is
                                     // probably from an extra arg where one is not needed.
-                                    return tcx.types.err;
+                                    return Some(tcx.types.err);
                                 }
                                 Def::Err => tcx.types.err,
-                                x => bug!("unexpected const parent path def {:?}", x),
+                                x => {
+                                    if !fail {
+                                        return None;
+                                    }
+                                    bug!("unexpected const parent path def {:?}", x);
+                                }
+                            }
+                        }
+                        x => {
+                            if !fail {
+                                return None;
                             }
+                            bug!("unexpected const parent path {:?}", x);
                         }
-                        x => bug!("unexpected const parent path {:?}", x),
                     }
                 }
 
                 x => {
+                    if !fail {
+                        return None;
+                    }
                     bug!("unexpected const parent in type_of_def_id(): {:?}", x);
                 }
             }
@@ -1388,13 +1430,21 @@ fn type_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> Ty<'tcx> {
             hir::GenericParamKind::Const { ref ty, .. } => {
                 icx.to_ty(ty)
             }
-            x => bug!("unexpected non-type Node::GenericParam: {:?}", x),
+            x => {
+                if !fail {
+                    return None;
+                }
+                bug!("unexpected non-type Node::GenericParam: {:?}", x)
+            },
         },
 
         x => {
+            if !fail {
+                return None;
+            }
             bug!("unexpected sort of node in type_of_def_id(): {:?}", x);
         }
-    }
+    })
 }
 
 fn find_existential_constraints<'a, 'tcx>(
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index b2c2b233c81..3ddc18284b3 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -115,6 +115,8 @@ use util::common::time;
 
 use std::iter;
 
+pub use collect::checked_type_of;
+
 pub struct TypeAndSubsts<'tcx> {
     substs: SubstsRef<'tcx>,
     ty: Ty<'tcx>,
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 5969aa758df..d9a63969fec 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -210,15 +210,20 @@ fn build_external_function(cx: &DocContext<'_>, did: DefId) -> clean::Function {
     };
 
     let predicates = cx.tcx.predicates_of(did);
+    let generics = (cx.tcx.generics_of(did), &predicates).clean(cx);
+    let decl = (did, sig).clean(cx);
+    let (all_types, ret_types) = clean::get_all_types(&generics, &decl, cx);
     clean::Function {
-        decl: (did, sig).clean(cx),
-        generics: (cx.tcx.generics_of(did), &predicates).clean(cx),
+        decl,
+        generics,
         header: hir::FnHeader {
             unsafety: sig.unsafety(),
             abi: sig.abi(),
             constness,
             asyncness: hir::IsAsync::NotAsync,
-        }
+        },
+        all_types,
+        ret_types,
     }
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 61dbf00a1f5..31f757c87b2 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1084,9 +1084,10 @@ impl GenericBound {
 
     fn get_trait_type(&self) -> Option<Type> {
         if let GenericBound::TraitBound(PolyTrait { ref trait_, .. }, _) = *self {
-            return Some(trait_.clone());
+            Some(trait_.clone())
+        } else {
+            None
         }
-        None
     }
 }
 
@@ -1325,6 +1326,16 @@ pub enum WherePredicate {
     EqPredicate { lhs: Type, rhs: Type },
 }
 
+impl WherePredicate {
+    pub fn get_bounds(&self) -> Option<&[GenericBound]> {
+        match *self {
+            WherePredicate::BoundPredicate { ref bounds, .. } => Some(bounds),
+            WherePredicate::RegionPredicate { ref bounds, .. } => Some(bounds),
+            _ => None,
+        }
+    }
+}
+
 impl Clean<WherePredicate> for hir::WherePredicate {
     fn clean(&self, cx: &DocContext<'_>) -> WherePredicate {
         match *self {
@@ -1461,6 +1472,25 @@ pub enum GenericParamDefKind {
     },
 }
 
+impl GenericParamDefKind {
+    pub fn is_type(&self) -> bool {
+        match *self {
+            GenericParamDefKind::Type { .. } => true,
+            _ => false,
+        }
+    }
+
+    pub fn get_type(&self, cx: &DocContext<'_>) -> Option<Type> {
+        match *self {
+            GenericParamDefKind::Type { did, .. } => {
+                rustc_typeck::checked_type_of(cx.tcx, did, false).map(|t| t.clean(cx))
+            }
+            GenericParamDefKind::Const { ref ty, .. } => Some(ty.clone()),
+            GenericParamDefKind::Lifetime => None,
+        }
+    }
+}
+
 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Debug, Hash)]
 pub struct GenericParamDef {
     pub name: String,
@@ -1472,12 +1502,25 @@ impl GenericParamDef {
     pub fn is_synthetic_type_param(&self) -> bool {
         match self.kind {
             GenericParamDefKind::Lifetime |
-            GenericParamDefKind::Const { .. } => {
-                false
-            }
+            GenericParamDefKind::Const { .. } => false,
             GenericParamDefKind::Type { ref synthetic, .. } => synthetic.is_some(),
         }
     }
+
+    pub fn is_type(&self) -> bool {
+        self.kind.is_type()
+    }
+
+    pub fn get_type(&self, cx: &DocContext<'_>) -> Option<Type> {
+        self.kind.get_type(cx)
+    }
+
+    pub fn get_bounds(&self) -> Option<&[GenericBound]> {
+        match self.kind {
+            GenericParamDefKind::Type { ref bounds, .. } => Some(bounds),
+            _ => None,
+        }
+    }
 }
 
 impl Clean<GenericParamDef> for ty::GenericParamDef {
@@ -1714,12 +1757,122 @@ impl<'a, 'tcx> Clean<Generics> for (&'a ty::Generics,
     }
 }
 
+/// The point of this function is to replace bounds with types.
+///
+/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
+/// `[Display, Option]` (we just returns the list of the types, we don't care about the
+/// wrapped types in here).
+fn get_real_types(
+    generics: &Generics,
+    arg: &Type,
+    cx: &DocContext<'_>,
+) -> FxHashSet<Type> {
+    let arg_s = arg.to_string();
+    let mut res = FxHashSet::default();
+    if arg.is_full_generic() {
+        if let Some(where_pred) = generics.where_predicates.iter().find(|g| {
+            match g {
+                &WherePredicate::BoundPredicate { ref ty, .. } => ty.def_id() == arg.def_id(),
+                _ => false,
+            }
+        }) {
+            let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
+            for bound in bounds.iter() {
+                match *bound {
+                    GenericBound::TraitBound(ref poly_trait, _) => {
+                        for x in poly_trait.generic_params.iter() {
+                            if !x.is_type() {
+                                continue
+                            }
+                            if let Some(ty) = x.get_type(cx) {
+                                let adds = get_real_types(generics, &ty, cx);
+                                if !adds.is_empty() {
+                                    res.extend(adds);
+                                } else if !ty.is_full_generic() {
+                                    res.insert(ty);
+                                }
+                            }
+                        }
+                    }
+                    _ => {}
+                }
+            }
+        }
+        if let Some(bound) = generics.params.iter().find(|g| {
+            g.is_type() && g.name == arg_s
+        }) {
+            for bound in bound.get_bounds().unwrap_or_else(|| &[]) {
+                if let Some(ty) = bound.get_trait_type() {
+                    let adds = get_real_types(generics, &ty, cx);
+                    if !adds.is_empty() {
+                        res.extend(adds);
+                    } else if !ty.is_full_generic() {
+                        res.insert(ty.clone());
+                    }
+                }
+            }
+        }
+    } else {
+        res.insert(arg.clone());
+        if let Some(gens) = arg.generics() {
+            for gen in gens.iter() {
+                if gen.is_full_generic() {
+                    let adds = get_real_types(generics, gen, cx);
+                    if !adds.is_empty() {
+                        res.extend(adds);
+                    }
+                } else {
+                    res.insert(gen.clone());
+                }
+            }
+        }
+    }
+    res
+}
+
+/// Return the full list of types when bounds have been resolved.
+///
+/// i.e. `fn foo<A: Display, B: Option<A>>(x: u32, y: B)` will return
+/// `[u32, Display, Option]`.
+pub fn get_all_types(
+    generics: &Generics,
+    decl: &FnDecl,
+    cx: &DocContext<'_>,
+) -> (Vec<Type>, Vec<Type>) {
+    let mut all_types = FxHashSet::default();
+    for arg in decl.inputs.values.iter() {
+        if arg.type_.is_self_type() {
+            continue;
+        }
+        let args = get_real_types(generics, &arg.type_, cx);
+        if !args.is_empty() {
+            all_types.extend(args);
+        } else {
+            all_types.insert(arg.type_.clone());
+        }
+    }
+
+    let ret_types = match decl.output {
+        FunctionRetTy::Return(ref return_type) => {
+            let mut ret = get_real_types(generics, &return_type, cx);
+            if ret.is_empty() {
+                ret.insert(return_type.clone());
+            }
+            ret.into_iter().collect()
+        }
+        _ => Vec::new(),
+    };
+    (all_types.into_iter().collect(), ret_types)
+}
+
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct Method {
     pub generics: Generics,
     pub decl: FnDecl,
     pub header: hir::FnHeader,
     pub defaultness: Option<hir::Defaultness>,
+    pub all_types: Vec<Type>,
+    pub ret_types: Vec<Type>,
 }
 
 impl<'a> Clean<Method> for (&'a hir::MethodSig, &'a hir::Generics, hir::BodyId,
@@ -1728,11 +1881,14 @@ impl<'a> Clean<Method> for (&'a hir::MethodSig, &'a hir::Generics, hir::BodyId,
         let (generics, decl) = enter_impl_trait(cx, || {
             (self.1.clean(cx), (&*self.0.decl, self.2).clean(cx))
         });
+        let (all_types, ret_types) = get_all_types(&generics, &decl, cx);
         Method {
             decl,
             generics,
             header: self.0.header,
             defaultness: self.3,
+            all_types,
+            ret_types,
         }
     }
 }
@@ -1742,6 +1898,8 @@ pub struct TyMethod {
     pub header: hir::FnHeader,
     pub decl: FnDecl,
     pub generics: Generics,
+    pub all_types: Vec<Type>,
+    pub ret_types: Vec<Type>,
 }
 
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
@@ -1749,6 +1907,8 @@ pub struct Function {
     pub decl: FnDecl,
     pub generics: Generics,
     pub header: hir::FnHeader,
+    pub all_types: Vec<Type>,
+    pub ret_types: Vec<Type>,
 }
 
 impl Clean<Item> for doctree::Function {
@@ -1763,6 +1923,7 @@ impl Clean<Item> for doctree::Function {
         } else {
             hir::Constness::NotConst
         };
+        let (all_types, ret_types) = get_all_types(&generics, &decl, cx);
         Item {
             name: Some(self.name.clean(cx)),
             attrs: self.attrs.clean(cx),
@@ -1775,6 +1936,8 @@ impl Clean<Item> for doctree::Function {
                 decl,
                 generics,
                 header: hir::FnHeader { constness, ..self.header },
+                all_types,
+                ret_types,
             }),
         }
     }
@@ -1862,7 +2025,7 @@ impl<'a, A: Copy> Clean<FnDecl> for (&'a hir::FnDecl, A)
         FnDecl {
             inputs: (&self.0.inputs[..], self.1).clean(cx),
             output: self.0.output.clean(cx),
-            attrs: Attributes::default()
+            attrs: Attributes::default(),
         }
     }
 }
@@ -2044,10 +2207,13 @@ impl Clean<Item> for hir::TraitItem {
                 let (generics, decl) = enter_impl_trait(cx, || {
                     (self.generics.clean(cx), (&*sig.decl, &names[..]).clean(cx))
                 });
+                let (all_types, ret_types) = get_all_types(&generics, &decl, cx);
                 TyMethodItem(TyMethod {
                     header: sig.header,
                     decl,
                     generics,
+                    all_types,
+                    ret_types,
                 })
             }
             hir::TraitItemKind::Type(ref bounds, ref default) => {
@@ -2145,6 +2311,7 @@ impl<'tcx> Clean<Item> for ty::AssociatedItem {
                     ty::ImplContainer(_) => true,
                     ty::TraitContainer(_) => self.defaultness.has_value()
                 };
+                let (all_types, ret_types) = get_all_types(&generics, &decl, cx);
                 if provided {
                     let constness = if cx.tcx.is_min_const_fn(self.def_id) {
                         hir::Constness::Const
@@ -2161,6 +2328,8 @@ impl<'tcx> Clean<Item> for ty::AssociatedItem {
                             asyncness: hir::IsAsync::NotAsync,
                         },
                         defaultness: Some(self.defaultness),
+                        all_types,
+                        ret_types,
                     })
                 } else {
                     TyMethodItem(TyMethod {
@@ -2171,7 +2340,9 @@ impl<'tcx> Clean<Item> for ty::AssociatedItem {
                             abi: sig.abi(),
                             constness: hir::Constness::NotConst,
                             asyncness: hir::IsAsync::NotAsync,
-                        }
+                        },
+                        all_types,
+                        ret_types,
                     })
                 }
             }
@@ -2420,6 +2591,13 @@ impl Type {
             _ => None
         }
     }
+
+    pub fn is_full_generic(&self) -> bool {
+        match *self {
+            Type::Generic(_) => true,
+            _ => false,
+        }
+    }
 }
 
 impl GetDefId for Type {
@@ -3849,6 +4027,7 @@ impl Clean<Item> for hir::ForeignItem {
                 let (generics, decl) = enter_impl_trait(cx, || {
                     (generics.clean(cx), (&**decl, &names[..]).clean(cx))
                 });
+                let (all_types, ret_types) = get_all_types(&generics, &decl, cx);
                 ForeignFunctionItem(Function {
                     decl,
                     generics,
@@ -3858,6 +4037,8 @@ impl Clean<Item> for hir::ForeignItem {
                         constness: hir::Constness::NotConst,
                         asyncness: hir::IsAsync::NotAsync,
                     },
+                    all_types,
+                    ret_types,
                 })
             }
             hir::ForeignItemKind::Static(ref ty, mutbl) => {
diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs
index bead4c78e47..866d8fe682a 100644
--- a/src/librustdoc/html/render.rs
+++ b/src/librustdoc/html/render.rs
@@ -446,7 +446,7 @@ impl ToJson for Type {
                 }
                 Json::Array(data)
             }
-            None => Json::Null
+            None => Json::Null,
         }
     }
 }
@@ -455,19 +455,27 @@ impl ToJson for Type {
 #[derive(Debug)]
 struct IndexItemFunctionType {
     inputs: Vec<Type>,
-    output: Option<Type>,
+    output: Option<Vec<Type>>,
 }
 
 impl ToJson for IndexItemFunctionType {
     fn to_json(&self) -> Json {
         // If we couldn't figure out a type, just write `null`.
-        if self.inputs.iter().chain(self.output.iter()).any(|ref i| i.name.is_none()) {
+        let mut iter = self.inputs.iter();
+        if match self.output {
+            Some(ref output) => iter.chain(output.iter()).any(|ref i| i.name.is_none()),
+            None => iter.any(|ref i| i.name.is_none()),
+        } {
             Json::Null
         } else {
             let mut data = Vec::with_capacity(2);
             data.push(self.inputs.to_json());
             if let Some(ref output) = self.output {
-                data.push(output.to_json());
+                if output.len() > 1 {
+                    data.push(output.to_json());
+                } else {
+                    data.push(output[0].to_json());
+                }
             }
             Json::Array(data)
         }
@@ -5025,20 +5033,26 @@ fn make_item_keywords(it: &clean::Item) -> String {
 }
 
 fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> {
-    let decl = match item.inner {
-        clean::FunctionItem(ref f) => &f.decl,
-        clean::MethodItem(ref m) => &m.decl,
-        clean::TyMethodItem(ref m) => &m.decl,
-        _ => return None
+    let (all_types, ret_types) = match item.inner {
+        clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
+        clean::MethodItem(ref m) => (&m.all_types, &m.ret_types),
+        clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
+        _ => return None,
     };
 
-    let inputs = decl.inputs.values.iter().map(|arg| get_index_type(&arg.type_)).collect();
-    let output = match decl.output {
-        clean::FunctionRetTy::Return(ref return_type) => Some(get_index_type(return_type)),
-        _ => None
+    let inputs = all_types.iter().map(|arg| {
+        get_index_type(&arg)
+    }).filter(|a| a.name.is_some()).collect();
+    let output = ret_types.iter().map(|arg| {
+        get_index_type(&arg)
+    }).filter(|a| a.name.is_some()).collect::<Vec<_>>();
+    let output = if output.is_empty() {
+        None
+    } else {
+        Some(output)
     };
 
-    Some(IndexItemFunctionType { inputs: inputs, output: output })
+    Some(IndexItemFunctionType { inputs, output })
 }
 
 fn get_index_type(clean_type: &clean::Type) -> Type {
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index fef6910f40a..aad7eb627bf 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -714,7 +714,10 @@ if (!DOMTokenList.prototype.remove) {
                 }
                 lev_distance = Math.min(levenshtein(obj[NAME], val.name), lev_distance);
                 if (lev_distance <= MAX_LEV_DISTANCE) {
-                    lev_distance = Math.min(checkGenerics(obj, val), lev_distance);
+                    // The generics didn't match but the name kinda did so we give it
+                    // a levenshtein distance value that isn't *this* good so it goes
+                    // into the search results but not too high.
+                    lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
                 } else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
                     // We can check if the type we're looking for is inside the generics!
                     var olength = obj[GENERICS_DATA].length;
@@ -752,13 +755,26 @@ if (!DOMTokenList.prototype.remove) {
                 var lev_distance = MAX_LEV_DISTANCE + 1;
 
                 if (obj && obj.type && obj.type.length > OUTPUT_DATA) {
-                    var tmp = checkType(obj.type[OUTPUT_DATA], val, literalSearch);
-                    if (literalSearch === true && tmp === true) {
-                        return true;
+                    var ret = obj.type[OUTPUT_DATA];
+                    if (!obj.type[OUTPUT_DATA].length) {
+                        ret = [ret];
                     }
-                    lev_distance = Math.min(tmp, lev_distance);
-                    if (lev_distance === 0) {
-                        return 0;
+                    for (var x = 0; x < ret.length; ++x) {
+                        var r = ret[x];
+                        if (typeof r === "string") {
+                            r = [r];
+                        }
+                        var tmp = checkType(r, val, literalSearch);
+                        if (literalSearch === true) {
+                            if (tmp === true) {
+                                return true;
+                            }
+                            continue;
+                        }
+                        lev_distance = Math.min(tmp, lev_distance);
+                        if (lev_distance === 0) {
+                            return 0;
+                        }
                     }
                 }
                 return literalSearch === true ? false : lev_distance;