Skip to content

Commit 402a9c9

Browse files
thomccjyn514
authored andcommitted
Uplift the invalid_atomic_ordering lint from clippy to rustc
- Deprecate clippy::invalid_atomic_ordering - Use rustc_diagnostic_item for the orderings in the invalid_atomic_ordering lint - Reduce code duplication - Give up on making enum variants diagnostic items and just look for `Ordering` instead I ran into tons of trouble with this because apparently the change to store HIR attrs in a side table also gave the DefIds of the constructor instead of the variant itself. So I had to change `matches_ordering` to also check the grandparent of the defid as well. - Rename `atomic_ordering_x` symbols to just the name of the variant - Fix typos in checks - there were a few places that said "may not be Release" in the diagnostic but actually checked for SeqCst in the lint. - Make constant items const - Use fewer diagnostic items - Only look at arguments after making sure the method matches This prevents an ICE when there aren't enough arguments. - Ignore trait methods - Only check Ctors instead of going through `qpath_res` The functions take values, so this couldn't ever be anything else. - Add if_chain to allowed dependencies - Fix grammar - Remove unnecessary allow
1 parent 7069a8c commit 402a9c9

28 files changed

+637
-437
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -3923,6 +3923,7 @@ dependencies = [
39233923
name = "rustc_lint"
39243924
version = "0.0.0"
39253925
dependencies = [
3926+
"if_chain",
39263927
"rustc_ast",
39273928
"rustc_ast_pretty",
39283929
"rustc_attr",

compiler/rustc_lint/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ version = "0.0.0"
55
edition = "2018"
66

77
[dependencies]
8+
if_chain = "1.0"
89
tracing = "0.1"
910
unicode-security = "0.0.5"
1011
rustc_middle = { path = "../rustc_middle" }

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ macro_rules! late_lint_passes {
170170
TemporaryCStringAsPtr: TemporaryCStringAsPtr,
171171
NonPanicFmt: NonPanicFmt,
172172
NoopMethodCall: NoopMethodCall,
173+
InvalidAtomicOrdering: InvalidAtomicOrdering,
173174
]
174175
);
175176
};

compiler/rustc_lint/src/types.rs

+236-3
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@ use rustc_attr as attr;
44
use rustc_data_structures::fx::FxHashSet;
55
use rustc_errors::Applicability;
66
use rustc_hir as hir;
7-
use rustc_hir::{is_range_literal, ExprKind, Node};
7+
use rustc_hir::def_id::DefId;
8+
use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
89
use rustc_middle::ty::layout::{IntegerExt, SizeSkeleton};
910
use rustc_middle::ty::subst::SubstsRef;
10-
use rustc_middle::ty::{self, AdtKind, Ty, TyCtxt, TypeFoldable};
11+
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TyCtxt, TypeFoldable};
1112
use rustc_span::source_map;
1213
use rustc_span::symbol::sym;
13-
use rustc_span::{Span, DUMMY_SP};
14+
use rustc_span::{Span, Symbol, DUMMY_SP};
1415
use rustc_target::abi::Abi;
1516
use rustc_target::abi::{Integer, LayoutOf, TagEncoding, Variants};
1617
use rustc_target::spec::abi::Abi as SpecAbi;
1718

