Fix invalid handling of generics
This commit is contained in:
parent
f2707fec04
commit
355e6eddfe
7 changed files with 147 additions and 93 deletions
|
@ -48,6 +48,7 @@ crate enum ItemType {
|
||||||
ProcAttribute = 23,
|
ProcAttribute = 23,
|
||||||
ProcDerive = 24,
|
ProcDerive = 24,
|
||||||
TraitAlias = 25,
|
TraitAlias = 25,
|
||||||
|
Generic = 26,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for ItemType {
|
impl Serialize for ItemType {
|
||||||
|
@ -173,6 +174,7 @@ impl ItemType {
|
||||||
ItemType::ProcAttribute => "attr",
|
ItemType::ProcAttribute => "attr",
|
||||||
ItemType::ProcDerive => "derive",
|
ItemType::ProcDerive => "derive",
|
||||||
ItemType::TraitAlias => "traitalias",
|
ItemType::TraitAlias => "traitalias",
|
||||||
|
ItemType::Generic => "generic",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_span::symbol::Symbol;
|
use rustc_span::symbol::Symbol;
|
||||||
use serde::ser::{Serialize, SerializeStruct, Serializer};
|
use serde::ser::{Serialize, SerializeStruct, Serializer};
|
||||||
|
@ -192,32 +192,24 @@ crate fn get_index_search_type<'tcx>(
|
||||||
item: &clean::Item,
|
item: &clean::Item,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
) -> Option<IndexItemFunctionType> {
|
) -> Option<IndexItemFunctionType> {
|
||||||
let (all_types, ret_types) = match *item.kind {
|
let (mut inputs, mut output) = match *item.kind {
|
||||||
clean::FunctionItem(ref f) => get_all_types(&f.generics, &f.decl, tcx),
|
clean::FunctionItem(ref f) => get_all_types(&f.generics, &f.decl, tcx),
|
||||||
clean::MethodItem(ref m, _) => get_all_types(&m.generics, &m.decl, tcx),
|
clean::MethodItem(ref m, _) => get_all_types(&m.generics, &m.decl, tcx),
|
||||||
clean::TyMethodItem(ref m) => get_all_types(&m.generics, &m.decl, tcx),
|
clean::TyMethodItem(ref m) => get_all_types(&m.generics, &m.decl, tcx),
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inputs = all_types
|
inputs.retain(|a| a.ty.name.is_some());
|
||||||
.iter()
|
output.retain(|a| a.ty.name.is_some());
|
||||||
.map(|(ty, kind)| TypeWithKind::from((get_index_type(ty), *kind)))
|
|
||||||
.filter(|a| a.ty.name.is_some())
|
|
||||||
.collect();
|
|
||||||
let output = ret_types
|
|
||||||
.iter()
|
|
||||||
.map(|(ty, kind)| TypeWithKind::from((get_index_type(ty), *kind)))
|
|
||||||
.filter(|a| a.ty.name.is_some())
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let output = if output.is_empty() { None } else { Some(output) };
|
let output = if output.is_empty() { None } else { Some(output) };
|
||||||
|
|
||||||
Some(IndexItemFunctionType { inputs, output })
|
Some(IndexItemFunctionType { inputs, output })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_index_type(clean_type: &clean::Type) -> RenderType {
|
fn get_index_type(clean_type: &clean::Type, generics: Vec<TypeWithKind>) -> RenderType {
|
||||||
RenderType {
|
RenderType {
|
||||||
name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
|
name: get_index_type_name(clean_type, true).map(|s| s.as_str().to_ascii_lowercase()),
|
||||||
generics: get_generics(clean_type),
|
generics: if generics.is_empty() { None } else { Some(generics) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,23 +238,6 @@ fn get_index_type_name(clean_type: &clean::Type, accept_generic: bool) -> Option
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a list of generic parameters for use in the search index.
|
|
||||||
///
|
|
||||||
/// This function replaces bounds with types, so that `T where T: Debug` just becomes `Debug`.
|
|
||||||
/// It does return duplicates, and that's intentional, since search queries like `Result<usize, usize>`
|
|
||||||
/// are supposed to match only results where both parameters are `usize`.
|
|
||||||
fn get_generics(clean_type: &clean::Type) -> Option<Vec<String>> {
|
|
||||||
clean_type.generics().and_then(|types| {
|
|
||||||
let r = types
|
|
||||||
.iter()
|
|
||||||
.filter_map(|t| {
|
|
||||||
get_index_type_name(t, false).map(|name| name.as_str().to_ascii_lowercase())
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
if r.is_empty() { None } else { Some(r) }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The point of this function is to replace bounds with types.
|
/// 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
|
/// i.e. `[T, U]` when you have the following bounds: `T: Display, U: Option<T>` will return
|
||||||
|
@ -272,27 +247,77 @@ crate fn get_real_types<'tcx>(
|
||||||
generics: &Generics,
|
generics: &Generics,
|
||||||
arg: &Type,
|
arg: &Type,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
recurse: i32,
|
recurse: usize,
|
||||||
res: &mut FxHashSet<(Type, ItemType)>,
|
res: &mut Vec<TypeWithKind>,
|
||||||
) -> usize {
|
) {
|
||||||
fn insert(res: &mut FxHashSet<(Type, ItemType)>, tcx: TyCtxt<'_>, ty: Type) -> usize {
|
fn insert_ty(
|
||||||
if let Some(kind) = ty.def_id_no_primitives().map(|did| tcx.def_kind(did).into()) {
|
res: &mut Vec<TypeWithKind>,
|
||||||
res.insert((ty, kind));
|
tcx: TyCtxt<'_>,
|
||||||
1
|
ty: Type,
|
||||||
|
mut generics: Vec<TypeWithKind>,
|
||||||
|
) {
|
||||||
|
let is_full_generic = ty.is_full_generic();
|
||||||
|
|
||||||
|
if is_full_generic && generics.len() == 1 {
|
||||||
|
// In this case, no need to go through an intermediate state if the generics
|
||||||
|
// contains only one element.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// fn foo<T: Display>(r: Option<T>) {}
|
||||||
|
//
|
||||||
|
// In this case, it would contain:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// [{
|
||||||
|
// name: "option",
|
||||||
|
// generics: [{
|
||||||
|
// name: "",
|
||||||
|
// generics: [
|
||||||
|
// name: "Display",
|
||||||
|
// generics: []
|
||||||
|
// }]
|
||||||
|
// }]
|
||||||
|
// }]
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// After removing the intermediate (unnecessary) full generic, it'll become:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// [{
|
||||||
|
// name: "option",
|
||||||
|
// generics: [{
|
||||||
|
// name: "Display",
|
||||||
|
// generics: []
|
||||||
|
// }]
|
||||||
|
// }]
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// To be noted that it can work if there is ONLY ONE generic, otherwise we still
|
||||||
|
// need to keep it as is!
|
||||||
|
res.push(generics.pop().unwrap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut index_ty = get_index_type(&ty, generics);
|
||||||
|
if index_ty.name.as_ref().map(|s| s.is_empty()).unwrap_or(true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if is_full_generic {
|
||||||
|
// We remove the name of the full generic because we have no use for it.
|
||||||
|
index_ty.name = Some(String::new());
|
||||||
|
res.push(TypeWithKind::from((index_ty, ItemType::Generic)));
|
||||||
|
} else if let Some(kind) = ty.def_id_no_primitives().map(|did| tcx.def_kind(did).into()) {
|
||||||
|
res.push(TypeWithKind::from((index_ty, kind)));
|
||||||
} else if ty.is_primitive() {
|
} else if ty.is_primitive() {
|
||||||
// This is a primitive, let's store it as such.
|
// This is a primitive, let's store it as such.
|
||||||
res.insert((ty, ItemType::Primitive));
|
res.push(TypeWithKind::from((index_ty, ItemType::Primitive)));
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if recurse >= 10 {
|
if recurse >= 10 {
|
||||||
// FIXME: remove this whole recurse thing when the recursion bug is fixed
|
// FIXME: remove this whole recurse thing when the recursion bug is fixed
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
let mut nb_added = 0;
|
|
||||||
|
|
||||||
if let Type::Generic(arg_s) = *arg {
|
if let Type::Generic(arg_s) = *arg {
|
||||||
if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
|
if let Some(where_pred) = generics.where_predicates.iter().find(|g| match g {
|
||||||
|
@ -301,6 +326,7 @@ crate fn get_real_types<'tcx>(
|
||||||
}
|
}
|
||||||
_ => false,
|
_ => false,
|
||||||
}) {
|
}) {
|
||||||
|
let mut ty_generics = Vec::new();
|
||||||
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
|
let bounds = where_pred.get_bounds().unwrap_or_else(|| &[]);
|
||||||
for bound in bounds.iter() {
|
for bound in bounds.iter() {
|
||||||
if let GenericBound::TraitBound(poly_trait, _) = bound {
|
if let GenericBound::TraitBound(poly_trait, _) = bound {
|
||||||
|
@ -309,41 +335,32 @@ crate fn get_real_types<'tcx>(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if let Some(ty) = x.get_type() {
|
if let Some(ty) = x.get_type() {
|
||||||
let adds = get_real_types(generics, &ty, tcx, recurse + 1, res);
|
get_real_types(generics, &ty, tcx, recurse + 1, &mut ty_generics);
|
||||||
nb_added += adds;
|
|
||||||
if adds == 0 && !ty.is_full_generic() {
|
|
||||||
nb_added += insert(res, tcx, ty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
insert_ty(res, tcx, arg.clone(), ty_generics);
|
||||||
}
|
}
|
||||||
if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
|
if let Some(bound) = generics.params.iter().find(|g| g.is_type() && g.name == arg_s) {
|
||||||
|
let mut ty_generics = Vec::new();
|
||||||
for bound in bound.get_bounds().unwrap_or(&[]) {
|
for bound in bound.get_bounds().unwrap_or(&[]) {
|
||||||
if let Some(path) = bound.get_trait_path() {
|
if let Some(path) = bound.get_trait_path() {
|
||||||
let ty = Type::ResolvedPath { did: path.def_id(), path };
|
let ty = Type::ResolvedPath { did: path.def_id(), path };
|
||||||
let adds = get_real_types(generics, &ty, tcx, recurse + 1, res);
|
get_real_types(generics, &ty, tcx, recurse + 1, &mut ty_generics);
|
||||||
nb_added += adds;
|
|
||||||
if adds == 0 && !ty.is_full_generic() {
|
|
||||||
nb_added += insert(res, tcx, ty);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
insert_ty(res, tcx, arg.clone(), ty_generics);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
nb_added += insert(res, tcx, arg.clone());
|
let mut ty_generics = Vec::new();
|
||||||
if let Some(gens) = arg.generics() {
|
if let Some(arg_generics) = arg.generics() {
|
||||||
for gen in gens.iter() {
|
for gen in arg_generics.iter() {
|
||||||
if gen.is_full_generic() {
|
get_real_types(generics, gen, tcx, recurse + 1, &mut ty_generics);
|
||||||
nb_added += get_real_types(generics, gen, tcx, recurse + 1, res);
|
|
||||||
} else {
|
|
||||||
nb_added += insert(res, tcx, (*gen).clone());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
insert_ty(res, tcx, arg.clone(), ty_generics);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
nb_added
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the full list of types when bounds have been resolved.
|
/// Return the full list of types when bounds have been resolved.
|
||||||
|
@ -354,38 +371,41 @@ crate fn get_all_types<'tcx>(
|
||||||
generics: &Generics,
|
generics: &Generics,
|
||||||
decl: &FnDecl,
|
decl: &FnDecl,
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
) -> (Vec<(Type, ItemType)>, Vec<(Type, ItemType)>) {
|
) -> (Vec<TypeWithKind>, Vec<TypeWithKind>) {
|
||||||
let mut all_types = FxHashSet::default();
|
let mut all_types = Vec::new();
|
||||||
for arg in decl.inputs.values.iter() {
|
for arg in decl.inputs.values.iter() {
|
||||||
if arg.type_.is_self_type() {
|
if arg.type_.is_self_type() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut args = FxHashSet::default();
|
// FIXME: performance wise, it'd be much better to move `args` declaration outside of the
|
||||||
|
// loop and replace this line with `args.clear()`.
|
||||||
|
let mut args = Vec::new();
|
||||||
get_real_types(generics, &arg.type_, tcx, 0, &mut args);
|
get_real_types(generics, &arg.type_, tcx, 0, &mut args);
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
|
// FIXME: once back to performance improvements, replace this line with:
|
||||||
|
// `all_types.extend(args.drain(..));`.
|
||||||
all_types.extend(args);
|
all_types.extend(args);
|
||||||
} else {
|
} else {
|
||||||
if let Some(kind) = arg.type_.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
|
if let Some(kind) = arg.type_.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
|
||||||
{
|
{
|
||||||
all_types.insert((arg.type_.clone(), kind));
|
all_types.push(TypeWithKind::from((get_index_type(&arg.type_, vec![]), kind)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_types = match decl.output {
|
let mut ret_types = Vec::new();
|
||||||
|
match decl.output {
|
||||||
FnRetTy::Return(ref return_type) => {
|
FnRetTy::Return(ref return_type) => {
|
||||||
let mut ret = FxHashSet::default();
|
get_real_types(generics, return_type, tcx, 0, &mut ret_types);
|
||||||
get_real_types(generics, return_type, tcx, 0, &mut ret);
|
if ret_types.is_empty() {
|
||||||
if ret.is_empty() {
|
|
||||||
if let Some(kind) =
|
if let Some(kind) =
|
||||||
return_type.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
|
return_type.def_id_no_primitives().map(|did| tcx.def_kind(did).into())
|
||||||
{
|
{
|
||||||
ret.insert((return_type.clone(), kind));
|
ret_types.push(TypeWithKind::from((get_index_type(return_type, vec![]), kind)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret.into_iter().collect()
|
|
||||||
}
|
}
|
||||||
_ => Vec::new(),
|
_ => {}
|
||||||
};
|
};
|
||||||
(all_types.into_iter().collect(), ret_types)
|
(all_types, ret_types)
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ crate struct IndexItem {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
crate struct RenderType {
|
crate struct RenderType {
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
generics: Option<Vec<String>>,
|
generics: Option<Vec<TypeWithKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Full type of functions/methods in the search index.
|
/// Full type of functions/methods in the search index.
|
||||||
|
@ -2387,6 +2387,7 @@ fn item_ty_to_strs(ty: ItemType) -> (&'static str, &'static str) {
|
||||||
ItemType::ProcAttribute => ("attributes", "Attribute Macros"),
|
ItemType::ProcAttribute => ("attributes", "Attribute Macros"),
|
||||||
ItemType::ProcDerive => ("derives", "Derive Macros"),
|
ItemType::ProcDerive => ("derives", "Derive Macros"),
|
||||||
ItemType::TraitAlias => ("trait-aliases", "Trait aliases"),
|
ItemType::TraitAlias => ("trait-aliases", "Trait aliases"),
|
||||||
|
ItemType::Generic => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -299,10 +299,10 @@ window.initSearch = function(rawSearchIndex) {
|
||||||
var elems = Object.create(null);
|
var elems = Object.create(null);
|
||||||
var elength = obj[GENERICS_DATA].length;
|
var elength = obj[GENERICS_DATA].length;
|
||||||
for (var x = 0; x < elength; ++x) {
|
for (var x = 0; x < elength; ++x) {
|
||||||
if (!elems[obj[GENERICS_DATA][x]]) {
|
if (!elems[obj[GENERICS_DATA][x][NAME]]) {
|
||||||
elems[obj[GENERICS_DATA][x]] = 0;
|
elems[obj[GENERICS_DATA][x][NAME]] = 0;
|
||||||
}
|
}
|
||||||
elems[obj[GENERICS_DATA][x]] += 1;
|
elems[obj[GENERICS_DATA][x][NAME]] += 1;
|
||||||
}
|
}
|
||||||
var total = 0;
|
var total = 0;
|
||||||
var done = 0;
|
var done = 0;
|
||||||
|
@ -345,6 +345,7 @@ window.initSearch = function(rawSearchIndex) {
|
||||||
// Check for type name and type generics (if any).
|
// Check for type name and type generics (if any).
|
||||||
function checkType(obj, val, literalSearch) {
|
function checkType(obj, val, literalSearch) {
|
||||||
var lev_distance = MAX_LEV_DISTANCE + 1;
|
var lev_distance = MAX_LEV_DISTANCE + 1;
|
||||||
|
var tmp_lev = MAX_LEV_DISTANCE + 1;
|
||||||
var len, x, firstGeneric;
|
var len, x, firstGeneric;
|
||||||
if (obj[NAME] === val.name) {
|
if (obj[NAME] === val.name) {
|
||||||
if (literalSearch) {
|
if (literalSearch) {
|
||||||
|
@ -354,10 +355,10 @@ window.initSearch = function(rawSearchIndex) {
|
||||||
var elems = Object.create(null);
|
var elems = Object.create(null);
|
||||||
len = obj[GENERICS_DATA].length;
|
len = obj[GENERICS_DATA].length;
|
||||||
for (x = 0; x < len; ++x) {
|
for (x = 0; x < len; ++x) {
|
||||||
if (!elems[obj[GENERICS_DATA][x]]) {
|
if (!elems[obj[GENERICS_DATA][x][NAME]]) {
|
||||||
elems[obj[GENERICS_DATA][x]] = 0;
|
elems[obj[GENERICS_DATA][x][NAME]] = 0;
|
||||||
}
|
}
|
||||||
elems[obj[GENERICS_DATA][x]] += 1;
|
elems[obj[GENERICS_DATA][x][NAME]] += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var allFound = true;
|
var allFound = true;
|
||||||
|
@ -382,7 +383,7 @@ window.initSearch = function(rawSearchIndex) {
|
||||||
// If the type has generics but don't match, then it won't return at this point.
|
// If the type has generics but don't match, then it won't return at this point.
|
||||||
// Otherwise, `checkGenerics` will return 0 and it'll return.
|
// Otherwise, `checkGenerics` will return 0 and it'll return.
|
||||||
if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
|
if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length !== 0) {
|
||||||
var tmp_lev = checkGenerics(obj, val);
|
tmp_lev = checkGenerics(obj, val);
|
||||||
if (tmp_lev <= MAX_LEV_DISTANCE) {
|
if (tmp_lev <= MAX_LEV_DISTANCE) {
|
||||||
return tmp_lev;
|
return tmp_lev;
|
||||||
}
|
}
|
||||||
|
@ -392,8 +393,8 @@ window.initSearch = function(rawSearchIndex) {
|
||||||
if ((!val.generics || val.generics.length === 0) &&
|
if ((!val.generics || val.generics.length === 0) &&
|
||||||
obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
|
obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
|
||||||
return obj[GENERICS_DATA].some(
|
return obj[GENERICS_DATA].some(
|
||||||
function(name) {
|
function(gen) {
|
||||||
return name === val.name;
|
return gen[NAME] === val.name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -404,17 +405,27 @@ window.initSearch = function(rawSearchIndex) {
|
||||||
// a levenshtein distance value that isn't *this* good so it goes
|
// a levenshtein distance value that isn't *this* good so it goes
|
||||||
// into the search results but not too high.
|
// into the search results but not too high.
|
||||||
lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
|
lev_distance = Math.ceil((checkGenerics(obj, val) + lev_distance) / 2);
|
||||||
} else if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
|
}
|
||||||
|
if (obj.length > GENERICS_DATA && obj[GENERICS_DATA].length > 0) {
|
||||||
// We can check if the type we're looking for is inside the generics!
|
// We can check if the type we're looking for is inside the generics!
|
||||||
var olength = obj[GENERICS_DATA].length;
|
var olength = obj[GENERICS_DATA].length;
|
||||||
for (x = 0; x < olength; ++x) {
|
for (x = 0; x < olength; ++x) {
|
||||||
lev_distance = Math.min(levenshtein(obj[GENERICS_DATA][x], val.name),
|
tmp_lev = Math.min(levenshtein(obj[GENERICS_DATA][x][NAME], val.name), tmp_lev);
|
||||||
lev_distance);
|
}
|
||||||
|
if (tmp_lev !== 0) {
|
||||||
|
// If we didn't find a good enough result, we go check inside the generics of
|
||||||
|
// the generics.
|
||||||
|
for (x = 0; x < olength && tmp_lev !== 0; ++x) {
|
||||||
|
tmp_lev = Math.min(
|
||||||
|
checkType(obj[GENERICS_DATA][x], val, literalSearch),
|
||||||
|
tmp_lev
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now whatever happens, the returned distance is "less good" so we should mark it
|
// Now whatever happens, the returned distance is "less good" so we should mark it
|
||||||
// as such, and so we add 1 to the distance to make it "less good".
|
// as such, and so we add 1 to the distance to make it "less good".
|
||||||
return lev_distance + 1;
|
return Math.min(lev_distance, tmp_lev) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function findArg(obj, val, literalSearch, typeFilter) {
|
function findArg(obj, val, literalSearch, typeFilter) {
|
||||||
|
|
|
@ -697,6 +697,7 @@ impl FromWithTcx<ItemType> for ItemKind {
|
||||||
TraitAlias => ItemKind::TraitAlias,
|
TraitAlias => ItemKind::TraitAlias,
|
||||||
ProcAttribute => ItemKind::ProcAttribute,
|
ProcAttribute => ItemKind::ProcAttribute,
|
||||||
ProcDerive => ItemKind::ProcDerive,
|
ProcDerive => ItemKind::ProcDerive,
|
||||||
|
Generic => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,8 @@ const QUERY = [
|
||||||
'"P"',
|
'"P"',
|
||||||
'P',
|
'P',
|
||||||
'"ExtraCreditStructMulti<ExtraCreditInnerMulti, ExtraCreditInnerMulti>"',
|
'"ExtraCreditStructMulti<ExtraCreditInnerMulti, ExtraCreditInnerMulti>"',
|
||||||
|
'TraitCat',
|
||||||
|
'TraitDog',
|
||||||
];
|
];
|
||||||
|
|
||||||
const EXPECTED = [
|
const EXPECTED = [
|
||||||
|
@ -30,9 +32,11 @@ const EXPECTED = [
|
||||||
{
|
{
|
||||||
'returned': [
|
'returned': [
|
||||||
{ 'path': 'generics', 'name': 'alef' },
|
{ 'path': 'generics', 'name': 'alef' },
|
||||||
|
{ 'path': 'generics', 'name': 'bet' },
|
||||||
],
|
],
|
||||||
'in_args': [
|
'in_args': [
|
||||||
{ 'path': 'generics', 'name': 'alpha' },
|
{ 'path': 'generics', 'name': 'alpha' },
|
||||||
|
{ 'path': 'generics', 'name': 'beta' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -41,4 +45,14 @@ const EXPECTED = [
|
||||||
],
|
],
|
||||||
'returned': [],
|
'returned': [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'in_args': [
|
||||||
|
{ 'path': 'generics', 'name': 'gamma' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'in_args': [
|
||||||
|
{ 'path': 'generics', 'name': 'gamma' },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
@ -19,3 +19,8 @@ pub fn extracreditlabhomework(
|
||||||
pub fn redherringmatchforextracredit(
|
pub fn redherringmatchforextracredit(
|
||||||
_param: ExtraCreditStructMulti<ExtraCreditInnerMulti, ()>
|
_param: ExtraCreditStructMulti<ExtraCreditInnerMulti, ()>
|
||||||
) { loop {} }
|
) { loop {} }
|
||||||
|
|
||||||
|
pub trait TraitCat {}
|
||||||
|
pub trait TraitDog {}
|
||||||
|
|
||||||
|
pub fn gamma<T: TraitCat + TraitDog>(t: T) {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue