about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md1
-rw-r--r--clippy_lints/src/declared_lints.rs1
-rw-r--r--clippy_lints/src/lib.rs2
-rw-r--r--clippy_lints/src/question_mark_used.rs52
-rw-r--r--tests/ui/question_mark_used.rs15
-rw-r--r--tests/ui/question_mark_used.stderr11
6 files changed, 82 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index feab94c52a5..73ae215ce1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4676,6 +4676,7 @@ Released 2018-09-13
 [`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
 [`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
 [`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
+[`question_mark_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark_used
 [`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 [`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 0962c3ce66c..0dab6b5f3f1 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -508,6 +508,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
     crate::pub_use::PUB_USE_INFO,
     crate::question_mark::QUESTION_MARK_INFO,
+    crate::question_mark_used::QUESTION_MARK_USED_INFO,
     crate::ranges::MANUAL_RANGE_CONTAINS_INFO,
     crate::ranges::RANGE_MINUS_ONE_INFO,
     crate::ranges::RANGE_PLUS_ONE_INFO,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 223e6698b72..ce5df8c7a7b 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -243,6 +243,7 @@ mod ptr;
 mod ptr_offset_with_cast;
 mod pub_use;
 mod question_mark;
+mod question_mark_used;
 mod ranges;
 mod rc_clone_in_vec_init;
 mod read_zero_byte_vec;
@@ -696,6 +697,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
     store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
     store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
+    store.register_late_pass(|_| Box::new(question_mark_used::QuestionMarkUsed));
     store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
     store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
     store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit));
diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs
new file mode 100644
index 00000000000..9b678e8d753
--- /dev/null
+++ b/clippy_lints/src/question_mark_used.rs
@@ -0,0 +1,52 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+
+use clippy_utils::macros::span_is_local;
+use rustc_hir::{Expr, ExprKind, MatchSource};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for expressions that use the question mark operator and rejects them.
+    ///
+    /// ### Why is this bad?
+    /// Sometimes code wants to avoid the question mark operator because for instance a local
+    /// block requires a macro to re-throw errors to attach additional information to the
+    /// error.
+    ///
+    /// ### Example
+    /// ```ignore
+    /// let result = expr?;
+    /// ```
+    ///
+    /// Could be written:
+    ///
+    /// ```ignore
+    /// utility_macro!(expr);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub QUESTION_MARK_USED,
+    restriction,
+    "complains if the question mark operator is used"
+}
+
+declare_lint_pass!(QuestionMarkUsed => [QUESTION_MARK_USED]);
+
+impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Match(_, _, MatchSource::TryDesugar) = expr.kind {
+            if !span_is_local(expr.span) {
+                return;
+            }
+
+            span_lint_and_help(
+                cx,
+                QUESTION_MARK_USED,
+                expr.span,
+                "question mark operator was used",
+                None,
+                "consider using a custom macro or match expression",
+            );
+        }
+    }
+}
diff --git a/tests/ui/question_mark_used.rs b/tests/ui/question_mark_used.rs
new file mode 100644
index 00000000000..8c3ef789697
--- /dev/null
+++ b/tests/ui/question_mark_used.rs
@@ -0,0 +1,15 @@
+// non rustfixable
+#![allow(unreachable_code)]
+#![allow(dead_code)]
+#![warn(clippy::question_mark_used)]
+
+fn other_function() -> Option<i32> {
+    Some(32)
+}
+
+fn my_function() -> Option<i32> {
+    other_function()?;
+    None
+}
+
+fn main() {}
diff --git a/tests/ui/question_mark_used.stderr b/tests/ui/question_mark_used.stderr
new file mode 100644
index 00000000000..8b5fcbcdbfd
--- /dev/null
+++ b/tests/ui/question_mark_used.stderr
@@ -0,0 +1,11 @@
+error: question mark operator was used
+  --> $DIR/question_mark_used.rs:11:5
+   |
+LL |     other_function()?;
+   |     ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using a custom macro or match expression
+   = note: `-D clippy::question-mark-used` implied by `-D warnings`
+
+error: aborting due to previous error
+