19+
use if_chain::if_chain;
1820
use std::cmp;
1921
use std::iter;
2022
use std::ops::ControlFlow;
@@ -1379,3 +1381,234 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
13791381
}
13801382
}
13811383
}
1384+
1385+
declare_lint! {
1386+
/// The `invalid_atomic_ordering` lint detects passing an `Ordering`
1387+
/// to an atomic operation that does not support that ordering.
1388+
///
1389+
/// ### Example
1390+
///
1391+
/// ```rust,compile_fail
1392+
/// # use core::sync::atomic::{AtomicU8, Ordering};
1393+
/// let atom = AtomicU8::new(0);
1394+
/// let value = atom.load(Ordering::Release);
1395+
/// # let _ = value;
1396+
/// ```
1397+
///
1398+
/// {{produces}}
1399+
///
1400+
/// ### Explanation
1401+
///
1402+
/// Some atomic operations are only supported for a subset of the
1403+
/// `atomic::Ordering` variants. Passing an unsupported variant will cause
1404+
/// an unconditional panic at runtime, which is detected by this lint.
1405+
///
1406+
/// This lint will trigger in the following cases: (where `AtomicType` is an
1407+
/// atomic type from `core::sync::atomic`, such as `AtomicBool`,
1408+
/// `AtomicPtr`, `AtomicUsize`, or any of the other integer atomics).
1409+
///
1410+
/// - Passing `Ordering::Acquire` or `Ordering::AcqRel` to
1411+
/// `AtomicType::store`.
1412+
///
1413+
/// - Passing `Ordering::Release` or `Ordering::AcqRel` to
1414+
/// `AtomicType::load`.
1415+
///
1416+
/// - Passing `Ordering::Relaxed` to `core::sync::atomic::fence` or
1417+
/// `core::sync::atomic::compiler_fence`.
1418+
///
1419+
/// - Passing `Ordering::Release` or `Ordering::AcqRel` as the failure
1420+
/// ordering for any of `AtomicType::compare_exchange`,
1421+
/// `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`.
1422+
///
1423+
/// - Passing in a pair of orderings to `AtomicType::compare_exchange`,
1424+
/// `AtomicType::compare_exchange_weak`, or `AtomicType::fetch_update`
1425+
/// where the failure ordering is stronger than the success ordering.
1426+
INVALID_ATOMIC_ORDERING,
1427+
Deny,
1428+
"usage of invalid atomic ordering in atomic operations and memory fences"
1429+
}
1430+
1431+
declare_lint_pass!(InvalidAtomicOrdering => [INVALID_ATOMIC_ORDERING]);
1432+
1433+
impl InvalidAtomicOrdering {
1434+
fn inherent_atomic_method_call<'hir>(
1435+
cx: &LateContext<'_>,
1436+
expr: &Expr<'hir>,
1437+
) -> Option<(Symbol, &'hir [Expr<'hir>])> {
1438+
const ATOMIC_TYPES: &[Symbol] = &[
1439+
sym::AtomicBool,
1440+
sym::AtomicPtr,
1441+
sym::AtomicUsize,
1442+
sym::AtomicU8,
1443+
sym::AtomicU16,
1444+
sym::AtomicU32,
1445+
sym::AtomicU64,
1446+
sym::AtomicU128,
1447+
sym::AtomicIsize,
1448+
sym::AtomicI8,
1449+
sym::AtomicI16,
1450+
sym::AtomicI32,
1451+
sym::AtomicI64,
1452+
sym::AtomicI128,
1453+
];
1454+
if_chain! {
1455+
if let ExprKind::MethodCall(ref method_path, _, args, _) = &expr.kind;
1456+
if let Some(m_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
1457+
if let Some(impl_did) = cx.tcx.impl_of_method(m_def_id);
1458+
if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def();
1459+
// skip extension traits, only lint functions from the standard library
1460+
if cx.tcx.trait_id_of_impl(impl_did).is_none();
1461+
1462+
if let Some(parent) = cx.tcx.parent(adt.did);
1463+
if cx.tcx.is_diagnostic_item(sym::atomic_mod, parent);
1464+
if ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did));
1465+
then {
1466+
return Some((method_path.ident.name, args));
1467+
}
1468+
}
1469+
None
1470+
}
1471+
1472+
fn matches_ordering(cx: &LateContext<'_>, did: DefId, orderings: &[Symbol]) -> bool {
1473+
let tcx = cx.tcx;
1474+
let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
1475+
orderings.iter().any(|ordering| {
1476+
tcx.item_name(did) == *ordering && {
1477+
let parent = tcx.parent(did);
1478+
parent == atomic_ordering
1479+
// needed in case this is a ctor, not a variant
1480+
|| parent.map_or(false, |parent| tcx.parent(parent) == atomic_ordering)
1481+
}
1482+
})
1483+
}
1484+
1485+
fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
1486+
if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
1487+
cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
1488+
} else {
1489+
None
1490+
}
1491+
}
1492+
1493+
fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
1494+
use rustc_hir::def::{DefKind, Res};
1495+
use rustc_hir::QPath;
1496+
if_chain! {
1497+
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr);
1498+
if let Some((ordering_arg, invalid_ordering)) = match method {
1499+
sym::load => Some((&args[1], sym::Release)),
1500+
sym::store => Some((&args[2], sym::Acquire)),
1501+
_ => None,
1502+
};
1503+
1504+
if let ExprKind::Path(QPath::Resolved(_, path)) = ordering_arg.kind;
1505+
if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res;
1506+
if Self::matches_ordering(cx, ctor_id, &[invalid_ordering, sym::AcqRel]);
1507+
then {
1508+
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| {
1509+
if method == sym::load {
1510+
diag.build("atomic loads cannot have `Release` or `AcqRel` ordering")
1511+
.help("consider using ordering modes `Acquire`, `SeqCst` or `Relaxed`")
1512+
.emit()
1513+
} else {
1514+
debug_assert_eq!(method, sym::store);
1515+
diag.build("atomic stores cannot have `Acquire` or `AcqRel` ordering")
1516+
.help("consider using ordering modes `Release`, `SeqCst` or `Relaxed`")
1517+
.emit();
1518+
}
1519+
});
1520+
}
1521+
}
1522+
}
1523+
1524+
fn check_memory_fence(cx: &LateContext<'_>, expr: &Expr<'_>) {
1525+
if_chain! {
1526+
if let ExprKind::Call(ref func, ref args) = expr.kind;
1527+
if let ExprKind::Path(ref func_qpath) = func.kind;
1528+
if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
1529+
if cx.tcx.is_diagnostic_item(sym::fence, def_id) ||
1530+
cx.tcx.is_diagnostic_item(sym::compiler_fence, def_id);
1531+
if let ExprKind::Path(ref ordering_qpath) = &args[0].kind;
1532+
if let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id();
1533+
if Self::matches_ordering(cx, ordering_def_id, &[sym::Relaxed]);
1534+
then {
1535+
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| {
1536+
diag.build("memory fences cannot have `Relaxed` ordering")
1537+
.help("consider using ordering modes `Acquire`, `Release`, `AcqRel` or `SeqCst`")
1538+
.emit();
1539+
});
1540+
}
1541+
}
1542+
}
1543+
1544+
fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
1545+
if_chain! {
1546+
if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr);
1547+
if let Some((success_order_arg, failure_order_arg)) = match method {
1548+
sym::fetch_update => Some((&args[1], &args[2])),
1549+
sym::compare_exchange | sym::compare_exchange_weak => Some((&args[3], &args[4])),
1550+
_ => None,
1551+
};
1552+
1553+
if let Some(fail_ordering_def_id) = Self::opt_ordering_defid(cx, failure_order_arg);
1554+
then {
1555+
// Helper type holding on to some checking and error reporting data. Has
1556+
// - (success ordering,
1557+
// - list of failure orderings forbidden by the success order,
1558+
// - suggestion message)
1559+
type OrdLintInfo = (Symbol, &'static [Symbol], &'static str);
1560+
const RELAXED: OrdLintInfo = (sym::Relaxed, &[sym::SeqCst, sym::Acquire], "ordering mode `Relaxed`");
1561+
const ACQUIRE: OrdLintInfo = (sym::Acquire, &[sym::SeqCst], "ordering modes `Acquire` or `Relaxed`");
1562+
const SEQ_CST: OrdLintInfo = (sym::SeqCst, &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
1563+
const RELEASE: OrdLintInfo = (sym::Release, RELAXED.1, RELAXED.2);
1564+
const ACQREL: OrdLintInfo = (sym::AcqRel, ACQUIRE.1, ACQUIRE.2);
1565+
const SEARCH: [OrdLintInfo; 5] = [RELAXED, ACQUIRE, SEQ_CST, RELEASE, ACQREL];
1566+
1567+
let success_lint_info = Self::opt_ordering_defid(cx, success_order_arg)
1568+
.and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
1569+
SEARCH
1570+
.iter()
1571+
.copied()
1572+
.find(|(ordering, ..)| {
1573+
Self::matches_ordering(cx, success_ord_def_id, &[*ordering])
1574+
})
1575+
});
1576+
if Self::matches_ordering(cx, fail_ordering_def_id, &[sym::Release, sym::AcqRel]) {
1577+
// If we don't know the success order is, use what we'd suggest
1578+
// if it were maximally permissive.
1579+
let suggested = success_lint_info.unwrap_or(SEQ_CST).2;
1580+
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| {
1581+
let msg = format!(
1582+
"{}'s failure ordering may not be `Release` or `AcqRel`",
1583+
method,
1584+
);
1585+
diag.build(&msg)
1586+
.help(&format!("consider using {} instead", suggested))
1587+
.emit();
1588+
});
1589+
} else if let Some((success_ord, bad_ords_given_success, suggested)) = success_lint_info {
1590+
if Self::matches_ordering(cx, fail_ordering_def_id, bad_ords_given_success) {
1591+
cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| {
1592+
let msg = format!(
1593+
"{}'s failure ordering may not be stronger than the success ordering of `{}`",
1594+
method,
1595+
success_ord,
1596+
);
1597+
diag.build(&msg)
1598+
.help(&format!("consider using {} instead", suggested))
1599+
.emit();
1600+
});
1601+
}
1602+
}
1603+
}
1604+
}
1605+
}
1606+
}
1607+
1608+
impl<'tcx> LateLintPass<'tcx> for InvalidAtomicOrdering {
1609+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
1610+
Self::check_atomic_load_store(cx, expr);
1611+
Self::check_memory_fence(cx, expr);
1612+
Self::check_atomic_compare_exchange(cx, expr);
1613+
}
1614+
}

