Merge from rustc
This commit is contained in:
commit
b8ee38d3e8
322 changed files with 5569 additions and 3047 deletions
|
|
@ -29,7 +29,7 @@ ENV PATH="/node/bin:${PATH}"
|
|||
|
||||
# Install es-check
|
||||
# Pin its version to prevent unrelated CI failures due to future es-check versions.
|
||||
RUN npm install es-check@6.1.1 eslint@8.6.0 -g
|
||||
RUN npm install es-check@6.1.1 eslint@8.6.0 typescript@5.7.3 -g
|
||||
|
||||
COPY scripts/sccache.sh /scripts/
|
||||
RUN sh /scripts/sccache.sh
|
||||
|
|
@ -68,4 +68,5 @@ ENV SCRIPT \
|
|||
es-check es2019 ../src/librustdoc/html/static/js/*.js && \
|
||||
eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \
|
||||
eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \
|
||||
eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js
|
||||
eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js && \
|
||||
tsc --project ../src/librustdoc/html/static/js/tsconfig.json
|
||||
|
|
|
|||
|
|
@ -43,10 +43,6 @@ runners:
|
|||
os: windows-2022-8core-32gb
|
||||
<<: *base-job
|
||||
|
||||
- &job-windows-16c
|
||||
os: windows-2022-16core-64gb
|
||||
<<: *base-job
|
||||
|
||||
- &job-aarch64-linux
|
||||
# Free some disk space to avoid running out of space during the build.
|
||||
free_disk: true
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 82a4a49789bc96db1a1b2a210b4c5ed7c9ef0c0d
|
||||
Subproject commit fa312a343fbff01bc6cef393e326817f70719813
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit d56e0f3a0656b7702ca466d4b191e16c28262b82
|
||||
Subproject commit 4ed5a1a4a2a7ecc2e529a5baaef04f7bc7917eda
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 625b200e5b33a5af35589db0bc454203a3d46d20
|
||||
Subproject commit bc2298865544695c63454fc1f9f98a3dc22e9948
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 293af991003772bdccf2d6b980182d84dd055942
|
||||
Subproject commit 93b921c7d3213d38d920f7f905a3bec093d2217d
|
||||
|
|
@ -58,7 +58,7 @@ impl rustc_driver::Callbacks for MyCallbacks {
|
|||
fn after_crate_root_parsing(
|
||||
&mut self,
|
||||
_compiler: &Compiler,
|
||||
krate: &rustc_ast::Crate,
|
||||
krate: &mut rustc_ast::Crate,
|
||||
) -> Compilation {
|
||||
for item in &krate.items {
|
||||
println!("{}", item_to_string(&item));
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ impl rustc_driver::Callbacks for MyCallbacks {
|
|||
fn after_crate_root_parsing(
|
||||
&mut self,
|
||||
_compiler: &Compiler,
|
||||
krate: &rustc_ast::Crate,
|
||||
krate: &mut rustc_ast::Crate,
|
||||
) -> Compilation {
|
||||
for item in &krate.items {
|
||||
println!("{}", item_to_string(&item));
|
||||
|
|
|
|||
|
|
@ -303,7 +303,8 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
|
|||
return kw::Underscore;
|
||||
}
|
||||
PatKind::Binding(_, _, ident, _) => return ident.name,
|
||||
PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p),
|
||||
PatKind::TupleStruct(ref p, ..)
|
||||
| PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p),
|
||||
PatKind::Or(pats) => {
|
||||
pats.iter().map(|p| name_from_pat(p).to_string()).collect::<Vec<String>>().join(" | ")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
|
|||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatKind, QPath};
|
||||
use rustc_hir::{
|
||||
ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath,
|
||||
};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
|
|
@ -191,17 +193,21 @@ impl SpanMapVisitor<'_> {
|
|||
}
|
||||
|
||||
fn handle_pat(&mut self, p: &Pat<'_>) {
|
||||
let mut check_qpath = |qpath, hir_id| match qpath {
|
||||
QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => {
|
||||
self.infer_id(path.hir_id, Some(hir_id), qpath.span());
|
||||
}
|
||||
QPath::Resolved(_, path) => self.handle_path(path),
|
||||
_ => {}
|
||||
};
|
||||
match p.kind {
|
||||
PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p),
|
||||
PatKind::Struct(qpath, _, _)
|
||||
| PatKind::TupleStruct(qpath, _, _)
|
||||
| PatKind::Path(qpath) => match qpath {
|
||||
QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => {
|
||||
self.infer_id(path.hir_id, Some(p.hir_id), qpath.span());
|
||||
}
|
||||
QPath::Resolved(_, path) => self.handle_path(path),
|
||||
_ => {}
|
||||
},
|
||||
PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => {
|
||||
check_qpath(qpath, p.hir_id)
|
||||
}
|
||||
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => {
|
||||
check_qpath(*qpath, *hir_id)
|
||||
}
|
||||
PatKind::Or(pats) => {
|
||||
for pat in pats {
|
||||
self.handle_pat(pat);
|
||||
|
|
|
|||
|
|
@ -3,13 +3,9 @@
|
|||
These JavaScript files are incorporated into the rustdoc binary at build time,
|
||||
and are minified and written to the filesystem as part of the doc build process.
|
||||
|
||||
We use the [Closure Compiler](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler)
|
||||
We use the [TypeScript Compiler](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html)
|
||||
dialect of JSDoc to comment our code and annotate params and return types.
|
||||
To run a check:
|
||||
|
||||
./x.py doc library/std
|
||||
npm i -g google-closure-compiler
|
||||
google-closure-compiler -W VERBOSE \
|
||||
build/<YOUR PLATFORM>/doc/{search-index*.js,crates*.js} \
|
||||
src/librustdoc/html/static/js/{search.js,main.js,storage.js} \
|
||||
--externs src/librustdoc/html/static/js/externs.js >/dev/null
|
||||
npm i -g typescript
|
||||
tsc --project tsconfig.json
|
||||
|
|
|
|||
|
|
@ -1,270 +0,0 @@
|
|||
// This file contains type definitions that are processed by the Closure Compiler but are
|
||||
// not put into the JavaScript we include as part of the documentation. It is used for
|
||||
// type checking. See README.md in this directory for more info.
|
||||
|
||||
/* eslint-disable */
|
||||
let searchState;
|
||||
function initSearch(searchIndex){}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* name: string,
|
||||
* id: number|null,
|
||||
* fullPath: Array<string>,
|
||||
* pathWithoutLast: Array<string>,
|
||||
* pathLast: string,
|
||||
* generics: Array<QueryElement>,
|
||||
* bindings: Map<number, Array<QueryElement>>,
|
||||
* }}
|
||||
*/
|
||||
let QueryElement;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* pos: number,
|
||||
* totalElems: number,
|
||||
* typeFilter: (null|string),
|
||||
* userQuery: string,
|
||||
* isInBinding: (null|string),
|
||||
* }}
|
||||
*/
|
||||
let ParserState;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* original: string,
|
||||
* userQuery: string,
|
||||
* typeFilter: number,
|
||||
* elems: Array<QueryElement>,
|
||||
* args: Array<QueryElement>,
|
||||
* returned: Array<QueryElement>,
|
||||
* foundElems: number,
|
||||
* totalElems: number,
|
||||
* literalSearch: boolean,
|
||||
* hasReturnArrow: boolean,
|
||||
* corrections: Array<{from: string, to: integer}> | null,
|
||||
* typeFingerprint: Uint32Array,
|
||||
* error: Array<string> | null,
|
||||
* }}
|
||||
*/
|
||||
let ParsedQuery;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* crate: string,
|
||||
* desc: string,
|
||||
* id: number,
|
||||
* name: string,
|
||||
* normalizedName: string,
|
||||
* parent: (Object|null|undefined),
|
||||
* path: string,
|
||||
* ty: (Number|null|number),
|
||||
* type: FunctionSearchType?
|
||||
* }}
|
||||
*/
|
||||
let Row;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* in_args: Array<Object>,
|
||||
* returned: Array<Object>,
|
||||
* others: Array<Object>,
|
||||
* query: ParsedQuery,
|
||||
* }}
|
||||
*/
|
||||
let ResultsTable;
|
||||
|
||||
/**
|
||||
* @typedef {Map<String, ResultObject>}
|
||||
*/
|
||||
let Results;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* desc: string,
|
||||
* displayPath: string,
|
||||
* fullPath: string,
|
||||
* href: string,
|
||||
* id: number,
|
||||
* lev: number,
|
||||
* name: string,
|
||||
* normalizedName: string,
|
||||
* parent: (Object|undefined),
|
||||
* path: string,
|
||||
* ty: number,
|
||||
* type: FunctionSearchType?,
|
||||
* displayType: Promise<Array<Array<string>>>|null,
|
||||
* displayTypeMappedNames: Promise<Array<[string, Array<string>]>>|null,
|
||||
* }}
|
||||
*/
|
||||
let ResultObject;
|
||||
|
||||
/**
|
||||
* A pair of [inputs, outputs], or 0 for null. This is stored in the search index.
|
||||
* The JavaScript deserializes this into FunctionSearchType.
|
||||
*
|
||||
* Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null`
|
||||
* because `null` is four bytes while `0` is one byte.
|
||||
*
|
||||
* An input or output can be encoded as just a number if there is only one of them, AND
|
||||
* it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had
|
||||
* a function with a single output, and that output had a single generic:
|
||||
*
|
||||
* fn something() -> Result<usize, usize>
|
||||
*
|
||||
* If output was allowed to be any RawFunctionType, it would look like thi
|
||||
*
|
||||
* [[], [50, [3, 3]]]
|
||||
*
|
||||
* The problem is that the above output could be interpreted as either a type with ID 50 and two
|
||||
* generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second
|
||||
* with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing
|
||||
* in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)`
|
||||
* is used instead of `(RawFunctionType|Array<RawFunctionType>)`.
|
||||
*
|
||||
* The output can be skipped if it's actually unit and there's no type constraints. If thi
|
||||
* function accepts constrained generics, then the output will be unconditionally emitted, and
|
||||
* after it will come a list of trait constraints. The position of the item in the list will
|
||||
* determine which type parameter it is. For example:
|
||||
*
|
||||
* [1, 2, 3, 4, 5]
|
||||
* ^ ^ ^ ^ ^
|
||||
* | | | | - generic parameter (-3) of trait 5
|
||||
* | | | - generic parameter (-2) of trait 4
|
||||
* | | - generic parameter (-1) of trait 3
|
||||
* | - this function returns a single value (type 2)
|
||||
* - this function takes a single input parameter (type 1)
|
||||
*
|
||||
* Or, for a less contrived version:
|
||||
*
|
||||
* [[[4, -1], 3], [[5, -1]], 11]
|
||||
* -^^^^^^^---- ^^^^^^^ ^^
|
||||
* | | | - generic parameter, roughly `where -1: 11`
|
||||
* | | | since -1 is the type parameter and 11 the trait
|
||||
* | | - function output 5<-1>
|
||||
* | - the overall function signature is something like
|
||||
* | `fn(4<-1>, 3) -> 5<-1> where -1: 11`
|
||||
* - function input, corresponds roughly to 4<-1>
|
||||
* 4 is an index into the `p` array for a type
|
||||
* -1 is the generic parameter, given by 11
|
||||
*
|
||||
* If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like
|
||||
* function inputs and outputs:
|
||||
*
|
||||
* [-1, -1, [4, 3]]
|
||||
* ^^^^^^ where -1: 4 + 3
|
||||
*
|
||||
* If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array
|
||||
* even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in
|
||||
* favor of `4 + 3`:
|
||||
*
|
||||
* [-1, -1, [[4, 3]]]
|
||||
* ^^^^^^^^ where -1: 4 + 3
|
||||
*
|
||||
* [-1, -1, [5, [4, 3]]]
|
||||
* ^^^^^^^^^^^ where -1: 5, -2: 4 + 3
|
||||
*
|
||||
* If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i
|
||||
* implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0.
|
||||
*
|
||||
* @typedef {(
|
||||
* 0 |
|
||||
* [(number|Array<RawFunctionType>)] |
|
||||
* [(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] |
|
||||
* Array<(number|Array<RawFunctionType>)>
|
||||
* )}
|
||||
*/
|
||||
let RawFunctionSearchType;
|
||||
|
||||
/**
|
||||
* A single function input or output type. This is either a single path ID, or a pair of
|
||||
* [path ID, generics].
|
||||
*
|
||||
* Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null`
|
||||
* because `null` is four bytes while `0` is one byte.
|
||||
*
|
||||
* @typedef {number | [number, Array<RawFunctionType>]}
|
||||
*/
|
||||
let RawFunctionType;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* inputs: Array<FunctionType>,
|
||||
* output: Array<FunctionType>,
|
||||
* where_clause: Array<Array<FunctionType>>,
|
||||
* }}
|
||||
*/
|
||||
let FunctionSearchType;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* id: (null|number),
|
||||
* ty: number,
|
||||
* generics: Array<FunctionType>,
|
||||
* bindings: Map<integer, Array<FunctionType>>,
|
||||
* }}
|
||||
*/
|
||||
let FunctionType;
|
||||
|
||||
/**
|
||||
* The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f`
|
||||
* are arrays with the same length. `q`, `a`, and `c` use a sparse
|
||||
* representation for compactness.
|
||||
*
|
||||
* `n[i]` contains the name of an item.
|
||||
*
|
||||
* `t[i]` contains the type of that item
|
||||
* (as a string of characters that represent an offset in `itemTypes`).
|
||||
*
|
||||
* `d[i]` contains the description of that item.
|
||||
*
|
||||
* `q` contains the full paths of the items. For compactness, it is a set of
|
||||
* (index, path) pairs used to create a map. If a given index `i` is
|
||||
* not present, this indicates "same as the last index present".
|
||||
*
|
||||
* `i[i]` contains an item's parent, usually a module. For compactness,
|
||||
* it is a set of indexes into the `p` array.
|
||||
*
|
||||
* `f` contains function signatures, or `0` if the item isn't a function.
|
||||
* More information on how they're encoded can be found in rustc-dev-guide
|
||||
*
|
||||
* Functions are themselves encoded as arrays. The first item is a list of
|
||||
* types representing the function's inputs, and the second list item is a list
|
||||
* of types representing the function's output. Tuples are flattened.
|
||||
* Types are also represented as arrays; the first item is an index into the `p`
|
||||
* array, while the second is a list of types representing any generic parameters.
|
||||
*
|
||||
* b[i] contains an item's impl disambiguator. This is only present if an item
|
||||
* is defined in an impl block and, the impl block's type has more than one associated
|
||||
* item with the same name.
|
||||
*
|
||||
* `a` defines aliases with an Array of pairs: [name, offset], where `offset`
|
||||
* points into the n/t/d/q/i/f arrays.
|
||||
*
|
||||
* `doc` contains the description of the crate.
|
||||
*
|
||||
* `p` is a list of path/type pairs. It is used for parents and function parameters.
|
||||
* The first item is the type, the second is the name, the third is the visible path (if any) and
|
||||
* the fourth is the canonical path used for deduplication (if any).
|
||||
*
|
||||
* `r` is the canonical path used for deduplication of re-exported items.
|
||||
* It is not used for associated items like methods (that's the fourth element
|
||||
* of `p`) but is used for modules items like free functions.
|
||||
*
|
||||
* `c` is an array of item indices that are deprecated.
|
||||
* @typedef {{
|
||||
* doc: string,
|
||||
* a: Object,
|
||||
* n: Array<string>,
|
||||
* t: string,
|
||||
* d: Array<string>,
|
||||
* q: Array<[number, string]>,
|
||||
* i: Array<number>,
|
||||
* f: string,
|
||||
* p: Array<[number, string] | [number, string, number] | [number, string, number, number]>,
|
||||
* b: Array<[number, String]>,
|
||||
* c: Array<number>,
|
||||
* r: Array<[number, number]>,
|
||||
* }}
|
||||
*/
|
||||
let RawSearchIndexCrate;
|
||||
File diff suppressed because it is too large
Load diff
387
src/librustdoc/html/static/js/rustdoc.d.ts
vendored
Normal file
387
src/librustdoc/html/static/js/rustdoc.d.ts
vendored
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
// This file contains type definitions that are processed by the TypeScript Compiler but are
|
||||
// not put into the JavaScript we include as part of the documentation. It is used for
|
||||
// type checking. See README.md in this directory for more info.
|
||||
|
||||
/* eslint-disable */
|
||||
declare global {
|
||||
interface Window {
|
||||
/** Make the current theme easy to find */
|
||||
currentTheme: HTMLLinkElement|null;
|
||||
/** Used by the popover tooltip code. */
|
||||
RUSTDOC_TOOLTIP_HOVER_MS: number;
|
||||
/** Used by the popover tooltip code. */
|
||||
RUSTDOC_TOOLTIP_HOVER_EXIT_MS: number;
|
||||
/** Search engine data used by main.js and search.js */
|
||||
searchState: rustdoc.SearchState;
|
||||
/** Global option, with a long list of "../"'s */
|
||||
rootPath: string|null;
|
||||
/**
|
||||
* Currently opened crate.
|
||||
* As a multi-page application, we know this never changes once set.
|
||||
*/
|
||||
currentCrate: string|null;
|
||||
}
|
||||
interface HTMLElement {
|
||||
/** Used by the popover tooltip code. */
|
||||
TOOLTIP_FORCE_VISIBLE: boolean|undefined,
|
||||
/** Used by the popover tooltip code */
|
||||
TOOLTIP_HOVER_TIMEOUT: Timeout|undefined,
|
||||
}
|
||||
}
|
||||
|
||||
export = rustdoc;
|
||||
|
||||
declare namespace rustdoc {
|
||||
interface SearchState {
|
||||
rustdocToolbar: HTMLElement|null;
|
||||
loadingText: string;
|
||||
input: HTMLInputElement|null;
|
||||
title: string;
|
||||
titleBeforeSearch: string;
|
||||
timeout: number|null;
|
||||
currentTab: number;
|
||||
focusedByTab: [number|null, number|null, number|null];
|
||||
clearInputTimeout: function;
|
||||
outputElement: function(): HTMLElement|null;
|
||||
focus: function();
|
||||
defocus: function();
|
||||
showResults: function(HTMLElement|null|undefined);
|
||||
removeQueryParameters: function();
|
||||
hideResults: function();
|
||||
getQueryStringParams: function(): Object.<any, string>;
|
||||
origPlaceholder: string;
|
||||
setup: function();
|
||||
setLoadingSearch: function();
|
||||
descShards: Map<string, SearchDescShard[]>;
|
||||
loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise<string|null>;
|
||||
loadedDescShard: function(string, number, string);
|
||||
isDisplayed: function(): boolean,
|
||||
}
|
||||
|
||||
interface SearchDescShard {
|
||||
crate: string;
|
||||
promise: Promise<string[]>|null;
|
||||
resolve: function(string[])|null;
|
||||
shard: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A single parsed "atom" in a search query. For example,
|
||||
*
|
||||
* std::fmt::Formatter, Write -> Result<()>
|
||||
* ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
|
||||
* ┃ │ ┗ QueryElement { ┊
|
||||
* ┃ │ name: Result ┊
|
||||
* ┃ │ generics: [ ┊
|
||||
* ┃ │ QueryElement ┘
|
||||
* ┃ │ name: ()
|
||||
* ┃ │ ]
|
||||
* ┃ │ }
|
||||
* ┃ └ QueryElement {
|
||||
* ┃ name: Write
|
||||
* ┃ }
|
||||
* ┗ QueryElement {
|
||||
* name: Formatter
|
||||
* pathWithoutLast: std::fmt
|
||||
* }
|
||||
*/
|
||||
interface QueryElement {
|
||||
name: string,
|
||||
id: number|null,
|
||||
fullPath: Array<string>,
|
||||
pathWithoutLast: Array<string>,
|
||||
pathLast: string,
|
||||
normalizedPathLast: string,
|
||||
generics: Array<QueryElement>,
|
||||
bindings: Map<number, Array<QueryElement>>,
|
||||
typeFilter: number|null,
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as QueryElement, but bindings and typeFilter support strings
|
||||
*/
|
||||
interface ParserQueryElement {
|
||||
name: string,
|
||||
id: number|null,
|
||||
fullPath: Array<string>,
|
||||
pathWithoutLast: Array<string>,
|
||||
pathLast: string,
|
||||
normalizedPathLast: string,
|
||||
generics: Array<ParserQueryElement>,
|
||||
bindings: Map<string, Array<ParserQueryElement>>,
|
||||
bindingName: {name: string, generics: ParserQueryElement[]}|null,
|
||||
typeFilter: string|null,
|
||||
}
|
||||
|
||||
/**
|
||||
* Intermediate parser state. Discarded when parsing is done.
|
||||
*/
|
||||
interface ParserState {
|
||||
pos: number;
|
||||
length: number;
|
||||
totalElems: number;
|
||||
genericsElems: number;
|
||||
typeFilter: (null|string);
|
||||
userQuery: string;
|
||||
isInBinding: (null|{name: string, generics: ParserQueryElement[]});
|
||||
}
|
||||
|
||||
/**
|
||||
* A complete parsed query.
|
||||
*/
|
||||
interface ParsedQuery<T> {
|
||||
userQuery: string,
|
||||
elems: Array<T>,
|
||||
returned: Array<T>,
|
||||
foundElems: number,
|
||||
totalElems: number,
|
||||
literalSearch: boolean,
|
||||
hasReturnArrow: boolean,
|
||||
correction: string|null,
|
||||
proposeCorrectionFrom: string|null,
|
||||
proposeCorrectionTo: string|null,
|
||||
typeFingerprint: Uint32Array,
|
||||
error: Array<string> | null,
|
||||
}
|
||||
|
||||
/**
|
||||
* An entry in the search index database.
|
||||
*/
|
||||
interface Row {
|
||||
crate: string,
|
||||
descShard: SearchDescShard,
|
||||
id: number,
|
||||
name: string,
|
||||
normalizedName: string,
|
||||
word: string,
|
||||
parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined),
|
||||
path: string,
|
||||
ty: number,
|
||||
type?: FunctionSearchType
|
||||
}
|
||||
|
||||
/**
|
||||
* The viewmodel for the search engine results page.
|
||||
*/
|
||||
interface ResultsTable {
|
||||
in_args: Array<ResultObject>,
|
||||
returned: Array<ResultObject>,
|
||||
others: Array<ResultObject>,
|
||||
query: ParsedQuery,
|
||||
}
|
||||
|
||||
type Results = Map<String, ResultObject>;
|
||||
|
||||
/**
|
||||
* An annotated `Row`, used in the viewmodel.
|
||||
*/
|
||||
interface ResultObject {
|
||||
desc: string,
|
||||
displayPath: string,
|
||||
fullPath: string,
|
||||
href: string,
|
||||
id: number,
|
||||
dist: number,
|
||||
path_dist: number,
|
||||
name: string,
|
||||
normalizedName: string,
|
||||
word: string,
|
||||
index: number,
|
||||
parent: (Object|undefined),
|
||||
path: string,
|
||||
ty: number,
|
||||
type?: FunctionSearchType,
|
||||
paramNames?: string[],
|
||||
displayType: Promise<Array<Array<string>>>|null,
|
||||
displayTypeMappedNames: Promise<Array<[string, Array<string>]>>|null,
|
||||
item: Row,
|
||||
dontValidate?: boolean,
|
||||
}
|
||||
|
||||
/**
|
||||
* A pair of [inputs, outputs], or 0 for null. This is stored in the search index.
|
||||
* The JavaScript deserializes this into FunctionSearchType.
|
||||
*
|
||||
* Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null`
|
||||
* because `null` is four bytes while `0` is one byte.
|
||||
*
|
||||
* An input or output can be encoded as just a number if there is only one of them, AND
|
||||
* it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had
|
||||
* a function with a single output, and that output had a single generic:
|
||||
*
|
||||
* fn something() -> Result<usize, usize>
|
||||
*
|
||||
* If output was allowed to be any RawFunctionType, it would look like thi
|
||||
*
|
||||
* [[], [50, [3, 3]]]
|
||||
*
|
||||
* The problem is that the above output could be interpreted as either a type with ID 50 and two
|
||||
* generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second
|
||||
* with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing
|
||||
* in favor of the pair of types interpretation. This is why the `(number|Array<RawFunctionType>)`
|
||||
* is used instead of `(RawFunctionType|Array<RawFunctionType>)`.
|
||||
*
|
||||
* The output can be skipped if it's actually unit and there's no type constraints. If thi
|
||||
* function accepts constrained generics, then the output will be unconditionally emitted, and
|
||||
* after it will come a list of trait constraints. The position of the item in the list will
|
||||
* determine which type parameter it is. For example:
|
||||
*
|
||||
* [1, 2, 3, 4, 5]
|
||||
* ^ ^ ^ ^ ^
|
||||
* | | | | - generic parameter (-3) of trait 5
|
||||
* | | | - generic parameter (-2) of trait 4
|
||||
* | | - generic parameter (-1) of trait 3
|
||||
* | - this function returns a single value (type 2)
|
||||
* - this function takes a single input parameter (type 1)
|
||||
*
|
||||
* Or, for a less contrived version:
|
||||
*
|
||||
* [[[4, -1], 3], [[5, -1]], 11]
|
||||
* -^^^^^^^---- ^^^^^^^ ^^
|
||||
* | | | - generic parameter, roughly `where -1: 11`
|
||||
* | | | since -1 is the type parameter and 11 the trait
|
||||
* | | - function output 5<-1>
|
||||
* | - the overall function signature is something like
|
||||
* | `fn(4<-1>, 3) -> 5<-1> where -1: 11`
|
||||
* - function input, corresponds roughly to 4<-1>
|
||||
* 4 is an index into the `p` array for a type
|
||||
* -1 is the generic parameter, given by 11
|
||||
*
|
||||
* If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like
|
||||
* function inputs and outputs:
|
||||
*
|
||||
* [-1, -1, [4, 3]]
|
||||
* ^^^^^^ where -1: 4 + 3
|
||||
*
|
||||
* If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array
|
||||
* even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in
|
||||
* favor of `4 + 3`:
|
||||
*
|
||||
* [-1, -1, [[4, 3]]]
|
||||
* ^^^^^^^^ where -1: 4 + 3
|
||||
*
|
||||
* [-1, -1, [5, [4, 3]]]
|
||||
* ^^^^^^^^^^^ where -1: 5, -2: 4 + 3
|
||||
*
|
||||
* If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i
|
||||
* implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0.
|
||||
*/
|
||||
type RawFunctionSearchType =
|
||||
0 |
|
||||
[(number|Array<RawFunctionType>)] |
|
||||
[(number|Array<RawFunctionType>), (number|Array<RawFunctionType>)] |
|
||||
Array<(number|Array<RawFunctionType>)>
|
||||
;
|
||||
|
||||
/**
|
||||
* A single function input or output type. This is either a single path ID, or a pair of
|
||||
* [path ID, generics].
|
||||
*
|
||||
* Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null`
|
||||
* because `null` is four bytes while `0` is one byte.
|
||||
*/
|
||||
type RawFunctionType = number | [number, Array<RawFunctionType>];
|
||||
|
||||
/**
|
||||
* The type signature entry in the decoded search index.
|
||||
* (The "Raw" objects are encoded differently to save space in the JSON).
|
||||
*/
|
||||
interface FunctionSearchType {
|
||||
inputs: Array<FunctionType>,
|
||||
output: Array<FunctionType>,
|
||||
where_clause: Array<Array<FunctionType>>,
|
||||
}
|
||||
|
||||
/**
|
||||
* A decoded function type, made from real objects.
|
||||
* `ty` will be negative for generics, positive for types, and 0 for placeholders.
|
||||
*/
|
||||
interface FunctionType {
|
||||
id: null|number,
|
||||
ty: number|null,
|
||||
name?: string,
|
||||
path: string|null,
|
||||
exactPath: string|null,
|
||||
unboxFlag: boolean,
|
||||
generics: Array<FunctionType>,
|
||||
bindings: Map<number, Array<FunctionType>>,
|
||||
};
|
||||
|
||||
interface HighlightedFunctionType extends FunctionType {
|
||||
generics: HighlightedFunctionType[],
|
||||
bindings: Map<number, HighlightedFunctionType[]>,
|
||||
highlighted?: boolean;
|
||||
}
|
||||
|
||||
interface FingerprintableType {
|
||||
id: number|null;
|
||||
generics: FingerprintableType[];
|
||||
bindings: Map<number, FingerprintableType[]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f`
|
||||
* are arrays with the same length. `q`, `a`, and `c` use a sparse
|
||||
* representation for compactness.
|
||||
*
|
||||
* `n[i]` contains the name of an item.
|
||||
*
|
||||
* `t[i]` contains the type of that item
|
||||
* (as a string of characters that represent an offset in `itemTypes`).
|
||||
*
|
||||
* `d[i]` contains the description of that item.
|
||||
*
|
||||
* `q` contains the full paths of the items. For compactness, it is a set of
|
||||
* (index, path) pairs used to create a map. If a given index `i` is
|
||||
* not present, this indicates "same as the last index present".
|
||||
*
|
||||
* `i[i]` contains an item's parent, usually a module. For compactness,
|
||||
* it is a set of indexes into the `p` array.
|
||||
*
|
||||
* `f` contains function signatures, or `0` if the item isn't a function.
|
||||
* More information on how they're encoded can be found in rustc-dev-guide
|
||||
*
|
||||
* Functions are themselves encoded as arrays. The first item is a list of
|
||||
* types representing the function's inputs, and the second list item is a list
|
||||
* of types representing the function's output. Tuples are flattened.
|
||||
* Types are also represented as arrays; the first item is an index into the `p`
|
||||
* array, while the second is a list of types representing any generic parameters.
|
||||
*
|
||||
* b[i] contains an item's impl disambiguator. This is only present if an item
|
||||
* is defined in an impl block and, the impl block's type has more than one associated
|
||||
* item with the same name.
|
||||
*
|
||||
* `a` defines aliases with an Array of pairs: [name, offset], where `offset`
|
||||
* points into the n/t/d/q/i/f arrays.
|
||||
*
|
||||
* `doc` contains the description of the crate.
|
||||
*
|
||||
* `p` is a list of path/type pairs. It is used for parents and function parameters.
|
||||
* The first item is the type, the second is the name, the third is the visible path (if any) and
|
||||
* the fourth is the canonical path used for deduplication (if any).
|
||||
*
|
||||
* `r` is the canonical path used for deduplication of re-exported items.
|
||||
* It is not used for associated items like methods (that's the fourth element
|
||||
* of `p`) but is used for modules items like free functions.
|
||||
*
|
||||
* `c` is an array of item indices that are deprecated.
|
||||
*/
|
||||
type RawSearchIndexCrate = {
|
||||
doc: string,
|
||||
a: Object,
|
||||
n: Array<string>,
|
||||
t: string,
|
||||
D: string,
|
||||
e: string,
|
||||
q: Array<[number, string]>,
|
||||
i: string,
|
||||
f: string,
|
||||
p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>,
|
||||
b: Array<[number, String]>,
|
||||
c: string,
|
||||
r: Array<[number, number]>,
|
||||
P: Array<[number, string]>,
|
||||
};
|
||||
|
||||
type VlqData = VlqData[] | number;
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
/* global addClass, hasClass, removeClass, onEachLazy */
|
||||
|
||||
// Eventually fix this.
|
||||
// @ts-nocheck
|
||||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,9 @@
|
|||
/* global addClass, removeClass, onEach, onEachLazy */
|
||||
/* global MAIN_ID, getVar, getSettingsButton, getHelpButton */
|
||||
|
||||
// Eventually fix this.
|
||||
// @ts-nocheck
|
||||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@
|
|||
/* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */
|
||||
/* global updateLocalStorage, getVar */
|
||||
|
||||
// Eventually fix this.
|
||||
// @ts-nocheck
|
||||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
|
|
|
|||
|
|
@ -5,15 +5,28 @@
|
|||
// the page, so we don't see major layout changes during the load of the page.
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* @import * as rustdoc from "./rustdoc.d.ts";
|
||||
*/
|
||||
|
||||
const builtinThemes = ["light", "dark", "ayu"];
|
||||
const darkThemes = ["dark", "ayu"];
|
||||
window.currentTheme = document.getElementById("themeStyle");
|
||||
window.currentTheme = (function() {
|
||||
const currentTheme = document.getElementById("themeStyle");
|
||||
return currentTheme instanceof HTMLLinkElement ? currentTheme : null;
|
||||
})();
|
||||
|
||||
const settingsDataset = (function() {
|
||||
const settingsElement = document.getElementById("default-settings");
|
||||
return settingsElement && settingsElement.dataset ? settingsElement.dataset : null;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Get a configuration value. If it's not set, get the default.
|
||||
*
|
||||
* @param {string} settingName
|
||||
* @returns
|
||||
*/
|
||||
function getSettingValue(settingName) {
|
||||
const current = getCurrentValue(settingName);
|
||||
if (current === null && settingsDataset !== null) {
|
||||
|
|
@ -29,17 +42,39 @@ function getSettingValue(settingName) {
|
|||
|
||||
const localStoredTheme = getSettingValue("theme");
|
||||
|
||||
/**
|
||||
* Check if a DOM Element has the given class set.
|
||||
* If `elem` is null, returns false.
|
||||
*
|
||||
* @param {HTMLElement|null} elem
|
||||
* @param {string} className
|
||||
* @returns {boolean}
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function hasClass(elem, className) {
|
||||
return elem && elem.classList && elem.classList.contains(className);
|
||||
return !!elem && !!elem.classList && elem.classList.contains(className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to a DOM Element. If `elem` is null,
|
||||
* does nothing. This function is idempotent.
|
||||
*
|
||||
* @param {HTMLElement|null} elem
|
||||
* @param {string} className
|
||||
*/
|
||||
function addClass(elem, className) {
|
||||
if (elem && elem.classList) {
|
||||
elem.classList.add(className);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a class from a DOM Element. If `elem` is null,
|
||||
* does nothing. This function is idempotent.
|
||||
*
|
||||
* @param {HTMLElement|null} elem
|
||||
* @param {string} className
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function removeClass(elem, className) {
|
||||
if (elem && elem.classList) {
|
||||
|
|
@ -49,8 +84,8 @@ function removeClass(elem, className) {
|
|||
|
||||
/**
|
||||
* Run a callback for every element of an Array.
|
||||
* @param {Array<?>} arr - The array to iterate over
|
||||
* @param {function(?)} func - The callback
|
||||
* @param {Array<?>} arr - The array to iterate over
|
||||
* @param {function(?): boolean|undefined} func - The callback
|
||||
*/
|
||||
function onEach(arr, func) {
|
||||
for (const elem of arr) {
|
||||
|
|
@ -67,8 +102,8 @@ function onEach(arr, func) {
|
|||
* or a "live" NodeList while modifying it can be very slow.
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/NodeList
|
||||
* @param {NodeList<?>|HTMLCollection<?>} lazyArray - An array to iterate over
|
||||
* @param {function(?)} func - The callback
|
||||
* @param {NodeList|HTMLCollection} lazyArray - An array to iterate over
|
||||
* @param {function(?): boolean} func - The callback
|
||||
*/
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function onEachLazy(lazyArray, func) {
|
||||
|
|
@ -77,6 +112,15 @@ function onEachLazy(lazyArray, func) {
|
|||
func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a configuration value. This uses localstorage,
|
||||
* with a `rustdoc-` prefix, to avoid clashing with other
|
||||
* web apps that may be running in the same domain (for example, mdBook).
|
||||
* If localStorage is disabled, this function does nothing.
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
*/
|
||||
function updateLocalStorage(name, value) {
|
||||
try {
|
||||
window.localStorage.setItem("rustdoc-" + name, value);
|
||||
|
|
@ -85,6 +129,15 @@ function updateLocalStorage(name, value) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a configuration value. If localStorage is disabled,
|
||||
* this function returns null. If the setting was never
|
||||
* changed by the user, it also returns null; if you want to
|
||||
* be able to use a default value, call `getSettingValue` instead.
|
||||
*
|
||||
* @param {string} name
|
||||
* @returns {string|null}
|
||||
*/
|
||||
function getCurrentValue(name) {
|
||||
try {
|
||||
return window.localStorage.getItem("rustdoc-" + name);
|
||||
|
|
@ -93,19 +146,29 @@ function getCurrentValue(name) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get a value from the rustdoc-vars div, which is used to convey data from
|
||||
// Rust to the JS. If there is no such element, return null.
|
||||
const getVar = (function getVar(name) {
|
||||
/**
|
||||
* Get a value from the rustdoc-vars div, which is used to convey data from
|
||||
* Rust to the JS. If there is no such element, return null.
|
||||
*
|
||||
* @param {string} name
|
||||
* @returns {string|null}
|
||||
*/
|
||||
function getVar(name) {
|
||||
const el = document.querySelector("head > meta[name='rustdoc-vars']");
|
||||
return el ? el.attributes["data-" + name].value : null;
|
||||
});
|
||||
return el ? el.getAttribute("data-" + name) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the current theme.
|
||||
* @param {string|null} newThemeName
|
||||
* @param {boolean} saveTheme
|
||||
*/
|
||||
function switchTheme(newThemeName, saveTheme) {
|
||||
const themeNames = getVar("themes").split(",").filter(t => t);
|
||||
const themeNames = (getVar("themes") || "").split(",").filter(t => t);
|
||||
themeNames.push(...builtinThemes);
|
||||
|
||||
// Ensure that the new theme name is among the defined themes
|
||||
if (themeNames.indexOf(newThemeName) === -1) {
|
||||
if (newThemeName === null || themeNames.indexOf(newThemeName) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +181,7 @@ function switchTheme(newThemeName, saveTheme) {
|
|||
document.documentElement.setAttribute("data-theme", newThemeName);
|
||||
|
||||
if (builtinThemes.indexOf(newThemeName) !== -1) {
|
||||
if (window.currentTheme) {
|
||||
if (window.currentTheme && window.currentTheme.parentNode) {
|
||||
window.currentTheme.parentNode.removeChild(window.currentTheme);
|
||||
window.currentTheme = null;
|
||||
}
|
||||
|
|
@ -130,7 +193,10 @@ function switchTheme(newThemeName, saveTheme) {
|
|||
// rendering, but if we are done, it would blank the page.
|
||||
if (document.readyState === "loading") {
|
||||
document.write(`<link rel="stylesheet" id="themeStyle" href="${newHref}">`);
|
||||
window.currentTheme = document.getElementById("themeStyle");
|
||||
window.currentTheme = (function() {
|
||||
const currentTheme = document.getElementById("themeStyle");
|
||||
return currentTheme instanceof HTMLLinkElement ? currentTheme : null;
|
||||
})();
|
||||
} else {
|
||||
window.currentTheme = document.createElement("link");
|
||||
window.currentTheme.rel = "stylesheet";
|
||||
|
|
@ -179,11 +245,13 @@ const updateTheme = (function() {
|
|||
return updateTheme;
|
||||
})();
|
||||
|
||||
// @ts-ignore
|
||||
if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) {
|
||||
// update the preferred dark theme if the user is already using a dark theme
|
||||
// See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732
|
||||
if (getSettingValue("use-system-theme") === null
|
||||
&& getSettingValue("preferred-dark-theme") === null
|
||||
&& localStoredTheme !== null
|
||||
&& darkThemes.indexOf(localStoredTheme) >= 0) {
|
||||
updateLocalStorage("preferred-dark-theme", localStoredTheme);
|
||||
}
|
||||
|
|
|
|||
15
src/librustdoc/html/static/js/tsconfig.json
Normal file
15
src/librustdoc/html/static/js/tsconfig.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2023",
|
||||
"module": "esnext",
|
||||
"rootDir": "./",
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"typeAcquisition": {
|
||||
"include": ["./rustdoc.d.ts"]
|
||||
}
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
|
|||
PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
|
||||
PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
|
||||
PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
|
||||
PatKind::Path(_) | PatKind::Expr(_) => true,
|
||||
PatKind::Expr(_) => true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
|
|||
&& let Some(future_trait) = cx.tcx.lang_items().future_trait()
|
||||
&& let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send)
|
||||
{
|
||||
let preds = cx.tcx.explicit_item_super_predicates(def_id);
|
||||
let preds = cx.tcx.explicit_item_self_bounds(def_id);
|
||||
let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| {
|
||||
p.as_trait_clause()
|
||||
.is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
|
|||
use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
|
||||
|
|
@ -292,7 +292,12 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo
|
|||
// Only do the check if the type is "spelled out" in the pattern
|
||||
if !matches!(
|
||||
pat.kind,
|
||||
PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..)
|
||||
PatKind::Struct(..)
|
||||
| PatKind::TupleStruct(..)
|
||||
| PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(..),
|
||||
..
|
||||
},)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -68,7 +68,7 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<HirId> {
|
|||
}
|
||||
|
||||
fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
if let PatKind::Path(QPath::Resolved(_, path)) = arm.pat.kind
|
||||
if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind
|
||||
&& let Some(def_id) = path.res.opt_def_id()
|
||||
// Since it comes from a pattern binding, we need to get the parent to actually match
|
||||
// against it.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use clippy_utils::{
|
|||
};
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, Expr, HirId, Pat, PatKind};
|
||||
use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
@ -119,7 +119,11 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
|||
}
|
||||
match arm.pat.kind {
|
||||
PatKind::Binding(..) | PatKind::Wild => true,
|
||||
PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(qpath),
|
||||
hir_id,
|
||||
..
|
||||
}) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_ast::BindingMode;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, Path, QPath};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::symbol::Ident;
|
||||
|
|
@ -60,7 +60,16 @@ pub(crate) fn check_match(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Exp
|
|||
/// accepted.
|
||||
fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool, must_match_err: bool) -> bool {
|
||||
match pat.kind {
|
||||
PatKind::Wild | PatKind::Path(..) | PatKind::Binding(_, _, _, None) if can_be_wild => true,
|
||||
PatKind::Wild
|
||||
| PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(_),
|
||||
..
|
||||
})
|
||||
| PatKind::Binding(_, _, _, None)
|
||||
if can_be_wild =>
|
||||
{
|
||||
true
|
||||
},
|
||||
PatKind::TupleStruct(qpath, ..) => {
|
||||
is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, ResultErr};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, Pat, PatKind};
|
||||
use rustc_hir::{Arm, Expr, Pat, PatExpr, PatExprKind, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::sym;
|
||||
|
|
@ -89,7 +89,11 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<(&
|
|||
if arms.len() == 2
|
||||
&& arms.iter().all(|arm| arm.guard.is_none())
|
||||
&& let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind {
|
||||
PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
|
||||
PatKind::Expr(PatExpr {
|
||||
hir_id,
|
||||
kind: PatExprKind::Path(qpath),
|
||||
..
|
||||
}) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, [pat], _) => {
|
||||
matches!(pat.kind, PatKind::Wild)
|
||||
&& is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath};
|
||||
use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::{SyntaxContext, sym};
|
||||
|
||||
|
|
@ -256,9 +256,11 @@ pub(super) fn try_parse_pattern<'tcx>(
|
|||
match pat.kind {
|
||||
PatKind::Wild => Some(OptionPat::Wild),
|
||||
PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
|
||||
PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => {
|
||||
Some(OptionPat::None)
|
||||
},
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(qpath),
|
||||
hir_id,
|
||||
..
|
||||
}) if is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone) => Some(OptionPat::None),
|
||||
PatKind::TupleStruct(ref qpath, [pattern], _)
|
||||
if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath};
|
||||
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatExpr, PatExprKind, PatKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr:
|
|||
fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
matches!(
|
||||
arm.pat.kind,
|
||||
PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone)
|
||||
PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_arena::DroplessArena;
|
|||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd};
|
||||
use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExpr, PatExprKind, PatKind, RangeEnd};
|
||||
use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty;
|
||||
|
|
@ -292,7 +292,11 @@ impl<'a> NormalizedPat<'a> {
|
|||
Self::Tuple(var_id, pats)
|
||||
},
|
||||
PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))),
|
||||
PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()),
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(path),
|
||||
hir_id,
|
||||
..
|
||||
}) => Self::Path(cx.qpath_res(path, *hir_id).opt_def_id()),
|
||||
PatKind::Tuple(pats, wild_idx) => {
|
||||
let field_count = match cx.typeck_results().pat_ty(pat).kind() {
|
||||
ty::Tuple(subs) => subs.len(),
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item;
|
|||
use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind};
|
||||
use rustc_hir::{Arm, Expr, PatExpr, PatExprKind, PatKind, PathSegment, QPath, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, VariantDef};
|
||||
use rustc_span::sym;
|
||||
|
|
@ -60,8 +60,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
|
|||
// covered by the set of guards that cover it, but that's really hard to do.
|
||||
recurse_or_patterns(arm.pat, |pat| {
|
||||
let path = match &peel_hir_pat_refs(pat).0.kind {
|
||||
PatKind::Path(path) => {
|
||||
let id = match cx.qpath_res(path, pat.hir_id) {
|
||||
PatKind::Expr(PatExpr {
|
||||
hir_id,
|
||||
kind: PatExprKind::Path(path),
|
||||
..
|
||||
}) => {
|
||||
// FIXME(clippy): don't you want to use the hir id of the peeled pat?
|
||||
let id = match cx.qpath_res(path, *hir_id) {
|
||||
Res::Def(
|
||||
DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst,
|
||||
_,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,9 @@ use clippy_utils::{
|
|||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath};
|
||||
use rustc_hir::{
|
||||
Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExpr, PatExprKind, PatKind, Path, QPath,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
|
||||
|
|
@ -183,7 +185,13 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
|
|||
return !matches!(annot, BindingMode(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name;
|
||||
},
|
||||
// Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
|
||||
(PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
|
||||
(
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(QPath::Resolved(_, p_path)),
|
||||
..
|
||||
}),
|
||||
ExprKind::Path(QPath::Resolved(_, e_path)),
|
||||
) => {
|
||||
return over(p_path.segments, e_path.segments, |p_seg, e_seg| {
|
||||
p_seg.ident.name == e_seg.ident.name
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind;
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind, Ty};
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
|
@ -149,8 +149,12 @@ fn find_method_and_type<'tcx>(
|
|||
None
|
||||
}
|
||||
},
|
||||
PatKind::Path(ref path) => {
|
||||
if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id)
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(path),
|
||||
hir_id,
|
||||
..
|
||||
}) => {
|
||||
if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, *hir_id)
|
||||
&& let Some(variant_id) = cx.tcx.opt_parent(ctor_id)
|
||||
{
|
||||
let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) {
|
||||
|
|
@ -351,10 +355,20 @@ fn found_good_method<'tcx>(
|
|||
None
|
||||
}
|
||||
},
|
||||
(PatKind::TupleStruct(path_left, patterns, _), PatKind::Path(path_right))
|
||||
| (PatKind::Path(path_left), PatKind::TupleStruct(path_right, patterns, _))
|
||||
if patterns.len() == 1 =>
|
||||
{
|
||||
(
|
||||
PatKind::TupleStruct(path_left, patterns, _),
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(path_right),
|
||||
..
|
||||
}),
|
||||
)
|
||||
| (
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(path_left),
|
||||
..
|
||||
}),
|
||||
PatKind::TupleStruct(path_right, patterns, _),
|
||||
) if patterns.len() == 1 => {
|
||||
if let PatKind::Wild = patterns[0].kind {
|
||||
find_good_method_for_match(
|
||||
cx,
|
||||
|
|
@ -389,7 +403,13 @@ fn found_good_method<'tcx>(
|
|||
None
|
||||
}
|
||||
},
|
||||
(PatKind::Path(path_left), PatKind::Wild) => get_good_method(cx, arms, path_left),
|
||||
(
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(path_left),
|
||||
..
|
||||
}),
|
||||
PatKind::Wild,
|
||||
) => get_good_method(cx, arms, path_left),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp
|
|||
}
|
||||
|
||||
let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
|
||||
let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind
|
||||
let (msg, sugg) = if let PatKind::Expr(_) = pat.kind
|
||||
&& let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
|
||||
&& let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
|
||||
&& let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait()
|
||||
|
|
@ -331,14 +331,16 @@ impl<'a> PatState<'a> {
|
|||
#[expect(clippy::similar_names)]
|
||||
fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool {
|
||||
match pat.kind {
|
||||
PatKind::Path(_)
|
||||
if match *cx.typeck.pat_ty(pat).peel_refs().kind() {
|
||||
ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()),
|
||||
ty::Tuple(tys) => !tys.is_empty(),
|
||||
ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1),
|
||||
ty::Slice(..) => true,
|
||||
_ => false,
|
||||
} =>
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(_),
|
||||
..
|
||||
}) if match *cx.typeck.pat_ty(pat).peel_refs().kind() {
|
||||
ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()),
|
||||
ty::Tuple(tys) => !tys.is_empty(),
|
||||
ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1),
|
||||
ty::Slice(..) => true,
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
matches!(self, Self::Wild)
|
||||
},
|
||||
|
|
@ -386,7 +388,6 @@ impl<'a> PatState<'a> {
|
|||
| PatKind::Binding(_, _, _, None)
|
||||
| PatKind::Expr(_)
|
||||
| PatKind::Range(..)
|
||||
| PatKind::Path(_)
|
||||
| PatKind::Never
|
||||
| PatKind::Err(_) => {
|
||||
*self = PatState::Wild;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_ast::visit::FnKind;
|
||||
use rustc_ast::{NodeId, WherePredicateKind};
|
||||
use rustc_ast::{Fn, NodeId, WherePredicateKind};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_lint::{EarlyContext, EarlyLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -39,7 +39,7 @@ declare_lint_pass!(MultipleBoundLocations => [MULTIPLE_BOUND_LOCATIONS]);
|
|||
|
||||
impl EarlyLintPass for MultipleBoundLocations {
|
||||
fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) {
|
||||
if let FnKind::Fn(_, _, _, _, generics, _) = kind
|
||||
if let FnKind::Fn(_, _, _, Fn { generics, .. }) = kind
|
||||
&& !generics.params.is_empty()
|
||||
&& !generics.where_clause.predicates.is_empty()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,7 +7,9 @@ use clippy_utils::{
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp};
|
||||
use rustc_hir::{
|
||||
Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, UnOp,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
|
|
@ -281,7 +283,11 @@ fn try_convert_match<'tcx>(
|
|||
|
||||
fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
|
||||
match arm.pat.kind {
|
||||
PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(qpath),
|
||||
hir_id,
|
||||
..
|
||||
}) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone),
|
||||
PatKind::TupleStruct(ref qpath, [first_pat], _) => {
|
||||
is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr)
|
||||
&& matches!(first_pat.kind, PatKind::Wild)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use rustc_hir::def_id::LocalDefId;
|
|||
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
|
||||
use rustc_hir::{
|
||||
self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind,
|
||||
HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind,
|
||||
HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind,
|
||||
};
|
||||
use rustc_hir_analysis::lower_ty;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -258,7 +258,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
|
|||
&& self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS)
|
||||
&& let Some(&StackItem::Check { impl_id, .. }) = self.stack.last()
|
||||
// get the path from the pattern
|
||||
&& let PatKind::Path(QPath::Resolved(_, path))
|
||||
&& let PatKind::Expr(&PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. })
|
||||
| PatKind::TupleStruct(QPath::Resolved(_, path), _, _)
|
||||
| PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind
|
||||
&& cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity()
|
||||
|
|
|
|||
|
|
@ -708,11 +708,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
self.qpath(qpath);
|
||||
self.slice(fields, |pat| self.pat(pat));
|
||||
},
|
||||
PatKind::Path(ref qpath) => {
|
||||
bind!(self, qpath);
|
||||
kind!("Path(ref {qpath})");
|
||||
self.qpath(qpath);
|
||||
},
|
||||
PatKind::Tuple(fields, skip_pos) => {
|
||||
bind!(self, fields);
|
||||
kind!("Tuple({fields}, {skip_pos:?})");
|
||||
|
|
|
|||
|
|
@ -524,7 +524,6 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
}
|
||||
eq
|
||||
},
|
||||
(PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r),
|
||||
(&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r),
|
||||
(&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
|
||||
(&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
|
||||
|
|
@ -1120,7 +1119,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
self.hash_pat(pat);
|
||||
}
|
||||
},
|
||||
PatKind::Path(ref qpath) => self.hash_qpath(qpath),
|
||||
PatKind::Range(s, e, i) => {
|
||||
if let Some(s) = s {
|
||||
self.hash_pat_expr(s);
|
||||
|
|
|
|||
|
|
@ -106,8 +106,8 @@ use rustc_hir::{
|
|||
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext,
|
||||
Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind,
|
||||
ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat,
|
||||
PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef,
|
||||
TyKind, UnOp, def,
|
||||
PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
|
||||
TraitItemRef, TraitRef, TyKind, UnOp, def,
|
||||
};
|
||||
use rustc_lexer::{TokenKind, tokenize};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
|
|
@ -560,7 +560,20 @@ macro_rules! maybe_path {
|
|||
};
|
||||
}
|
||||
maybe_path!(Expr, ExprKind);
|
||||
maybe_path!(Pat, PatKind);
|
||||
impl<'hir> MaybePath<'hir> for Pat<'hir> {
|
||||
fn hir_id(&self) -> HirId {
|
||||
self.hir_id
|
||||
}
|
||||
fn qpath_opt(&self) -> Option<&QPath<'hir>> {
|
||||
match &self.kind {
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(qpath),
|
||||
..
|
||||
}) => Some(qpath),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
maybe_path!(Ty, TyKind);
|
||||
|
||||
/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
|
||||
|
|
@ -1753,7 +1766,11 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
|||
PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.
|
||||
PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
|
||||
PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
|
||||
PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
|
||||
PatKind::Expr(PatExpr {
|
||||
kind: PatExprKind::Path(qpath),
|
||||
hir_id,
|
||||
..
|
||||
}) => is_enum_variant(cx, qpath, *hir_id),
|
||||
PatKind::Or(pats) => {
|
||||
// TODO: should be the honest check, that pats is exhaustive set
|
||||
are_refutable(cx, pats)
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
|
|||
return false;
|
||||
}
|
||||
|
||||
for (predicate, _span) in cx.tcx.explicit_item_super_predicates(def_id).iter_identity_copied() {
|
||||
for (predicate, _span) in cx.tcx.explicit_item_self_bounds(def_id).iter_identity_copied() {
|
||||
match predicate.kind().skip_binder() {
|
||||
// For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
|
||||
// and check substitutions to find `U`.
|
||||
|
|
@ -322,7 +322,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
|||
},
|
||||
ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)),
|
||||
ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => {
|
||||
for (predicate, _) in cx.tcx.explicit_item_super_predicates(def_id).skip_binder() {
|
||||
for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() {
|
||||
if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
|
||||
if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
|
||||
return true;
|
||||
|
|
@ -712,7 +712,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
|
|||
ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) => sig_from_bounds(
|
||||
cx,
|
||||
ty,
|
||||
cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args),
|
||||
cx.tcx.item_self_bounds(def_id).iter_instantiated(cx.tcx, args),
|
||||
cx.tcx.opt_parent(def_id),
|
||||
),
|
||||
ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::iter;
|
||||
|
||||
use rustc_abi::{Align, Size};
|
||||
use rustc_ast::expand::allocator::AllocatorKind;
|
||||
|
||||
|
|
@ -80,18 +78,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn malloc(&mut self, size: u64, zero_init: bool) -> InterpResult<'tcx, Pointer> {
|
||||
fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> {
|
||||
let this = self.eval_context_mut();
|
||||
let align = this.malloc_align(size);
|
||||
let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?;
|
||||
if zero_init {
|
||||
// We just allocated this, the access is definitely in-bounds and fits into our address space.
|
||||
this.write_bytes_ptr(
|
||||
ptr.into(),
|
||||
iter::repeat(0u8).take(usize::try_from(size).unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?;
|
||||
interp_ok(ptr.into())
|
||||
}
|
||||
|
||||
|
|
@ -115,6 +105,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(size),
|
||||
Align::from_bytes(align).unwrap(),
|
||||
MiriMemoryKind::C.into(),
|
||||
AllocInit::Uninit
|
||||
)?;
|
||||
this.write_pointer(ptr, &memptr)?;
|
||||
interp_ok(Scalar::from_i32(0))
|
||||
|
|
@ -134,7 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let new_align = this.malloc_align(new_size);
|
||||
if this.ptr_is_null(old_ptr)? {
|
||||
// Here we must behave like `malloc`.
|
||||
self.malloc(new_size, /*zero_init*/ false)
|
||||
self.malloc(new_size, AllocInit::Uninit)
|
||||
} else {
|
||||
if new_size == 0 {
|
||||
// C, in their infinite wisdom, made this UB.
|
||||
|
|
@ -147,6 +138,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(new_size),
|
||||
new_align,
|
||||
MiriMemoryKind::C.into(),
|
||||
AllocInit::Uninit
|
||||
)?;
|
||||
interp_ok(new_ptr.into())
|
||||
}
|
||||
|
|
@ -187,6 +179,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(size),
|
||||
Align::from_bytes(align).unwrap(),
|
||||
MiriMemoryKind::C.into(),
|
||||
AllocInit::Uninit
|
||||
)?;
|
||||
interp_ok(ptr.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::collections::hash_map::Entry;
|
||||
use std::io::Write;
|
||||
use std::iter;
|
||||
use std::path::Path;
|
||||
|
||||
use rustc_abi::{Align, AlignFromBytesError, Size};
|
||||
|
|
@ -9,6 +8,7 @@ use rustc_ast::expand::allocator::alloc_error_handler_name;
|
|||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::interpret::AllocInit;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_middle::{mir, ty};
|
||||
use rustc_span::Symbol;
|
||||
|
|
@ -442,7 +442,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let [size] = this.check_shim(abi, Conv::C, link_name, args)?;
|
||||
let size = this.read_target_usize(size)?;
|
||||
if size <= this.max_size_of_val().bytes() {
|
||||
let res = this.malloc(size, /*zero_init:*/ false)?;
|
||||
let res = this.malloc(size, AllocInit::Uninit)?;
|
||||
this.write_pointer(res, dest)?;
|
||||
} else {
|
||||
// If this does not fit in an isize, return null and, on Unix, set errno.
|
||||
|
|
@ -457,7 +457,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
let items = this.read_target_usize(items)?;
|
||||
let elem_size = this.read_target_usize(elem_size)?;
|
||||
if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
|
||||
let res = this.malloc(size.bytes(), /*zero_init:*/ true)?;
|
||||
let res = this.malloc(size.bytes(), AllocInit::Zero)?;
|
||||
this.write_pointer(res, dest)?;
|
||||
} else {
|
||||
// On size overflow, return null and, on Unix, set errno.
|
||||
|
|
@ -509,6 +509,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(size),
|
||||
Align::from_bytes(align).unwrap(),
|
||||
memory_kind.into(),
|
||||
AllocInit::Uninit
|
||||
)?;
|
||||
|
||||
ecx.write_pointer(ptr, dest)
|
||||
|
|
@ -537,14 +538,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(size),
|
||||
Align::from_bytes(align).unwrap(),
|
||||
MiriMemoryKind::Rust.into(),
|
||||
AllocInit::Zero
|
||||
)?;
|
||||
|
||||
// We just allocated this, the access is definitely in-bounds.
|
||||
this.write_bytes_ptr(
|
||||
ptr.into(),
|
||||
iter::repeat(0u8).take(usize::try_from(size).unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
this.write_pointer(ptr, dest)
|
||||
});
|
||||
}
|
||||
|
|
@ -604,6 +599,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(new_size),
|
||||
align,
|
||||
MiriMemoryKind::Rust.into(),
|
||||
AllocInit::Uninit
|
||||
)?;
|
||||
this.write_pointer(new_ptr, dest)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1112,6 +1112,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(size),
|
||||
dirent_layout.align.abi,
|
||||
MiriMemoryKind::Runtime.into(),
|
||||
AllocInit::Uninit
|
||||
)?;
|
||||
let entry: Pointer = entry.into();
|
||||
|
||||
|
|
|
|||
|
|
@ -49,16 +49,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(new_size),
|
||||
align,
|
||||
MiriMemoryKind::Mmap.into(),
|
||||
AllocInit::Zero
|
||||
)?;
|
||||
if let Some(increase) = new_size.checked_sub(old_size) {
|
||||
// We just allocated this, the access is definitely in-bounds and fits into our address space.
|
||||
// mmap guarantees new mappings are zero-init.
|
||||
this.write_bytes_ptr(
|
||||
ptr.wrapping_offset(Size::from_bytes(old_size), this).into(),
|
||||
std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
interp_ok(Scalar::from_pointer(ptr, this))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,15 +111,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
return interp_ok(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
let ptr =
|
||||
this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?;
|
||||
// We just allocated this, the access is definitely in-bounds and fits into our address space.
|
||||
// mmap guarantees new mappings are zero-init.
|
||||
this.write_bytes_ptr(
|
||||
ptr.into(),
|
||||
std::iter::repeat(0u8).take(usize::try_from(map_length).unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
let ptr = this.allocate_ptr(
|
||||
Size::from_bytes(map_length),
|
||||
align,
|
||||
MiriMemoryKind::Mmap.into(),
|
||||
// mmap guarantees new mappings are zero-init.
|
||||
AllocInit::Zero
|
||||
)?;
|
||||
|
||||
interp_ok(Scalar::from_pointer(ptr, this))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,8 +253,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
this.read_target_isize(handle)?;
|
||||
let flags = this.read_scalar(flags)?.to_u32()?;
|
||||
let size = this.read_target_usize(size)?;
|
||||
let heap_zero_memory = 0x00000008; // HEAP_ZERO_MEMORY
|
||||
let zero_init = (flags & heap_zero_memory) == heap_zero_memory;
|
||||
const HEAP_ZERO_MEMORY: u32 = 0x00000008;
|
||||
let init = if (flags & HEAP_ZERO_MEMORY) == HEAP_ZERO_MEMORY {
|
||||
AllocInit::Zero
|
||||
} else {
|
||||
AllocInit::Uninit
|
||||
};
|
||||
// Alignment is twice the pointer size.
|
||||
// Source: <https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc>
|
||||
let align = this.tcx.pointer_size().bytes().strict_mul(2);
|
||||
|
|
@ -262,13 +266,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(size),
|
||||
Align::from_bytes(align).unwrap(),
|
||||
MiriMemoryKind::WinHeap.into(),
|
||||
init
|
||||
)?;
|
||||
if zero_init {
|
||||
this.write_bytes_ptr(
|
||||
ptr.into(),
|
||||
iter::repeat(0u8).take(usize::try_from(size).unwrap()),
|
||||
)?;
|
||||
}
|
||||
this.write_pointer(ptr, dest)?;
|
||||
}
|
||||
"HeapFree" => {
|
||||
|
|
@ -300,6 +299,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
|||
Size::from_bytes(size),
|
||||
Align::from_bytes(align).unwrap(),
|
||||
MiriMemoryKind::WinHeap.into(),
|
||||
AllocInit::Uninit
|
||||
)?;
|
||||
this.write_pointer(new_ptr, dest)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// We're testing x86 target specific features
|
||||
//@only-target: x86_64 i686
|
||||
// We're testing x86-32 target specific features. SSE always exists on x86-64.
|
||||
//@only-target: i686
|
||||
//@compile-flags: -C target-feature=-sse2
|
||||
|
||||
#[cfg(target_arch = "x86")]
|
||||
|
|
|
|||
|
|
@ -388,9 +388,13 @@ impl CompletedProcess {
|
|||
self
|
||||
}
|
||||
|
||||
/// Check the **exit status** of the process. On Unix, this is *not* the **wait status**.
|
||||
///
|
||||
/// See [`std::process::ExitStatus::code`]. This is not to be confused with
|
||||
/// [`std::process::ExitCode`].
|
||||
#[track_caller]
|
||||
pub fn assert_exit_code(&self, code: i32) -> &Self {
|
||||
assert!(self.output.status.code() == Some(code));
|
||||
assert_eq!(self.output.status.code(), Some(code));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use std::ffi::{OsStr, OsString};
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr as _;
|
||||
|
||||
use crate::command::Command;
|
||||
use crate::env::env_var;
|
||||
|
|
@ -390,3 +391,10 @@ impl Rustc {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Query the sysroot path corresponding `rustc --print=sysroot`.
|
||||
#[track_caller]
|
||||
pub fn sysroot() -> PathBuf {
|
||||
let path = rustc().print("sysroot").run().stdout_utf8();
|
||||
PathBuf::from_str(path.trim()).unwrap()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -846,7 +846,6 @@ dependencies = [
|
|||
"dashmap",
|
||||
"hashbrown",
|
||||
"rustc-hash 2.0.0",
|
||||
"sptr",
|
||||
"triomphe",
|
||||
]
|
||||
|
||||
|
|
@ -1927,12 +1926,6 @@ dependencies = [
|
|||
"vfs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sptr"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a"
|
||||
|
||||
[[package]]
|
||||
name = "stdx"
|
||||
version = "0.0.0"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
|
|||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.83"
|
||||
rust-version = "1.84"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
authors = ["rust-analyzer team"]
|
||||
|
|
|
|||
|
|
@ -1381,6 +1381,9 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
}
|
||||
ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
|
||||
if self.check_cfg(¯o_).is_none() {
|
||||
return;
|
||||
}
|
||||
let Some(name) = macro_.name() else {
|
||||
statements.push(Statement::Item(Item::Other));
|
||||
return;
|
||||
|
|
@ -1390,6 +1393,9 @@ impl ExprCollector<'_> {
|
|||
self.collect_macro_def(statements, macro_id);
|
||||
}
|
||||
ast::Stmt::Item(ast::Item::MacroRules(macro_)) => {
|
||||
if self.check_cfg(¯o_).is_none() {
|
||||
return;
|
||||
}
|
||||
let Some(name) = macro_.name() else {
|
||||
statements.push(Statement::Item(Item::Other));
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ impl ExprCollector<'_> {
|
|||
};
|
||||
for piece in unverified_pieces {
|
||||
match piece {
|
||||
rustc_parse_format::Piece::String(_) => {}
|
||||
rustc_parse_format::Piece::Lit(_) => {}
|
||||
rustc_parse_format::Piece::NextArgument(arg) => {
|
||||
// let span = arg_spans.next();
|
||||
|
||||
|
|
|
|||
|
|
@ -475,7 +475,7 @@ fn outer() {
|
|||
|
||||
block scope::tests
|
||||
name: _
|
||||
outer: v
|
||||
outer: vg
|
||||
|
||||
crate
|
||||
outer: v
|
||||
|
|
|
|||
|
|
@ -445,6 +445,10 @@ fn find_in_dep(
|
|||
};
|
||||
cov_mark::hit!(partially_imported);
|
||||
if info.is_unstable {
|
||||
if !ctx.cfg.allow_unstable {
|
||||
// the item is unstable and we are not allowed to use unstable items
|
||||
continue;
|
||||
}
|
||||
choice.stability = Unstable;
|
||||
}
|
||||
|
||||
|
|
@ -670,6 +674,7 @@ mod tests {
|
|||
prefer_prelude: bool,
|
||||
prefer_absolute: bool,
|
||||
prefer_no_std: bool,
|
||||
allow_unstable: bool,
|
||||
expect: Expect,
|
||||
) {
|
||||
let (db, pos) = TestDB::with_position(ra_fixture);
|
||||
|
|
@ -711,7 +716,7 @@ mod tests {
|
|||
module,
|
||||
prefix,
|
||||
ignore_local_imports,
|
||||
ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute },
|
||||
ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable },
|
||||
);
|
||||
format_to!(
|
||||
res,
|
||||
|
|
@ -732,7 +737,7 @@ mod tests {
|
|||
path: &str,
|
||||
expect: Expect,
|
||||
) {
|
||||
check_found_path_(ra_fixture, path, false, false, false, expect);
|
||||
check_found_path_(ra_fixture, path, false, false, false, false, expect);
|
||||
}
|
||||
|
||||
fn check_found_path_prelude(
|
||||
|
|
@ -740,7 +745,7 @@ mod tests {
|
|||
path: &str,
|
||||
expect: Expect,
|
||||
) {
|
||||
check_found_path_(ra_fixture, path, true, false, false, expect);
|
||||
check_found_path_(ra_fixture, path, true, false, false, false, expect);
|
||||
}
|
||||
|
||||
fn check_found_path_absolute(
|
||||
|
|
@ -748,7 +753,7 @@ mod tests {
|
|||
path: &str,
|
||||
expect: Expect,
|
||||
) {
|
||||
check_found_path_(ra_fixture, path, false, true, false, expect);
|
||||
check_found_path_(ra_fixture, path, false, true, false, false, expect);
|
||||
}
|
||||
|
||||
fn check_found_path_prefer_no_std(
|
||||
|
|
@ -756,7 +761,15 @@ mod tests {
|
|||
path: &str,
|
||||
expect: Expect,
|
||||
) {
|
||||
check_found_path_(ra_fixture, path, false, false, true, expect);
|
||||
check_found_path_(ra_fixture, path, false, false, true, false, expect);
|
||||
}
|
||||
|
||||
fn check_found_path_prefer_no_std_allow_unstable(
|
||||
#[rust_analyzer::rust_fixture] ra_fixture: &str,
|
||||
path: &str,
|
||||
expect: Expect,
|
||||
) {
|
||||
check_found_path_(ra_fixture, path, false, false, true, true, expect);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1951,7 +1964,7 @@ pub mod ops {
|
|||
|
||||
#[test]
|
||||
fn respect_unstable_modules() {
|
||||
check_found_path_prefer_no_std(
|
||||
check_found_path_prefer_no_std_allow_unstable(
|
||||
r#"
|
||||
//- /main.rs crate:main deps:std,core
|
||||
extern crate std;
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ pub(crate) fn parse(
|
|||
|
||||
for piece in pieces {
|
||||
match piece {
|
||||
parse::Piece::String(s) => {
|
||||
parse::Piece::Lit(s) => {
|
||||
unfinished_literal.push_str(s);
|
||||
}
|
||||
parse::Piece::NextArgument(arg) => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use rustc_hash::FxHashSet;
|
|||
use smallvec::SmallVec;
|
||||
use span::Edition;
|
||||
use stdx::{format_to, TupleExt};
|
||||
use syntax::ToSmolStr;
|
||||
use triomphe::Arc;
|
||||
|
||||
use crate::{
|
||||
|
|
@ -88,9 +87,9 @@ impl ImportMap {
|
|||
.iter()
|
||||
// We've only collected items, whose name cannot be tuple field so unwrapping is fine.
|
||||
.flat_map(|(&item, (info, _))| {
|
||||
info.iter().enumerate().map(move |(idx, info)| {
|
||||
(item, info.name.unescaped().display(db.upcast()).to_smolstr(), idx as u32)
|
||||
})
|
||||
info.iter()
|
||||
.enumerate()
|
||||
.map(move |(idx, info)| (item, info.name.as_str(), idx as u32))
|
||||
})
|
||||
.collect();
|
||||
importables.sort_by(|(_, l_info, _), (_, r_info, _)| {
|
||||
|
|
@ -168,7 +167,8 @@ impl ImportMap {
|
|||
let attr_id = if let Some(import) = import {
|
||||
match import {
|
||||
ImportOrExternCrate::ExternCrate(id) => Some(id.into()),
|
||||
ImportOrExternCrate::Import(id) => Some(id.import.into()),
|
||||
ImportOrExternCrate::Import(id) => Some(id.use_.into()),
|
||||
ImportOrExternCrate::Glob(id) => Some(id.use_.into()),
|
||||
}
|
||||
} else {
|
||||
match item {
|
||||
|
|
@ -441,7 +441,7 @@ pub fn search_dependencies(
|
|||
}
|
||||
|
||||
fn search_maps(
|
||||
db: &dyn DefDatabase,
|
||||
_db: &dyn DefDatabase,
|
||||
import_maps: &[Arc<ImportMap>],
|
||||
mut stream: fst::map::Union<'_>,
|
||||
query: &Query,
|
||||
|
|
@ -464,11 +464,7 @@ fn search_maps(
|
|||
.then(|| (item, &import_infos[info_idx as usize]))
|
||||
})
|
||||
.filter(|&(_, info)| {
|
||||
query.search_mode.check(
|
||||
&query.query,
|
||||
query.case_sensitive,
|
||||
&info.name.unescaped().display(db.upcast()).to_smolstr(),
|
||||
)
|
||||
query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str())
|
||||
});
|
||||
res.extend(iter.map(TupleExt::head));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,35 +31,103 @@ pub struct PerNsGlobImports {
|
|||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ImportOrExternCrate {
|
||||
Glob(GlobId),
|
||||
Import(ImportId),
|
||||
ExternCrate(ExternCrateId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) enum ImportType {
|
||||
Import(ImportId),
|
||||
Glob(UseId),
|
||||
ExternCrate(ExternCrateId),
|
||||
impl From<ImportOrGlob> for ImportOrExternCrate {
|
||||
fn from(value: ImportOrGlob) -> Self {
|
||||
match value {
|
||||
ImportOrGlob::Glob(it) => ImportOrExternCrate::Glob(it),
|
||||
ImportOrGlob::Import(it) => ImportOrExternCrate::Import(it),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ImportOrExternCrate {
|
||||
pub fn into_import(self) -> Option<ImportId> {
|
||||
pub fn import_or_glob(self) -> Option<ImportOrGlob> {
|
||||
match self {
|
||||
ImportOrExternCrate::Import(it) => Some(ImportOrGlob::Import(it)),
|
||||
ImportOrExternCrate::Glob(it) => Some(ImportOrGlob::Glob(it)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn import(self) -> Option<ImportId> {
|
||||
match self {
|
||||
ImportOrExternCrate::Import(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn glob(self) -> Option<GlobId> {
|
||||
match self {
|
||||
ImportOrExternCrate::Glob(id) => Some(id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn use_(self) -> Option<UseId> {
|
||||
match self {
|
||||
ImportOrExternCrate::Glob(id) => Some(id.use_),
|
||||
ImportOrExternCrate::Import(id) => Some(id.use_),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ImportOrGlob {
|
||||
Glob(GlobId),
|
||||
Import(ImportId),
|
||||
}
|
||||
|
||||
impl ImportOrGlob {
|
||||
pub fn into_import(self) -> Option<ImportId> {
|
||||
match self {
|
||||
ImportOrGlob::Import(it) => Some(it),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ImportOrDef {
|
||||
Import(ImportId),
|
||||
Glob(GlobId),
|
||||
ExternCrate(ExternCrateId),
|
||||
Def(ModuleDefId),
|
||||
}
|
||||
|
||||
impl From<ImportOrExternCrate> for ImportOrDef {
|
||||
fn from(value: ImportOrExternCrate) -> Self {
|
||||
match value {
|
||||
ImportOrExternCrate::Import(it) => ImportOrDef::Import(it),
|
||||
ImportOrExternCrate::Glob(it) => ImportOrDef::Glob(it),
|
||||
ImportOrExternCrate::ExternCrate(it) => ImportOrDef::ExternCrate(it),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImportOrGlob> for ImportOrDef {
|
||||
fn from(value: ImportOrGlob) -> Self {
|
||||
match value {
|
||||
ImportOrGlob::Import(it) => ImportOrDef::Import(it),
|
||||
ImportOrGlob::Glob(it) => ImportOrDef::Glob(it),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct ImportId {
|
||||
pub import: UseId,
|
||||
pub use_: UseId,
|
||||
pub idx: Idx<ast::UseTree>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
pub struct GlobId {
|
||||
pub use_: UseId,
|
||||
pub idx: Idx<ast::UseTree>,
|
||||
}
|
||||
|
||||
|
|
@ -96,8 +164,8 @@ pub struct ItemScope {
|
|||
|
||||
// the resolutions of the imports of this scope
|
||||
use_imports_types: FxHashMap<ImportOrExternCrate, ImportOrDef>,
|
||||
use_imports_values: FxHashMap<ImportId, ImportOrDef>,
|
||||
use_imports_macros: FxHashMap<ImportId, ImportOrDef>,
|
||||
use_imports_values: FxHashMap<ImportOrGlob, ImportOrDef>,
|
||||
use_imports_macros: FxHashMap<ImportOrGlob, ImportOrDef>,
|
||||
|
||||
use_decls: Vec<UseId>,
|
||||
extern_crate_decls: Vec<ExternCrateId>,
|
||||
|
|
@ -162,7 +230,7 @@ impl ItemScope {
|
|||
.map(move |name| (name, self.get(name)))
|
||||
}
|
||||
|
||||
pub fn values(&self) -> impl Iterator<Item = (&Name, Item<ModuleDefId, ImportId>)> + '_ {
|
||||
pub fn values(&self) -> impl Iterator<Item = (&Name, Item<ModuleDefId, ImportOrGlob>)> + '_ {
|
||||
self.values.iter().map(|(n, &i)| (n, i))
|
||||
}
|
||||
|
||||
|
|
@ -172,7 +240,7 @@ impl ItemScope {
|
|||
self.types.iter().map(|(n, &i)| (n, i))
|
||||
}
|
||||
|
||||
pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportId>)> + '_ {
|
||||
pub fn macros(&self) -> impl Iterator<Item = (&Name, Item<MacroId, ImportOrGlob>)> + '_ {
|
||||
self.macros.iter().map(|(n, &i)| (n, i))
|
||||
}
|
||||
|
||||
|
|
@ -180,9 +248,10 @@ impl ItemScope {
|
|||
self.use_imports_types
|
||||
.keys()
|
||||
.copied()
|
||||
.filter_map(ImportOrExternCrate::into_import)
|
||||
.filter_map(ImportOrExternCrate::import_or_glob)
|
||||
.chain(self.use_imports_values.keys().copied())
|
||||
.chain(self.use_imports_macros.keys().copied())
|
||||
.filter_map(ImportOrGlob::into_import)
|
||||
.sorted()
|
||||
.dedup()
|
||||
}
|
||||
|
|
@ -192,10 +261,10 @@ impl ItemScope {
|
|||
|
||||
let mut def_map;
|
||||
let mut scope = self;
|
||||
while let Some(&m) = scope.use_imports_macros.get(&import) {
|
||||
while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) {
|
||||
match m {
|
||||
ImportOrDef::Import(i) => {
|
||||
let module_id = i.import.lookup(db).container;
|
||||
let module_id = i.use_.lookup(db).container;
|
||||
def_map = module_id.def_map(db);
|
||||
scope = &def_map[module_id.local_id].scope;
|
||||
import = i;
|
||||
|
|
@ -211,7 +280,7 @@ impl ItemScope {
|
|||
while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) {
|
||||
match m {
|
||||
ImportOrDef::Import(i) => {
|
||||
let module_id = i.import.lookup(db).container;
|
||||
let module_id = i.use_.lookup(db).container;
|
||||
def_map = module_id.def_map(db);
|
||||
scope = &def_map[module_id.local_id].scope;
|
||||
import = i;
|
||||
|
|
@ -224,10 +293,10 @@ impl ItemScope {
|
|||
}
|
||||
}
|
||||
let mut scope = self;
|
||||
while let Some(&m) = scope.use_imports_values.get(&import) {
|
||||
while let Some(&m) = scope.use_imports_values.get(&ImportOrGlob::Import(import)) {
|
||||
match m {
|
||||
ImportOrDef::Import(i) => {
|
||||
let module_id = i.import.lookup(db).container;
|
||||
let module_id = i.use_.lookup(db).container;
|
||||
def_map = module_id.def_map(db);
|
||||
scope = &def_map[module_id.local_id].scope;
|
||||
import = i;
|
||||
|
|
@ -488,9 +557,13 @@ impl ItemScope {
|
|||
self.unnamed_trait_imports.get(&tr).map(|trait_| trait_.vis)
|
||||
}
|
||||
|
||||
pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) {
|
||||
// FIXME: import
|
||||
self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import: None });
|
||||
pub(crate) fn push_unnamed_trait(
|
||||
&mut self,
|
||||
tr: TraitId,
|
||||
vis: Visibility,
|
||||
import: Option<ImportId>,
|
||||
) {
|
||||
self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import });
|
||||
}
|
||||
|
||||
pub(crate) fn push_res_with_import(
|
||||
|
|
@ -498,7 +571,7 @@ impl ItemScope {
|
|||
glob_imports: &mut PerNsGlobImports,
|
||||
lookup: (LocalModuleId, Name),
|
||||
def: PerNs,
|
||||
import: Option<ImportType>,
|
||||
import: Option<ImportOrExternCrate>,
|
||||
) -> bool {
|
||||
let mut changed = false;
|
||||
|
||||
|
|
@ -509,41 +582,22 @@ impl ItemScope {
|
|||
match existing {
|
||||
Entry::Vacant(entry) => {
|
||||
match import {
|
||||
Some(ImportType::Glob(_)) => {
|
||||
Some(ImportOrExternCrate::Glob(_)) => {
|
||||
glob_imports.types.insert(lookup.clone());
|
||||
}
|
||||
_ => _ = glob_imports.types.remove(&lookup),
|
||||
}
|
||||
let import = match import {
|
||||
Some(ImportType::ExternCrate(extern_crate)) => {
|
||||
Some(ImportOrExternCrate::ExternCrate(extern_crate))
|
||||
}
|
||||
Some(ImportType::Import(import)) => {
|
||||
Some(ImportOrExternCrate::Import(import))
|
||||
}
|
||||
None | Some(ImportType::Glob(_)) => None,
|
||||
};
|
||||
let prev = std::mem::replace(&mut fld.import, import);
|
||||
if let Some(import) = import {
|
||||
self.use_imports_types.insert(
|
||||
import,
|
||||
match prev {
|
||||
Some(ImportOrExternCrate::Import(import)) => {
|
||||
ImportOrDef::Import(import)
|
||||
}
|
||||
Some(ImportOrExternCrate::ExternCrate(import)) => {
|
||||
ImportOrDef::ExternCrate(import)
|
||||
}
|
||||
None => ImportOrDef::Def(fld.def),
|
||||
},
|
||||
);
|
||||
self.use_imports_types
|
||||
.insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into));
|
||||
}
|
||||
entry.insert(fld);
|
||||
changed = true;
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
match import {
|
||||
Some(ImportType::Glob(..)) => {
|
||||
Some(ImportOrExternCrate::Glob(..)) => {
|
||||
// Multiple globs may import the same item and they may
|
||||
// override visibility from previously resolved globs. This is
|
||||
// currently handled by `DefCollector`, because we need to
|
||||
|
|
@ -552,28 +606,11 @@ impl ItemScope {
|
|||
}
|
||||
_ => {
|
||||
if glob_imports.types.remove(&lookup) {
|
||||
let import = match import {
|
||||
Some(ImportType::ExternCrate(extern_crate)) => {
|
||||
Some(ImportOrExternCrate::ExternCrate(extern_crate))
|
||||
}
|
||||
Some(ImportType::Import(import)) => {
|
||||
Some(ImportOrExternCrate::Import(import))
|
||||
}
|
||||
None | Some(ImportType::Glob(_)) => None,
|
||||
};
|
||||
let prev = std::mem::replace(&mut fld.import, import);
|
||||
if let Some(import) = import {
|
||||
self.use_imports_types.insert(
|
||||
import,
|
||||
match prev {
|
||||
Some(ImportOrExternCrate::Import(import)) => {
|
||||
ImportOrDef::Import(import)
|
||||
}
|
||||
Some(ImportOrExternCrate::ExternCrate(import)) => {
|
||||
ImportOrDef::ExternCrate(import)
|
||||
}
|
||||
None => ImportOrDef::Def(fld.def),
|
||||
},
|
||||
prev.map_or(ImportOrDef::Def(fld.def), Into::into),
|
||||
);
|
||||
}
|
||||
cov_mark::hit!(import_shadowed);
|
||||
|
|
@ -591,44 +628,31 @@ impl ItemScope {
|
|||
match existing {
|
||||
Entry::Vacant(entry) => {
|
||||
match import {
|
||||
Some(ImportType::Glob(_)) => {
|
||||
Some(ImportOrExternCrate::Glob(_)) => {
|
||||
glob_imports.values.insert(lookup.clone());
|
||||
}
|
||||
_ => _ = glob_imports.values.remove(&lookup),
|
||||
}
|
||||
let import = match import {
|
||||
Some(ImportType::Import(import)) => Some(import),
|
||||
_ => None,
|
||||
};
|
||||
let import = import.and_then(ImportOrExternCrate::import_or_glob);
|
||||
let prev = std::mem::replace(&mut fld.import, import);
|
||||
if let Some(import) = import {
|
||||
self.use_imports_values.insert(
|
||||
import,
|
||||
match prev {
|
||||
Some(import) => ImportOrDef::Import(import),
|
||||
None => ImportOrDef::Def(fld.def),
|
||||
},
|
||||
);
|
||||
self.use_imports_values
|
||||
.insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into));
|
||||
}
|
||||
entry.insert(fld);
|
||||
changed = true;
|
||||
}
|
||||
Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
|
||||
Entry::Occupied(mut entry)
|
||||
if !matches!(import, Some(ImportOrExternCrate::Glob(..))) =>
|
||||
{
|
||||
if glob_imports.values.remove(&lookup) {
|
||||
cov_mark::hit!(import_shadowed);
|
||||
let import = match import {
|
||||
Some(ImportType::Import(import)) => Some(import),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let import = import.and_then(ImportOrExternCrate::import_or_glob);
|
||||
let prev = std::mem::replace(&mut fld.import, import);
|
||||
if let Some(import) = import {
|
||||
self.use_imports_values.insert(
|
||||
import,
|
||||
match prev {
|
||||
Some(import) => ImportOrDef::Import(import),
|
||||
None => ImportOrDef::Def(fld.def),
|
||||
},
|
||||
);
|
||||
self.use_imports_values
|
||||
.insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into));
|
||||
}
|
||||
entry.insert(fld);
|
||||
changed = true;
|
||||
|
|
@ -643,43 +667,33 @@ impl ItemScope {
|
|||
match existing {
|
||||
Entry::Vacant(entry) => {
|
||||
match import {
|
||||
Some(ImportType::Glob(_)) => {
|
||||
Some(ImportOrExternCrate::Glob(_)) => {
|
||||
glob_imports.macros.insert(lookup.clone());
|
||||
}
|
||||
_ => _ = glob_imports.macros.remove(&lookup),
|
||||
}
|
||||
let import = match import {
|
||||
Some(ImportType::Import(import)) => Some(import),
|
||||
_ => None,
|
||||
};
|
||||
let import = import.and_then(ImportOrExternCrate::import_or_glob);
|
||||
let prev = std::mem::replace(&mut fld.import, import);
|
||||
if let Some(import) = import {
|
||||
self.use_imports_macros.insert(
|
||||
import,
|
||||
match prev {
|
||||
Some(import) => ImportOrDef::Import(import),
|
||||
None => ImportOrDef::Def(fld.def.into()),
|
||||
},
|
||||
prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into),
|
||||
);
|
||||
}
|
||||
entry.insert(fld);
|
||||
changed = true;
|
||||
}
|
||||
Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => {
|
||||
Entry::Occupied(mut entry)
|
||||
if !matches!(import, Some(ImportOrExternCrate::Glob(..))) =>
|
||||
{
|
||||
if glob_imports.macros.remove(&lookup) {
|
||||
cov_mark::hit!(import_shadowed);
|
||||
let import = match import {
|
||||
Some(ImportType::Import(import)) => Some(import),
|
||||
_ => None,
|
||||
};
|
||||
let import = import.and_then(ImportOrExternCrate::import_or_glob);
|
||||
let prev = std::mem::replace(&mut fld.import, import);
|
||||
if let Some(import) = import {
|
||||
self.use_imports_macros.insert(
|
||||
import,
|
||||
match prev {
|
||||
Some(import) => ImportOrDef::Import(import),
|
||||
None => ImportOrDef::Def(fld.def.into()),
|
||||
},
|
||||
prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into),
|
||||
);
|
||||
}
|
||||
entry.insert(fld);
|
||||
|
|
@ -704,16 +718,27 @@ impl ItemScope {
|
|||
.map(|def| &mut def.vis)
|
||||
.chain(self.values.values_mut().map(|def| &mut def.vis))
|
||||
.chain(self.unnamed_trait_imports.values_mut().map(|def| &mut def.vis))
|
||||
.for_each(|vis| {
|
||||
*vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
|
||||
.for_each(|vis| match vis {
|
||||
&mut Visibility::Module(_, visibility_explicitness) => {
|
||||
*vis = Visibility::Module(this_module, visibility_explicitness)
|
||||
}
|
||||
Visibility::Public => {
|
||||
*vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
|
||||
}
|
||||
});
|
||||
|
||||
for mac in self.macros.values_mut() {
|
||||
if matches!(mac.def, MacroId::ProcMacroId(_) if mac.import.is_none()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit);
|
||||
match mac.vis {
|
||||
Visibility::Module(_, visibility_explicitness) => {
|
||||
mac.vis = Visibility::Module(this_module, visibility_explicitness)
|
||||
}
|
||||
Visibility::Public => {
|
||||
mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -732,20 +757,25 @@ impl ItemScope {
|
|||
buf.push_str(" t");
|
||||
match import {
|
||||
Some(ImportOrExternCrate::Import(_)) => buf.push('i'),
|
||||
Some(ImportOrExternCrate::Glob(_)) => buf.push('g'),
|
||||
Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
if let Some(Item { import, .. }) = def.values {
|
||||
buf.push_str(" v");
|
||||
if import.is_some() {
|
||||
buf.push('i');
|
||||
match import {
|
||||
Some(ImportOrGlob::Import(_)) => buf.push('i'),
|
||||
Some(ImportOrGlob::Glob(_)) => buf.push('g'),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
if let Some(Item { import, .. }) = def.macros {
|
||||
buf.push_str(" m");
|
||||
if import.is_some() {
|
||||
buf.push('i');
|
||||
match import {
|
||||
Some(ImportOrGlob::Import(_)) => buf.push('i'),
|
||||
Some(ImportOrGlob::Glob(_)) => buf.push('g'),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
if def.is_none() {
|
||||
|
|
@ -828,7 +858,7 @@ impl PerNs {
|
|||
match def {
|
||||
ModuleDefId::ModuleId(_) => PerNs::types(def, v, import),
|
||||
ModuleDefId::FunctionId(_) => {
|
||||
PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
|
||||
PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob))
|
||||
}
|
||||
ModuleDefId::AdtId(adt) => match adt {
|
||||
AdtId::UnionId(_) => PerNs::types(def, v, import),
|
||||
|
|
@ -843,14 +873,14 @@ impl PerNs {
|
|||
},
|
||||
ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import),
|
||||
ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => {
|
||||
PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import))
|
||||
PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob))
|
||||
}
|
||||
ModuleDefId::TraitId(_) => PerNs::types(def, v, import),
|
||||
ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import),
|
||||
ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import),
|
||||
ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import),
|
||||
ModuleDefId::MacroId(mac) => {
|
||||
PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import))
|
||||
PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::import_or_glob))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@ language_item_table! {
|
|||
DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0);
|
||||
DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None;
|
||||
Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None;
|
||||
ReceiverTarget, sym::receiver_target, receiver_target, Target::AssocTy, GenericRequirement::None;
|
||||
|
||||
Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
|
||||
|
|
|
|||
|
|
@ -114,6 +114,9 @@ pub struct ImportPathConfig {
|
|||
pub prefer_prelude: bool,
|
||||
/// If true, prefer abs path (starting with `::`) where it is available.
|
||||
pub prefer_absolute: bool,
|
||||
/// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no
|
||||
/// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`.
|
||||
pub allow_unstable: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -910,6 +913,7 @@ pub enum AssocItemId {
|
|||
ConstId(ConstId),
|
||||
TypeAliasId(TypeAliasId),
|
||||
}
|
||||
|
||||
// FIXME: not every function, ... is actually an assoc item. maybe we should make
|
||||
// sure that you can only turn actual assoc items into AssocItemIds. This would
|
||||
// require not implementing From, and instead having some checked way of
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ use triomphe::Arc;
|
|||
use crate::{
|
||||
attr::Attrs,
|
||||
db::DefDatabase,
|
||||
item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports},
|
||||
item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports},
|
||||
item_tree::{
|
||||
self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId,
|
||||
ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind,
|
||||
|
|
@ -208,7 +208,7 @@ struct DefCollector<'a> {
|
|||
def_map: DefMap,
|
||||
// The dependencies of the current crate, including optional deps like `test`.
|
||||
deps: FxHashMap<Name, Dependency>,
|
||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, UseId)>>,
|
||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, Visibility, GlobId)>>,
|
||||
unresolved_imports: Vec<ImportDirective>,
|
||||
indeterminate_imports: Vec<(ImportDirective, PerNs)>,
|
||||
unresolved_macros: Vec<MacroDirective>,
|
||||
|
|
@ -524,11 +524,7 @@ impl DefCollector<'_> {
|
|||
|
||||
match per_ns.types {
|
||||
Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => {
|
||||
// FIXME: This should specifically look for a glob import somehow and record that here
|
||||
self.def_map.prelude = Some((
|
||||
m,
|
||||
import.and_then(ImportOrExternCrate::into_import).map(|it| it.import),
|
||||
));
|
||||
self.def_map.prelude = Some((m, import.and_then(ImportOrExternCrate::use_)));
|
||||
}
|
||||
types => {
|
||||
tracing::debug!(
|
||||
|
|
@ -845,13 +841,14 @@ impl DefCollector<'_> {
|
|||
def.values = None;
|
||||
def.macros = None;
|
||||
}
|
||||
let imp = ImportType::Import(ImportId { import: id, idx: use_tree });
|
||||
let imp = ImportOrExternCrate::Import(ImportId { use_: id, idx: use_tree });
|
||||
tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def);
|
||||
|
||||
self.update(module_id, &[(name.cloned(), def)], vis, Some(imp));
|
||||
}
|
||||
ImportSource { kind: ImportKind::Glob, id, is_prelude, .. } => {
|
||||
ImportSource { kind: ImportKind::Glob, id, is_prelude, use_tree } => {
|
||||
tracing::debug!("glob import: {:?}", import);
|
||||
let glob = GlobId { use_: id, idx: use_tree };
|
||||
match def.take_types() {
|
||||
Some(ModuleDefId::ModuleId(m)) => {
|
||||
if is_prelude {
|
||||
|
|
@ -875,7 +872,12 @@ impl DefCollector<'_> {
|
|||
.filter(|(_, res)| !res.is_none())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
|
||||
self.update(
|
||||
module_id,
|
||||
&items,
|
||||
vis,
|
||||
Some(ImportOrExternCrate::Glob(glob)),
|
||||
);
|
||||
} else {
|
||||
// glob import from same crate => we do an initial
|
||||
// import, and then need to propagate any further
|
||||
|
|
@ -907,11 +909,16 @@ impl DefCollector<'_> {
|
|||
.filter(|(_, res)| !res.is_none())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
self.update(module_id, &items, vis, Some(ImportType::Glob(id)));
|
||||
self.update(
|
||||
module_id,
|
||||
&items,
|
||||
vis,
|
||||
Some(ImportOrExternCrate::Glob(glob)),
|
||||
);
|
||||
// record the glob import in case we add further items
|
||||
let glob = self.glob_imports.entry(m.local_id).or_default();
|
||||
match glob.iter_mut().find(|(mid, _, _)| *mid == module_id) {
|
||||
None => glob.push((module_id, vis, id)),
|
||||
let glob_imports = self.glob_imports.entry(m.local_id).or_default();
|
||||
match glob_imports.iter_mut().find(|(mid, _, _)| *mid == module_id) {
|
||||
None => glob_imports.push((module_id, vis, glob)),
|
||||
Some((_, old_vis, _)) => {
|
||||
if let Some(new_vis) = old_vis.max(vis, &self.def_map) {
|
||||
*old_vis = new_vis;
|
||||
|
|
@ -944,7 +951,12 @@ impl DefCollector<'_> {
|
|||
(Some(name), res)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id)));
|
||||
self.update(
|
||||
module_id,
|
||||
&resolutions,
|
||||
vis,
|
||||
Some(ImportOrExternCrate::Glob(glob)),
|
||||
);
|
||||
}
|
||||
Some(d) => {
|
||||
tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d);
|
||||
|
|
@ -964,7 +976,7 @@ impl DefCollector<'_> {
|
|||
resolutions: &[(Option<Name>, PerNs)],
|
||||
// Visibility this import will have
|
||||
vis: Visibility,
|
||||
import: Option<ImportType>,
|
||||
import: Option<ImportOrExternCrate>,
|
||||
) {
|
||||
self.db.unwind_if_cancelled();
|
||||
self.update_recursive(module_id, resolutions, vis, import, 0)
|
||||
|
|
@ -978,7 +990,7 @@ impl DefCollector<'_> {
|
|||
// All resolutions are imported with this visibility; the visibilities in
|
||||
// the `PerNs` values are ignored and overwritten
|
||||
vis: Visibility,
|
||||
import: Option<ImportType>,
|
||||
import: Option<ImportOrExternCrate>,
|
||||
depth: usize,
|
||||
) {
|
||||
if GLOB_RECURSION_LIMIT.check(depth).is_err() {
|
||||
|
|
@ -994,8 +1006,10 @@ impl DefCollector<'_> {
|
|||
self.push_res_and_update_glob_vis(module_id, name, *res, vis, import);
|
||||
}
|
||||
None => {
|
||||
let tr = match res.take_types() {
|
||||
Some(ModuleDefId::TraitId(tr)) => tr,
|
||||
let (tr, import) = match res.take_types_full() {
|
||||
Some(Item { def: ModuleDefId::TraitId(tr), vis: _, import }) => {
|
||||
(tr, import)
|
||||
}
|
||||
Some(other) => {
|
||||
tracing::debug!("non-trait `_` import of {:?}", other);
|
||||
continue;
|
||||
|
|
@ -1021,7 +1035,11 @@ impl DefCollector<'_> {
|
|||
|
||||
if should_update {
|
||||
changed = true;
|
||||
self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis);
|
||||
self.def_map.modules[module_id].scope.push_unnamed_trait(
|
||||
tr,
|
||||
vis,
|
||||
import.and_then(ImportOrExternCrate::import),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1043,13 +1061,13 @@ impl DefCollector<'_> {
|
|||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for (glob_importing_module, glob_import_vis, use_) in glob_imports {
|
||||
for (glob_importing_module, glob_import_vis, glob) in glob_imports {
|
||||
let vis = glob_import_vis.min(vis, &self.def_map).unwrap_or(glob_import_vis);
|
||||
self.update_recursive(
|
||||
glob_importing_module,
|
||||
resolutions,
|
||||
vis,
|
||||
Some(ImportType::Glob(use_)),
|
||||
Some(ImportOrExternCrate::Glob(glob)),
|
||||
depth + 1,
|
||||
);
|
||||
}
|
||||
|
|
@ -1061,7 +1079,7 @@ impl DefCollector<'_> {
|
|||
name: &Name,
|
||||
mut defs: PerNs,
|
||||
vis: Visibility,
|
||||
def_import_type: Option<ImportType>,
|
||||
def_import_type: Option<ImportOrExternCrate>,
|
||||
) -> bool {
|
||||
// `extern crate crate_name` things can be re-exported as `pub use crate_name`.
|
||||
// But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name`
|
||||
|
|
@ -1074,10 +1092,10 @@ impl DefCollector<'_> {
|
|||
let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else {
|
||||
return false;
|
||||
};
|
||||
let Some(ImportType::Import(id)) = def_import_type else {
|
||||
let Some(ImportOrExternCrate::Import(id)) = def_import_type else {
|
||||
return false;
|
||||
};
|
||||
let use_id = id.import.lookup(self.db).id;
|
||||
let use_id = id.use_.lookup(self.db).id;
|
||||
let item_tree = use_id.item_tree(self.db);
|
||||
let use_kind = item_tree[use_id.value].use_tree.kind();
|
||||
let UseTreeKind::Single { path, .. } = use_kind else {
|
||||
|
|
@ -1100,7 +1118,7 @@ impl DefCollector<'_> {
|
|||
|
||||
let mut changed = false;
|
||||
|
||||
if let Some(ImportType::Glob(_)) = def_import_type {
|
||||
if let Some(ImportOrExternCrate::Glob(_)) = def_import_type {
|
||||
let prev_defs = self.def_map[module_id].scope.get(name);
|
||||
|
||||
// Multiple globs may import the same item and they may override visibility from
|
||||
|
|
@ -1727,7 +1745,7 @@ impl ModCollector<'_, '_> {
|
|||
),
|
||||
)],
|
||||
vis,
|
||||
Some(ImportType::ExternCrate(id)),
|
||||
Some(ImportOrExternCrate::ExternCrate(id)),
|
||||
);
|
||||
} else {
|
||||
if let Some(name) = name {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use base_db::AnchoredPath;
|
|||
use hir_expand::{name::Name, HirFileIdExt};
|
||||
use limit::Limit;
|
||||
use span::EditionedFileId;
|
||||
use syntax::ToSmolStr as _;
|
||||
|
||||
use crate::{db::DefDatabase, HirFileId};
|
||||
|
||||
|
|
@ -35,7 +34,7 @@ impl ModDir {
|
|||
let path = match attr_path {
|
||||
None => {
|
||||
let mut path = self.dir_path.clone();
|
||||
path.push(&name.unescaped().display_no_db().to_smolstr());
|
||||
path.push(name.as_str());
|
||||
path
|
||||
}
|
||||
Some(attr_path) => {
|
||||
|
|
@ -66,7 +65,7 @@ impl ModDir {
|
|||
name: &Name,
|
||||
attr_path: Option<&str>,
|
||||
) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> {
|
||||
let name = name.unescaped();
|
||||
let name = name.as_str();
|
||||
|
||||
let mut candidate_files = ArrayVec::<_, 2>::new();
|
||||
match attr_path {
|
||||
|
|
@ -74,16 +73,8 @@ impl ModDir {
|
|||
candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner))
|
||||
}
|
||||
None => {
|
||||
candidate_files.push(format!(
|
||||
"{}{}.rs",
|
||||
self.dir_path.0,
|
||||
name.display(db.upcast())
|
||||
));
|
||||
candidate_files.push(format!(
|
||||
"{}{}/mod.rs",
|
||||
self.dir_path.0,
|
||||
name.display(db.upcast())
|
||||
));
|
||||
candidate_files.push(format!("{}{}.rs", self.dir_path.0, name));
|
||||
candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -97,7 +88,7 @@ impl ModDir {
|
|||
let dir_path = if root_dir_owner {
|
||||
DirPath::empty()
|
||||
} else {
|
||||
DirPath::new(format!("{}/", name.display(db.upcast())))
|
||||
DirPath::new(format!("{}/", name))
|
||||
};
|
||||
if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) {
|
||||
return Ok((
|
||||
|
|
|
|||
|
|
@ -103,8 +103,8 @@ mod a {
|
|||
c: t
|
||||
|
||||
crate::a::b::c
|
||||
A: v
|
||||
b: t
|
||||
A: vg
|
||||
b: tg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -256,8 +256,8 @@ pub enum Foo { Bar, Baz }
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Bar: t v
|
||||
Baz: t v
|
||||
Bar: tg vg
|
||||
Baz: tg vg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -421,10 +421,10 @@ pub struct NotExported;
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Exported: t v
|
||||
PublicItem: t v
|
||||
allowed_reexport: t
|
||||
exported: t
|
||||
Exported: tg vg
|
||||
PublicItem: tg vg
|
||||
allowed_reexport: tg
|
||||
exported: tg
|
||||
not_allowed_reexport1: _
|
||||
not_allowed_reexport2: _
|
||||
"#]],
|
||||
|
|
@ -692,7 +692,7 @@ mod b {
|
|||
b: t
|
||||
|
||||
crate::a
|
||||
T: t v
|
||||
T: t vg
|
||||
|
||||
crate::b
|
||||
T: v
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ pub struct Baz;
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Baz: t v
|
||||
Foo: t v
|
||||
bar: t
|
||||
Baz: tg vg
|
||||
Foo: tg vg
|
||||
bar: tg
|
||||
foo: t
|
||||
|
||||
crate::foo
|
||||
|
|
@ -53,20 +53,20 @@ pub use super::*;
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Baz: t v
|
||||
Foo: t v
|
||||
bar: t
|
||||
Baz: tg vg
|
||||
Foo: tg vg
|
||||
bar: tg
|
||||
foo: t
|
||||
|
||||
crate::foo
|
||||
Baz: t v
|
||||
Baz: tg vg
|
||||
Foo: t v
|
||||
bar: t
|
||||
|
||||
crate::foo::bar
|
||||
Baz: t v
|
||||
Foo: t v
|
||||
bar: t
|
||||
Foo: tg vg
|
||||
bar: tg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -91,20 +91,20 @@ pub use super::*;
|
|||
",
|
||||
expect![[r#"
|
||||
crate
|
||||
Baz: t v
|
||||
bar: t
|
||||
Baz: tg vg
|
||||
bar: tg
|
||||
foo: t
|
||||
|
||||
crate::foo
|
||||
Baz: t v
|
||||
Baz: tg vg
|
||||
PrivateStructFoo: t v
|
||||
bar: t
|
||||
|
||||
crate::foo::bar
|
||||
Baz: t v
|
||||
PrivateStructBar: t v
|
||||
PrivateStructFoo: t v
|
||||
bar: t
|
||||
PrivateStructFoo: tg vg
|
||||
bar: tg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -130,9 +130,9 @@ pub(crate) struct PubCrateStruct;
|
|||
",
|
||||
expect![[r#"
|
||||
crate
|
||||
Foo: t
|
||||
PubCrateStruct: t v
|
||||
bar: t
|
||||
Foo: tg
|
||||
PubCrateStruct: tg vg
|
||||
bar: tg
|
||||
foo: t
|
||||
|
||||
crate::foo
|
||||
|
|
@ -160,7 +160,7 @@ pub struct Baz;
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Baz: t v
|
||||
Baz: tg vg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -178,7 +178,7 @@ struct Foo;
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Baz: t v
|
||||
Baz: tg vg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -193,8 +193,8 @@ use self::Foo::*;
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Bar: t v
|
||||
Baz: t v
|
||||
Bar: tg vg
|
||||
Baz: tg vg
|
||||
Foo: t
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -210,8 +210,8 @@ use self::Foo::{*};
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Bar: t v
|
||||
Baz: t v
|
||||
Bar: tg vg
|
||||
Baz: tg vg
|
||||
Foo: t
|
||||
"#]],
|
||||
);
|
||||
|
|
@ -359,7 +359,7 @@ use event::Event;
|
|||
event: t
|
||||
|
||||
crate::event
|
||||
Event: t v
|
||||
Event: t vg
|
||||
serenity: t
|
||||
|
||||
crate::event::serenity
|
||||
|
|
@ -388,10 +388,10 @@ use reexport::*;
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Trait: t
|
||||
Trait: tg
|
||||
defs: t
|
||||
function: v
|
||||
makro: m
|
||||
function: vg
|
||||
makro: mg
|
||||
reexport: t
|
||||
|
||||
crate::defs
|
||||
|
|
@ -400,10 +400,10 @@ use reexport::*;
|
|||
makro: m
|
||||
|
||||
crate::reexport
|
||||
Trait: t
|
||||
function: v
|
||||
Trait: tg
|
||||
function: vg
|
||||
inner: t
|
||||
makro: m
|
||||
makro: mg
|
||||
|
||||
crate::reexport::inner
|
||||
Trait: ti
|
||||
|
|
@ -442,12 +442,12 @@ mod glob_target {
|
|||
ShouldBePrivate: t v
|
||||
|
||||
crate::outer
|
||||
ShouldBePrivate: t v
|
||||
ShouldBePrivate: tg vg
|
||||
inner_superglob: t
|
||||
|
||||
crate::outer::inner_superglob
|
||||
ShouldBePrivate: t v
|
||||
inner_superglob: t
|
||||
ShouldBePrivate: tg vg
|
||||
inner_superglob: tg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -473,20 +473,20 @@ use reexport_2::*;
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Placeholder: t v
|
||||
Placeholder: tg vg
|
||||
libs: t
|
||||
reexport_1: t
|
||||
reexport_1: tg
|
||||
reexport_2: t
|
||||
|
||||
crate::libs
|
||||
Placeholder: t v
|
||||
|
||||
crate::reexport_2
|
||||
Placeholder: t v
|
||||
Placeholder: tg vg
|
||||
reexport_1: t
|
||||
|
||||
crate::reexport_2::reexport_1
|
||||
Placeholder: t v
|
||||
Placeholder: tg vg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,9 +97,9 @@ macro_rules! structs {
|
|||
bar: t
|
||||
|
||||
crate::bar
|
||||
Bar: t
|
||||
Foo: t
|
||||
bar: t
|
||||
Bar: tg
|
||||
Foo: tg
|
||||
bar: tg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -130,9 +130,9 @@ macro_rules! structs {
|
|||
bar: t
|
||||
|
||||
crate::bar
|
||||
Bar: t
|
||||
Foo: t
|
||||
bar: t
|
||||
Bar: tg
|
||||
Foo: tg
|
||||
bar: tg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -169,9 +169,9 @@ macro_rules! inner {
|
|||
bar: t
|
||||
|
||||
crate::bar
|
||||
Bar: t
|
||||
Foo: t
|
||||
bar: t
|
||||
Bar: tg
|
||||
Foo: tg
|
||||
bar: tg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -794,7 +794,7 @@ pub trait Clone {}
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
Clone: t m
|
||||
Clone: tg mg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
@ -1075,9 +1075,9 @@ macro_rules! mbe {
|
|||
"#,
|
||||
expect![[r#"
|
||||
crate
|
||||
DummyTrait: m
|
||||
attribute_macro: m
|
||||
function_like_macro: m
|
||||
DummyTrait: mg
|
||||
attribute_macro: mg
|
||||
function_like_macro: mg
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
use bitflags::bitflags;
|
||||
|
||||
use crate::{
|
||||
item_scope::{ImportId, ImportOrExternCrate, ItemInNs},
|
||||
item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ItemInNs},
|
||||
visibility::Visibility,
|
||||
MacroId, ModuleDefId,
|
||||
};
|
||||
|
|
@ -36,8 +36,8 @@ pub struct Item<Def, Import = ImportId> {
|
|||
}
|
||||
|
||||
pub type TypesItem = Item<ModuleDefId, ImportOrExternCrate>;
|
||||
pub type ValuesItem = Item<ModuleDefId>;
|
||||
pub type MacrosItem = Item<MacroId>;
|
||||
pub type ValuesItem = Item<ModuleDefId, ImportOrGlob>;
|
||||
pub type MacrosItem = Item<MacroId, ImportOrGlob>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
pub struct PerNs {
|
||||
|
|
@ -59,7 +59,7 @@ impl PerNs {
|
|||
PerNs { types: None, values: None, macros: None }
|
||||
}
|
||||
|
||||
pub fn values(def: ModuleDefId, vis: Visibility, import: Option<ImportId>) -> PerNs {
|
||||
pub fn values(def: ModuleDefId, vis: Visibility, import: Option<ImportOrGlob>) -> PerNs {
|
||||
PerNs { types: None, values: Some(Item { def, vis, import }), macros: None }
|
||||
}
|
||||
|
||||
|
|
@ -78,13 +78,13 @@ impl PerNs {
|
|||
values: Some(Item {
|
||||
def: values,
|
||||
vis,
|
||||
import: import.and_then(ImportOrExternCrate::into_import),
|
||||
import: import.and_then(ImportOrExternCrate::import_or_glob),
|
||||
}),
|
||||
macros: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn macros(def: MacroId, vis: Visibility, import: Option<ImportId>) -> PerNs {
|
||||
pub fn macros(def: MacroId, vis: Visibility, import: Option<ImportOrGlob>) -> PerNs {
|
||||
PerNs { types: None, values: None, macros: Some(Item { def, vis, import }) }
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ impl PerNs {
|
|||
self.values.map(|it| it.def)
|
||||
}
|
||||
|
||||
pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportId>)> {
|
||||
pub fn take_values_import(self) -> Option<(ModuleDefId, Option<ImportOrGlob>)> {
|
||||
self.values.map(|it| (it.def, it.import))
|
||||
}
|
||||
|
||||
|
|
@ -116,7 +116,7 @@ impl PerNs {
|
|||
self.macros.map(|it| it.def)
|
||||
}
|
||||
|
||||
pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportId>)> {
|
||||
pub fn take_macros_import(self) -> Option<(MacroId, Option<ImportOrGlob>)> {
|
||||
self.macros.map(|it| (it.def, it.import))
|
||||
}
|
||||
|
||||
|
|
@ -159,14 +159,12 @@ impl PerNs {
|
|||
.map(|it| (ItemInNs::Types(it.def), it.import))
|
||||
.into_iter()
|
||||
.chain(
|
||||
self.values.map(|it| {
|
||||
(ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::Import))
|
||||
}),
|
||||
self.values
|
||||
.map(|it| (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::from))),
|
||||
)
|
||||
.chain(
|
||||
self.macros.map(|it| {
|
||||
(ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::Import))
|
||||
}),
|
||||
self.macros
|
||||
.map(|it| (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::from))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use crate::{
|
|||
db::DefDatabase,
|
||||
generics::{GenericParams, TypeOrConstParamData},
|
||||
hir::{BindingId, ExprId, LabelId},
|
||||
item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE},
|
||||
item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE},
|
||||
lang_item::LangItemTarget,
|
||||
nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo},
|
||||
path::{ModPath, Path, PathKind},
|
||||
|
|
@ -107,7 +107,7 @@ pub enum TypeNs {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ResolveValueResult {
|
||||
ValueNs(ValueNs, Option<ImportId>),
|
||||
ValueNs(ValueNs, Option<ImportOrGlob>),
|
||||
Partial(TypeNs, usize, Option<ImportOrExternCrate>),
|
||||
}
|
||||
|
||||
|
|
@ -485,7 +485,7 @@ impl Resolver {
|
|||
db: &dyn DefDatabase,
|
||||
path: &ModPath,
|
||||
expected_macro_kind: Option<MacroSubNs>,
|
||||
) -> Option<(MacroId, Option<ImportId>)> {
|
||||
) -> Option<(MacroId, Option<ImportOrGlob>)> {
|
||||
let (item_map, module) = self.item_scope();
|
||||
item_map
|
||||
.resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind)
|
||||
|
|
@ -1014,7 +1014,7 @@ impl ModuleItemMap {
|
|||
}
|
||||
}
|
||||
|
||||
fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportId>)> {
|
||||
fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option<ImportOrGlob>)> {
|
||||
let (def, import) = per_ns.take_values_import()?;
|
||||
let res = match def {
|
||||
ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it),
|
||||
|
|
|
|||
|
|
@ -23,15 +23,6 @@ pub struct ModPath {
|
|||
segments: SmallVec<[Name; 1]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct UnescapedModPath<'a>(&'a ModPath);
|
||||
|
||||
impl<'a> UnescapedModPath<'a> {
|
||||
pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
UnescapedDisplay { db, path: self }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum PathKind {
|
||||
Plain,
|
||||
|
|
@ -135,9 +126,11 @@ impl ModPath {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unescaped(&self) -> UnescapedModPath<'_> {
|
||||
UnescapedModPath(self)
|
||||
pub fn display_verbatim<'a>(
|
||||
&'a self,
|
||||
db: &'a dyn crate::db::ExpandDatabase,
|
||||
) -> impl fmt::Display + 'a {
|
||||
Display { db, path: self, edition: None }
|
||||
}
|
||||
|
||||
pub fn display<'a>(
|
||||
|
|
@ -145,7 +138,7 @@ impl ModPath {
|
|||
db: &'a dyn crate::db::ExpandDatabase,
|
||||
edition: Edition,
|
||||
) -> impl fmt::Display + 'a {
|
||||
Display { db, path: self, edition }
|
||||
Display { db, path: self, edition: Some(edition) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -158,23 +151,12 @@ impl Extend<Name> for ModPath {
|
|||
struct Display<'a> {
|
||||
db: &'a dyn ExpandDatabase,
|
||||
path: &'a ModPath,
|
||||
edition: Edition,
|
||||
edition: Option<Edition>,
|
||||
}
|
||||
|
||||
impl fmt::Display for Display<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
display_fmt_path(self.db, self.path, f, Escape::IfNeeded(self.edition))
|
||||
}
|
||||
}
|
||||
|
||||
struct UnescapedDisplay<'a> {
|
||||
db: &'a dyn ExpandDatabase,
|
||||
path: &'a UnescapedModPath<'a>,
|
||||
}
|
||||
|
||||
impl fmt::Display for UnescapedDisplay<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
display_fmt_path(self.db, self.path.0, f, Escape::No)
|
||||
display_fmt_path(self.db, self.path, f, self.edition)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -184,16 +166,11 @@ impl From<Name> for ModPath {
|
|||
}
|
||||
}
|
||||
|
||||
enum Escape {
|
||||
No,
|
||||
IfNeeded(Edition),
|
||||
}
|
||||
|
||||
fn display_fmt_path(
|
||||
db: &dyn ExpandDatabase,
|
||||
path: &ModPath,
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
escaped: Escape,
|
||||
edition: Option<Edition>,
|
||||
) -> fmt::Result {
|
||||
let mut first_segment = true;
|
||||
let mut add_segment = |s| -> fmt::Result {
|
||||
|
|
@ -221,10 +198,10 @@ fn display_fmt_path(
|
|||
f.write_str("::")?;
|
||||
}
|
||||
first_segment = false;
|
||||
match escaped {
|
||||
Escape::IfNeeded(edition) => segment.display(db, edition).fmt(f)?,
|
||||
Escape::No => segment.unescaped().display(db).fmt(f)?,
|
||||
}
|
||||
match edition {
|
||||
Some(edition) => segment.display(db, edition).fmt(f)?,
|
||||
None => fmt::Display::fmt(segment.as_str(), f)?,
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ use std::fmt;
|
|||
|
||||
use intern::{sym, Symbol};
|
||||
use span::{Edition, SyntaxContextId};
|
||||
use syntax::ast;
|
||||
use syntax::utils::is_raw_identifier;
|
||||
use syntax::{ast, format_smolstr};
|
||||
|
||||
/// `Name` is a wrapper around string, which is used in hir for both references
|
||||
/// and declarations. In theory, names should also carry hygiene info, but we are
|
||||
|
|
@ -51,33 +51,26 @@ impl PartialEq<Symbol> for Name {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq<&Symbol> for Name {
|
||||
fn eq(&self, &sym: &&Symbol) -> bool {
|
||||
self.symbol == *sym
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Name> for Symbol {
|
||||
fn eq(&self, name: &Name) -> bool {
|
||||
*self == name.symbol
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct UnescapedName<'a>(&'a Name);
|
||||
|
||||
impl<'a> UnescapedName<'a> {
|
||||
pub fn display(self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a {
|
||||
_ = db;
|
||||
UnescapedDisplay { name: self }
|
||||
}
|
||||
#[doc(hidden)]
|
||||
pub fn display_no_db(self) -> impl fmt::Display + 'a {
|
||||
UnescapedDisplay { name: self }
|
||||
impl PartialEq<Name> for &Symbol {
|
||||
fn eq(&self, name: &Name) -> bool {
|
||||
**self == name.symbol
|
||||
}
|
||||
}
|
||||
|
||||
impl Name {
|
||||
/// Note: this is private to make creating name from random string hard.
|
||||
/// Hopefully, this should allow us to integrate hygiene cleaner in the
|
||||
/// future, and to switch to interned representation of names.
|
||||
fn new_text(text: &str) -> Name {
|
||||
debug_assert!(!text.starts_with("r#"));
|
||||
Name { symbol: Symbol::intern(text), ctx: () }
|
||||
}
|
||||
|
||||
|
|
@ -87,12 +80,15 @@ impl Name {
|
|||
// Can't do that for all `SyntaxContextId`s because it breaks Salsa.
|
||||
ctx.remove_root_edition();
|
||||
_ = ctx;
|
||||
Self::new_text(text)
|
||||
match text.strip_prefix("r#") {
|
||||
Some(text) => Self::new_text(text),
|
||||
None => Self::new_text(text),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_root(text: &str) -> Name {
|
||||
// The edition doesn't matter for hygiene.
|
||||
Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015))
|
||||
Self::new(text, SyntaxContextId::root(Edition::Edition2015))
|
||||
}
|
||||
|
||||
pub fn new_tuple_field(idx: usize) -> Name {
|
||||
|
|
@ -119,12 +115,22 @@ impl Name {
|
|||
}
|
||||
|
||||
pub fn new_lifetime(lt: &ast::Lifetime) -> Name {
|
||||
Self::new_text(lt.text().as_str().trim_start_matches("r#"))
|
||||
let text = lt.text();
|
||||
match text.strip_prefix("'r#") {
|
||||
Some(text) => Self::new_text(&format_smolstr!("'{text}")),
|
||||
None => Self::new_text(text.as_str()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a name from the text of token.
|
||||
fn resolve(raw_text: &str) -> Name {
|
||||
Name::new_text(raw_text.trim_start_matches("r#"))
|
||||
pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
|
||||
debug_assert!(!symbol.as_str().starts_with("r#"));
|
||||
_ = ctx;
|
||||
Self { symbol, ctx: () }
|
||||
}
|
||||
|
||||
// FIXME: This needs to go once we have hygiene
|
||||
pub fn new_symbol_root(sym: Symbol) -> Self {
|
||||
Self::new_symbol(sym, SyntaxContextId::root(Edition::Edition2015))
|
||||
}
|
||||
|
||||
/// A fake name for things missing in the source code.
|
||||
|
|
@ -161,22 +167,19 @@ impl Name {
|
|||
self.symbol.as_str().parse().ok()
|
||||
}
|
||||
|
||||
/// Whether this name needs to be escaped in the given edition via `r#`.
|
||||
pub fn needs_escape(&self, edition: Edition) -> bool {
|
||||
is_raw_identifier(self.symbol.as_str(), edition)
|
||||
}
|
||||
|
||||
/// Returns the text this name represents if it isn't a tuple field.
|
||||
///
|
||||
/// Do not use this for user-facing text, use `display` instead to handle editions properly.
|
||||
// FIXME: This should take a database argument to hide the interning
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.symbol.as_str()
|
||||
}
|
||||
|
||||
// FIXME: Remove this
|
||||
pub fn unescaped(&self) -> UnescapedName<'_> {
|
||||
UnescapedName(self)
|
||||
}
|
||||
|
||||
pub fn needs_escape(&self, edition: Edition) -> bool {
|
||||
is_raw_identifier(self.symbol.as_str(), edition)
|
||||
}
|
||||
|
||||
pub fn display<'a>(
|
||||
&'a self,
|
||||
db: &dyn crate::db::ExpandDatabase,
|
||||
|
|
@ -186,7 +189,7 @@ impl Name {
|
|||
self.display_no_db(edition)
|
||||
}
|
||||
|
||||
// FIXME: Remove this
|
||||
// FIXME: Remove this in favor of `display`, see fixme on `as_str`
|
||||
#[doc(hidden)]
|
||||
pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ {
|
||||
Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) }
|
||||
|
|
@ -195,24 +198,6 @@ impl Name {
|
|||
pub fn symbol(&self) -> &Symbol {
|
||||
&self.symbol
|
||||
}
|
||||
|
||||
pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self {
|
||||
debug_assert!(!symbol.as_str().starts_with("r#"));
|
||||
_ = ctx;
|
||||
Self { symbol, ctx: () }
|
||||
}
|
||||
|
||||
// FIXME: This needs to go once we have hygiene
|
||||
pub fn new_symbol_root(sym: Symbol) -> Self {
|
||||
debug_assert!(!sym.as_str().starts_with("r#"));
|
||||
Self { symbol: sym, ctx: () }
|
||||
}
|
||||
|
||||
// FIXME: Remove this
|
||||
#[inline]
|
||||
pub fn eq_ident(&self, ident: &str) -> bool {
|
||||
self.as_str() == ident.trim_start_matches("r#")
|
||||
}
|
||||
}
|
||||
|
||||
struct Display<'a> {
|
||||
|
|
@ -229,17 +214,6 @@ impl fmt::Display for Display<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
struct UnescapedDisplay<'a> {
|
||||
name: UnescapedName<'a>,
|
||||
}
|
||||
|
||||
impl fmt::Display for UnescapedDisplay<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let symbol = self.name.0.symbol.as_str();
|
||||
fmt::Display::fmt(symbol, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AsName {
|
||||
fn as_name(&self) -> Name;
|
||||
}
|
||||
|
|
@ -248,14 +222,14 @@ impl AsName for ast::NameRef {
|
|||
fn as_name(&self) -> Name {
|
||||
match self.as_tuple_field() {
|
||||
Some(idx) => Name::new_tuple_field(idx),
|
||||
None => Name::resolve(&self.text()),
|
||||
None => Name::new_root(&self.text()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsName for ast::Name {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::resolve(&self.text())
|
||||
Name::new_root(&self.text())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +244,7 @@ impl AsName for ast::NameOrNameRef {
|
|||
|
||||
impl<Span> AsName for tt::Ident<Span> {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::resolve(self.sym.as_str())
|
||||
Name::new_root(self.sym.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -288,6 +262,6 @@ impl AsName for ast::FieldKind {
|
|||
|
||||
impl AsName for base_db::Dependency {
|
||||
fn as_name(&self) -> Name {
|
||||
Name::new_text(&self.name)
|
||||
Name::new_root(&self.name)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ use crate::{
|
|||
TraitEnvironment, Ty, TyBuilder, TyKind,
|
||||
};
|
||||
|
||||
static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10);
|
||||
static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(20);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum AutoderefKind {
|
||||
|
|
@ -39,7 +39,7 @@ pub fn autoderef(
|
|||
) -> impl Iterator<Item = Ty> {
|
||||
let mut table = InferenceTable::new(db, env);
|
||||
let ty = table.instantiate_canonical(ty);
|
||||
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false);
|
||||
let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false);
|
||||
let mut v = Vec::new();
|
||||
while let Some((ty, _steps)) = autoderef.next() {
|
||||
// `ty` may contain unresolved inference variables. Since there's no chance they would be
|
||||
|
|
@ -49,7 +49,7 @@ pub fn autoderef(
|
|||
// If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we
|
||||
// would revisit some already visited types. Stop here to avoid duplication.
|
||||
//
|
||||
// XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't
|
||||
// XXX: The recursion limit for `Autoderef` is currently 20, so `Vec::contains()` shouldn't
|
||||
// be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more
|
||||
// performant.
|
||||
if v.contains(&resolved) {
|
||||
|
|
@ -89,12 +89,18 @@ pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> {
|
|||
at_start: bool,
|
||||
steps: T,
|
||||
explicit: bool,
|
||||
use_receiver_trait: bool,
|
||||
}
|
||||
|
||||
impl<'table, 'db> Autoderef<'table, 'db> {
|
||||
pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self {
|
||||
pub(crate) fn new(
|
||||
table: &'table mut InferenceTable<'db>,
|
||||
ty: Ty,
|
||||
explicit: bool,
|
||||
use_receiver_trait: bool,
|
||||
) -> Self {
|
||||
let ty = table.resolve_ty_shallow(&ty);
|
||||
Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit }
|
||||
Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait }
|
||||
}
|
||||
|
||||
pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] {
|
||||
|
|
@ -107,9 +113,10 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> {
|
|||
table: &'table mut InferenceTable<'db>,
|
||||
ty: Ty,
|
||||
explicit: bool,
|
||||
use_receiver_trait: bool,
|
||||
) -> Self {
|
||||
let ty = table.resolve_ty_shallow(&ty);
|
||||
Autoderef { table, ty, at_start: true, steps: 0, explicit }
|
||||
Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +144,8 @@ impl<T: TrackAutoderefSteps> Iterator for Autoderef<'_, '_, T> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?;
|
||||
let (kind, new_ty) =
|
||||
autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?;
|
||||
|
||||
self.steps.push(kind, &self.ty);
|
||||
self.ty = new_ty;
|
||||
|
|
@ -150,11 +158,12 @@ pub(crate) fn autoderef_step(
|
|||
table: &mut InferenceTable<'_>,
|
||||
ty: Ty,
|
||||
explicit: bool,
|
||||
use_receiver_trait: bool,
|
||||
) -> Option<(AutoderefKind, Ty)> {
|
||||
if let Some(derefed) = builtin_deref(table.db, &ty, explicit) {
|
||||
Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed)))
|
||||
} else {
|
||||
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?))
|
||||
Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -176,6 +185,7 @@ pub(crate) fn builtin_deref<'ty>(
|
|||
pub(crate) fn deref_by_trait(
|
||||
table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>,
|
||||
ty: Ty,
|
||||
use_receiver_trait: bool,
|
||||
) -> Option<Ty> {
|
||||
let _p = tracing::info_span!("deref_by_trait").entered();
|
||||
if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() {
|
||||
|
|
@ -183,14 +193,25 @@ pub(crate) fn deref_by_trait(
|
|||
return None;
|
||||
}
|
||||
|
||||
let deref_trait =
|
||||
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?;
|
||||
let trait_id = || {
|
||||
if use_receiver_trait {
|
||||
if let Some(receiver) =
|
||||
db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait())
|
||||
{
|
||||
return Some(receiver);
|
||||
}
|
||||
}
|
||||
// Old rustc versions might not have `Receiver` trait.
|
||||
// Fallback to `Deref` if they don't
|
||||
db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())
|
||||
};
|
||||
let trait_id = trait_id()?;
|
||||
let target = db
|
||||
.trait_data(deref_trait)
|
||||
.trait_data(trait_id)
|
||||
.associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?;
|
||||
|
||||
let projection = {
|
||||
let b = TyBuilder::subst_for_def(db, deref_trait, None);
|
||||
let b = TyBuilder::subst_for_def(db, trait_id, None);
|
||||
if b.remaining() != 1 {
|
||||
// the Target type + Deref trait should only have one generic parameter,
|
||||
// namely Deref's Self type
|
||||
|
|
|
|||
|
|
@ -231,8 +231,7 @@ impl<'a> DeclValidator<'a> {
|
|||
.filter_map(|(pat_id, pat)| match pat {
|
||||
Pat::Bind { id, .. } => {
|
||||
let bind_name = &body.bindings[*id].name;
|
||||
let mut suggested_text =
|
||||
to_lower_snake_case(&bind_name.unescaped().display_no_db().to_smolstr())?;
|
||||
let mut suggested_text = to_lower_snake_case(bind_name.as_str())?;
|
||||
if is_raw_identifier(&suggested_text, edition) {
|
||||
suggested_text.insert_str(0, "r#");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ use rustc_apfloat::{
|
|||
ieee::{Half as f16, Quad as f128},
|
||||
Float,
|
||||
};
|
||||
use rustc_hash::FxHashSet;
|
||||
use smallvec::SmallVec;
|
||||
use span::Edition;
|
||||
use stdx::never;
|
||||
|
|
@ -87,6 +88,35 @@ pub struct HirFormatter<'a> {
|
|||
omit_verbose_types: bool,
|
||||
closure_style: ClosureStyle,
|
||||
display_target: DisplayTarget,
|
||||
bounds_formatting_ctx: BoundsFormattingCtx,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum BoundsFormattingCtx {
|
||||
Entered {
|
||||
/// We can have recursive bounds like the following case:
|
||||
/// ```rust
|
||||
/// where
|
||||
/// T: Foo,
|
||||
/// T::FooAssoc: Baz<<T::FooAssoc as Bar>::BarAssoc> + Bar
|
||||
/// ```
|
||||
/// So, record the projection types met while formatting bounds and
|
||||
//. prevent recursing into their bounds to avoid infinite loops.
|
||||
projection_tys_met: FxHashSet<ProjectionTy>,
|
||||
},
|
||||
#[default]
|
||||
Exited,
|
||||
}
|
||||
|
||||
impl BoundsFormattingCtx {
|
||||
fn contains(&mut self, proj: &ProjectionTy) -> bool {
|
||||
match self {
|
||||
BoundsFormattingCtx::Entered { projection_tys_met } => {
|
||||
projection_tys_met.contains(proj)
|
||||
}
|
||||
BoundsFormattingCtx::Exited => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HirFormatter<'_> {
|
||||
|
|
@ -97,6 +127,30 @@ impl HirFormatter<'_> {
|
|||
fn end_location_link(&mut self) {
|
||||
self.fmt.end_location_link();
|
||||
}
|
||||
|
||||
fn format_bounds_with<T, F: FnOnce(&mut Self) -> T>(
|
||||
&mut self,
|
||||
target: ProjectionTy,
|
||||
format_bounds: F,
|
||||
) -> T {
|
||||
match self.bounds_formatting_ctx {
|
||||
BoundsFormattingCtx::Entered { ref mut projection_tys_met } => {
|
||||
projection_tys_met.insert(target);
|
||||
format_bounds(self)
|
||||
}
|
||||
BoundsFormattingCtx::Exited => {
|
||||
let mut projection_tys_met = FxHashSet::default();
|
||||
projection_tys_met.insert(target);
|
||||
self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met };
|
||||
let res = format_bounds(self);
|
||||
// Since we want to prevent only the infinite recursions in bounds formatting
|
||||
// and do not want to skip formatting of other separate bounds, clear context
|
||||
// when exiting the formatting of outermost bounds
|
||||
self.bounds_formatting_ctx = BoundsFormattingCtx::Exited;
|
||||
res
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait HirDisplay {
|
||||
|
|
@ -220,6 +274,7 @@ pub trait HirDisplay {
|
|||
closure_style: ClosureStyle::ImplFn,
|
||||
display_target: DisplayTarget::SourceCode { module_id, allow_opaque },
|
||||
show_container_bounds: false,
|
||||
bounds_formatting_ctx: Default::default(),
|
||||
}) {
|
||||
Ok(()) => {}
|
||||
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
|
||||
|
|
@ -427,6 +482,7 @@ impl<T: HirDisplay> HirDisplayWrapper<'_, T> {
|
|||
display_target: self.display_target,
|
||||
closure_style: self.closure_style,
|
||||
show_container_bounds: self.show_container_bounds,
|
||||
bounds_formatting_ctx: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -479,42 +535,46 @@ impl HirDisplay for ProjectionTy {
|
|||
// `<Param as Trait>::Assoc`
|
||||
if !f.display_target.is_source_code() {
|
||||
if let TyKind::Placeholder(idx) = self_ty.kind(Interner) {
|
||||
let db = f.db;
|
||||
let id = from_placeholder_idx(db, *idx);
|
||||
let generics = generics(db.upcast(), id.parent);
|
||||
if !f.bounds_formatting_ctx.contains(self) {
|
||||
let db = f.db;
|
||||
let id = from_placeholder_idx(db, *idx);
|
||||
let generics = generics(db.upcast(), id.parent);
|
||||
|
||||
let substs = generics.placeholder_subst(db);
|
||||
let bounds = db
|
||||
.generic_predicates(id.parent)
|
||||
.iter()
|
||||
.map(|pred| pred.clone().substitute(Interner, &substs))
|
||||
.filter(|wc| match wc.skip_binders() {
|
||||
WhereClause::Implemented(tr) => {
|
||||
match tr.self_type_parameter(Interner).kind(Interner) {
|
||||
TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
|
||||
_ => false,
|
||||
let substs = generics.placeholder_subst(db);
|
||||
let bounds = db
|
||||
.generic_predicates(id.parent)
|
||||
.iter()
|
||||
.map(|pred| pred.clone().substitute(Interner, &substs))
|
||||
.filter(|wc| match wc.skip_binders() {
|
||||
WhereClause::Implemented(tr) => {
|
||||
matches!(
|
||||
tr.self_type_parameter(Interner).kind(Interner),
|
||||
TyKind::Alias(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) {
|
||||
TyKind::Alias(AliasTy::Projection(proj)) => proj == self,
|
||||
_ => false,
|
||||
},
|
||||
// We shouldn't be here if these exist
|
||||
WhereClause::AliasEq(_) => false,
|
||||
WhereClause::LifetimeOutlives(_) => false,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !bounds.is_empty() {
|
||||
return write_bounds_like_dyn_trait_with_prefix(
|
||||
f,
|
||||
"impl",
|
||||
Either::Left(
|
||||
&TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner),
|
||||
),
|
||||
&bounds,
|
||||
SizedByDefault::NotSized,
|
||||
);
|
||||
};
|
||||
WhereClause::TypeOutlives(t) => {
|
||||
matches!(t.ty.kind(Interner), TyKind::Alias(_))
|
||||
}
|
||||
// We shouldn't be here if these exist
|
||||
WhereClause::AliasEq(_) => false,
|
||||
WhereClause::LifetimeOutlives(_) => false,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if !bounds.is_empty() {
|
||||
return f.format_bounds_with(self.clone(), |f| {
|
||||
write_bounds_like_dyn_trait_with_prefix(
|
||||
f,
|
||||
"impl",
|
||||
Either::Left(
|
||||
&TyKind::Alias(AliasTy::Projection(self.clone()))
|
||||
.intern(Interner),
|
||||
),
|
||||
&bounds,
|
||||
SizedByDefault::NotSized,
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1159,6 +1219,7 @@ impl HirDisplay for Ty {
|
|||
prefer_no_std: false,
|
||||
prefer_prelude: true,
|
||||
prefer_absolute: false,
|
||||
allow_unstable: true,
|
||||
},
|
||||
) {
|
||||
write!(f, "{}", path.display(f.db.upcast(), f.edition()))?;
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ impl CapturedItem {
|
|||
/// Converts the place to a name that can be inserted into source code.
|
||||
pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String {
|
||||
let body = db.body(owner);
|
||||
let mut result = body[self.place.local].name.unescaped().display(db.upcast()).to_string();
|
||||
let mut result = body[self.place.local].name.as_str().to_owned();
|
||||
for proj in &self.place.projections {
|
||||
match proj {
|
||||
ProjectionElem::Deref => {}
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ impl InferenceTable<'_> {
|
|||
|
||||
let snapshot = self.snapshot();
|
||||
|
||||
let mut autoderef = Autoderef::new(self, from_ty.clone(), false);
|
||||
let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false);
|
||||
let mut first_error = None;
|
||||
let mut found = None;
|
||||
|
||||
|
|
|
|||
|
|
@ -487,7 +487,7 @@ impl InferenceContext<'_> {
|
|||
}
|
||||
Expr::Call { callee, args, .. } => {
|
||||
let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes);
|
||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false);
|
||||
let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true);
|
||||
let (res, derefed_callee) = loop {
|
||||
let Some((callee_deref_ty, _)) = derefs.next() else {
|
||||
break (None, callee_ty.clone());
|
||||
|
|
@ -854,7 +854,7 @@ impl InferenceContext<'_> {
|
|||
if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) {
|
||||
self.resolve_ty_shallow(derefed)
|
||||
} else {
|
||||
deref_by_trait(&mut self.table, inner_ty)
|
||||
deref_by_trait(&mut self.table, inner_ty, false)
|
||||
.unwrap_or_else(|| self.err_ty())
|
||||
}
|
||||
}
|
||||
|
|
@ -1718,7 +1718,7 @@ impl InferenceContext<'_> {
|
|||
receiver_ty: &Ty,
|
||||
name: &Name,
|
||||
) -> Option<(Ty, Either<FieldId, TupleFieldId>, Vec<Adjustment>, bool)> {
|
||||
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false);
|
||||
let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false);
|
||||
let mut private_field = None;
|
||||
let res = autoderef.by_ref().find_map(|(derefed_ty, _)| {
|
||||
let (field_id, parameters) = match derefed_ty.kind(Interner) {
|
||||
|
|
|
|||
|
|
@ -528,7 +528,7 @@ impl ReceiverAdjustments {
|
|||
let mut ty = table.resolve_ty_shallow(&ty);
|
||||
let mut adjust = Vec::new();
|
||||
for _ in 0..self.autoderefs {
|
||||
match autoderef::autoderef_step(table, ty.clone(), true) {
|
||||
match autoderef::autoderef_step(table, ty.clone(), true, false) {
|
||||
None => {
|
||||
never!("autoderef not possible for {:?}", ty);
|
||||
ty = TyKind::Error.intern(Interner);
|
||||
|
|
@ -1106,7 +1106,8 @@ fn iterate_method_candidates_by_receiver(
|
|||
// be found in any of the derefs of receiver_ty, so we have to go through
|
||||
// that, including raw derefs.
|
||||
table.run_in_snapshot(|table| {
|
||||
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
|
||||
let mut autoderef =
|
||||
autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true);
|
||||
while let Some((self_ty, _)) = autoderef.next() {
|
||||
iterate_inherent_methods(
|
||||
&self_ty,
|
||||
|
|
@ -1123,7 +1124,8 @@ fn iterate_method_candidates_by_receiver(
|
|||
ControlFlow::Continue(())
|
||||
})?;
|
||||
table.run_in_snapshot(|table| {
|
||||
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true);
|
||||
let mut autoderef =
|
||||
autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true);
|
||||
while let Some((self_ty, _)) = autoderef.next() {
|
||||
if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) {
|
||||
// don't try to resolve methods on unknown types
|
||||
|
|
@ -1709,7 +1711,7 @@ fn autoderef_method_receiver(
|
|||
ty: Ty,
|
||||
) -> Vec<(Canonical<Ty>, ReceiverAdjustments)> {
|
||||
let mut deref_chain: Vec<_> = Vec::new();
|
||||
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false);
|
||||
let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true);
|
||||
while let Some((ty, derefs)) = autoderef.next() {
|
||||
deref_chain.push((
|
||||
autoderef.table.canonicalize(ty),
|
||||
|
|
|
|||
|
|
@ -1343,7 +1343,7 @@ fn foo<T: Trait>(a: &T) {
|
|||
fn autoderef_visibility_field() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
//- minicore: receiver
|
||||
mod a {
|
||||
pub struct Foo(pub char);
|
||||
pub struct Bar(i32);
|
||||
|
|
@ -1375,7 +1375,7 @@ fn autoderef_visibility_method() {
|
|||
cov_mark::check!(autoderef_candidate_not_visible);
|
||||
check(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
//- minicore: receiver
|
||||
mod a {
|
||||
pub struct Foo(pub char);
|
||||
impl Foo {
|
||||
|
|
@ -1741,7 +1741,7 @@ fn main() {
|
|||
fn deref_fun_1() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
//- minicore: receiver
|
||||
|
||||
struct A<T, U>(T, U);
|
||||
struct B<T>(T);
|
||||
|
|
@ -1782,7 +1782,7 @@ fn test() {
|
|||
fn deref_fun_2() {
|
||||
check_types(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
//- minicore: receiver
|
||||
|
||||
struct A<T, U>(T, U);
|
||||
struct B<T>(T);
|
||||
|
|
@ -1903,7 +1903,7 @@ pub fn test(generic_args: impl Into<Foo>) {
|
|||
fn bad_inferred_reference_2() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
//- minicore: receiver
|
||||
trait ExactSizeIterator {
|
||||
fn len(&self) -> usize;
|
||||
}
|
||||
|
|
@ -2054,7 +2054,7 @@ fn foo() {
|
|||
fn box_deref_is_builtin() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
//- minicore: receiver
|
||||
use core::ops::Deref;
|
||||
|
||||
#[lang = "owned_box"]
|
||||
|
|
@ -2087,7 +2087,7 @@ fn test() {
|
|||
fn manually_drop_deref_is_not_builtin() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: manually_drop, deref
|
||||
//- minicore: manually_drop, receiver
|
||||
struct Foo;
|
||||
impl Foo {
|
||||
fn foo(&self) {}
|
||||
|
|
@ -2105,7 +2105,7 @@ fn test() {
|
|||
fn mismatched_args_due_to_supertraits_with_deref() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
//- minicore: deref
|
||||
//- minicore: receiver
|
||||
use core::ops::Deref;
|
||||
|
||||
trait Trait1 {
|
||||
|
|
@ -2139,3 +2139,34 @@ fn problem_method<T: Trait3>() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receiver_without_deref_impl() {
|
||||
check(
|
||||
r#"
|
||||
//- minicore: receiver
|
||||
use core::ops::Receiver;
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn foo1(self: &Bar) -> i32 { 42 }
|
||||
fn foo2(self: Bar) -> bool { true }
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
|
||||
impl Receiver for Bar {
|
||||
type Target = Foo;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let bar = Bar;
|
||||
let _v1 = bar.foo1();
|
||||
//^^^ type: i32
|
||||
let _v2 = bar.foo2();
|
||||
//^^^ type: bool
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ use hir_def::{
|
|||
body::BodyDiagnostic,
|
||||
data::{adt::VariantData, TraitFlags},
|
||||
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
|
||||
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat},
|
||||
hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat},
|
||||
item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
|
||||
lang_item::LangItemTarget,
|
||||
layout::{self, ReprOptions, TargetDataLayout},
|
||||
|
|
@ -2470,20 +2470,31 @@ impl Param {
|
|||
}
|
||||
|
||||
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
|
||||
let parent = match self.func {
|
||||
Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it),
|
||||
Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0,
|
||||
_ => return None,
|
||||
};
|
||||
let body = db.body(parent);
|
||||
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
|
||||
Some(Local { parent, binding_id: self_param })
|
||||
} else if let Pat::Bind { id, .. } =
|
||||
&body[body.params[self.idx - body.self_param.is_some() as usize]]
|
||||
{
|
||||
Some(Local { parent, binding_id: *id })
|
||||
} else {
|
||||
None
|
||||
match self.func {
|
||||
Callee::Def(CallableDefId::FunctionId(it)) => {
|
||||
let parent = DefWithBodyId::FunctionId(it);
|
||||
let body = db.body(parent);
|
||||
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
|
||||
Some(Local { parent, binding_id: self_param })
|
||||
} else if let Pat::Bind { id, .. } =
|
||||
&body[body.params[self.idx - body.self_param.is_some() as usize]]
|
||||
{
|
||||
Some(Local { parent, binding_id: *id })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Callee::Closure(closure, _) => {
|
||||
let c = db.lookup_intern_closure(closure.into());
|
||||
let body = db.body(c.0);
|
||||
if let Expr::Closure { args, .. } = &body[c.1] {
|
||||
if let Pat::Bind { id, .. } = &body[args[self.idx]] {
|
||||
return Some(Local { parent: c.0, binding_id: *id });
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2756,6 +2767,15 @@ impl Trait {
|
|||
traits.iter().map(|tr| Trait::from(*tr)).collect()
|
||||
}
|
||||
|
||||
pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq<Name>) -> Option<Function> {
|
||||
db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then(
|
||||
|&(_, it)| match it {
|
||||
AssocItemId::FunctionId(id) => Some(Function { id }),
|
||||
_ => None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn items(self, db: &dyn HirDatabase) -> Vec<AssocItem> {
|
||||
db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect()
|
||||
}
|
||||
|
|
@ -4673,6 +4693,10 @@ impl Type {
|
|||
matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool))
|
||||
}
|
||||
|
||||
pub fn is_str(&self) -> bool {
|
||||
matches!(self.ty.kind(Interner), TyKind::Str)
|
||||
}
|
||||
|
||||
pub fn is_never(&self) -> bool {
|
||||
matches!(self.ty.kind(Interner), TyKind::Never)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> {
|
|||
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
|
||||
}
|
||||
|
||||
pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
||||
self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call)
|
||||
/// Env is used to derive the trait environment
|
||||
// FIXME: better api for the trait environment
|
||||
pub fn resolve_trait_impl_method(
|
||||
&self,
|
||||
env: Type,
|
||||
trait_: Trait,
|
||||
func: Function,
|
||||
subst: impl IntoIterator<Item = Type>,
|
||||
) -> Option<Function> {
|
||||
let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None);
|
||||
for s in subst {
|
||||
substs = substs.push(s.ty);
|
||||
}
|
||||
Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into())
|
||||
}
|
||||
|
||||
fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option<StructId> {
|
||||
|
|
@ -1471,6 +1483,8 @@ impl<'db> SemanticsImpl<'db> {
|
|||
self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr)
|
||||
}
|
||||
|
||||
// This does not resolve the method call to the correct trait impl!
|
||||
// We should probably fix that.
|
||||
pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option<Callable> {
|
||||
self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -322,68 +322,6 @@ impl SourceAnalyzer {
|
|||
}
|
||||
}
|
||||
|
||||
// If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str.
|
||||
pub(crate) fn resolve_known_blanket_dual_impls(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
call: &ast::MethodCallExpr,
|
||||
) -> Option<Function> {
|
||||
// e.g. if the method call is let b = a.into(),
|
||||
// - receiver_type is A (type of a)
|
||||
// - return_type is B (type of b)
|
||||
// We will find the definition of B::from(a: A).
|
||||
let callable = self.resolve_method_call_as_callable(db, call)?;
|
||||
let (_, receiver_type) = callable.receiver_param(db)?;
|
||||
let return_type = callable.return_type();
|
||||
let (search_method, substs) = match call.name_ref()?.text().as_str() {
|
||||
"into" => {
|
||||
let trait_ =
|
||||
self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?;
|
||||
(
|
||||
self.trait_fn(db, trait_, "from")?,
|
||||
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
|
||||
.push(return_type.ty)
|
||||
.push(receiver_type.ty)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
"try_into" => {
|
||||
let trait_ = self
|
||||
.resolver
|
||||
.resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?;
|
||||
(
|
||||
self.trait_fn(db, trait_, "try_from")?,
|
||||
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
|
||||
// If the method is try_into() or parse(), return_type is Result<T, Error>.
|
||||
// Get T from type arguments of Result<T, Error>.
|
||||
.push(return_type.type_arguments().next()?.ty)
|
||||
.push(receiver_type.ty)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
"parse" => {
|
||||
let trait_ =
|
||||
self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?;
|
||||
(
|
||||
self.trait_fn(db, trait_, "from_str")?,
|
||||
hir_ty::TyBuilder::subst_for_def(db, trait_, None)
|
||||
.push(return_type.type_arguments().next()?.ty)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs);
|
||||
// If found_method == search_method, the method in trait itself is resolved.
|
||||
// It means the blanket dual impl is not found.
|
||||
if found_method == search_method {
|
||||
None
|
||||
} else {
|
||||
Some(found_method.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_expr_as_callable(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
|
|
@ -1309,18 +1247,6 @@ impl SourceAnalyzer {
|
|||
Some((trait_id, fn_id))
|
||||
}
|
||||
|
||||
fn trait_fn(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
trait_id: TraitId,
|
||||
method_name: &str,
|
||||
) -> Option<FunctionId> {
|
||||
db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item {
|
||||
AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
||||
fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> {
|
||||
self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
use either::Either;
|
||||
use hir_def::{
|
||||
db::DefDatabase,
|
||||
item_scope::{ImportId, ImportOrExternCrate},
|
||||
item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob},
|
||||
per_ns::Item,
|
||||
src::{HasChildSource, HasSource},
|
||||
visibility::{Visibility, VisibilityExplicitness},
|
||||
|
|
@ -55,9 +55,10 @@ impl DeclarationLocation {
|
|||
}
|
||||
|
||||
/// Represents an outstanding module that the symbol collector must collect symbols from.
|
||||
#[derive(Debug)]
|
||||
struct SymbolCollectorWork {
|
||||
module_id: ModuleId,
|
||||
parent: Option<DefWithBodyId>,
|
||||
parent: Option<Name>,
|
||||
}
|
||||
|
||||
pub struct SymbolCollector<'a> {
|
||||
|
|
@ -81,7 +82,15 @@ impl<'a> SymbolCollector<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> {
|
||||
let mut symbol_collector = SymbolCollector::new(db);
|
||||
symbol_collector.collect(module);
|
||||
symbol_collector.finish()
|
||||
}
|
||||
|
||||
pub fn collect(&mut self, module: Module) {
|
||||
let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered();
|
||||
tracing::info!(?module, "SymbolCollector::collect",);
|
||||
self.edition = module.krate().edition(self.db);
|
||||
|
||||
// The initial work is the root module we're collecting, additional work will
|
||||
|
|
@ -97,16 +106,12 @@ impl<'a> SymbolCollector<'a> {
|
|||
self.symbols.into_iter().collect()
|
||||
}
|
||||
|
||||
pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> {
|
||||
let mut symbol_collector = SymbolCollector::new(db);
|
||||
symbol_collector.collect(module);
|
||||
symbol_collector.finish()
|
||||
}
|
||||
|
||||
fn do_work(&mut self, work: SymbolCollectorWork) {
|
||||
let _p = tracing::info_span!("SymbolCollector::do_work", ?work).entered();
|
||||
tracing::info!(?work, "SymbolCollector::do_work");
|
||||
self.db.unwind_if_cancelled();
|
||||
|
||||
let parent_name = work.parent.and_then(|id| self.def_with_body_id_name(id));
|
||||
let parent_name = work.parent.map(|name| name.as_str().to_smolstr());
|
||||
self.with_container_name(parent_name, |s| s.collect_from_module(work.module_id));
|
||||
}
|
||||
|
||||
|
|
@ -116,18 +121,18 @@ impl<'a> SymbolCollector<'a> {
|
|||
ModuleDefId::ModuleId(id) => this.push_module(id, name),
|
||||
ModuleDefId::FunctionId(id) => {
|
||||
this.push_decl(id, name, false);
|
||||
this.collect_from_body(id);
|
||||
this.collect_from_body(id, Some(name.clone()));
|
||||
}
|
||||
ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false),
|
||||
ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false),
|
||||
ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false),
|
||||
ModuleDefId::ConstId(id) => {
|
||||
this.push_decl(id, name, false);
|
||||
this.collect_from_body(id);
|
||||
this.collect_from_body(id, Some(name.clone()));
|
||||
}
|
||||
ModuleDefId::StaticId(id) => {
|
||||
this.push_decl(id, name, false);
|
||||
this.collect_from_body(id);
|
||||
this.collect_from_body(id, Some(name.clone()));
|
||||
}
|
||||
ModuleDefId::TraitId(id) => {
|
||||
this.push_decl(id, name, false);
|
||||
|
|
@ -153,24 +158,32 @@ impl<'a> SymbolCollector<'a> {
|
|||
// Nested trees are very common, so a cache here will hit a lot.
|
||||
let import_child_source_cache = &mut FxHashMap::default();
|
||||
|
||||
let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| {
|
||||
let is_explicit_import = |vis| match vis {
|
||||
Visibility::Public => true,
|
||||
Visibility::Module(_, VisibilityExplicitness::Explicit) => true,
|
||||
Visibility::Module(_, VisibilityExplicitness::Implicit) => false,
|
||||
};
|
||||
|
||||
let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId, vis| {
|
||||
let source = import_child_source_cache
|
||||
.entry(i.import)
|
||||
.or_insert_with(|| i.import.child_source(this.db.upcast()));
|
||||
.entry(i.use_)
|
||||
.or_insert_with(|| i.use_.child_source(this.db.upcast()));
|
||||
let Some(use_tree_src) = source.value.get(i.idx) else { return };
|
||||
let Some(name_ptr) = use_tree_src
|
||||
.rename()
|
||||
.and_then(|rename| rename.name())
|
||||
.map(Either::Left)
|
||||
.or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))
|
||||
.map(|it| AstPtr::new(&it))
|
||||
else {
|
||||
let rename = use_tree_src.rename().and_then(|rename| rename.name());
|
||||
let name_syntax = match rename {
|
||||
Some(name) => Some(Either::Left(name)),
|
||||
None if is_explicit_import(vis) => {
|
||||
(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))()
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
let Some(name_syntax) = name_syntax else {
|
||||
return;
|
||||
};
|
||||
let dec_loc = DeclarationLocation {
|
||||
hir_file_id: source.file_id,
|
||||
ptr: SyntaxNodePtr::new(use_tree_src.syntax()),
|
||||
name_ptr,
|
||||
name_ptr: AstPtr::new(&name_syntax),
|
||||
};
|
||||
this.symbols.insert(FileSymbol {
|
||||
name: name.symbol().clone(),
|
||||
|
|
@ -183,23 +196,23 @@ impl<'a> SymbolCollector<'a> {
|
|||
};
|
||||
|
||||
let push_extern_crate =
|
||||
|this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| {
|
||||
|this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId, vis| {
|
||||
let loc = i.lookup(this.db.upcast());
|
||||
let source = loc.source(this.db.upcast());
|
||||
let Some(name_ptr) = source
|
||||
.value
|
||||
.rename()
|
||||
.and_then(|rename| rename.name())
|
||||
.map(Either::Left)
|
||||
.or_else(|| source.value.name_ref().map(Either::Right))
|
||||
.map(|it| AstPtr::new(&it))
|
||||
else {
|
||||
let rename = source.value.rename().and_then(|rename| rename.name());
|
||||
|
||||
let name_syntax = match rename {
|
||||
Some(name) => Some(Either::Left(name)),
|
||||
None if is_explicit_import(vis) => None,
|
||||
None => source.value.name_ref().map(Either::Right),
|
||||
};
|
||||
let Some(name_syntax) = name_syntax else {
|
||||
return;
|
||||
};
|
||||
let dec_loc = DeclarationLocation {
|
||||
hir_file_id: source.file_id,
|
||||
ptr: SyntaxNodePtr::new(source.value.syntax()),
|
||||
name_ptr,
|
||||
name_ptr: AstPtr::new(&name_syntax),
|
||||
};
|
||||
this.symbols.insert(FileSymbol {
|
||||
name: name.symbol().clone(),
|
||||
|
|
@ -211,18 +224,6 @@ impl<'a> SymbolCollector<'a> {
|
|||
});
|
||||
};
|
||||
|
||||
let is_explicit_import = |vis| {
|
||||
match vis {
|
||||
Visibility::Module(_, VisibilityExplicitness::Explicit) => true,
|
||||
Visibility::Module(_, VisibilityExplicitness::Implicit) => {
|
||||
// consider imports in the crate root explicit, as these are visibly
|
||||
// crate-wide anyways
|
||||
module_id.is_crate_root()
|
||||
}
|
||||
Visibility::Public => true,
|
||||
}
|
||||
};
|
||||
|
||||
let def_map = module_id.def_map(self.db.upcast());
|
||||
let scope = &def_map[module_id.local_id].scope;
|
||||
|
||||
|
|
@ -232,14 +233,14 @@ impl<'a> SymbolCollector<'a> {
|
|||
|
||||
for (name, Item { def, vis, import }) in scope.types() {
|
||||
if let Some(i) = import {
|
||||
if is_explicit_import(vis) {
|
||||
match i {
|
||||
ImportOrExternCrate::Import(i) => push_import(self, i, name, def),
|
||||
ImportOrExternCrate::ExternCrate(i) => {
|
||||
push_extern_crate(self, i, name, def)
|
||||
}
|
||||
match i {
|
||||
ImportOrExternCrate::Import(i) => push_import(self, i, name, def, vis),
|
||||
ImportOrExternCrate::Glob(_) => (),
|
||||
ImportOrExternCrate::ExternCrate(i) => {
|
||||
push_extern_crate(self, i, name, def, vis)
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
// self is a declaration
|
||||
|
|
@ -248,8 +249,9 @@ impl<'a> SymbolCollector<'a> {
|
|||
|
||||
for (name, Item { def, vis, import }) in scope.macros() {
|
||||
if let Some(i) = import {
|
||||
if is_explicit_import(vis) {
|
||||
push_import(self, i, name, def.into());
|
||||
match i {
|
||||
ImportOrGlob::Import(i) => push_import(self, i, name, def.into(), vis),
|
||||
ImportOrGlob::Glob(_) => (),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -259,8 +261,9 @@ impl<'a> SymbolCollector<'a> {
|
|||
|
||||
for (name, Item { def, vis, import }) in scope.values() {
|
||||
if let Some(i) = import {
|
||||
if is_explicit_import(vis) {
|
||||
push_import(self, i, name, def);
|
||||
match i {
|
||||
ImportOrGlob::Import(i) => push_import(self, i, name, def, vis),
|
||||
ImportOrGlob::Glob(_) => (),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
@ -269,7 +272,7 @@ impl<'a> SymbolCollector<'a> {
|
|||
}
|
||||
|
||||
for const_id in scope.unnamed_consts() {
|
||||
self.collect_from_body(const_id);
|
||||
self.collect_from_body(const_id, None);
|
||||
}
|
||||
|
||||
for (name, id) in scope.legacy_macros() {
|
||||
|
|
@ -285,7 +288,7 @@ impl<'a> SymbolCollector<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn collect_from_body(&mut self, body_id: impl Into<DefWithBodyId>) {
|
||||
fn collect_from_body(&mut self, body_id: impl Into<DefWithBodyId>, name: Option<Name>) {
|
||||
let body_id = body_id.into();
|
||||
let body = self.db.body(body_id);
|
||||
|
||||
|
|
@ -294,7 +297,7 @@ impl<'a> SymbolCollector<'a> {
|
|||
for (id, _) in def_map.modules() {
|
||||
self.work.push(SymbolCollectorWork {
|
||||
module_id: def_map.module_id(id),
|
||||
parent: Some(body_id),
|
||||
parent: name.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -333,24 +336,6 @@ impl<'a> SymbolCollector<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option<SmolStr> {
|
||||
match body_id {
|
||||
DefWithBodyId::FunctionId(id) => {
|
||||
Some(self.db.function_data(id).name.display_no_db(self.edition).to_smolstr())
|
||||
}
|
||||
DefWithBodyId::StaticId(id) => {
|
||||
Some(self.db.static_data(id).name.display_no_db(self.edition).to_smolstr())
|
||||
}
|
||||
DefWithBodyId::ConstId(id) => {
|
||||
Some(self.db.const_data(id).name.as_ref()?.display_no_db(self.edition).to_smolstr())
|
||||
}
|
||||
DefWithBodyId::VariantId(id) => {
|
||||
Some(self.db.enum_variant_data(id).name.display_no_db(self.edition).to_smolstr())
|
||||
}
|
||||
DefWithBodyId::InTypeConstId(_) => Some("in type const".into()),
|
||||
}
|
||||
}
|
||||
|
||||
fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) {
|
||||
match assoc_item_id {
|
||||
AssocItemId::FunctionId(id) => self.push_decl(id, name, true),
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ impl AssistConfig {
|
|||
prefer_no_std: self.prefer_no_std,
|
||||
prefer_prelude: self.prefer_prelude,
|
||||
prefer_absolute: self.prefer_absolute,
|
||||
allow_unstable: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_>
|
|||
};
|
||||
// Verify this is `bool::then` that is being called.
|
||||
let func = ctx.sema.resolve_method_call(&mcall)?;
|
||||
if !func.name(ctx.sema.db).eq_ident("then") {
|
||||
if func.name(ctx.sema.db) != sym::then {
|
||||
return None;
|
||||
}
|
||||
let assoc = func.as_assoc_item(ctx.sema.db)?;
|
||||
|
|
|
|||
|
|
@ -343,11 +343,9 @@ fn compute_closure_type_params(
|
|||
let mut mentioned_names = mentioned_generic_params
|
||||
.iter()
|
||||
.filter_map(|param| match param {
|
||||
hir::GenericParam::TypeParam(param) => {
|
||||
Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr())
|
||||
}
|
||||
hir::GenericParam::TypeParam(param) => Some(param.name(ctx.db()).as_str().to_smolstr()),
|
||||
hir::GenericParam::ConstParam(param) => {
|
||||
Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr())
|
||||
Some(param.name(ctx.db()).as_str().to_smolstr())
|
||||
}
|
||||
hir::GenericParam::LifetimeParam(_) => None,
|
||||
})
|
||||
|
|
@ -390,7 +388,7 @@ fn compute_closure_type_params(
|
|||
let has_name = syntax
|
||||
.descendants()
|
||||
.filter_map(ast::NameOrNameRef::cast)
|
||||
.any(|name| mentioned_names.contains(&*name.text()));
|
||||
.any(|name| mentioned_names.contains(name.text().trim_start_matches("r#")));
|
||||
let mut has_new_params = false;
|
||||
if has_name {
|
||||
syntax
|
||||
|
|
|
|||
|
|
@ -170,7 +170,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va
|
|||
),
|
||||
_ => false,
|
||||
})
|
||||
.any(|(name, _)| name.eq_ident(variant_name.text().as_str()))
|
||||
.any(|(name, _)| name.as_str() == variant_name.text().trim_start_matches("r#"))
|
||||
}
|
||||
|
||||
fn extract_generic_params(
|
||||
|
|
|
|||
|
|
@ -1672,8 +1672,8 @@ macro_rules! vec {
|
|||
() => {Vec}
|
||||
}
|
||||
fn main() {
|
||||
let $0vec = vec![];
|
||||
let _ = vec;
|
||||
let $0items = vec![];
|
||||
let _ = items;
|
||||
}
|
||||
"#,
|
||||
"Extract into variable",
|
||||
|
|
@ -1696,8 +1696,8 @@ macro_rules! vec {
|
|||
() => {Vec}
|
||||
}
|
||||
fn main() {
|
||||
const $0VEC: Vec = vec![];
|
||||
let _ = VEC;
|
||||
const $0ITEMS: Vec = vec![];
|
||||
let _ = ITEMS;
|
||||
}
|
||||
"#,
|
||||
"Extract into constant",
|
||||
|
|
@ -1720,8 +1720,8 @@ macro_rules! vec {
|
|||
() => {Vec}
|
||||
}
|
||||
fn main() {
|
||||
static $0VEC: Vec = vec![];
|
||||
let _ = VEC;
|
||||
static $0ITEMS: Vec = vec![];
|
||||
let _ = ITEMS;
|
||||
}
|
||||
"#,
|
||||
"Extract into static",
|
||||
|
|
@ -2019,8 +2019,8 @@ impl<T> Vec<T> {
|
|||
}
|
||||
|
||||
fn foo(s: &mut S) {
|
||||
let $0vec = &mut s.vec;
|
||||
vec.push(0);
|
||||
let $0items = &mut s.vec;
|
||||
items.push(0);
|
||||
}"#,
|
||||
"Extract into variable",
|
||||
);
|
||||
|
|
@ -2106,8 +2106,8 @@ impl<T> Vec<T> {
|
|||
}
|
||||
|
||||
fn foo(f: &mut Y) {
|
||||
let $0vec = &mut f.field.field.vec;
|
||||
vec.push(0);
|
||||
let $0items = &mut f.field.field.vec;
|
||||
items.push(0);
|
||||
}"#,
|
||||
"Extract into variable",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>)
|
|||
let (_, def) = module
|
||||
.scope(ctx.db(), None)
|
||||
.into_iter()
|
||||
.find(|(name, _)| name.eq_ident(name_ref.text().as_str()))?;
|
||||
.find(|(name, _)| name.as_str() == name_ref.text().trim_start_matches("r#"))?;
|
||||
let ScopeDef::ModuleDef(def) = def else {
|
||||
return None;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use ide_db::{
|
|||
assists::{AssistId, AssistKind},
|
||||
base_db::AnchoredPathBuf,
|
||||
};
|
||||
use syntax::{ast, AstNode};
|
||||
use syntax::{ast, AstNode, ToSmolStr};
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
|
|
@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
|
|||
}
|
||||
|
||||
let target = source_file.syntax().text_range();
|
||||
let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string();
|
||||
let module_name = module.name(ctx.db())?.as_str().to_smolstr();
|
||||
let path = format!("../{module_name}.rs");
|
||||
let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path };
|
||||
acc.add(
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) ->
|
|||
.string_value_unescape()
|
||||
.is_none() =>
|
||||
{
|
||||
format_to!(buf, "{}/", name.unescaped().display(db))
|
||||
format_to!(buf, "{}/", name.as_str())
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use ide_db::{
|
|||
assists::{AssistId, AssistKind},
|
||||
base_db::AnchoredPathBuf,
|
||||
};
|
||||
use syntax::{ast, AstNode};
|
||||
use syntax::{ast, AstNode, ToSmolStr};
|
||||
|
||||
use crate::{
|
||||
assist_context::{AssistContext, Assists},
|
||||
|
|
@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
|||
}
|
||||
|
||||
let target = source_file.syntax().text_range();
|
||||
let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string();
|
||||
let module_name = module.name(ctx.db())?.as_str().to_smolstr();
|
||||
let path = format!("./{module_name}/mod.rs");
|
||||
let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path };
|
||||
acc.add(
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ fn find_trait_method(
|
|||
if let Some(hir::AssocItem::Function(method)) =
|
||||
trait_.items(db).into_iter().find(|item: &hir::AssocItem| {
|
||||
item.name(db)
|
||||
.map(|name| name.eq_ident(trait_method_name.text().as_str()))
|
||||
.map(|name| name.as_str() == trait_method_name.text().trim_start_matches("r#"))
|
||||
.unwrap_or(false)
|
||||
})
|
||||
{
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ fn compute_fields_ranks(
|
|||
.fields(ctx.db())
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(idx, field)| (field.name(ctx.db()).unescaped().display(ctx.db()).to_string(), idx))
|
||||
.map(|(idx, field)| (field.name(ctx.db()).as_str().to_owned(), idx))
|
||||
.collect();
|
||||
|
||||
Some(res)
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ fn compute_item_ranks(
|
|||
.iter()
|
||||
.flat_map(|i| i.name(ctx.db()))
|
||||
.enumerate()
|
||||
.map(|(idx, name)| (name.unescaped().display(ctx.db()).to_string(), idx))
|
||||
.map(|(idx, name)| (name.as_str().to_owned(), idx))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,9 +188,6 @@ impl Completions {
|
|||
resolution: hir::ScopeDef,
|
||||
doc_aliases: Vec<syntax::SmolStr>,
|
||||
) {
|
||||
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.def_is_visible(&resolution) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -216,9 +213,6 @@ impl Completions {
|
|||
local_name: hir::Name,
|
||||
resolution: hir::ScopeDef,
|
||||
) {
|
||||
if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.def_is_visible(&resolution) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -241,7 +235,7 @@ impl Completions {
|
|||
path_ctx: &PathCompletionCtx,
|
||||
e: hir::Enum,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&e.attrs(ctx.db))) {
|
||||
if !ctx.check_stability_and_hidden(e) {
|
||||
return;
|
||||
}
|
||||
e.variants(ctx.db)
|
||||
|
|
@ -257,9 +251,6 @@ impl Completions {
|
|||
local_name: hir::Name,
|
||||
doc_aliases: Vec<syntax::SmolStr>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&module.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
self.add_path_resolution(
|
||||
ctx,
|
||||
path_ctx,
|
||||
|
|
@ -276,9 +267,6 @@ impl Completions {
|
|||
mac: hir::Macro,
|
||||
local_name: hir::Name,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&mac.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&mac) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -302,9 +290,6 @@ impl Completions {
|
|||
func: hir::Function,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&func) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -332,9 +317,6 @@ impl Completions {
|
|||
receiver: Option<SmolStr>,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&func) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -362,9 +344,6 @@ impl Completions {
|
|||
func: hir::Function,
|
||||
import: LocatedImport,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&func.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&func) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -387,9 +366,6 @@ impl Completions {
|
|||
}
|
||||
|
||||
pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) {
|
||||
if !ctx.check_stability(Some(&konst.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&konst) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -406,9 +382,6 @@ impl Completions {
|
|||
ctx: &CompletionContext<'_>,
|
||||
type_alias: hir::TypeAlias,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&type_alias) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -438,7 +411,7 @@ impl Completions {
|
|||
variant: hir::Variant,
|
||||
path: hir::ModPath,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
|
||||
if !ctx.check_stability_and_hidden(variant) {
|
||||
return;
|
||||
}
|
||||
if let Some(builder) =
|
||||
|
|
@ -455,7 +428,7 @@ impl Completions {
|
|||
variant: hir::Variant,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
|
||||
if !ctx.check_stability_and_hidden(variant) {
|
||||
return;
|
||||
}
|
||||
if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx {
|
||||
|
|
@ -479,9 +452,6 @@ impl Completions {
|
|||
field: hir::Field,
|
||||
ty: &hir::Type,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&field.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let is_private_editable = match ctx.is_visible(&field) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
|
|
@ -506,12 +476,18 @@ impl Completions {
|
|||
path: Option<hir::ModPath>,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
if let Some(builder) =
|
||||
render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name)
|
||||
{
|
||||
let is_private_editable = match ctx.is_visible(&strukt) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
Visible::No => return,
|
||||
};
|
||||
if let Some(builder) = render_struct_literal(
|
||||
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||
path_ctx,
|
||||
strukt,
|
||||
path,
|
||||
local_name,
|
||||
) {
|
||||
self.add(builder.build(ctx.db));
|
||||
}
|
||||
}
|
||||
|
|
@ -523,10 +499,17 @@ impl Completions {
|
|||
path: Option<hir::ModPath>,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&un.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
let item = render_union_literal(RenderContext::new(ctx), un, path, local_name);
|
||||
let is_private_editable = match ctx.is_visible(&un) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
Visible::No => return,
|
||||
};
|
||||
let item = render_union_literal(
|
||||
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||
un,
|
||||
path,
|
||||
local_name,
|
||||
);
|
||||
self.add_opt(item);
|
||||
}
|
||||
|
||||
|
|
@ -571,7 +554,7 @@ impl Completions {
|
|||
variant: hir::Variant,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
|
||||
if !ctx.check_stability_and_hidden(variant) {
|
||||
return;
|
||||
}
|
||||
self.add_opt(render_variant_pat(
|
||||
|
|
@ -591,7 +574,7 @@ impl Completions {
|
|||
variant: hir::Variant,
|
||||
path: hir::ModPath,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&variant.attrs(ctx.db))) {
|
||||
if !ctx.check_stability_and_hidden(variant) {
|
||||
return;
|
||||
}
|
||||
let path = Some(&path);
|
||||
|
|
@ -612,10 +595,17 @@ impl Completions {
|
|||
strukt: hir::Struct,
|
||||
local_name: Option<hir::Name>,
|
||||
) {
|
||||
if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) {
|
||||
return;
|
||||
}
|
||||
self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name));
|
||||
let is_private_editable = match ctx.is_visible(&strukt) {
|
||||
Visible::Yes => false,
|
||||
Visible::Editable => true,
|
||||
Visible::No => return,
|
||||
};
|
||||
self.add_opt(render_struct_pat(
|
||||
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||
pattern_ctx,
|
||||
strukt,
|
||||
local_name,
|
||||
));
|
||||
}
|
||||
|
||||
pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) {
|
||||
|
|
@ -660,7 +650,7 @@ fn enum_variants_with_paths(
|
|||
if let Some(path) = ctx.module.find_path(
|
||||
ctx.db,
|
||||
hir::ModuleDef::from(variant),
|
||||
ctx.config.import_path_config(),
|
||||
ctx.config.import_path_config(ctx.is_nightly),
|
||||
) {
|
||||
// Variants with trivial paths are already added by the existing completion logic,
|
||||
// so we should avoid adding these twice
|
||||
|
|
|
|||
|
|
@ -42,31 +42,38 @@ pub(crate) fn complete_dot(
|
|||
item.detail("expr.await");
|
||||
item.add_to(acc, ctx.db);
|
||||
|
||||
// Completions that skip `.await`, e.g. `.await.foo()`.
|
||||
let dot_access_kind = match &dot_access.kind {
|
||||
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
|
||||
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
|
||||
}
|
||||
it @ DotAccessKind::Method { .. } => *it,
|
||||
};
|
||||
let dot_access = DotAccess {
|
||||
receiver: dot_access.receiver.clone(),
|
||||
receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }),
|
||||
kind: dot_access_kind,
|
||||
ctx: dot_access.ctx,
|
||||
};
|
||||
complete_fields(
|
||||
acc,
|
||||
ctx,
|
||||
&future_output,
|
||||
|acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty),
|
||||
|acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty),
|
||||
is_field_access,
|
||||
is_method_access_with_parens,
|
||||
);
|
||||
complete_methods(ctx, &future_output, &traits_in_scope, |func| {
|
||||
acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None)
|
||||
});
|
||||
if ctx.config.enable_auto_await {
|
||||
// Completions that skip `.await`, e.g. `.await.foo()`.
|
||||
let dot_access_kind = match &dot_access.kind {
|
||||
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
|
||||
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
|
||||
}
|
||||
it @ DotAccessKind::Method { .. } => *it,
|
||||
};
|
||||
let dot_access = DotAccess {
|
||||
receiver: dot_access.receiver.clone(),
|
||||
receiver_ty: Some(hir::TypeInfo {
|
||||
original: future_output.clone(),
|
||||
adjusted: None,
|
||||
}),
|
||||
kind: dot_access_kind,
|
||||
ctx: dot_access.ctx,
|
||||
};
|
||||
complete_fields(
|
||||
acc,
|
||||
ctx,
|
||||
&future_output,
|
||||
|acc, field, ty| {
|
||||
acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty)
|
||||
},
|
||||
|acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty),
|
||||
is_field_access,
|
||||
is_method_access_with_parens,
|
||||
);
|
||||
complete_methods(ctx, &future_output, &traits_in_scope, |func| {
|
||||
acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
complete_fields(
|
||||
|
|
@ -82,39 +89,41 @@ pub(crate) fn complete_dot(
|
|||
acc.add_method(ctx, dot_access, func, None, None)
|
||||
});
|
||||
|
||||
// FIXME:
|
||||
// Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
|
||||
// its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
|
||||
// Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid
|
||||
let iter = receiver_ty
|
||||
.strip_references()
|
||||
.add_reference(hir::Mutability::Shared)
|
||||
.into_iterator_iter(ctx.db)
|
||||
.map(|ty| (ty, SmolStr::new_static("iter()")));
|
||||
// Does <receiver_ty as IntoIterator>::IntoIter` exist?
|
||||
let into_iter = || {
|
||||
receiver_ty
|
||||
.clone()
|
||||
if ctx.config.enable_auto_iter {
|
||||
// FIXME:
|
||||
// Checking for the existence of `iter()` is complicated in our setup, because we need to substitute
|
||||
// its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`.
|
||||
// Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid
|
||||
let iter = receiver_ty
|
||||
.strip_references()
|
||||
.add_reference(hir::Mutability::Shared)
|
||||
.into_iterator_iter(ctx.db)
|
||||
.map(|ty| (ty, SmolStr::new_static("into_iter()")))
|
||||
};
|
||||
if let Some((iter, iter_sym)) = iter.or_else(into_iter) {
|
||||
// Skip iterators, e.g. complete `.iter().filter_map()`.
|
||||
let dot_access_kind = match &dot_access.kind {
|
||||
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
|
||||
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
|
||||
}
|
||||
it @ DotAccessKind::Method { .. } => *it,
|
||||
.map(|ty| (ty, SmolStr::new_static("iter()")));
|
||||
// Does <receiver_ty as IntoIterator>::IntoIter` exist?
|
||||
let into_iter = || {
|
||||
receiver_ty
|
||||
.clone()
|
||||
.into_iterator_iter(ctx.db)
|
||||
.map(|ty| (ty, SmolStr::new_static("into_iter()")))
|
||||
};
|
||||
let dot_access = DotAccess {
|
||||
receiver: dot_access.receiver.clone(),
|
||||
receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }),
|
||||
kind: dot_access_kind,
|
||||
ctx: dot_access.ctx,
|
||||
};
|
||||
complete_methods(ctx, &iter, &traits_in_scope, |func| {
|
||||
acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None)
|
||||
});
|
||||
if let Some((iter, iter_sym)) = iter.or_else(into_iter) {
|
||||
// Skip iterators, e.g. complete `.iter().filter_map()`.
|
||||
let dot_access_kind = match &dot_access.kind {
|
||||
DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => {
|
||||
DotAccessKind::Field { receiver_is_ambiguous_float_literal: false }
|
||||
}
|
||||
it @ DotAccessKind::Method { .. } => *it,
|
||||
};
|
||||
let dot_access = DotAccess {
|
||||
receiver: dot_access.receiver.clone(),
|
||||
receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }),
|
||||
kind: dot_access_kind,
|
||||
ctx: dot_access.ctx,
|
||||
};
|
||||
complete_methods(ctx, &iter, &traits_in_scope, |func| {
|
||||
acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1466,4 +1475,34 @@ async fn bar() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn receiver_without_deref_impl_completion() {
|
||||
check_no_kw(
|
||||
r#"
|
||||
//- minicore: receiver
|
||||
use core::ops::Receiver;
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn foo(self: Bar) {}
|
||||
}
|
||||
|
||||
struct Bar;
|
||||
|
||||
impl Receiver for Bar {
|
||||
type Target = Foo;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let bar = Bar;
|
||||
bar.$0
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
me foo() fn(self: Bar)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ pub(crate) fn complete_expr_path(
|
|||
.find_path(
|
||||
ctx.db,
|
||||
hir::ModuleDef::from(strukt),
|
||||
ctx.config.import_path_config(),
|
||||
ctx.config.import_path_config(ctx.is_nightly),
|
||||
)
|
||||
.filter(|it| it.len() > 1);
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ pub(crate) fn complete_expr_path(
|
|||
.find_path(
|
||||
ctx.db,
|
||||
hir::ModuleDef::from(un),
|
||||
ctx.config.import_path_config(),
|
||||
ctx.config.import_path_config(ctx.is_nightly),
|
||||
)
|
||||
.filter(|it| it.len() > 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use ide_db::imports::{
|
|||
insert_use::ImportScope,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use syntax::{ast, AstNode, SyntaxNode, ToSmolStr};
|
||||
use syntax::{ast, AstNode, SyntaxNode};
|
||||
|
||||
use crate::{
|
||||
config::AutoImportExclusionType,
|
||||
|
|
@ -257,7 +257,7 @@ fn import_on_the_fly(
|
|||
};
|
||||
let user_input_lowercased = potential_import_name.to_lowercase();
|
||||
|
||||
let import_cfg = ctx.config.import_path_config();
|
||||
let import_cfg = ctx.config.import_path_config(ctx.is_nightly);
|
||||
|
||||
import_assets
|
||||
.search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind)
|
||||
|
|
@ -316,7 +316,7 @@ fn import_on_the_fly_pat_(
|
|||
ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)),
|
||||
};
|
||||
let user_input_lowercased = potential_import_name.to_lowercase();
|
||||
let cfg = ctx.config.import_path_config();
|
||||
let cfg = ctx.config.import_path_config(ctx.is_nightly);
|
||||
|
||||
import_assets
|
||||
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
|
||||
|
|
@ -358,7 +358,7 @@ fn import_on_the_fly_method(
|
|||
|
||||
let user_input_lowercased = potential_import_name.to_lowercase();
|
||||
|
||||
let cfg = ctx.config.import_path_config();
|
||||
let cfg = ctx.config.import_path_config(ctx.is_nightly);
|
||||
|
||||
import_assets
|
||||
.search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind)
|
||||
|
|
@ -444,7 +444,7 @@ fn compute_fuzzy_completion_order_key(
|
|||
cov_mark::hit!(certain_fuzzy_order_test);
|
||||
let import_name = match proposed_mod_path.segments().last() {
|
||||
// FIXME: nasty alloc, this is a hot path!
|
||||
Some(name) => name.unescaped().display_no_db().to_smolstr().to_ascii_lowercase(),
|
||||
Some(name) => name.as_str().to_ascii_lowercase(),
|
||||
None => return usize::MAX,
|
||||
};
|
||||
match import_name.match_indices(user_input_lowercased).next() {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
//! }
|
||||
//! ```
|
||||
|
||||
use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name};
|
||||
use hir::{db::ExpandDatabase, MacroFileId, Name};
|
||||
use ide_db::text_edit::TextEdit;
|
||||
use ide_db::{
|
||||
documentation::HasDocs, path_transform::PathTransform,
|
||||
|
|
@ -85,7 +85,7 @@ fn complete_trait_impl_name(
|
|||
name: &Option<ast::Name>,
|
||||
kind: ImplCompletionKind,
|
||||
) -> Option<()> {
|
||||
let item = match name {
|
||||
let macro_file_item = match name {
|
||||
Some(name) => name.syntax().parent(),
|
||||
None => {
|
||||
let token = &ctx.token;
|
||||
|
|
@ -96,12 +96,12 @@ fn complete_trait_impl_name(
|
|||
.parent()
|
||||
}
|
||||
}?;
|
||||
let item = ctx.sema.original_syntax_node_rooted(&item)?;
|
||||
let real_file_item = ctx.sema.original_syntax_node_rooted(¯o_file_item)?;
|
||||
// item -> ASSOC_ITEM_LIST -> IMPL
|
||||
let impl_def = ast::Impl::cast(item.parent()?.parent()?)?;
|
||||
let impl_def = ast::Impl::cast(macro_file_item.parent()?.parent()?)?;
|
||||
let replacement_range = {
|
||||
// ctx.sema.original_ast_node(item)?;
|
||||
let first_child = item
|
||||
let first_child = real_file_item
|
||||
.children_with_tokens()
|
||||
.find(|child| {
|
||||
!matches!(
|
||||
|
|
@ -109,7 +109,7 @@ fn complete_trait_impl_name(
|
|||
SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR
|
||||
)
|
||||
})
|
||||
.unwrap_or_else(|| SyntaxElement::Node(item.clone()));
|
||||
.unwrap_or_else(|| SyntaxElement::Node(real_file_item.clone()));
|
||||
|
||||
TextRange::new(first_child.text_range().start(), ctx.source_range().end())
|
||||
};
|
||||
|
|
@ -133,8 +133,11 @@ pub(crate) fn complete_trait_impl_item_by_name(
|
|||
acc,
|
||||
ctx,
|
||||
ImplCompletionKind::All,
|
||||
match name_ref {
|
||||
Some(name) => name.syntax().text_range(),
|
||||
match name_ref
|
||||
.as_ref()
|
||||
.and_then(|name| ctx.sema.original_syntax_node_rooted(name.syntax()))
|
||||
{
|
||||
Some(name) => name.text_range(),
|
||||
None => ctx.source_range(),
|
||||
},
|
||||
impl_,
|
||||
|
|
@ -152,7 +155,7 @@ fn complete_trait_impl(
|
|||
if let Some(hir_impl) = ctx.sema.to_def(impl_def) {
|
||||
get_missing_assoc_items(&ctx.sema, impl_def)
|
||||
.into_iter()
|
||||
.filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db))))
|
||||
.filter(|item| ctx.check_stability_and_hidden(*item))
|
||||
.for_each(|item| {
|
||||
use self::ImplCompletionKind::*;
|
||||
match (item, kind) {
|
||||
|
|
@ -359,7 +362,7 @@ fn add_type_alias_impl(
|
|||
type_alias: hir::TypeAlias,
|
||||
impl_def: hir::Impl,
|
||||
) {
|
||||
let alias_name = type_alias.name(ctx.db).unescaped().display(ctx.db).to_smolstr();
|
||||
let alias_name = type_alias.name(ctx.db).as_str().to_smolstr();
|
||||
|
||||
let label = format_smolstr!("type {alias_name} =");
|
||||
|
||||
|
|
@ -516,7 +519,7 @@ fn function_declaration(
|
|||
mod tests {
|
||||
use expect_test::expect;
|
||||
|
||||
use crate::tests::{check_edit, check_no_kw};
|
||||
use crate::tests::{check, check_edit, check_no_kw};
|
||||
|
||||
#[test]
|
||||
fn no_completion_inside_fn() {
|
||||
|
|
@ -1639,4 +1642,51 @@ impl DesugaredAsyncTrait for () {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn within_attr_macro() {
|
||||
check(
|
||||
r#"
|
||||
//- proc_macros: identity
|
||||
trait Trait {
|
||||
fn foo(&self) {}
|
||||
fn bar(&self) {}
|
||||
fn baz(&self) {}
|
||||
}
|
||||
|
||||
#[proc_macros::identity]
|
||||
impl Trait for () {
|
||||
f$0
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
me fn bar(..)
|
||||
me fn baz(..)
|
||||
me fn foo(..)
|
||||
md proc_macros
|
||||
kw crate::
|
||||
kw self::
|
||||
"#]],
|
||||
);
|
||||
check(
|
||||
r#"
|
||||
//- proc_macros: identity
|
||||
trait Trait {
|
||||
fn foo(&self) {}
|
||||
fn bar(&self) {}
|
||||
fn baz(&self) {}
|
||||
}
|
||||
|
||||
#[proc_macros::identity]
|
||||
impl Trait for () {
|
||||
fn $0
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
me fn bar(..)
|
||||
me fn baz(..)
|
||||
me fn foo(..)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use ide_db::{
|
|||
base_db::{SourceRootDatabase, VfsPath},
|
||||
FxHashSet, RootDatabase, SymbolKind,
|
||||
};
|
||||
use syntax::{ast, AstNode, SyntaxKind, ToSmolStr};
|
||||
use syntax::{ast, AstNode, SyntaxKind};
|
||||
|
||||
use crate::{context::CompletionContext, CompletionItem, Completions};
|
||||
|
||||
|
|
@ -140,9 +140,7 @@ fn directory_to_look_for_submodules(
|
|||
module_chain_to_containing_module_file(module, db)
|
||||
.into_iter()
|
||||
.filter_map(|module| module.name(db))
|
||||
.try_fold(base_directory, |path, name| {
|
||||
path.join(&name.unescaped().display_no_db().to_smolstr())
|
||||
})
|
||||
.try_fold(base_directory, |path, name| path.join(name.as_str()))
|
||||
}
|
||||
|
||||
fn module_chain_to_containing_module_file(
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue