Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-01-30 05:06:57 +00:00
commit b8ee38d3e8
322 changed files with 5569 additions and 3047 deletions

View file

@ -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

View file

@ -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

View file

@ -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));

View file

@ -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));

View file

@ -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(" | ")
}

View file

@ -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);

View file

@ -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

View file

@ -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

View 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;
}

View file

@ -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

View file

@ -3,6 +3,9 @@
/* global addClass, removeClass, onEach, onEachLazy */
/* global MAIN_ID, getVar, getSettingsButton, getHelpButton */
// Eventually fix this.
// @ts-nocheck
"use strict";
(function() {

View file

@ -5,6 +5,9 @@
/* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */
/* global updateLocalStorage, getVar */
// Eventually fix this.
// @ts-nocheck
"use strict";
(function() {

View file

@ -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);
}

View 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"]
}
}

View file

@ -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,
}
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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.

View file

@ -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,
}
}

View file

@ -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
},

View file

@ -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)

View file

@ -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 =>
{

View file

@ -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)
)
}

View file

@ -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(),

View file

@ -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,
_,

View file

@ -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
});

View file

@ -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,
}
}

View file

@ -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;

View file

@ -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()
{

View file

@ -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)

View file

@ -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()

View file

@ -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:?})");

View file

@ -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);

View file

@ -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)

View file

@ -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)),

View file

@ -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())
}

View file

@ -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)
});

View file

@ -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();

View file

@ -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))
}

View file

@ -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))
}

View file

@ -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)?;
}

View file

@ -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")]

View file

@ -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
}
}

View file

@ -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()
}

View file

@ -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"

View file

@ -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"]

View file

@ -1381,6 +1381,9 @@ impl ExprCollector<'_> {
}
}
ast::Stmt::Item(ast::Item::MacroDef(macro_)) => {
if self.check_cfg(&macro_).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(&macro_).is_none() {
return;
}
let Some(name) = macro_.name() else {
statements.push(Statement::Item(Item::Other));
return;

View file

@ -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();

View file

@ -475,7 +475,7 @@ fn outer() {
block scope::tests
name: _
outer: v
outer: vg
crate
outer: v

View file

@ -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;

View file

@ -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) => {

View file

@ -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));
}

View file

@ -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))
}
}
}

View file

@ -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);

View file

@ -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

View file

@ -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 {

View file

@ -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((

View file

@ -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

View file

@ -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
"#]],
);
}

View file

@ -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
"#]],
);
}

View file

@ -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))),
)
}
}

View file

@ -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),

View file

@ -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(())
}

View file

@ -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)
}
}

View file

@ -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

View file

@ -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#");
}

View file

@ -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()))?;

View file

@ -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 => {}

View file

@ -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;

View file

@ -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) {

View file

@ -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),

View file

@ -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
}
"#,
);
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)?)
}

View file

@ -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),

View file

@ -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,
}
}
}

View file

@ -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)?;

View file

@ -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

View file

@ -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(

View file

@ -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",
);

View file

@ -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;
};

View file

@ -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(

View file

@ -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())
}
_ => (),
}

View file

@ -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(

View file

@ -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)
})
{

View file

@ -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)

View file

@ -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(),
)
}

View file

@ -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

View file

@ -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)
"#]],
);
}
}

View file

@ -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);

View file

@ -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() {

View file

@ -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(&macro_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(..)
"#]],
);
}
}

View file

@ -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