compiler/rustc_span/src/symbol.rs

+29
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ symbols! {
121121
// There is currently no checking that all symbols are used; that would be
122122
// nice to have.
123123
Symbols {
124+
AcqRel,
125+
Acquire,
124126
Alignment,
125127
Any,
126128
Arc,
@@ -129,6 +131,20 @@ symbols! {
129131
Arguments,
130132
AsMut,
131133
AsRef,
134+
AtomicBool,
135+
AtomicI128,
136+
AtomicI16,
137+
AtomicI32,
138+
AtomicI64,
139+
AtomicI8,
140+
AtomicIsize,
141+
AtomicPtr,
142+
AtomicU128,
143+
AtomicU16,
144+
AtomicU32,
145+
AtomicU64,
146+
AtomicU8,
147+
AtomicUsize,
132148
BTreeEntry,
133149
BTreeMap,
134150
BTreeSet,
@@ -215,12 +231,15 @@ symbols! {
215231
Rc,
216232
Ready,
217233
Receiver,
234+
Relaxed,
235+
Release,
218236
Result,
219237
Return,
220238
Right,
221239
RustcDecodable,
222240
RustcEncodable,
223241
Send,
242+
SeqCst,
224243
Some,
225244
StructuralEq,
226245
StructuralPartialEq,
@@ -311,6 +330,8 @@ symbols! {
311330
assume_init,
312331
async_await,
313332
async_closure,
333+
atomic,
334+
atomic_mod,
314335
atomics,
315336
att_syntax,
316337
attr,
@@ -390,8 +411,12 @@ symbols! {
390411
coerce_unsized,
391412
cold,
392413
column,
414+
compare_and_swap,
415+
compare_exchange,
416+
compare_exchange_weak,
393417
compile_error,
394418
compiler_builtins,
419+
compiler_fence,
395420
concat,
396421
concat_idents,
397422
conservative_impl_trait,
@@ -575,6 +600,8 @@ symbols! {
575600
fadd_fast,
576601
fdiv_fast,
577602
feature,
603+
fence,
604+
fetch_update,
578605
ffi,
579606
ffi_const,
580607
ffi_pure,
@@ -728,6 +755,7 @@ symbols! {
728755
lint_reasons,
729756
literal,
730757
llvm_asm,
758+
load,
731759
local,
732760
local_inner_macros,
733761
log10f32,
@@ -1217,6 +1245,7 @@ symbols! {
12171245
stmt,
12181246
stmt_expr_attributes,
12191247
stop_after_dataflow,
1248+
store,
12201249
str,
12211250
str_alloc,
12221251
string_type,

0 commit comments

Comments
 (0)