}
impl GenericArgs {
- pub fn is_parenthesized(&self) -> bool {
- match *self {
- Parenthesized(..) => true,
- _ => false,
- }
- }
-
pub fn is_angle_bracketed(&self) -> bool {
match *self {
AngleBracketed(..) => true,
}
}
- pub fn is_shift(&self) -> bool {
- match *self {
- BinOpKind::Shl | BinOpKind::Shr => true,
- _ => false,
- }
- }
-
pub fn is_comparison(&self) -> bool {
use BinOpKind::*;
// Note for developers: please keep this as is;
And | Or | Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr => false,
}
}
-
- /// Returns `true` if the binary operator takes its arguments by value
- pub fn is_by_value(&self) -> bool {
- !self.is_comparison()
- }
}
pub type BinOp = Spanned<BinOpKind>;
}
impl UnOp {
- /// Returns `true` if the unary operator takes its argument by value
- pub fn is_by_value(u: UnOp) -> bool {
- match u {
- UnOp::Neg | UnOp::Not => true,
- _ => false,
- }
- }
-
pub fn to_string(op: UnOp) -> &'static str {
match op {
UnOp::Deref => "*",
}
}
- pub fn val_to_string(&self, val: i128) -> String {
- // Cast to a `u128` so we can correctly print `INT128_MIN`. All integral types
- // are parsed as `u128`, so we wouldn't want to print an extra negative
- // sign.
- format!("{}{}", val as u128, self.name_str())
- }
-
pub fn bit_width(&self) -> Option<u64> {
Some(match *self {
IntTy::Isize => return None,
}
}
- pub fn val_to_string(&self, val: u128) -> String {
- format!("{}{}", val, self.name_str())
- }
-
pub fn bit_width(&self) -> Option<u64> {
Some(match *self {
UintTy::Usize => return None,
self.meta_item().is_some()
}
- /// Returns `true` if the variant is `Literal`.
- pub fn is_literal(&self) -> bool {
- self.literal().is_some()
- }
-
/// Returns `true` if `self` is a `MetaItem` and the meta item is a word.
pub fn is_word(&self) -> bool {
self.meta_item().map_or(false, |meta_item| meta_item.is_word())
pub fn is_value_str(&self) -> bool {
self.value_str().is_some()
}
-
- pub fn is_meta_item_list(&self) -> bool {
- self.meta_item_list().is_some()
- }
}
impl AttrItem {
NoDelim,
}
-impl DelimToken {
- pub fn len(self) -> usize {
- if self == NoDelim { 0 } else { 1 }
- }
-
- pub fn is_empty(self) -> bool {
- self == NoDelim
- }
-}
-
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
pub enum LitKind {
Bool, // AST only, must never appear in a `Token`
.collect(),
))
}
-
- pub fn map<F: FnMut(TokenTree) -> TokenTree>(self, mut f: F) -> TokenStream {
- TokenStream(Lrc::new(
- self.0.iter().map(|(tree, is_joint)| (f(tree.clone()), *is_joint)).collect(),
- ))
- }
}
// 99.5%+ of the time we have 1 or 2 elements in this vector.
}
}
-pub const PREC_RESET: i8 = -100;
pub const PREC_CLOSURE: i8 = -40;
pub const PREC_JUMP: i8 = -30;
pub const PREC_RANGE: i8 = -10;
}
}
-pub fn set_thread_local(global: &'a Value, is_thread_local: bool) {
- unsafe {
- LLVMSetThreadLocal(global, is_thread_local as Bool);
- }
-}
pub fn set_thread_local_mode(global: &'a Value, mode: ThreadLocalMode) {
unsafe {
LLVMSetThreadLocalMode(global, mode);
}
}
-pub const CODEGEN_WORKER_ID: usize = usize::MAX;
-
/// `FatalError` is explicitly not `Send`.
#[must_use]
pub struct WorkerFatalError;
}
}
-pub const CODEGEN_WORKER_ID: usize = usize::MAX;
-
pub fn codegen_crate<B: ExtraBackendMethods>(
backend: B,
tcx: TyCtxt<'tcx>,
}
impl<T: Idx> WorkQueue<T> {
- /// Creates a new work queue with all the elements from (0..len).
- #[inline]
- pub fn with_all(len: usize) -> Self {
- WorkQueue { deque: (0..len).map(T::new).collect(), set: BitSet::new_filled(len) }
- }
-
/// Creates a new work queue that starts empty, where elements range from (0..len).
#[inline]
pub fn with_none(len: usize) -> Self {
self.level == Level::Cancelled
}
- /// Set the sorting span.
- pub fn set_sort_span(&mut self, sp: Span) {
- self.sort_span = sp;
- }
-
/// Adds a span/label to be included in the resulting snippet.
///
/// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
&self.message
}
- /// Used by a lint. Copies over all details *but* the "main
- /// message".
- pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
- self.span = from.span.clone();
- self.code = from.code.clone();
- self.children.extend(from.children.iter().cloned())
- }
-
/// Convenience function for internal use, clients should use one of the
/// public methods above.
pub fn sub(
fn emit_diagnostic(&mut self, _: &Diagnostic) {}
}
-/// Maximum number of lines we will print for each error; arbitrary.
-pub const MAX_HIGHLIGHT_LINES: usize = 6;
/// Maximum number of lines we will print for a multiline suggestion; arbitrary.
///
/// This should be replaced with a more involved mechanism to output multiline suggestions that
}
}
- pub fn map_item_or<F, G>(self, mut f: F, mut or: G) -> Annotatable
- where
- F: FnMut(P<ast::Item>) -> P<ast::Item>,
- G: FnMut(Annotatable) -> Annotatable,
- {
- match self {
- Annotatable::Item(i) => Annotatable::Item(f(i)),
- _ => or(self),
- }
- }
-
pub fn expect_trait_item(self) -> P<ast::AssocItem> {
match self {
Annotatable::TraitItem(i) => i,
.chain(components.iter().map(|&s| Ident::with_dummy_span(s)))
.collect()
}
- pub fn name_of(&self, st: &str) -> Symbol {
- Symbol::intern(st)
- }
pub fn check_unused_macros(&mut self) {
self.resolver.check_unused_macros();
ast::Lifetime { id: ast::DUMMY_NODE_ID, ident: ident.with_span_pos(span) }
}
- pub fn lifetime_def(
- &self,
- span: Span,
- ident: Ident,
- attrs: Vec<ast::Attribute>,
- bounds: ast::GenericBounds,
- ) -> ast::GenericParam {
- let lifetime = self.lifetime(span, ident);
- ast::GenericParam {
- ident: lifetime.ident,
- id: lifetime.id,
- attrs: attrs.into(),
- bounds,
- kind: ast::GenericParamKind::Lifetime,
- is_placeholder: false,
- }
- }
-
pub fn stmt_expr(&self, expr: P<ast::Expr>) -> ast::Stmt {
ast::Stmt {
id: ast::DUMMY_NODE_ID,
self.pat_tuple_struct(span, path, vec![pat])
}
- pub fn pat_none(&self, span: Span) -> P<ast::Pat> {
- let some = self.std_path(&[sym::option, sym::Option, sym::None]);
- let path = self.path_global(span, some);
- self.pat_path(span, path)
- }
-
- pub fn pat_ok(&self, span: Span, pat: P<ast::Pat>) -> P<ast::Pat> {
- let some = self.std_path(&[sym::result, sym::Result, sym::Ok]);
- let path = self.path_global(span, some);
- self.pat_tuple_struct(span, path, vec![pat])
- }
-
- pub fn pat_err(&self, span: Span, pat: P<ast::Pat>) -> P<ast::Pat> {
- let some = self.std_path(&[sym::result, sym::Result, sym::Err]);
- let path = self.path_global(span, some);
- self.pat_tuple_struct(span, path, vec![pat])
- }
-
pub fn arm(&self, span: Span, pat: P<ast::Pat>, expr: P<ast::Expr>) -> ast::Arm {
ast::Arm {
attrs: vec![],
self.expr(span, ast::ExprKind::If(cond, self.block_expr(then), els))
}
- pub fn lambda_fn_decl(
- &self,
- span: Span,
- fn_decl: P<ast::FnDecl>,
- body: P<ast::Expr>,
- fn_decl_span: Span,
- ) -> P<ast::Expr> {
- self.expr(
- span,
- ast::ExprKind::Closure(
- ast::CaptureBy::Ref,
- ast::Async::No,
- ast::Movability::Movable,
- fn_decl,
- body,
- fn_decl_span,
- ),
- )
- }
-
pub fn lambda(&self, span: Span, ids: Vec<Ident>, body: P<ast::Expr>) -> P<ast::Expr> {
let fn_decl = self.fn_decl(
ids.iter().map(|id| self.param(span, *id, self.ty(span, ast::TyKind::Infer))).collect(),
})
}
- pub fn variant(&self, span: Span, ident: Ident, tys: Vec<P<ast::Ty>>) -> ast::Variant {
- let vis_span = span.shrink_to_lo();
- let fields: Vec<_> = tys
- .into_iter()
- .map(|ty| ast::StructField {
- span: ty.span,
- ty,
- ident: None,
- vis: ast::Visibility {
- span: vis_span,
- kind: ast::VisibilityKind::Inherited,
- tokens: None,
- },
- attrs: Vec::new(),
- id: ast::DUMMY_NODE_ID,
- is_placeholder: false,
- })
- .collect();
-
- let vdata = if fields.is_empty() {
- ast::VariantData::Unit(ast::DUMMY_NODE_ID)
- } else {
- ast::VariantData::Tuple(fields, ast::DUMMY_NODE_ID)
- };
-
- ast::Variant {
- attrs: Vec::new(),
- data: vdata,
- disr_expr: None,
- id: ast::DUMMY_NODE_ID,
- ident,
- vis: ast::Visibility {
- span: vis_span,
- kind: ast::VisibilityKind::Inherited,
- tokens: None,
- },
- span,
- is_placeholder: false,
- }
- }
-
pub fn item_static(
&self,
span: Span,
}
}
-#[derive(Debug)]
-pub enum RenameOrCopyRemove {
- Rename,
- CopyRemove,
-}
-
-/// Rename `p` into `q`, preferring to use `rename` if possible.
-/// If `rename` fails (rename may fail for reasons such as crossing
-/// filesystem), fallback to copy & remove
-pub fn rename_or_copy_remove<P: AsRef<Path>, Q: AsRef<Path>>(
- p: P,
- q: Q,
-) -> io::Result<RenameOrCopyRemove> {
- let p = p.as_ref();
- let q = q.as_ref();
- match fs::rename(p, q) {
- Ok(()) => Ok(RenameOrCopyRemove::Rename),
- Err(_) => match fs::copy(p, q) {
- Ok(_) => {
- fs::remove_file(p)?;
- Ok(RenameOrCopyRemove::CopyRemove)
- }
- Err(e) => Err(e),
- },
- }
-}
-
#[cfg(unix)]
pub fn path_to_c_string(p: &Path) -> CString {
use std::ffi::OsStr;
}
impl DefPath {
- pub fn is_local(&self) -> bool {
- self.krate == LOCAL_CRATE
- }
-
pub fn make<FN>(krate: CrateNum, start_index: DefIndex, mut get_key: FN) -> DefPath
where
FN: FnMut(DefIndex) -> DefKey,
crate use crate::hir_id::HirId;
use crate::{itemlikevisit, LangItem};
-use rustc_ast::node_id::NodeMap;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast::{self as ast, CrateSugar, LlvmAsmDialect};
use rustc_ast::{AttrVec, Attribute, FloatTy, IntTy, Label, LitKind, StrStyle, UintTy};
Self { args: &[], bindings: &[], parenthesized: false }
}
- pub fn is_empty(&self) -> bool {
- self.args.is_empty() && self.bindings.is_empty() && !self.parenthesized
- }
-
pub fn inputs(&self) -> &[Ty<'_>] {
if self.parenthesized {
for arg in self.args {
}
}
- pub fn own_counts(&self) -> GenericParamCount {
- // We could cache this as a property of `GenericParamCount`, but
- // the aim is to refactor this away entirely eventually and the
- // presence of this method will be a constant reminder.
- let mut own_counts: GenericParamCount = Default::default();
-
- for param in self.params {
- match param.kind {
- GenericParamKind::Lifetime { .. } => own_counts.lifetimes += 1,
- GenericParamKind::Type { .. } => own_counts.types += 1,
- GenericParamKind::Const { .. } => own_counts.consts += 1,
- };
- }
-
- own_counts
- }
-
pub fn get_named(&self, name: Symbol) -> Option<&GenericParam<'_>> {
for param in self.params {
if name == param.name.ident().name {
pub span: Span,
}
-pub type CaptureModeMap = NodeMap<CaptureBy>;
-
// The TraitCandidate's import_ids is empty if the trait is defined in the same module, and
// has length > 0 if the trait is found through an chain of imports, starting with the
// import/use statement in the scope where the trait is used.
_ => None,
}
}
-
- pub fn hir_id(&self) -> Option<HirId> {
- match self {
- Node::Item(Item { hir_id, .. })
- | Node::ForeignItem(ForeignItem { hir_id, .. })
- | Node::TraitItem(TraitItem { hir_id, .. })
- | Node::ImplItem(ImplItem { hir_id, .. })
- | Node::Field(StructField { hir_id, .. })
- | Node::AnonConst(AnonConst { hir_id, .. })
- | Node::Expr(Expr { hir_id, .. })
- | Node::Stmt(Stmt { hir_id, .. })
- | Node::Ty(Ty { hir_id, .. })
- | Node::Binding(Pat { hir_id, .. })
- | Node::Pat(Pat { hir_id, .. })
- | Node::Arm(Arm { hir_id, .. })
- | Node::Block(Block { hir_id, .. })
- | Node::Local(Local { hir_id, .. })
- | Node::MacroDef(MacroDef { hir_id, .. })
- | Node::Lifetime(Lifetime { hir_id, .. })
- | Node::Param(Param { hir_id, .. })
- | Node::GenericParam(GenericParam { hir_id, .. }) => Some(*hir_id),
- Node::TraitRef(TraitRef { hir_ref_id, .. }) => Some(*hir_ref_id),
- Node::PathSegment(PathSegment { hir_id, .. }) => *hir_id,
- Node::Variant(Variant { id, .. }) => Some(*id),
- Node::Ctor(variant) => variant.ctor_hir_id(),
- Node::Crate(_) | Node::Visibility(_) => None,
- }
- }
}
owner: LocalDefId { local_def_index: CRATE_DEF_INDEX },
local_id: ItemLocalId::from_u32(0),
};
-
-pub const DUMMY_ITEM_LOCAL_ID: ItemLocalId = ItemLocalId::MAX;
}
impl hir::Pat<'_> {
- pub fn is_refutable(&self) -> bool {
- match self.kind {
- PatKind::Lit(_)
- | PatKind::Range(..)
- | PatKind::Path(hir::QPath::Resolved(Some(..), _) | hir::QPath::TypeRelative(..)) => {
- true
- }
-
- PatKind::Path(hir::QPath::Resolved(_, ref path))
- | PatKind::TupleStruct(hir::QPath::Resolved(_, ref path), ..)
- | PatKind::Struct(hir::QPath::Resolved(_, ref path), ..) => match path.res {
- Res::Def(DefKind::Variant, _) => true,
- _ => false,
- },
- PatKind::Slice(..) => true,
- _ => false,
- }
- }
-
/// Call `f` on every "binding" in a pattern, e.g., on `a` in
/// `match foo() { Some(a) => (), None => () }`
pub fn each_binding(&self, mut f: impl FnMut(hir::BindingAnnotation, HirId, Span, Ident)) {
})
}
- /// Checks if the pattern contains any patterns that bind something to
- /// an ident or wildcard, e.g., `foo`, or `Foo(_)`, `foo @ Bar(..)`,
- pub fn contains_bindings_or_wild(&self) -> bool {
- self.satisfies(|p| match p.kind {
- PatKind::Binding(..) | PatKind::Wild => true,
- _ => false,
- })
- }
-
/// Checks if the pattern satisfies the given predicate on some sub-pattern.
fn satisfies(&self, pred: impl Fn(&hir::Pat<'_>) -> bool) -> bool {
let mut satisfies = false;
fn nested(&self, _state: &mut State<'_>, _nested: Nested) {}
fn pre(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
fn post(&self, _state: &mut State<'_>, _node: AnnNode<'_>) {}
- fn try_fetch_item(&self, _: hir::HirId) -> Option<&hir::Item<'_>> {
- None
- }
}
pub struct NoAnn;
pub const NO_ANN: &dyn PpAnn = &NoAnn;
impl PpAnn for hir::Crate<'_> {
- fn try_fetch_item(&self, item: hir::HirId) -> Option<&hir::Item<'_>> {
- Some(self.item(item))
- }
fn nested(&self, state: &mut State<'_>, nested: Nested) {
match nested {
Nested::Item(id) => state.print_item(self.item(id.id)),
}
impl RegionckMode {
- pub fn suppressed(self) -> bool {
- match self {
- Self::Solve => false,
- Self::Erase { suppress_errors } => suppress_errors,
- }
- }
-
/// Indicates that the MIR borrowck will repeat these region
/// checks, so we should ignore errors if NLL is (unconditionally)
/// enabled.
#[cfg(target_arch = "x86_64")]
static_assert_size!(SubregionOrigin<'_>, 32);
-/// Places that type/region parameters can appear.
-#[derive(Clone, Copy, Debug)]
-pub enum ParameterOrigin {
- Path, // foo::bar
- MethodCall, // foo.bar() <-- parameters on impl providing bar()
- OverloadedOperator, // a + b when overloaded
- OverloadedDeref, // *a when overloaded
-}
-
/// Times when we replace late-bound regions with variables:
#[derive(Clone, Copy, Debug)]
pub enum LateBoundRegionConversionTime {
},
}
-impl NLLRegionVariableOrigin {
- pub fn is_universal(self) -> bool {
- match self {
- NLLRegionVariableOrigin::FreeRegion => true,
- NLLRegionVariableOrigin::Placeholder(..) => true,
- NLLRegionVariableOrigin::Existential { .. } => false,
- NLLRegionVariableOrigin::RootEmptyRegion => false,
- }
- }
-
- pub fn is_existential(self) -> bool {
- !self.is_universal()
- }
-}
-
// FIXME(eddyb) investigate overlap between this and `TyOrConstInferVar`.
#[derive(Copy, Clone, Debug)]
pub enum FixupError<'tcx> {
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor};
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::subst::GenericArg;
use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
use std::fmt::Debug;
fn forbid_inference_vars() -> bool;
}
-#[derive(Clone, Debug)]
-struct ScopesAndKind<'tcx> {
- scopes: Vec<BoundRegionScope<'tcx>>,
- kind: GenericArg<'tcx>,
-}
-
#[derive(Clone, Debug, Default)]
struct BoundRegionScope<'tcx> {
map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
#[cfg(target_arch = "x86_64")]
static_assert_size!(PredicateObligation<'_>, 32);
-pub type Obligations<'tcx, O> = Vec<Obligation<'tcx, O>>;
pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>;
-pub type TraitObligations<'tcx> = Vec<TraitObligation<'tcx>>;
pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>;
}
}
- pub fn current_lint_root(&self) -> hir::HirId {
- self.last_node_with_lint_attrs
- }
-
/// Check if a `DefId`'s path matches the given absolute type path usage.
///
/// Anonymous scopes such as `extern` imports are matched with `kw::Invalid`;
Ok(ty)
}
- fn cached_predicate_for_shorthand<F>(
- &mut self,
- shorthand: usize,
- or_insert_with: F,
- ) -> Result<ty::Predicate<'tcx>, Self::Error>
- where
- F: FnOnce(&mut Self) -> Result<ty::Predicate<'tcx>, Self::Error>,
- {
- let tcx = self.tcx();
-
- let key = ty::CReaderCacheKey { cnum: self.cdata().cnum, pos: shorthand };
-
- if let Some(&pred) = tcx.pred_rcache.borrow().get(&key) {
- return Ok(pred);
- }
-
- let pred = or_insert_with(self)?;
- tcx.pred_rcache.borrow_mut().insert(key, pred);
- Ok(pred)
- }
-
fn with_position<F, R>(&mut self, pos: usize, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
//! which are available for use externally when compiled as a library.
use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::def_id::DefIdSet;
use rustc_hir::HirId;
use rustc_macros::HashStable;
use std::fmt;
fmt::Debug::fmt(&self.map, f)
}
}
-
-/// A set containing all exported definitions from external crates.
-/// The set does not contain any entries from local crates.
-pub type ExternalExports = DefIdSet;
}
}
- pub fn try_to_str_slice(&self) -> Option<&'tcx str> {
- if let ConstValue::Slice { data, start, end } = *self {
- std::str::from_utf8(data.inspect_with_uninit_and_ptr_outside_interpreter(start..end))
- .ok()
- } else {
- None
- }
- }
-
pub fn try_to_bits(&self, size: Size) -> Option<u128> {
self.try_to_scalar()?.to_bits(size).ok()
}
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
use crate::mir::coverage::{CodeRegion, CoverageKind};
-use crate::mir::interpret::{Allocation, ConstValue, GlobalAlloc, Scalar};
+use crate::mir::interpret::{Allocation, GlobalAlloc, Scalar};
use crate::mir::visit::MirVisitable;
use crate::ty::adjustment::PointerCast;
use crate::ty::codec::{TyDecoder, TyEncoder};
}
}
- /// Checks if `sub` is a sub scope of `sup`
- pub fn is_sub_scope(&self, mut sub: SourceScope, sup: SourceScope) -> bool {
- while sub != sup {
- match self.source_scopes[sub].parent_scope {
- None => return false,
- Some(p) => sub = p,
- }
- }
- true
- }
-
/// Returns the return type; it always return first element from `local_decls` array.
#[inline]
pub fn return_ty(&self) -> Ty<'tcx> {
})
}
- /// Convenience helper to make a `Scalar` from the given `Operand`, assuming that `Operand`
- /// wraps a constant literal value. Panics if this is not the case.
- pub fn scalar_from_const(operand: &Operand<'tcx>) -> Scalar {
- match operand {
- Operand::Constant(constant) => match constant.literal.val.try_to_scalar() {
- Some(scalar) => scalar,
- _ => panic!("{:?}: Scalar value expected", constant.literal.val),
- },
- _ => panic!("{:?}: Constant expected", operand),
- }
- }
-
- /// Convenience helper to make a literal-like constant from a given `&str` slice.
- /// Since this is used to synthesize MIR, assumes `user_ty` is None.
- pub fn const_from_str(tcx: TyCtxt<'tcx>, val: &str, span: Span) -> Operand<'tcx> {
- let tcx = tcx;
- let allocation = Allocation::from_byte_aligned_bytes(val.as_bytes());
- let allocation = tcx.intern_const_alloc(allocation);
- let const_val = ConstValue::Slice { data: allocation, start: 0, end: val.len() };
- let ty = tcx.mk_imm_ref(tcx.lifetimes.re_erased, tcx.types.str_);
- Operand::Constant(box Constant {
- span,
- user_ty: None,
- literal: ty::Const::from_value(tcx, const_val, ty),
- })
- }
-
- /// Convenience helper to make a `ConstValue` from the given `Operand`, assuming that `Operand`
- /// wraps a constant value (such as a `&str` slice). Panics if this is not the case.
- pub fn value_from_const(operand: &Operand<'tcx>) -> ConstValue<'tcx> {
- match operand {
- Operand::Constant(constant) => match constant.literal.val.try_to_value() {
- Some(const_value) => const_value,
- _ => panic!("{:?}: ConstValue expected", constant.literal.val),
- },
- _ => panic!("{:?}: Constant expected", operand),
- }
- }
-
pub fn to_copy(&self) -> Self {
match *self {
Operand::Copy(_) | Operand::Constant(_) => self.clone(),
self.contents.is_empty()
}
- pub fn from_projections(projs: impl Iterator<Item = (UserTypeProjection, Span)>) -> Self {
- UserTypeProjections { contents: projs.collect() }
- }
-
pub fn projections_and_spans(
&self,
) -> impl Iterator<Item = &(UserTypeProjection, Span)> + ExactSizeIterator {
/// For more information on why this is needed, consider looking
/// at the docs for `WithOptConstParam` itself.
impl<'tcx> TyCtxt<'tcx> {
- #[inline]
- pub fn mir_borrowck_opt_const_arg(
- self,
- def: ty::WithOptConstParam<LocalDefId>,
- ) -> &'tcx BorrowCheckResult<'tcx> {
- if let Some(param_did) = def.const_param_did {
- self.mir_borrowck_const_arg((def.did, param_did))
- } else {
- self.mir_borrowck(def.did)
- }
- }
-
#[inline]
pub fn mir_const_qualif_opt_const_arg(
self,
)
}
- /// Returns `true` if this place context represents a storage live marker.
- pub fn is_storage_live_marker(&self) -> bool {
- matches!(self, PlaceContext::NonUse(NonUseContext::StorageLive))
- }
-
- /// Returns `true` if this place context represents a storage dead marker.
- pub fn is_storage_dead_marker(&self) -> bool {
- matches!(self, PlaceContext::NonUse(NonUseContext::StorageDead))
- }
-
/// Returns `true` if this place context represents a use that potentially changes the value.
pub fn is_mutating_use(&self) -> bool {
matches!(self, PlaceContext::MutatingUse(..))
where
F: FnOnce(&mut Self) -> Result<Ty<'tcx>, Self::Error>;
- fn cached_predicate_for_shorthand<F>(
- &mut self,
- shorthand: usize,
- or_insert_with: F,
- ) -> Result<ty::Predicate<'tcx>, Self::Error>
- where
- F: FnOnce(&mut Self) -> Result<ty::Predicate<'tcx>, Self::Error>;
-
fn with_position<F, R>(&mut self, pos: usize, f: F) -> R
where
F: FnOnce(&mut Self) -> R;
self.node_type(pat.hir_id)
}
- pub fn pat_ty_opt(&self, pat: &hir::Pat<'_>) -> Option<Ty<'tcx>> {
- self.node_type_opt(pat.hir_id)
- }
-
// Returns the type of an expression as a monotype.
//
// NB (1): This is the PRE-ADJUSTMENT TYPE for the expression. That is, in
fn has_infer_types_or_consts(&self) -> bool {
self.has_type_flags(TypeFlags::HAS_TY_INFER | TypeFlags::HAS_CT_INFER)
}
- fn has_infer_consts(&self) -> bool {
- self.has_type_flags(TypeFlags::HAS_CT_INFER)
- }
fn needs_infer(&self) -> bool {
self.has_type_flags(TypeFlags::NEEDS_INFER)
}
fn needs_subst(&self) -> bool {
self.has_type_flags(TypeFlags::NEEDS_SUBST)
}
- fn has_re_placeholders(&self) -> bool {
- self.has_type_flags(TypeFlags::HAS_RE_PLACEHOLDER)
- }
/// "Free" regions in this context means that it has any region
/// that is not (a) erased or (b) late-bound.
fn has_free_regions(&self) -> bool {
// vars. See comment on `shift_vars_through_binders` method in
// `subst.rs` for more details.
-enum Direction {
- In,
- Out,
-}
-
struct Shifter<'tcx> {
tcx: TyCtxt<'tcx>,
current_index: ty::DebruijnIndex,
amount: u32,
- direction: Direction,
}
impl Shifter<'tcx> {
- pub fn new(tcx: TyCtxt<'tcx>, amount: u32, direction: Direction) -> Self {
- Shifter { tcx, current_index: ty::INNERMOST, amount, direction }
+ pub fn new(tcx: TyCtxt<'tcx>, amount: u32) -> Self {
+ Shifter { tcx, current_index: ty::INNERMOST, amount }
}
}
if self.amount == 0 || debruijn < self.current_index {
r
} else {
- let debruijn = match self.direction {
- Direction::In => debruijn.shifted_in(self.amount),
- Direction::Out => {
- assert!(debruijn.as_u32() >= self.amount);
- debruijn.shifted_out(self.amount)
- }
- };
+ let debruijn = debruijn.shifted_in(self.amount);
let shifted = ty::ReLateBound(debruijn, br);
self.tcx.mk_region(shifted)
}
if self.amount == 0 || debruijn < self.current_index {
ty
} else {
- let debruijn = match self.direction {
- Direction::In => debruijn.shifted_in(self.amount),
- Direction::Out => {
- assert!(debruijn.as_u32() >= self.amount);
- debruijn.shifted_out(self.amount)
- }
- };
+ let debruijn = debruijn.shifted_in(self.amount);
self.tcx.mk_ty(ty::Bound(debruijn, bound_ty))
}
}
if self.amount == 0 || debruijn < self.current_index {
ct
} else {
- let debruijn = match self.direction {
- Direction::In => debruijn.shifted_in(self.amount),
- Direction::Out => {
- assert!(debruijn.as_u32() >= self.amount);
- debruijn.shifted_out(self.amount)
- }
- };
+ let debruijn = debruijn.shifted_in(self.amount);
self.tcx.mk_const(ty::Const { val: ty::ConstKind::Bound(debruijn, bound_ct), ty })
}
} else {
{
debug!("shift_vars(value={:?}, amount={})", value, amount);
- value.fold_with(&mut Shifter::new(tcx, amount, Direction::In))
-}
-
-pub fn shift_out_vars<'tcx, T>(tcx: TyCtxt<'tcx>, value: &T, amount: u32) -> T
-where
- T: TypeFoldable<'tcx>,
-{
- debug!("shift_out_vars(value={:?}, amount={})", value, amount);
-
- value.fold_with(&mut Shifter::new(tcx, amount, Direction::Out))
+ value.fold_with(&mut Shifter::new(tcx, amount))
}
/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a
// ```
ty.uninhabited_from(self, param_env).contains(self, module)
}
-
- pub fn is_ty_uninhabited_from_any_module(
- self,
- ty: Ty<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- ) -> bool {
- !ty.uninhabited_from(self, param_env).is_empty()
- }
}
impl<'tcx> AdtDef {
use crate::hir::exports::ExportMap;
use crate::ich::StableHashingContext;
-use crate::infer::canonical::Canonical;
use crate::middle::cstore::CrateStoreDyn;
use crate::middle::resolve_lifetime::ObjectLifetimeDefault;
use crate::mir::interpret::ErrorHandled;
#[rustc_diagnostic_item = "Ty"]
pub type Ty<'tcx> = &'tcx TyS<'tcx>;
-pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>;
-
#[derive(Clone, Copy, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
pub struct UpvarPath {
pub hir_id: hir::HirId,
pub struct FloatVarValue(pub ast::FloatTy);
impl ty::EarlyBoundRegion {
- pub fn to_bound_region(&self) -> ty::BoundRegion {
- ty::BoundRegion::BrNamed(self.def_id, self.name)
- }
-
/// Does this early bound region have a name? Early bound regions normally
/// always have names except when using anonymous lifetimes (`'_`).
pub fn has_name(&self) -> bool {
bug!("cannot convert a non-lifetime parameter def to an early bound region")
}
}
-
- pub fn to_bound_region(&self) -> ty::BoundRegion {
- if let GenericParamDefKind::Lifetime = self.kind {
- self.to_early_bound_region_data().to_bound_region()
- } else {
- bug!("cannot convert a non-lifetime parameter def to an early bound region")
- }
- }
}
#[derive(Default)]
instantiated.predicates.extend(self.predicates.iter().map(|(p, _)| p));
instantiated.spans.extend(self.predicates.iter().map(|(_, s)| s));
}
-
- pub fn instantiate_supertrait(
- &self,
- tcx: TyCtxt<'tcx>,
- poly_trait_ref: &ty::PolyTraitRef<'tcx>,
- ) -> InstantiatedPredicates<'tcx> {
- assert_eq!(self.parent, None);
- InstantiatedPredicates {
- predicates: self
- .predicates
- .iter()
- .map(|(pred, _)| pred.subst_supertrait(tcx, poly_trait_ref))
- .collect(),
- spans: self.predicates.iter().map(|(_, sp)| *sp).collect(),
- }
- }
}
#[derive(Debug)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
#[derive(HashStable, TypeFoldable)]
pub struct OutlivesPredicate<A, B>(pub A, pub B); // `A: B`
-pub type PolyOutlivesPredicate<A, B> = ty::Binder<OutlivesPredicate<A, B>>;
pub type RegionOutlivesPredicate<'tcx> = OutlivesPredicate<ty::Region<'tcx>, ty::Region<'tcx>>;
pub type TypeOutlivesPredicate<'tcx> = OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>;
pub type PolyRegionOutlivesPredicate<'tcx> = ty::Binder<RegionOutlivesPredicate<'tcx>>;
Ok(ty)
}
- fn cached_predicate_for_shorthand<F>(
- &mut self,
- shorthand: usize,
- or_insert_with: F,
- ) -> Result<ty::Predicate<'tcx>, Self::Error>
- where
- F: FnOnce(&mut Self) -> Result<ty::Predicate<'tcx>, Self::Error>,
- {
- let tcx = self.tcx();
-
- let cache_key =
- ty::CReaderCacheKey { cnum: CrateNum::ReservedForIncrCompCache, pos: shorthand };
-
- if let Some(&pred) = tcx.pred_rcache.borrow().get(&cache_key) {
- return Ok(pred);
- }
-
- let pred = or_insert_with(self)?;
- // This may overwrite the entry, but it should overwrite with the same value.
- tcx.pred_rcache.borrow_mut().insert_same(cache_key, pred);
- Ok(pred)
- }
-
fn with_position<F, R>(&mut self, pos: usize, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
// Type substitutions.
-use crate::infer::canonical::Canonical;
use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
use crate::ty::sty::{ClosureSubsts, GeneratorSubsts};
}
}
-pub type CanonicalUserSubsts<'tcx> = Canonical<'tcx, UserSubsts<'tcx>>;
-
/// Stores the user-given substs to reach some fully qualified path
/// (e.g., `<T>::Item` or `<T as Trait>::Item`).
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable)]
let (field_count, variant, down) = match val.ty.kind() {
ty::Array(_, len) => (usize::try_from(len.eval_usize(tcx, param_env)).unwrap(), None, op),
ty::Adt(def, _) if def.variants.is_empty() => {
- return mir::DestructuredConst { variant: None, fields: tcx.arena.alloc_slice(&[]) };
+ return mir::DestructuredConst { variant: None, fields: &[] };
}
ty::Adt(def, _) => {
let variant = ecx.read_discriminant(op).unwrap().1;
let blocks = mir::traversal::reachable(body);
visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
}
-
- pub fn visit_in_rpo_with(
- &self,
- body: &'mir mir::Body<'tcx>,
- vis: &mut impl ResultsVisitor<'mir, 'tcx, FlowState = A::Domain>,
- ) {
- let blocks = mir::traversal::reverse_postorder(body);
- visit_results(body, blocks.map(|(bb, _)| bb), self, vis)
- }
}
/// A solver for dataflow problems.
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{
AggregateKind, BasicBlock, Body, BorrowKind, Local, Location, MirPhase, Operand, Rvalue,
- Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfo,
+ SourceScope, Statement, StatementKind, Terminator, TerminatorKind, VarDebugInfo,
};
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
}
}
- fn visit_var_debug_info(&mut self, _var_debug_info: &VarDebugInfo<'tcx>) {
+ fn visit_var_debug_info(&mut self, var_debug_info: &VarDebugInfo<'tcx>) {
// Debuginfo can contain field projections, which count as a use of the base local. Skip
// debuginfo so that we avoid the storage liveness assertion in that case.
+ self.visit_source_info(&var_debug_info.source_info);
}
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
| TerminatorKind::GeneratorDrop => {}
}
}
+
+ fn visit_source_scope(&mut self, scope: &SourceScope) {
+ if self.body.source_scopes.get(*scope).is_none() {
+ self.tcx.sess.diagnostic().delay_span_bug(
+ self.body.span,
+ &format!(
+ "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
+ self.body.source.instance, self.when, scope,
+ ),
+ );
+ }
+ }
}
source_file_to_parser(sess, file_to_source_file(sess, path, sp))
}
-/// Creates a new parser, returning buffered diagnostics if the file doesn't exist,
-/// or from lexing the initial token stream.
-pub fn maybe_new_parser_from_file<'a>(
- sess: &'a ParseSess,
- path: &Path,
-) -> Result<Parser<'a>, Vec<Diagnostic>> {
- let file = try_file_to_source_file(sess, path, None).map_err(|db| vec![db])?;
- maybe_source_file_to_parser(sess, file)
-}
-
/// Given a `source_file` and config, returns a parser.
fn source_file_to_parser(sess: &ParseSess, source_file: Lrc<SourceFile>) -> Parser<'_> {
panictry_buffer!(&sess.span_diagnostic, maybe_source_file_to_parser(sess, source_file))
Ok(parser)
}
-// Must preserve old name for now, because `quote!` from the *existing*
-// compiler expands into it.
-pub fn new_parser_from_tts(sess: &ParseSess, tts: Vec<TokenTree>) -> Parser<'_> {
- stream_to_parser(sess, tts.into_iter().collect(), crate::MACRO_ARGUMENTS)
-}
-
// Base abstractions
/// Given a session and a path and an optional span (for error reporting),
self.doc_alias_str_error(meta);
return false;
}
- if let Some(c) =
- doc_alias.chars().find(|&c| c == '"' || c == '\'' || c.is_whitespace())
+ if let Some(c) = doc_alias
+ .chars()
+ .find(|&c| c == '"' || c == '\'' || (c.is_whitespace() && c != ' '))
{
self.tcx
.sess
.emit();
return false;
}
+ if doc_alias.starts_with(' ') || doc_alias.ends_with(' ') {
+ self.tcx
+ .sess
+ .struct_span_err(
+ meta.span(),
+ "`#[doc(alias = \"...\")]` cannot start or end with ' '",
+ )
+ .emit();
+ return false;
+ }
if let Some(err) = match target {
Target::Impl => Some("implementation block"),
Target::ForeignMod => Some("extern block"),
cgu_name.hash(&mut hasher);
WorkProductId { hash: hasher.finish() }
}
-
- pub fn from_fingerprint(fingerprint: Fingerprint) -> WorkProductId {
- WorkProductId { hash: fingerprint }
- }
}
impl<HCX> HashStable<HCX> for WorkProductId {
self.data.as_ref().unwrap().previous.fingerprint_of(dep_node)
}
- #[inline]
- pub fn prev_dep_node_index_of(&self, dep_node: &DepNode<K>) -> SerializedDepNodeIndex {
- self.data.as_ref().unwrap().previous.node_to_index(dep_node)
- }
-
/// Checks whether a previous work product exists for `v` and, if
/// so, return the path that leads to it. Used to skip doing work.
pub fn previous_work_product(&self, v: &WorkProductId) -> Option<WorkProduct> {
use rustc_data_structures::fx::FxHashMap;
-use rustc_data_structures::graph::implementation::{
- Direction, Graph, NodeIndex, INCOMING, OUTGOING,
-};
+use rustc_data_structures::graph::implementation::{Direction, Graph, NodeIndex, INCOMING};
use super::{DepKind, DepNode};
}
}
- /// All nodes reachable from `node`. In other words, things that
- /// will have to be recomputed if `node` changes.
- pub fn transitive_successors(&self, node: &DepNode<K>) -> Vec<&DepNode<K>> {
- self.reachable_nodes(node, OUTGOING)
- }
-
/// All nodes that can reach `node`.
pub fn transitive_predecessors(&self, node: &DepNode<K>) -> Vec<&DepNode<K>> {
self.reachable_nodes(node, INCOMING)
}
-
- /// Just the outgoing edges from `node`.
- pub fn immediate_successors(&self, node: &DepNode<K>) -> Vec<&DepNode<K>> {
- if let Some(&index) = self.indices.get(&node) {
- self.graph.successor_nodes(index).map(|s| self.graph.node_data(s)).collect()
- } else {
- vec![]
- }
- }
}
let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
handler.struct_warn(msg).emit();
}
-
-pub type CompileResult = Result<(), ErrorReported>;
HygieneData::with(|data| data.outer_mark(self))
}
- #[inline]
- pub fn outer_mark_with_data(self) -> (ExpnId, Transparency, ExpnData) {
- HygieneData::with(|data| {
- let (expn_id, transparency) = data.outer_mark(self);
- (expn_id, transparency, data.expn_data(expn_id).clone())
- })
- }
-
pub fn dollar_crate_name(self) -> Symbol {
HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name)
}
}
}
- pub fn quote_expansion_source_code(src: &str) -> FileName {
- let mut hasher = StableHasher::new();
- src.hash(&mut hasher);
- FileName::QuoteExpansion(hasher.finish())
- }
-
pub fn macro_expansion_source_code(src: &str) -> FileName {
let mut hasher = StableHasher::new();
src.hash(&mut hasher);
}
}
- /// Returns a new `Span` covering the start and end `BytePos`s of the file containing the given
- /// `pos`. This can be used to quickly determine if another `BytePos` or `Span` is from the same
- /// file.
- pub fn lookup_file_span(&self, pos: BytePos) -> Span {
- let idx = self.lookup_source_file_idx(pos);
- let SourceFile { start_pos, end_pos, .. } = *(*self.files.borrow().source_files)[idx];
- Span::with_root_ctxt(start_pos, end_pos)
- }
-
/// Returns `Some(span)`, a union of the LHS and RHS span. The LHS must precede the RHS. If
/// there are gaps between LHS and RHS, the resulting union will cross these gaps.
/// For this to work,
self.infcx.tcx
}
- pub fn closure_typer(&self) -> &'cx InferCtxt<'cx, 'tcx> {
- self.infcx
- }
-
///////////////////////////////////////////////////////////////////////////
// Selection
//
dist Build distribution artifacts
install Install distribution artifacts
run, r Run tools contained in this repository
+ setup Create a config.toml (making it easier to use `x.py` itself)
To learn more about a subcommand, run `./x.py <subcommand> -h`",
);
);
}
"setup" => {
- subcommand_help.push_str(
+ subcommand_help.push_str(&format!(
"\n
+x.py setup creates a `config.toml` which changes the defaults for x.py itself.
+
Arguments:
This subcommand accepts a 'profile' to use for builds. For example:
./x.py setup library
- The profile is optional and you will be prompted interactively if it is not given.",
- );
+ The profile is optional and you will be prompted interactively if it is not given.
+ The following profiles are available:
+
+{}",
+ Profile::all_for_help(" ").trim_end()
+ ));
}
_ => {}
};
profile_string.parse().unwrap_or_else(|err| {
eprintln!("error: {}", err);
eprintln!("help: the available profiles are:");
- for choice in Profile::all() {
- eprintln!("- {}", choice);
- }
+ eprint!("{}", Profile::all_for_help("- "));
std::process::exit(1);
})
} else {
use crate::{t, VERSION};
+use std::fmt::Write as _;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{
}
pub fn all() -> impl Iterator<Item = Self> {
- [Profile::Compiler, Profile::Codegen, Profile::Library, Profile::User].iter().copied()
+ use Profile::*;
+ // N.B. these are ordered by how they are displayed, not alphabetically
+ [Library, Compiler, Codegen, User].iter().copied()
+ }
+
+ pub fn purpose(&self) -> String {
+ use Profile::*;
+ match self {
+ Library => "Contribute to the standard library",
+ Compiler => "Contribute to the compiler or rustdoc",
+ Codegen => "Contribute to the compiler, and also modify LLVM or codegen",
+ User => "Install Rust from source",
+ }
+ .to_string()
+ }
+
+ pub fn all_for_help(indent: &str) -> String {
+ let mut out = String::new();
+ for choice in Profile::all() {
+ writeln!(&mut out, "{}{}: {}", indent, choice, choice.purpose()).unwrap();
+ }
+ out
}
}
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
- "a" | "lib" | "library" => Ok(Profile::Library),
- "b" | "compiler" | "rustdoc" => Ok(Profile::Compiler),
- "c" | "llvm" | "codegen" => Ok(Profile::Codegen),
- "d" | "maintainer" | "user" => Ok(Profile::User),
+ "lib" | "library" => Ok(Profile::Library),
+ "compiler" | "rustdoc" => Ok(Profile::Compiler),
+ "llvm" | "codegen" => Ok(Profile::Codegen),
+ "maintainer" | "user" => Ok(Profile::User),
_ => Err(format!("unknown profile: '{}'", s)),
}
}
// Used to get the path for `Subcommand::Setup`
pub fn interactive_path() -> io::Result<Profile> {
- let mut input = String::new();
- println!(
- "Welcome to the Rust project! What do you want to do with x.py?
-a) Contribute to the standard library
-b) Contribute to the compiler or rustdoc
-c) Contribute to the compiler, and also modify LLVM or codegen
-d) Install Rust from source"
- );
+ fn abbrev_all() -> impl Iterator<Item = (String, Profile)> {
+ ('a'..).map(|c| c.to_string()).zip(Profile::all())
+ }
+
+ fn parse_with_abbrev(input: &str) -> Result<Profile, String> {
+ let input = input.trim().to_lowercase();
+ for (letter, profile) in abbrev_all() {
+ if input == letter {
+ return Ok(profile);
+ }
+ }
+ input.parse()
+ }
+
+ println!("Welcome to the Rust project! What do you want to do with x.py?");
+ for (letter, profile) in abbrev_all() {
+ println!("{}) {}: {}", letter, profile, profile.purpose());
+ }
let template = loop {
- print!("Please choose one (a/b/c/d): ");
+ print!(
+ "Please choose one ({}): ",
+ abbrev_all().map(|(l, _)| l).collect::<Vec<_>>().join("/")
+ );
io::stdout().flush()?;
+ let mut input = String::new();
io::stdin().read_line(&mut input)?;
- break match input.trim().to_lowercase().parse() {
+ if input == "" {
+ eprintln!("EOF on stdin, when expecting answer to question. Giving up.");
+ std::process::exit(1);
+ }
+ break match parse_with_abbrev(&input) {
Ok(profile) => profile,
Err(err) => {
println!("error: {}", err);
--- /dev/null
+# `codegen-backend`
+
+The tracking issue for this feature is: [#77933](https://github.com/rust-lang/rust/issues/77933).
+
+------------------------
+
+This feature allows you to specify a path to a dynamic library to use as rustc's
+code generation backend at runtime.
+
+Set the `-Zcodegen-backend=<path>` compiler flag to specify the location of the
+backend. The library must be of crate type `dylib` and must contain a function
+named `__rustc_codegen_backend` with a signature of `fn() -> Box<dyn rustc_codegen_ssa::traits::CodegenBackend>`.
+
+## Example
+See also the [`hotplug_codegen_backend`](https://github.com/rust-lang/rust/tree/master/src/test/run-make-fulldeps/hotplug_codegen_backend) test
+for a full example.
+
+```rust,ignore
+use rustc_codegen_ssa::traits::CodegenBackend;
+
+struct MyBackend;
+
+impl CodegenBackend for MyBackend {
+ // Implement codegen methods
+}
+
+#[no_mangle]
+pub fn __rustc_codegen_backend() -> Box<dyn CodegenBackend> {
+ Box::new(MyBackend)
+}
+```
use pulldown_cmark::{Event, Parser};
use rustc_feature::UnstableFeatures;
use rustc_session::lint;
+use std::iter::Peekable;
+use std::str::CharIndices;
pub const CHECK_INVALID_HTML_TAGS: Pass = Pass {
name: "check-invalid-html-tags",
}
}
-fn extract_tag(
+fn extract_html_tag(
tags: &mut Vec<(String, Range<usize>)>,
text: &str,
- range: Range<usize>,
+ range: &Range<usize>,
+ start_pos: usize,
+ iter: &mut Peekable<CharIndices<'_>>,
f: &impl Fn(&str, &Range<usize>),
) {
- let mut iter = text.char_indices().peekable();
+ let mut tag_name = String::new();
+ let mut is_closing = false;
+ let mut prev_pos = start_pos;
- while let Some((start_pos, c)) = iter.next() {
- if c == '<' {
- let mut tag_name = String::new();
- let mut is_closing = false;
- let mut prev_pos = start_pos;
- loop {
- let (pos, c) = match iter.peek() {
- Some((pos, c)) => (*pos, *c),
- // In case we reached the of the doc comment, we want to check that it's an
- // unclosed HTML tag. For example "/// <h3".
- None => (prev_pos, '\0'),
- };
- prev_pos = pos;
- // Checking if this is a closing tag (like `</a>` for `<a>`).
- if c == '/' && tag_name.is_empty() {
- is_closing = true;
- } else if c.is_ascii_alphanumeric() {
- tag_name.push(c);
- } else {
- if !tag_name.is_empty() {
- let mut r =
- Range { start: range.start + start_pos, end: range.start + pos };
- if c == '>' {
- // In case we have a tag without attribute, we can consider the span to
- // refer to it fully.
- r.end += 1;
+ loop {
+ let (pos, c) = match iter.peek() {
+ Some((pos, c)) => (*pos, *c),
+ // In case we reached the of the doc comment, we want to check that it's an
+ // unclosed HTML tag. For example "/// <h3".
+ None => (prev_pos, '\0'),
+ };
+ prev_pos = pos;
+ // Checking if this is a closing tag (like `</a>` for `<a>`).
+ if c == '/' && tag_name.is_empty() {
+ is_closing = true;
+ } else if c.is_ascii_alphanumeric() {
+ tag_name.push(c);
+ } else {
+ if !tag_name.is_empty() {
+ let mut r = Range { start: range.start + start_pos, end: range.start + pos };
+ if c == '>' {
+ // In case we have a tag without attribute, we can consider the span to
+ // refer to it fully.
+ r.end += 1;
+ }
+ if is_closing {
+ // In case we have "</div >" or even "</div >".
+ if c != '>' {
+ if !c.is_whitespace() {
+ // It seems like it's not a valid HTML tag.
+ break;
}
- if is_closing {
- // In case we have "</div >" or even "</div >".
- if c != '>' {
- if !c.is_whitespace() {
- // It seems like it's not a valid HTML tag.
- break;
- }
- let mut found = false;
- for (new_pos, c) in text[pos..].char_indices() {
- if !c.is_whitespace() {
- if c == '>' {
- r.end = range.start + new_pos + 1;
- found = true;
- }
- break;
- }
- }
- if !found {
- break;
+ let mut found = false;
+ for (new_pos, c) in text[pos..].char_indices() {
+ if !c.is_whitespace() {
+ if c == '>' {
+ r.end = range.start + new_pos + 1;
+ found = true;
}
+ break;
}
- drop_tag(tags, tag_name, r, f);
- } else {
- tags.push((tag_name, r));
+ }
+ if !found {
+ break;
}
}
- break;
+ drop_tag(tags, tag_name, r, f);
+ } else {
+ tags.push((tag_name, r));
}
+ }
+ break;
+ }
+ iter.next();
+ }
+}
+
+fn extract_tags(
+ tags: &mut Vec<(String, Range<usize>)>,
+ text: &str,
+ range: Range<usize>,
+ is_in_comment: &mut Option<Range<usize>>,
+ f: &impl Fn(&str, &Range<usize>),
+) {
+ let mut iter = text.char_indices().peekable();
+
+ while let Some((start_pos, c)) = iter.next() {
+ if is_in_comment.is_some() {
+ if text[start_pos..].starts_with("-->") {
+ *is_in_comment = None;
+ }
+ } else if c == '<' {
+ if text[start_pos..].starts_with("<!--") {
+ // We skip the "!--" part. (Once `advance_by` is stable, might be nice to use it!)
+ iter.next();
iter.next();
+ iter.next();
+ *is_in_comment = Some(Range {
+ start: range.start + start_pos,
+ end: range.start + start_pos + 3,
+ });
+ } else {
+ extract_html_tag(tags, text, &range, start_pos, &mut iter, f);
}
}
}
};
let mut tags = Vec::new();
+ let mut is_in_comment = None;
let p = Parser::new_ext(&dox, opts()).into_offset_iter();
for (event, range) in p {
match event {
- Event::Html(text) => extract_tag(&mut tags, &text, range, &report_diag),
+ Event::Html(text) | Event::Text(text) => {
+ extract_tags(&mut tags, &text, range, &mut is_in_comment, &report_diag)
+ }
_ => {}
}
}
}) {
report_diag(&format!("unclosed HTML tag `{}`", tag), range);
}
+
+ if let Some(range) = is_in_comment {
+ report_diag("Unclosed HTML comment", &range);
+ }
}
self.fold_item_recur(item)
--- /dev/null
+// exact-check
+
+const QUERY = [
+ 'Demon Lord',
+];
+
+const EXPECTED = [
+ {
+ 'others': [
+ {
+ 'path': 'doc_alias_whitespace',
+ 'name': 'Struct',
+ 'alias': 'Demon Lord',
+ 'href': '../doc_alias_whitespace/struct.Struct.html',
+ 'is_alias': true
+ },
+ ],
+ },
+];
--- /dev/null
+#![feature(doc_alias)]
+
+#[doc(alias = "Demon Lord")]
+pub struct Struct;
#[doc(alias = "\n")] //~ ERROR
#[doc(alias = "
")] //~^ ERROR
-#[doc(alias = " ")] //~ ERROR
#[doc(alias = "\t")] //~ ERROR
+#[doc(alias = " hello")] //~ ERROR
+#[doc(alias = "hello ")] //~ ERROR
pub struct Foo;
LL | | ")]
| |_^
-error: ' ' character isn't allowed in `#[doc(alias = "...")]`
+error: '\t' character isn't allowed in `#[doc(alias = "...")]`
--> $DIR/check-doc-alias-attr.rs:14:7
|
-LL | #[doc(alias = " ")]
- | ^^^^^^^^^^^
+LL | #[doc(alias = "\t")]
+ | ^^^^^^^^^^^^
-error: '\t' character isn't allowed in `#[doc(alias = "...")]`
+error: `#[doc(alias = "...")]` cannot start or end with ' '
--> $DIR/check-doc-alias-attr.rs:15:7
|
-LL | #[doc(alias = "\t")]
- | ^^^^^^^^^^^^
+LL | #[doc(alias = " hello")]
+ | ^^^^^^^^^^^^^^^^
+
+error: `#[doc(alias = "...")]` cannot start or end with ' '
+ --> $DIR/check-doc-alias-attr.rs:16:7
+ |
+LL | #[doc(alias = "hello ")]
+ | ^^^^^^^^^^^^^^^^
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
/// <div></div
//~^ ERROR unclosed HTML tag `div`
pub fn f() {}
+
+/// <!---->
+/// <!-- -->
+/// <!-- <div> -->
+/// <!-- <!-- -->
+pub fn g() {}
+
+/// <!--
+/// -->
+pub fn h() {}
+
+/// <!--
+//~^ ERROR Unclosed HTML comment
+pub fn i() {}
LL | /// <div></div
| ^^^^^
-error: aborting due to 12 previous errors
+error: Unclosed HTML comment
+ --> $DIR/invalid-html-tags.rs:87:5
+ |
+LL | /// <!--
+ | ^^^
+
+error: aborting due to 13 previous errors
#[doc(alias = "\n")] //~ ERROR
#[doc(alias = "
")] //~^ ERROR
-#[doc(alias = " ")] //~ ERROR
#[doc(alias = "\t")] //~ ERROR
+#[doc(alias = " hello")] //~ ERROR
+#[doc(alias = "hello ")] //~ ERROR
pub struct Foo;
LL | | ")]
| |_^
-error: ' ' character isn't allowed in `#[doc(alias = "...")]`
+error: '\t' character isn't allowed in `#[doc(alias = "...")]`
--> $DIR/check-doc-alias-attr.rs:14:7
|
-LL | #[doc(alias = " ")]
- | ^^^^^^^^^^^
+LL | #[doc(alias = "\t")]
+ | ^^^^^^^^^^^^
-error: '\t' character isn't allowed in `#[doc(alias = "...")]`
+error: `#[doc(alias = "...")]` cannot start or end with ' '
--> $DIR/check-doc-alias-attr.rs:15:7
|
-LL | #[doc(alias = "\t")]
- | ^^^^^^^^^^^^
+LL | #[doc(alias = " hello")]
+ | ^^^^^^^^^^^^^^^^
+
+error: `#[doc(alias = "...")]` cannot start or end with ' '
+ --> $DIR/check-doc-alias-attr.rs:16:7
+ |
+LL | #[doc(alias = "hello ")]
+ | ^^^^^^^^^^^^^^^^
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
static HOSTS: &[&str] = &[
"aarch64-apple-darwin",
+ "aarch64-pc-windows-msvc",
"aarch64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
"arm-unknown-linux-gnueabi",