From a71f1997d861ac7235c918e6d4c35987359080b3 Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Fri, 3 Dec 2021 15:05:55 +0200 Subject: [PATCH 001/536] Updated styles for checkboxes --- util/gh-pages/index.html | 123 ++++++++++++++++++++++++++++++--------- 1 file changed, 96 insertions(+), 27 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index f175700a3f47..e96d13dc6825 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -25,7 +25,34 @@ Otherwise, have a great day =^.^= blockquote { font-size: 1em; } [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; } - .form-inline .checkbox { margin-right: 0.6em } + .dropdown-menu .checkbox { + width: 100%;; + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap; + margin: 0; + } + + .dropdown-menu .checkbox label { + padding-left: 0; + width: 100%; + } + + .dropdown-menu .checkbox input { + position: relative; + margin: 0; + padding: 0; + } + + .dropdown-menu .checkbox:hover { + color: #262626; + text-decoration: none; + background-color: #f5f5f5; + } .panel-heading { cursor: pointer; } @@ -250,7 +277,7 @@ Otherwise, have a great day =^.^=
  • Ayu
  • -
    +
    @@ -272,32 +299,54 @@ Otherwise, have a great day =^.^=
    -
    -
    -

    - Lint levels - (?) -

    -
    - -
    +
    +
    + +
    -
    -
    -
    -

    - Lint groups - (?) -

    -
    - -
    +
    + +
    @@ -462,6 +511,26 @@ Otherwise, have a great day =^.^= suspicious: true, }; $scope.groups = GROUPS_FILTER_DEFAULT; + $scope.toggleDropdown = function (name, $event) { + $scope.selectedDropdown = name; + $event.stopPropagation(); + } + $scope.toggleLevels = function (value) { + const levels = $scope.levels; + for (const key in levels) { + if (levels.hasOwnProperty(key)) { + levels[key] = value; + } + } + }; + $scope.toggleGroups = function (value) { + const groups = $scope.groups; + for (const key in groups) { + if (groups.hasOwnProperty(key)) { + groups[key] = value; + } + } + }; $scope.byGroups = function (lint) { return $scope.groups[lint.group]; }; From 0f655db00d50f53ddd078a40d7df23a08b7b34cc Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Sat, 4 Dec 2021 19:47:07 +0200 Subject: [PATCH 002/536] Added badge to display selected elemenets count --- util/gh-pages/index.html | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index e96d13dc6825..3bf1bbd107af 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -302,7 +302,7 @@ Otherwise, have a great day =^.^=
    -
    -
    -
    +
    From dcfb756b97789b327f38c201d7f8ba34a91368ee Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Tue, 7 Dec 2021 18:42:07 +0200 Subject: [PATCH 007/536] Fixed overlapping "Fork me" logo --- util/gh-pages/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index b79cb64aa018..f3aeb07a0eb4 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -454,7 +454,7 @@ Otherwise, have a great day =^.^=
    - Fork me on Github + Fork me on Github From bb531320ebe6c3ac6798bee4201f33741aed8fdc Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Tue, 7 Dec 2021 19:41:49 +0200 Subject: [PATCH 008/536] Improved performance by adding ng-if --- util/gh-pages/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index f3aeb07a0eb4..88b5c9c3f77f 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -425,7 +425,7 @@ Otherwise, have a great day =^.^= -
    +
      From 4161a67c1bf0215523738e419b996ed5d2ea8f97 Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Thu, 9 Dec 2021 13:49:07 +0200 Subject: [PATCH 009/536] Added dropdown directive to theme-selector --- util/gh-pages/index.html | 133 +++++++++++++++++++++++---------------- 1 file changed, 79 insertions(+), 54 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 88b5c9c3f77f..13f280799eb3 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -38,7 +38,6 @@ Otherwise, have a great day =^.^= .dropdown-menu .checkbox { width: 100%; display: block; - padding: 3px 20px; clear: both; font-weight: 400; line-height: 1.42857143; @@ -47,8 +46,8 @@ Otherwise, have a great day =^.^= } .dropdown-menu .checkbox label { - padding-left: 0; - width: 100%; + padding: 3px 20px; + width: 100%; } .dropdown-menu .checkbox input { @@ -209,13 +208,17 @@ Otherwise, have a great day =^.^= --inline-code-bg: #191f26; } + .theme-dropdown { + position: absolute; + margin: 0.7em; + z-index: 10; + } + /* Applying the mdBook theme */ .theme-icon { - position: absolute; text-align: center; width: 2em; height: 2em; - margin: 0.7em; line-height: 2em; border: solid 1px var(--icons); border-radius: 5px; @@ -226,24 +229,28 @@ Otherwise, have a great day =^.^= background: var(--theme-hover); } .theme-choice { - position: absolute; - margin-top: calc(2em + 0.7em); - margin-left: 0.7em; + display: none; list-style: none; border: 1px solid var(--theme-popup-border); border-radius: 5px; color: var(--fg); background: var(--theme-popup-bg); padding: 0 0; - z-index: 10; + overflow: hidden; } - .theme-choice > li { + + .theme-dropdown.open .theme-choice { + display: block; + } + + .theme-choice>li { padding: 5px 10px; font-size: 0.8em; user-select: none; cursor: pointer; } - .theme-choice > li:hover { + + .theme-choice>li:hover { background: var(--theme-hover); } @@ -307,15 +314,13 @@ Otherwise, have a great day =^.^= - -
      🖌
      - + +
      +
      🖌
      +
        +
      • {{name}}
      • +
      +
    -
    +
    @@ -518,6 +521,46 @@ Otherwise, have a great day =^.^= ); }; }) + .directive('themeDropdown', function ($document) { + return { + restrict: 'A', + link: function ($scope, $element, $attr) { + $element.bind('click', function () { + $element.toggleClass('open'); + $element.addClass('open-recent'); + }); + + $document.bind('click', function () { + if (!$element.hasClass('open-recent')) { + $element.removeClass('open'); + } + $element.removeClass('open-recent'); + }) + } + } + }) + .directive('filterDropdown', function ($document) { + return { + restrict: 'A', + link: function ($scope, $element, $attr) { + $element.bind('click', function (event) { + if (event.target.closest('button')) { + $element.toggleClass('open'); + } else { + $element.addClass('open'); + } + $element.addClass('open-recent'); + }); + + $document.bind('click', function () { + if (!$element.hasClass('open-recent')) { + $element.removeClass('open'); + } + $element.removeClass('open-recent'); + }) + } + } + }) .directive('onFinishRender', function ($timeout) { return { restrict: 'A', @@ -551,15 +594,19 @@ Otherwise, have a great day =^.^= suspicious: true, }; $scope.groups = GROUPS_FILTER_DEFAULT; - $scope.toggleDropdown = function (name, $event) { - const target = $event.target; - if (name === $scope.selectedDropdown && target.closest('button')) { - $scope.selectedDropdown = undefined; - } else { - $scope.selectedDropdown = name; - } - $event.stopPropagation(); + const THEMES_DEFAULT = { + light: "Light", + rust: "Rust", + coal: "Coal", + navy: "Navy", + ayu: "Ayu" + }; + $scope.themes = THEMES_DEFAULT; + + $scope.selectTheme = function (theme) { + setTheme(theme, true); } + $scope.toggleLevels = function (value) { const levels = $scope.levels; for (const key in levels) { @@ -670,28 +717,6 @@ Otherwise, have a great day =^.^= } } - function setupListeners() { - let themeIcon = document.getElementById("theme-icon"); - let themeMenu = document.getElementById("theme-menu"); - themeIcon.addEventListener("click", function(e) { - if (themeMenu.style.display == "none") { - themeMenu.style.display = "block"; - } else { - themeMenu.style.display = "none"; - } - }); - - let children = themeMenu.children; - for (let index = 0; index < children.length; index++) { - let child = children[index]; - child.addEventListener("click", function(e) { - setTheme(child.id, true); - }); - } - } - - setupListeners(); - function setTheme(theme, store) { let enableHighlight = false; let enableNight = false; From 296af08b3c4ee2cd74b53e71279c8d2be254031e Mon Sep 17 00:00:00 2001 From: CrazyRoka Date: Thu, 16 Dec 2021 18:59:04 +0200 Subject: [PATCH 010/536] Removed unused styles and labels in dropdowns --- util/gh-pages/index.html | 104 +++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 13f280799eb3..dcc0978fe109 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -36,28 +36,22 @@ Otherwise, have a great day =^.^= } .dropdown-menu .checkbox { - width: 100%; display: block; - clear: both; - font-weight: 400; - line-height: 1.42857143; white-space: nowrap; margin: 0; - } - - .dropdown-menu .checkbox label { + } + .dropdown-menu .checkbox label { padding: 3px 20px; width: 100%; - } + } - .dropdown-menu .checkbox input { - position: relative; - margin: 0 0.5rem 0; - padding: 0; - vertical-align: middle; - } + .dropdown-menu .checkbox input { + position: relative; + margin: 0 0.5rem 0; + padding: 0; + } - .dropdown-menu .checkbox:hover { + .dropdown-menu .checkbox:hover { background-color: var(--theme-hover); } @@ -243,7 +237,7 @@ Otherwise, have a great day =^.^= display: block; } - .theme-choice>li { + .theme-choice > li { padding: 5px 10px; font-size: 0.8em; user-select: none; @@ -352,11 +346,13 @@ Otherwise, have a great day =^.^=
    +
    +
    + + +
    +
    -
    +
    - - + + - @@ -406,7 +460,7 @@ Otherwise, have a great day =^.^=
    -
    +

    @@ -601,6 +655,15 @@ Otherwise, have a great day =^.^= }; $scope.themes = THEMES_DEFAULT; + const DEFAULT_VERSION_FILTERS = { + ">=": { enabled: false, version_str: "" }, + "<=": { enabled: false, version_str: "" }, + "==": { enabled: false, version_str: "" }, + }; + // Weird workaround to get a copy of the object + $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); + $scope.version_regex = new RegExp('\\d\.\\d{2}\.\\d'); + $scope.selectTheme = function (theme) { setTheme(theme, true); } @@ -613,6 +676,7 @@ Otherwise, have a great day =^.^= } } }; + $scope.toggleGroups = function (value) { const groups = $scope.groups; for (const key in groups) { @@ -621,9 +685,83 @@ Otherwise, have a great day =^.^= } } }; + $scope.selectedValuesCount = function (obj) { return Object.values(obj).filter(x => x).length; } + + $scope.clearVersionFilters = function () { + $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); + } + + $scope.versionSymbol = function() { + const version_filters = $scope.version_filters; + let filter = ">="; + for (const key in version_filters) { + if (version_filters[key]) { + filter = key; + } + } + + return filter; + } + + $scope.byVersion = function(lint) { + function validate_version_str(ver) { + return ver.length === 6 && $scope.version_regex.test(ver); + } + + function cmp_version(ver1, ver2, filter) { + // < 0: lint_version < version + // 0: equal + // > 0: lint_version > version + let result = ver1.localeCompare(ver2, undefined, { + numeric: true, + sensitivity: "base" + }); + + // "==" gets the highest priority, since all filters are inclusive + return (result === 0) || (filter === ">=" && result > 0) || (filter === "<=" && result < 0); + } + + let filters = $scope.version_filters; + + // Strip the "pre " prefix for pre 1.29.0 lints + let lint_version = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; + + for (const filter in filters) { + let version_str = filters[filter].version_str; + + // Skip the work for version strings with invalid lengths or characters + if (!filters[filter].enabled || !validate_version_str(version_str)) { + continue; + } + + let result = cmp_version(lint_version, version_str, filter); + if (result && filter === "==") { + return true; + } else if (!result) { + return false; + } + + let cmp_filter; + if (filter === ">=") { + cmp_filter = "<="; + } else { + cmp_filter = ">="; + } + + let cmp_version_str = filters[cmp_filter].version_str; + if (!filters[cmp_filter].enabled || !validate_version_str(cmp_version_str)) { + return true; + } + + return cmp_version(lint_version, cmp_version_str, cmp_filter); + } + + return true; + } + $scope.byGroups = function (lint) { return $scope.groups[lint.group]; }; @@ -753,4 +891,4 @@ Otherwise, have a great day =^.^= setTheme(localStorage.getItem('clippy-lint-list-theme'), false); - + \ No newline at end of file From 06cc1abbb1b64a60c059137b3d610bbe0e0f3bea Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 26 Apr 2022 18:22:37 -0400 Subject: [PATCH 416/536] Move js out to its own file --- .github/deploy.sh | 1 + util/gh-pages/index.html | 375 +-------------------------------------- util/gh-pages/script.js | 372 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+), 374 deletions(-) create mode 100644 util/gh-pages/script.js diff --git a/.github/deploy.sh b/.github/deploy.sh index 34225a540290..5a59f94ec918 100644 --- a/.github/deploy.sh +++ b/.github/deploy.sh @@ -8,6 +8,7 @@ rm -rf out/master/ || exit 0 echo "Making the docs for master" mkdir out/master/ cp util/gh-pages/index.html out/master +cp util/gh-pages/script.js out/master cp util/gh-pages/lints.json out/master if [[ -n $TAG_NAME ]]; then diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 286f3a34753d..552c30f8353f 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -516,379 +516,6 @@ Otherwise, have a great day =^.^= - + \ No newline at end of file diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js new file mode 100644 index 000000000000..469a9306aee5 --- /dev/null +++ b/util/gh-pages/script.js @@ -0,0 +1,372 @@ +(function () { + var md = window.markdownit({ + html: true, + linkify: true, + typographer: true, + highlight: function (str, lang) { + if (lang && hljs.getLanguage(lang)) { + try { + return '
    ' +
    +                        hljs.highlight(lang, str, true).value +
    +                        '
    '; + } catch (__) {} + } + + return '
    ' + md.utils.escapeHtml(str) + '
    '; + } + }); + + function scrollToLint(lintId) { + var target = document.getElementById(lintId); + if (!target) { + return; + } + target.scrollIntoView(); + } + + function scrollToLintByURL($scope) { + var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) { + scrollToLint(window.location.hash.slice(1)); + removeListener(); + }); + } + + function selectGroup($scope, selectedGroup) { + var groups = $scope.groups; + for (var group in groups) { + if (groups.hasOwnProperty(group)) { + if (group === selectedGroup) { + groups[group] = true; + } else { + groups[group] = false; + } + } + } + } + + angular.module("clippy", []) + .filter('markdown', function ($sce) { + return function (text) { + return $sce.trustAsHtml( + md.render(text || '') + // Oh deer, what a hack :O + .replace('=": { enabled: false, version_str: "" }, + "<=": { enabled: false, version_str: "" }, + "==": { enabled: false, version_str: "" }, + }; + // Weird workaround to get a copy of the object + $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); + $scope.version_regex = new RegExp('\\d\.\\d{2}\.\\d'); + + $scope.selectTheme = function (theme) { + setTheme(theme, true); + } + + $scope.toggleLevels = function (value) { + const levels = $scope.levels; + for (const key in levels) { + if (levels.hasOwnProperty(key)) { + levels[key] = value; + } + } + }; + + $scope.toggleGroups = function (value) { + const groups = $scope.groups; + for (const key in groups) { + if (groups.hasOwnProperty(key)) { + groups[key] = value; + } + } + }; + + $scope.selectedValuesCount = function (obj) { + return Object.values(obj).filter(x => x).length; + } + + $scope.clearVersionFilters = function () { + $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); + } + + $scope.versionSymbol = function() { + const version_filters = $scope.version_filters; + let filter = ">="; + for (const key in version_filters) { + if (version_filters[key]) { + filter = key; + } + } + + return filter; + } + + $scope.byVersion = function(lint) { + function validate_version_str(ver) { + return ver.length === 6 && $scope.version_regex.test(ver); + } + + function cmp_version(ver1, ver2, filter) { + // < 0: lint_version < version + // 0: equal + // > 0: lint_version > version + let result = ver1.localeCompare(ver2, undefined, { + numeric: true, + sensitivity: "base" + }); + + // "==" gets the highest priority, since all filters are inclusive + return (result === 0) || (filter === ">=" && result > 0) || (filter === "<=" && result < 0); + } + + let filters = $scope.version_filters; + + // Strip the "pre " prefix for pre 1.29.0 lints + let lint_version = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; + + for (const filter in filters) { + let version_str = filters[filter].version_str; + + // Skip the work for version strings with invalid lengths or characters + if (!filters[filter].enabled || !validate_version_str(version_str)) { + continue; + } + + let result = cmp_version(lint_version, version_str, filter); + if (result && filter === "==") { + return true; + } else if (!result) { + return false; + } + + let cmp_filter; + if (filter === ">=") { + cmp_filter = "<="; + } else { + cmp_filter = ">="; + } + + let cmp_version_str = filters[cmp_filter].version_str; + if (!filters[cmp_filter].enabled || !validate_version_str(cmp_version_str)) { + return true; + } + + return cmp_version(lint_version, cmp_version_str, cmp_filter); + } + + return true; + } + + $scope.byGroups = function (lint) { + return $scope.groups[lint.group]; + }; + + $scope.bySearch = function (lint, index, array) { + let searchStr = $scope.search; + // It can be `null` I haven't missed this value + if (searchStr == null || searchStr.length < 3) { + return true; + } + searchStr = searchStr.toLowerCase(); + + // Search by id + if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) { + return true; + } + + // Search the description + // The use of `for`-loops instead of `foreach` enables us to return early + let terms = searchStr.split(" "); + let docsLowerCase = lint.docs.toLowerCase(); + for (index = 0; index < terms.length; index++) { + // This is more likely and will therefor be checked first + if (docsLowerCase.indexOf(terms[index]) !== -1) { + continue; + } + + if (lint.id.indexOf(terms[index]) !== -1) { + continue; + } + + return false; + } + + return true; + } + + // Get data + $scope.open = {}; + $scope.loading = true; + // This will be used to jump into the source code of the version that this documentation is for. + $scope.docVersion = window.location.pathname.split('/')[2] || "master"; + + if (window.location.hash.length > 1) { + $scope.search = window.location.hash.slice(1); + $scope.open[window.location.hash.slice(1)] = true; + scrollToLintByURL($scope); + } + + $http.get('./lints.json') + .success(function (data) { + $scope.data = data; + $scope.loading = false; + + var selectedGroup = getQueryVariable("sel"); + if (selectedGroup) { + selectGroup($scope, selectedGroup.toLowerCase()); + } + + scrollToLintByURL($scope); + + setTimeout(function () { + var el = document.getElementById('filter-input'); + if (el) { el.focus() } + }, 0); + }) + .error(function (data) { + $scope.error = data; + $scope.loading = false; + }); + + window.addEventListener('hashchange', function () { + // trigger re-render + $timeout(function () { + $scope.levels = LEVEL_FILTERS_DEFAULT; + $scope.search = window.location.hash.slice(1); + $scope.open[window.location.hash.slice(1)] = true; + + scrollToLintByURL($scope); + }); + return true; + }, false); + }); +})(); + +function getQueryVariable(variable) { + var query = window.location.search.substring(1); + var vars = query.split('&'); + for (var i = 0; i < vars.length; i++) { + var pair = vars[i].split('='); + if (decodeURIComponent(pair[0]) == variable) { + return decodeURIComponent(pair[1]); + } + } +} + +function setTheme(theme, store) { + let enableHighlight = false; + let enableNight = false; + let enableAyu = false; + + if (theme == "ayu") { + enableAyu = true; + } else if (theme == "coal" || theme == "navy") { + enableNight = true; + } else if (theme == "rust") { + enableHighlight = true; + } else { + enableHighlight = true; + // this makes sure that an unknown theme request gets set to a known one + theme = "light"; + } + document.getElementsByTagName("body")[0].className = theme; + + document.getElementById("styleHighlight").disabled = !enableHighlight; + document.getElementById("styleNight").disabled = !enableNight; + document.getElementById("styleAyu").disabled = !enableAyu; + + if (store) { + try { + localStorage.setItem('clippy-lint-list-theme', theme); + } catch (e) { } + } +} + +// loading the theme after the initial load +setTheme(localStorage.getItem('clippy-lint-list-theme'), false); From 948af01633337b3ab57f0db84d49128c5dbc417b Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 14 Apr 2022 15:13:10 -0400 Subject: [PATCH 417/536] Don't lint `vec_init_then_push` when further extended --- clippy_lints/src/vec_init_then_push.rs | 181 +++++++++++++++++-------- clippy_utils/src/higher.rs | 19 ++- clippy_utils/src/visitors.rs | 60 +++++++- tests/ui/vec_init_then_push.rs | 42 ++++++ tests/ui/vec_init_then_push.stderr | 24 +++- 5 files changed, 255 insertions(+), 71 deletions(-) diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index fbf2b3e081b8..5a5dfcfdc8ab 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -1,14 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; use clippy_utils::source::snippet; -use clippy_utils::{path_to_local, path_to_local_id}; -use if_chain::if_chain; +use clippy_utils::visitors::for_each_local_use_after_expr; +use clippy_utils::{get_parent_expr, path_to_local_id}; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, Stmt, StmtKind}; +use rustc_hir::def::Res; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, Stmt, StmtKind, UnOp, +}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::Span; +use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -43,26 +47,86 @@ pub struct VecInitThenPush { struct VecPushSearcher { local_id: HirId, init: VecInitKind, - lhs_is_local: bool, - lhs_span: Span, + lhs_is_let: bool, + let_ty_span: Option, + name: Symbol, err_span: Span, - found: u64, + found: u128, + last_push_expr: HirId, } impl VecPushSearcher { fn display_err(&self, cx: &LateContext<'_>) { - match self.init { + let min_pushes_for_extension = match self.init { _ if self.found == 0 => return, - VecInitKind::WithLiteralCapacity(x) if x > self.found => return, + VecInitKind::WithConstCapacity(x) if x > self.found => return, + VecInitKind::WithConstCapacity(x) => x, VecInitKind::WithExprCapacity(_) => return, - _ => (), + _ => 3, }; - let mut s = if self.lhs_is_local { + let mut needs_mut = false; + let res = for_each_local_use_after_expr(cx, self.local_id, self.last_push_expr, |e| { + let Some(parent) = get_parent_expr(cx, e) else { + return ControlFlow::Continue(()) + }; + let adjusted_ty = cx.typeck_results().expr_ty_adjusted(e); + let adjusted_mut = adjusted_ty.ref_mutability().unwrap_or(Mutability::Not); + needs_mut |= adjusted_mut == Mutability::Mut; + match parent.kind { + ExprKind::AddrOf(_, Mutability::Mut, _) => { + needs_mut = true; + return ControlFlow::Break(true); + }, + ExprKind::Unary(UnOp::Deref, _) | ExprKind::Index(..) if !needs_mut => { + let mut last_place = parent; + while let Some(parent) = get_parent_expr(cx, parent) { + if matches!(parent.kind, ExprKind::Unary(UnOp::Deref, _) | ExprKind::Field(..)) + || matches!(parent.kind, ExprKind::Index(e, _) if e.hir_id == last_place.hir_id) + { + last_place = parent; + } else { + break; + } + } + needs_mut |= cx.typeck_results().expr_ty_adjusted(last_place).ref_mutability() + == Some(Mutability::Mut) + || get_parent_expr(cx, last_place) + .map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _))); + }, + ExprKind::MethodCall(_, [recv, ..], _) + if recv.hir_id == e.hir_id + && adjusted_mut == Mutability::Mut + && !adjusted_ty.peel_refs().is_slice() => + { + return ControlFlow::Break(true); + }, + ExprKind::Assign(lhs, ..) if e.hir_id == lhs.hir_id => { + needs_mut = true; + return ControlFlow::Break(false); + }, + _ => (), + } + ControlFlow::Continue(()) + }); + + // Avoid allocating small `Vec`s when they'll be extended right after. + if res == ControlFlow::Break(true) && self.found <= min_pushes_for_extension { + return; + } + + let mut s = if self.lhs_is_let { String::from("let ") } else { String::new() }; - s.push_str(&snippet(cx, self.lhs_span, "..")); + if needs_mut { + s.push_str("mut "); + } + s.push_str(self.name.as_str()); + if let Some(span) = self.let_ty_span { + s.push_str(": "); + s.push_str(&snippet(cx, span, "_")); + } s.push_str(" = vec![..];"); span_lint_and_sugg( @@ -83,60 +147,63 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { } fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { - if_chain! { - if !in_external_macro(cx.sess(), local.span); - if let Some(init) = local.init; - if let PatKind::Binding(BindingAnnotation::Mutable, id, _, None) = local.pat.kind; - if let Some(init_kind) = get_vec_init_kind(cx, init); - then { - self.searcher = Some(VecPushSearcher { - local_id: id, - init: init_kind, - lhs_is_local: true, - lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)), - err_span: local.span, - found: 0, - }); - } + if let Some(init_expr) = local.init + && let PatKind::Binding(BindingAnnotation::Mutable, id, name, None) = local.pat.kind + && !in_external_macro(cx.sess(), local.span) + && let Some(init) = get_vec_init_kind(cx, init_expr) + && !matches!(init, VecInitKind::WithExprCapacity(_)) + { + self.searcher = Some(VecPushSearcher { + local_id: id, + init, + lhs_is_let: true, + name: name.name, + let_ty_span: local.ty.map(|ty| ty.span), + err_span: local.span, + found: 0, + last_push_expr: init_expr.hir_id, + }); } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if_chain! { - if self.searcher.is_none(); - if !in_external_macro(cx.sess(), expr.span); - if let ExprKind::Assign(left, right, _) = expr.kind; - if let Some(id) = path_to_local(left); - if let Some(init_kind) = get_vec_init_kind(cx, right); - then { - self.searcher = Some(VecPushSearcher { - local_id: id, - init: init_kind, - lhs_is_local: false, - lhs_span: left.span, - err_span: expr.span, - found: 0, - }); - } + if self.searcher.is_none() + && let ExprKind::Assign(left, right, _) = expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = left.kind + && let [name] = &path.segments + && let Res::Local(id) = path.res + && !in_external_macro(cx.sess(), expr.span) + && let Some(init) = get_vec_init_kind(cx, right) + && !matches!(init, VecInitKind::WithExprCapacity(_)) + { + self.searcher = Some(VecPushSearcher { + local_id: id, + init, + lhs_is_let: false, + let_ty_span: None, + name: name.ident.name, + err_span: expr.span, + found: 0, + last_push_expr: expr.hir_id, + }); } } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { if let Some(searcher) = self.searcher.take() { - if_chain! { - if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind; - if let ExprKind::MethodCall(path, [self_arg, _], _) = expr.kind; - if path_to_local_id(self_arg, searcher.local_id); - if path.ident.name.as_str() == "push"; - then { - self.searcher = Some(VecPushSearcher { - found: searcher.found + 1, - err_span: searcher.err_span.to(stmt.span), - .. searcher - }); - } else { - searcher.display_err(cx); - } + if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind + && let ExprKind::MethodCall(name, [self_arg, _], _) = expr.kind + && path_to_local_id(self_arg, searcher.local_id) + && name.ident.as_str() == "push" + { + self.searcher = Some(VecPushSearcher { + found: searcher.found + 1, + err_span: searcher.err_span.to(stmt.span), + last_push_expr: expr.hir_id, + .. searcher + }); + } else { + searcher.display_err(cx); } } } diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 2095fc966c5d..1e0fc789af24 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -2,10 +2,11 @@ #![deny(clippy::missing_docs_in_private_items)] +use crate::consts::{constant_simple, Constant}; use crate::ty::is_type_diagnostic_item; use crate::{is_expn_of, match_def_path, paths}; use if_chain::if_chain; -use rustc_ast::ast::{self, LitKind}; +use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; use rustc_lint::LateContext; @@ -431,7 +432,7 @@ pub enum VecInitKind { /// `Vec::default()` or `Default::default()` Default, /// `Vec::with_capacity(123)` - WithLiteralCapacity(u64), + WithConstCapacity(u128), /// `Vec::with_capacity(slice.len())` WithExprCapacity(HirId), } @@ -449,15 +450,11 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - return Some(VecInitKind::Default); } else if name.ident.name.as_str() == "with_capacity" { let arg = args.get(0)?; - if_chain! { - if let ExprKind::Lit(lit) = &arg.kind; - if let LitKind::Int(num, _) = lit.node; - then { - return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?)); - } - } - return Some(VecInitKind::WithExprCapacity(arg.hir_id)); - } + return match constant_simple(cx, cx.typeck_results(), arg) { + Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), + _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), + }; + }; }, ExprKind::Path(QPath::Resolved(_, path)) if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index c00bc2bd213f..b6c8f1d516e5 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,4 +1,4 @@ -use crate::path_to_local_id; +use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -436,3 +436,61 @@ pub fn for_each_value_source<'tcx, B>( _ => f(e), } } + +/// Runs the given function for each path expression referencing the given local which occur after +/// the given expression. +pub fn for_each_local_use_after_expr<'tcx, B>( + cx: &LateContext<'tcx>, + local_id: HirId, + expr_id: HirId, + f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> ControlFlow { + struct V<'cx, 'tcx, F, B> { + cx: &'cx LateContext<'tcx>, + local_id: HirId, + expr_id: HirId, + found: bool, + res: ControlFlow, + f: F, + } + impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> { + type NestedFilter = nested_filter::OnlyBodies; + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) { + if !self.found { + if e.hir_id == self.expr_id { + self.found = true; + } else { + walk_expr(self, e); + } + return; + } + if self.res.is_break() { + return; + } + if path_to_local_id(e, self.local_id) { + self.res = (self.f)(e); + } else { + walk_expr(self, e); + } + } + } + + if let Some(b) = get_enclosing_block(cx, local_id) { + let mut v = V { + cx, + local_id, + expr_id, + found: false, + res: ControlFlow::Continue(()), + f, + }; + v.visit_block(b); + v.res + } else { + ControlFlow::Continue(()) + } +} diff --git a/tests/ui/vec_init_then_push.rs b/tests/ui/vec_init_then_push.rs index 5099aad83bcb..768ea3e668a3 100644 --- a/tests/ui/vec_init_then_push.rs +++ b/tests/ui/vec_init_then_push.rs @@ -44,3 +44,45 @@ pub fn no_lint() -> Vec { } } } + +fn _from_iter(items: impl Iterator) -> Vec { + let mut v = Vec::new(); + v.push(0); + v.push(1); + v.extend(items); + v +} + +fn _cond_push(x: bool) -> Vec { + let mut v = Vec::new(); + v.push(0); + if x { + v.push(1); + } + v.push(2); + v +} + +fn _push_then_edit(x: u32) -> Vec { + let mut v = Vec::new(); + v.push(x); + v.push(1); + v[0] = v[1] + 5; + v +} + +fn _cond_push_with_large_start(x: bool) -> Vec { + let mut v = Vec::new(); + v.push(0); + v.push(1); + v.push(0); + v.push(1); + v.push(0); + v.push(0); + v.push(1); + v.push(0); + if x { + v.push(1); + } + v +} diff --git a/tests/ui/vec_init_then_push.stderr b/tests/ui/vec_init_then_push.stderr index 9ec3e10e6247..362c9603bbfb 100644 --- a/tests/ui/vec_init_then_push.stderr +++ b/tests/ui/vec_init_then_push.stderr @@ -3,7 +3,7 @@ error: calls to `push` immediately after creation | LL | / let mut def_err: Vec = Default::default(); LL | | def_err.push(0); - | |____________________^ help: consider using the `vec![]` macro: `let mut def_err: Vec = vec![..];` + | |____________________^ help: consider using the `vec![]` macro: `let def_err: Vec = vec![..];` | = note: `-D clippy::vec-init-then-push` implied by `-D warnings` @@ -30,5 +30,25 @@ LL | / new_err = Vec::new(); LL | | new_err.push(0); | |____________________^ help: consider using the `vec![]` macro: `new_err = vec![..];` -error: aborting due to 4 previous errors +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:67:5 + | +LL | / let mut v = Vec::new(); +LL | | v.push(x); +LL | | v.push(1); + | |______________^ help: consider using the `vec![]` macro: `let mut v = vec![..];` + +error: calls to `push` immediately after creation + --> $DIR/vec_init_then_push.rs:75:5 + | +LL | / let mut v = Vec::new(); +LL | | v.push(0); +LL | | v.push(1); +LL | | v.push(0); +... | +LL | | v.push(1); +LL | | v.push(0); + | |______________^ help: consider using the `vec![]` macro: `let mut v = vec![..];` + +error: aborting due to 6 previous errors From 33bf9e9a54697bcdaba30291f2d4e357ee84777f Mon Sep 17 00:00:00 2001 From: dswij Date: Wed, 23 Feb 2022 23:04:06 +0800 Subject: [PATCH 418/536] `redundant_closure` ignore coerced closure --- clippy_lints/src/eta_reduction.rs | 4 +++- tests/ui/eta.fixed | 16 ++++++++++++++-- tests/ui/eta.rs | 12 ++++++++++++ tests/ui/eta.stderr | 14 +------------- 4 files changed, 30 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index c2e32f1d9a23..1b19868e4c70 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{higher, path_to_local, path_to_local_id}; +use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { let closure_ty = cx.typeck_results().expr_ty(expr); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::Call(callee, args) = body.value.kind; if let ExprKind::Path(_) = callee.kind; if check_inputs(cx, body.params, args); @@ -144,6 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { ); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::MethodCall(path, args, _) = body.value.kind; if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 873ed689a5be..6c2272f4dff9 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -37,7 +37,7 @@ fn main() { } // See #815 - let e = Some(1u8).map(divergent); + let e = Some(1u8).map(|a| divergent(a)); let e = Some(1u8).map(generic); let e = Some(1u8).map(generic); // See #515 @@ -233,7 +233,7 @@ fn late_bound_lifetimes() { { } map_str(|s| take_asref_path(s)); - map_str_to_path(std::convert::AsRef::as_ref); + map_str_to_path(|s| s.as_ref()); } mod type_param_bound { @@ -279,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 4cb58eec94cd..a1a9c0dfbf38 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -279,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index d1ae889d6f36..bf2e97e744ab 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -24,12 +24,6 @@ error: redundant closure LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` -error: redundant closure - --> $DIR/eta.rs:40:27 - | -LL | let e = Some(1u8).map(|a| divergent(a)); - | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent` - error: redundant closure --> $DIR/eta.rs:41:27 | @@ -122,11 +116,5 @@ error: redundant closure LL | Some(1).map(|n| in_loop(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` -error: redundant closure - --> $DIR/eta.rs:236:21 - | -LL | map_str_to_path(|s| s.as_ref()); - | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` - -error: aborting due to 21 previous errors +error: aborting due to 19 previous errors From 5ffe8a1a9049c58501132a07f1567605acff4541 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 26 Apr 2022 06:17:33 +0100 Subject: [PATCH 419/536] errors: `span_suggestion` takes `impl ToString` Change `span_suggestion` (and variants) to take `impl ToString` rather than `String` for the suggested code, as this simplifies the requirements on the diagnostic derive. Signed-off-by: David Wood --- clippy_lints/src/functions/must_use.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 0709580c8adf..5462d913fb44 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -108,7 +108,7 @@ fn check_needless_must_use( diag.span_suggestion( attr.span, "remove the attribute", - "".into(), + "", Applicability::MachineApplicable, ); }, diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 9c734221ebce..4034079a90c0 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -241,7 +241,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { |x| Cow::from(format!("change `{}` to", x)), ) .as_ref(), - suggestion.into(), + suggestion, Applicability::Unspecified, ); } @@ -271,7 +271,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { |x| Cow::from(format!("change `{}` to", x)) ) .as_ref(), - suggestion.into(), + suggestion, Applicability::Unspecified, ); } From 8f8fc9f71752e8df80152a402b992bdd819be7f3 Mon Sep 17 00:00:00 2001 From: asquared31415 <34665709+asquared31415@users.noreply.github.com> Date: Thu, 28 Apr 2022 21:27:32 -0400 Subject: [PATCH 420/536] use non-panicking snippet, use struct update syntax and add comment --- .../src/casts/cast_slice_different_sizes.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 16c9e49ff11d..2238668abca7 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt}; +use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; @@ -39,7 +39,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Opti start_ty.ty, from_size, end_ty.ty, to_size, ), |diag| { - let ptr_snippet = snippet_opt(cx, left_cast.span).unwrap(); + let ptr_snippet = source::snippet(cx, left_cast.span, ".."); let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { Mutability::Mut => ("_mut", "mut"), @@ -119,16 +119,14 @@ fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind { let cast_to = cx.typeck_results().expr_ty(expr); let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; - if let Some(CastChainInfo { - left_cast, - start_ty, - end_ty: _, - }) = expr_cast_chain_tys(cx, cast_expr) - { + + // If the expression that makes up the source of this cast is itself a cast, recursively + // call `expr_cast_chain_tys` and update the end type with the final tartet type. + // Otherwise, this cast is not immediately nested, just construct the info for this cast + if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) { Some(CastChainInfo { - left_cast, - start_ty, end_ty: to_slice_ty, + ..prev_info }) } else { let cast_from = cx.typeck_results().expr_ty(cast_expr); From 9173780568144717da3a4986f67ad375b8fa1ded Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Fri, 29 Apr 2022 19:57:16 -0400 Subject: [PATCH 421/536] Make filters implicit; Update symbols --- util/gh-pages/index.html | 15 ++++++++------- util/gh-pages/script.js | 31 +++++++++++++------------------ 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 552c30f8353f..0a990df96f32 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -109,6 +109,10 @@ Otherwise, have a great day =^.^= right: 0; left: auto; } + + #version-filter-count { + display: none; + } } .label { @@ -297,7 +301,7 @@ Otherwise, have a great day =^.^= #version-filter li label { padding-right: 0; - width: 80%; + width: 40%; } .version-filter-input { @@ -425,7 +429,7 @@ Otherwise, have a great day =^.^=
    diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 469a9306aee5..8fbd3f50d3c3 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -137,13 +137,13 @@ $scope.themes = THEMES_DEFAULT; const DEFAULT_VERSION_FILTERS = { - ">=": { enabled: false, version_str: "" }, - "<=": { enabled: false, version_str: "" }, - "==": { enabled: false, version_str: "" }, + "≥": { enabled: false, version_str: "" }, + "≤": { enabled: false, version_str: "" }, + "=": { enabled: false, version_str: "" }, }; // Weird workaround to get a copy of the object $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); - $scope.version_regex = new RegExp('\\d\.\\d{2}\.\\d'); + $scope.version_regex = new RegExp('1\.\\d{2}\.\\d'); $scope.selectTheme = function (theme) { setTheme(theme, true); @@ -175,16 +175,8 @@ $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); } - $scope.versionSymbol = function() { - const version_filters = $scope.version_filters; - let filter = ">="; - for (const key in version_filters) { - if (version_filters[key]) { - filter = key; - } - } - - return filter; + $scope.versionFilterCount = function(obj) { + return Object.values(obj).filter(x => x.enabled).length; } $scope.byVersion = function(lint) { @@ -214,10 +206,13 @@ let version_str = filters[filter].version_str; // Skip the work for version strings with invalid lengths or characters - if (!filters[filter].enabled || !validate_version_str(version_str)) { + if (!validate_version_str(version_str)) { + filters[filter].enabled = false; continue; } + filters[filter].enabled = true; + let result = cmp_version(lint_version, version_str, filter); if (result && filter === "==") { return true; @@ -226,10 +221,10 @@ } let cmp_filter; - if (filter === ">=") { - cmp_filter = "<="; + if (filter === "≥") { + cmp_filter = "≤"; } else { - cmp_filter = ">="; + cmp_filter = "≥"; } let cmp_version_str = filters[cmp_filter].version_str; From d10296910f36cf51187b1b5183a3a628422dd757 Mon Sep 17 00:00:00 2001 From: Ariel Uy Date: Thu, 28 Apr 2022 20:54:58 -0700 Subject: [PATCH 422/536] Support negative ints in manual_range_contains Fixes issue where ranges containing ints with different signs would be incorrect due to comparing as unsigned. --- clippy_lints/src/ranges.rs | 17 +++++++++++------ clippy_utils/src/consts.rs | 10 ++++------ tests/ui/range_contains.fixed | 8 +++++++- tests/ui/range_contains.rs | 8 +++++++- tests/ui/range_contains.stderr | 14 +++++++++++++- 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index e213c208794c..043299333b12 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -219,15 +219,17 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' _ => return, }; // value, name, order (higher/lower), inclusiveness - if let (Some((lval, lid, name_span, lval_span, lord, linc)), Some((rval, rid, _, rval_span, rord, rinc))) = - (check_range_bounds(cx, l), check_range_bounds(cx, r)) + if let ( + Some((lval, lexpr, lid, name_span, lval_span, lord, linc)), + Some((rval, _, rid, _, rval_span, rord, rinc)), + ) = (check_range_bounds(cx, l), check_range_bounds(cx, r)) { // we only lint comparisons on the same name and with different // direction if lid != rid || lord == rord { return; } - let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval); + let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(lexpr), &lval, &rval); if combine_and && ord == Some(rord) { // order lower bound and upper bound let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less { @@ -292,7 +294,10 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' } } -fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, HirId, Span, Span, Ordering, bool)> { +fn check_range_bounds<'a>( + cx: &'a LateContext<'_>, + ex: &'a Expr<'_>, +) -> Option<(Constant, &'a Expr<'a>, HirId, Span, Span, Ordering, bool)> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), @@ -303,11 +308,11 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, }; if let Some(id) = path_to_local(l) { if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { - return Some((c, id, l.span, r.span, ordering, inclusive)); + return Some((c, r, id, l.span, r.span, ordering, inclusive)); } } else if let Some(id) = path_to_local(r) { if let Some((c, _)) = constant(cx, cx.typeck_results(), l) { - return Some((c, id, r.span, l.span, ordering.reverse(), inclusive)); + return Some((c, l, id, r.span, l.span, ordering.reverse(), inclusive)); } } } diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 0e916ca81642..3d34eaa916ed 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -130,12 +130,10 @@ impl Constant { match (left, right) { (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)), (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)), - (&Self::Int(l), &Self::Int(r)) => { - if let ty::Int(int_ty) = *cmp_type.kind() { - Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))) - } else { - Some(l.cmp(&r)) - } + (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() { + ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))), + ty::Uint(_) => Some(l.cmp(&r)), + _ => bug!("Not an int type"), }, (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r), (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r), diff --git a/tests/ui/range_contains.fixed b/tests/ui/range_contains.fixed index 47c974e614b9..f49771997117 100644 --- a/tests/ui/range_contains.fixed +++ b/tests/ui/range_contains.fixed @@ -6,7 +6,7 @@ #[allow(clippy::short_circuit_statement)] #[allow(clippy::unnecessary_operation)] fn main() { - let x = 9_u32; + let x = 9_i32; // order shouldn't matter (8..12).contains(&x); @@ -43,6 +43,12 @@ fn main() { let y = 3.; (0. ..1.).contains(&y); !(0. ..=1.).contains(&y); + + // handle negatives #8721 + (-10..=10).contains(&x); + x >= 10 && x <= -10; + (-3. ..=3.).contains(&y); + y >= 3. && y <= -3.; } // Fix #6373 diff --git a/tests/ui/range_contains.rs b/tests/ui/range_contains.rs index 835deced5e4c..9e2180b0c994 100644 --- a/tests/ui/range_contains.rs +++ b/tests/ui/range_contains.rs @@ -6,7 +6,7 @@ #[allow(clippy::short_circuit_statement)] #[allow(clippy::unnecessary_operation)] fn main() { - let x = 9_u32; + let x = 9_i32; // order shouldn't matter x >= 8 && x < 12; @@ -43,6 +43,12 @@ fn main() { let y = 3.; y >= 0. && y < 1.; y < 0. || y > 1.; + + // handle negatives #8721 + x >= -10 && x <= 10; + x >= 10 && x <= -10; + y >= -3. && y <= 3.; + y >= 3. && y <= -3.; } // Fix #6373 diff --git a/tests/ui/range_contains.stderr b/tests/ui/range_contains.stderr index bc79f1bca846..1817ee1715d1 100644 --- a/tests/ui/range_contains.stderr +++ b/tests/ui/range_contains.stderr @@ -84,5 +84,17 @@ error: manual `!RangeInclusive::contains` implementation LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` -error: aborting due to 14 previous errors +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:48:5 + | +LL | x >= -10 && x <= 10; + | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` + +error: manual `RangeInclusive::contains` implementation + --> $DIR/range_contains.rs:50:5 + | +LL | y >= -3. && y <= 3.; + | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` + +error: aborting due to 16 previous errors From 8ef4b8d248ba55c52eb6ddc1b8631de98b64564e Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Fri, 29 Apr 2022 19:28:03 -0700 Subject: [PATCH 423/536] Update GitHub Actions actions/checkout@v2 to v3 The v2 implementation uses Node 12, which is end-of-life on April 30, 2022. See https://nodejs.org/en/about/releases/. Update to v3, which is based on Node 16 whose support lasts until April 30, 2024. --- .github/workflows/clippy.yml | 2 +- .github/workflows/clippy_bors.yml | 8 ++++---- .github/workflows/clippy_dev.yml | 2 +- .github/workflows/deploy.yml | 4 ++-- .github/workflows/remark.yml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 4292949a02df..0e27cc927ace 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -37,7 +37,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index f571485e6d38..9b3fd3ddfeb3 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -25,7 +25,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ github.ref }} @@ -88,7 +88,7 @@ jobs: if: matrix.host == 'i686-unknown-linux-gnu' - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -154,7 +154,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -212,7 +212,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 5dfab1d2ebc4..22051093c9cf 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -23,7 +23,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 # Run - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b8be730be32b..71d71d10359e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 56c00544c93a..a179bfa72617 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Setup Node.js uses: actions/setup-node@v1.4.4 From faadd8fd14852957c3f7252420439b10668fe409 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 5 Feb 2022 15:26:49 +0100 Subject: [PATCH 424/536] Box HIR Generics and Impl. --- clippy_lints/src/new_without_default.rs | 2 +- clippy_lints/src/partialeq_ne_impl.rs | 2 +- clippy_lints/src/serde_api.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 9419056be143..96c00c205ff2 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { .. }) = item.kind { - for assoc_item in items { + for assoc_item in *items { if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) { let impl_item = cx.tcx.hir().impl_item(assoc_item.id); if in_external_macro(cx.sess(), impl_item.span) { diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index e827cdaae872..1469cb434c00 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -42,7 +42,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); if trait_ref.path.res.def_id() == eq_trait; then { - for impl_item in impl_items { + for impl_item in *impl_items { if impl_item.ident.name == sym::ne { span_lint_hir( cx, diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs index 398e2c200de3..fc1c2af9257b 100644 --- a/clippy_lints/src/serde_api.rs +++ b/clippy_lints/src/serde_api.rs @@ -36,7 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi { if did == visit_did { let mut seen_str = None; let mut seen_string = None; - for item in items { + for item in *items { match item.ident.as_str() { "visit_str" => seen_str = Some(item.span), "visit_string" => seen_string = Some(item.span), From 67241bb03ca83e132efbce8deffe5ca3438dd529 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Sat, 5 Feb 2022 15:48:02 +0100 Subject: [PATCH 425/536] Inline WhereClause into Generics. --- clippy_lints/src/lifetimes.rs | 8 ++++---- clippy_lints/src/trait_bounds.rs | 9 ++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index b09c23f31e97..4ec7c2362f08 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -8,7 +8,7 @@ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, - TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate, + TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -130,7 +130,7 @@ fn check_fn_inner<'tcx>( span: Span, report_extra_lifetimes: bool, ) { - if span.from_expansion() || has_where_lifetimes(cx, &generics.where_clause) { + if span.from_expansion() || has_where_lifetimes(cx, generics) { return; } @@ -445,8 +445,8 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { /// Are any lifetimes mentioned in the `where` clause? If so, we don't try to /// reason about elision. -fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereClause<'_>) -> bool { - for predicate in where_clause.predicates { +fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool { + for predicate in generics.predicates { match *predicate { WherePredicate::RegionPredicate(..) => return true, WherePredicate::BoundPredicate(ref pred) => { diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 43e0132a7ec7..c388d2854cc2 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -90,10 +90,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { - let Generics { where_clause, .. } = &item.generics; let mut self_bounds_map = FxHashMap::default(); - for predicate in where_clause.predicates { + for predicate in item.generics.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !bound_predicate.span.from_expansion(); @@ -166,7 +165,7 @@ impl TraitBounds { } let mut map: UnhashMap, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; - for bound in gen.where_clause.predicates { + for bound in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref p) = bound; if p.bounds.len() as u64 <= self.max_trait_bounds; @@ -216,7 +215,7 @@ impl TraitBounds { } fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { - if gen.span.from_expansion() || gen.params.is_empty() || gen.where_clause.predicates.is_empty() { + if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() { return; } @@ -232,7 +231,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { } } - for predicate in gen.where_clause.predicates { + for predicate in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !bound_predicate.span.from_expansion(); From e2d923ac3b138bb69511bb1f6212633ca14e61ee Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 7 Feb 2022 22:58:30 +0100 Subject: [PATCH 426/536] Store all generic bounds as where predicates. --- clippy_lints/src/lifetimes.rs | 53 ++++++++++++++------------ clippy_lints/src/trait_bounds.rs | 27 +++++-------- clippy_lints/src/types/borrowed_box.rs | 4 +- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 4ec7c2362f08..662a561f171e 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -139,28 +139,35 @@ fn check_fn_inner<'tcx>( .iter() .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { - for bound in typ.bounds { - let mut visitor = RefVisitor::new(cx); - walk_param_bound(&mut visitor, bound); - if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { - return; + for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { + if pred.in_where_clause { + // has_where_lifetimes checked that this predicate contains no lifetime. + continue; } - if let GenericBound::Trait(ref trait_ref, _) = *bound { - let params = &trait_ref - .trait_ref - .path - .segments - .last() - .expect("a path must have at least one segment") - .args; - if let Some(params) = *params { - let lifetimes = params.args.iter().filter_map(|arg| match arg { - GenericArg::Lifetime(lt) => Some(lt), - _ => None, - }); - for bound in lifetimes { - if bound.name != LifetimeName::Static && !bound.is_elided() { - return; + + for bound in pred.bounds { + let mut visitor = RefVisitor::new(cx); + walk_param_bound(&mut visitor, bound); + if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) { + return; + } + if let GenericBound::Trait(ref trait_ref, _) = *bound { + let params = &trait_ref + .trait_ref + .path + .segments + .last() + .expect("a path must have at least one segment") + .args; + if let Some(params) = *params { + let lifetimes = params.args.iter().filter_map(|arg| match arg { + GenericArg::Lifetime(lt) => Some(lt), + _ => None, + }); + for bound in lifetimes { + if bound.name != LifetimeName::Static && !bound.is_elided() { + return; + } } } } @@ -322,9 +329,7 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet { let mut allowed_lts = FxHashSet::default(); for par in named_generics.iter() { if let GenericParamKind::Lifetime { .. } = par.kind { - if par.bounds.is_empty() { - allowed_lts.insert(RefLt::Named(par.name.ident().name)); - } + allowed_lts.insert(RefLt::Named(par.name.ident().name)); } } allowed_lts.insert(RefLt::Unnamed); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index c388d2854cc2..3d1b2ee925bc 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -8,8 +8,7 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind, - WherePredicate, + GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -219,30 +218,19 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { return; } - let mut map = FxHashMap::default(); - for param in gen.params { - if let ParamName::Plain(ref ident) = param.name { - let res = param - .bounds - .iter() - .filter_map(get_trait_info_from_bound) - .collect::>(); - map.insert(*ident, res); - } - } - + let mut map = FxHashMap::<_, Vec<_>>::default(); for predicate in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; if !bound_predicate.span.from_expansion(); if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); - if let Some(trait_resolutions_direct) = map.get(&segment.ident); then { - for (res_where, _, _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { - if let Some((_, _, span_direct)) = trait_resolutions_direct + for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) { + let trait_resolutions_direct = map.entry(segment.ident).or_default(); + if let Some((_, span_direct)) = trait_resolutions_direct .iter() - .find(|(res_direct, _, _)| *res_direct == res_where) { + .find(|(res_direct, _)| *res_direct == res_where) { span_lint_and_help( cx, TRAIT_DUPLICATION_IN_BOUNDS, @@ -252,6 +240,9 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { "consider removing this trait bound", ); } + else { + trait_resolutions_direct.push((res_where, span_where)) + } } } } diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs index 7c06906293b1..f35f44eda567 100644 --- a/clippy_lints/src/types/borrowed_box.rs +++ b/clippy_lints/src/types/borrowed_box.rs @@ -104,8 +104,10 @@ fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did); if let GenericParamKind::Type { synthetic, .. } = generic_param.kind; if synthetic; + if let Some(generics) = cx.tcx.hir().get_generics(id.owner); + if let Some(pred) = generics.bounds_for_param(did.expect_local()).next(); then { - Some(generic_param.bounds) + Some(pred.bounds) } else { None } From e46c782662f1e9fcaa7978810d19d1cb1d342f01 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Mon, 14 Mar 2022 15:56:37 +0100 Subject: [PATCH 427/536] Bless tests. --- tests/ui/extra_unused_lifetimes.stderr | 8 +------- tests/ui/needless_lifetimes.stderr | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index ebdb8e749520..9143fb2c208a 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -6,12 +6,6 @@ LL | fn unused_lt<'a>(x: u8) {} | = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings` -error: this lifetime isn't used in the function definition - --> $DIR/extra_unused_lifetimes.rs:16:25 - | -LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) { - | ^^ - error: this lifetime isn't used in the function definition --> $DIR/extra_unused_lifetimes.rs:41:10 | @@ -24,5 +18,5 @@ error: this lifetime isn't used in the function definition LL | fn unused_lt<'a>(x: u8) {} | ^^ -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/needless_lifetimes.stderr b/tests/ui/needless_lifetimes.stderr index ffa152427a97..a488bc01fffa 100644 --- a/tests/ui/needless_lifetimes.stderr +++ b/tests/ui/needless_lifetimes.stderr @@ -108,12 +108,6 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) - --> $DIR/needless_lifetimes.rs:307:5 - | -LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration) --> $DIR/needless_lifetimes.rs:310:5 | @@ -192,5 +186,5 @@ error: explicit lifetimes given in parameter types where they could be elided (o LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 32 previous errors +error: aborting due to 31 previous errors From 959ed524cef3dde4bf006d3f363d9f8cf6f0005e Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 30 Apr 2022 19:16:19 +0200 Subject: [PATCH 428/536] Add missing quite in `large_include_file` example --- clippy_lints/src/large_include_file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs index 5ed275726c53..8bef13c682db 100644 --- a/clippy_lints/src/large_include_file.rs +++ b/clippy_lints/src/large_include_file.rs @@ -19,7 +19,7 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// let included_str = include_str!("very_large_file.txt"); - /// let included_bytes = include_bytes!("very_large_file.txt); + /// let included_bytes = include_bytes!("very_large_file.txt"); /// ``` /// /// Instead, you can load the file at runtime: From defc537a2e7dc469d6fc1429854a24ca2914469f Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 24 Apr 2022 19:08:23 -0700 Subject: [PATCH 429/536] Fix the clippy build --- clippy_utils/src/sugg.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 1fc9979f3dd7..794d2e1026f8 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -214,6 +214,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Path(..) | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..) + | ast::ExprKind::Yeet(..) | ast::ExprKind::Struct(..) | ast::ExprKind::Try(..) | ast::ExprKind::TryBlock(..) From 6ad810f94eec6b49af026d44c70066284120612d Mon Sep 17 00:00:00 2001 From: tamaron Date: Fri, 22 Apr 2022 10:39:38 +0900 Subject: [PATCH 430/536] improve identity_op fix Update identity_op.rs ok update without_loop_counters test chore fix chore remove visitor and leftmost fix add document --- clippy_lints/src/identity_op.rs | 82 ++++++++++++++++++++--------- tests/ui/identity_op.rs | 30 +++++++++++ tests/ui/identity_op.stderr | 92 ++++++++++++++++++++++++++++++++- 3 files changed, 180 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 4d6bef89bea7..40cc5cd4bcf9 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,3 +1,4 @@ +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -22,6 +23,11 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` + /// + /// ### Known problems + /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to + /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. + /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -31,36 +37,66 @@ declare_clippy_lint! { declare_lint_pass!(IdentityOp => [IDENTITY_OP]); impl<'tcx> LateLintPass<'tcx> for IdentityOp { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { return; } - if let ExprKind::Binary(cmp, left, right) = e.kind { - if is_allowed(cx, cmp, left, right) { - return; - } - match cmp.node { - BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check(cx, left, 0, e.span, right.span); - check(cx, right, 0, e.span, left.span); - }, - BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span), - BinOpKind::Mul => { - check(cx, left, 1, e.span, right.span); - check(cx, right, 1, e.span, left.span); - }, - BinOpKind::Div => check(cx, right, 1, e.span, left.span), - BinOpKind::BitAnd => { - check(cx, left, -1, e.span, right.span); - check(cx, right, -1, e.span, left.span); - }, - BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span), - _ => (), + if let ExprKind::Binary(cmp, left, right) = &expr.kind { + if !is_allowed(cx, *cmp, left, right) { + match cmp.node { + BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 0, expr.span, right.span); + } + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Mul => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 1, expr.span, right.span); + } + check(cx, right, 1, expr.span, left.span); + }, + BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::BitAnd => { + if reducible_to_right(cx, expr, right) { + check(cx, left, -1, expr.span, right.span); + } + check(cx, right, -1, expr.span, left.span); + }, + BinOpKind::Rem => { + // Don't call reducible_to_right because N % N is always reducible to 1 + check_remainder(cx, left, right, expr.span, left.span); + }, + _ => (), + } } } } } +/// Checks if `left op ..right` can be actually reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// See #8724 +fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { + if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { + is_toplevel_binary(cx, binary) + } else { + true + } +} + +fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { + false + } else { + true + } +} + fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { // This lint applies to integers !cx.typeck_results().expr_ty(left).peel_refs().is_integral() diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index 9d39d7696354..fec54d00ccb4 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -77,4 +77,34 @@ fn main() { (x + 1) % 3; // no error 4 % 3; // no error 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + 0 + if b { 1 } else { 2 }; + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + match a { 0 => 10, _ => 20 }; + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error + + 0 + if b { 0 + 1 } else { 2 }; + 0 + match a { 0 => 0 + 10, _ => 20 }; + 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + + let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + + 0 + { a } + 3; // no error + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error + + fn f(_: i32) { + todo!(); + } + f(1 * a + { 8 * 5 }); + f(0 + if b { 1 } else { 2 } + 3); // no error + const _: i32 = { 2 * 4 } + 0 + 3; + const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index e3c156b54291..d8cb65839cbb 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -108,5 +108,95 @@ error: the operation is ineffective. Consider reducing it to `1` LL | x + 1 % 3; | ^^^^^ -error: aborting due to 18 previous errors +error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` + --> $DIR/identity_op.rs:84:5 + | +LL | 0 + if b { 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` + --> $DIR/identity_op.rs:86:5 + | +LL | 0 + match a { 0 => 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` + --> $DIR/identity_op.rs:91:5 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:91:16 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` + --> $DIR/identity_op.rs:92:5 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `10` + --> $DIR/identity_op.rs:92:25 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:93:16 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `30` + --> $DIR/identity_op.rs:93:52 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:20 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:52 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:23 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:58 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` + --> $DIR/identity_op.rs:98:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `a` + --> $DIR/identity_op.rs:106:7 + | +LL | f(1 * a + { 8 * 5 }); + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` + --> $DIR/identity_op.rs:108:20 + | +LL | const _: i32 = { 2 * 4 } + 0 + 3; + | ^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors From 8172166f34fe5077b3873a6254d379b2282e1530 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 25 Apr 2022 22:08:45 +0300 Subject: [PATCH 431/536] rustc: Panic by default in `DefIdTree::parent` Only crate root def-ids don't have a parent, and in majority of cases the argument of `DefIdTree::parent` cannot be a crate root. So we now panic by default in `parent` and introduce a new non-panicing function `opt_parent` for cases where the argument can be a crate root. Same applies to `local_parent`/`opt_local_parent`. --- clippy_lints/src/matches/redundant_pattern_match.rs | 2 +- clippy_lints/src/methods/bind_instead_of_map.rs | 8 ++++---- clippy_lints/src/methods/chars_cmp.rs | 2 +- clippy_lints/src/methods/option_map_or_none.rs | 2 +- clippy_lints/src/missing_doc.rs | 2 +- clippy_utils/src/lib.rs | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 777ec9b75bc2..aa3552001f46 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -193,7 +193,7 @@ fn find_sugg_for_if_let<'tcx>( PatKind::TupleStruct(ref qpath, [sub_pat], _) => { if let PatKind::Wild = sub_pat.kind { let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id); - let Some(id) = res.opt_def_id().and_then(|ctor_id| cx.tcx.parent(ctor_id)) else { return }; + let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return }; let lang_items = cx.tcx.lang_items(); if Some(id) == lang_items.result_ok_variant() { ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty)) diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs index eec232e6d098..b88ec0963f2b 100644 --- a/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/clippy_lints/src/methods/bind_instead_of_map.rs @@ -42,7 +42,7 @@ pub(crate) trait BindInsteadOfMap { fn no_op_msg(cx: &LateContext<'_>) -> Option { let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; - let item_id = cx.tcx.parent(variant_id)?; + let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}({})`, which is a no-op", cx.tcx.item_name(item_id), @@ -53,7 +53,7 @@ pub(crate) trait BindInsteadOfMap { fn lint_msg(cx: &LateContext<'_>) -> Option { let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?; - let item_id = cx.tcx.parent(variant_id)?; + let item_id = cx.tcx.parent(variant_id); Some(format!( "using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`", cx.tcx.item_name(item_id), @@ -145,7 +145,7 @@ pub(crate) trait BindInsteadOfMap { if_chain! { if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def(); if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM); - if Some(adt.did()) == cx.tcx.parent(vid); + if adt.did() == cx.tcx.parent(vid); then {} else { return false; } } @@ -182,7 +182,7 @@ pub(crate) trait BindInsteadOfMap { fn is_variant(cx: &LateContext<'_>, res: Res) -> bool { if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res { if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) { - return cx.tcx.parent(id) == Some(variant_id); + return cx.tcx.parent(id) == variant_id; } } false diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs index 2cf2c5641bf1..f7b79f0839ba 100644 --- a/clippy_lints/src/methods/chars_cmp.rs +++ b/clippy_lints/src/methods/chars_cmp.rs @@ -19,7 +19,7 @@ pub(super) fn check( if_chain! { if let Some(args) = method_chain_args(info.chain, chain_methods); if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind; - if let Some(id) = path_def_id(cx, fun).and_then(|ctor_id| cx.tcx.parent(ctor_id)); + if let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id)); if Some(id) == cx.tcx.lang_items().option_some_variant(); then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs index 2a5ab6e625c1..76bc9466ed81 100644 --- a/clippy_lints/src/methods/option_map_or_none.rs +++ b/clippy_lints/src/methods/option_map_or_none.rs @@ -75,7 +75,7 @@ pub(super) fn check<'tcx>( let arg_snippet = snippet(cx, span, ".."); let body = cx.tcx.hir().body(id); if let Some((func, [arg_char])) = reduce_unit_expression(&body.value); - if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id)); + if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id)); if Some(id) == cx.tcx.lang_items().option_some_variant(); then { let func_snippet = snippet(cx, arg_char.span, ".."); diff --git a/clippy_lints/src/missing_doc.rs b/clippy_lints/src/missing_doc.rs index 5816a95dcebf..a20377f320b2 100644 --- a/clippy_lints/src/missing_doc.rs +++ b/clippy_lints/src/missing_doc.rs @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { hir::ItemKind::Fn(..) => { // ignore main() if it.ident.name == sym::main { - let at_root = cx.tcx.local_parent(it.def_id) == Some(CRATE_DEF_ID); + let at_root = cx.tcx.local_parent(it.def_id) == CRATE_DEF_ID; if at_root { return; } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 74978720424d..7d46952d9718 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -235,7 +235,7 @@ pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem if let QPath::Resolved(_, path) = qpath { if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res { if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) { - return cx.tcx.parent(ctor_id) == Some(item_id); + return cx.tcx.parent(ctor_id) == item_id; } } } From 6ff77b96f11143cf63f3522e4ffee4568b72330c Mon Sep 17 00:00:00 2001 From: Dan Gohman Date: Mon, 2 May 2022 13:46:29 -0700 Subject: [PATCH 432/536] Fix `cast_lossless` to avoid warning on `usize` to `f64` conversion. Previously, the `cast_lossless` lint would issue a warning on code that converted a `usize` value to `f64`, on 32-bit targets. `usize` to `f64` is a lossless cast on 32-bit targets, however there is no corresponding `f64::from` that takes a `usize`, so `cast_lossless`'s suggested replacement does not compile. This PR disables the lint in the case of casting from `usize` or `isize`. Fixes #3689. changelog: [`cast_lossless`] no longer gives wrong suggestion on usize->f64 --- clippy_lints/src/casts/cast_lossless.rs | 2 +- tests/ui/cast_size_32bit.stderr | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 4a95bed1148d..7717c1e9e31d 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -93,7 +93,7 @@ fn should_lint( } else { 64 }; - from_nbits < to_nbits + !is_isize_or_usize(cast_from) && from_nbits < to_nbits }, (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv.as_ref(), &msrvs::FROM_BOOL) => true, (_, _) => { diff --git a/tests/ui/cast_size_32bit.stderr b/tests/ui/cast_size_32bit.stderr index 140676a5ffcf..7125f741c150 100644 --- a/tests/ui/cast_size_32bit.stderr +++ b/tests/ui/cast_size_32bit.stderr @@ -14,26 +14,12 @@ LL | x0 as f64; | = note: `-D clippy::cast-precision-loss` implied by `-D warnings` -error: casting `isize` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_size_32bit.rs:15:5 - | -LL | x0 as f64; - | ^^^^^^^^^ help: try: `f64::from(x0)` - | - = note: `-D clippy::cast-lossless` implied by `-D warnings` - error: casting `usize` to `f64` causes a loss of precision on targets with 64-bit wide pointers (`usize` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide) --> $DIR/cast_size_32bit.rs:16:5 | LL | x1 as f64; | ^^^^^^^^^ -error: casting `usize` to `f64` may become silently lossy if you later change the type - --> $DIR/cast_size_32bit.rs:16:5 - | -LL | x1 as f64; - | ^^^^^^^^^ help: try: `f64::from(x1)` - error: casting `isize` to `f32` causes a loss of precision (`isize` is 32 or 64 bits wide, but `f32`'s mantissa is only 23 bits wide) --> $DIR/cast_size_32bit.rs:17:5 | From 34760560ed8b1462b5dd0604fee5bd8dc071a6eb Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 29 Apr 2022 18:48:58 +0200 Subject: [PATCH 433/536] Make rustc_parse_format compile on stable This allows it to be used by lightweight formatting systems and may allow it to be used by rust-analyzer. --- clippy_lints/src/write.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index f3d818cc3485..54b93a20a057 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -13,7 +13,7 @@ use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_parse::parser; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, Symbol}; -use rustc_span::{sym, BytePos, Span, DUMMY_SP}; +use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP}; declare_clippy_lint! { /// ### What it does @@ -454,6 +454,7 @@ impl SimpleFormatArgs { } }, ArgumentNamed(n, _) => { + let n = Symbol::intern(n); if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) { match x.1.as_slice() { // A non-empty format string has been seen already. @@ -495,7 +496,7 @@ impl Write { let span = parser .arg_places .last() - .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x)); + .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end))); if !self.in_debug_impl && arg.format.ty == "?" { // FIXME: modify rustc's fmt string parser to give us the current span From f505cc9f7265eee332984f30516852e24d6e8385 Mon Sep 17 00:00:00 2001 From: binggh Date: Tue, 3 May 2022 17:51:23 +0800 Subject: [PATCH 434/536] Easier readability for needless_late_init --- clippy_lints/src/needless_late_init.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index a863a7990ca8..b70871b38bea 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -3,7 +3,7 @@ use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; -use rustc_errors::Applicability; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::Visitor; use rustc_hir::{ BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, @@ -267,11 +267,14 @@ fn check<'tcx>( match usage.expr.kind { ExprKind::Assign(..) => { let assign = LocalAssign::new(cx, usage.expr, binding_id)?; + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); span_lint_and_then( cx, NEEDLESS_LATE_INIT, - local_stmt.span, + msg_span, "unneeded late initialization", |diag| { diag.tool_only_span_suggestion( @@ -365,7 +368,6 @@ fn check<'tcx>( impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); - if_chain! { if let Local { init: None, From 7017eb102d71526205b2feded779ff1f26d11da1 Mon Sep 17 00:00:00 2001 From: binggh Date: Tue, 3 May 2022 17:51:34 +0800 Subject: [PATCH 435/536] cargo dev bless --- tests/ui/needless_late_init.stderr | 15 ++++++++++++--- tests/ui/needless_late_init_fixable.stderr | 22 +++++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index 015c173561ac..d33a117b288c 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -123,7 +123,10 @@ error: unneeded late initialization --> $DIR/needless_late_init.rs:60:5 | LL | let x; - | ^^^^^^ + | ^^^^^^ created here +LL | let y = SignificantDrop; +LL | x = 1; + | ^^^^^ initialised here | help: declare `x` here | @@ -134,7 +137,10 @@ error: unneeded late initialization --> $DIR/needless_late_init.rs:64:5 | LL | let x; - | ^^^^^^ + | ^^^^^^ created here +LL | let y = 1; +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `x` here | @@ -145,7 +151,10 @@ error: unneeded late initialization --> $DIR/needless_late_init.rs:68:5 | LL | let x; - | ^^^^^^ + | ^^^^^^ created here +... +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `x` here | diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr index c97cd93fd2fe..8c664309e3e8 100644 --- a/tests/ui/needless_late_init_fixable.stderr +++ b/tests/ui/needless_late_init_fixable.stderr @@ -2,7 +2,9 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:6:5 | LL | let a; - | ^^^^^^ + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here | = note: `-D clippy::needless-late-init` implied by `-D warnings` help: declare `a` here @@ -14,7 +16,10 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:9:5 | LL | let b; - | ^^^^^^ + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here | help: declare `b` here | @@ -25,7 +30,10 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:10:5 | LL | let c; - | ^^^^^^ + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here | help: declare `c` here | @@ -36,7 +44,9 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:14:5 | LL | let d: usize; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here | help: declare `d` here | @@ -47,7 +57,9 @@ error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:17:5 | LL | let e; - | ^^^^^^ + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `e` here | From d53293d52ac69d238b86a8ec8690e446e7ec789a Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Tue, 3 May 2022 13:39:38 +0100 Subject: [PATCH 436/536] Ignore type aliases in `init_numbered_fields` --- clippy_lints/src/init_numbered_fields.rs | 2 ++ tests/ui/numbered_fields.fixed | 5 +++++ tests/ui/numbered_fields.rs | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index 9284e0024099..7e1548531f10 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -49,6 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) + && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) { let expr_spans = fields .iter() diff --git a/tests/ui/numbered_fields.fixed b/tests/ui/numbered_fields.fixed index 1da97e968798..3710b3e9c81e 100644 --- a/tests/ui/numbered_fields.fixed +++ b/tests/ui/numbered_fields.fixed @@ -30,4 +30,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } diff --git a/tests/ui/numbered_fields.rs b/tests/ui/numbered_fields.rs index 08ec405a5606..2af84bc0642a 100644 --- a/tests/ui/numbered_fields.rs +++ b/tests/ui/numbered_fields.rs @@ -38,4 +38,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } From 1834f326c4a2b732c7c62bf7b00efca8af3b290a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Tue, 3 May 2022 16:41:12 +0100 Subject: [PATCH 437/536] Move only_used_in_recursion to nursery --- clippy_lints/src/lib.register_all.rs | 1 - clippy_lints/src/lib.register_complexity.rs | 1 - clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/only_used_in_recursion.rs | 9 ++++++++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 1cccfdb24c27..325e74a17788 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -248,7 +248,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index df646ff5c5fe..6f3c433af31a 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -68,7 +68,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(no_effect::NO_EFFECT), LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(precedence::PRECEDENCE), diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index c2fc67afba51..18904a945389 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -20,6 +20,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), + LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index f946fc111928..beb812793f81 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lint_allowed; use itertools::{izip, Itertools}; use rustc_ast::{walk_list, Label, Mutability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -33,6 +34,9 @@ declare_clippy_lint! { /// and the assigned variables are also only in recursion, it is useless. /// /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// /// In some cases, this would not catch all useless arguments. /// /// ```rust @@ -85,7 +89,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.60.0"] pub ONLY_USED_IN_RECURSION, - complexity, + nursery, "arguments that is only used in recursion can be removed" } declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); @@ -100,6 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { _: Span, id: HirId, ) { + if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) { + return; + } if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind { let def_id = id.owner.to_def_id(); let data = cx.tcx.def_path(def_id).data; From 2c70af3cd59504a79359808d39843132f47c9e32 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Tue, 3 May 2022 18:12:31 -0400 Subject: [PATCH 438/536] Only require minor version --- util/gh-pages/index.html | 25 ++++++++++++++++--- util/gh-pages/script.js | 53 ++++++++++++++++++++-------------------- 2 files changed, 47 insertions(+), 31 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 0a990df96f32..69dcd947dfbd 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -301,11 +301,15 @@ Otherwise, have a great day =^.^= #version-filter li label { padding-right: 0; - width: 40%; + width: 35%; } .version-filter-input { - height: 75%; + height: 60%; + width: 30%; + text-align: center; + border: none; + border-bottom: 1px solid #000000; } #filter-label, .filter-clear { @@ -429,7 +433,11 @@ Otherwise, have a great day =^.^=
    +
    diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 8fbd3f50d3c3..b04a93745749 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -137,13 +137,12 @@ $scope.themes = THEMES_DEFAULT; const DEFAULT_VERSION_FILTERS = { - "≥": { enabled: false, version_str: "" }, - "≤": { enabled: false, version_str: "" }, - "=": { enabled: false, version_str: "" }, + "≥": { enabled: false, minor_version: "" }, + "≤": { enabled: false, minor_version: "" }, + "=": { enabled: false, minor_version: "" }, }; // Weird workaround to get a copy of the object $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); - $scope.version_regex = new RegExp('1\.\\d{2}\.\\d'); $scope.selectTheme = function (theme) { setTheme(theme, true); @@ -181,42 +180,42 @@ $scope.byVersion = function(lint) { function validate_version_str(ver) { - return ver.length === 6 && $scope.version_regex.test(ver); - } - - function cmp_version(ver1, ver2, filter) { - // < 0: lint_version < version - // 0: equal - // > 0: lint_version > version - let result = ver1.localeCompare(ver2, undefined, { - numeric: true, - sensitivity: "base" - }); - - // "==" gets the highest priority, since all filters are inclusive - return (result === 0) || (filter === ">=" && result > 0) || (filter === "<=" && result < 0); + return ver.length === 2 && !isNaN(ver); } let filters = $scope.version_filters; // Strip the "pre " prefix for pre 1.29.0 lints let lint_version = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; + let lint_minor_verison = lint_version.substring(2, 4); for (const filter in filters) { - let version_str = filters[filter].version_str; + let minor_version = filters[filter].minor_version; // Skip the work for version strings with invalid lengths or characters - if (!validate_version_str(version_str)) { + if (!validate_version_str(minor_version)) { filters[filter].enabled = false; continue; } filters[filter].enabled = true; - let result = cmp_version(lint_version, version_str, filter); - if (result && filter === "==") { - return true; - } else if (!result) { + let result; + switch (filter) { + case "≥": + result = (lint_minor_verison >= minor_version); + break; + case "≤": + result = (lint_minor_verison <= minor_version); + break; + // "=" gets the highest priority, since all filters are inclusive + case "=": + return (lint_minor_verison === minor_version); + default: + return true + } + + if (!result) { return false; } @@ -227,12 +226,12 @@ cmp_filter = "≥"; } - let cmp_version_str = filters[cmp_filter].version_str; - if (!filters[cmp_filter].enabled || !validate_version_str(cmp_version_str)) { + let cmp_minor_version = filters[cmp_filter].minor_version; + if (!filters[cmp_filter].enabled || !validate_version_str(cmp_minor_version)) { return true; } - return cmp_version(lint_version, cmp_version_str, cmp_filter); + return (cmp_filter === "≥") ? (lint_minor_verison > minor_version) : (lint_minor_verison < minor_version); } return true; From fea177fafe44c6ea950b71d812102167dfefd6b6 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Tue, 22 Mar 2022 21:15:09 +0100 Subject: [PATCH 439/536] add trim_split_whitespace --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/lib.rs | 1 + clippy_lints/src/strings.rs | 56 ++++++++++++++++ tests/ui/trim_split_whitespace.fixed | 91 ++++++++++++++++++++++++++ tests/ui/trim_split_whitespace.rs | 91 ++++++++++++++++++++++++++ tests/ui/trim_split_whitespace.stderr | 52 +++++++++++++++ 9 files changed, 295 insertions(+) create mode 100644 tests/ui/trim_split_whitespace.fixed create mode 100644 tests/ui/trim_split_whitespace.rs create mode 100644 tests/ui/trim_split_whitespace.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index c88ead99fa6f..751f9fccd88d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3736,6 +3736,7 @@ Released 2018-09-13 [`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr [`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts [`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace [`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 1cccfdb24c27..a49f8656ba3d 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -281,6 +281,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index e36243160279..5768edc50188 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -482,6 +482,7 @@ store.register_lints(&[ strings::STRING_SLICE, strings::STRING_TO_STRING, strings::STR_TO_STRING, + strings::TRIM_SPLIT_WHITESPACE, strlen_on_c_strings::STRLEN_ON_C_STRINGS, suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 5a85a9e209eb..d183c0449cd5 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -105,6 +105,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(returns::NEEDLESS_RETURN), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), LintId::of(unit_types::LET_UNIT_VALUE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 57d48e176270..caf48194ad47 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -887,6 +887,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); let max_include_file_size = conf.max_include_file_size; store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); + store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3573f632a367..7c196ccaa8cc 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -5,6 +5,7 @@ use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -451,3 +452,58 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { } } } + +declare_clippy_lint! { + /// ### What it does + /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`. + /// + /// ### Why is this bad? + /// `split_whitespace` already ignores leading and trailing whitespace. + /// + /// ### Example + /// ```rust + /// " A B C ".trim().split_whitespace(); + /// ``` + /// Use instead: + /// ```rust + /// " A B C ".split_whitespace(); + /// ``` + #[clippy::version = "1.62.0"] + pub TRIM_SPLIT_WHITESPACE, + style, + "using `str::trim()` or alike before `str::split_whitespace`" +} +declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]); + +impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + let tyckres = cx.typeck_results(); + if_chain! { + if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind; + if path.ident.name == sym!(split_whitespace); + if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id); + if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind; + if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str(); + if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id); + if is_one_of_trim_diagnostic_items(cx, trim_def_id); + then { + span_lint_and_sugg( + cx, + TRIM_SPLIT_WHITESPACE, + trim_span.with_hi(split_ws_span.lo()), + &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name), + &format!("remove `{}()`", trim_fn_name), + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool { + cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id) +} diff --git a/tests/ui/trim_split_whitespace.fixed b/tests/ui/trim_split_whitespace.fixed new file mode 100644 index 000000000000..e4d352f7367e --- /dev/null +++ b/tests/ui/trim_split_whitespace.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref + let s = DerefStr(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + + // Deref + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/tests/ui/trim_split_whitespace.rs b/tests/ui/trim_split_whitespace.rs new file mode 100644 index 000000000000..f98451a98371 --- /dev/null +++ b/tests/ui/trim_split_whitespace.rs @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".trim().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref + let s = DerefStr(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + + // Deref + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/tests/ui/trim_split_whitespace.stderr b/tests/ui/trim_split_whitespace.stderr new file mode 100644 index 000000000000..5ae7849e27d2 --- /dev/null +++ b/tests/ui/trim_split_whitespace.stderr @@ -0,0 +1,52 @@ +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:62:23 + | +LL | let _ = " A B C ".trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + | + = note: `-D clippy::trim-split-whitespace` implied by `-D warnings` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:63:23 + | +LL | let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:64:23 + | +LL | let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:67:37 + | +LL | let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:68:37 + | +LL | let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:69:37 + | +LL | let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:76:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:84:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: aborting due to 8 previous errors + From ee8fae3f5d4aac2bcbea8936e39d3027aade8e32 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Wed, 4 May 2022 18:13:06 +0100 Subject: [PATCH 440/536] identity_op: add parenthesis to suggestions where required --- clippy_lints/src/identity_op.rs | 127 +++++++------ tests/ui/identity_op.fixed | 119 +++++++++++++ tests/ui/identity_op.rs | 59 ++++--- tests/ui/identity_op.stderr | 304 ++++++++++++++++++-------------- tests/ui/modulo_one.rs | 2 +- tests/ui/modulo_one.stderr | 10 +- 6 files changed, 398 insertions(+), 223 deletions(-) create mode 100644 tests/ui/identity_op.fixed diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 40cc5cd4bcf9..419ea5a6811b 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,15 +1,14 @@ -use clippy_utils::get_parent_expr; -use clippy_utils::source::snippet; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; +use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{clip, unsext}; +use rustc_errors::Applicability; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; -use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::{clip, unsext}; - declare_clippy_lint! { /// ### What it does /// Checks for identity operations, e.g., `x + 0`. @@ -23,11 +22,6 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` - /// - /// ### Known problems - /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to - /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. - /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -45,31 +39,22 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { if !is_allowed(cx, *cmp, left, right) { match cmp.node { BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - if reducible_to_right(cx, expr, right) { - check(cx, left, 0, expr.span, right.span); - } - check(cx, right, 0, expr.span, left.span); + check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, 0, expr.span, left.span, Parens::Unneeded); }, BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { - check(cx, right, 0, expr.span, left.span); + check(cx, right, 0, expr.span, left.span, Parens::Unneeded); }, BinOpKind::Mul => { - if reducible_to_right(cx, expr, right) { - check(cx, left, 1, expr.span, right.span); - } - check(cx, right, 1, expr.span, left.span); + check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, 1, expr.span, left.span, Parens::Unneeded); }, - BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded), BinOpKind::BitAnd => { - if reducible_to_right(cx, expr, right) { - check(cx, left, -1, expr.span, right.span); - } - check(cx, right, -1, expr.span, left.span); - }, - BinOpKind::Rem => { - // Don't call reducible_to_right because N % N is always reducible to 1 - check_remainder(cx, left, right, expr.span, left.span); + check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right)); + check(cx, right, -1, expr.span, left.span, Parens::Unneeded); }, + BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span), _ => (), } } @@ -77,24 +62,50 @@ impl<'tcx> LateLintPass<'tcx> for IdentityOp { } } -/// Checks if `left op ..right` can be actually reduced to `right` -/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` -/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` -/// See #8724 -fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { - if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { - is_toplevel_binary(cx, binary) - } else { - true - } +#[derive(Copy, Clone)] +enum Parens { + Needed, + Unneeded, } -fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { - if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { - false - } else { - true +/// Checks if `left op right` needs parenthesis when reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced +/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be +/// interpreted as a statement +/// +/// See #8724 +fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens { + match right.kind { + ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => { + // ensure we're checking against the leftmost expression of `right` + // + // ~~~ `lhs` + // 0 + {4} * 2 + // ~~~~~~~ `right` + return needs_parenthesis(cx, binary, lhs); + }, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {}, + _ => return Parens::Unneeded, } + + let mut prev_id = binary.hir_id; + for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) { + if let Node::Expr(expr) = node + && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind + && lhs.hir_id == prev_id + { + // keep going until we find a node that encompasses left of `binary` + prev_id = expr.hir_id; + continue; + } + + match node { + Node::Block(_) | Node::Stmt(_) => break, + _ => return Parens::Unneeded, + }; + } + + Parens::Needed } fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { @@ -115,11 +126,11 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv, _ => return, } { - span_ineffective_operation(cx, span, arg); + span_ineffective_operation(cx, span, arg, Parens::Unneeded); } } -fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { +fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) { if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) { let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() { ty::Int(ity) => unsext(cx.tcx, -1_i128, ity), @@ -132,19 +143,27 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) { 1 => v == 1, _ => unreachable!(), } { - span_ineffective_operation(cx, span, arg); + span_ineffective_operation(cx, span, arg, parens); } } } -fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) { - span_lint( +fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) { + let mut applicability = Applicability::MachineApplicable; + let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability); + + let suggestion = match parens { + Parens::Needed => format!("({expr_snippet})"), + Parens::Unneeded => expr_snippet.into_owned(), + }; + + span_lint_and_sugg( cx, IDENTITY_OP, span, - &format!( - "the operation is ineffective. Consider reducing it to `{}`", - snippet(cx, arg, "..") - ), + "this operation has no effect", + "consider reducing it to", + suggestion, + applicability, ); } diff --git a/tests/ui/identity_op.fixed b/tests/ui/identity_op.fixed new file mode 100644 index 000000000000..5f9cebe212ab --- /dev/null +++ b/tests/ui/identity_op.fixed @@ -0,0 +1,119 @@ +// run-rustfix + +#![warn(clippy::identity_op)] +#![allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::double_parens, + unused +)] + +use std::fmt::Write as _; + +const ONE: i64 = 1; +const NEG_ONE: i64 = -1; +const ZERO: i64 = 0; + +struct A(String); + +impl std::ops::Shl for A { + type Output = A; + fn shl(mut self, other: i32) -> Self { + let _ = write!(self.0, "{}", other); + self + } +} + +struct Length(u8); +struct Meter; + +impl core::ops::Mul for u8 { + type Output = Length; + fn mul(self, _: Meter) -> Length { + Length(self) + } +} + +#[rustfmt::skip] +fn main() { + let x = 0; + + x; + x; + x + 1; + x; + 1 + x; + x - ZERO; //no error, as we skip lookups (for now) + x; + ((ZERO)) | x; //no error, as we skip lookups (for now) + + x; + x; + x / ONE; //no error, as we skip lookups (for now) + + x / 2; //no false positive + + x & NEG_ONE; //no error, as we skip lookups (for now) + x; + + let u: u8 = 0; + u; + + 1 << 0; // no error, this case is allowed, see issue 3430 + 42; + 1; + 42; + &x; + x; + + let mut a = A("".into()); + let b = a << 0; // no error: non-integer + + 1 * Meter; // no error: non-integer + + 2; + -2; + 2 + x; + -2 + x; + x + 1; + (x + 1) % 3; // no error + 4 % 3; // no error + 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + (if b { 1 } else { 2 }); + (if b { 1 } else { 2 }) + if b { 3 } else { 4 }; + (match a { 0 => 10, _ => 20 }); + (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 }; + (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 }; + (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 }; + (if b { 1 } else { 2 }); + + ({ a }) + 3; + ({ a } * 2); + (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 }; + + fn f(_: i32) { + todo!(); + } + f(a + { 8 * 5 }); + f(if b { 1 } else { 2 } + 3); + const _: i32 = { 2 * 4 } + 3; + const _: i32 = { 1 + 2 * 3 } + 3; + + a as usize; + let _ = a as usize; + ({ a } as usize); + + 2 * { a }; + (({ a } + 4)); + 1; +} + +pub fn decide(a: bool, b: bool) -> u32 { + (if a { 1 } else { 2 }) + if b { 3 } else { 5 } +} diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index fec54d00ccb4..ca799c9cfac0 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,3 +1,15 @@ +// run-rustfix + +#![warn(clippy::identity_op)] +#![allow( + clippy::eq_op, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::op_ref, + clippy::double_parens, + unused +)] + use std::fmt::Write as _; const ONE: i64 = 1; @@ -24,14 +36,6 @@ impl core::ops::Mul for u8 { } } -#[allow( - clippy::eq_op, - clippy::no_effect, - clippy::unnecessary_operation, - clippy::op_ref, - clippy::double_parens -)] -#[warn(clippy::identity_op)] #[rustfmt::skip] fn main() { let x = 0; @@ -82,29 +86,34 @@ fn main() { let a = 0; let b = true; 0 + if b { 1 } else { 2 }; - 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; 0 + match a { 0 => 10, _ => 20 }; - 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error - 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error - 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error - - 0 + if b { 0 + 1 } else { 2 }; - 0 + match a { 0 => 0 + 10, _ => 20 }; - 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + (if b { 1 } else { 2 }) + 0; - let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + 0 + { a } + 3; + 0 + { a } * 2; + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; - 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; - - 0 + { a } + 3; // no error - 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error - fn f(_: i32) { todo!(); } f(1 * a + { 8 * 5 }); - f(0 + if b { 1 } else { 2 } + 3); // no error + f(0 + if b { 1 } else { 2 } + 3); const _: i32 = { 2 * 4 } + 0 + 3; - const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error + const _: i32 = 0 + { 1 + 2 * 3 } + 3; + + 0 + a as usize; + let _ = 0 + a as usize; + 0 + { a } as usize; + + 2 * (0 + { a }); + 1 * ({ a } + 4); + 1 * 1; +} + +pub fn decide(a: bool, b: bool) -> u32 { + 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index d8cb65839cbb..1a104a20b841 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -1,202 +1,238 @@ -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:39:5 +error: this operation has no effect + --> $DIR/identity_op.rs:43:5 | LL | x + 0; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` | = note: `-D clippy::identity-op` implied by `-D warnings` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:40:5 +error: this operation has no effect + --> $DIR/identity_op.rs:44:5 | LL | x + (1 - 1); - | ^^^^^^^^^^^ + | ^^^^^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:42:5 +error: this operation has no effect + --> $DIR/identity_op.rs:46:5 | LL | 0 + x; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:45:5 - | -LL | x | (0); - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:48:5 - | -LL | x * 1; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `x` +error: this operation has no effect --> $DIR/identity_op.rs:49:5 | -LL | 1 * x; - | ^^^^^ +LL | x | (0); + | ^^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:55:5 +error: this operation has no effect + --> $DIR/identity_op.rs:52:5 + | +LL | x * 1; + | ^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:53:5 + | +LL | 1 * x; + | ^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:59:5 | LL | -1 & x; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `x` -error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:58:5 - | -LL | u & 255; - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:61:5 - | -LL | 42 << 0; - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `1` +error: this operation has no effect --> $DIR/identity_op.rs:62:5 | -LL | 1 >> 0; - | ^^^^^^ +LL | u & 255; + | ^^^^^^^ help: consider reducing it to: `u` -error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:63:5 - | -LL | 42 >> 0; - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:64:5 - | -LL | &x >> 0; - | ^^^^^^^ - -error: the operation is ineffective. Consider reducing it to `x` +error: this operation has no effect --> $DIR/identity_op.rs:65:5 | -LL | x >> &0; - | ^^^^^^^ +LL | 42 << 0; + | ^^^^^^^ help: consider reducing it to: `42` -error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:72:5 +error: this operation has no effect + --> $DIR/identity_op.rs:66:5 + | +LL | 1 >> 0; + | ^^^^^^ help: consider reducing it to: `1` + +error: this operation has no effect + --> $DIR/identity_op.rs:67:5 + | +LL | 42 >> 0; + | ^^^^^^^ help: consider reducing it to: `42` + +error: this operation has no effect + --> $DIR/identity_op.rs:68:5 + | +LL | &x >> 0; + | ^^^^^^^ help: consider reducing it to: `&x` + +error: this operation has no effect + --> $DIR/identity_op.rs:69:5 + | +LL | x >> &0; + | ^^^^^^^ help: consider reducing it to: `x` + +error: this operation has no effect + --> $DIR/identity_op.rs:76:5 | LL | 2 % 3; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `2` -error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:73:5 +error: this operation has no effect + --> $DIR/identity_op.rs:77:5 | LL | -2 % 3; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `-2` -error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:74:5 +error: this operation has no effect + --> $DIR/identity_op.rs:78:5 | LL | 2 % -3 + x; - | ^^^^^^ + | ^^^^^^ help: consider reducing it to: `2` -error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:75:5 +error: this operation has no effect + --> $DIR/identity_op.rs:79:5 | LL | -2 % -3 + x; - | ^^^^^^^ + | ^^^^^^^ help: consider reducing it to: `-2` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:76:9 +error: this operation has no effect + --> $DIR/identity_op.rs:80:9 | LL | x + 1 % 3; - | ^^^^^ + | ^^^^^ help: consider reducing it to: `1` -error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` - --> $DIR/identity_op.rs:84:5 +error: this operation has no effect + --> $DIR/identity_op.rs:88:5 | LL | 0 + if b { 1 } else { 2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` - --> $DIR/identity_op.rs:86:5 +error: this operation has no effect + --> $DIR/identity_op.rs:89:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` + +error: this operation has no effect + --> $DIR/identity_op.rs:90:5 | LL | 0 + match a { 0 => 10, _ => 20 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` +error: this operation has no effect --> $DIR/identity_op.rs:91:5 | -LL | 0 + if b { 0 + 1 } else { 2 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:91:16 - | -LL | 0 + if b { 0 + 1 } else { 2 }; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` +error: this operation has no effect --> $DIR/identity_op.rs:92:5 | -LL | 0 + match a { 0 => 0 + 10, _ => 20 }; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `10` - --> $DIR/identity_op.rs:92:25 +error: this operation has no effect + --> $DIR/identity_op.rs:93:5 | -LL | 0 + match a { 0 => 0 + 10, _ => 20 }; - | ^^^^^^ +LL | 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:93:16 +error: this operation has no effect + --> $DIR/identity_op.rs:94:5 | -LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - | ^^^^^ +LL | (if b { 1 } else { 2 }) + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })` -error: the operation is ineffective. Consider reducing it to `30` - --> $DIR/identity_op.rs:93:52 +error: this operation has no effect + --> $DIR/identity_op.rs:96:5 | -LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; - | ^^^^^^ +LL | 0 + { a } + 3; + | ^^^^^^^^^ help: consider reducing it to: `({ a })` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:95:20 +error: this operation has no effect + --> $DIR/identity_op.rs:97:5 | -LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - | ^^^^^ +LL | 0 + { a } * 2; + | ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)` -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:95:52 - | -LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:96:23 - | -LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:96:58 - | -LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; - | ^^^^^ - -error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` +error: this operation has no effect --> $DIR/identity_op.rs:98:5 | -LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })` -error: the operation is ineffective. Consider reducing it to `a` - --> $DIR/identity_op.rs:106:7 +error: this operation has no effect + --> $DIR/identity_op.rs:103:7 | LL | f(1 * a + { 8 * 5 }); - | ^^^^^ + | ^^^^^ help: consider reducing it to: `a` -error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` - --> $DIR/identity_op.rs:108:20 +error: this operation has no effect + --> $DIR/identity_op.rs:104:7 + | +LL | f(0 + if b { 1 } else { 2 } + 3); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }` + +error: this operation has no effect + --> $DIR/identity_op.rs:105:20 | LL | const _: i32 = { 2 * 4 } + 0 + 3; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }` -error: aborting due to 33 previous errors +error: this operation has no effect + --> $DIR/identity_op.rs:106:20 + | +LL | const _: i32 = 0 + { 1 + 2 * 3 } + 3; + | ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }` + +error: this operation has no effect + --> $DIR/identity_op.rs:108:5 + | +LL | 0 + a as usize; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` + +error: this operation has no effect + --> $DIR/identity_op.rs:109:13 + | +LL | let _ = 0 + a as usize; + | ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize` + +error: this operation has no effect + --> $DIR/identity_op.rs:110:5 + | +LL | 0 + { a } as usize; + | ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)` + +error: this operation has no effect + --> $DIR/identity_op.rs:112:9 + | +LL | 2 * (0 + { a }); + | ^^^^^^^^^^^ help: consider reducing it to: `{ a }` + +error: this operation has no effect + --> $DIR/identity_op.rs:113:5 + | +LL | 1 * ({ a } + 4); + | ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))` + +error: this operation has no effect + --> $DIR/identity_op.rs:114:5 + | +LL | 1 * 1; + | ^^^^^ help: consider reducing it to: `1` + +error: this operation has no effect + --> $DIR/identity_op.rs:118:5 + | +LL | 0 + if a { 1 } else { 2 } + if b { 3 } else { 5 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })` + +error: aborting due to 39 previous errors diff --git a/tests/ui/modulo_one.rs b/tests/ui/modulo_one.rs index 678a312f66e5..adff08e5d1e8 100644 --- a/tests/ui/modulo_one.rs +++ b/tests/ui/modulo_one.rs @@ -1,5 +1,5 @@ #![warn(clippy::modulo_one)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)] static STATIC_ONE: usize = 2 - 1; static STATIC_NEG_ONE: i64 = 1 - 2; diff --git a/tests/ui/modulo_one.stderr b/tests/ui/modulo_one.stderr index 03f460897fce..04ecdef5e994 100644 --- a/tests/ui/modulo_one.stderr +++ b/tests/ui/modulo_one.stderr @@ -38,14 +38,6 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | i32::MIN % (-1); // also caught by rustc | ^^^^^^^^^^^^^^^ -error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/modulo_one.rs:13:22 - | -LL | const ONE: u32 = 1 * 1; - | ^^^^^ - | - = note: `-D clippy::identity-op` implied by `-D warnings` - error: any number modulo 1 will be 0 --> $DIR/modulo_one.rs:17:5 | @@ -64,5 +56,5 @@ error: any number modulo -1 will panic/overflow or result in 0 LL | INT_MIN % NEG_ONE; // also caught by rustc | ^^^^^^^^^^^^^^^^^ -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors From c318cf453dafbd8129203c3117d8f7be799f62ef Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 29 Apr 2022 06:52:01 +1000 Subject: [PATCH 441/536] Overhaul `MacArgs::Eq`. The value in `MacArgs::Eq` is currently represented as a `Token`. Because of `TokenKind::Interpolated`, `Token` can be either a token or an arbitrary AST fragment. In practice, a `MacArgs::Eq` starts out as a literal or macro call AST fragment, and then is later lowered to a literal token. But this is very non-obvious. `Token` is a much more general type than what is needed. This commit restricts things, by introducing a new type `MacArgsEqKind` that is either an AST expression (pre-lowering) or an AST literal (post-lowering). The downside is that the code is a bit more verbose in a few places. The benefit is that makes it much clearer what the possibilities are (though also shorter in some other places). Also, it removes one use of `TokenKind::Interpolated`, taking us a step closer to removing that variant, which will let us make `Token` impl `Copy` and remove many "handle Interpolated" code paths in the parser. Things to note: - Error messages have improved. Messages like this: ``` unexpected token: `"bug" + "found"` ``` now say "unexpected expression", which makes more sense. Although arbitrary expressions can exist within tokens thanks to `TokenKind::Interpolated`, that's not obvious to anyone who doesn't know compiler internals. - In `parse_mac_args_common`, we no longer need to collect tokens for the value expression. --- clippy_utils/src/ast_utils.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 3fce4987679a..7919800483f5 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -688,7 +688,8 @@ pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool { match (l, r) { (Empty, Empty) => true, (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts), - (Eq(_, lt), Eq(_, rt)) => lt.kind == rt.kind, + (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re), + (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind, _ => false, } } From dd7bc86f227ca30f1c8a77955c60bced3a9c92db Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 4 May 2022 20:43:08 -0400 Subject: [PATCH 442/536] Change input placeholder --- util/gh-pages/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 69dcd947dfbd..ec62b3682f23 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -454,7 +454,7 @@ Otherwise, have a great day =^.^= ng-attr-id="filter-{filter}" class="version-filter-input form-control filter-input" maxlength="2" - placeholder="62" + placeholder="xx" ng-model="version_filters[filter].minor_version" ng-model-options="{debounce: 50}" /> .0 From 7e017dbe8d53bb8bce36871a53d8cdc47de5bc1a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 13:32:18 +0100 Subject: [PATCH 443/536] Bump nightly version -> 2022-05-05 --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index bb29c71e9f45..03acb51306d7 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-04-07" +channel = "nightly-2022-05-05" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] From bb01aca86f66954b80798213711e8eadc4b26902 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 13:32:31 +0100 Subject: [PATCH 444/536] HACK: Move buggy lints to nursery Those lints are trait_duplication_in_bounds and type_repetition_in_bounds. I don't think those can be fixed on the Clippy side alone, but need changes in the compiler. So let's move them to nursery to get the sync through and then fix them on the rustc side. Also adds a regression test that has to be fixed before they can be moved back to pedantic. --- clippy_lints/src/lib.register_nursery.rs | 2 ++ clippy_lints/src/lib.register_pedantic.rs | 2 -- clippy_lints/src/trait_bounds.rs | 4 ++-- tests/ui/trait_duplication_in_bounds.rs | 3 +++ tests/ui/trait_duplication_in_bounds.stderr | 10 +++++++++- tests/ui/type_repetition_in_bounds.rs | 3 +++ tests/ui/type_repetition_in_bounds.stderr | 10 +++++++++- 7 files changed, 28 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index 18904a945389..ec187563b3f6 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -28,6 +28,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 63232fd41130..2ee2c6e3358c 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -84,8 +84,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index c0aca2d517cf..78e388a49af1 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, - pedantic, + nursery, "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } @@ -65,7 +65,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub TRAIT_DUPLICATION_IN_BOUNDS, - pedantic, + nursery, "Check if the same trait bounds are specified twice during a function declaration" } diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index f5ca91143af2..a21d4c5d637d 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -95,4 +95,7 @@ trait FooIter: Iterator { } } +// This should not lint +fn impl_trait(_: impl AsRef, _: impl AsRef) {} + fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index 6f8c8e47dfbf..d0a4cfb88370 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -67,5 +67,13 @@ LL | Self: Iterator, | = help: consider removing this trait bound -error: aborting due to 8 previous errors +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:99:23 + | +LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 9 previous errors diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index fc740ee11d6a..d11432f90461 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -79,4 +79,7 @@ where u: U, } +// This should not lint +fn impl_trait(_: impl AsRef, _: impl AsRef) {} + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 148c19c7d070..abc25e59496b 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -19,5 +19,13 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: aborting due to 2 previous errors +error: this type has already been used as a bound predicate + --> $DIR/type_repetition_in_bounds.rs:83:43 + | +LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} + | ^^^^^^^^^^ + | + = help: consider combining the bounds: `impl AsRef: AsRef + AsRef` + +error: aborting due to 3 previous errors From 006282964f1e741a58c72d8e12f872bf5c227af9 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 14:10:06 +0100 Subject: [PATCH 445/536] Fix ICE in EarlyAttribtues lints --- clippy_lints/src/attrs.rs | 6 +++++- tests/ui/crashes/ice-96721.rs | 10 ++++++++++ tests/ui/crashes/ice-96721.stderr | 8 ++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/ui/crashes/ice-96721.rs create mode 100644 tests/ui/crashes/ice-96721.stderr diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index e880876218ec..8b0e11cb802e 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -593,6 +593,10 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It }; if attr.style == AttrStyle::Outer { + if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args + && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) { + return; + } if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { return; } diff --git a/tests/ui/crashes/ice-96721.rs b/tests/ui/crashes/ice-96721.rs new file mode 100644 index 000000000000..4b3fb7640108 --- /dev/null +++ b/tests/ui/crashes/ice-96721.rs @@ -0,0 +1,10 @@ +macro_rules! foo { + () => { + "bar.rs" + }; +} + +#[path = foo!()] //~ ERROR malformed `path` attribute +mod abc {} + +fn main() {} diff --git a/tests/ui/crashes/ice-96721.stderr b/tests/ui/crashes/ice-96721.stderr new file mode 100644 index 000000000000..78c567b8e772 --- /dev/null +++ b/tests/ui/crashes/ice-96721.stderr @@ -0,0 +1,8 @@ +error: malformed `path` attribute input + --> $DIR/ice-96721.rs:7:1 + | +LL | #[path = foo!()] //~ ERROR malformed `path` attribute + | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]` + +error: aborting due to previous error + From f0c2ac8a298f7bf828334972cf7c00a20389bbdb Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Thu, 5 May 2022 14:27:11 +0100 Subject: [PATCH 446/536] Bless clippy error msg --- tests/ui/indexing_slicing_index.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/indexing_slicing_index.stderr b/tests/ui/indexing_slicing_index.stderr index 83a36f407d5d..6ae700753f06 100644 --- a/tests/ui/indexing_slicing_index.stderr +++ b/tests/ui/indexing_slicing_index.stderr @@ -1,4 +1,4 @@ -error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed +error[E0080]: evaluation of `main::{constant#3}` failed --> $DIR/indexing_slicing_index.rs:31:14 | LL | const { &ARR[idx4()] }; // Ok, let rustc handle const contexts. From 7cd86aa1be18d608d828239c11e887a762efc92a Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 15:12:52 +0100 Subject: [PATCH 447/536] Merge commit '7c21f91b15b7604f818565646b686d90f99d1baf' into clippyup --- .github/ISSUE_TEMPLATE/blank_issue.yml | 2 +- .github/ISSUE_TEMPLATE/false_negative.yml | 2 +- .github/ISSUE_TEMPLATE/false_positive.yml | 2 +- .github/workflows/clippy.yml | 6 +- .github/workflows/clippy_bors.yml | 8 +- .github/workflows/clippy_dev.yml | 2 +- .github/workflows/deploy.yml | 4 +- .github/workflows/remark.yml | 2 +- CHANGELOG.md | 113 +++- clippy_dev/Cargo.toml | 1 + clippy_dev/src/lib.rs | 1 + clippy_dev/src/main.rs | 84 ++- clippy_dev/src/new_lint.rs | 6 +- clippy_dev/src/setup/mod.rs | 4 +- clippy_dev/src/update_lints.rs | 405 +++++++++++- clippy_lints/src/attrs.rs | 19 +- clippy_lints/src/await_holding_invalid.rs | 167 +++-- clippy_lints/src/bytes_count_to_len.rs | 70 +++ ...se_sensitive_file_extension_comparisons.rs | 4 +- .../src/casts/cast_possible_truncation.rs | 20 +- .../src/casts/cast_slice_different_sizes.rs | 144 +++-- clippy_lints/src/casts/mod.rs | 3 +- clippy_lints/src/collapsible_if.rs | 18 +- clippy_lints/src/doc.rs | 10 +- clippy_lints/src/drop_forget_ref.rs | 2 +- clippy_lints/src/empty_drop.rs | 65 ++ .../src/empty_structs_with_brackets.rs | 2 +- clippy_lints/src/eta_reduction.rs | 7 +- clippy_lints/src/format_push_string.rs | 77 +++ clippy_lints/src/functions/must_use.rs | 9 +- clippy_lints/src/functions/result_unit_err.rs | 2 +- clippy_lints/src/identity_op.rs | 82 ++- clippy_lints/src/implicit_hasher.rs | 4 +- .../src/inconsistent_struct_constructor.rs | 3 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/inherent_impl.rs | 2 +- clippy_lints/src/init_numbered_fields.rs | 2 + clippy_lints/src/large_include_file.rs | 86 +++ clippy_lints/src/lib.register_all.rs | 10 +- clippy_lints/src/lib.register_complexity.rs | 3 +- clippy_lints/src/lib.register_lints.rs | 10 + clippy_lints/src/lib.register_nursery.rs | 3 + clippy_lints/src/lib.register_pedantic.rs | 4 +- clippy_lints/src/lib.register_perf.rs | 2 +- clippy_lints/src/lib.register_restriction.rs | 3 + clippy_lints/src/lib.register_style.rs | 4 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 68 +-- clippy_lints/src/lifetimes.rs | 72 ++- clippy_lints/src/literal_representation.rs | 50 +- clippy_lints/src/loops/never_loop.rs | 5 +- clippy_lints/src/manual_bits.rs | 48 +- clippy_lints/src/manual_non_exhaustive.rs | 221 ++++--- clippy_lints/src/map_clone.rs | 12 +- clippy_lints/src/match_result_ok.rs | 4 +- ...h.rs => infallible_destructuring_match.rs} | 0 clippy_lints/src/matches/match_same_arms.rs | 4 +- clippy_lints/src/matches/mod.rs | 9 +- clippy_lints/src/matches/needless_match.rs | 6 +- .../src/matches/redundant_pattern_match.rs | 66 +- .../matches/rest_pat_in_fully_bound_struct.rs | 1 + .../src/methods/is_digit_ascii_radix.rs | 50 ++ clippy_lints/src/methods/iter_with_drain.rs | 95 ++- clippy_lints/src/methods/mod.rs | 111 +++- .../src/methods/needless_option_take.rs | 41 ++ clippy_lints/src/methods/str_splitn.rs | 419 +++++++------ .../src/methods/unnecessary_to_owned.rs | 23 +- .../src/methods/wrong_self_convention.rs | 2 +- clippy_lints/src/misc_early/mod.rs | 2 +- clippy_lints/src/missing_inline.rs | 4 +- clippy_lints/src/needless_bitwise_bool.rs | 4 +- clippy_lints/src/needless_late_init.rs | 54 +- clippy_lints/src/new_without_default.rs | 2 +- clippy_lints/src/non_expressive_names.rs | 2 +- clippy_lints/src/octal_escapes.rs | 2 +- clippy_lints/src/only_used_in_recursion.rs | 56 +- clippy_lints/src/option_if_let_else.rs | 8 +- clippy_lints/src/ptr.rs | 81 +-- clippy_lints/src/pub_use.rs | 56 ++ clippy_lints/src/redundant_pub_crate.rs | 21 +- clippy_lints/src/renamed_lints.rs | 39 ++ clippy_lints/src/same_name_method.rs | 206 +++---- clippy_lints/src/stable_sort_primitive.rs | 20 +- clippy_lints/src/strings.rs | 56 ++ .../src/suspicious_operation_groupings.rs | 2 +- clippy_lints/src/trait_bounds.rs | 15 +- clippy_lints/src/transmute/utils.rs | 20 +- clippy_lints/src/types/mod.rs | 4 +- .../src/undocumented_unsafe_blocks.rs | 7 +- clippy_lints/src/unit_types/let_unit_value.rs | 80 ++- clippy_lints/src/unit_types/mod.rs | 2 +- .../src/unnecessary_owned_empty_strings.rs | 81 +++ clippy_lints/src/unnested_or_patterns.rs | 14 +- clippy_lints/src/use_self.rs | 2 +- clippy_lints/src/utils/author.rs | 2 +- clippy_lints/src/utils/conf.rs | 6 + clippy_lints/src/utils/dump_hir.rs | 55 ++ clippy_lints/src/utils/inspector.rs | 577 ------------------ clippy_lints/src/utils/internal_lints.rs | 4 +- .../internal_lints/metadata_collector.rs | 8 +- clippy_lints/src/utils/mod.rs | 2 +- clippy_utils/src/consts.rs | 18 +- clippy_utils/src/hir_utils.rs | 42 +- clippy_utils/src/macros.rs | 2 +- clippy_utils/src/msrvs.rs | 1 + clippy_utils/src/numeric_literal.rs | 2 +- clippy_utils/src/paths.rs | 3 + clippy_utils/src/qualify_min_const_fn.rs | 5 +- clippy_utils/src/source.rs | 21 +- clippy_utils/src/sugg.rs | 10 +- clippy_utils/src/ty.rs | 71 ++- clippy_utils/src/usage.rs | 33 +- clippy_utils/src/visitors.rs | 68 ++- doc/adding_lints.md | 14 + lintcheck/src/main.rs | 19 +- rust-toolchain | 2 +- src/driver.rs | 3 +- tests/dogfood.rs | 6 +- tests/lint_message_convention.rs | 2 +- .../collapsible_span_lint_calls.stderr | 4 +- .../interning_defined_symbol.fixed | 2 +- tests/ui-internal/interning_defined_symbol.rs | 2 +- .../await_holding_invalid_type.rs | 41 ++ .../await_holding_invalid_type.stderr | 25 + .../await_holding_invalid_type/clippy.toml | 4 + tests/ui-toml/functions_maxlines/test.rs | 1 + tests/ui-toml/functions_maxlines/test.stderr | 8 +- tests/ui-toml/large_include_file/clippy.toml | 1 + .../large_include_file/large_include_file.rs | 16 + .../large_include_file.stderr | 21 + tests/ui-toml/large_include_file/too_big.txt | 1 + .../min_rust_version/min_rust_version.stderr | 2 +- .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui/assertions_on_constants.rs | 10 +- tests/ui/auxiliary/proc_macro_derive.rs | 2 +- tests/ui/auxiliary/proc_macro_with_span.rs | 32 + tests/ui/bytes_count_to_len.fixed | 34 ++ tests/ui/bytes_count_to_len.rs | 34 ++ tests/ui/bytes_count_to_len.stderr | 28 + tests/ui/cast.rs | 14 +- tests/ui/cast.stderr | 14 +- tests/ui/cast_alignment.rs | 6 +- tests/ui/cast_slice_different_sizes.rs | 43 ++ tests/ui/cast_slice_different_sizes.stderr | 85 ++- tests/ui/collapsible_else_if.fixed | 7 + tests/ui/collapsible_else_if.rs | 9 + tests/ui/collapsible_else_if.stderr | 11 +- tests/ui/crashes/auxiliary/ice-8681-aux.rs | 6 + tests/ui/crashes/ice-2865.rs | 2 +- tests/ui/crashes/ice-3151.rs | 2 +- tests/ui/crashes/ice-5944.rs | 1 + tests/ui/crashes/ice-8250.stderr | 10 +- tests/ui/crashes/ice-8681.rs | 10 + tests/ui/crashes/ice-96721.rs | 10 + tests/ui/crashes/ice-96721.stderr | 8 + tests/ui/default_numeric_fallback_f64.fixed | 15 +- tests/ui/default_numeric_fallback_f64.rs | 15 +- tests/ui/default_numeric_fallback_f64.stderr | 46 +- tests/ui/default_numeric_fallback_i32.fixed | 13 +- tests/ui/default_numeric_fallback_i32.rs | 13 +- tests/ui/default_numeric_fallback_i32.stderr | 50 +- tests/ui/deprecated.rs | 4 + tests/ui/deprecated.stderr | 32 +- tests/ui/doc_unsafe.rs | 2 + tests/ui/doc_unsafe.stderr | 12 +- tests/ui/drop_non_drop.stderr | 4 +- tests/ui/empty_drop.fixed | 24 + tests/ui/empty_drop.rs | 30 + tests/ui/empty_drop.stderr | 22 + tests/ui/eta.fixed | 20 +- tests/ui/eta.rs | 16 + tests/ui/eta.stderr | 14 +- tests/ui/extra_unused_lifetimes.rs | 42 ++ tests/ui/extra_unused_lifetimes.stderr | 20 +- tests/ui/format_push_string.rs | 7 + tests/ui/format_push_string.stderr | 19 + tests/ui/identity_op.rs | 34 +- tests/ui/identity_op.stderr | 128 +++- tests/ui/impl.rs | 2 +- tests/ui/is_digit_ascii_radix.fixed | 18 + tests/ui/is_digit_ascii_radix.rs | 18 + tests/ui/is_digit_ascii_radix.stderr | 22 + tests/ui/iter_overeager_cloned.fixed | 2 +- tests/ui/iter_overeager_cloned.rs | 2 +- tests/ui/iter_with_drain.fixed | 9 + tests/ui/iter_with_drain.rs | 9 + tests/ui/let_underscore_drop.rs | 1 + tests/ui/let_underscore_drop.stderr | 6 +- tests/ui/let_unit.fixed | 52 ++ tests/ui/let_unit.rs | 52 ++ tests/ui/let_unit.stderr | 71 ++- tests/ui/manual_bits.fixed | 63 +- tests/ui/manual_bits.rs | 13 +- tests/ui/manual_bits.stderr | 170 +++--- tests/ui/manual_non_exhaustive_enum.rs | 78 +++ tests/ui/manual_non_exhaustive_enum.stderr | 41 ++ ...ive.rs => manual_non_exhaustive_struct.rs} | 63 -- ...rr => manual_non_exhaustive_struct.stderr} | 58 +- tests/ui/manual_split_once.fixed | 117 +++- tests/ui/manual_split_once.rs | 117 +++- tests/ui/manual_split_once.stderr | 221 +++++-- tests/ui/mistyped_literal_suffix.fixed | 16 +- tests/ui/mistyped_literal_suffix.rs | 16 +- tests/ui/mistyped_literal_suffix.stderr | 54 +- tests/ui/mut_from_ref.rs | 20 +- tests/ui/mut_from_ref.stderr | 14 +- tests/ui/needless_for_each_fixable.fixed | 7 +- tests/ui/needless_for_each_fixable.rs | 7 +- tests/ui/needless_for_each_fixable.stderr | 16 +- tests/ui/needless_late_init.rs | 73 ++- tests/ui/needless_late_init.stderr | 99 +-- tests/ui/needless_late_init_fixable.fixed | 8 +- tests/ui/needless_late_init_fixable.rs | 10 +- tests/ui/needless_late_init_fixable.stderr | 49 +- tests/ui/needless_match.fixed | 12 + tests/ui/needless_match.rs | 12 + tests/ui/needless_match.stderr | 8 +- tests/ui/needless_option_as_deref.fixed | 14 + tests/ui/needless_option_as_deref.rs | 14 + tests/ui/needless_option_take.fixed | 15 + tests/ui/needless_option_take.rs | 15 + tests/ui/needless_option_take.stderr | 10 + tests/ui/needless_splitn.fixed | 20 + tests/ui/needless_splitn.rs | 20 + tests/ui/needless_splitn.stderr | 44 +- tests/ui/new_without_default.rs | 2 +- tests/ui/non_expressive_names.rs | 2 +- tests/ui/numbered_fields.fixed | 5 + tests/ui/numbered_fields.rs | 5 + tests/ui/option_if_let_else.fixed | 7 +- tests/ui/option_if_let_else.rs | 7 +- tests/ui/option_if_let_else.stderr | 30 +- tests/ui/option_take_on_temporary.fixed | 15 + tests/ui/or_then_unwrap.fixed | 2 +- tests/ui/or_then_unwrap.rs | 2 +- tests/ui/panicking_macros.rs | 2 +- tests/ui/pub_use.rs | 14 + tests/ui/pub_use.stderr | 11 + tests/ui/redundant_pub_crate.fixed | 10 + tests/ui/redundant_pub_crate.rs | 10 + tests/ui/rename.fixed | 93 ++- tests/ui/rename.rs | 93 ++- tests/ui/rename.stderr | 270 ++++---- tests/ui/rest_pat_in_fully_bound_structs.rs | 15 + tests/ui/same_functions_in_if_condition.rs | 19 + .../ui/same_functions_in_if_condition.stderr | 24 +- tests/ui/shadow.rs | 1 + tests/ui/shadow.stderr | 88 +-- tests/ui/similar_names.rs | 3 +- tests/ui/similar_names.stderr | 28 +- tests/ui/single_char_lifetime_names.rs | 1 + tests/ui/single_char_lifetime_names.stderr | 10 +- tests/ui/single_match_else.rs | 18 +- tests/ui/single_match_else.stderr | 11 +- tests/ui/stable_sort_primitive.stderr | 14 +- tests/ui/suspicious_else_formatting.rs | 2 +- tests/ui/to_digit_is_some.fixed | 4 +- tests/ui/to_digit_is_some.rs | 4 +- tests/ui/to_digit_is_some.stderr | 8 +- tests/ui/trait_duplication_in_bounds.rs | 3 + tests/ui/trait_duplication_in_bounds.stderr | 10 +- tests/ui/transmute_collection.rs | 5 +- tests/ui/trim_split_whitespace.fixed | 91 +++ tests/ui/trim_split_whitespace.rs | 91 +++ tests/ui/trim_split_whitespace.stderr | 52 ++ tests/ui/type_repetition_in_bounds.rs | 3 + tests/ui/type_repetition_in_bounds.stderr | 10 +- tests/ui/undocumented_unsafe_blocks.rs | 1 + tests/ui/undocumented_unsafe_blocks.stderr | 36 +- tests/ui/uninit.rs | 1 + tests/ui/uninit.stderr | 6 +- tests/ui/unit_arg.rs | 3 +- tests/ui/unit_arg.stderr | 20 +- tests/ui/unit_hash.rs | 1 + tests/ui/unit_hash.stderr | 6 +- .../ui/unnecessary_owned_empty_strings.fixed | 22 + tests/ui/unnecessary_owned_empty_strings.rs | 22 + .../ui/unnecessary_owned_empty_strings.stderr | 16 + tests/ui/unnecessary_to_owned.fixed | 12 + tests/ui/unnecessary_to_owned.rs | 12 + tests/ui/unnecessary_to_owned.stderr | 196 +++--- tests/ui/unnested_or_patterns.fixed | 7 +- tests/ui/unnested_or_patterns.rs | 7 +- tests/ui/unnested_or_patterns.stderr | 40 +- tests/ui/useless_attribute.fixed | 6 + tests/ui/useless_attribute.rs | 6 + tests/ui/useless_attribute.stderr | 2 +- tests/ui/wildcard_imports.fixed | 3 +- tests/ui/wildcard_imports.rs | 3 +- tests/ui/wildcard_imports.stderr | 42 +- tests/ui/wrong_self_convention.rs | 5 - tests/ui/wrong_self_convention.stderr | 18 +- tests/ui/wrong_self_convention2.rs | 10 + 293 files changed, 6318 insertions(+), 2753 deletions(-) create mode 100644 clippy_lints/src/bytes_count_to_len.rs create mode 100644 clippy_lints/src/empty_drop.rs create mode 100644 clippy_lints/src/format_push_string.rs create mode 100644 clippy_lints/src/large_include_file.rs rename clippy_lints/src/matches/{infalliable_detructuring_match.rs => infallible_destructuring_match.rs} (100%) create mode 100644 clippy_lints/src/methods/is_digit_ascii_radix.rs create mode 100644 clippy_lints/src/methods/needless_option_take.rs create mode 100644 clippy_lints/src/pub_use.rs create mode 100644 clippy_lints/src/renamed_lints.rs create mode 100644 clippy_lints/src/unnecessary_owned_empty_strings.rs create mode 100644 clippy_lints/src/utils/dump_hir.rs delete mode 100644 clippy_lints/src/utils/inspector.rs create mode 100644 tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs create mode 100644 tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr create mode 100644 tests/ui-toml/await_holding_invalid_type/clippy.toml create mode 100644 tests/ui-toml/large_include_file/clippy.toml create mode 100644 tests/ui-toml/large_include_file/large_include_file.rs create mode 100644 tests/ui-toml/large_include_file/large_include_file.stderr create mode 100644 tests/ui-toml/large_include_file/too_big.txt create mode 100644 tests/ui/auxiliary/proc_macro_with_span.rs create mode 100644 tests/ui/bytes_count_to_len.fixed create mode 100644 tests/ui/bytes_count_to_len.rs create mode 100644 tests/ui/bytes_count_to_len.stderr create mode 100644 tests/ui/crashes/auxiliary/ice-8681-aux.rs create mode 100644 tests/ui/crashes/ice-8681.rs create mode 100644 tests/ui/crashes/ice-96721.rs create mode 100644 tests/ui/crashes/ice-96721.stderr create mode 100644 tests/ui/empty_drop.fixed create mode 100644 tests/ui/empty_drop.rs create mode 100644 tests/ui/empty_drop.stderr create mode 100644 tests/ui/format_push_string.rs create mode 100644 tests/ui/format_push_string.stderr create mode 100644 tests/ui/is_digit_ascii_radix.fixed create mode 100644 tests/ui/is_digit_ascii_radix.rs create mode 100644 tests/ui/is_digit_ascii_radix.stderr create mode 100644 tests/ui/manual_non_exhaustive_enum.rs create mode 100644 tests/ui/manual_non_exhaustive_enum.stderr rename tests/ui/{manual_non_exhaustive.rs => manual_non_exhaustive_struct.rs} (58%) rename tests/ui/{manual_non_exhaustive.stderr => manual_non_exhaustive_struct.stderr} (53%) create mode 100644 tests/ui/needless_option_take.fixed create mode 100644 tests/ui/needless_option_take.rs create mode 100644 tests/ui/needless_option_take.stderr create mode 100644 tests/ui/option_take_on_temporary.fixed create mode 100644 tests/ui/pub_use.rs create mode 100644 tests/ui/pub_use.stderr create mode 100644 tests/ui/trim_split_whitespace.fixed create mode 100644 tests/ui/trim_split_whitespace.rs create mode 100644 tests/ui/trim_split_whitespace.stderr create mode 100644 tests/ui/unnecessary_owned_empty_strings.fixed create mode 100644 tests/ui/unnecessary_owned_empty_strings.rs create mode 100644 tests/ui/unnecessary_owned_empty_strings.stderr diff --git a/.github/ISSUE_TEMPLATE/blank_issue.yml b/.github/ISSUE_TEMPLATE/blank_issue.yml index d610e8c7bc47..89884bfc8590 100644 --- a/.github/ISSUE_TEMPLATE/blank_issue.yml +++ b/.github/ISSUE_TEMPLATE/blank_issue.yml @@ -9,7 +9,7 @@ body: attributes: label: Description description: > - Please provide a discription of the issue, along with any information + Please provide a description of the issue, along with any information you feel relevant to replicate it. validations: required: true diff --git a/.github/ISSUE_TEMPLATE/false_negative.yml b/.github/ISSUE_TEMPLATE/false_negative.yml index 9357ccc4f4e7..25e436d30b97 100644 --- a/.github/ISSUE_TEMPLATE/false_negative.yml +++ b/.github/ISSUE_TEMPLATE/false_negative.yml @@ -23,7 +23,7 @@ body: id: reproducer attributes: label: Reproducer - description: Please provide the code and steps to repoduce the bug + description: Please provide the code and steps to reproduce the bug value: | I tried this code: diff --git a/.github/ISSUE_TEMPLATE/false_positive.yml b/.github/ISSUE_TEMPLATE/false_positive.yml index b7dd400ee733..561b65c93a7f 100644 --- a/.github/ISSUE_TEMPLATE/false_positive.yml +++ b/.github/ISSUE_TEMPLATE/false_positive.yml @@ -24,7 +24,7 @@ body: attributes: label: Reproducer description: > - Please provide the code and steps to repoduce the bug together with the + Please provide the code and steps to reproduce the bug together with the output from Clippy. value: | I tried this code: diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index cd83bc9642b6..0e27cc927ace 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -6,14 +6,14 @@ on: branches-ignore: - auto - try - # Don't run Clippy tests, when only textfiles were modified + # Don't run Clippy tests, when only text files were modified paths-ignore: - 'COPYRIGHT' - 'LICENSE-*' - '**.md' - '**.txt' pull_request: - # Don't run Clippy tests, when only textfiles were modified + # Don't run Clippy tests, when only text files were modified paths-ignore: - 'COPYRIGHT' - 'LICENSE-*' @@ -37,7 +37,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml index f571485e6d38..9b3fd3ddfeb3 100644 --- a/.github/workflows/clippy_bors.yml +++ b/.github/workflows/clippy_bors.yml @@ -25,7 +25,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ github.ref }} @@ -88,7 +88,7 @@ jobs: if: matrix.host == 'i686-unknown-linux-gnu' - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -154,7 +154,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain @@ -212,7 +212,7 @@ jobs: github_token: "${{ secrets.github_token }}" - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Install toolchain run: rustup show active-toolchain diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml index 5dfab1d2ebc4..22051093c9cf 100644 --- a/.github/workflows/clippy_dev.yml +++ b/.github/workflows/clippy_dev.yml @@ -23,7 +23,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 # Run - name: Build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b8be730be32b..71d71d10359e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -21,10 +21,10 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/.github/workflows/remark.yml b/.github/workflows/remark.yml index 56c00544c93a..a179bfa72617 100644 --- a/.github/workflows/remark.yml +++ b/.github/workflows/remark.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3.0.2 - name: Setup Node.js uses: actions/setup-node@v1.4.4 diff --git a/CHANGELOG.md b/CHANGELOG.md index b4097ea86a51..751f9fccd88d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,108 @@ document. ## Unreleased / In Rust Nightly -[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master) +[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master) + +## Rust 1.61 (beta) + +Current beta, released 2022-05-19 + +[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481) + +### New Lints + +* [`only_used_in_recursion`] + [#8422](https://github.com/rust-lang/rust-clippy/pull/8422) +* [`cast_enum_truncation`] + [#8381](https://github.com/rust-lang/rust-clippy/pull/8381) +* [`missing_spin_loop`] + [#8174](https://github.com/rust-lang/rust-clippy/pull/8174) +* [`deref_by_slicing`] + [#8218](https://github.com/rust-lang/rust-clippy/pull/8218) +* [`needless_match`] + [#8471](https://github.com/rust-lang/rust-clippy/pull/8471) +* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`) + [#8504](https://github.com/rust-lang/rust-clippy/pull/8504) +* [`print_in_format_impl`] + [#8253](https://github.com/rust-lang/rust-clippy/pull/8253) +* [`unnecessary_find_map`] + [#8489](https://github.com/rust-lang/rust-clippy/pull/8489) +* [`or_then_unwrap`] + [#8561](https://github.com/rust-lang/rust-clippy/pull/8561) +* [`unnecessary_join`] + [#8579](https://github.com/rust-lang/rust-clippy/pull/8579) +* [`iter_with_drain`] + [#8483](https://github.com/rust-lang/rust-clippy/pull/8483) +* [`cast_enum_constructor`] + [#8562](https://github.com/rust-lang/rust-clippy/pull/8562) +* [`cast_slice_different_sizes`] + [#8445](https://github.com/rust-lang/rust-clippy/pull/8445) + +### Moves and Deprecations + +* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default) + [#8432](https://github.com/rust-lang/rust-clippy/pull/8432) +* Moved [`try_err`] to `restriction` + [#8544](https://github.com/rust-lang/rust-clippy/pull/8544) +* Move [`iter_with_drain`] to `nursery` + [#8541](https://github.com/rust-lang/rust-clippy/pull/8541) +* Renamed `to_string_in_display` to [`recursive_format_impl`] + [#8188](https://github.com/rust-lang/rust-clippy/pull/8188) + +### Enhancements + +* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros + [#8411](https://github.com/rust-lang/rust-clippy/pull/8411) +* [`ptr_as_ptr`]: Now works inside macros + [#8442](https://github.com/rust-lang/rust-clippy/pull/8442) +* [`use_self`]: Now works for variants in match expressions + [#8456](https://github.com/rust-lang/rust-clippy/pull/8456) +* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}` + [#8419](https://github.com/rust-lang/rust-clippy/pull/8419) +* [`recursive_format_impl`]: Now checks for format calls on `self` + [#8188](https://github.com/rust-lang/rust-clippy/pull/8188) + +### False Positive Fixes + +* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]` + [#8472](https://github.com/rust-lang/rust-clippy/pull/8472) +* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`, + generic parameters, wide pointers, unions, tuples and allow several forms of type erasure + [#8425](https://github.com/rust-lang/rust-clippy/pull/8425) + [#8553](https://github.com/rust-lang/rust-clippy/pull/8553) + [#8440](https://github.com/rust-lang/rust-clippy/pull/8440) + [#8547](https://github.com/rust-lang/rust-clippy/pull/8547) +* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer + lint `match` expressions with `cfg`ed arms + [#8443](https://github.com/rust-lang/rust-clippy/pull/8443) +* [`single_component_path_imports`]: No longer lint on macros + [#8537](https://github.com/rust-lang/rust-clippy/pull/8537) +* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>` + [#8552](https://github.com/rust-lang/rust-clippy/pull/8552) +* [`needless_borrow`]: No longer lints for method calls + [#8441](https://github.com/rust-lang/rust-clippy/pull/8441) +* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap + [#8232](https://github.com/rust-lang/rust-clippy/pull/8232) +* [`default_trait_access`]: Now allows `Default::default` in update expressions + [#8433](https://github.com/rust-lang/rust-clippy/pull/8433) + +### Suggestion Fixes/Improvements + +* [`redundant_slicing`]: Fixed suggestion for a method calls + [#8218](https://github.com/rust-lang/rust-clippy/pull/8218) +* [`map_flatten`]: Long suggestions will now be split up into two help messages + [#8520](https://github.com/rust-lang/rust-clippy/pull/8520) +* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets + [#8543](https://github.com/rust-lang/rust-clippy/pull/8543) +* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path + [#8462](https://github.com/rust-lang/rust-clippy/pull/8462) +* [`search_is_some`]: More suggestions are now `MachineApplicable` + [#8536](https://github.com/rust-lang/rust-clippy/pull/8536) + +### Documentation Improvements + +* [`new_without_default`]: Document `pub` requirement for the struct and fields + [#8429](https://github.com/rust-lang/rust-clippy/pull/8429) ## Rust 1.60 @@ -3182,6 +3283,7 @@ Released 2018-09-13 [`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern [`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops [`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type [`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock [`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref [`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask @@ -3198,6 +3300,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons @@ -3262,6 +3365,7 @@ Released 2018-09-13 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop @@ -3314,6 +3418,7 @@ Released 2018-09-13 [`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect [`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into [`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10 @@ -3356,6 +3461,7 @@ Released 2018-09-13 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements [`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect [`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count @@ -3372,6 +3478,7 @@ Released 2018-09-13 [`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays [`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups [`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file [`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays [`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value [`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty @@ -3472,6 +3579,7 @@ Released 2018-09-13 [`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes [`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match [`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref +[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take [`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value [`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark [`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop @@ -3527,6 +3635,7 @@ Released 2018-09-13 [`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq [`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast [`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 [`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 @@ -3627,6 +3736,7 @@ Released 2018-09-13 [`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr [`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts [`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace [`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex [`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref [`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err @@ -3650,6 +3760,7 @@ Released 2018-09-13 [`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation +[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml index 81faa5fe5e14..2cfbcea5034e 100644 --- a/clippy_dev/Cargo.toml +++ b/clippy_dev/Cargo.toml @@ -4,6 +4,7 @@ version = "0.0.1" edition = "2021" [dependencies] +aho-corasick = "0.7" clap = "2.33" indoc = "1.0" itertools = "0.10.1" diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs index 9c6d754b400f..81e807cf10c7 100644 --- a/clippy_dev/src/lib.rs +++ b/clippy_dev/src/lib.rs @@ -1,3 +1,4 @@ +#![feature(let_chains)] #![feature(let_else)] #![feature(once_cell)] #![feature(rustc_private)] diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index b1fe35a0243f..ebf8f38d4906 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -19,9 +19,9 @@ fn main() { if matches.is_present("print-only") { update_lints::print_lints(); } else if matches.is_present("check") { - update_lints::run(update_lints::UpdateMode::Check); + update_lints::update(update_lints::UpdateMode::Check); } else { - update_lints::run(update_lints::UpdateMode::Change); + update_lints::update(update_lints::UpdateMode::Change); } }, ("new_lint", Some(matches)) => { @@ -31,18 +31,36 @@ fn main() { matches.value_of("category"), matches.is_present("msrv"), ) { - Ok(_) => update_lints::run(update_lints::UpdateMode::Change), + Ok(_) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {}", e), } }, ("setup", Some(sub_command)) => match sub_command.subcommand() { - ("intellij", Some(matches)) => setup::intellij::setup_rustc_src( - matches - .value_of("rustc-repo-path") - .expect("this field is mandatory and therefore always valid"), - ), - ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")), - ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")), + ("intellij", Some(matches)) => { + if matches.is_present("remove") { + setup::intellij::remove_rustc_src(); + } else { + setup::intellij::setup_rustc_src( + matches + .value_of("rustc-repo-path") + .expect("this field is mandatory and therefore always valid"), + ); + } + }, + ("git-hook", Some(matches)) => { + if matches.is_present("remove") { + setup::git_hook::remove_hook(); + } else { + setup::git_hook::install_hook(matches.is_present("force-override")); + } + }, + ("vscode-tasks", Some(matches)) => { + if matches.is_present("remove") { + setup::vscode::remove_tasks(); + } else { + setup::vscode::install_tasks(matches.is_present("force-override")); + } + }, _ => {}, }, ("remove", Some(sub_command)) => match sub_command.subcommand() { @@ -60,6 +78,12 @@ fn main() { let path = matches.value_of("path").unwrap(); lint::run(path); }, + ("rename_lint", Some(matches)) => { + let old_name = matches.value_of("old_name").unwrap(); + let new_name = matches.value_of("new_name").unwrap_or(old_name); + let uplift = matches.is_present("uplift"); + update_lints::rename(old_name, new_name, uplift); + }, _ => {}, } } @@ -167,6 +191,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .subcommand( SubCommand::with_name("intellij") .about("Alter dependencies so Intellij Rust can find rustc internals") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the dependencies added with 'cargo dev setup intellij'") + .required(false), + ) .arg( Arg::with_name("rustc-repo-path") .long("repo-path") @@ -174,12 +204,19 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .help("The path to a rustc repo that will be used for setting the dependencies") .takes_value(true) .value_name("path") + .conflicts_with("remove") .required(true), ), ) .subcommand( SubCommand::with_name("git-hook") .about("Add a pre-commit git hook that formats your code to make it look pretty") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'") + .required(false), + ) .arg( Arg::with_name("force-override") .long("force-override") @@ -191,6 +228,12 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .subcommand( SubCommand::with_name("vscode-tasks") .about("Add several tasks to vscode for formatting, validation and testing") + .arg( + Arg::with_name("remove") + .long("remove") + .help("Remove the tasks added with 'cargo dev setup vscode-tasks'") + .required(false), + ) .arg( Arg::with_name("force-override") .long("force-override") @@ -242,5 +285,26 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { .help("The path to a file or package directory to lint"), ), ) + .subcommand( + SubCommand::with_name("rename_lint") + .about("Renames the given lint") + .arg( + Arg::with_name("old_name") + .index(1) + .required(true) + .help("The name of the lint to rename"), + ) + .arg( + Arg::with_name("new_name") + .index(2) + .required_unless("uplift") + .help("The new name of the lint"), + ) + .arg( + Arg::with_name("uplift") + .long("uplift") + .help("This lint will be uplifted into rustc"), + ), + ) .get_matches() } diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 7a3fd1317619..10f67d301f88 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -1,5 +1,6 @@ use crate::clippy_project_root; use indoc::indoc; +use std::fmt::Write as _; use std::fs::{self, OpenOptions}; use std::io::prelude::*; use std::io::{self, ErrorKind}; @@ -232,7 +233,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { ) }); - result.push_str(&format!( + let _ = write!( + result, indoc! {r#" declare_clippy_lint! {{ /// ### What it does @@ -256,7 +258,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { version = version, name_upper = name_upper, category = category, - )); + ); result.push_str(&if enable_msrv { format!( diff --git a/clippy_dev/src/setup/mod.rs b/clippy_dev/src/setup/mod.rs index a1e4dd103b88..f691ae4fa45d 100644 --- a/clippy_dev/src/setup/mod.rs +++ b/clippy_dev/src/setup/mod.rs @@ -7,7 +7,7 @@ use std::path::Path; const CLIPPY_DEV_DIR: &str = "clippy_dev"; /// This function verifies that the tool is being executed in the clippy directory. -/// This is useful to ensure that setups only modify Clippys resources. The verification +/// This is useful to ensure that setups only modify Clippy's resources. The verification /// is done by checking that `clippy_dev` is a sub directory of the current directory. /// /// It will print an error message and return `false` if the directory could not be @@ -17,7 +17,7 @@ fn verify_inside_clippy_dir() -> bool { if path.exists() && path.is_dir() { true } else { - eprintln!("error: unable to verify that the working directory is clippys directory"); + eprintln!("error: unable to verify that the working directory is clippy's directory"); false } } diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 59db51fbfac5..1a6a4336da27 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -1,11 +1,13 @@ -use core::fmt::Write; +use aho_corasick::AhoCorasickBuilder; +use core::fmt::Write as _; use itertools::Itertools; use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fs; -use std::path::Path; -use walkdir::WalkDir; +use std::io::{self, Read as _, Seek as _, Write as _}; +use std::path::{Path, PathBuf}; +use walkdir::{DirEntry, WalkDir}; use crate::clippy_project_root; @@ -30,12 +32,19 @@ pub enum UpdateMode { /// # Panics /// /// Panics if a file path could not read from or then written to -#[allow(clippy::too_many_lines)] -pub fn run(update_mode: UpdateMode) { - let (lints, deprecated_lints) = gather_all(); +pub fn update(update_mode: UpdateMode) { + let (lints, deprecated_lints, renamed_lints) = gather_all(); + generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints); +} - let internal_lints = Lint::internal_lints(&lints); - let usable_lints = Lint::usable_lints(&lints); +fn generate_lint_files( + update_mode: UpdateMode, + lints: &[Lint], + deprecated_lints: &[DeprecatedLint], + renamed_lints: &[RenamedLint], +) { + let internal_lints = Lint::internal_lints(lints); + let usable_lints = Lint::usable_lints(lints); let mut sorted_usable_lints = usable_lints.clone(); sorted_usable_lints.sort_by_key(|lint| lint.name.clone()); @@ -87,7 +96,7 @@ pub fn run(update_mode: UpdateMode) { process_file( "clippy_lints/src/lib.deprecated.rs", update_mode, - &gen_deprecated(&deprecated_lints), + &gen_deprecated(deprecated_lints), ); let all_group_lints = usable_lints.iter().filter(|l| { @@ -107,10 +116,16 @@ pub fn run(update_mode: UpdateMode) { &content, ); } + + let content = gen_deprecated_lints_test(deprecated_lints); + process_file("tests/ui/deprecated.rs", update_mode, &content); + + let content = gen_renamed_lints_test(renamed_lints); + process_file("tests/ui/rename.rs", update_mode, &content); } pub fn print_lints() { - let (lint_list, _) = gather_all(); + let (lint_list, _, _) = gather_all(); let usable_lints = Lint::usable_lints(&lint_list); let usable_lint_count = usable_lints.len(); let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter()); @@ -128,6 +143,209 @@ pub fn print_lints() { println!("there are {} lints", usable_lint_count); } +/// Runs the `rename_lint` command. +/// +/// This does the following: +/// * Adds an entry to `renamed_lints.rs`. +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`). +/// * Renames the lint struct to the new name. +/// * Renames the module containing the lint struct to the new name if it shares a name with the +/// lint. +/// +/// # Panics +/// Panics for the following conditions: +/// * If a file path could not read from or then written to +/// * If either lint name has a prefix +/// * If `old_name` doesn't name an existing lint. +/// * If `old_name` names a deprecated or renamed lint. +#[allow(clippy::too_many_lines)] +pub fn rename(old_name: &str, new_name: &str, uplift: bool) { + if let Some((prefix, _)) = old_name.split_once("::") { + panic!("`{}` should not contain the `{}` prefix", old_name, prefix); + } + if let Some((prefix, _)) = new_name.split_once("::") { + panic!("`{}` should not contain the `{}` prefix", new_name, prefix); + } + + let (mut lints, deprecated_lints, mut renamed_lints) = gather_all(); + let mut old_lint_index = None; + let mut found_new_name = false; + for (i, lint) in lints.iter().enumerate() { + if lint.name == old_name { + old_lint_index = Some(i); + } else if lint.name == new_name { + found_new_name = true; + } + } + let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name)); + + let lint = RenamedLint { + old_name: format!("clippy::{}", old_name), + new_name: if uplift { + new_name.into() + } else { + format!("clippy::{}", new_name) + }, + }; + + // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in + // case. + assert!( + !renamed_lints.iter().any(|l| lint.old_name == l.old_name), + "`{}` has already been renamed", + old_name + ); + assert!( + !deprecated_lints.iter().any(|l| lint.old_name == l.name), + "`{}` has already been deprecated", + old_name + ); + + // Update all lint level attributes. (`clippy::lint_name`) + for file in WalkDir::new(clippy_project_root()) + .into_iter() + .map(Result::unwrap) + .filter(|f| { + let name = f.path().file_name(); + let ext = f.path().extension(); + (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed"))) + && name != Some(OsStr::new("rename.rs")) + && name != Some(OsStr::new("renamed_lints.rs")) + }) + { + rewrite_file(file.path(), |s| { + replace_ident_like(s, &[(&lint.old_name, &lint.new_name)]) + }); + } + + renamed_lints.push(lint); + renamed_lints.sort_by(|lhs, rhs| { + lhs.new_name + .starts_with("clippy::") + .cmp(&rhs.new_name.starts_with("clippy::")) + .reverse() + .then_with(|| lhs.old_name.cmp(&rhs.old_name)) + }); + + write_file( + Path::new("clippy_lints/src/renamed_lints.rs"), + &gen_renamed_lints_list(&renamed_lints), + ); + + if uplift { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.", + old_name + ); + } else if found_new_name { + write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints)); + println!( + "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.", + new_name + ); + } else { + // Rename the lint struct and source files sharing a name with the lint. + let lint = &mut lints[old_lint_index]; + let old_name_upper = old_name.to_uppercase(); + let new_name_upper = new_name.to_uppercase(); + lint.name = new_name.into(); + + // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist. + if try_rename_file( + Path::new(&format!("tests/ui/{}.rs", old_name)), + Path::new(&format!("tests/ui/{}.rs", new_name)), + ) { + try_rename_file( + Path::new(&format!("tests/ui/{}.stderr", old_name)), + Path::new(&format!("tests/ui/{}.stderr", new_name)), + ); + try_rename_file( + Path::new(&format!("tests/ui/{}.fixed", old_name)), + Path::new(&format!("tests/ui/{}.fixed", new_name)), + ); + } + + // Try to rename the file containing the lint if the file name matches the lint's name. + let replacements; + let replacements = if lint.module == old_name + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}.rs", old_name)), + Path::new(&format!("clippy_lints/src/{}.rs", new_name)), + ) { + // Edit the module name in the lint list. Note there could be multiple lints. + for lint in lints.iter_mut().filter(|l| l.module == old_name) { + lint.module = new_name.into(); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else if !lint.module.contains("::") + // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs` + && try_rename_file( + Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)), + Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)), + ) + { + // Edit the module name in the lint list. Note there could be multiple lints, or none. + let renamed_mod = format!("{}::{}", lint.module, old_name); + for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) { + lint.module = format!("{}::{}", lint.module, new_name); + } + replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)]; + replacements.as_slice() + } else { + replacements = [(&*old_name_upper, &*new_name_upper), ("", "")]; + &replacements[0..1] + }; + + // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being + // renamed. + for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) { + rewrite_file(file.path(), |s| replace_ident_like(s, replacements)); + } + + generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints); + println!("{} has been successfully renamed", old_name); + } + + println!("note: `cargo uitest` still needs to be run to update the test results"); +} + +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there +/// were no replacements. +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option { + fn is_ident_char(c: u8) -> bool { + matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') + } + + let searcher = AhoCorasickBuilder::new() + .dfa(true) + .match_kind(aho_corasick::MatchKind::LeftmostLongest) + .build_with_size::(replacements.iter().map(|&(x, _)| x.as_bytes())) + .unwrap(); + + let mut result = String::with_capacity(contents.len() + 1024); + let mut pos = 0; + let mut edited = false; + for m in searcher.find_iter(contents) { + let (old, new) = replacements[m.pattern()]; + result.push_str(&contents[pos..m.start()]); + result.push_str( + if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0)) + && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0)) + { + edited = true; + new + } else { + old + }, + ); + pos = m.end(); + } + result.push_str(&contents[pos..]); + edited.then(|| result) +} + fn round_to_fifty(count: usize) -> usize { count / 50 * 50 } @@ -210,6 +428,19 @@ impl DeprecatedLint { } } +struct RenamedLint { + old_name: String, + new_name: String, +} +impl RenamedLint { + fn new(old_name: &str, new_name: &str) -> Self { + Self { + old_name: remove_line_splices(old_name), + new_name: remove_line_splices(new_name), + } + } +} + /// Generates the code for registering a group fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator) -> String { let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect(); @@ -217,12 +448,13 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator String { let mut output = GENERATED_FILE_COMMENT.to_string(); output.push_str("{\n"); for lint in lints { - output.push_str(&format!( + let _ = write!( + output, concat!( " store.register_removed(\n", " \"clippy::{}\",\n", @@ -243,7 +476,7 @@ fn gen_deprecated(lints: &[DeprecatedLint]) -> String { " );\n" ), lint.name, lint.reason, - )); + ); } output.push_str("}\n"); @@ -269,25 +502,62 @@ fn gen_register_lint_list<'a>( if !is_public { output.push_str(" #[cfg(feature = \"internal\")]\n"); } - output.push_str(&format!(" {}::{},\n", module_name, lint_name)); + let _ = writeln!(output, " {}::{},", module_name, lint_name); } output.push_str("])\n"); output } +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String { + let mut res: String = GENERATED_FILE_COMMENT.into(); + for lint in lints { + writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap(); + } + res.push_str("\nfn main() {}\n"); + res +} + +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String { + let mut seen_lints = HashSet::new(); + let mut res: String = GENERATED_FILE_COMMENT.into(); + res.push_str("// run-rustfix\n\n"); + for lint in lints { + if seen_lints.insert(&lint.new_name) { + writeln!(res, "#![allow({})]", lint.new_name).unwrap(); + } + } + seen_lints.clear(); + for lint in lints { + if seen_lints.insert(&lint.old_name) { + writeln!(res, "#![warn({})]", lint.old_name).unwrap(); + } + } + res.push_str("\nfn main() {}\n"); + res +} + +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String { + const HEADER: &str = "\ + // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\ + #[rustfmt::skip]\n\ + pub static RENAMED_LINTS: &[(&str, &str)] = &[\n"; + + let mut res = String::from(HEADER); + for lint in lints { + writeln!(res, " (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap(); + } + res.push_str("];\n"); + res +} + /// Gathers all lints defined in `clippy_lints/src` -fn gather_all() -> (Vec, Vec) { +fn gather_all() -> (Vec, Vec, Vec) { let mut lints = Vec::with_capacity(1000); let mut deprecated_lints = Vec::with_capacity(50); - let root_path = clippy_project_root().join("clippy_lints/src"); + let mut renamed_lints = Vec::with_capacity(50); - for (rel_path, file) in WalkDir::new(&root_path) - .into_iter() - .map(Result::unwrap) - .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) - .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) - { + for (rel_path, file) in clippy_lints_src_files() { let path = file.path(); let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e)); @@ -305,13 +575,21 @@ fn gather_all() -> (Vec, Vec) { module.strip_suffix(".rs").unwrap_or(&module) }; - if module == "deprecated_lints" { - parse_deprecated_contents(&contents, &mut deprecated_lints); - } else { - parse_contents(&contents, module, &mut lints); + match module { + "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints), + "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints), + _ => parse_contents(&contents, module, &mut lints), } } - (lints, deprecated_lints) + (lints, deprecated_lints, renamed_lints) +} + +fn clippy_lints_src_files() -> impl Iterator { + let root_path = clippy_project_root().join("clippy_lints/src"); + let iter = WalkDir::new(&root_path).into_iter(); + iter.map(Result::unwrap) + .filter(|f| f.path().extension() == Some(OsStr::new("rs"))) + .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f)) } macro_rules! match_tokens { @@ -394,6 +672,25 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec) { } } +fn parse_renamed_contents(contents: &str, lints: &mut Vec) { + for line in contents.lines() { + let mut offset = 0usize; + let mut iter = tokenize(line).map(|t| { + let range = offset..offset + t.len; + offset = range.end; + (t.kind, &line[range]) + }); + let (old_name, new_name) = match_tokens!( + iter, + // ("old_name", + Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma + // "new_name"), + Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma + ); + lints.push(RenamedLint::new(old_name, new_name)); + } +} + /// Removes the line splices and surrounding quotes from a string literal fn remove_line_splices(s: &str) -> String { let s = s @@ -462,6 +759,52 @@ fn replace_region_in_text<'a>( Ok(res) } +fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { + match fs::OpenOptions::new().create_new(true).write(true).open(new_name) { + Ok(file) => drop(file), + Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, + Err(e) => panic_file(e, new_name, "create"), + }; + match fs::rename(old_name, new_name) { + Ok(()) => true, + Err(e) => { + drop(fs::remove_file(new_name)); + if e.kind() == io::ErrorKind::NotFound { + false + } else { + panic_file(e, old_name, "rename"); + } + }, + } +} + +#[allow(clippy::needless_pass_by_value)] +fn panic_file(error: io::Error, name: &Path, action: &str) -> ! { + panic!("failed to {} file `{}`: {}", action, name.display(), error) +} + +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option) { + let mut file = fs::OpenOptions::new() + .write(true) + .read(true) + .open(path) + .unwrap_or_else(|e| panic_file(e, path, "open")); + let mut buf = String::new(); + file.read_to_string(&mut buf) + .unwrap_or_else(|e| panic_file(e, path, "read")); + if let Some(new_contents) = f(&buf) { + file.rewind().unwrap_or_else(|e| panic_file(e, path, "write")); + file.write_all(new_contents.as_bytes()) + .unwrap_or_else(|e| panic_file(e, path, "write")); + file.set_len(new_contents.len() as u64) + .unwrap_or_else(|e| panic_file(e, path, "write")); + } +} + +fn write_file(path: &Path, contents: &str) { + fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write")); +} + #[cfg(test)] mod tests { use super::*; diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index d0b03c0a9c27..8b0e11cb802e 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -335,9 +335,6 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { } if let Some(lint_list) = &attr.meta_item_list() { if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) { - // permit `unused_imports`, `deprecated`, `unreachable_pub`, - // `clippy::wildcard_imports`, and `clippy::enum_glob_use` for `use` items - // and `unused_imports` for `extern crate` items with `macro_use` for lint in lint_list { match item.kind { ItemKind::Use(..) => { @@ -345,10 +342,12 @@ impl<'tcx> LateLintPass<'tcx> for Attributes { || is_word(lint, sym::deprecated) || is_word(lint, sym!(unreachable_pub)) || is_word(lint, sym!(unused)) - || extract_clippy_lint(lint) - .map_or(false, |s| s.as_str() == "wildcard_imports") - || extract_clippy_lint(lint) - .map_or(false, |s| s.as_str() == "enum_glob_use") + || extract_clippy_lint(lint).map_or(false, |s| { + matches!( + s.as_str(), + "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate", + ) + }) { return; } @@ -594,6 +593,10 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It }; if attr.style == AttrStyle::Outer { + if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args + && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) { + return; + } if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { return; } diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 4592ca727488..5b7c4591504e 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,12 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{match_def_path, paths}; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; -use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind}; +use rustc_hir::{def::Res, AsyncGeneratorKind, Body, BodyId, GeneratorKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::GeneratorInteriorTypeCause; -use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; +use crate::utils::conf::DisallowedType; + declare_clippy_lint! { /// ### What it does /// Checks for calls to await while holding a non-async-aware MutexGuard. @@ -127,9 +130,75 @@ declare_clippy_lint! { "inside an async function, holding a `RefCell` ref while calling `await`" } -declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]); +declare_clippy_lint! { + /// ### What it does + /// Allows users to configure types which should not be held across `await` + /// suspension points. + /// + /// ### Why is this bad? + /// There are some types which are perfectly "safe" to be used concurrently + /// from a memory access perspective but will cause bugs at runtime if they + /// are held in such a way. + /// + /// ### Known problems + /// + /// ### Example + /// + /// ```toml + /// await-holding-invalid-types = [ + /// # You can specify a type name + /// "CustomLockType", + /// # You can (optionally) specify a reason + /// { path = "OtherCustomLockType", reason = "Relies on a thread local" } + /// ] + /// ``` + /// + /// ```rust + /// # async fn baz() {} + /// struct CustomLockType; + /// struct OtherCustomLockType; + /// async fn foo() { + /// let _x = CustomLockType; + /// let _y = OtherCustomLockType; + /// baz().await; // Lint violation + /// } + /// ``` + #[clippy::version = "1.49.0"] + pub AWAIT_HOLDING_INVALID_TYPE, + suspicious, + "holding a type across an await point which is not allowed to be held as per the configuration" +} + +impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]); + +#[derive(Debug)] +pub struct AwaitHolding { + conf_invalid_types: Vec, + def_ids: FxHashMap, +} + +impl AwaitHolding { + pub(crate) fn new(conf_invalid_types: Vec) -> Self { + Self { + conf_invalid_types, + def_ids: FxHashMap::default(), + } + } +} impl LateLintPass<'_> for AwaitHolding { + fn check_crate(&mut self, cx: &LateContext<'_>) { + for conf in &self.conf_invalid_types { + let path = match conf { + DisallowedType::Simple(path) | DisallowedType::WithReason { path, .. } => path, + }; + let segs: Vec<_> = path.split("::").collect(); + if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) { + self.def_ids.insert(id, conf.clone()); + } + } + } + fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) { use AsyncGeneratorKind::{Block, Closure, Fn}; if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind { @@ -137,7 +206,7 @@ impl LateLintPass<'_> for AwaitHolding { hir_id: body.value.hir_id, }; let typeck_results = cx.tcx.typeck_body(body_id); - check_interior_types( + self.check_interior_types( cx, typeck_results.generator_interior_types.as_ref().skip_binder(), body.value.span, @@ -146,46 +215,68 @@ impl LateLintPass<'_> for AwaitHolding { } } -fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { - for ty_cause in ty_causes { - if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { - if is_mutex_guard(cx, adt.did()) { - span_lint_and_then( - cx, - AWAIT_HOLDING_LOCK, - ty_cause.span, - "this `MutexGuard` is held across an `await` point", - |diag| { - diag.help( - "consider using an async-aware `Mutex` type or ensuring the \ +impl AwaitHolding { + fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) { + for ty_cause in ty_causes { + if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() { + if is_mutex_guard(cx, adt.did()) { + span_lint_and_then( + cx, + AWAIT_HOLDING_LOCK, + ty_cause.span, + "this `MutexGuard` is held across an `await` point", + |diag| { + diag.help( + "consider using an async-aware `Mutex` type or ensuring the \ `MutexGuard` is dropped before calling await", - ); - diag.span_note( - ty_cause.scope_span.unwrap_or(span), - "these are all the `await` points this lock is held through", - ); - }, - ); - } - if is_refcell_ref(cx, adt.did()) { - span_lint_and_then( - cx, - AWAIT_HOLDING_REFCELL_REF, - ty_cause.span, - "this `RefCell` reference is held across an `await` point", - |diag| { - diag.help("ensure the reference is dropped before calling `await`"); - diag.span_note( - ty_cause.scope_span.unwrap_or(span), - "these are all the `await` points this reference is held through", - ); - }, - ); + ); + diag.span_note( + ty_cause.scope_span.unwrap_or(span), + "these are all the `await` points this lock is held through", + ); + }, + ); + } else if is_refcell_ref(cx, adt.did()) { + span_lint_and_then( + cx, + AWAIT_HOLDING_REFCELL_REF, + ty_cause.span, + "this `RefCell` reference is held across an `await` point", + |diag| { + diag.help("ensure the reference is dropped before calling `await`"); + diag.span_note( + ty_cause.scope_span.unwrap_or(span), + "these are all the `await` points this reference is held through", + ); + }, + ); + } else if let Some(disallowed) = self.def_ids.get(&adt.did()) { + emit_invalid_type(cx, ty_cause.span, disallowed); + } } } } } +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedType) { + let (type_name, reason) = match disallowed { + DisallowedType::Simple(path) => (path, &None), + DisallowedType::WithReason { path, reason } => (path, reason), + }; + + span_lint_and_then( + cx, + AWAIT_HOLDING_INVALID_TYPE, + span, + &format!("`{type_name}` may not be held across an `await` point per `clippy.toml`",), + |diag| { + if let Some(reason) = reason { + diag.note(reason.clone()); + } + }, + ); +} + fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool { match_def_path(cx, def_id, &paths::MUTEX_GUARD) || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD) diff --git a/clippy_lints/src/bytes_count_to_len.rs b/clippy_lints/src/bytes_count_to_len.rs new file mode 100644 index 000000000000..d70dbf5b2390 --- /dev/null +++ b/clippy_lints/src/bytes_count_to_len.rs @@ -0,0 +1,70 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// It checks for `str::bytes().count()` and suggests replacing it with + /// `str::len()`. + /// + /// ### Why is this bad? + /// `str::bytes().count()` is longer and may not be as performant as using + /// `str::len()`. + /// + /// ### Example + /// ```rust + /// "hello".bytes().count(); + /// String::from("hello").bytes().count(); + /// ``` + /// Use instead: + /// ```rust + /// "hello".len(); + /// String::from("hello").len(); + /// ``` + #[clippy::version = "1.62.0"] + pub BYTES_COUNT_TO_LEN, + complexity, + "Using `bytes().count()` when `len()` performs the same functionality" +} + +declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]); + +impl<'tcx> LateLintPass<'tcx> for BytesCountToLen { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if_chain! { + if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind; + if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); + if match_def_path(cx, expr_def_id, &paths::ITER_COUNT); + + if let [bytes_expr] = &**expr_args; + if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind; + if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id); + if match_def_path(cx, bytes_def_id, &paths::STR_BYTES); + + if let [str_expr] = &**bytes_args; + let ty = cx.typeck_results().expr_ty(str_expr).peel_refs(); + + if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str; + then { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + BYTES_COUNT_TO_LEN, + expr.span, + "using long and hard to read `.bytes().count()`", + "consider calling `.len()` instead", + format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)), + applicability + ); + } + }; + } +} diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index a8f9c189adec..7af200708ff0 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -42,8 +42,8 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind; if (2..=6).contains(&ext_literal.as_str().len()); if ext_literal.as_str().starts_with('.'); - if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_digit(10)) - || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_digit(10)); + if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit()) + || ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit()); then { let mut ty = ctx.typeck_results().expr_ty(obj); ty = match ty.kind() { diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index 421bd6f53f71..64f87c80f8d1 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -29,21 +29,19 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)), ExprKind::Binary(op, left, right) => match op.node { BinOpKind::Div => { - apply_reductions(cx, nbits, left, signed) - - (if signed { - 0 // let's be conservative here - } else { - // by dividing by 1, we remove 0 bits, etc. - get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) - }) + apply_reductions(cx, nbits, left, signed).saturating_sub(if signed { + // let's be conservative here + 0 + } else { + // by dividing by 1, we remove 0 bits, etc. + get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1)) + }) }, BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right) .unwrap_or(u64::max_value()) .min(apply_reductions(cx, nbits, left, signed)), - BinOpKind::Shr => { - apply_reductions(cx, nbits, left, signed) - - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high")) - }, + BinOpKind::Shr => apply_reductions(cx, nbits, left, signed) + .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))), _ => nbits, }, ExprKind::MethodCall(method, [left, right], _) => { diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 3608c1654d5c..2238668abca7 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,4 +1,4 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt}; +use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; @@ -8,6 +8,63 @@ use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option) { + // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist + if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { + return; + } + + // if this cast is the child of another cast expression then don't emit something for it, the full + // chain will be analyzed + if is_child_of_cast(cx, expr) { + return; + } + + if let Some(CastChainInfo { + left_cast, + start_ty, + end_ty, + }) = expr_cast_chain_tys(cx, expr) + { + if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) { + let from_size = from_layout.size.bytes(); + let to_size = to_layout.size.bytes(); + if from_size != to_size && from_size != 0 && to_size != 0 { + span_lint_and_then( + cx, + CAST_SLICE_DIFFERENT_SIZES, + expr.span, + &format!( + "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", + start_ty.ty, from_size, end_ty.ty, to_size, + ), + |diag| { + let ptr_snippet = source::snippet(cx, left_cast.span, ".."); + + let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl { + Mutability::Mut => ("_mut", "mut"), + Mutability::Not => ("", "const"), + }; + let sugg = format!( + "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)", + // get just the ty from the TypeAndMut so that the printed type isn't something like `mut + // T`, extract just the `T` + end_ty.ty + ); + + diag.span_suggestion( + expr.span, + &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), + sugg, + rustc_errors::Applicability::HasPlaceholders, + ); + }, + ); + } + } + } +} + fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let map = cx.tcx.hir(); if_chain! { @@ -33,59 +90,6 @@ fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option) { - // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { - return; - } - - // if this cast is the child of another cast expression then don't emit something for it, the full - // chain will be analyzed - if is_child_of_cast(cx, expr) { - return; - } - - if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) { - if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) { - let from_size = from_layout.size.bytes(); - let to_size = to_layout.size.bytes(); - if from_size != to_size && from_size != 0 && to_size != 0 { - span_lint_and_then( - cx, - CAST_SLICE_DIFFERENT_SIZES, - expr.span, - &format!( - "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count", - from_slice_ty, from_size, to_slice_ty, to_size, - ), - |diag| { - let cast_expr = match expr.kind { - ExprKind::Cast(cast_expr, ..) => cast_expr, - _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"), - }; - let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap(); - - let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl { - Mutability::Mut => ("_mut", "mut"), - Mutability::Not => ("", "const"), - }; - let sugg = format!( - "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)" - ); - - diag.span_suggestion( - expr.span, - &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"), - sugg, - rustc_errors::Applicability::HasPlaceholders, - ); - }, - ); - } - } - } -} - /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if /// the type is one of those slices fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option> { @@ -98,18 +102,40 @@ fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option> { } } -/// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts +struct CastChainInfo<'tcx> { + /// The left most part of the cast chain, or in other words, the first cast in the chain + /// Used for diagnostics + left_cast: &'tcx Expr<'tcx>, + /// The starting type of the cast chain + start_ty: TypeAndMut<'tcx>, + /// The final type of the cast chain + end_ty: TypeAndMut<'tcx>, +} + +/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final +/// ptr U if the expression is composed of casts. /// Returns None if the expr is not a Cast -fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> { +fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option> { if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind { let cast_to = cx.typeck_results().expr_ty(expr); let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; - if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) { - Some((inner_from_ty, to_slice_ty)) + + // If the expression that makes up the source of this cast is itself a cast, recursively + // call `expr_cast_chain_tys` and update the end type with the final tartet type. + // Otherwise, this cast is not immediately nested, just construct the info for this cast + if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) { + Some(CastChainInfo { + end_ty: to_slice_ty, + ..prev_info + }) } else { let cast_from = cx.typeck_results().expr_ty(cast_expr); let from_slice_ty = get_raw_slice_ty_mut(cast_from)?; - Some((from_slice_ty, to_slice_ty)) + Some(CastChainInfo { + left_cast: cast_expr, + start_ty: from_slice_ty, + end_ty: to_slice_ty, + }) } } else { None diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 55c1f085657b..b58252dcb942 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -270,7 +270,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Casting a function pointer to an integer can have surprising results and can occur - /// accidentally if parantheses are omitted from a function call. If you aren't doing anything + /// accidentally if parentheses are omitted from a function call. If you aren't doing anything /// low-level with function pointers then you can opt-out of casting functions to integers in /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function /// pointer casts in your code. @@ -487,6 +487,7 @@ declare_clippy_lint! { /// let y: u32 = x.abs() as u32; /// ``` /// Use instead: + /// ```rust /// let x: i32 = -42; /// let y: u32 = x.unsigned_abs(); /// ``` diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index eae2723a7dac..3227e6e86af4 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -13,13 +13,14 @@ //! This lint is **warn** by default use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet_block, snippet_block_with_applicability}; +use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; use rustc_ast::ast; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -102,7 +103,7 @@ impl EarlyLintPass for CollapsibleIf { fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) { if let ast::ExprKind::If(check, then, else_) = &expr.kind { if let Some(else_) = else_ { - check_collapsible_maybe_if_let(cx, else_); + check_collapsible_maybe_if_let(cx, then.span, else_); } else if let ast::ExprKind::Let(..) = check.kind { // Prevent triggering on `if let a = b { if c { .. } }`. } else { @@ -119,7 +120,7 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool { trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*") } -fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { +fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { if_chain! { if let ast::ExprKind::Block(ref block, _) = else_.kind; if !block_starts_with_comment(cx, block); @@ -128,6 +129,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { if !else_.span.from_expansion(); if let ast::ExprKind::If(..) = else_.kind; then { + // Prevent "elseif" + // Check that the "else" is followed by whitespace + let up_to_else = then_span.between(block.span); + let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false }; + let mut applicability = Applicability::MachineApplicable; span_lint_and_sugg( cx, @@ -135,7 +141,11 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, else_: &ast::Expr) { block.span, "this `else { if .. }` block can be collapsed", "collapse nested if block", - snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability).into_owned(), + format!( + "{}{}", + if requires_space { " " } else { "" }, + snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability) + ), applicability, ); } diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 503cef76775e..b3fd8af4730d 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown { lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span); } }, - hir::ItemKind::Impl(ref impl_) => { + hir::ItemKind::Impl(impl_) => { self.in_trait_impl = impl_.of_trait.is_some(); }, hir::ItemKind::Trait(_, unsafety, ..) => { @@ -621,10 +621,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let filename = FileName::anon_source_code(&code); let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - let fallback_bundle = rustc_errors::fallback_fluent_bundle( - rustc_errors::DEFAULT_LOCALE_RESOURCES, - false - ); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); let emitter = EmitterWriter::new( Box::new(io::sink()), None, @@ -741,7 +739,7 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span) { /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok). /// Plurals are also excluded (`IDs` is ok). fn is_camel_case(s: &str) -> bool { - if s.starts_with(|c: char| c.is_digit(10)) { + if s.starts_with(|c: char| c.is_ascii_digit()) { return false; } diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs index 88c54828da83..25014bfa1a5b 100644 --- a/clippy_lints/src/drop_forget_ref.rs +++ b/clippy_lints/src/drop_forget_ref.rs @@ -180,7 +180,7 @@ const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that imp const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \ Forgetting a copy leaves the original intact"; const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \ - Dropping such a type only extends it's contained lifetimes"; + Dropping such a type only extends its contained lifetimes"; const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \ Forgetting such a type is the same as dropping it"; diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs new file mode 100644 index 000000000000..325ae2356c14 --- /dev/null +++ b/clippy_lints/src/empty_drop.rs @@ -0,0 +1,65 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, peel_blocks}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty `Drop` implementations. + /// + /// ### Why is this bad? + /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are + /// most likely useless. However, an empty `Drop` implementation prevents a type from being + /// destructured, which might be the intention behind adding the implementation as a marker. + /// + /// ### Example + /// ```rust + /// struct S; + /// + /// impl Drop for S { + /// fn drop(&mut self) {} + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct S; + /// ``` + #[clippy::version = "1.61.0"] + pub EMPTY_DROP, + restriction, + "empty `Drop` implementations" +} +declare_lint_pass!(EmptyDrop => [EMPTY_DROP]); + +impl LateLintPass<'_> for EmptyDrop { + fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { + if_chain! { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + items: [child], + .. + }) = item.kind; + if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait(); + if let impl_item_hir = child.id.hir_id(); + if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); + if let ImplItemKind::Fn(_, b) = &impl_item.kind; + if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); + let func_expr = peel_blocks(func_expr); + if let ExprKind::Block(block, _) = func_expr.kind; + if block.stmts.is_empty() && block.expr.is_none(); + then { + span_lint_and_sugg( + cx, + EMPTY_DROP, + item.span, + "empty drop implementation", + "try removing this impl", + String::new(), + Applicability::MaybeIncorrect + ); + } + } + } +} diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs index fdeac8d82557..8430e7b4c827 100644 --- a/clippy_lints/src/empty_structs_with_brackets.rs +++ b/clippy_lints/src/empty_structs_with_brackets.rs @@ -66,7 +66,7 @@ fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Spa } // there might still be field declarations hidden from the AST - // (conditionaly compiled code using #[cfg(..)]) + // (conditionally compiled code using #[cfg(..)]) let Some(braces_span_str) = snippet_opt(cx, braces_span) else { return false; diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 845863bd209c..1b19868e4c70 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id}; +use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; @@ -103,6 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { let closure_ty = cx.typeck_results().expr_ty(expr); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::Call(callee, args) = body.value.kind; if let ExprKind::Path(_) = callee.kind; if check_inputs(cx, body.params, args); @@ -125,8 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if_chain! { if let ty::Closure(_, substs) = callee_ty.peel_refs().kind(); if substs.as_closure().kind() == ClosureKind::FnMut; - if get_enclosing_loop_or_closure(cx.tcx, expr).is_some() - || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee)); + if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)); then { // Mutable closure is used after current expr; we cannot consume it. @@ -145,6 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { ); if_chain!( + if !is_adjusted(cx, &body.value); if let ExprKind::MethodCall(path, args, _) = body.value.kind; if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs new file mode 100644 index 000000000000..ee15ae9f59ac --- /dev/null +++ b/clippy_lints/src/format_push_string.rs @@ -0,0 +1,77 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::{match_def_path, paths, peel_hir_expr_refs}; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Detects cases where the result of a `format!` call is + /// appended to an existing `String`. + /// + /// ### Why is this bad? + /// Introduces an extra, avoidable heap allocation. + /// + /// ### Example + /// ```rust + /// let mut s = String::new(); + /// s += &format!("0x{:X}", 1024); + /// s.push_str(&format!("0x{:X}", 1024)); + /// ``` + /// Use instead: + /// ```rust + /// use std::fmt::Write as _; // import without risk of name clashing + /// + /// let mut s = String::new(); + /// let _ = write!(s, "0x{:X}", 1024); + /// ``` + #[clippy::version = "1.61.0"] + pub FORMAT_PUSH_STRING, + perf, + "`format!(..)` appended to existing `String`" +} +declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]); + +fn is_string(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(e).peel_refs(), sym::String) +} +fn is_format(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + if let Some(macro_def_id) = e.span.ctxt().outer_expn_data().macro_def_id { + cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro) + } else { + false + } +} + +impl<'tcx> LateLintPass<'tcx> for FormatPushString { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let arg = match expr.kind { + ExprKind::MethodCall(_, [_, arg], _) => { + if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) && + match_def_path(cx, fn_def_id, &paths::PUSH_STR) { + arg + } else { + return; + } + } + ExprKind::AssignOp(op, left, arg) + if op.node == BinOpKind::Add && is_string(cx, left) => { + arg + }, + _ => return, + }; + let (arg, _) = peel_hir_expr_refs(arg); + if is_format(cx, arg) { + span_lint_and_help( + cx, + FORMAT_PUSH_STRING, + expr.span, + "`format!(..)` appended to existing `String`", + None, + "consider using `write!` to avoid the extra allocation", + ); + } + } +} diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 5462d913fb44..38e943d2eb87 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -20,7 +20,7 @@ use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); - if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { + if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if let Some(attr) = attr { @@ -105,12 +105,7 @@ fn check_needless_must_use( fn_header_span, "this unit-returning function has a `#[must_use]` attribute", |diag| { - diag.span_suggestion( - attr.span, - "remove the attribute", - "", - Applicability::MachineApplicable, - ); + diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable); }, ); } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs index 120fcb2619c7..2e63a1f920d6 100644 --- a/clippy_lints/src/functions/result_unit_err.rs +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -14,7 +14,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use super::RESULT_UNIT_ERR; pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { + if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); if is_public { diff --git a/clippy_lints/src/identity_op.rs b/clippy_lints/src/identity_op.rs index 4d6bef89bea7..40cc5cd4bcf9 100644 --- a/clippy_lints/src/identity_op.rs +++ b/clippy_lints/src/identity_op.rs @@ -1,3 +1,4 @@ +use clippy_utils::get_parent_expr; use clippy_utils::source::snippet; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -22,6 +23,11 @@ declare_clippy_lint! { /// # let x = 1; /// x / 1 + 0 * 1 - 0 | 0; /// ``` + /// + /// ### Known problems + /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to + /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code. + /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724) #[clippy::version = "pre 1.29.0"] pub IDENTITY_OP, complexity, @@ -31,36 +37,66 @@ declare_clippy_lint! { declare_lint_pass!(IdentityOp => [IDENTITY_OP]); impl<'tcx> LateLintPass<'tcx> for IdentityOp { - fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if e.span.from_expansion() { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if expr.span.from_expansion() { return; } - if let ExprKind::Binary(cmp, left, right) = e.kind { - if is_allowed(cx, cmp, left, right) { - return; - } - match cmp.node { - BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { - check(cx, left, 0, e.span, right.span); - check(cx, right, 0, e.span, left.span); - }, - BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span), - BinOpKind::Mul => { - check(cx, left, 1, e.span, right.span); - check(cx, right, 1, e.span, left.span); - }, - BinOpKind::Div => check(cx, right, 1, e.span, left.span), - BinOpKind::BitAnd => { - check(cx, left, -1, e.span, right.span); - check(cx, right, -1, e.span, left.span); - }, - BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span), - _ => (), + if let ExprKind::Binary(cmp, left, right) = &expr.kind { + if !is_allowed(cx, *cmp, left, right) { + match cmp.node { + BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 0, expr.span, right.span); + } + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => { + check(cx, right, 0, expr.span, left.span); + }, + BinOpKind::Mul => { + if reducible_to_right(cx, expr, right) { + check(cx, left, 1, expr.span, right.span); + } + check(cx, right, 1, expr.span, left.span); + }, + BinOpKind::Div => check(cx, right, 1, expr.span, left.span), + BinOpKind::BitAnd => { + if reducible_to_right(cx, expr, right) { + check(cx, left, -1, expr.span, right.span); + } + check(cx, right, -1, expr.span, left.span); + }, + BinOpKind::Rem => { + // Don't call reducible_to_right because N % N is always reducible to 1 + check_remainder(cx, left, right, expr.span, left.span); + }, + _ => (), + } } } } } +/// Checks if `left op ..right` can be actually reduced to `right` +/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// cannot be reduced to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` +/// See #8724 +fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool { + if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind { + is_toplevel_binary(cx, binary) + } else { + true + } +} + +fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool { + if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind { + false + } else { + true + } +} + fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool { // This lint applies to integers !cx.typeck_results().expr_ty(left).peel_refs().is_integral() diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index d5430a8c9175..feb1b1014b18 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -117,7 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { } match item.kind { - ItemKind::Impl(ref impl_) => { + ItemKind::Impl(impl_) => { let mut vis = ImplicitHasherTypeVisitor::new(cx); vis.visit_ty(impl_.self_ty); @@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { ); } }, - ItemKind::Fn(ref sig, ref generics, body_id) => { + ItemKind::Fn(ref sig, generics, body_id) => { let body = cx.tcx.hir().body(body_id); for ty in sig.decl.inputs { diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index c8ec2f451370..14b22d2b50d0 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -7,6 +7,7 @@ use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Symbol; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -89,7 +90,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { let mut fields_snippet = String::new(); let (last_ident, idents) = ordered_fields.split_last().unwrap(); for ident in idents { - fields_snippet.push_str(&format!("{}, ", ident)); + let _ = write!(fields_snippet, "{}, ", ident); } fields_snippet.push_str(&last_ident.to_string()); diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 6b62748ffef2..8a84513b7792 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -116,7 +116,7 @@ fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap LateLintPass<'tcx> for MultipleInherentImpl { fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option { let id = cx.tcx.hir().local_def_id_to_hir_id(id); if let Node::Item(&Item { - kind: ItemKind::Impl(ref impl_item), + kind: ItemKind::Impl(impl_item), span, .. }) = cx.tcx.hir().get(id) diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index 9284e0024099..7e1548531f10 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -49,6 +50,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) + && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..)) { let expr_spans = fields .iter() diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs new file mode 100644 index 000000000000..8bef13c682db --- /dev/null +++ b/clippy_lints/src/large_include_file.rs @@ -0,0 +1,86 @@ +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::is_lint_allowed; +use clippy_utils::macros::root_macro_call_first_node; +use rustc_ast::LitKind; +use rustc_hir::Expr; +use rustc_hir::ExprKind; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the inclusion of large files via `include_bytes!()` + /// and `include_str!()` + /// + /// ### Why is this bad? + /// Including large files can increase the size of the binary + /// + /// ### Example + /// ```rust,ignore + /// let included_str = include_str!("very_large_file.txt"); + /// let included_bytes = include_bytes!("very_large_file.txt"); + /// ``` + /// + /// Instead, you can load the file at runtime: + /// ```rust,ignore + /// use std::fs; + /// + /// let string = fs::read_to_string("very_large_file.txt")?; + /// let bytes = fs::read("very_large_file.txt")?; + /// ``` + #[clippy::version = "1.62.0"] + pub LARGE_INCLUDE_FILE, + restriction, + "including a large file" +} + +pub struct LargeIncludeFile { + max_file_size: u64, +} + +impl LargeIncludeFile { + #[must_use] + pub fn new(max_file_size: u64) -> Self { + Self { max_file_size } + } +} + +impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]); + +impl LateLintPass<'_> for LargeIncludeFile { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { + if_chain! { + if let Some(macro_call) = root_macro_call_first_node(cx, expr); + if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) + || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id); + if let ExprKind::Lit(lit) = &expr.kind; + then { + let len = match &lit.node { + // include_bytes + LitKind::ByteStr(bstr) => bstr.len(), + // include_str + LitKind::Str(sym, _) => sym.as_str().len(), + _ => return, + }; + + if len as u64 <= self.max_file_size { + return; + } + + span_lint_and_note( + cx, + LARGE_INCLUDE_FILE, + expr.span, + "attempted to include a large file", + None, + &format!( + "the configuration allows a maximum size of {} bytes", + self.max_file_size + ), + ); + } + } + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 14ca93b5f3c1..e68718f9fdf9 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -14,6 +14,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(attrs::DEPRECATED_SEMVER), LintId::of(attrs::MISMATCHED_TARGET_OS), LintId::of(attrs::USELESS_ATTRIBUTE), + LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(bit_mask::BAD_BIT_MASK), @@ -23,6 +24,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON), LintId::of(booleans::LOGIC_BUG), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CAST_ABS_TO_UNSIGNED), LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_TRUNCATION), @@ -77,6 +79,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), LintId::of(format_impl::RECURSIVE_FORMAT_IMPL), + LintId::of(format_push_string::FORMAT_PUSH_STRING), LintId::of(formatting::POSSIBLE_MISSING_COMMA), LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING), LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING), @@ -165,6 +168,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::FLAT_MAP_IDENTITY), LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITERATOR_STEP_BY_ZERO), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_COUNT), @@ -182,6 +186,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), + LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::OK_EXPECT), @@ -243,7 +248,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(octal_escapes::OCTAL_ESCAPES), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS), LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), @@ -275,8 +279,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), @@ -306,10 +310,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(uninit_vec::UNINIT_VEC), LintId::of(unit_hash::UNIT_HASH), LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD), + LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_CMP), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), + LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), diff --git a/clippy_lints/src/lib.register_complexity.rs b/clippy_lints/src/lib.register_complexity.rs index 10369a855ae6..6f3c433af31a 100644 --- a/clippy_lints/src/lib.register_complexity.rs +++ b/clippy_lints/src/lib.register_complexity.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![ LintId::of(attrs::DEPRECATED_CFG_ATTR), LintId::of(booleans::NONMINIMAL_BOOL), + LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN), LintId::of(casts::CHAR_LIT_AS_U8), LintId::of(casts::UNNECESSARY_CAST), LintId::of(derivable_impls::DERIVABLE_IMPLS), @@ -45,6 +46,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_IDENTITY), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), + LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::OPTION_AS_REF_DEREF), LintId::of(methods::OPTION_FILTER_MAP), @@ -66,7 +68,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec! LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD), LintId::of(no_effect::NO_EFFECT), LintId::of(no_effect::UNNECESSARY_OPERATION), - LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL), LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL), LintId::of(precedence::PRECEDENCE), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 532590aaa5a3..5768edc50188 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -52,6 +52,7 @@ store.register_lints(&[ attrs::INLINE_ALWAYS, attrs::MISMATCHED_TARGET_OS, attrs::USELESS_ATTRIBUTE, + await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE, await_holding_invalid::AWAIT_HOLDING_LOCK, await_holding_invalid::AWAIT_HOLDING_REFCELL_REF, bit_mask::BAD_BIT_MASK, @@ -64,6 +65,7 @@ store.register_lints(&[ booleans::NONMINIMAL_BOOL, borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, + bytes_count_to_len::BYTES_COUNT_TO_LEN, cargo::CARGO_COMMON_METADATA, cargo::MULTIPLE_CRATE_VERSIONS, cargo::NEGATIVE_FEATURE_NAMES, @@ -132,6 +134,7 @@ store.register_lints(&[ drop_forget_ref::UNDROPPED_MANUALLY_DROPS, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, + empty_drop::EMPTY_DROP, empty_enum::EMPTY_ENUM, empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS, entry::MAP_ENTRY, @@ -165,6 +168,7 @@ store.register_lints(&[ format_args::TO_STRING_IN_FORMAT_ARGS, format_impl::PRINT_IN_FORMAT_IMPL, format_impl::RECURSIVE_FORMAT_IMPL, + format_push_string::FORMAT_PUSH_STRING, formatting::POSSIBLE_MISSING_COMMA, formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING, formatting::SUSPICIOUS_ELSE_FORMATTING, @@ -205,6 +209,7 @@ store.register_lints(&[ iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR, large_const_arrays::LARGE_CONST_ARRAYS, large_enum_variant::LARGE_ENUM_VARIANT, + large_include_file::LARGE_INCLUDE_FILE, large_stack_arrays::LARGE_STACK_ARRAYS, len_zero::COMPARISON_TO_EMPTY, len_zero::LEN_WITHOUT_IS_EMPTY, @@ -302,6 +307,7 @@ store.register_lints(&[ methods::INEFFICIENT_TO_STRING, methods::INSPECT_FOR_EACH, methods::INTO_ITER_ON_REF, + methods::IS_DIGIT_ASCII_RADIX, methods::ITERATOR_STEP_BY_ZERO, methods::ITER_CLONED_COLLECT, methods::ITER_COUNT, @@ -321,6 +327,7 @@ store.register_lints(&[ methods::MAP_IDENTITY, methods::MAP_UNWRAP_OR, methods::NEEDLESS_OPTION_AS_DEREF, + methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_SPLITN, methods::NEW_RET_NO_SELF, methods::OK_EXPECT, @@ -432,6 +439,7 @@ store.register_lints(&[ ptr::PTR_ARG, ptr_eq::PTR_EQ, ptr_offset_with_cast::PTR_OFFSET_WITH_CAST, + pub_use::PUB_USE, question_mark::QUESTION_MARK, ranges::MANUAL_RANGE_CONTAINS, ranges::RANGE_MINUS_ONE, @@ -474,6 +482,7 @@ store.register_lints(&[ strings::STRING_SLICE, strings::STRING_TO_STRING, strings::STR_TO_STRING, + strings::TRIM_SPLIT_WHITESPACE, strlen_on_c_strings::STRLEN_ON_C_STRINGS, suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS, suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL, @@ -523,6 +532,7 @@ store.register_lints(&[ unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index c2fc67afba51..ec187563b3f6 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -20,6 +20,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), + LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION), LintId::of(option_if_let_else::OPTION_IF_LET_ELSE), LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), @@ -27,6 +28,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index eb6534cb8cae..2ee2c6e3358c 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -82,14 +82,12 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(ref_option_ref::REF_OPTION_REF), LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), + LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), LintId::of(unicode::UNICODE_NOT_NFC), - LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_async::UNUSED_ASYNC), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index f2f5c988d8f9..82431863e6cf 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(escape::BOXED_LOCAL), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS), + LintId::of(format_push_string::FORMAT_PUSH_STRING), LintId::of(large_const_arrays::LARGE_CONST_ARRAYS), LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(loops::MANUAL_MEMCPY), @@ -23,7 +24,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(misc::CMP_OWNED), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), - LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(types::BOX_COLLECTION), LintId::of(types::REDUNDANT_ALLOCATION), LintId::of(vec::USELESS_VEC), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index 4802dd877e99..77ec6c83ba4b 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION), LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS), LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE), + LintId::of(empty_drop::EMPTY_DROP), LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS), LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS), LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS), @@ -26,6 +27,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(indexing_slicing::INDEXING_SLICING), LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL), LintId::of(integer_division::INTEGER_DIVISION), + LintId::of(large_include_file::LARGE_INCLUDE_FILE), LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), LintId::of(map_err_ignore::MAP_ERR_IGNORE), @@ -53,6 +55,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(panic_unimplemented::UNIMPLEMENTED), LintId::of(panic_unimplemented::UNREACHABLE), LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH), + LintId::of(pub_use::PUB_USE), LintId::of(redundant_slicing::DEREF_BY_SLICING), LintId::of(same_name_method::SAME_NAME_METHOD), LintId::of(shadow::SHADOW_REUSE), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 3114afac8863..d183c0449cd5 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -61,6 +61,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::ERR_EXPECT), LintId::of(methods::INTO_ITER_ON_REF), + LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::ITER_CLONED_COLLECT), LintId::of(methods::ITER_NEXT_SLICE), LintId::of(methods::ITER_NTH_ZERO), @@ -104,8 +105,11 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(returns::NEEDLESS_RETURN), LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS), + LintId::of(strings::TRIM_SPLIT_WHITESPACE), LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS), LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME), + LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME), LintId::of(unused_unit::UNUSED_UNIT), LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS), diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 82f45b5fd58b..7a881bfe3991 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -5,6 +5,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![ LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP), LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS), + LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE), LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(casts::CAST_ABS_TO_UNSIGNED), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9b836f95808..3bb821a14829 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -163,6 +163,8 @@ mod deprecated_lints; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; +mod renamed_lints; + // begin lints modules, do not remove this comment, it’s used in `update_lints` mod absurd_extreme_comparisons; mod approx_const; @@ -181,6 +183,7 @@ mod bool_assert_comparison; mod booleans; mod borrow_as_ptr; mod bytecount; +mod bytes_count_to_len; mod cargo; mod case_sensitive_file_extension_comparisons; mod casts; @@ -209,6 +212,7 @@ mod double_parens; mod drop_forget_ref; mod duration_subsec; mod else_if_without_else; +mod empty_drop; mod empty_enum; mod empty_structs_with_brackets; mod entry; @@ -231,6 +235,7 @@ mod floating_point_arithmetic; mod format; mod format_args; mod format_impl; +mod format_push_string; mod formatting; mod from_over_into; mod from_str_radix_10; @@ -259,6 +264,7 @@ mod items_after_statements; mod iter_not_returning_iterator; mod large_const_arrays; mod large_enum_variant; +mod large_include_file; mod large_stack_arrays; mod len_zero; mod let_if_seq; @@ -336,6 +342,7 @@ mod precedence; mod ptr; mod ptr_eq; mod ptr_offset_with_cast; +mod pub_use; mod question_mark; mod ranges; mod redundant_clone; @@ -383,6 +390,7 @@ mod unit_hash; mod unit_return_expecting_ord; mod unit_types; mod unnamed_address; +mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; @@ -499,7 +507,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: { store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); - store.register_late_pass(|| Box::new(utils::inspector::DeepCodeInspector)); store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls)); store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new())); store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle)); @@ -511,8 +518,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl)); } + store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|| Box::new(utils::author::Author)); - store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding)); + let await_holding_invalid_types = conf.await_holding_invalid_types.clone(); + store.register_late_pass(move || { + Box::new(await_holding_invalid::AwaitHolding::new( + await_holding_invalid_types.clone(), + )) + }); store.register_late_pass(|| Box::new(serde_api::SerdeApi)); let vec_box_size_threshold = conf.vec_box_size_threshold; let type_complexity_threshold = conf.type_complexity_threshold; @@ -572,7 +585,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv))); store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::new(msrv))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); + store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv))); store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); @@ -812,6 +826,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); + store.register_late_pass(|| Box::new(empty_drop::EmptyDrop)); store.register_late_pass(|| Box::new(strings::StrToString)); store.register_late_pass(|| Box::new(strings::StringToString)); store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); @@ -868,6 +883,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef)); store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets)); + store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); + store.register_early_pass(|| Box::new(pub_use::PubUse)); + store.register_late_pass(|| Box::new(format_push_string::FormatPushString)); + store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen)); + let max_include_file_size = conf.max_include_file_size; + store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); + store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); // add lints here, do not remove this comment, it's used in `new_lint` } @@ -919,43 +941,9 @@ fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) { /// /// Used in `./src/driver.rs`. pub fn register_renamed(ls: &mut rustc_lint::LintStore) { - // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs - ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions"); - ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default"); - ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"); - ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"); - ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map"); - ls.register_renamed("clippy::box_vec", "clippy::box_collection"); - ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"); - ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"); - ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"); - ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used"); - ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used"); - ls.register_renamed("clippy::option_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::result_expect_used", "clippy::expect_used"); - ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"); - ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"); - ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion"); - ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters"); - ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str"); - ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok"); - ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types"); - ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods"); - ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow"); - ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl"); - - // uplifted lints - ls.register_renamed("clippy::invalid_ref", "invalid_value"); - ls.register_renamed("clippy::into_iter_on_array", "array_into_iter"); - ls.register_renamed("clippy::unused_label", "unused_labels"); - ls.register_renamed("clippy::drop_bounds", "drop_bounds"); - ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"); - ls.register_renamed("clippy::panic_params", "non_fmt_panics"); - ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints"); - ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"); - ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"); + for (old_name, new_name) in renamed_lints::RENAMED_LINTS { + ls.register_renamed(old_name, new_name); + } } // only exists to let the dogfood integration test works. diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 662a561f171e..ab5d3fa7b6d9 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -1,16 +1,19 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty, Visitor, + walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; use rustc_hir::FnRetTy::Return; use rustc_hir::{ - BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, + BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, Ident, Symbol}; @@ -82,8 +85,10 @@ declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Fn(ref sig, ref generics, id) = item.kind { + if let ItemKind::Fn(ref sig, generics, id) = item.kind { check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true); + } else if let ItemKind::Impl(impl_) = item.kind { + report_extra_impl_lifetimes(cx, impl_); } } @@ -95,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { sig.decl, Some(id), None, - &item.generics, + item.generics, item.span, report_extra_lifetimes, ); @@ -108,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig.decl, body, trait_sig, &item.generics, item.span, true); + check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true); } } } @@ -201,8 +206,7 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: visitor.visit_ty(self_ty); !visitor.all_lts().is_empty() - } - else { + } else { false } } @@ -486,11 +490,29 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } -struct LifetimeChecker { +struct LifetimeChecker<'cx, 'tcx, F> { + cx: &'cx LateContext<'tcx>, map: FxHashMap, + phantom: std::marker::PhantomData, } -impl<'tcx> Visitor<'tcx> for LifetimeChecker { +impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> { + fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap) -> LifetimeChecker<'cx, 'tcx, F> { + Self { + cx, + map, + phantom: std::marker::PhantomData, + } + } +} + +impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F> +where + F: NestedFilter<'tcx>, +{ + type Map = rustc_middle::hir::map::Map<'tcx>; + type NestedFilter = F; + // for lifetimes as parameters of generics fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) { self.map.remove(&lifetime.name.ident().name); @@ -506,6 +528,10 @@ impl<'tcx> Visitor<'tcx> for LifetimeChecker { walk_generic_param(self, param); } } + + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } } fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { @@ -517,7 +543,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, _ => None, }) .collect(); - let mut checker = LifetimeChecker { map: hs }; + let mut checker = LifetimeChecker::::new(cx, hs); walk_generics(&mut checker, generics); walk_fn_decl(&mut checker, func); @@ -532,6 +558,32 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, } } +fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) { + let hs = impl_ + .generics + .params + .iter() + .filter_map(|par| match par.kind { + GenericParamKind::Lifetime { .. } => Some((par.name.ident().name, par.span)), + _ => None, + }) + .collect(); + let mut checker = LifetimeChecker::::new(cx, hs); + + walk_generics(&mut checker, impl_.generics); + if let Some(ref trait_ref) = impl_.of_trait { + walk_trait_ref(&mut checker, trait_ref); + } + walk_ty(&mut checker, impl_.self_ty); + for item in impl_.items { + walk_impl_item_ref(&mut checker, item); + } + + for &v in checker.map.values() { + span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl"); + } +} + struct BodyLifetimeChecker { lifetimes_used_in_body: bool, } diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index b7430f49229a..269d3c62eafc 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -42,17 +42,12 @@ declare_clippy_lint! { /// This is most probably a typo /// /// ### Known problems - /// - Recommends a signed suffix, even though the number might be too big and an unsigned - /// suffix is required + /// - Does not match on integers too large to fit in the corresponding unsigned type /// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers /// /// ### Example - /// ```rust - /// // Probably mistyped - /// 2_32; - /// - /// // Good - /// 2_i32; + /// `2_32` => `2_i32` + /// `250_8 => `250_u8` /// ``` #[clippy::version = "1.30.0"] pub MISTYPED_LITERAL_SUFFIXES, @@ -310,18 +305,47 @@ impl LiteralDigitGrouping { return true; } - let (part, mistyped_suffixes, missing_char) = if let Some((_, exponent)) = &mut num_lit.exponent { - (exponent, &["32", "64"][..], 'f') + let (part, mistyped_suffixes, is_float) = if let Some((_, exponent)) = &mut num_lit.exponent { + (exponent, &["32", "64"][..], true) } else if num_lit.fraction.is_some() { - (&mut num_lit.integer, &["32", "64"][..], 'f') + return true; } else { - (&mut num_lit.integer, &["8", "16", "32", "64"][..], 'i') + (&mut num_lit.integer, &["8", "16", "32", "64"][..], false) }; let mut split = part.rsplit('_'); let last_group = split.next().expect("At least one group"); if split.next().is_some() && mistyped_suffixes.contains(&last_group) { - *part = &part[..part.len() - last_group.len()]; + let main_part = &part[..part.len() - last_group.len()]; + let missing_char; + if is_float { + missing_char = 'f'; + } else { + let radix = match num_lit.radix { + Radix::Binary => 2, + Radix::Octal => 8, + Radix::Decimal => 10, + Radix::Hexadecimal => 16, + }; + if let Ok(int) = u64::from_str_radix(&main_part.replace('_', ""), radix) { + missing_char = match (last_group, int) { + ("8", i) if i8::try_from(i).is_ok() => 'i', + ("16", i) if i16::try_from(i).is_ok() => 'i', + ("32", i) if i32::try_from(i).is_ok() => 'i', + ("64", i) if i64::try_from(i).is_ok() => 'i', + ("8", u) if u8::try_from(u).is_ok() => 'u', + ("16", u) if u16::try_from(u).is_ok() => 'u', + ("32", u) if u32::try_from(u).is_ok() => 'u', + ("64", _) => 'u', + _ => { + return true; + }, + } + } else { + return true; + } + } + *part = main_part; let mut sugg = num_lit.format(); sugg.push('_'); sugg.push(missing_char); diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 9ba9642fcc83..70a118d6b353 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -168,8 +168,9 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult { .operands .iter() .map(|(o, _)| match o { - InlineAsmOperand::In { expr, .. } - | InlineAsmOperand::InOut { expr, .. } => never_loop_expr(expr, main_loop_id), + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => { + never_loop_expr(expr, main_loop_id) + }, InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id), InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id) diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index dcf44303cf44..ac3d9447b6bd 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::{get_parent_expr, meets_msrv, msrvs}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; @@ -24,7 +24,7 @@ declare_clippy_lint! { /// ``` /// Use instead: /// ```rust - /// usize::BITS; + /// usize::BITS as usize; /// ``` #[clippy::version = "1.60.0"] pub MANUAL_BITS, @@ -59,16 +59,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits { if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); if let ExprKind::Lit(lit) = &other_expr.kind; if let LitKind::Int(8, _) = lit.node; - then { + let mut app = Applicability::MachineApplicable; + let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app); + let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); + span_lint_and_sugg( cx, MANUAL_BITS, expr.span, "usage of `mem::size_of::()` to obtain the size of `T` in bits", "consider using", - format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()), - Applicability::MachineApplicable, + sugg, + app, ); } } @@ -108,3 +111,36 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< } } } + +fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String { + if let Some(parent_expr) = get_parent_expr(cx, expr) { + if is_ty_conversion(parent_expr) { + return base_sugg; + } + + // These expressions have precedence over casts, the suggestion therefore + // needs to be wrapped into parentheses + match parent_expr.kind { + ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => { + return format!("({base_sugg} as usize)"); + }, + _ => {}, + } + } + + format!("{base_sugg} as usize") +} + +fn is_ty_conversion(expr: &Expr<'_>) -> bool { + if let ExprKind::Cast(..) = expr.kind { + true + } else if let ExprKind::MethodCall(path, [_], _) = expr.kind + && path.ident.name == rustc_span::sym::try_into + { + // This is only called for `usize` which implements `TryInto`. Therefore, + // we don't have to check here if `self` implements the `TryInto` trait. + true + } else { + false + } +} diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 33d1bb2985f4..b8d620d81713 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,13 +1,17 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs}; -use if_chain::if_chain; -use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind}; +use clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; +use rustc_ast::ast::{self, VisibilityKind}; +use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::{self as hir, Expr, ExprKind, QPath}; +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::DefIdTree; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; declare_clippy_lint! { @@ -58,129 +62,160 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[derive(Clone)] -pub struct ManualNonExhaustive { +#[allow(clippy::module_name_repetitions)] +pub struct ManualNonExhaustiveStruct { msrv: Option, } -impl ManualNonExhaustive { +impl ManualNonExhaustiveStruct { #[must_use] pub fn new(msrv: Option) -> Self { Self { msrv } } } -impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); +impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); -impl EarlyLintPass for ManualNonExhaustive { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { +#[allow(clippy::module_name_repetitions)] +pub struct ManualNonExhaustiveEnum { + msrv: Option, + constructed_enum_variants: FxHashSet<(DefId, DefId)>, + potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, +} + +impl ManualNonExhaustiveEnum { + #[must_use] + pub fn new(msrv: Option) -> Self { + Self { + msrv, + constructed_enum_variants: FxHashSet::default(), + potential_enums: Vec::new(), + } + } +} + +impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); + +impl EarlyLintPass for ManualNonExhaustiveStruct { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { return; } - match &item.kind { - ItemKind::Enum(def, _) => { - check_manual_non_exhaustive_enum(cx, item, &def.variants); - }, - ItemKind::Struct(variant_data, _) => { - if let VariantData::Unit(..) = variant_data { - return; - } - - check_manual_non_exhaustive_struct(cx, item, variant_data); - }, - _ => {}, + if let ast::ItemKind::Struct(variant_data, _) = &item.kind { + let (fields, delimiter) = match variant_data { + ast::VariantData::Struct(fields, _) => (&**fields, '{'), + ast::VariantData::Tuple(fields, _) => (&**fields, '('), + ast::VariantData::Unit(_) => return, + }; + if fields.len() <= 1 { + return; + } + let mut iter = fields.iter().filter_map(|f| match f.vis.kind { + VisibilityKind::Public => None, + VisibilityKind::Inherited => Some(Ok(f)), + _ => Some(Err(())), + }); + if let Some(Ok(field)) = iter.next() + && iter.next().is_none() + && field.ty.kind.is_unit() + && field.ident.map_or(true, |name| name.as_str().starts_with('_')) + { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)) + && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter) + && let Some(snippet) = snippet_opt(cx, header_span) + { + diag.span_suggestion( + header_span, + "add the attribute", + format!("#[non_exhaustive] {}", snippet), + Applicability::Unspecified, + ); + } + diag.span_help(field.span, "remove this field"); + } + ); + } } } extract_msrv_attr!(EarlyContext); } -fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants: &[Variant]) { - fn is_non_exhaustive_marker(variant: &Variant) -> bool { - matches!(variant.data, VariantData::Unit(_)) - && variant.ident.as_str().starts_with('_') - && is_doc_hidden(&variant.attrs) +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + return; + } + + if let hir::ItemKind::Enum(def, _) = &item.kind + && def.variants.len() > 1 + { + let mut iter = def.variants.iter().filter_map(|v| { + let id = cx.tcx.hir().local_def_id(v.id); + (matches!(v.data, hir::VariantData::Unit(_)) + && v.ident.as_str().starts_with('_') + && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id()))) + .then(|| (id, v.span)) + }); + if let Some((id, span)) = iter.next() + && iter.next().is_none() + { + self.potential_enums.push((item.def_id, id, item.span, span)); + } + } } - let mut markers = variants.iter().filter(|v| is_non_exhaustive_marker(v)); - if_chain! { - if let Some(marker) = markers.next(); - if markers.count() == 0 && variants.len() > 1; - then { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind + && let [.., name] = p.segments + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && name.ident.as_str().starts_with('_') + { + let variant_id = cx.tcx.parent(id); + let enum_id = cx.tcx.parent(variant_id); + + self.constructed_enum_variants.insert((enum_id, variant_id)); + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { + for &(enum_id, _, enum_span, variant_span) in + self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| { + !self + .constructed_enum_variants + .contains(&(enum_id.to_def_id(), variant_id.to_def_id())) + && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id)) + }) + { span_lint_and_then( cx, MANUAL_NON_EXHAUSTIVE, - item.span, + enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = cx.sess().source_map().span_until_char(item.span, '{'); - if let Some(snippet) = snippet_opt(cx, header_span); - then { + if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive() + && let header_span = cx.sess().source_map().span_until_char(enum_span, '{') + && let Some(snippet) = snippet_opt(cx, header_span) + { diag.span_suggestion( header_span, "add the attribute", format!("#[non_exhaustive] {}", snippet), Applicability::Unspecified, ); - } } - diag.span_help(marker.span, "remove this variant"); - }); - } - } -} - -fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) { - fn is_private(field: &FieldDef) -> bool { - matches!(field.vis.kind, VisibilityKind::Inherited) - } - - fn is_non_exhaustive_marker(field: &FieldDef) -> bool { - is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_')) - } - - fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span { - let delimiter = match data { - VariantData::Struct(..) => '{', - VariantData::Tuple(..) => '(', - VariantData::Unit(_) => unreachable!("`VariantData::Unit` is already handled above"), - }; - - cx.sess().source_map().span_until_char(item.span, delimiter) - } - - let fields = data.fields(); - let private_fields = fields.iter().filter(|f| is_private(f)).count(); - let public_fields = fields.iter().filter(|f| f.vis.kind.is_pub()).count(); - - if_chain! { - if private_fields == 1 && public_fields >= 1 && public_fields == fields.len() - 1; - if let Some(marker) = fields.iter().find(|f| is_non_exhaustive_marker(f)); - then { - span_lint_and_then( - cx, - MANUAL_NON_EXHAUSTIVE, - item.span, - "this seems like a manual implementation of the non-exhaustive pattern", - |diag| { - if_chain! { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)); - let header_span = find_header_span(cx, item, data); - if let Some(snippet) = snippet_opt(cx, header_span); - then { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {}", snippet), - Applicability::Unspecified, - ); - } - } - diag.span_help(marker.span, "remove this field"); - }); + diag.span_help(variant_span, "remove this variant"); + }, + ); } } + + extract_msrv_attr!(LateContext); } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index e233300e26ab..ceb66947d02c 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -143,15 +143,11 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { impl MapClone { fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; - let message = if is_copy { - "you are using an explicit closure for copying elements" + + let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { + ("you are using an explicit closure for copying elements", "copied") } else { - "you are using an explicit closure for cloning elements" - }; - let sugg_method = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { - "copied" - } else { - "cloned" + ("you are using an explicit closure for cloning elements", "cloned") }; span_lint_and_sugg( diff --git a/clippy_lints/src/match_result_ok.rs b/clippy_lints/src/match_result_ok.rs index 77a4917ec58f..3349b85f1347 100644 --- a/clippy_lints/src/match_result_ok.rs +++ b/clippy_lints/src/match_result_ok.rs @@ -24,7 +24,7 @@ declare_clippy_lint! { /// vec.push(value) /// } /// - /// if let Some(valie) = iter.next().ok() { + /// if let Some(value) = iter.next().ok() { /// vec.push(value) /// } /// ``` @@ -60,7 +60,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk { if_chain! { if let ExprKind::MethodCall(ok_path, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result.ok(, _) if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = let_pat.kind; //get operation - if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized; + if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() method use std::marker::Sized; if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result); if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some"; diff --git a/clippy_lints/src/matches/infalliable_detructuring_match.rs b/clippy_lints/src/matches/infallible_destructuring_match.rs similarity index 100% rename from clippy_lints/src/matches/infalliable_detructuring_match.rs rename to clippy_lints/src/matches/infallible_destructuring_match.rs diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index b8591fe0db0a..9b7344fb8b0b 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { .map(|a| NormalizedPat::from_pat(cx, &arena, a.pat)) .collect(); - // The furthast forwards a pattern can move without semantic changes + // The furthest forwards a pattern can move without semantic changes let forwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { }) .collect(); - // The furthast backwards a pattern can move without semantic changes + // The furthest backwards a pattern can move without semantic changes let backwards_blocking_idxs: Vec<_> = normalized_pats .iter() .enumerate() diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index e93b494653fc..401ecef460c3 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -1,4 +1,4 @@ -use clippy_utils::source::{snippet_opt, walk_span_to_context}; +use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context}; use clippy_utils::{meets_msrv, msrvs}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; @@ -7,7 +7,7 @@ use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; -mod infalliable_detructuring_match; +mod infallible_destructuring_match; mod match_as_ref; mod match_bool; mod match_like_matches; @@ -653,6 +653,9 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if let ExprKind::Match(ex, arms, source) = expr.kind { + if !span_starts_with(cx, expr.span, "match") { + return; + } if !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) @@ -691,7 +694,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) { - self.infallible_destructuring_match_linted |= infalliable_detructuring_match::check(cx, local); + self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local); } fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) { diff --git a/clippy_lints/src/matches/needless_match.rs b/clippy_lints/src/matches/needless_match.rs index 2105a03e03a3..f920ad4651f9 100644 --- a/clippy_lints/src/matches/needless_match.rs +++ b/clippy_lints/src/matches/needless_match.rs @@ -83,7 +83,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { return false; } - // Recurrsively check for each `else if let` phrase, + // Recursively check for each `else if let` phrase, if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) { return check_if_let(cx, nested_if_let); } @@ -99,7 +99,7 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool { if let ExprKind::Path(ref qpath) = ret.kind { return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret); } - return true; + return false; } return eq_expr_value(cx, if_let.let_expr, ret); } @@ -118,7 +118,7 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> { } /// Manually check for coercion casting by checking if the type of the match operand or let expr -/// differs with the assigned local variable or the funtion return type. +/// differs with the assigned local variable or the function return type. fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool { if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) { match p_node { diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index aa3552001f46..37b67647efe9 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -2,17 +2,16 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type}; +use clippy_utils::ty::needs_ordered_drop; use clippy_utils::{higher, match_def_path}; use clippy_utils::{is_lang_ctor, is_trait_method, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; -use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, PollPending}; use rustc_hir::{ intravisit::{walk_expr, Visitor}, - Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp, + Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp, }; use rustc_lint::LateContext; use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty}; @@ -32,59 +31,6 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { } } -/// Checks if the drop order for a type matters. Some std types implement drop solely to -/// deallocate memory. For these types, and composites containing them, changing the drop order -/// won't result in any observable side effects. -fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) -} - -fn type_needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet>) -> bool { - if !seen.insert(ty) { - return false; - } - if !ty.needs_drop(cx.tcx, cx.param_env) { - false - } else if !cx - .tcx - .lang_items() - .drop_trait() - .map_or(false, |id| implements_trait(cx, ty, id, &[])) - { - // This type doesn't implement drop, so no side effects here. - // Check if any component type has any. - match ty.kind() { - ty::Tuple(fields) => fields.iter().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), - ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen), - ty::Adt(adt, subs) => adt - .all_fields() - .map(|f| f.ty(cx.tcx, subs)) - .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)), - _ => true, - } - } - // Check for std types which implement drop, but only for memory allocation. - else if is_type_diagnostic_item(cx, ty, sym::Vec) - || is_type_lang_item(cx, ty, LangItem::OwnedBox) - || is_type_diagnostic_item(cx, ty, sym::Rc) - || is_type_diagnostic_item(cx, ty, sym::Arc) - || is_type_diagnostic_item(cx, ty, sym::cstring_type) - || is_type_diagnostic_item(cx, ty, sym::BTreeMap) - || is_type_diagnostic_item(cx, ty, sym::LinkedList) - || match_type(cx, ty, &paths::WEAK_RC) - || match_type(cx, ty, &paths::WEAK_ARC) - { - // Check all of the generic arguments. - if let ty::Adt(_, subs) = ty.kind() { - subs.types().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)) - } else { - true - } - } else { - true - } -} - // Extract the generic arguments out of a type fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option> { if_chain! { @@ -115,7 +61,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< // e.g. In `(String::new(), 0).1` the string is a temporary value. ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => { if !matches!(expr.kind, ExprKind::Path(_)) { - if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { + if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) { self.res = true; } else { self.visit_expr(expr); @@ -126,7 +72,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< // e.g. In `(vec![0])[0]` the vector is a temporary value. ExprKind::Index(base, index) => { if !matches!(base.kind, ExprKind::Path(_)) { - if type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { + if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) { self.res = true; } else { self.visit_expr(base); @@ -143,7 +89,7 @@ fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr< .typeck_results() .type_dependent_def_id(expr.hir_id) .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref()); - if self_by_ref && type_needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) { + if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) { self.res = true; } else { self.visit_expr(self_arg); @@ -243,7 +189,7 @@ fn find_sugg_for_if_let<'tcx>( // scrutinee would be, so they have to be considered as well. // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held // for the duration if body. - let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); + let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr); // check that `while_let_on_iterator` lint does not trigger if_chain! { diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs index 5076239a57c4..0aadb482acdd 100644 --- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs +++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs @@ -14,6 +14,7 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) { if let ty::Adt(def, _) = ty.kind(); if def.is_struct() || def.is_union(); if fields.len() == def.non_enum_variant().fields.len(); + if !def.non_enum_variant().is_field_list_non_exhaustive(); then { span_lint_and_help( diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs new file mode 100644 index 000000000000..ad333df2f2d5 --- /dev/null +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -0,0 +1,50 @@ +//! Lint for `c.is_digit(10)` + +use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::{ + consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs, + source::snippet_with_applicability, +}; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_semver::RustcVersion; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + self_arg: &'tcx Expr<'_>, + radix: &'tcx Expr<'_>, + msrv: Option<&RustcVersion>, +) { + if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) { + return; + } + + if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() { + return; + } + + if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) { + let (num, replacement) = match radix_val { + FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"), + FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"), + _ => return, + }; + let mut applicability = Applicability::MachineApplicable; + + span_lint_and_sugg( + cx, + IS_DIGIT_ASCII_RADIX, + expr.span, + &format!("use of `char::is_digit` with literal radix of {}", num), + "try", + format!( + "{}.{}()", + snippet_with_applicability(cx, self_arg.span, "..", &mut applicability), + replacement + ), + applicability, + ); + } +} diff --git a/clippy_lints/src/methods/iter_with_drain.rs b/clippy_lints/src/methods/iter_with_drain.rs index 958c3773087b..152072e09c77 100644 --- a/clippy_lints/src/methods/iter_with_drain.rs +++ b/clippy_lints/src/methods/iter_with_drain.rs @@ -1,72 +1,47 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::higher::Range; use clippy_utils::is_integer_const; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{ - higher::{self, Range}, - SpanlessEq, -}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::sym; use rustc_span::Span; use super::ITER_WITH_DRAIN; -const DRAIN_TYPES: &[Symbol] = &[sym::Vec, sym::VecDeque]; - pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: &Expr<'_>) { - let ty = cx.typeck_results().expr_ty(recv).peel_refs(); - if let Some(drained_type) = DRAIN_TYPES.iter().find(|&&sym| is_type_diagnostic_item(cx, ty, sym)) { - // Refuse to emit `into_iter` suggestion on draining struct fields due - // to the strong possibility of processing unmovable field. - if let ExprKind::Field(..) = recv.kind { - return; - } - - if let Some(range) = higher::Range::hir(arg) { - let left_full = match range { - Range { start: Some(start), .. } if is_integer_const(cx, start, 0) => true, - Range { start: None, .. } => true, - _ => false, - }; - let full = left_full - && match range { - Range { - end: Some(end), - limits: RangeLimits::HalfOpen, - .. - } => { - // `x.drain(..x.len())` call - if_chain! { - if let ExprKind::MethodCall(len_path, len_args, _) = end.kind; - if len_path.ident.name == sym::len && len_args.len() == 1; - if let ExprKind::Path(QPath::Resolved(_, drain_path)) = recv.kind; - if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind; - if SpanlessEq::new(cx).eq_path(drain_path, len_path); - then { true } - else { false } - } - }, - Range { - end: None, - limits: RangeLimits::HalfOpen, - .. - } => true, - _ => false, - }; - if full { - span_lint_and_sugg( - cx, - ITER_WITH_DRAIN, - span.with_hi(expr.span.hi()), - &format!("`drain(..)` used on a `{}`", drained_type), - "try this", - "into_iter()".to_string(), - Applicability::MaybeIncorrect, - ); - } - } - } + if !matches!(recv.kind, ExprKind::Field(..)) + && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() + && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did()) + && matches!(ty_name, sym::Vec | sym::VecDeque) + && let Some(range) = Range::hir(arg) + && is_full_range(cx, recv, range) + { + span_lint_and_sugg( + cx, + ITER_WITH_DRAIN, + span.with_hi(expr.span.hi()), + &format!("`drain(..)` used on a `{}`", ty_name), + "try this", + "into_iter()".to_string(), + Applicability::MaybeIncorrect, + ); + }; +} + +fn is_full_range(cx: &LateContext<'_>, container: &Expr<'_>, range: Range<'_>) -> bool { + range.start.map_or(true, |e| is_integer_const(cx, e, 0)) + && range.end.map_or(true, |e| { + if range.limits == RangeLimits::HalfOpen + && let ExprKind::Path(QPath::Resolved(None, container_path)) = container.kind + && let ExprKind::MethodCall(name, [self_arg], _) = e.kind + && name.ident.name == sym::len + && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind + { + container_path.res == path.res + } else { + false + } + }) } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 70d021a1668e..f3be71f6b8bb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -26,6 +26,7 @@ mod implicit_clone; mod inefficient_to_string; mod inspect_for_each; mod into_iter_on_ref; +mod is_digit_ascii_radix; mod iter_cloned_collect; mod iter_count; mod iter_next_slice; @@ -42,6 +43,7 @@ mod map_flatten; mod map_identity; mod map_unwrap_or; mod needless_option_as_deref; +mod needless_option_take; mod ok_expect; mod option_as_ref_deref; mod option_map_or_none; @@ -294,15 +296,15 @@ declare_clippy_lint! { /// Checks for methods with certain name prefixes and which /// doesn't match how self is taken. The actual rules are: /// - /// |Prefix |Postfix |`self` taken | `self` type | - /// |-------|------------|-----------------------|--------------| - /// |`as_` | none |`&self` or `&mut self` | any | - /// |`from_`| none | none | any | - /// |`into_`| none |`self` | any | - /// |`is_` | none |`&self` or none | any | - /// |`to_` | `_mut` |`&mut self` | any | - /// |`to_` | not `_mut` |`self` | `Copy` | - /// |`to_` | not `_mut` |`&self` | not `Copy` | + /// |Prefix |Postfix |`self` taken | `self` type | + /// |-------|------------|-------------------------------|--------------| + /// |`as_` | none |`&self` or `&mut self` | any | + /// |`from_`| none | none | any | + /// |`into_`| none |`self` | any | + /// |`is_` | none |`&mut self` or `&self` or none | any | + /// |`to_` | `_mut` |`&mut self` | any | + /// |`to_` | not `_mut` |`self` | `Copy` | + /// |`to_` | not `_mut` |`&self` | not `Copy` | /// /// Note: Clippy doesn't trigger methods with `to_` prefix in: /// - Traits definition. @@ -1265,7 +1267,7 @@ declare_clippy_lint! { #[clippy::version = "1.55.0"] pub EXTEND_WITH_DRAIN, perf, - "using vec.append(&mut vec) to move the full range of a vecor to another" + "using vec.append(&mut vec) to move the full range of a vector to another" } declare_clippy_lint! { @@ -2007,13 +2009,27 @@ declare_clippy_lint! { /// ### Example /// ```rust,ignore /// // Bad - /// let (key, value) = _.splitn(2, '=').next_tuple()?; - /// let value = _.splitn(2, '=').nth(1)?; + /// let s = "key=value=add"; + /// let (key, value) = s.splitn(2, '=').next_tuple()?; + /// let value = s.splitn(2, '=').nth(1)?; /// - /// // Good - /// let (key, value) = _.split_once('=')?; - /// let value = _.split_once('=')?.1; + /// let mut parts = s.splitn(2, '='); + /// let key = parts.next()?; + /// let value = parts.next()?; /// ``` + /// Use instead: + /// ```rust,ignore + /// // Good + /// let s = "key=value=add"; + /// let (key, value) = s.split_once('=')?; + /// let value = s.split_once('=')?.1; + /// + /// let (key, value) = s.split_once('=')?; + /// ``` + /// + /// ### Limitations + /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()` + /// in two separate `let` statements that immediately follow the `splitn()` #[clippy::version = "1.57.0"] pub MANUAL_SPLIT_ONCE, complexity, @@ -2099,7 +2115,7 @@ declare_clippy_lint! { /// using `.collect::()` over `.collect::>().join("")` /// will prevent loop unrolling and will result in a negative performance impact. /// - /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output, + /// Additionally, differences have been observed between aarch64 and x86_64 assembly output, /// with aarch64 tending to producing faster assembly in more cases when using `.collect::()` #[clippy::version = "1.61.0"] pub UNNECESSARY_JOIN, @@ -2131,6 +2147,56 @@ declare_clippy_lint! { "no-op use of `deref` or `deref_mut` method to `Option`." } +declare_clippy_lint! { + /// ### What it does + /// Finds usages of [`char::is_digit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that + /// can be replaced with [`is_ascii_digit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or + /// [`is_ascii_hexdigit`] + /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit). + /// + /// ### Why is this bad? + /// `is_digit(..)` is slower and requires specifying the radix. + /// + /// ### Example + /// ```rust + /// let c: char = '6'; + /// c.is_digit(10); + /// c.is_digit(16); + /// ``` + /// Use instead: + /// ```rust + /// let c: char = '6'; + /// c.is_ascii_digit(); + /// c.is_ascii_hexdigit(); + /// ``` + #[clippy::version = "1.61.0"] + pub IS_DIGIT_ASCII_RADIX, + style, + "use of `char::is_digit(..)` with literal radix of 10 or 16" +} + +declare_clippy_lint! { + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```rust + /// let x = Some(3); + /// x.as_ref().take(); + /// ``` + /// Use instead: + /// ```rust + /// let x = Some(3); + /// x.as_ref(); + /// ``` + #[clippy::version = "1.61.0"] + pub NEEDLESS_OPTION_TAKE, + complexity, + "using `.as_ref().take()` on a temporary value" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, @@ -2219,6 +2285,8 @@ impl_lint_pass!(Methods => [ UNNECESSARY_JOIN, ERR_EXPECT, NEEDLESS_OPTION_AS_DEREF, + IS_DIGIT_ASCII_RADIX, + NEEDLESS_OPTION_TAKE, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -2254,7 +2322,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, args); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref()); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -2516,6 +2584,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), ("is_file", []) => filetype_is_file::check(cx, expr, recv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("join", [join_arg]) => { @@ -2574,12 +2643,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); - if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) { - str_splitn::check_manual_split_once(cx, name, expr, recv, pat_arg); - } - if count >= 2 { - str_splitn::check_needless_splitn(cx, name, expr, recv, pat_arg, count); - } + str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { @@ -2595,6 +2659,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, + ("take", []) => needless_option_take::check(cx, expr, recv), ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv); }, diff --git a/clippy_lints/src/methods/needless_option_take.rs b/clippy_lints/src/methods/needless_option_take.rs new file mode 100644 index 000000000000..829c118d2916 --- /dev/null +++ b/clippy_lints/src/methods/needless_option_take.rs @@ -0,0 +1,41 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_def_path; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::Expr; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::NEEDLESS_OPTION_TAKE; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) { + // Checks if expression type is equal to sym::Option and if the expr is not a syntactic place + if !recv.is_syntactic_place_expr() && is_expr_option(cx, recv) && has_expr_as_ref_path(cx, recv) { + let mut applicability = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + NEEDLESS_OPTION_TAKE, + expr.span, + "called `Option::take()` on a temporary value", + "try", + format!( + "{}", + snippet_with_applicability(cx, recv.span, "..", &mut applicability) + ), + applicability, + ); + } +} + +fn is_expr_option(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_type = cx.typeck_results().expr_ty(expr); + is_type_diagnostic_item(cx, expr_type, sym::Option) +} + +fn has_expr_as_ref_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(ref_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + return match_def_path(cx, ref_id, &["core", "option", "Option", "as_ref"]); + } + false +} diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 8125930b3046..52891eeed069 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,36 +1,83 @@ use clippy_utils::consts::{constant, Constant}; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; -use clippy_utils::{is_diag_item_method, match_def_path, paths}; +use clippy_utils::usage::local_used_after_expr; +use clippy_utils::visitors::expr_visitor; +use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, HirId, LangItem, Node, QPath}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::{ + BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind, +}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, adjustment::Adjust}; -use rustc_span::{symbol::sym, Span, SyntaxContext}; +use rustc_middle::ty; +use rustc_semver::RustcVersion; +use rustc_span::{sym, Span, Symbol, SyntaxContext}; -use super::MANUAL_SPLIT_ONCE; +use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; -pub(super) fn check_manual_split_once( +pub(super) fn check( cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>, + count: u128, + msrv: Option<&RustcVersion>, ) { - if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { + if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; } - let ctxt = expr.span.ctxt(); - let (method_name, msg, reverse) = if method_name == "splitn" { - ("split_once", "manual implementation of `split_once`", false) - } else { - ("rsplit_once", "manual implementation of `rsplit_once`", true) + let needless = |usage_kind| match usage_kind { + IterUsageKind::Nth(n) => count > n + 1, + IterUsageKind::NextTuple => count > 2, }; - let usage = match parse_iter_usage(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), reverse) { - Some(x) => x, - None => return, + let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE); + + match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { + Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), + Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage), + None if manual => { + check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg); + }, + _ => {}, + } +} + +fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) { + let mut app = Applicability::MachineApplicable; + let r = if method_name == "splitn" { "" } else { "r" }; + + span_lint_and_sugg( + cx, + NEEDLESS_SPLITN, + expr.span, + &format!("unnecessary use of `{r}splitn`"), + "try this", + format!( + "{}.{r}split({})", + snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0, + snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0, + ), + app, + ); +} + +fn check_manual_split_once( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + pat_arg: &Expr<'_>, + usage: &IterUsage, +) { + let ctxt = expr.span.ctxt(); + let (msg, reverse) = if method_name == "splitn" { + ("manual implementation of `split_once`", false) + } else { + ("manual implementation of `rsplit_once`", true) }; let mut app = Applicability::MachineApplicable; @@ -39,84 +86,198 @@ pub(super) fn check_manual_split_once( let sugg = match usage.kind { IterUsageKind::NextTuple => { - format!("{}.{}({})", self_snip, method_name, pat_snip) - }, - IterUsageKind::RNextTuple => format!("{}.{}({}).map(|(x, y)| (y, x))", self_snip, method_name, pat_snip), - IterUsageKind::Next | IterUsageKind::Second => { - let self_deref = { - let adjust = cx.typeck_results().expr_adjustments(self_arg); - if adjust.len() < 2 { - String::new() - } else if cx.typeck_results().expr_ty(self_arg).is_box() - || adjust - .iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(_))) || a.target.is_box()) - { - format!("&{}", "*".repeat(adjust.len().saturating_sub(1))) - } else { - "*".repeat(adjust.len().saturating_sub(2)) - } - }; - if matches!(usage.kind, IterUsageKind::Next) { - match usage.unwrap_kind { - Some(UnwrapKind::Unwrap) => { - if reverse { - format!("{}.{}({}).unwrap().0", self_snip, method_name, pat_snip) - } else { - format!( - "{}.{}({}).map_or({}{}, |x| x.0)", - self_snip, method_name, pat_snip, self_deref, &self_snip - ) - } - }, - Some(UnwrapKind::QuestionMark) => { - format!( - "{}.{}({}).map_or({}{}, |x| x.0)", - self_snip, method_name, pat_snip, self_deref, &self_snip - ) - }, - None => { - format!( - "Some({}.{}({}).map_or({}{}, |x| x.0))", - &self_snip, method_name, pat_snip, self_deref, &self_snip - ) - }, - } + if reverse { + format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))") } else { - match usage.unwrap_kind { - Some(UnwrapKind::Unwrap) => { - if reverse { - // In this case, no better suggestion is offered. - return; - } - format!("{}.{}({}).unwrap().1", self_snip, method_name, pat_snip) - }, - Some(UnwrapKind::QuestionMark) => { - format!("{}.{}({})?.1", self_snip, method_name, pat_snip) - }, - None => { - format!("{}.{}({}).map(|x| x.1)", self_snip, method_name, pat_snip) - }, - } + format!("{self_snip}.split_once({pat_snip})") } }, + IterUsageKind::Nth(1) => { + let (r, field) = if reverse { ("r", 0) } else { ("", 1) }; + + match usage.unwrap_kind { + Some(UnwrapKind::Unwrap) => { + format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}") + }, + Some(UnwrapKind::QuestionMark) => { + format!("{self_snip}.{r}split_once({pat_snip})?.{field}") + }, + None => { + format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})") + }, + } + }, + IterUsageKind::Nth(_) => return, }; span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app); } -enum IterUsageKind { - Next, - Second, - NextTuple, - RNextTuple, +/// checks for +/// +/// ``` +/// let mut iter = "a.b.c".splitn(2, '.'); +/// let a = iter.next(); +/// let b = iter.next(); +/// ``` +fn check_manual_split_once_indirect( + cx: &LateContext<'_>, + method_name: &str, + expr: &Expr<'_>, + self_arg: &Expr<'_>, + pat_arg: &Expr<'_>, +) -> Option<()> { + let ctxt = expr.span.ctxt(); + let mut parents = cx.tcx.hir().parent_iter(expr.hir_id); + if let (_, Node::Local(local)) = parents.next()? + && let PatKind::Binding(BindingAnnotation::Mutable, iter_binding_id, iter_ident, None) = local.pat.kind + && let (iter_stmt_id, Node::Stmt(_)) = parents.next()? + && let (_, Node::Block(enclosing_block)) = parents.next()? + + && let mut stmts = enclosing_block + .stmts + .iter() + .skip_while(|stmt| stmt.hir_id != iter_stmt_id) + .skip(1) + + && let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)? + && let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)? + && first.unwrap_kind == second.unwrap_kind + && first.name != second.name + && !local_used_after_expr(cx, iter_binding_id, second.init_expr) + { + let (r, lhs, rhs) = if method_name == "splitn" { + ("", first.name, second.name) + } else { + ("r", second.name, first.name) + }; + let msg = format!("manual implementation of `{r}split_once`"); + + let mut app = Applicability::MachineApplicable; + let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0; + let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0; + + span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| { + diag.span_label(first.span, "first usage here"); + diag.span_label(second.span, "second usage here"); + + let unwrap = match first.unwrap_kind { + UnwrapKind::Unwrap => ".unwrap()", + UnwrapKind::QuestionMark => "?", + }; + diag.span_suggestion_verbose( + local.span, + &format!("try `{r}split_once`"), + format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"), + app, + ); + + let remove_msg = format!("remove the `{iter_ident}` usages"); + diag.span_suggestion( + first.span, + &remove_msg, + String::new(), + app, + ); + diag.span_suggestion( + second.span, + &remove_msg, + String::new(), + app, + ); + }); + } + + Some(()) } +#[derive(Debug)] +struct IndirectUsage<'a> { + name: Symbol, + span: Span, + init_expr: &'a Expr<'a>, + unwrap_kind: UnwrapKind, +} + +/// returns `Some(IndirectUsage)` for e.g. +/// +/// ```ignore +/// let name = binding.next()?; +/// let name = binding.next().unwrap(); +/// ``` +fn indirect_usage<'tcx>( + cx: &LateContext<'tcx>, + stmt: &Stmt<'tcx>, + binding: HirId, + ctxt: SyntaxContext, +) -> Option> { + if let StmtKind::Local(Local { + pat: + Pat { + kind: PatKind::Binding(BindingAnnotation::Unannotated, _, ident, None), + .. + }, + init: Some(init_expr), + hir_id: local_hir_id, + .. + }) = stmt.kind + { + let mut path_to_binding = None; + expr_visitor(cx, |expr| { + if path_to_local_id(expr, binding) { + path_to_binding = Some(expr); + } + + path_to_binding.is_none() + }) + .visit_expr(init_expr); + + let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id); + let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?; + + let (parent_id, _) = parents.find(|(_, node)| { + !matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Match(.., MatchSource::TryDesugar), + .. + }) + ) + })?; + + if let IterUsage { + kind: IterUsageKind::Nth(0), + unwrap_kind: Some(unwrap_kind), + .. + } = iter_usage + { + if parent_id == *local_hir_id { + return Some(IndirectUsage { + name: ident.name, + span: stmt.span, + init_expr, + unwrap_kind, + }); + } + } + } + + None +} + +#[derive(Debug, Clone, Copy)] +enum IterUsageKind { + Nth(u128), + NextTuple, +} + +#[derive(Debug, PartialEq)] enum UnwrapKind { Unwrap, QuestionMark, } +#[derive(Debug)] struct IterUsage { kind: IterUsageKind, unwrap_kind: Option, @@ -128,7 +289,6 @@ fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator)>, - reverse: bool, ) -> Option { let (kind, span) = match iter.next() { Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { @@ -141,13 +301,7 @@ fn parse_iter_usage<'tcx>( let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?; match (name.ident.as_str(), args) { - ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if reverse { - (IterUsageKind::Second, e.span) - } else { - (IterUsageKind::Next, e.span) - } - }, + ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span), ("next_tuple", []) => { return if_chain! { if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE); @@ -157,7 +311,7 @@ fn parse_iter_usage<'tcx>( if subs.len() == 2; then { Some(IterUsage { - kind: if reverse { IterUsageKind::RNextTuple } else { IterUsageKind::NextTuple }, + kind: IterUsageKind::NextTuple, span: e.span, unwrap_kind: None }) @@ -185,11 +339,7 @@ fn parse_iter_usage<'tcx>( } } }; - match if reverse { idx ^ 1 } else { idx } { - 0 => (IterUsageKind::Next, span), - 1 => (IterUsageKind::Second, span), - _ => return None, - } + (IterUsageKind::Nth(idx), span) } else { return None; } @@ -238,86 +388,3 @@ fn parse_iter_usage<'tcx>( span, }) } - -use super::NEEDLESS_SPLITN; - -pub(super) fn check_needless_splitn( - cx: &LateContext<'_>, - method_name: &str, - expr: &Expr<'_>, - self_arg: &Expr<'_>, - pat_arg: &Expr<'_>, - count: u128, -) { - if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { - return; - } - let ctxt = expr.span.ctxt(); - let mut app = Applicability::MachineApplicable; - let (reverse, message) = if method_name == "splitn" { - (false, "unnecessary use of `splitn`") - } else { - (true, "unnecessary use of `rsplitn`") - }; - if_chain! { - if count >= 2; - if check_iter(cx, ctxt, cx.tcx.hir().parent_iter(expr.hir_id), count); - then { - span_lint_and_sugg( - cx, - NEEDLESS_SPLITN, - expr.span, - message, - "try this", - format!( - "{}.{}({})", - snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0, - if reverse {"rsplit"} else {"split"}, - snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0 - ), - app, - ); - } - } -} - -fn check_iter<'tcx>( - cx: &LateContext<'tcx>, - ctxt: SyntaxContext, - mut iter: impl Iterator)>, - count: u128, -) -> bool { - match iter.next() { - Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => { - let (name, args) = if let ExprKind::MethodCall(name, [_, args @ ..], _) = e.kind { - (name, args) - } else { - return false; - }; - if_chain! { - if let Some(did) = cx.typeck_results().type_dependent_def_id(e.hir_id); - if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - then { - match (name.ident.as_str(), args) { - ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - return true; - }, - ("next_tuple", []) if count > 2 => { - return true; - }, - ("nth", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => { - if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) { - if count > idx + 1 { - return true; - } - } - }, - _ => return false, - } - } - } - }, - _ => return false, - }; - false -} diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 1555758fc4ad..02b882e8b55e 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -5,6 +5,8 @@ use clippy_utils::source::snippet_opt; use clippy_utils::ty::{ contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs, }; +use clippy_utils::{meets_msrv, msrvs}; + use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind}; @@ -13,12 +15,19 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; +use rustc_semver::RustcVersion; use rustc_span::{sym, Symbol}; use std::cmp::max; use super::UNNECESSARY_TO_OWNED; -pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { +pub fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + method_name: Symbol, + args: &'tcx [Expr<'tcx>], + msrv: Option<&RustcVersion>, +) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let [receiver] = args; @@ -33,7 +42,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) { return; } - if check_into_iter_call_arg(cx, expr, method_name, receiver) { + if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) { return; } check_other_call_arg(cx, expr, method_name, receiver); @@ -178,7 +187,13 @@ fn check_addr_of_expr( /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { +fn check_into_iter_call_arg( + cx: &LateContext<'_>, + expr: &Expr<'_>, + method_name: Symbol, + receiver: &Expr<'_>, + msrv: Option<&RustcVersion>, +) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -192,7 +207,7 @@ fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) { "copied" } else { "cloned" }; + let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" }; // The next suggestion may be incorrect because the removal of the `to_owned`-like // function could cause the iterator to hold a reference to a resource that is used // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs index aecfea9c141c..4b368d3ffae2 100644 --- a/clippy_lints/src/methods/wrong_self_convention.rs +++ b/clippy_lints/src/methods/wrong_self_convention.rs @@ -14,7 +14,7 @@ const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [ (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]), (&[Convention::StartsWith("from_")], &[SelfKind::No]), (&[Convention::StartsWith("into_")], &[SelfKind::Value]), - (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]), + (&[Convention::StartsWith("is_")], &[SelfKind::RefMut, SelfKind::Ref, SelfKind::No]), (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]), (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]), diff --git a/clippy_lints/src/misc_early/mod.rs b/clippy_lints/src/misc_early/mod.rs index d955fad7d41a..6860b60acbdb 100644 --- a/clippy_lints/src/misc_early/mod.rs +++ b/clippy_lints/src/misc_early/mod.rs @@ -361,7 +361,7 @@ impl MiscEarlyLints { // See for a regression. // FIXME: Find a better way to detect those cases. let lit_snip = match snippet_opt(cx, lit.span) { - Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip, + Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip, _ => return, }; diff --git a/clippy_lints/src/missing_inline.rs b/clippy_lints/src/missing_inline.rs index ac2f16b49e3f..0d9532991898 100644 --- a/clippy_lints/src/missing_inline.rs +++ b/clippy_lints/src/missing_inline.rs @@ -44,7 +44,7 @@ declare_clippy_lint! { /// pub struct PubBaz; /// impl PubBaz { /// fn private() {} // ok - /// pub fn not_ptrivate() {} // missing #[inline] + /// pub fn not_private() {} // missing #[inline] /// } /// /// impl Bar for PubBaz { @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { let attrs = cx.tcx.hir().attrs(it.hir_id()); check_missing_inline_attrs(cx, attrs, it.span, desc); }, - hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => { + hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _generics, _bounds, trait_items) => { // note: we need to check if the trait is exported so we can't use // `LateLintPass::check_trait_item` here. for tit in trait_items { diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs index a8a8d174a823..95395e2e136d 100644 --- a/clippy_lints/src/needless_bitwise_bool.rs +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } -fn suggession_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Binary(ref op, left, right) = expr.kind { if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { let op_snippet = match op.node { @@ -75,7 +75,7 @@ impl LateLintPass<'_> for NeedlessBitwiseBool { expr.span, "use of bitwise operator instead of lazy operator between booleans", |diag| { - if let Some(sugg) = suggession_snippet(cx, expr) { + if let Some(sugg) = suggesstion_snippet(cx, expr) { diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 9957afcbf04a..b70871b38bea 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -1,10 +1,14 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_opt; -use clippy_utils::visitors::{expr_visitor, is_local_used}; -use rustc_errors::Applicability; +use clippy_utils::ty::needs_ordered_drop; +use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used}; +use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::intravisit::Visitor; -use rustc_hir::{Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind}; +use rustc_hir::{ + BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, + StmtKind, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -73,6 +77,31 @@ fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> seen } +fn contains_let(cond: &Expr<'_>) -> bool { + let mut seen = false; + expr_visitor_no_bodies(|expr| { + if let ExprKind::Let(_) = expr.kind { + seen = true; + } + + !seen + }) + .visit_expr(cond); + + seen +} + +fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { + let StmtKind::Local(local) = stmt.kind else { return false }; + !local.pat.walk_short(|pat| { + if let PatKind::Binding(.., None) = pat.kind { + !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat)) + } else { + true + } + }) +} + #[derive(Debug)] struct LocalAssign { lhs_id: HirId, @@ -187,11 +216,14 @@ fn first_usage<'tcx>( local_stmt_id: HirId, block: &'tcx Block<'tcx>, ) -> Option> { + let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id)); + block .stmts .iter() .skip_while(|stmt| stmt.hir_id != local_stmt_id) .skip(1) + .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt)) .find(|&stmt| is_local_used(cx, stmt, binding_id)) .and_then(|stmt| match stmt.kind { StmtKind::Expr(expr) => Some(Usage { @@ -235,12 +267,15 @@ fn check<'tcx>( match usage.expr.kind { ExprKind::Assign(..) => { let assign = LocalAssign::new(cx, usage.expr, binding_id)?; + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); span_lint_and_then( cx, NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initalization", + msg_span, + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion( local_stmt.span, @@ -258,14 +293,14 @@ fn check<'tcx>( }, ); }, - ExprKind::If(_, then_expr, Some(else_expr)) => { + ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; span_lint_and_then( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); @@ -296,7 +331,7 @@ fn check<'tcx>( cx, NEEDLESS_LATE_INIT, local_stmt.span, - "unneeded late initalization", + "unneeded late initialization", |diag| { diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability); @@ -333,12 +368,11 @@ fn check<'tcx>( impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); - if_chain! { if let Local { init: None, pat: &Pat { - kind: PatKind::Binding(_, binding_id, _, None), + kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None), .. }, source: LocalSource::Normal, diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 96c00c205ff2..2f733f221d57 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, - ref generics, + generics, self_ty: impl_self_ty, items, .. diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 0d0c88b02c78..e3bc40c4b491 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -197,7 +197,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { if interned_name.chars().any(char::is_uppercase) { return; } - if interned_name.chars().all(|c| c.is_digit(10) || c == '_') { + if interned_name.chars().all(|c| c.is_ascii_digit() || c == '_') { span_lint( self.0.cx, JUST_UNDERSCORES_AND_DIGITS, diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index c19cea661042..e8532db4f711 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// ### Known problems /// The actual meaning can be the intended one. `\x00` can be used in these - /// cases to be unambigious. + /// cases to be unambiguous. /// /// The lint does not trigger for format strings in `print!()`, `write!()` /// and friends since the string is already preprocessed when Clippy lints diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index 8e61f2347767..beb812793f81 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -1,6 +1,7 @@ use std::collections::VecDeque; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_lint_allowed; use itertools::{izip, Itertools}; use rustc_ast::{walk_list, Label, Mutability}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -8,7 +9,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor}; use rustc_hir::{ Arm, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp, @@ -33,6 +34,9 @@ declare_clippy_lint! { /// and the assigned variables are also only in recursion, it is useless. /// /// ### Known problems + /// Too many code paths in the linting code are currently untested and prone to produce false + /// positives or are prone to have performance implications. + /// /// In some cases, this would not catch all useless arguments. /// /// ```rust @@ -85,7 +89,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.60.0"] pub ONLY_USED_IN_RECURSION, - complexity, + nursery, "arguments that is only used in recursion can be removed" } declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]); @@ -100,6 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { _: Span, id: HirId, ) { + if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) { + return; + } if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind { let def_id = id.owner.to_def_id(); let data = cx.tcx.def_path(def_id).data; @@ -145,7 +152,8 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { is_method: matches!(kind, FnKind::Method(..)), has_self, ty_res, - ty_ctx: cx.tcx, + tcx: cx.tcx, + visited_exprs: FxHashSet::default(), }; visitor.visit_expr(&body.value); @@ -206,19 +214,13 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion { } pub fn is_primitive(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true, - ty::Ref(_, t, _) => is_primitive(*t), - _ => false, - } + let ty = ty.peel_refs(); + ty.is_primitive() || ty.is_str() } pub fn is_array(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Array(..) | ty::Slice(..) => true, - ty::Ref(_, t, _) => is_array(*t), - _ => false, - } + let ty = ty.peel_refs(); + ty.is_array() || ty.is_array_slice() } /// This builds the graph of side effect. @@ -250,40 +252,30 @@ pub struct SideEffectVisit<'tcx> { is_method: bool, has_self: bool, ty_res: &'tcx TypeckResults<'tcx>, - ty_ctx: TyCtxt<'tcx>, + tcx: TyCtxt<'tcx>, + visited_exprs: FxHashSet, } impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { - fn visit_block(&mut self, b: &'tcx Block<'tcx>) { - b.stmts.iter().for_each(|stmt| { - self.visit_stmt(stmt); - self.ret_vars.clear(); - }); - walk_list!(self, visit_expr, b.expr); - } - fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { match s.kind { StmtKind::Local(Local { pat, init: Some(init), .. }) => { self.visit_pat_expr(pat, init, false); - self.ret_vars.clear(); }, - StmtKind::Item(i) => { - let item = self.ty_ctx.hir().item(i); - self.visit_item(item); - self.ret_vars.clear(); - }, - StmtKind::Expr(e) | StmtKind::Semi(e) => { - self.visit_expr(e); - self.ret_vars.clear(); + StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => { + walk_stmt(self, s); }, StmtKind::Local(_) => {}, } + self.ret_vars.clear(); } fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if !self.visited_exprs.insert(ex.hir_id) { + return; + } match ex.kind { ExprKind::Array(exprs) | ExprKind::Tup(exprs) => { self.ret_vars = exprs @@ -307,7 +299,7 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> { ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms), // since analysing the closure is not easy, just set all variables in it to side-effect ExprKind::Closure(_, _, body_id, _, _) => { - let body = self.ty_ctx.hir().body(body_id); + let body = self.tcx.hir().body(body_id); self.visit_body(body); let vars = std::mem::take(&mut self.ret_vars); self.add_side_effect(vars); diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs index c9f807f2aa3a..ea5a8f0858b6 100644 --- a/clippy_lints/src/option_if_let_else.rs +++ b/clippy_lints/src/option_if_let_else.rs @@ -78,7 +78,7 @@ fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool { /// A struct containing information about occurrences of the /// `if let Some(..) = .. else` construct that this lint detects. -struct OptionIfLetElseOccurence { +struct OptionIfLetElseOccurrence { option: String, method_sugg: String, some_expr: String, @@ -100,9 +100,9 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo } /// If this expression is the option if let/else construct we're detecting, then -/// this function returns an `OptionIfLetElseOccurence` struct with details if +/// this function returns an `OptionIfLetElseOccurrence` struct with details if /// this construct is found, or None if this construct is not found. -fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option { if_chain! { if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly if !in_constant(cx, expr.hir_id); @@ -154,7 +154,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> } } } - Some(OptionIfLetElseOccurence { + Some(OptionIfLetElseOccurrence { option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut), method_sugg: method_sugg.to_string(), some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")), diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 48a2666a2e0c..c35eeeac67a3 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -3,6 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::expr_sig; +use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths}; use if_chain::if_chain; use rustc_errors::{Applicability, MultiSpan}; @@ -10,9 +11,9 @@ use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirIdMap; use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{ - self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, + self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn, - TraitItem, TraitItemKind, TyKind, + TraitItem, TraitItemKind, TyKind, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; @@ -88,19 +89,26 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// This lint checks for functions that take immutable - /// references and return mutable ones. + /// This lint checks for functions that take immutable references and return + /// mutable ones. This will not trigger if no unsafe code exists as there + /// are multiple safe functions which will do this transformation + /// + /// To be on the conservative side, if there's at least one mutable + /// reference with the output lifetime, this lint will not trigger. /// /// ### Why is this bad? - /// This is trivially unsound, as one can create two - /// mutable references from the same (immutable!) source. - /// This [error](https://github.com/rust-lang/rust/issues/39465) - /// actually lead to an interim Rust release 1.15.1. + /// Creating a mutable reference which can be repeatably derived from an + /// immutable reference is unsound as it allows creating multiple live + /// mutable references to the same object. + /// + /// This [error](https://github.com/rust-lang/rust/issues/39465) actually + /// lead to an interim Rust release 1.15.1. /// /// ### Known problems - /// To be on the conservative side, if there's at least one - /// mutable reference with the output lifetime, this lint will not trigger. - /// In practice, this case is unlikely anyway. + /// This pattern is used by memory allocators to allow allocating multiple + /// objects while returning mutable references to each one. So long as + /// different mutable references are returned each time such a function may + /// be safe. /// /// ### Example /// ```ignore @@ -145,7 +153,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } - check_mut_from_ref(cx, sig.decl); + check_mut_from_ref(cx, sig, None); for arg in check_fn_args( cx, cx.tcx.fn_sig(item.def_id).skip_binder().inputs(), @@ -170,10 +178,10 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { let hir = cx.tcx.hir(); let mut parents = hir.parent_iter(body.value.hir_id); - let (item_id, decl, is_trait_item) = match parents.next() { + let (item_id, sig, is_trait_item) = match parents.next() { Some((_, Node::Item(i))) => { if let ItemKind::Fn(sig, ..) = &i.kind { - (i.def_id, sig.decl, false) + (i.def_id, sig, false) } else { return; } @@ -185,14 +193,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { return; } if let ImplItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig.decl, false) + (i.def_id, sig, false) } else { return; } }, Some((_, Node::TraitItem(i))) => { if let TraitItemKind::Fn(sig, _) = &i.kind { - (i.def_id, sig.decl, true) + (i.def_id, sig, true) } else { return; } @@ -200,7 +208,8 @@ impl<'tcx> LateLintPass<'tcx> for Ptr { _ => return, }; - check_mut_from_ref(cx, decl); + check_mut_from_ref(cx, sig, Some(body)); + let decl = sig.decl; let sig = cx.tcx.fn_sig(item_id).skip_binder(); let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params) .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not) @@ -473,31 +482,31 @@ fn check_fn_args<'cx, 'tcx: 'cx>( }) } -fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) { - if let FnRetTy::Return(ty) = decl.output { - if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) { - let mut immutables = vec![]; - for (_, mutbl, argspan) in decl - .inputs - .iter() - .filter_map(get_rptr_lm) - .filter(|&(lt, _, _)| lt.name == out.name) - { - if mutbl == Mutability::Mut { - return; - } - immutables.push(argspan); - } - if immutables.is_empty() { - return; - } +fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) { + if let FnRetTy::Return(ty) = sig.decl.output + && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) + { + let args: Option> = sig + .decl + .inputs + .iter() + .filter_map(get_rptr_lm) + .filter(|&(lt, _, _)| lt.name == out.name) + .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span)) + .collect(); + if let Some(args) = args + && !args.is_empty() + && body.map_or(true, |body| { + sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value) + }) + { span_lint_and_then( cx, MUT_FROM_REF, ty.span, "mutable borrow from immutable input(s)", |diag| { - let ms = MultiSpan::from_spans(immutables); + let ms = MultiSpan::from_spans(args); diag.span_note(ms, "immutable borrow here"); }, ); diff --git a/clippy_lints/src/pub_use.rs b/clippy_lints/src/pub_use.rs new file mode 100644 index 000000000000..9d2b0cedb60a --- /dev/null +++ b/clippy_lints/src/pub_use.rs @@ -0,0 +1,56 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Item, ItemKind, VisibilityKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// + /// Restricts the usage of `pub use ...` + /// + /// ### Why is this bad? + /// + /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent + /// unintentional exports or to encourage placing exported items directly in public modules + /// + /// ### Example + /// ```rust + /// pub mod outer { + /// mod inner { + /// pub struct Test {} + /// } + /// pub use inner::Test; + /// } + /// + /// use outer::Test; + /// ``` + /// Use instead: + /// ```rust + /// pub mod outer { + /// pub struct Test {} + /// } + /// + /// use outer::Test; + /// ``` + #[clippy::version = "1.62.0"] + pub PUB_USE, + restriction, + "restricts the usage of `pub use`" +} +declare_lint_pass!(PubUse => [PUB_USE]); + +impl EarlyLintPass for PubUse { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Use(_) = item.kind && + let VisibilityKind::Public = item.vis.kind { + span_lint_and_help( + cx, + PUB_USE, + item.span, + "using `pub use`", + None, + "move the exported item to a public module instead", + ); + } + } +} diff --git a/clippy_lints/src/redundant_pub_crate.rs b/clippy_lints/src/redundant_pub_crate.rs index e2e2400f8e26..323326381d40 100644 --- a/clippy_lints/src/redundant_pub_crate.rs +++ b/clippy_lints/src/redundant_pub_crate.rs @@ -1,10 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::CRATE_DEF_ID; +use rustc_span::hygiene::MacroKind; declare_clippy_lint! { /// ### What it does @@ -43,8 +45,11 @@ impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]); impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) { - if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) { + if_chain! { + if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()); + if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false); + if is_not_macro_export(item); + then { let span = item.span.with_hi(item.ident.span.hi()); let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id()); span_lint_and_then( @@ -75,3 +80,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { } } } + +fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { + if let ItemKind::Use(path, _) = item.kind { + if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res { + return false; + } + } else if let ItemKind::Macro(..) = item.kind { + return false; + } + + true +} diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs new file mode 100644 index 000000000000..bfc03116fe2d --- /dev/null +++ b/clippy_lints/src/renamed_lints.rs @@ -0,0 +1,39 @@ +// This file is managed by `cargo dev rename_lint`. Prefer using that when possible. + +#[rustfmt::skip] +pub static RENAMED_LINTS: &[(&str, &str)] = &[ + ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"), + ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"), + ("clippy::box_vec", "clippy::box_collection"), + ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"), + ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"), + ("clippy::disallowed_method", "clippy::disallowed_methods"), + ("clippy::disallowed_type", "clippy::disallowed_types"), + ("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"), + ("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"), + ("clippy::identity_conversion", "clippy::useless_conversion"), + ("clippy::if_let_some_result", "clippy::match_result_ok"), + ("clippy::new_without_default_derive", "clippy::new_without_default"), + ("clippy::option_and_then_some", "clippy::bind_instead_of_map"), + ("clippy::option_expect_used", "clippy::expect_used"), + ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"), + ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::option_unwrap_used", "clippy::unwrap_used"), + ("clippy::ref_in_deref", "clippy::needless_borrow"), + ("clippy::result_expect_used", "clippy::expect_used"), + ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"), + ("clippy::result_unwrap_used", "clippy::unwrap_used"), + ("clippy::single_char_push_str", "clippy::single_char_add_str"), + ("clippy::stutter", "clippy::module_name_repetitions"), + ("clippy::to_string_in_display", "clippy::recursive_format_impl"), + ("clippy::zero_width_space", "clippy::invisible_characters"), + ("clippy::drop_bounds", "drop_bounds"), + ("clippy::into_iter_on_array", "array_into_iter"), + ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"), + ("clippy::invalid_ref", "invalid_value"), + ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"), + ("clippy::panic_params", "non_fmt_panics"), + ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"), + ("clippy::unknown_clippy_lints", "unknown_lints"), + ("clippy::unused_label", "unused_labels"), +]; diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index a01e2f2db3af..f63925a2f143 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -51,114 +51,110 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { let mut map = FxHashMap::::default(); for id in cx.tcx.hir().items() { - if !matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) { - continue; - } - - let item = cx.tcx.hir().item(id); - if let ItemKind::Impl(Impl { - items, - of_trait, - self_ty, - .. - }) = &item.kind + if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) + && let item = cx.tcx.hir().item(id) + && let ItemKind::Impl(Impl { + items, + of_trait, + self_ty, + .. + }) = &item.kind + && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { - if let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { - if !map.contains_key(res) { - map.insert( - *res, - ExistingName { - impl_methods: BTreeMap::new(), - trait_methods: BTreeMap::new(), - }, - ); - } - let existing_name = map.get_mut(res).unwrap(); - - match of_trait { - Some(trait_ref) => { - let mut methods_in_trait: BTreeSet = if_chain! { - if let Some(Node::TraitRef(TraitRef { path, .. })) = - cx.tcx.hir().find(trait_ref.hir_ref_id); - if let Res::Def(DefKind::Trait, did) = path.res; - then{ - // FIXME: if - // `rustc_middle::ty::assoc::AssocItems::items` is public, - // we can iterate its keys instead of `in_definition_order`, - // which's more efficient - cx.tcx - .associated_items(did) - .in_definition_order() - .filter(|assoc_item| { - matches!(assoc_item.kind, AssocKind::Fn) - }) - .map(|assoc_item| assoc_item.name) - .collect() - }else{ - BTreeSet::new() - } - }; - - let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { - if let Some(impl_span) = existing_name.impl_methods.get(&method_name) { - span_lint_and_then( - cx, - SAME_NAME_METHOD, - *impl_span, - "method's name is the same as an existing method in a trait", - |diag| { - diag.span_note( - trait_method_span, - &format!("existing `{}` defined here", method_name), - ); - }, - ); - } - if let Some(v) = existing_name.trait_methods.get_mut(&method_name) { - v.push(trait_method_span); - } else { - existing_name.trait_methods.insert(method_name, vec![trait_method_span]); - } - }; - - for impl_item_ref in (*items).iter().filter(|impl_item_ref| { - matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) - }) { - let method_name = impl_item_ref.ident.name; - methods_in_trait.remove(&method_name); - check_trait_method(method_name, impl_item_ref.span); - } - - for method_name in methods_in_trait { - check_trait_method(method_name, item.span); - } + if !map.contains_key(res) { + map.insert( + *res, + ExistingName { + impl_methods: BTreeMap::new(), + trait_methods: BTreeMap::new(), }, - None => { - for impl_item_ref in (*items).iter().filter(|impl_item_ref| { - matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) - }) { - let method_name = impl_item_ref.ident.name; - let impl_span = impl_item_ref.span; - if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) { - span_lint_and_then( - cx, - SAME_NAME_METHOD, - impl_span, - "method's name is the same as an existing method in a trait", - |diag| { - // TODO should we `span_note` on every trait? - // iterate on trait_spans? - diag.span_note( - trait_spans[0], - &format!("existing `{}` defined here", method_name), - ); - }, - ); - } - existing_name.impl_methods.insert(method_name, impl_span); + ); + } + let existing_name = map.get_mut(res).unwrap(); + + match of_trait { + Some(trait_ref) => { + let mut methods_in_trait: BTreeSet = if_chain! { + if let Some(Node::TraitRef(TraitRef { path, .. })) = + cx.tcx.hir().find(trait_ref.hir_ref_id); + if let Res::Def(DefKind::Trait, did) = path.res; + then{ + // FIXME: if + // `rustc_middle::ty::assoc::AssocItems::items` is public, + // we can iterate its keys instead of `in_definition_order`, + // which's more efficient + cx.tcx + .associated_items(did) + .in_definition_order() + .filter(|assoc_item| { + matches!(assoc_item.kind, AssocKind::Fn) + }) + .map(|assoc_item| assoc_item.name) + .collect() + }else{ + BTreeSet::new() } - }, - } + }; + + let mut check_trait_method = |method_name: Symbol, trait_method_span: Span| { + if let Some(impl_span) = existing_name.impl_methods.get(&method_name) { + span_lint_and_then( + cx, + SAME_NAME_METHOD, + *impl_span, + "method's name is the same as an existing method in a trait", + |diag| { + diag.span_note( + trait_method_span, + &format!("existing `{}` defined here", method_name), + ); + }, + ); + } + if let Some(v) = existing_name.trait_methods.get_mut(&method_name) { + v.push(trait_method_span); + } else { + existing_name.trait_methods.insert(method_name, vec![trait_method_span]); + } + }; + + for impl_item_ref in (*items).iter().filter(|impl_item_ref| { + matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) + }) { + let method_name = impl_item_ref.ident.name; + methods_in_trait.remove(&method_name); + check_trait_method(method_name, impl_item_ref.span); + } + + for method_name in methods_in_trait { + check_trait_method(method_name, item.span); + } + }, + None => { + for impl_item_ref in (*items).iter().filter(|impl_item_ref| { + matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }) + }) { + let method_name = impl_item_ref.ident.name; + let impl_span = impl_item_ref.span; + if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) { + span_lint_and_then( + cx, + SAME_NAME_METHOD, + impl_span, + "method's name is the same as an existing method in a trait", + |diag| { + // TODO should we `span_note` on every trait? + // iterate on trait_spans? + diag.span_note( + trait_spans[0], + &format!("existing `{}` defined here", method_name), + ); + }, + ); + } + existing_name.impl_methods.insert(method_name, impl_span); + } + }, } } } diff --git a/clippy_lints/src/stable_sort_primitive.rs b/clippy_lints/src/stable_sort_primitive.rs index bcd28b429784..a6c685df721d 100644 --- a/clippy_lints/src/stable_sort_primitive.rs +++ b/clippy_lints/src/stable_sort_primitive.rs @@ -9,15 +9,25 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does /// When sorting primitive values (integers, bools, chars, as well - /// as arrays, slices, and tuples of such items), it is better to + /// as arrays, slices, and tuples of such items), it is typically better to /// use an unstable sort than a stable sort. /// /// ### Why is this bad? - /// Using a stable sort consumes more memory and cpu cycles. Because - /// values which compare equal are identical, preserving their + /// Typically, using a stable sort consumes more memory and cpu cycles. + /// Because values which compare equal are identical, preserving their /// relative order (the guarantee that a stable sort provides) means /// nothing, while the extra costs still apply. /// + /// ### Known problems + /// + /// As pointed out in + /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241), + /// a stable sort can instead be significantly faster for certain scenarios + /// (eg. when a sorted vector is extended with new data and resorted). + /// + /// For more information and benchmarking results, please refer to the + /// issue linked above. + /// /// ### Example /// ```rust /// let mut vec = vec![2, 1, 3]; @@ -30,7 +40,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub STABLE_SORT_PRIMITIVE, - perf, + pedantic, "use of sort() when sort_unstable() is equivalent" } @@ -126,7 +136,7 @@ impl LateLintPass<'_> for StableSortPrimitive { Applicability::MachineApplicable, ); diag.note( - "an unstable sort would perform faster without any observable difference for this data type", + "an unstable sort typically performs faster without any observable difference for this data type", ); }, ); diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index 3573f632a367..7c196ccaa8cc 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -5,6 +5,7 @@ use clippy_utils::{get_parent_expr, is_lint_allowed, match_function_call, method use clippy_utils::{peel_blocks, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; +use rustc_hir::def_id::DefId; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; @@ -451,3 +452,58 @@ impl<'tcx> LateLintPass<'tcx> for StringToString { } } } + +declare_clippy_lint! { + /// ### What it does + /// Warns about calling `str::trim` (or variants) before `str::split_whitespace`. + /// + /// ### Why is this bad? + /// `split_whitespace` already ignores leading and trailing whitespace. + /// + /// ### Example + /// ```rust + /// " A B C ".trim().split_whitespace(); + /// ``` + /// Use instead: + /// ```rust + /// " A B C ".split_whitespace(); + /// ``` + #[clippy::version = "1.62.0"] + pub TRIM_SPLIT_WHITESPACE, + style, + "using `str::trim()` or alike before `str::split_whitespace`" +} +declare_lint_pass!(TrimSplitWhitespace => [TRIM_SPLIT_WHITESPACE]); + +impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + let tyckres = cx.typeck_results(); + if_chain! { + if let ExprKind::MethodCall(path, [split_recv], split_ws_span) = expr.kind; + if path.ident.name == sym!(split_whitespace); + if let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id); + if cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id); + if let ExprKind::MethodCall(path, [_trim_recv], trim_span) = split_recv.kind; + if let trim_fn_name @ ("trim" | "trim_start" | "trim_end") = path.ident.name.as_str(); + if let Some(trim_def_id) = tyckres.type_dependent_def_id(split_recv.hir_id); + if is_one_of_trim_diagnostic_items(cx, trim_def_id); + then { + span_lint_and_sugg( + cx, + TRIM_SPLIT_WHITESPACE, + trim_span.with_hi(split_ws_span.lo()), + &format!("found call to `str::{}` before `str::split_whitespace`", trim_fn_name), + &format!("remove `{}()`", trim_fn_name), + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool { + cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id) + || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id) +} diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index b5dd27ff80de..c4c1aa11004a 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -550,7 +550,7 @@ fn ident_difference_expr_with_base_location( // IdentIter, then the output of this function will be almost always be correct // in practice. // - // If it turns out that problematic cases are more prelavent than we assume, + // If it turns out that problematic cases are more prevalent than we assume, // then we should be able to change this function to do the correct traversal, // without needing to change the rest of the code. diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 3d1b2ee925bc..78e388a49af1 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -13,6 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; +use std::fmt::Write as _; declare_clippy_lint! { /// ### What it does @@ -34,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, - pedantic, + nursery, "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } @@ -64,7 +65,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub TRAIT_DUPLICATION_IN_BOUNDS, - pedantic, + nursery, "Check if the same trait bounds are specified twice during a function declaration" } @@ -182,19 +183,19 @@ impl TraitBounds { for b in v.iter() { if let GenericBound::Trait(ref poly_trait_ref, _) = b { let path = &poly_trait_ref.trait_ref.path; - hint_string.push_str(&format!( + let _ = write!(hint_string, " {} +", snippet_with_applicability(cx, path.span, "..", &mut applicability) - )); + ); } } for b in p.bounds.iter() { if let GenericBound::Trait(ref poly_trait_ref, _) = b { let path = &poly_trait_ref.trait_ref.path; - hint_string.push_str(&format!( + let _ = write!(hint_string, " {} +", snippet_with_applicability(cx, path.span, "..", &mut applicability) - )); + ); } } hint_string.truncate(hint_string.len() - 2); @@ -241,7 +242,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { ); } else { - trait_resolutions_direct.push((res_where, span_where)) + trait_resolutions_direct.push((res_where, span_where)); } } } diff --git a/clippy_lints/src/transmute/utils.rs b/clippy_lints/src/transmute/utils.rs index f359b606e454..0cbf5ccefa6d 100644 --- a/clippy_lints/src/transmute/utils.rs +++ b/clippy_lints/src/transmute/utils.rs @@ -1,10 +1,9 @@ use clippy_utils::last_path_segment; use clippy_utils::source::snippet; -use clippy_utils::ty::is_normalizable; use if_chain::if_chain; use rustc_hir::{Expr, GenericArg, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_middle::ty::{self, cast::CastKind, Ty}; +use rustc_middle::ty::{cast::CastKind, Ty}; use rustc_span::DUMMY_SP; use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited}; @@ -34,15 +33,12 @@ pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty // check if the component types of the transmuted collection and the result have different ABI, // size or alignment pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool { - let empty_param_env = ty::ParamEnv::empty(); - // check if `from` and `to` are normalizable to avoid ICE (#4968) - if !(is_normalizable(cx, empty_param_env, from) && is_normalizable(cx, empty_param_env, to)) { - return false; - } - let from_ty_layout = cx.tcx.layout_of(empty_param_env.and(from)); - let to_ty_layout = cx.tcx.layout_of(empty_param_env.and(to)); - if let (Ok(from_layout), Ok(to_layout)) = (from_ty_layout, to_ty_layout) { - from_layout.size != to_layout.size || from_layout.align != to_layout.align || from_layout.abi != to_layout.abi + if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from) + && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to) + && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from)) + && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to)) + { + from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi } else { // no idea about layout, so don't lint false @@ -91,7 +87,7 @@ fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx> let res = check.do_check(&fn_ctxt); // do_check's documentation says that it might return Ok and create - // errors in the fcx instead of returing Err in some cases. Those cases + // errors in the fcx instead of returning Err in some cases. Those cases // should be filtered out before getting here. assert!( !fn_ctxt.errors_reported_since_creation(), diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 67cc89133189..353a6f6b899e 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -432,8 +432,8 @@ impl Types { fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) { // Ignore functions in trait implementations as they are usually forced by the trait definition. // - // FIXME: idially we would like to warn *if the compicated type can be simplified*, but it's hard to - // check. + // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard + // to check. if context.is_in_trait_impl { return; } diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index c8912a18f185..465d8a914fb2 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -156,8 +156,9 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> .array_windows::<2>() .rev() .map_while(|[start, end]| { - src.get(start.to_usize() - offset..end.to_usize() - offset) - .map(|text| (start.to_usize(), text.trim_start())) + let start = start.to_usize() - offset; + let end = end.to_usize() - offset; + src.get(start..end).map(|text| (start, text.trim_start())) }) .filter(|(_, text)| !text.is_empty()); @@ -182,7 +183,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> let (mut line_start, mut line) = (line_start, line); loop { if line.starts_with("/*") { - let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start(); + let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start(); let mut tokens = tokenize(src); return src[..tokens.next().unwrap().len] .to_ascii_uppercase() diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs index b25a6e3375bb..f3f1f53aac56 100644 --- a/clippy_lints/src/unit_types/let_unit_value.rs +++ b/clippy_lints/src/unit_types/let_unit_value.rs @@ -1,18 +1,46 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_macro_callsite; +use clippy_utils::visitors::for_each_value_source; +use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::{self, Ty, TypeFoldable, TypeVisitor}; use super::LET_UNIT_VALUE; pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { - if let StmtKind::Local(local) = stmt.kind { - if cx.typeck_results().pat_ty(local.pat).is_unit() { - if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() { - return; + if let StmtKind::Local(local) = stmt.kind + && let Some(init) = local.init + && !local.pat.span.from_expansion() + && !in_external_macro(cx.sess(), stmt.span) + && cx.typeck_results().pat_ty(local.pat).is_unit() + { + let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) { + ControlFlow::Continue(()) + } else { + ControlFlow::Break(()) + }).is_continue(); + + if needs_inferred { + if !matches!(local.pat.kind, PatKind::Wild) { + span_lint_and_then( + cx, + LET_UNIT_VALUE, + stmt.span, + "this let-binding has unit value", + |diag| { + diag.span_suggestion( + local.pat.span, + "use a wild (`_`) binding", + "_", + Applicability::MaybeIncorrect, // snippet + ); + }, + ); } + } else { span_lint_and_then( cx, LET_UNIT_VALUE, @@ -33,3 +61,45 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } } } + +fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { + let id = match e.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(ref path), + hir_id, + .. + }, + _, + ) => cx.qpath_res(path, *hir_id).opt_def_id(), + ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(e.hir_id), + _ => return false, + }; + if let Some(id) = id + && let sig = cx.tcx.fn_sig(id).skip_binder() + && let ty::Param(output_ty) = *sig.output().kind() + { + sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index)) + } else { + false + } +} + +fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool { + struct Visitor(u32); + impl<'tcx> TypeVisitor<'tcx> for Visitor { + type BreakTy = (); + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow { + if let ty::Param(ty) = *ty.kind() { + if ty.index == self.0 { + ControlFlow::BREAK + } else { + ControlFlow::CONTINUE + } + } else { + ty.super_visit_with(self) + } + } + } + ty.visit_with(&mut Visitor(index)).is_break() +} diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs index d9f5b53b413a..a9e2073dec25 100644 --- a/clippy_lints/src/unit_types/mod.rs +++ b/clippy_lints/src/unit_types/mod.rs @@ -23,7 +23,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub LET_UNIT_VALUE, - pedantic, + style, "creating a `let` binding to a value of unit type, which usually can't be used afterwards" } diff --git a/clippy_lints/src/unnecessary_owned_empty_strings.rs b/clippy_lints/src/unnecessary_owned_empty_strings.rs new file mode 100644 index 000000000000..8a4f4c0ad971 --- /dev/null +++ b/clippy_lints/src/unnecessary_owned_empty_strings.rs @@ -0,0 +1,81 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_diagnostic_item}; +use clippy_utils::{match_def_path, paths}; +use if_chain::if_chain; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// + /// Detects cases of owned empty strings being passed as an argument to a function expecting `&str` + /// + /// ### Why is this bad? + /// + /// This results in longer and less readable code + /// + /// ### Example + /// ```rust + /// vec!["1", "2", "3"].join(&String::new()); + /// ``` + /// Use instead: + /// ```rust + /// vec!["1", "2", "3"].join(""); + /// ``` + #[clippy::version = "1.62.0"] + pub UNNECESSARY_OWNED_EMPTY_STRINGS, + style, + "detects cases of references to owned empty strings being passed as an argument to a function expecting `&str`" +} +declare_lint_pass!(UnnecessaryOwnedEmptyStrings => [UNNECESSARY_OWNED_EMPTY_STRINGS]); + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if_chain! { + if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner_expr) = expr.kind; + if let ExprKind::Call(fun, args) = inner_expr.kind; + if let ExprKind::Path(ref qpath) = fun.kind; + if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); + if let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind(); + if inner_str.is_str(); + then { + if match_def_path(cx, fun_def_id, &paths::STRING_NEW) { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::new()` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } else { + if_chain! { + if match_def_path(cx, fun_def_id, &paths::FROM_FROM); + if let [.., last_arg] = args; + if let ExprKind::Lit(spanned) = &last_arg.kind; + if let LitKind::Str(symbol, _) = spanned.node; + if symbol.is_empty(); + let inner_expr_type = cx.typeck_results().expr_ty(inner_expr); + if is_type_diagnostic_item(cx, inner_expr_type, sym::String); + then { + span_lint_and_sugg( + cx, + UNNECESSARY_OWNED_EMPTY_STRINGS, + expr.span, + "usage of `&String::from(\"\")` for a function expecting a `&str` argument", + "try", + "\"\"".to_owned(), + Applicability::MachineApplicable, + ); + } + } + } + } + } + } +} diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index 1d4fe9cfd3cf..ae431aac83b8 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{meets_msrv, msrvs, over}; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -25,7 +25,7 @@ declare_clippy_lint! { /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*. /// /// ### Why is this bad? - /// In the example above, `Some` is repeated, which unncessarily complicates the pattern. + /// In the example above, `Some` is repeated, which unnecessarily complicates the pattern. /// /// ### Example /// ```rust @@ -230,6 +230,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) // with which a pattern `C(p_0)` may be formed, // which we would want to join with other `C(p_j)`s. Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) + // Skip immutable refs, as grouping them saves few characters, + // and almost always requires adding parens (increasing noisiness). + // In the case of only two patterns, replacement adds net characters. + | Ref(_, Mutability::Not) // Dealt with elsewhere. | Or(_) | Paren(_) => false, // Transform `box x | ... | box y` into `box (x | y)`. @@ -241,10 +245,10 @@ fn transform_with_focus_on_idx(alternatives: &mut Vec>, focus_idx: usize) |k| matches!(k, Box(_)), |k| always_pat!(k, Box(p) => p), ), - // Transform `&m x | ... | &m y` into `&m (x | y)`. - Ref(target, m1) => extend_with_matching( + // Transform `&mut x | ... | &mut y` into `&mut (x | y)`. + Ref(target, Mutability::Mut) => extend_with_matching( target, start, alternatives, - |k| matches!(k, Ref(_, m2) if m1 == m2), // Mutabilities must match. + |k| matches!(k, Ref(_, Mutability::Mut)), |k| always_pat!(k, Ref(p, _) => p), ), // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`. diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index f8e1021af0ea..138f8bccb3f5 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -30,7 +30,7 @@ declare_clippy_lint! { /// /// ### Known problems /// - Unaddressed false negative in fn bodies of trait implementations - /// - False positive with assotiated types in traits (#4140) + /// - False positive with associated types in traits (#4140) /// /// ### Example /// ```rust diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index d23c85c033b2..ff5be825b781 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -70,7 +70,7 @@ macro_rules! bind { }; } -/// Transforms the given `Option` varibles into `OptionPat>`. +/// Transforms the given `Option` variables into `OptionPat>`. /// This displays as `Some($name)` or `None` when printed. The name of the inner binding /// is set to the name of the variable passed to the macro. macro_rules! opt_bind { diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 271c3a3dd181..74b0168a1794 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -310,6 +310,12 @@ define_Conf! { /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed. /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements. (max_suggested_slice_pattern_length: u64 = 3), + /// Lint: AWAIT_HOLDING_INVALID_TYPE + (await_holding_invalid_types: Vec = Vec::new()), + /// Lint: LARGE_INCLUDE_FILE. + /// + /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes + (max_include_file_size: u64 = 1_000_000), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/dump_hir.rs b/clippy_lints/src/utils/dump_hir.rs new file mode 100644 index 000000000000..01efc527a8c3 --- /dev/null +++ b/clippy_lints/src/utils/dump_hir.rs @@ -0,0 +1,55 @@ +use clippy_utils::get_attr; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// It formats the attached node with `{:#?}` and writes the result to the + /// standard output. This is intended for debugging. + /// + /// ### Examples + /// ```rs + /// #[clippy::dump] + /// use std::mem; + /// + /// #[clippy::dump] + /// fn foo(input: u32) -> u64 { + /// input as u64 + /// } + /// ``` + pub DUMP_HIR, + internal_warn, + "helper to dump info about code" +} + +declare_lint_pass!(DumpHir => [DUMP_HIR]); + +impl<'tcx> LateLintPass<'tcx> for DumpHir { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + if has_attr(cx, item.hir_id()) { + println!("{item:#?}"); + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if has_attr(cx, expr.hir_id) { + println!("{expr:#?}"); + } + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { + match stmt.kind { + hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return, + _ => {}, + } + if has_attr(cx, stmt.hir_id) { + println!("{stmt:#?}"); + } + } +} + +fn has_attr(cx: &LateContext<'_>, hir_id: hir::HirId) -> bool { + let attrs = cx.tcx.hir().attrs(hir_id); + get_attr(cx.sess(), attrs, "dump").count() > 0 +} diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs deleted file mode 100644 index 37b114a0cfbc..000000000000 --- a/clippy_lints/src/utils/inspector.rs +++ /dev/null @@ -1,577 +0,0 @@ -//! checks for attributes - -use clippy_utils::get_attr; -use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece}; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty; -use rustc_session::Session; -use rustc_session::{declare_lint_pass, declare_tool_lint}; - -declare_clippy_lint! { - /// ### What it does - /// Dumps every ast/hir node which has the `#[clippy::dump]` - /// attribute - /// - /// ### Example - /// ```rust,ignore - /// #[clippy::dump] - /// extern crate foo; - /// ``` - /// - /// prints - /// - /// ```text - /// item `foo` - /// visibility inherited from outer item - /// extern crate dylib source: "/path/to/foo.so" - /// ``` - pub DEEP_CODE_INSPECTION, - internal_warn, - "helper to dump info about code" -} - -declare_lint_pass!(DeepCodeInspector => [DEEP_CODE_INSPECTION]); - -impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { - return; - } - print_item(cx, item); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(item.hir_id())) { - return; - } - println!("impl item `{}`", item.ident.name); - match cx.tcx.visibility(item.def_id) { - ty::Visibility::Public => println!("public"), - ty::Visibility::Restricted(def_id) => { - if def_id.is_top_level_module() { - println!("visible crate wide") - } else { - println!("visible in module `{}`", cx.tcx.def_path_str(def_id)) - } - }, - ty::Visibility::Invisible => println!("invisible"), - } - match item.kind { - hir::ImplItemKind::Const(_, body_id) => { - println!("associated constant"); - print_expr(cx, &cx.tcx.hir().body(body_id).value, 1); - }, - hir::ImplItemKind::Fn(..) => println!("method"), - hir::ImplItemKind::TyAlias(_) => println!("associated type"), - } - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) { - return; - } - print_expr(cx, expr, 0); - } - - fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(arm.hir_id)) { - return; - } - print_pat(cx, arm.pat, 1); - if let Some(ref guard) = arm.guard { - println!("guard:"); - print_guard(cx, guard, 1); - } - println!("body:"); - print_expr(cx, arm.body, 1); - } - - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - if !has_attr(cx.sess(), cx.tcx.hir().attrs(stmt.hir_id)) { - return; - } - match stmt.kind { - hir::StmtKind::Local(local) => { - println!("local variable of type {}", cx.typeck_results().node_type(local.hir_id)); - println!("pattern:"); - print_pat(cx, local.pat, 0); - if let Some(e) = local.init { - println!("init expression:"); - print_expr(cx, e, 0); - } - }, - hir::StmtKind::Item(_) => println!("item decl"), - hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0), - } - } -} - -fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool { - get_attr(sess, attrs, "dump").count() > 0 -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - println!("{}ty: {}", ind, cx.typeck_results().expr_ty(expr)); - println!( - "{}adjustments: {:?}", - ind, - cx.typeck_results().adjustments().get(expr.hir_id) - ); - match expr.kind { - hir::ExprKind::Box(e) => { - println!("{}Box", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Array(v) => { - println!("{}Array", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Call(func, args) => { - println!("{}Call", ind); - println!("{}function:", ind); - print_expr(cx, func, indent + 1); - println!("{}arguments:", ind); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Let(hir::Let { pat, init, ty, .. }) => { - print_pat(cx, pat, indent + 1); - if let Some(ty) = ty { - println!("{} type annotation: {:?}", ind, ty); - } - print_expr(cx, init, indent + 1); - }, - hir::ExprKind::MethodCall(path, args, _) => { - println!("{}MethodCall", ind); - println!("{}method name: {}", ind, path.ident.name); - for arg in args { - print_expr(cx, arg, indent + 1); - } - }, - hir::ExprKind::Tup(v) => { - println!("{}Tup", ind); - for e in v { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Binary(op, lhs, rhs) => { - println!("{}Binary", ind); - println!("{}op: {:?}", ind, op.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Unary(op, inner) => { - println!("{}Unary", ind); - println!("{}op: {:?}", ind, op); - print_expr(cx, inner, indent + 1); - }, - hir::ExprKind::Lit(ref lit) => { - println!("{}Lit", ind); - println!("{}{:?}", ind, lit); - }, - hir::ExprKind::Cast(e, target) => { - println!("{}Cast", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Type(e, target) => { - println!("{}Type", ind); - print_expr(cx, e, indent + 1); - println!("{}target type: {:?}", ind, target); - }, - hir::ExprKind::Loop(..) => { - println!("{}Loop", ind); - }, - hir::ExprKind::If(cond, _, ref else_opt) => { - println!("{}If", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - if let Some(els) = *else_opt { - println!("{}else:", ind); - print_expr(cx, els, indent + 1); - } - }, - hir::ExprKind::Match(cond, _, ref source) => { - println!("{}Match", ind); - println!("{}condition:", ind); - print_expr(cx, cond, indent + 1); - println!("{}source: {:?}", ind, source); - }, - hir::ExprKind::Closure(ref clause, _, _, _, _) => { - println!("{}Closure", ind); - println!("{}clause: {:?}", ind, clause); - }, - hir::ExprKind::Yield(sub, _) => { - println!("{}Yield", ind); - print_expr(cx, sub, indent + 1); - }, - hir::ExprKind::Block(_, _) => { - println!("{}Block", ind); - }, - hir::ExprKind::Assign(lhs, rhs, _) => { - println!("{}Assign", ind); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::AssignOp(ref binop, lhs, rhs) => { - println!("{}AssignOp", ind); - println!("{}op: {:?}", ind, binop.node); - println!("{}lhs:", ind); - print_expr(cx, lhs, indent + 1); - println!("{}rhs:", ind); - print_expr(cx, rhs, indent + 1); - }, - hir::ExprKind::Field(e, ident) => { - println!("{}Field", ind); - println!("{}field name: {}", ind, ident.name); - println!("{}struct expr:", ind); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Index(arr, idx) => { - println!("{}Index", ind); - println!("{}array expr:", ind); - print_expr(cx, arr, indent + 1); - println!("{}index expr:", ind); - print_expr(cx, idx, indent + 1); - }, - hir::ExprKind::Path(hir::QPath::Resolved(ref ty, path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::ExprKind::Path(hir::QPath::TypeRelative(ty, seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::ExprKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::ExprKind::AddrOf(kind, ref muta, e) => { - println!("{}AddrOf", ind); - println!("kind: {:?}", kind); - println!("mutability: {:?}", muta); - print_expr(cx, e, indent + 1); - }, - hir::ExprKind::Break(_, ref e) => { - println!("{}Break", ind); - if let Some(e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::Continue(_) => println!("{}Again", ind), - hir::ExprKind::Ret(ref e) => { - println!("{}Ret", ind); - if let Some(e) = *e { - print_expr(cx, e, indent + 1); - } - }, - hir::ExprKind::InlineAsm(asm) => { - println!("{}InlineAsm", ind); - println!("{}template: {}", ind, InlineAsmTemplatePiece::to_string(asm.template)); - println!("{}options: {:?}", ind, asm.options); - println!("{}operands:", ind); - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } - | hir::InlineAsmOperand::InOut { expr, .. } => { - print_expr(cx, expr, indent + 1); - } - hir::InlineAsmOperand::Out { expr, .. } => { - if let Some(expr) = expr { - print_expr(cx, expr, indent + 1); - } - }, - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - print_expr(cx, in_expr, indent + 1); - if let Some(out_expr) = out_expr { - print_expr(cx, out_expr, indent + 1); - } - }, - hir::InlineAsmOperand::Const { anon_const } - | hir::InlineAsmOperand::SymFn { anon_const } => { - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::InlineAsmOperand::SymStatic { path, .. } => { - match path { - hir::QPath::Resolved(ref ty, path) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::QPath::TypeRelative(ty, seg) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::QPath::LangItem(lang_item, ..) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - } - } - } - } - }, - hir::ExprKind::Struct(path, fields, ref base) => { - println!("{}Struct", ind); - println!("{}path: {:?}", ind, path); - for field in fields { - println!("{}field \"{}\":", ind, field.ident.name); - print_expr(cx, field.expr, indent + 1); - } - if let Some(base) = *base { - println!("{}base:", ind); - print_expr(cx, base, indent + 1); - } - }, - hir::ExprKind::ConstBlock(ref anon_const) => { - println!("{}ConstBlock", ind); - println!("{}anon_const:", ind); - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - hir::ExprKind::Repeat(val, length) => { - println!("{}Repeat", ind); - println!("{}value:", ind); - print_expr(cx, val, indent + 1); - println!("{}repeat count:", ind); - match length { - hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind), - hir::ArrayLen::Body(anon_const) => { - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); - }, - } - }, - hir::ExprKind::Err => { - println!("{}Err", ind); - }, - hir::ExprKind::DropTemps(e) => { - println!("{}DropTemps", ind); - print_expr(cx, e, indent + 1); - }, - } -} - -fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { - let did = item.def_id; - println!("item `{}`", item.ident.name); - match cx.tcx.visibility(item.def_id) { - ty::Visibility::Public => println!("public"), - ty::Visibility::Restricted(def_id) => { - if def_id.is_top_level_module() { - println!("visible crate wide") - } else { - println!("visible in module `{}`", cx.tcx.def_path_str(def_id)) - } - }, - ty::Visibility::Invisible => println!("invisible"), - } - match item.kind { - hir::ItemKind::ExternCrate(ref _renamed_from) => { - if let Some(crate_id) = cx.tcx.extern_mod_stmt_cnum(did) { - let source = cx.tcx.used_crate_source(crate_id); - if let Some(ref src) = source.dylib { - println!("extern crate dylib source: {:?}", src.0); - } - if let Some(ref src) = source.rlib { - println!("extern crate rlib source: {:?}", src.0); - } - } else { - println!("weird extern crate without a crate id"); - } - }, - hir::ItemKind::Use(path, ref kind) => println!("{:?}, {:?}", path, kind), - hir::ItemKind::Static(..) => println!("static item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Const(..) => println!("const item of type {:#?}", cx.tcx.type_of(did)), - hir::ItemKind::Fn(..) => { - let item_ty = cx.tcx.type_of(did); - println!("function of type {:#?}", item_ty); - }, - hir::ItemKind::Macro(ref macro_def, _) => { - if macro_def.macro_rules { - println!("macro introduced by `macro_rules!`"); - } else { - println!("macro introduced by `macro`"); - } - }, - hir::ItemKind::Mod(..) => println!("module"), - hir::ItemKind::ForeignMod { abi, .. } => println!("foreign module with abi: {}", abi), - hir::ItemKind::GlobalAsm(asm) => println!("global asm: {:?}", asm), - hir::ItemKind::TyAlias(..) => { - println!("type alias for {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::OpaqueTy(..) => { - println!("existential type with real type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Enum(..) => { - println!("enum definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Struct(..) => { - println!("struct definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Union(..) => { - println!("union definition of type {:?}", cx.tcx.type_of(did)); - }, - hir::ItemKind::Trait(..) => { - println!("trait decl"); - if cx.tcx.trait_is_auto(did.to_def_id()) { - println!("trait is auto"); - } else { - println!("trait is not auto"); - } - }, - hir::ItemKind::TraitAlias(..) => { - println!("trait alias"); - }, - hir::ItemKind::Impl(hir::Impl { - of_trait: Some(ref _trait_ref), - .. - }) => { - println!("trait impl"); - }, - hir::ItemKind::Impl(hir::Impl { of_trait: None, .. }) => { - println!("impl"); - }, - } -} - -#[allow(clippy::similar_names)] -#[allow(clippy::too_many_lines)] -fn print_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match pat.kind { - hir::PatKind::Wild => println!("{}Wild", ind), - hir::PatKind::Binding(ref mode, .., ident, ref inner) => { - println!("{}Binding", ind); - println!("{}mode: {:?}", ind, mode); - println!("{}name: {}", ind, ident.name); - if let Some(inner) = *inner { - println!("{}inner:", ind); - print_pat(cx, inner, indent + 1); - } - }, - hir::PatKind::Or(fields) => { - println!("{}Or", ind); - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Struct(ref path, fields, ignore) => { - println!("{}Struct", ind); - println!( - "{}name: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - println!("{}ignore leftover fields: {}", ind, ignore); - println!("{}fields:", ind); - for field in fields { - println!("{} field name: {}", ind, field.ident.name); - if field.is_shorthand { - println!("{} in shorthand notation", ind); - } - print_pat(cx, field.pat, indent + 1); - } - }, - hir::PatKind::TupleStruct(ref path, fields, opt_dots_position) => { - println!("{}TupleStruct", ind); - println!( - "{}path: {}", - ind, - rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false)) - ); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in fields { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Path(hir::QPath::Resolved(ref ty, path)) => { - println!("{}Resolved Path, {:?}", ind, ty); - println!("{}path: {:?}", ind, path); - }, - hir::PatKind::Path(hir::QPath::TypeRelative(ty, seg)) => { - println!("{}Relative Path, {:?}", ind, ty); - println!("{}seg: {:?}", ind, seg); - }, - hir::PatKind::Path(hir::QPath::LangItem(lang_item, ..)) => { - println!("{}Lang Item Path, {:?}", ind, lang_item.name()); - }, - hir::PatKind::Tuple(pats, opt_dots_position) => { - println!("{}Tuple", ind); - if let Some(dot_position) = opt_dots_position { - println!("{}dot position: {}", ind, dot_position); - } - for field in pats { - print_pat(cx, field, indent + 1); - } - }, - hir::PatKind::Box(inner) => { - println!("{}Box", ind); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Ref(inner, ref muta) => { - println!("{}Ref", ind); - println!("{}mutability: {:?}", ind, muta); - print_pat(cx, inner, indent + 1); - }, - hir::PatKind::Lit(e) => { - println!("{}Lit", ind); - print_expr(cx, e, indent + 1); - }, - hir::PatKind::Range(ref l, ref r, ref range_end) => { - println!("{}Range", ind); - if let Some(expr) = l { - print_expr(cx, expr, indent + 1); - } - if let Some(expr) = r { - print_expr(cx, expr, indent + 1); - } - match *range_end { - hir::RangeEnd::Included => println!("{} end included", ind), - hir::RangeEnd::Excluded => println!("{} end excluded", ind), - } - }, - hir::PatKind::Slice(first_pats, ref range, last_pats) => { - println!("{}Slice [a, b, ..i, y, z]", ind); - println!("[a, b]:"); - for pat in first_pats { - print_pat(cx, pat, indent + 1); - } - println!("i:"); - if let Some(pat) = *range { - print_pat(cx, pat, indent + 1); - } - println!("[y, z]:"); - for pat in last_pats { - print_pat(cx, pat, indent + 1); - } - }, - } -} - -fn print_guard(cx: &LateContext<'_>, guard: &hir::Guard<'_>, indent: usize) { - let ind = " ".repeat(indent); - println!("{}+", ind); - match guard { - hir::Guard::If(expr) => { - println!("{}If", ind); - print_expr(cx, expr, indent + 1); - }, - hir::Guard::IfLet(pat, expr) => { - println!("{}IfLet", ind); - print_pat(cx, pat, indent + 1); - print_expr(cx, expr, indent + 1); - }, - } -} diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 25d74b8c4993..0e8f40e92101 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -292,7 +292,7 @@ declare_clippy_lint! { /// Checks for unnecessary conversion from Symbol to a string. /// /// ### Why is this bad? - /// It's faster use symbols directly intead of strings. + /// It's faster use symbols directly instead of strings. /// /// ### Example /// Bad: @@ -823,7 +823,7 @@ fn suggest_note( cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.span, - "this call is collspible", + "this call is collapsible", "collapse into", format!( "span_lint_and_note({}, {}, {}, {}, {}, {})", diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index ca03b8010dd8..8c1910b3b2af 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -33,7 +33,7 @@ use std::path::Path; /// This is the output file of the lint collector. const OUTPUT_FILE: &str = "../util/gh-pages/lints.json"; /// These lints are excluded from the export. -const BLACK_LISTED_LINTS: [&str; 3] = ["lint_author", "deep_code_inspection", "internal_metadata_collector"]; +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"]; /// These groups will be ignored by the lint group matcher. This is useful for collections like /// `clippy::all` const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"]; @@ -117,7 +117,7 @@ const APPLICABILITY_NAME_INDEX: usize = 2; /// This applicability will be set for unresolved applicability values. const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved"; /// The version that will be displayed if none has been defined -const VERION_DEFAULT_STR: &str = "Unknown"; +const VERSION_DEFAULT_STR: &str = "Unknown"; declare_clippy_lint! { /// ### What it does @@ -571,7 +571,7 @@ fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option { fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { extract_clippy_version_value(cx, item).map_or_else( - || VERION_DEFAULT_STR.to_string(), + || VERSION_DEFAULT_STR.to_string(), |version| version.as_str().to_string(), ) } @@ -872,7 +872,7 @@ impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> { self.suggestion_count += 2; } - /// Checks if the suggestions include multiple spanns + /// Checks if the suggestions include multiple spans fn is_multi_part(&self) -> bool { self.suggestion_count > 1 } diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index dc385ebacba6..787e9fd982c8 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; -pub mod inspector; +pub mod dump_hir; #[cfg(feature = "internal")] pub mod internal_lints; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index be46b791aa4b..fdb822c3e5b6 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -5,7 +5,7 @@ use if_chain::if_chain; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, QPath, UnOp}; +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::subst::{Subst, SubstsRef}; @@ -400,6 +400,22 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let res = self.typeck_results.qpath_res(qpath, id); match res { Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => { + // Check if this constant is based on `cfg!(..)`, + // which is NOT constant for our purposes. + if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) && + let Node::Item(&Item { + kind: ItemKind::Const(_, body_id), + .. + }) = node && + let Node::Expr(&Expr { + kind: ExprKind::Lit(_), + span, + .. + }) = self.lcx.tcx.hir().get(body_id.hir_id) && + is_direct_expn_of(span, "cfg").is_some() { + return None; + } + let substs = self.typeck_results.node_substs(id); let substs = if self.substs.is_empty() { substs diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index c05317f59b71..f4da625f1e30 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -1,4 +1,4 @@ -use crate::consts::{constant_context, constant_simple}; +use crate::consts::constant_simple; use crate::source::snippet_opt; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; @@ -16,15 +16,14 @@ use rustc_span::Symbol; use std::hash::{Hash, Hasher}; /// Type used to check whether two ast are the same. This is different from the -/// operator -/// `==` on ast types as this operator would compare true equality with ID and -/// span. +/// operator `==` on ast types as this operator would compare true equality with +/// ID and span. /// /// Note that some expressions kinds are not considered but could be added. pub struct SpanlessEq<'a, 'tcx> { /// Context used to evaluate constant expressions. cx: &'a LateContext<'tcx>, - maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>, + maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>, allow_side_effects: bool, expr_fallback: Option, &Expr<'_>) -> bool + 'a>>, } @@ -33,7 +32,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { pub fn new(cx: &'a LateContext<'tcx>) -> Self { Self { cx, - maybe_typeck_results: cx.maybe_typeck_results(), + maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)), allow_side_effects: true, expr_fallback: None, } @@ -102,9 +101,9 @@ impl HirEqInterExpr<'_, '_, '_> { (&StmtKind::Local(l), &StmtKind::Local(r)) => { // This additional check ensures that the type of the locals are equivalent even if the init // expression or type have some inferred parts. - if let Some(typeck) = self.inner.maybe_typeck_results { - let l_ty = typeck.pat_ty(l.pat); - let r_ty = typeck.pat_ty(r.pat); + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { + let l_ty = typeck_lhs.pat_ty(l.pat); + let r_ty = typeck_rhs.pat_ty(r.pat); if l_ty != r_ty { return false; } @@ -182,9 +181,17 @@ impl HirEqInterExpr<'_, '_, '_> { } pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool { - let cx = self.inner.cx; - let eval_const = |body| constant_context(cx, cx.tcx.typeck_body(body)).expr(&cx.tcx.hir().body(body).value); - eval_const(left) == eval_const(right) + // swap out TypeckResults when hashing a body + let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace(( + self.inner.cx.tcx.typeck_body(left), + self.inner.cx.tcx.typeck_body(right), + )); + let res = self.eq_expr( + &self.inner.cx.tcx.hir().body(left).value, + &self.inner.cx.tcx.hir().body(right).value, + ); + self.inner.maybe_typeck_results = old_maybe_typeck_results; + res } #[allow(clippy::similar_names)] @@ -193,10 +200,10 @@ impl HirEqInterExpr<'_, '_, '_> { return false; } - if let Some(typeck_results) = self.inner.maybe_typeck_results { + if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results { if let (Some(l), Some(r)) = ( - constant_simple(self.inner.cx, typeck_results, left), - constant_simple(self.inner.cx, typeck_results, right), + constant_simple(self.inner.cx, typeck_lhs, left), + constant_simple(self.inner.cx, typeck_rhs, right), ) { if l == r { return true; @@ -674,8 +681,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(out_expr); } }, - InlineAsmOperand::Const { anon_const } => self.hash_body(anon_const.body), - InlineAsmOperand::SymFn { anon_const } => self.hash_body(anon_const.body), + InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => { + self.hash_body(anon_const.body); + }, InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path), } } diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs index e7d4c5a49521..a268e339bb13 100644 --- a/clippy_utils/src/macros.rs +++ b/clippy_utils/src/macros.rs @@ -367,7 +367,7 @@ impl<'tcx> FormatArgsExpn<'tcx> { expr_visitor_no_bodies(|e| { // if we're still inside of the macro definition... if e.span.ctxt() == expr.span.ctxt() { - // ArgumnetV1::new_() + // ArgumentV1::new_() if_chain! { if let ExprKind::Call(callee, [val]) = e.kind; if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind; diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs index 0424e0672026..134fd1ce505a 100644 --- a/clippy_utils/src/msrvs.rs +++ b/clippy_utils/src/msrvs.rs @@ -32,4 +32,5 @@ msrv_aliases! { 1,28,0 { FROM_BOOL } 1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR } 1,16,0 { STR_REPEAT } + 1,24,0 { IS_ASCII_DIGIT } } diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index 908ff822712f..b92d42e83232 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -57,7 +57,7 @@ impl<'a> NumericLiteral<'a> { .trim_start() .chars() .next() - .map_or(false, |c| c.is_digit(10)) + .map_or(false, |c| c.is_ascii_digit()) { let (unsuffixed, suffix) = split_suffix(src, lit_kind); let float = matches!(lit_kind, LitKind::Float(..)); diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index e5fa6deefc5d..60971fb716db 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -61,6 +61,7 @@ pub const IO_READ: [&str; 3] = ["std", "io", "Read"]; pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"]; pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; #[allow(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; @@ -148,6 +149,8 @@ pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"]; pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"]; +pub const STR_BYTES: [&str; 4] = ["core", "str", "", "bytes"]; pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "", "ends_with"]; pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"]; pub const STR_LEN: [&str; 4] = ["core", "str", "", "len"]; diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index fe4112204848..75808b1b1746 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -211,8 +211,9 @@ fn check_statement<'tcx>( StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body), // just an assignment - StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => - check_place(tcx, **place, span, body), + StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => { + check_place(tcx, **place, span, body) + }, StatementKind::CopyNonOverlapping(box rustc_middle::mir::CopyNonOverlapping { dst, src, count }) => { check_operand(tcx, dst, span, body)?; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index dbad607c58ea..c69a3d8d2a15 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -7,9 +7,28 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_span::hygiene; +use rustc_span::source_map::SourceMap; use rustc_span::{BytePos, Pos, Span, SyntaxContext}; use std::borrow::Cow; +/// Checks if the span starts with the given text. This will return false if the span crosses +/// multiple files or if source is not available. +/// +/// This is used to check for proc macros giving unhelpful spans to things. +pub fn span_starts_with(cx: &T, span: Span, text: &str) -> bool { + fn helper(sm: &SourceMap, span: Span, text: &str) -> bool { + let pos = sm.lookup_byte_offset(span.lo()); + let Some(ref src) = pos.sf.src else { + return false; + }; + let end = span.hi() - pos.sf.start_pos; + src.get(pos.pos.0 as usize..end.0 as usize) + // Expression spans can include wrapping parenthesis. Remove them first. + .map_or(false, |s| s.trim_start_matches('(').starts_with(text)) + } + helper(cx.sess().source_map(), span, text) +} + /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`. /// Also takes an `Option` which can be put inside the braces. pub fn expr_block<'a, T: LintContext>( @@ -89,7 +108,7 @@ pub fn is_present_in_source(cx: &T, span: Span) -> bool { true } -/// Returns the positon just before rarrow +/// Returns the position just before rarrow /// /// ```rust,ignore /// fn into(self) -> () {} diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 794d2e1026f8..18915553e61c 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -18,7 +18,7 @@ use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; use std::convert::TryInto; -use std::fmt::Display; +use std::fmt::{Display, Write as _}; use std::iter; use std::ops::{Add, Neg, Not, Sub}; @@ -902,7 +902,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { if cmt.place.projections.is_empty() { // handle item without any projection, that needs an explicit borrowing // i.e.: suggest `&x` instead of `x` - self.suggestion_start.push_str(&format!("{}&{}", start_snip, ident_str)); + let _ = write!(self.suggestion_start, "{}&{}", start_snip, ident_str); } else { // cases where a parent `Call` or `MethodCall` is using the item // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()` @@ -917,8 +917,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { // given expression is the self argument and will be handled completely by the compiler // i.e.: `|x| x.is_something()` ExprKind::MethodCall(_, [self_expr, ..], _) if self_expr.hir_id == cmt.hir_id => { - self.suggestion_start - .push_str(&format!("{}{}", start_snip, ident_str_with_proj)); + let _ = write!(self.suggestion_start, "{}{}", start_snip, ident_str_with_proj); self.next_pos = span.hi(); return; }, @@ -1026,8 +1025,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { } } - self.suggestion_start - .push_str(&format!("{}{}", start_snip, replacement_str)); + let _ = write!(self.suggestion_start, "{}{}", start_snip, replacement_str); } self.next_pos = span.hi(); } diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index e3fc76f4e1a2..901e3e5390c5 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -3,11 +3,11 @@ #![allow(clippy::module_name_repetitions)] use rustc_ast::ast::Mutability; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::{Expr, TyKind, Unsafety}; +use rustc_hir::{Expr, LangItem, TyKind, Unsafety}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; @@ -22,7 +22,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; use std::iter; -use crate::{match_def_path, must_use_attr, path_res}; +use crate::{match_def_path, must_use_attr, path_res, paths}; // Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -83,6 +83,20 @@ pub fn get_associated_type<'tcx>( }) } +/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type +/// implements a trait marked with a diagnostic item use [`implements_trait`]. +/// +/// For a further exploitation what diagnostic items are see [diagnostic items] in +/// rustc-dev-guide. +/// +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html +pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option { + match ty.kind() { + ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()), + _ => None, + } +} + /// Returns true if ty has `iter` or `iter_mut` methods pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option { // FIXME: instead of this hard-coded list, we should check if `::iter` @@ -319,6 +333,57 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool { } } +/// Checks if the drop order for a type matters. Some std types implement drop solely to +/// deallocate memory. For these types, and composites containing them, changing the drop order +/// won't result in any observable side effects. +pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet>) -> bool { + if !seen.insert(ty) { + return false; + } + if !ty.has_significant_drop(cx.tcx, cx.param_env) { + false + } + // Check for std types which implement drop, but only for memory allocation. + else if is_type_lang_item(cx, ty, LangItem::OwnedBox) + || matches!( + get_type_diagnostic_name(cx, ty), + Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type) + ) + || match_type(cx, ty, &paths::WEAK_RC) + || match_type(cx, ty, &paths::WEAK_ARC) + { + // Check all of the generic arguments. + if let ty::Adt(_, subs) = ty.kind() { + subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen)) + } else { + true + } + } else if !cx + .tcx + .lang_items() + .drop_trait() + .map_or(false, |id| implements_trait(cx, ty, id, &[])) + { + // This type doesn't implement drop, so no side effects here. + // Check if any component type has any. + match ty.kind() { + ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)), + ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen), + ty::Adt(adt, subs) => adt + .all_fields() + .map(|f| f.ty(cx.tcx, subs)) + .any(|ty| needs_ordered_drop_inner(cx, ty, seen)), + _ => true, + } + } else { + true + } + } + + needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) +} + /// Peels off all references on the type. Returns the underlying type and the number of references /// removed. pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 405e306359bc..4236e3aae2fb 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -3,7 +3,7 @@ use crate::visitors::{expr_visitor, expr_visitor_no_bodies}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirIdSet; -use rustc_hir::{Expr, ExprKind, HirId}; +use rustc_hir::{Expr, ExprKind, HirId, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -169,6 +169,32 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool { let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false }; + + // for _ in 1..3 { + // local + // } + // + // let closure = || local; + // closure(); + // closure(); + let in_loop_or_closure = cx + .tcx + .hir() + .parent_iter(after.hir_id) + .take_while(|&(id, _)| id != block.hir_id) + .any(|(_, node)| { + matches!( + node, + Node::Expr(Expr { + kind: ExprKind::Loop(..) | ExprKind::Closure(..), + .. + }) + ) + }); + if in_loop_or_closure { + return true; + } + let mut used_after_expr = false; let mut past_expr = false; expr_visitor(cx, |expr| { @@ -178,7 +204,10 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr if expr.hir_id == after.hir_id { past_expr = true; - } else if past_expr && utils::path_to_local_id(expr, local_id) { + return false; + } + + if past_expr && utils::path_to_local_id(expr, local_id) { used_after_expr = true; } !used_after_expr diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs index 40451b17a9c6..c00bc2bd213f 100644 --- a/clippy_utils/src/visitors.rs +++ b/clippy_utils/src/visitors.rs @@ -1,9 +1,11 @@ use crate::path_to_local_id; +use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; use rustc_hir::{ - Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, Unsafety, + Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource, + Unsafety, }; use rustc_lint::LateContext; use rustc_middle::hir::map::Map; @@ -370,3 +372,67 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { v.visit_expr(e); v.is_unsafe } + +/// Checks if the given expression contains an unsafe block +pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { + struct V<'cx, 'tcx> { + cx: &'cx LateContext<'tcx>, + found_unsafe: bool, + } + impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + fn nested_visit_map(&mut self) -> Self::Map { + self.cx.tcx.hir() + } + + fn visit_block(&mut self, b: &'tcx Block<'_>) { + if self.found_unsafe { + return; + } + if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) { + self.found_unsafe = true; + return; + } + walk_block(self, b); + } + } + let mut v = V { + cx, + found_unsafe: false, + }; + v.visit_expr(e); + v.found_unsafe +} + +/// Runs the given function for each sub-expression producing the final value consumed by the parent +/// of the give expression. +/// +/// e.g. for the following expression +/// ```rust,ignore +/// if foo { +/// f(0) +/// } else { +/// 1 + 1 +/// } +/// ``` +/// this will pass both `f(0)` and `1+1` to the given function. +pub fn for_each_value_source<'tcx, B>( + e: &'tcx Expr<'tcx>, + f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, +) -> ControlFlow { + match e.kind { + ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f), + ExprKind::Match(_, arms, _) => { + for arm in arms { + for_each_value_source(arm.body, f)?; + } + ControlFlow::Continue(()) + }, + ExprKind::If(_, if_expr, Some(else_expr)) => { + for_each_value_source(if_expr, f)?; + for_each_value_source(else_expr, f) + }, + ExprKind::DropTemps(e) => for_each_value_source(e, f), + _ => f(e), + } +} diff --git a/doc/adding_lints.md b/doc/adding_lints.md index cf16a1d5d3dc..307cf2f3a904 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -22,6 +22,7 @@ because that's clearly a non-descriptive name. - [Adding the lint logic](#adding-the-lint-logic) - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv) - [Author lint](#author-lint) + - [Print HIR lint](#print-hir-lint) - [Documentation](#documentation) - [Running rustfmt](#running-rustfmt) - [Debugging](#debugging) @@ -484,6 +485,19 @@ you are implementing your lint. [author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 +## Print HIR lint + +To implement a lint, it's helpful to first understand the internal representation +that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the +[_High-Level Intermediate Representation (HIR)_] of the item, statement, or +expression that the attribute is attached to. To attach the attribute to expressions +you often need to enable `#![feature(stmt_expr_attributes)]`. + +[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_. + +[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb + ## Documentation The final thing before submitting our PR is to add some documentation to our diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 9af8dcc7726f..816efbdaedf3 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -8,6 +8,7 @@ #![allow(clippy::collapsible_else_if)] use std::ffi::OsStr; +use std::fmt::Write as _; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{collections::HashMap, io::ErrorKind}; @@ -110,11 +111,12 @@ impl ClippyWarning { let lint = format!("`{}`", self.linttype); let mut output = String::from("| "); - output.push_str(&format!( + let _ = write!( + output, "[`{}`](../target/lintcheck/sources/{}#L{})", file_with_pos, file, self.line - )); - output.push_str(&format!(r#" | {:<50} | "{}" |"#, lint, self.message)); + ); + let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); output.push('\n'); output } else { @@ -304,7 +306,7 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); let mut args = if fix { - vec!["--fix", "--allow-no-vcs", "--"] + vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] }; @@ -391,7 +393,7 @@ struct LintcheckConfig { lintcheck_results_path: PathBuf, /// whether to just run --fix and not collect all the warnings fix: bool, - /// A list of lint that this lintcheck run shound focus on + /// A list of lints that this lintcheck run should focus on lint_filter: Vec, /// Indicate if the output should support markdown syntax markdown: bool, @@ -835,10 +837,11 @@ pub fn main() { text.push_str("| file | lint | message |\n"); text.push_str("| --- | --- | --- |\n"); } - text.push_str(&format!("{}", all_msgs.join(""))); + write!(text, "{}", all_msgs.join("")); text.push_str("\n\n### ICEs:\n"); - ices.iter() - .for_each(|(cratename, msg)| text.push_str(&format!("{}: '{}'", cratename, msg))); + for (cratename, msg) in ices.iter() { + let _ = write!(text, "{}: '{}'", cratename, msg); + } println!("Writing logs to {}", config.lintcheck_results_path.display()); std::fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap(); diff --git a/rust-toolchain b/rust-toolchain index bb29c71e9f45..03acb51306d7 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2022-04-07" +channel = "nightly-2022-05-05" components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/driver.rs b/src/driver.rs index 32a09fdb9d9f..7de40fe63ac2 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -165,8 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) { // Separate the output with an empty line eprintln!(); - let fallback_bundle = - rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); + let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, None, diff --git a/tests/dogfood.rs b/tests/dogfood.rs index 67af9d05bf40..eb97d1933d5d 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -80,9 +80,13 @@ fn run_clippy_for_package(project: &str) { .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir - // internal lints only exist if we build with the internal feature if cfg!(feature = "internal") { + // internal lints only exist if we build with the internal feature command.args(&["-D", "clippy::internal"]); + } else { + // running a clippy built without internal lints on the clippy source + // that contains e.g. `allow(clippy::invalid_paths)` + command.args(&["-A", "unknown_lints"]); } let output = command.output().unwrap(); diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs index dc82ba891fb1..dd1d44120360 100644 --- a/tests/lint_message_convention.rs +++ b/tests/lint_message_convention.rs @@ -66,7 +66,7 @@ fn lint_message_convention() { // make sure that lint messages: // * are not capitalized - // * don't have puncuation at the end of the last sentence + // * don't have punctuation at the end of the last sentence // these directories have interesting tests let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"] diff --git a/tests/ui-internal/collapsible_span_lint_calls.stderr b/tests/ui-internal/collapsible_span_lint_calls.stderr index 558d12991607..0852fe65aafd 100644 --- a/tests/ui-internal/collapsible_span_lint_calls.stderr +++ b/tests/ui-internal/collapsible_span_lint_calls.stderr @@ -29,7 +29,7 @@ LL | | db.help(help_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg)` -error: this call is collspible +error: this call is collapsible --> $DIR/collapsible_span_lint_calls.rs:45:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { @@ -37,7 +37,7 @@ LL | | db.span_note(expr.span, note_msg); LL | | }); | |__________^ help: collapse into: `span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg)` -error: this call is collspible +error: this call is collapsible --> $DIR/collapsible_span_lint_calls.rs:48:9 | LL | / span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| { diff --git a/tests/ui-internal/interning_defined_symbol.fixed b/tests/ui-internal/interning_defined_symbol.fixed index 6b7fd6efe394..eaea218e1288 100644 --- a/tests/ui-internal/interning_defined_symbol.fixed +++ b/tests/ui-internal/interning_defined_symbol.fixed @@ -1,6 +1,6 @@ // run-rustfix #![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] #![feature(rustc_private)] extern crate rustc_span; diff --git a/tests/ui-internal/interning_defined_symbol.rs b/tests/ui-internal/interning_defined_symbol.rs index 98d7d7adad17..7efebb8fae48 100644 --- a/tests/ui-internal/interning_defined_symbol.rs +++ b/tests/ui-internal/interning_defined_symbol.rs @@ -1,6 +1,6 @@ // run-rustfix #![deny(clippy::internal)] -#![allow(clippy::missing_clippy_version_attribute)] +#![allow(clippy::missing_clippy_version_attribute, clippy::let_unit_value)] #![feature(rustc_private)] extern crate rustc_span; diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs new file mode 100644 index 000000000000..fbef5c4564b1 --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs @@ -0,0 +1,41 @@ +#![warn(clippy::await_holding_invalid_type)] +use std::net::Ipv4Addr; + +async fn bad() -> u32 { + let _x = String::from("hello"); + baz().await +} + +async fn bad_reason() -> u32 { + let _x = Ipv4Addr::new(127, 0, 0, 1); + baz().await +} + +async fn good() -> u32 { + { + let _x = String::from("hi!"); + let _y = Ipv4Addr::new(127, 0, 0, 1); + } + baz().await; + let _x = String::from("hi!"); + 47 +} + +async fn baz() -> u32 { + 42 +} + +#[allow(clippy::manual_async_fn)] +fn block_bad() -> impl std::future::Future { + async move { + let _x = String::from("hi!"); + baz().await + } +} + +fn main() { + good(); + bad(); + bad_reason(); + block_bad(); +} diff --git a/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr new file mode 100644 index 000000000000..62c45b54634f --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr @@ -0,0 +1,25 @@ +error: `std::string::String` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:5:9 + | +LL | let _x = String::from("hello"); + | ^^ + | + = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings` + = note: strings are bad + +error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:10:9 + | +LL | let _x = Ipv4Addr::new(127, 0, 0, 1); + | ^^ + +error: `std::string::String` may not be held across an `await` point per `clippy.toml` + --> $DIR/await_holding_invalid_type.rs:31:13 + | +LL | let _x = String::from("hi!"); + | ^^ + | + = note: strings are bad + +error: aborting due to 3 previous errors + diff --git a/tests/ui-toml/await_holding_invalid_type/clippy.toml b/tests/ui-toml/await_holding_invalid_type/clippy.toml new file mode 100644 index 000000000000..79990096b84e --- /dev/null +++ b/tests/ui-toml/await_holding_invalid_type/clippy.toml @@ -0,0 +1,4 @@ +await-holding-invalid-types = [ + { path = "std::string::String", reason = "strings are bad" }, + "std::net::Ipv4Addr", +] diff --git a/tests/ui-toml/functions_maxlines/test.rs b/tests/ui-toml/functions_maxlines/test.rs index e678c896fd3e..4ac0378544c7 100644 --- a/tests/ui-toml/functions_maxlines/test.rs +++ b/tests/ui-toml/functions_maxlines/test.rs @@ -1,4 +1,5 @@ #![warn(clippy::too_many_lines)] +#![allow(clippy::let_unit_value)] // This function should be considered one line. fn many_comments_but_one_line_of_code() { diff --git a/tests/ui-toml/functions_maxlines/test.stderr b/tests/ui-toml/functions_maxlines/test.stderr index d736bf899735..dc255bdcabaf 100644 --- a/tests/ui-toml/functions_maxlines/test.stderr +++ b/tests/ui-toml/functions_maxlines/test.stderr @@ -1,5 +1,5 @@ error: this function has too many lines (2/1) - --> $DIR/test.rs:18:1 + --> $DIR/test.rs:19:1 | LL | / fn too_many_lines() { LL | | println!("This is bad."); @@ -10,7 +10,7 @@ LL | | } = note: `-D clippy::too-many-lines` implied by `-D warnings` error: this function has too many lines (4/1) - --> $DIR/test.rs:24:1 + --> $DIR/test.rs:25:1 | LL | / async fn async_too_many_lines() { LL | | println!("This is bad."); @@ -19,7 +19,7 @@ LL | | } | |_^ error: this function has too many lines (4/1) - --> $DIR/test.rs:30:1 + --> $DIR/test.rs:31:1 | LL | / fn closure_too_many_lines() { LL | | let _ = { @@ -30,7 +30,7 @@ LL | | } | |_^ error: this function has too many lines (2/1) - --> $DIR/test.rs:52:1 + --> $DIR/test.rs:53:1 | LL | / fn comment_before_code() { LL | | let _ = "test"; diff --git a/tests/ui-toml/large_include_file/clippy.toml b/tests/ui-toml/large_include_file/clippy.toml new file mode 100644 index 000000000000..ea34bf9fbe01 --- /dev/null +++ b/tests/ui-toml/large_include_file/clippy.toml @@ -0,0 +1 @@ +max-include-file-size = 600 diff --git a/tests/ui-toml/large_include_file/large_include_file.rs b/tests/ui-toml/large_include_file/large_include_file.rs new file mode 100644 index 000000000000..f3dbb6ad1cf5 --- /dev/null +++ b/tests/ui-toml/large_include_file/large_include_file.rs @@ -0,0 +1,16 @@ +#![warn(clippy::large_include_file)] + +// Good +const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs"); +const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs"); + +#[allow(clippy::large_include_file)] +const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +#[allow(clippy::large_include_file)] +const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + +// Bad +const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); +const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + +fn main() {} diff --git a/tests/ui-toml/large_include_file/large_include_file.stderr b/tests/ui-toml/large_include_file/large_include_file.stderr new file mode 100644 index 000000000000..6a685a58318b --- /dev/null +++ b/tests/ui-toml/large_include_file/large_include_file.stderr @@ -0,0 +1,21 @@ +error: attempted to include a large file + --> $DIR/large_include_file.rs:13:43 + | +LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::large-include-file` implied by `-D warnings` + = note: the configuration allows a maximum size of 600 bytes + = note: this error originates in the macro `include_bytes` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: attempted to include a large file + --> $DIR/large_include_file.rs:14:35 + | +LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the configuration allows a maximum size of 600 bytes + = note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/large_include_file/too_big.txt b/tests/ui-toml/large_include_file/too_big.txt new file mode 100644 index 000000000000..9829c46bc00f --- /dev/null +++ b/tests/ui-toml/large_include_file/too_big.txt @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Maecenas accumsan lacus vel facilisis volutpat. Etiam dignissim diam quis enim lobortis scelerisque fermentum dui faucibus. Tellus id interdum velit laoreet id donec ultrices. Est ultricies integer quis auctor elit sed vulputate. Erat velit scelerisque in dictum non consectetur a erat nam. Sed blandit libero volutpat sed. Tortor condimentum lacinia quis vel eros. Enim ut tellus elementum sagittis vitae et leo duis. Congue mauris rhoncus aenean vel elit scelerisque. Id consectetur purus ut faucibus pulvinar elementum integer. \ No newline at end of file diff --git a/tests/ui-toml/min_rust_version/min_rust_version.stderr b/tests/ui-toml/min_rust_version/min_rust_version.stderr index a1e7361c0cb3..5dae5af7eb5a 100644 --- a/tests/ui-toml/min_rust_version/min_rust_version.stderr +++ b/tests/ui-toml/min_rust_version/min_rust_version.stderr @@ -1,4 +1,4 @@ -error: you are using an explicit closure for copying elements +error: you are using an explicit closure for cloning elements --> $DIR/min_rust_version.rs:74:26 | LL | let _: Option = Some(&16).map(|b| *b); diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 00ddbd608a7c..8701809b4daa 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui/assertions_on_constants.rs b/tests/ui/assertions_on_constants.rs index 7477c01ca782..7bea9563d47d 100644 --- a/tests/ui/assertions_on_constants.rs +++ b/tests/ui/assertions_on_constants.rs @@ -1,4 +1,4 @@ -#![allow(non_fmt_panics)] +#![allow(non_fmt_panics, clippy::needless_bool)] macro_rules! assert_const { ($len:expr) => { @@ -28,6 +28,12 @@ fn main() { assert_const!(3); assert_const!(-1); - // Don't lint on this: + // Don't lint if based on `cfg!(..)`: assert!(cfg!(feature = "hey") || cfg!(not(feature = "asdf"))); + + let flag: bool = cfg!(not(feature = "asdf")); + assert!(flag); + + const CFG_FLAG: &bool = &cfg!(feature = "hey"); + assert!(!CFG_FLAG); } diff --git a/tests/ui/auxiliary/proc_macro_derive.rs b/tests/ui/auxiliary/proc_macro_derive.rs index 4b7b7fec78fe..ed7b17651e67 100644 --- a/tests/ui/auxiliary/proc_macro_derive.rs +++ b/tests/ui/auxiliary/proc_macro_derive.rs @@ -13,7 +13,7 @@ use proc_macro::{quote, TokenStream}; #[proc_macro_derive(DeriveSomething)] pub fn derive(_: TokenStream) -> TokenStream { - // Shound not trigger `used_underscore_binding` + // Should not trigger `used_underscore_binding` let _inside_derive = 1; assert_eq!(_inside_derive, _inside_derive); diff --git a/tests/ui/auxiliary/proc_macro_with_span.rs b/tests/ui/auxiliary/proc_macro_with_span.rs new file mode 100644 index 000000000000..8ea631f2bbd4 --- /dev/null +++ b/tests/ui/auxiliary/proc_macro_with_span.rs @@ -0,0 +1,32 @@ +// compile-flags: --emit=link +// no-prefer-dynamic + +#![crate_type = "proc-macro"] + +extern crate proc_macro; + +use proc_macro::{token_stream::IntoIter, Group, Span, TokenStream, TokenTree}; + +#[proc_macro] +pub fn with_span(input: TokenStream) -> TokenStream { + let mut iter = input.into_iter(); + let span = iter.next().unwrap().span(); + let mut res = TokenStream::new(); + write_with_span(span, iter, &mut res); + res +} + +fn write_with_span(s: Span, input: IntoIter, out: &mut TokenStream) { + for mut tt in input { + if let TokenTree::Group(g) = tt { + let mut stream = TokenStream::new(); + write_with_span(s, g.stream().into_iter(), &mut stream); + let mut group = Group::new(g.delimiter(), stream); + group.set_span(s); + out.extend([TokenTree::Group(group)]); + } else { + tt.set_span(s); + out.extend([tt]); + } + } +} diff --git a/tests/ui/bytes_count_to_len.fixed b/tests/ui/bytes_count_to_len.fixed new file mode 100644 index 000000000000..860642363b5f --- /dev/null +++ b/tests/ui/bytes_count_to_len.fixed @@ -0,0 +1,34 @@ +// run-rustfix +#![warn(clippy::bytes_count_to_len)] +use std::fs::File; +use std::io::Read; + +fn main() { + // should fix, because type is String + let _ = String::from("foo").len(); + + let s1 = String::from("foo"); + let _ = s1.len(); + + // should fix, because type is &str + let _ = "foo".len(); + + let s2 = "foo"; + let _ = s2.len(); + + // make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let _ = vector.iter().count(); + + // The type is slice, so should not fix + let _ = &[1, 2, 3].bytes().count(); + + let bytes: &[u8] = &[1, 2, 3]; + bytes.bytes().count(); + + // The type is File, so should not fix + let _ = File::open("foobar").unwrap().bytes().count(); + + let f = File::open("foobar").unwrap(); + let _ = f.bytes().count(); +} diff --git a/tests/ui/bytes_count_to_len.rs b/tests/ui/bytes_count_to_len.rs new file mode 100644 index 000000000000..162730c2842a --- /dev/null +++ b/tests/ui/bytes_count_to_len.rs @@ -0,0 +1,34 @@ +// run-rustfix +#![warn(clippy::bytes_count_to_len)] +use std::fs::File; +use std::io::Read; + +fn main() { + // should fix, because type is String + let _ = String::from("foo").bytes().count(); + + let s1 = String::from("foo"); + let _ = s1.bytes().count(); + + // should fix, because type is &str + let _ = "foo".bytes().count(); + + let s2 = "foo"; + let _ = s2.bytes().count(); + + // make sure using count() normally doesn't trigger warning + let vector = [0, 1, 2]; + let _ = vector.iter().count(); + + // The type is slice, so should not fix + let _ = &[1, 2, 3].bytes().count(); + + let bytes: &[u8] = &[1, 2, 3]; + bytes.bytes().count(); + + // The type is File, so should not fix + let _ = File::open("foobar").unwrap().bytes().count(); + + let f = File::open("foobar").unwrap(); + let _ = f.bytes().count(); +} diff --git a/tests/ui/bytes_count_to_len.stderr b/tests/ui/bytes_count_to_len.stderr new file mode 100644 index 000000000000..224deb779871 --- /dev/null +++ b/tests/ui/bytes_count_to_len.stderr @@ -0,0 +1,28 @@ +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:8:13 + | +LL | let _ = String::from("foo").bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `String::from("foo").len()` + | + = note: `-D clippy::bytes-count-to-len` implied by `-D warnings` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:11:13 + | +LL | let _ = s1.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s1.len()` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:14:13 + | +LL | let _ = "foo".bytes().count(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `"foo".len()` + +error: using long and hard to read `.bytes().count()` + --> $DIR/bytes_count_to_len.rs:17:13 + | +LL | let _ = s2.bytes().count(); + | ^^^^^^^^^^^^^^^^^^ help: consider calling `.len()` instead: `s2.len()` + +error: aborting due to 4 previous errors + diff --git a/tests/ui/cast.rs b/tests/ui/cast.rs index cf85a5ca931d..e6031e9adaeb 100644 --- a/tests/ui/cast.rs +++ b/tests/ui/cast.rs @@ -1,13 +1,13 @@ #![feature(repr128)] #![allow(incomplete_features)] - -#[warn( +#![warn( clippy::cast_precision_loss, clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_possible_wrap )] -#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] +#![allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)] + fn main() { // Test clippy::cast_precision_loss let x0 = 1i32; @@ -252,3 +252,11 @@ fn main() { } } } + +fn avoid_subtract_overflow(q: u32) { + let c = (q >> 16) as u8; + c as usize; + + let c = (q / 1000) as u8; + c as usize; +} diff --git a/tests/ui/cast.stderr b/tests/ui/cast.stderr index 7a68c0984f14..0c63b4af3086 100644 --- a/tests/ui/cast.stderr +++ b/tests/ui/cast.stderr @@ -194,5 +194,17 @@ error: casting `main::E10` to `u16` may truncate the value LL | let _ = self as u16; | ^^^^^^^^^^^ -error: aborting due to 31 previous errors +error: casting `u32` to `u8` may truncate the value + --> $DIR/cast.rs:257:13 + | +LL | let c = (q >> 16) as u8; + | ^^^^^^^^^^^^^^^ + +error: casting `u32` to `u8` may truncate the value + --> $DIR/cast.rs:260:13 + | +LL | let c = (q / 1000) as u8; + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors diff --git a/tests/ui/cast_alignment.rs b/tests/ui/cast_alignment.rs index e4e7290a30e9..95bb883df1bf 100644 --- a/tests/ui/cast_alignment.rs +++ b/tests/ui/cast_alignment.rs @@ -44,8 +44,8 @@ fn main() { let _ = core::ptr::read_unaligned(ptr as *const u16); let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16); let ptr = &mut data as *mut [u8; 2] as *mut u8; - let _ = (ptr as *mut u16).write_unaligned(0); - let _ = core::ptr::write_unaligned(ptr as *mut u16, 0); - let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); + (ptr as *mut u16).write_unaligned(0); + core::ptr::write_unaligned(ptr as *mut u16, 0); + core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0); } } diff --git a/tests/ui/cast_slice_different_sizes.rs b/tests/ui/cast_slice_different_sizes.rs index cfe1cca2eba9..24d7eb28a197 100644 --- a/tests/ui/cast_slice_different_sizes.rs +++ b/tests/ui/cast_slice_different_sizes.rs @@ -1,3 +1,5 @@ +#![allow(clippy::let_unit_value)] + fn main() { let x: [i32; 3] = [1_i32, 2, 3]; let r_x = &x; @@ -37,3 +39,44 @@ fn main() { let long_chain_restore = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8] as *const [u32]; } + +// foo and foo2 should not fire, they're the same size +fn foo(x: *mut [u8]) -> *mut [u8] { + x as *mut [u8] +} + +fn foo2(x: *mut [u8]) -> *mut [u8] { + x as *mut _ +} + +// Test that casts as part of function returns work +fn bar(x: *mut [u16]) -> *mut [u8] { + x as *mut [u8] +} + +fn uwu(x: *mut [u16]) -> *mut [u8] { + x as *mut _ +} + +fn bar2(x: *mut [u16]) -> *mut [u8] { + x as _ +} + +// constify +fn bar3(x: *mut [u16]) -> *const [u8] { + x as _ +} + +// unconstify +fn bar4(x: *const [u16]) -> *mut [u8] { + x as _ +} + +// function returns plus blocks +fn blocks(x: *mut [u16]) -> *mut [u8] { + ({ x }) as _ +} + +fn more_blocks(x: *mut [u16]) -> *mut [u8] { + { ({ x }) as _ } +} diff --git a/tests/ui/cast_slice_different_sizes.stderr b/tests/ui/cast_slice_different_sizes.stderr index a37cec7cb3be..40721dcd05d5 100644 --- a/tests/ui/cast_slice_different_sizes.stderr +++ b/tests/ui/cast_slice_different_sizes.stderr @@ -1,5 +1,5 @@ error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:7:13 + --> $DIR/cast_slice_different_sizes.rs:9:13 | LL | let b = a as *const [u8]; | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(a as *const u8, ..)` @@ -7,25 +7,25 @@ LL | let b = a as *const [u8]; = note: `#[deny(clippy::cast_slice_different_sizes)]` on by default error: casting between raw pointers to `[u8]` (element size 1) and `[u32]` (element size 4) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:8:13 + --> $DIR/cast_slice_different_sizes.rs:10:13 | LL | let c = b as *const [u32]; | ^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(b as *const u32, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:11:16 + --> $DIR/cast_slice_different_sizes.rs:13:16 | LL | let loss = r_x as *const [i32] as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:18:24 + --> $DIR/cast_slice_different_sizes.rs:20:24 | LL | let loss_block_1 = { r_x as *const [i32] } as *const [u8]; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts({ r_x as *const [i32] } as *const u8, ..)` error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:19:24 + --> $DIR/cast_slice_different_sizes.rs:21:24 | LL | let loss_block_2 = { | ________________________^ @@ -43,10 +43,79 @@ LL ~ } as *const u8, ..); | error: casting between raw pointers to `[i32]` (element size 4) and `[u8]` (element size 1) does not adjust the count - --> $DIR/cast_slice_different_sizes.rs:36:27 + --> $DIR/cast_slice_different_sizes.rs:38:27 | LL | let long_chain_loss = r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const [u8]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const [u32] as *const [u16] as *const [i8] as *const u8, ..)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(r_x as *const [i32] as *const u8, ..)` -error: aborting due to 6 previous errors +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:53:36 + | +LL | fn bar(x: *mut [u16]) -> *mut [u8] { + | ____________________________________^ +LL | | x as *mut [u8] +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:57:36 + | +LL | fn uwu(x: *mut [u16]) -> *mut [u8] { + | ____________________________________^ +LL | | x as *mut _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:61:37 + | +LL | fn bar2(x: *mut [u16]) -> *mut [u8] { + | _____________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:66:39 + | +LL | fn bar3(x: *mut [u16]) -> *const [u8] { + | _______________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts`: `core::ptr::slice_from_raw_parts(x as *const u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:71:39 + | +LL | fn bar4(x: *const [u16]) -> *mut [u8] { + | _______________________________________^ +LL | | x as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(x as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:76:39 + | +LL | fn blocks(x: *mut [u16]) -> *mut [u8] { + | _______________________________________^ +LL | | ({ x }) as _ +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:80:44 + | +LL | fn more_blocks(x: *mut [u16]) -> *mut [u8] { + | ____________________________________________^ +LL | | { ({ x }) as _ } +LL | | } + | |_^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: casting between raw pointers to `[u16]` (element size 2) and `[u8]` (element size 1) does not adjust the count + --> $DIR/cast_slice_different_sizes.rs:81:5 + | +LL | { ({ x }) as _ } + | ^^^^^^^^^^^^^^^^ help: replace with `ptr::slice_from_raw_parts_mut`: `core::ptr::slice_from_raw_parts_mut(({ x }) as *mut u8, ..)` + +error: aborting due to 14 previous errors diff --git a/tests/ui/collapsible_else_if.fixed b/tests/ui/collapsible_else_if.fixed index bb6c4c0703d5..d6a5a7850679 100644 --- a/tests/ui/collapsible_else_if.fixed +++ b/tests/ui/collapsible_else_if.fixed @@ -75,3 +75,10 @@ fn main() { } } } + +#[rustfmt::skip] +#[allow(dead_code)] +fn issue_7318() { + if true { println!("I've been resolved!") + }else if false {} +} diff --git a/tests/ui/collapsible_else_if.rs b/tests/ui/collapsible_else_if.rs index 6d4f688db8c0..4399fc8b2bd1 100644 --- a/tests/ui/collapsible_else_if.rs +++ b/tests/ui/collapsible_else_if.rs @@ -89,3 +89,12 @@ fn main() { } } } + +#[rustfmt::skip] +#[allow(dead_code)] +fn issue_7318() { + if true { println!("I've been resolved!") + }else{ + if false {} + } +} diff --git a/tests/ui/collapsible_else_if.stderr b/tests/ui/collapsible_else_if.stderr index 6970f6609790..45b2094c9948 100644 --- a/tests/ui/collapsible_else_if.stderr +++ b/tests/ui/collapsible_else_if.stderr @@ -150,5 +150,14 @@ LL + println!("!") LL + } | -error: aborting due to 7 previous errors +error: this `else { if .. }` block can be collapsed + --> $DIR/collapsible_else_if.rs:97:10 + | +LL | }else{ + | __________^ +LL | | if false {} +LL | | } + | |_____^ help: collapse nested if block: `if false {}` + +error: aborting due to 8 previous errors diff --git a/tests/ui/crashes/auxiliary/ice-8681-aux.rs b/tests/ui/crashes/auxiliary/ice-8681-aux.rs new file mode 100644 index 000000000000..95b631513252 --- /dev/null +++ b/tests/ui/crashes/auxiliary/ice-8681-aux.rs @@ -0,0 +1,6 @@ +pub fn foo(x: &u32) -> u32 { + /* Safety: + * This is totally ok. + */ + unsafe { *(x as *const u32) } +} diff --git a/tests/ui/crashes/ice-2865.rs b/tests/ui/crashes/ice-2865.rs index 6b1ceb505693..c62981396016 100644 --- a/tests/ui/crashes/ice-2865.rs +++ b/tests/ui/crashes/ice-2865.rs @@ -1,4 +1,4 @@ -#[allow(dead_code)] +#![allow(dead_code, clippy::extra_unused_lifetimes)] /// Test for https://github.com/rust-lang/rust-clippy/issues/2865 diff --git a/tests/ui/crashes/ice-3151.rs b/tests/ui/crashes/ice-3151.rs index fef4d7db84dd..268ba86fc7aa 100644 --- a/tests/ui/crashes/ice-3151.rs +++ b/tests/ui/crashes/ice-3151.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 +/// Test for https://github.com/rust-lang/rust-clippy/issues/3151 #[derive(Clone)] pub struct HashMap { diff --git a/tests/ui/crashes/ice-5944.rs b/tests/ui/crashes/ice-5944.rs index 5caf29c61973..ce46bc1acc1b 100644 --- a/tests/ui/crashes/ice-5944.rs +++ b/tests/ui/crashes/ice-5944.rs @@ -1,4 +1,5 @@ #![warn(clippy::repeat_once)] +#![allow(clippy::let_unit_value)] trait Repeat { fn repeat(&self) {} diff --git a/tests/ui/crashes/ice-8250.stderr b/tests/ui/crashes/ice-8250.stderr index 04ea44566565..8ed8f3b3a064 100644 --- a/tests/ui/crashes/ice-8250.stderr +++ b/tests/ui/crashes/ice-8250.stderr @@ -1,11 +1,3 @@ -error: manual implementation of `split_once` - --> $DIR/ice-8250.rs:2:13 - | -LL | let _ = s[1..].splitn(2, '.').next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s[1..].split_once('.').map_or(s[1..], |x| x.0)` - | - = note: `-D clippy::manual-split-once` implied by `-D warnings` - error: unnecessary use of `splitn` --> $DIR/ice-8250.rs:2:13 | @@ -14,5 +6,5 @@ LL | let _ = s[1..].splitn(2, '.').next()?; | = note: `-D clippy::needless-splitn` implied by `-D warnings` -error: aborting due to 2 previous errors +error: aborting due to previous error diff --git a/tests/ui/crashes/ice-8681.rs b/tests/ui/crashes/ice-8681.rs new file mode 100644 index 000000000000..ee14f011f631 --- /dev/null +++ b/tests/ui/crashes/ice-8681.rs @@ -0,0 +1,10 @@ +// aux-build: ice-8681-aux.rs + +#![warn(clippy::undocumented_unsafe_blocks)] + +#[path = "auxiliary/ice-8681-aux.rs"] +mod ice_8681_aux; + +fn main() { + let _ = ice_8681_aux::foo(&0u32); +} diff --git a/tests/ui/crashes/ice-96721.rs b/tests/ui/crashes/ice-96721.rs new file mode 100644 index 000000000000..4b3fb7640108 --- /dev/null +++ b/tests/ui/crashes/ice-96721.rs @@ -0,0 +1,10 @@ +macro_rules! foo { + () => { + "bar.rs" + }; +} + +#[path = foo!()] //~ ERROR malformed `path` attribute +mod abc {} + +fn main() {} diff --git a/tests/ui/crashes/ice-96721.stderr b/tests/ui/crashes/ice-96721.stderr new file mode 100644 index 000000000000..78c567b8e772 --- /dev/null +++ b/tests/ui/crashes/ice-96721.stderr @@ -0,0 +1,8 @@ +error: malformed `path` attribute input + --> $DIR/ice-96721.rs:7:1 + | +LL | #[path = foo!()] //~ ERROR malformed `path` attribute + | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]` + +error: aborting due to previous error + diff --git a/tests/ui/default_numeric_fallback_f64.fixed b/tests/ui/default_numeric_fallback_f64.fixed index e0b4a2f69423..a28bff76755b 100644 --- a/tests/ui/default_numeric_fallback_f64.fixed +++ b/tests/ui/default_numeric_fallback_f64.fixed @@ -2,12 +2,15 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] -#![allow(clippy::match_single_binding)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::match_single_binding, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_f64.rs b/tests/ui/default_numeric_fallback_f64.rs index 50bbb6eec6c7..b48435cc7b28 100644 --- a/tests/ui/default_numeric_fallback_f64.rs +++ b/tests/ui/default_numeric_fallback_f64.rs @@ -2,12 +2,15 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] -#![allow(clippy::match_single_binding)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::match_single_binding, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_f64.stderr b/tests/ui/default_numeric_fallback_f64.stderr index f8a2407b6933..f8b6c7746edb 100644 --- a/tests/ui/default_numeric_fallback_f64.stderr +++ b/tests/ui/default_numeric_fallback_f64.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:18:17 + --> $DIR/default_numeric_fallback_f64.rs:21:17 | LL | let x = 0.12; | ^^^^ help: consider adding suffix: `0.12_f64` @@ -7,133 +7,133 @@ LL | let x = 0.12; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:18 + --> $DIR/default_numeric_fallback_f64.rs:22:18 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:22 + --> $DIR/default_numeric_fallback_f64.rs:22:22 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:19:26 + --> $DIR/default_numeric_fallback_f64.rs:22:26 | LL | let x = [1., 2., 3.]; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:28 + --> $DIR/default_numeric_fallback_f64.rs:23:28 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:32 + --> $DIR/default_numeric_fallback_f64.rs:23:32 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `2.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:46 + --> $DIR/default_numeric_fallback_f64.rs:23:46 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `3.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:20:50 + --> $DIR/default_numeric_fallback_f64.rs:23:50 | LL | let x = if true { (1., 2.) } else { (3., 4.) }; | ^^ help: consider adding suffix: `4.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:21:23 + --> $DIR/default_numeric_fallback_f64.rs:24:23 | LL | let x = match 1. { | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:22:18 + --> $DIR/default_numeric_fallback_f64.rs:25:18 | LL | _ => 1., | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:40:21 + --> $DIR/default_numeric_fallback_f64.rs:43:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:48:21 + --> $DIR/default_numeric_fallback_f64.rs:51:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:54:21 + --> $DIR/default_numeric_fallback_f64.rs:57:21 | LL | let y = 1.; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:66:9 + --> $DIR/default_numeric_fallback_f64.rs:69:9 | LL | 1. | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:72:27 + --> $DIR/default_numeric_fallback_f64.rs:75:27 | LL | let f = || -> _ { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:76:29 + --> $DIR/default_numeric_fallback_f64.rs:79:29 | LL | let f = || -> f64 { 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:90:21 + --> $DIR/default_numeric_fallback_f64.rs:93:21 | LL | generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:93:32 + --> $DIR/default_numeric_fallback_f64.rs:96:32 | LL | let x: _ = generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:111:28 + --> $DIR/default_numeric_fallback_f64.rs:114:28 | LL | GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:114:36 + --> $DIR/default_numeric_fallback_f64.rs:117:36 | LL | let _ = GenericStruct { x: 1. }; | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:132:24 + --> $DIR/default_numeric_fallback_f64.rs:135:24 | LL | GenericEnum::X(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:152:23 + --> $DIR/default_numeric_fallback_f64.rs:155:23 | LL | s.generic_arg(1.); | ^^ help: consider adding suffix: `1.0_f64` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_f64.rs:159:21 + --> $DIR/default_numeric_fallback_f64.rs:162:21 | LL | let x = 22.; | ^^^ help: consider adding suffix: `22.0_f64` diff --git a/tests/ui/default_numeric_fallback_i32.fixed b/tests/ui/default_numeric_fallback_i32.fixed index bded9e2c0e80..fa85d278c8fc 100644 --- a/tests/ui/default_numeric_fallback_i32.fixed +++ b/tests/ui/default_numeric_fallback_i32.fixed @@ -2,11 +2,14 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_i32.rs b/tests/ui/default_numeric_fallback_i32.rs index 3fceefa551c7..71acccd702b0 100644 --- a/tests/ui/default_numeric_fallback_i32.rs +++ b/tests/ui/default_numeric_fallback_i32.rs @@ -2,11 +2,14 @@ // aux-build:macro_rules.rs #![warn(clippy::default_numeric_fallback)] -#![allow(unused)] -#![allow(clippy::never_loop)] -#![allow(clippy::no_effect)] -#![allow(clippy::unnecessary_operation)] -#![allow(clippy::branches_sharing_code)] +#![allow( + unused, + clippy::never_loop, + clippy::no_effect, + clippy::unnecessary_operation, + clippy::branches_sharing_code, + clippy::let_unit_value +)] #[macro_use] extern crate macro_rules; diff --git a/tests/ui/default_numeric_fallback_i32.stderr b/tests/ui/default_numeric_fallback_i32.stderr index 6f9e124704b2..3cc84ff11323 100644 --- a/tests/ui/default_numeric_fallback_i32.stderr +++ b/tests/ui/default_numeric_fallback_i32.stderr @@ -1,5 +1,5 @@ error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:17:17 + --> $DIR/default_numeric_fallback_i32.rs:20:17 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` @@ -7,145 +7,145 @@ LL | let x = 22; = note: `-D clippy::default-numeric-fallback` implied by `-D warnings` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:18 + --> $DIR/default_numeric_fallback_i32.rs:21:18 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:21 + --> $DIR/default_numeric_fallback_i32.rs:21:21 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:18:24 + --> $DIR/default_numeric_fallback_i32.rs:21:24 | LL | let x = [1, 2, 3]; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:28 + --> $DIR/default_numeric_fallback_i32.rs:22:28 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:31 + --> $DIR/default_numeric_fallback_i32.rs:22:31 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:44 + --> $DIR/default_numeric_fallback_i32.rs:22:44 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `3_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:19:47 + --> $DIR/default_numeric_fallback_i32.rs:22:47 | LL | let x = if true { (1, 2) } else { (3, 4) }; | ^ help: consider adding suffix: `4_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:20:23 + --> $DIR/default_numeric_fallback_i32.rs:23:23 | LL | let x = match 1 { | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:13 + --> $DIR/default_numeric_fallback_i32.rs:24:13 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:21:18 + --> $DIR/default_numeric_fallback_i32.rs:24:18 | LL | 1 => 1, | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:22:18 + --> $DIR/default_numeric_fallback_i32.rs:25:18 | LL | _ => 2, | ^ help: consider adding suffix: `2_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:39:21 + --> $DIR/default_numeric_fallback_i32.rs:42:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:47:21 + --> $DIR/default_numeric_fallback_i32.rs:50:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:53:21 + --> $DIR/default_numeric_fallback_i32.rs:56:21 | LL | let y = 1; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:65:9 + --> $DIR/default_numeric_fallback_i32.rs:68:9 | LL | 1 | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:71:27 + --> $DIR/default_numeric_fallback_i32.rs:74:27 | LL | let f = || -> _ { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:75:29 + --> $DIR/default_numeric_fallback_i32.rs:78:29 | LL | let f = || -> i32 { 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:89:21 + --> $DIR/default_numeric_fallback_i32.rs:92:21 | LL | generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:92:32 + --> $DIR/default_numeric_fallback_i32.rs:95:32 | LL | let x: _ = generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:110:28 + --> $DIR/default_numeric_fallback_i32.rs:113:28 | LL | GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:113:36 + --> $DIR/default_numeric_fallback_i32.rs:116:36 | LL | let _ = GenericStruct { x: 1 }; | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:131:24 + --> $DIR/default_numeric_fallback_i32.rs:134:24 | LL | GenericEnum::X(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:151:23 + --> $DIR/default_numeric_fallback_i32.rs:154:23 | LL | s.generic_arg(1); | ^ help: consider adding suffix: `1_i32` error: default numeric fallback might occur - --> $DIR/default_numeric_fallback_i32.rs:158:21 + --> $DIR/default_numeric_fallback_i32.rs:161:21 | LL | let x = 22; | ^^ help: consider adding suffix: `22_i32` diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs index 39a2601fee9a..07270bd76362 100644 --- a/tests/ui/deprecated.rs +++ b/tests/ui/deprecated.rs @@ -1,3 +1,7 @@ +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + #![warn(clippy::should_assert_eq)] #![warn(clippy::extend_from_slice)] #![warn(clippy::range_step_by_zero)] diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr index 6095f134d55e..0e142ac8f20e 100644 --- a/tests/ui/deprecated.stderr +++ b/tests/ui/deprecated.stderr @@ -1,5 +1,5 @@ error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011 - --> $DIR/deprecated.rs:1:9 + --> $DIR/deprecated.rs:5:9 | LL | #![warn(clippy::should_assert_eq)] | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,91 +7,91 @@ LL | #![warn(clippy::should_assert_eq)] = note: `-D renamed-and-removed-lints` implied by `-D warnings` error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice - --> $DIR/deprecated.rs:2:9 + --> $DIR/deprecated.rs:6:9 | LL | #![warn(clippy::extend_from_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays - --> $DIR/deprecated.rs:3:9 + --> $DIR/deprecated.rs:7:9 | LL | #![warn(clippy::range_step_by_zero)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7 - --> $DIR/deprecated.rs:4:9 + --> $DIR/deprecated.rs:8:9 | LL | #![warn(clippy::unstable_as_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7 - --> $DIR/deprecated.rs:5:9 + --> $DIR/deprecated.rs:9:9 | LL | #![warn(clippy::unstable_as_mut_slice)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr - --> $DIR/deprecated.rs:6:9 + --> $DIR/deprecated.rs:10:9 | LL | #![warn(clippy::misaligned_transmute)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless - --> $DIR/deprecated.rs:7:9 + --> $DIR/deprecated.rs:11:9 | LL | #![warn(clippy::assign_ops)] | ^^^^^^^^^^^^^^^^^^ error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching - --> $DIR/deprecated.rs:8:9 + --> $DIR/deprecated.rs:12:9 | LL | #![warn(clippy::if_let_redundant_pattern_matching)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior - --> $DIR/deprecated.rs:9:9 + --> $DIR/deprecated.rs:13:9 | LL | #![warn(clippy::unsafe_vector_initialization)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint - --> $DIR/deprecated.rs:10:9 + --> $DIR/deprecated.rs:14:9 | LL | #![warn(clippy::unused_collect)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants - --> $DIR/deprecated.rs:11:9 + --> $DIR/deprecated.rs:15:9 | LL | #![warn(clippy::replace_consts)] | ^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018 - --> $DIR/deprecated.rs:12:9 + --> $DIR/deprecated.rs:16:9 | LL | #![warn(clippy::regex_macro)] | ^^^^^^^^^^^^^^^^^^^ error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint - --> $DIR/deprecated.rs:13:9 + --> $DIR/deprecated.rs:17:9 | LL | #![warn(clippy::find_map)] | ^^^^^^^^^^^^^^^^ error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint - --> $DIR/deprecated.rs:14:9 + --> $DIR/deprecated.rs:18:9 | LL | #![warn(clippy::filter_map)] | ^^^^^^^^^^^^^^^^^^ error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items - --> $DIR/deprecated.rs:15:9 + --> $DIR/deprecated.rs:19:9 | LL | #![warn(clippy::pub_enum_variant_names)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items - --> $DIR/deprecated.rs:16:9 + --> $DIR/deprecated.rs:20:9 | LL | #![warn(clippy::wrong_pub_self_convention)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/doc_unsafe.rs b/tests/ui/doc_unsafe.rs index 4464a21b3b65..b91f7aa0dd8d 100644 --- a/tests/ui/doc_unsafe.rs +++ b/tests/ui/doc_unsafe.rs @@ -1,5 +1,7 @@ // aux-build:doc_unsafe_macros.rs +#![allow(clippy::let_unit_value)] + #[macro_use] extern crate doc_unsafe_macros; diff --git a/tests/ui/doc_unsafe.stderr b/tests/ui/doc_unsafe.stderr index d68b8a0c67be..904b88eaef62 100644 --- a/tests/ui/doc_unsafe.stderr +++ b/tests/ui/doc_unsafe.stderr @@ -1,5 +1,5 @@ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:7:1 + --> $DIR/doc_unsafe.rs:9:1 | LL | / pub unsafe fn destroy_the_planet() { LL | | unimplemented!(); @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::missing-safety-doc` implied by `-D warnings` error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:30:5 + --> $DIR/doc_unsafe.rs:32:5 | LL | / pub unsafe fn republished() { LL | | unimplemented!(); @@ -17,13 +17,13 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:38:5 + --> $DIR/doc_unsafe.rs:40:5 | LL | unsafe fn woefully_underdocumented(self); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: docs for unsafe trait missing `# Safety` section - --> $DIR/doc_unsafe.rs:44:1 + --> $DIR/doc_unsafe.rs:46:1 | LL | / pub unsafe trait UnsafeTrait { LL | | fn method(); @@ -31,7 +31,7 @@ LL | | } | |_^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:74:5 + --> $DIR/doc_unsafe.rs:76:5 | LL | / pub unsafe fn more_undocumented_unsafe() -> Self { LL | | unimplemented!(); @@ -39,7 +39,7 @@ LL | | } | |_____^ error: unsafe function's docs miss `# Safety` section - --> $DIR/doc_unsafe.rs:90:9 + --> $DIR/doc_unsafe.rs:92:9 | LL | / pub unsafe fn whee() { LL | | unimplemented!() diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr index f73068901c50..30121033de7e 100644 --- a/tests/ui/drop_non_drop.stderr +++ b/tests/ui/drop_non_drop.stderr @@ -1,4 +1,4 @@ -error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes --> $DIR/drop_non_drop.rs:22:5 | LL | drop(Foo); @@ -11,7 +11,7 @@ note: argument has type `main::Foo` LL | drop(Foo); | ^^^ -error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes +error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends its contained lifetimes --> $DIR/drop_non_drop.rs:37:5 | LL | drop(Baz(Foo)); diff --git a/tests/ui/empty_drop.fixed b/tests/ui/empty_drop.fixed new file mode 100644 index 000000000000..2e1b768461ab --- /dev/null +++ b/tests/ui/empty_drop.fixed @@ -0,0 +1,24 @@ +// run-rustfix +#![warn(clippy::empty_drop)] +#![allow(unused)] + +// should cause an error +struct Foo; + + + +// shouldn't cause an error +struct Bar; + +impl Drop for Bar { + fn drop(&mut self) { + println!("dropping bar!"); + } +} + +// should error +struct Baz; + + + +fn main() {} diff --git a/tests/ui/empty_drop.rs b/tests/ui/empty_drop.rs new file mode 100644 index 000000000000..75232b0334df --- /dev/null +++ b/tests/ui/empty_drop.rs @@ -0,0 +1,30 @@ +// run-rustfix +#![warn(clippy::empty_drop)] +#![allow(unused)] + +// should cause an error +struct Foo; + +impl Drop for Foo { + fn drop(&mut self) {} +} + +// shouldn't cause an error +struct Bar; + +impl Drop for Bar { + fn drop(&mut self) { + println!("dropping bar!"); + } +} + +// should error +struct Baz; + +impl Drop for Baz { + fn drop(&mut self) { + {} + } +} + +fn main() {} diff --git a/tests/ui/empty_drop.stderr b/tests/ui/empty_drop.stderr new file mode 100644 index 000000000000..70f7880d0360 --- /dev/null +++ b/tests/ui/empty_drop.stderr @@ -0,0 +1,22 @@ +error: empty drop implementation + --> $DIR/empty_drop.rs:8:1 + | +LL | / impl Drop for Foo { +LL | | fn drop(&mut self) {} +LL | | } + | |_^ help: try removing this impl + | + = note: `-D clippy::empty-drop` implied by `-D warnings` + +error: empty drop implementation + --> $DIR/empty_drop.rs:24:1 + | +LL | / impl Drop for Baz { +LL | | fn drop(&mut self) { +LL | | {} +LL | | } +LL | | } + | |_^ help: try removing this impl + +error: aborting due to 2 previous errors + diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed index 5aedbea381f2..6c2272f4dff9 100644 --- a/tests/ui/eta.fixed +++ b/tests/ui/eta.fixed @@ -37,7 +37,7 @@ fn main() { } // See #815 - let e = Some(1u8).map(divergent); + let e = Some(1u8).map(|a| divergent(a)); let e = Some(1u8).map(generic); let e = Some(1u8).map(generic); // See #515 @@ -211,6 +211,10 @@ fn mutable_closure_in_loop() { let mut closure = |n| value += n; for _ in 0..5 { Some(1).map(&mut closure); + + let mut value = 0; + let mut in_loop = |n| value += n; + Some(1).map(in_loop); } } @@ -229,7 +233,7 @@ fn late_bound_lifetimes() { { } map_str(|s| take_asref_path(s)); - map_str_to_path(std::convert::AsRef::as_ref); + map_str_to_path(|s| s.as_ref()); } mod type_param_bound { @@ -275,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs index 5fdf7fb97716..a1a9c0dfbf38 100644 --- a/tests/ui/eta.rs +++ b/tests/ui/eta.rs @@ -211,6 +211,10 @@ fn mutable_closure_in_loop() { let mut closure = |n| value += n; for _ in 0..5 { Some(1).map(|n| closure(n)); + + let mut value = 0; + let mut in_loop = |n| value += n; + Some(1).map(|n| in_loop(n)); } } @@ -275,3 +279,15 @@ mod bind_by_ref { Some(A).map(|ref a| B::from(a)); } } + +// #7812 False positive on coerced closure +fn coerced_closure() { + fn function_returning_unit(f: F) {} + function_returning_unit(|x| std::process::exit(x)); + + fn arr() -> &'static [u8; 0] { + &[] + } + fn slice_fn(_: impl FnOnce() -> &'static [u8]) {} + slice_fn(|| arr()); +} diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr index cda84982c9b7..bf2e97e744ab 100644 --- a/tests/ui/eta.stderr +++ b/tests/ui/eta.stderr @@ -24,12 +24,6 @@ error: redundant closure LL | all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted | ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below` -error: redundant closure - --> $DIR/eta.rs:40:27 - | -LL | let e = Some(1u8).map(|a| divergent(a)); - | ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `divergent` - error: redundant closure --> $DIR/eta.rs:41:27 | @@ -117,10 +111,10 @@ LL | Some(1).map(|n| closure(n)); | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure` error: redundant closure - --> $DIR/eta.rs:232:21 + --> $DIR/eta.rs:217:21 | -LL | map_str_to_path(|s| s.as_ref()); - | ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref` +LL | Some(1).map(|n| in_loop(n)); + | ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop` -error: aborting due to 20 previous errors +error: aborting due to 19 previous errors diff --git a/tests/ui/extra_unused_lifetimes.rs b/tests/ui/extra_unused_lifetimes.rs index 150acfbfee75..f76127a7105f 100644 --- a/tests/ui/extra_unused_lifetimes.rs +++ b/tests/ui/extra_unused_lifetimes.rs @@ -72,4 +72,46 @@ mod issue4291 { } } +mod issue6437 { + pub struct Scalar; + + impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { + fn add_assign(&mut self, _rhs: &Scalar) { + unimplemented!(); + } + } + + impl<'b> Scalar { + pub fn something<'c>() -> Self { + Self + } + } +} + +// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213 +mod first_case { + use serde::de::Visitor; + pub trait Expected { + fn fmt(&self, formatter: &mut std::fmt::Formatter); + } + + impl<'de, T> Expected for T + where + T: Visitor<'de>, + { + fn fmt(&self, formatter: &mut std::fmt::Formatter) {} + } +} + +// https://github.com/rust-lang/rust-clippy/pull/8737#pullrequestreview-951268213 +mod second_case { + pub trait Source { + fn hey(); + } + + impl<'a, T: Source + ?Sized + 'a> Source for Box { + fn hey() {} + } +} + fn main() {} diff --git a/tests/ui/extra_unused_lifetimes.stderr b/tests/ui/extra_unused_lifetimes.stderr index 9143fb2c208a..fcc12d4ce14b 100644 --- a/tests/ui/extra_unused_lifetimes.stderr +++ b/tests/ui/extra_unused_lifetimes.stderr @@ -18,5 +18,23 @@ error: this lifetime isn't used in the function definition LL | fn unused_lt<'a>(x: u8) {} | ^^ -error: aborting due to 3 previous errors +error: this lifetime isn't used in the impl + --> $DIR/extra_unused_lifetimes.rs:78:10 + | +LL | impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar { + | ^^ + +error: this lifetime isn't used in the impl + --> $DIR/extra_unused_lifetimes.rs:84:10 + | +LL | impl<'b> Scalar { + | ^^ + +error: this lifetime isn't used in the function definition + --> $DIR/extra_unused_lifetimes.rs:85:26 + | +LL | pub fn something<'c>() -> Self { + | ^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/format_push_string.rs b/tests/ui/format_push_string.rs new file mode 100644 index 000000000000..4db13d650eb1 --- /dev/null +++ b/tests/ui/format_push_string.rs @@ -0,0 +1,7 @@ +#![warn(clippy::format_push_string)] + +fn main() { + let mut string = String::new(); + string += &format!("{:?}", 1234); + string.push_str(&format!("{:?}", 5678)); +} diff --git a/tests/ui/format_push_string.stderr b/tests/ui/format_push_string.stderr new file mode 100644 index 000000000000..953784bcc068 --- /dev/null +++ b/tests/ui/format_push_string.stderr @@ -0,0 +1,19 @@ +error: `format!(..)` appended to existing `String` + --> $DIR/format_push_string.rs:5:5 + | +LL | string += &format!("{:?}", 1234); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::format-push-string` implied by `-D warnings` + = help: consider using `write!` to avoid the extra allocation + +error: `format!(..)` appended to existing `String` + --> $DIR/format_push_string.rs:6:5 + | +LL | string.push_str(&format!("{:?}", 5678)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider using `write!` to avoid the extra allocation + +error: aborting due to 2 previous errors + diff --git a/tests/ui/identity_op.rs b/tests/ui/identity_op.rs index edc3fe1aec13..fec54d00ccb4 100644 --- a/tests/ui/identity_op.rs +++ b/tests/ui/identity_op.rs @@ -1,3 +1,5 @@ +use std::fmt::Write as _; + const ONE: i64 = 1; const NEG_ONE: i64 = -1; const ZERO: i64 = 0; @@ -7,7 +9,7 @@ struct A(String); impl std::ops::Shl for A { type Output = A; fn shl(mut self, other: i32) -> Self { - self.0.push_str(&format!("{}", other)); + let _ = write!(self.0, "{}", other); self } } @@ -75,4 +77,34 @@ fn main() { (x + 1) % 3; // no error 4 % 3; // no error 4 % -3; // no error + + // See #8724 + let a = 0; + let b = true; + 0 + if b { 1 } else { 2 }; + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error + 0 + match a { 0 => 10, _ => 20 }; + 0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error + 0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error + 0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error + + 0 + if b { 0 + 1 } else { 2 }; + 0 + match a { 0 => 0 + 10, _ => 20 }; + 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + + let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + + 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + + 0 + { a } + 3; // no error + 0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error + + fn f(_: i32) { + todo!(); + } + f(1 * a + { 8 * 5 }); + f(0 + if b { 1 } else { 2 } + 3); // no error + const _: i32 = { 2 * 4 } + 0 + 3; + const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error } diff --git a/tests/ui/identity_op.stderr b/tests/ui/identity_op.stderr index 706f01a3dd6c..d8cb65839cbb 100644 --- a/tests/ui/identity_op.stderr +++ b/tests/ui/identity_op.stderr @@ -1,5 +1,5 @@ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:37:5 + --> $DIR/identity_op.rs:39:5 | LL | x + 0; | ^^^^^ @@ -7,106 +7,196 @@ LL | x + 0; = note: `-D clippy::identity-op` implied by `-D warnings` error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:38:5 + --> $DIR/identity_op.rs:40:5 | LL | x + (1 - 1); | ^^^^^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:40:5 + --> $DIR/identity_op.rs:42:5 | LL | 0 + x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:43:5 + --> $DIR/identity_op.rs:45:5 | LL | x | (0); | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:46:5 + --> $DIR/identity_op.rs:48:5 | LL | x * 1; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:47:5 + --> $DIR/identity_op.rs:49:5 | LL | 1 * x; | ^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:53:5 + --> $DIR/identity_op.rs:55:5 | LL | -1 & x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `u` - --> $DIR/identity_op.rs:56:5 + --> $DIR/identity_op.rs:58:5 | LL | u & 255; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:59:5 + --> $DIR/identity_op.rs:61:5 | LL | 42 << 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:60:5 + --> $DIR/identity_op.rs:62:5 | LL | 1 >> 0; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `42` - --> $DIR/identity_op.rs:61:5 + --> $DIR/identity_op.rs:63:5 | LL | 42 >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `&x` - --> $DIR/identity_op.rs:62:5 + --> $DIR/identity_op.rs:64:5 | LL | &x >> 0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `x` - --> $DIR/identity_op.rs:63:5 + --> $DIR/identity_op.rs:65:5 | LL | x >> &0; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:70:5 + --> $DIR/identity_op.rs:72:5 | LL | 2 % 3; | ^^^^^ error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:71:5 + --> $DIR/identity_op.rs:73:5 | LL | -2 % 3; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `2` - --> $DIR/identity_op.rs:72:5 + --> $DIR/identity_op.rs:74:5 | LL | 2 % -3 + x; | ^^^^^^ error: the operation is ineffective. Consider reducing it to `-2` - --> $DIR/identity_op.rs:73:5 + --> $DIR/identity_op.rs:75:5 | LL | -2 % -3 + x; | ^^^^^^^ error: the operation is ineffective. Consider reducing it to `1` - --> $DIR/identity_op.rs:74:9 + --> $DIR/identity_op.rs:76:9 | LL | x + 1 % 3; | ^^^^^ -error: aborting due to 18 previous errors +error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }` + --> $DIR/identity_op.rs:84:5 + | +LL | 0 + if b { 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }` + --> $DIR/identity_op.rs:86:5 + | +LL | 0 + match a { 0 => 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }` + --> $DIR/identity_op.rs:91:5 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:91:16 + | +LL | 0 + if b { 0 + 1 } else { 2 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `match a { 0 => 0 + 10, _ => 20 }` + --> $DIR/identity_op.rs:92:5 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `10` + --> $DIR/identity_op.rs:92:25 + | +LL | 0 + match a { 0 => 0 + 10, _ => 20 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:93:16 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `30` + --> $DIR/identity_op.rs:93:52 + | +LL | 0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 }; + | ^^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:20 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:95:52 + | +LL | let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:23 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `1` + --> $DIR/identity_op.rs:96:58 + | +LL | let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1 { 0 => 30, _ => 40 }; + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` + --> $DIR/identity_op.rs:98:5 + | +LL | 0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the operation is ineffective. Consider reducing it to `a` + --> $DIR/identity_op.rs:106:7 + | +LL | f(1 * a + { 8 * 5 }); + | ^^^^^ + +error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }` + --> $DIR/identity_op.rs:108:20 + | +LL | const _: i32 = { 2 * 4 } + 0 + 3; + | ^^^^^^^^^^^^^ + +error: aborting due to 33 previous errors diff --git a/tests/ui/impl.rs b/tests/ui/impl.rs index 394437750153..aea52a852f98 100644 --- a/tests/ui/impl.rs +++ b/tests/ui/impl.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] +#![allow(dead_code, clippy::extra_unused_lifetimes)] #![warn(clippy::multiple_inherent_impl)] struct MyStruct; diff --git a/tests/ui/is_digit_ascii_radix.fixed b/tests/ui/is_digit_ascii_radix.fixed new file mode 100644 index 000000000000..c0ba647d7079 --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.fixed @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::is_digit_ascii_radix)] + +const TEN: u32 = 10; + +fn main() { + let c: char = '6'; + + // Should trigger the lint. + let _ = c.is_ascii_digit(); + let _ = c.is_ascii_hexdigit(); + let _ = c.is_ascii_hexdigit(); + + // Should not trigger the lint. + let _ = c.is_digit(11); + let _ = c.is_digit(TEN); +} diff --git a/tests/ui/is_digit_ascii_radix.rs b/tests/ui/is_digit_ascii_radix.rs new file mode 100644 index 000000000000..68e3f3243d96 --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.rs @@ -0,0 +1,18 @@ +// run-rustfix + +#![warn(clippy::is_digit_ascii_radix)] + +const TEN: u32 = 10; + +fn main() { + let c: char = '6'; + + // Should trigger the lint. + let _ = c.is_digit(10); + let _ = c.is_digit(16); + let _ = c.is_digit(0x10); + + // Should not trigger the lint. + let _ = c.is_digit(11); + let _ = c.is_digit(TEN); +} diff --git a/tests/ui/is_digit_ascii_radix.stderr b/tests/ui/is_digit_ascii_radix.stderr new file mode 100644 index 000000000000..dc5cb2913ae1 --- /dev/null +++ b/tests/ui/is_digit_ascii_radix.stderr @@ -0,0 +1,22 @@ +error: use of `char::is_digit` with literal radix of 10 + --> $DIR/is_digit_ascii_radix.rs:11:13 + | +LL | let _ = c.is_digit(10); + | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_digit()` + | + = note: `-D clippy::is-digit-ascii-radix` implied by `-D warnings` + +error: use of `char::is_digit` with literal radix of 16 + --> $DIR/is_digit_ascii_radix.rs:12:13 + | +LL | let _ = c.is_digit(16); + | ^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()` + +error: use of `char::is_digit` with literal radix of 16 + --> $DIR/is_digit_ascii_radix.rs:13:13 + | +LL | let _ = c.is_digit(0x10); + | ^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_hexdigit()` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/iter_overeager_cloned.fixed b/tests/ui/iter_overeager_cloned.fixed index 56761ebbcb80..7c2b05d837ba 100644 --- a/tests/ui/iter_overeager_cloned.fixed +++ b/tests/ui/iter_overeager_cloned.fixed @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_unit_value)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/tests/ui/iter_overeager_cloned.rs b/tests/ui/iter_overeager_cloned.rs index 98321d889b58..f2d0b155d2c2 100644 --- a/tests/ui/iter_overeager_cloned.rs +++ b/tests/ui/iter_overeager_cloned.rs @@ -1,6 +1,6 @@ // run-rustfix #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)] -#![allow(dead_code)] +#![allow(dead_code, clippy::let_unit_value)] fn main() { let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()]; diff --git a/tests/ui/iter_with_drain.fixed b/tests/ui/iter_with_drain.fixed index aea4dba9dd5a..0330d5549264 100644 --- a/tests/ui/iter_with_drain.fixed +++ b/tests/ui/iter_with_drain.fixed @@ -39,6 +39,15 @@ fn should_not_help() { let _: Vec<_> = b.drain(0..a.len()).collect(); } +fn _closed_range(mut x: Vec) { + let _: Vec = x.drain(0..=x.len()).collect(); +} + +fn _with_mut(x: &mut Vec, y: &mut VecDeque) { + let _: Vec = x.drain(..).collect(); + let _: Vec = y.drain(..).collect(); +} + #[derive(Default)] struct Bomb { fire: Vec, diff --git a/tests/ui/iter_with_drain.rs b/tests/ui/iter_with_drain.rs index 271878cffb44..993936fb8de3 100644 --- a/tests/ui/iter_with_drain.rs +++ b/tests/ui/iter_with_drain.rs @@ -39,6 +39,15 @@ fn should_not_help() { let _: Vec<_> = b.drain(0..a.len()).collect(); } +fn _closed_range(mut x: Vec) { + let _: Vec = x.drain(0..=x.len()).collect(); +} + +fn _with_mut(x: &mut Vec, y: &mut VecDeque) { + let _: Vec = x.drain(..).collect(); + let _: Vec = y.drain(..).collect(); +} + #[derive(Default)] struct Bomb { fire: Vec, diff --git a/tests/ui/let_underscore_drop.rs b/tests/ui/let_underscore_drop.rs index 50744f81c3cf..11b50492ab29 100644 --- a/tests/ui/let_underscore_drop.rs +++ b/tests/ui/let_underscore_drop.rs @@ -1,4 +1,5 @@ #![warn(clippy::let_underscore_drop)] +#![allow(clippy::let_unit_value)] struct Droppable; diff --git a/tests/ui/let_underscore_drop.stderr b/tests/ui/let_underscore_drop.stderr index 66069e0c5e13..ee7bbe995f16 100644 --- a/tests/ui/let_underscore_drop.stderr +++ b/tests/ui/let_underscore_drop.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:16:5 + --> $DIR/let_underscore_drop.rs:17:5 | LL | let _ = Box::new(()); | ^^^^^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let _ = Box::new(()); = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:17:5 + --> $DIR/let_underscore_drop.rs:18:5 | LL | let _ = Droppable; | ^^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let _ = Droppable; = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop` error: non-binding `let` on a type that implements `Drop` - --> $DIR/let_underscore_drop.rs:18:5 + --> $DIR/let_underscore_drop.rs:19:5 | LL | let _ = Some(Droppable); | ^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/let_unit.fixed b/tests/ui/let_unit.fixed index f398edc23cb5..e72b74623255 100644 --- a/tests/ui/let_unit.fixed +++ b/tests/ui/let_unit.fixed @@ -61,3 +61,55 @@ fn multiline_sugg() { #[derive(Copy, Clone)] pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f() -> T { + unimplemented!() + } + fn f2(_: T) -> U { + unimplemented!() + } + fn f3(x: T) -> T { + x + } + fn f4(mut x: Vec) -> T { + x.pop().unwrap() + } + + let _: () = f(); // Ok + let _: () = f(); // Lint. + + let _: () = f2(0i32); // Ok + let _: () = f2(0i32); // Lint. + + f3(()); // Lint + f3(()); // Lint + + f4(vec![()]); // Lint + f4(vec![()]); // Lint + + // Ok + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; // Ok + let _: () = if true { f() } else { f2(0) }; // Lint + + // Ok + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => f2('x'), + }; + + // Lint + match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; +} diff --git a/tests/ui/let_unit.rs b/tests/ui/let_unit.rs index af5b1fb2ac7e..47ee0a767247 100644 --- a/tests/ui/let_unit.rs +++ b/tests/ui/let_unit.rs @@ -61,3 +61,55 @@ fn multiline_sugg() { #[derive(Copy, Clone)] pub struct ContainsUnit(()); // should be fine + +fn _returns_generic() { + fn f() -> T { + unimplemented!() + } + fn f2(_: T) -> U { + unimplemented!() + } + fn f3(x: T) -> T { + x + } + fn f4(mut x: Vec) -> T { + x.pop().unwrap() + } + + let _: () = f(); // Ok + let x: () = f(); // Lint. + + let _: () = f2(0i32); // Ok + let x: () = f2(0i32); // Lint. + + let _: () = f3(()); // Lint + let x: () = f3(()); // Lint + + let _: () = f4(vec![()]); // Lint + let x: () = f4(vec![()]); // Lint + + // Ok + let _: () = { + let x = 5; + f2(x) + }; + + let _: () = if true { f() } else { f2(0) }; // Ok + let x: () = if true { f() } else { f2(0) }; // Lint + + // Ok + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => f2('x'), + }; + + // Lint + let _: () = match Some(0) { + None => f2(1), + Some(0) => f(), + Some(1) => f2(3), + Some(_) => (), + }; +} diff --git a/tests/ui/let_unit.stderr b/tests/ui/let_unit.stderr index f2600c6c2288..13ec11a6d33e 100644 --- a/tests/ui/let_unit.stderr +++ b/tests/ui/let_unit.stderr @@ -34,5 +34,74 @@ LL + .map(|_| ()) LL + .next() ... -error: aborting due to 3 previous errors +error: this let-binding has unit value + --> $DIR/let_unit.rs:80:5 + | +LL | let x: () = f(); // Lint. + | ^^^^-^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:83:5 + | +LL | let x: () = f2(0i32); // Lint. + | ^^^^-^^^^^^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:85:5 + | +LL | let _: () = f3(()); // Lint + | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:86:5 + | +LL | let x: () = f3(()); // Lint + | ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:88:5 + | +LL | let _: () = f4(vec![()]); // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:89:5 + | +LL | let x: () = f4(vec![()]); // Lint + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:98:5 + | +LL | let x: () = if true { f() } else { f2(0) }; // Lint + | ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | help: use a wild (`_`) binding: `_` + +error: this let-binding has unit value + --> $DIR/let_unit.rs:109:5 + | +LL | / let _: () = match Some(0) { +LL | | None => f2(1), +LL | | Some(0) => f(), +LL | | Some(1) => f2(3), +LL | | Some(_) => (), +LL | | }; + | |______^ + | +help: omit the `let` binding + | +LL ~ match Some(0) { +LL + None => f2(1), +LL + Some(0) => f(), +LL + Some(1) => f2(3), +LL + Some(_) => (), +LL + }; + | + +error: aborting due to 11 previous errors diff --git a/tests/ui/manual_bits.fixed b/tests/ui/manual_bits.fixed index 4f1b19b75b8a..386360dbdcdb 100644 --- a/tests/ui/manual_bits.fixed +++ b/tests/ui/manual_bits.fixed @@ -1,38 +1,44 @@ // run-rustfix #![warn(clippy::manual_bits)] -#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::useless_conversion, + path_statements, + unused_must_use, + clippy::unnecessary_operation +)] use std::mem::{size_of, size_of_val}; fn main() { - i8::BITS; - i16::BITS; - i32::BITS; - i64::BITS; - i128::BITS; - isize::BITS; + i8::BITS as usize; + i16::BITS as usize; + i32::BITS as usize; + i64::BITS as usize; + i128::BITS as usize; + isize::BITS as usize; - u8::BITS; - u16::BITS; - u32::BITS; - u64::BITS; - u128::BITS; - usize::BITS; + u8::BITS as usize; + u16::BITS as usize; + u32::BITS as usize; + u64::BITS as usize; + u128::BITS as usize; + usize::BITS as usize; - i8::BITS; - i16::BITS; - i32::BITS; - i64::BITS; - i128::BITS; - isize::BITS; + i8::BITS as usize; + i16::BITS as usize; + i32::BITS as usize; + i64::BITS as usize; + i128::BITS as usize; + isize::BITS as usize; - u8::BITS; - u16::BITS; - u32::BITS; - u64::BITS; - u128::BITS; - usize::BITS; + u8::BITS as usize; + u16::BITS as usize; + u32::BITS as usize; + u64::BITS as usize; + u128::BITS as usize; + usize::BITS as usize; size_of::() * 4; 4 * size_of::(); @@ -42,7 +48,12 @@ fn main() { size_of_val(&0u32) * 8; type Word = u32; - Word::BITS; + Word::BITS as usize; type Bool = bool; size_of::() * 8; + + let _: u32 = u128::BITS as u32; + let _: u32 = u128::BITS.try_into().unwrap(); + let _ = (u128::BITS as usize).pow(5); + let _ = &(u128::BITS as usize); } diff --git a/tests/ui/manual_bits.rs b/tests/ui/manual_bits.rs index f8a01313e6ad..62638f047eb0 100644 --- a/tests/ui/manual_bits.rs +++ b/tests/ui/manual_bits.rs @@ -1,7 +1,13 @@ // run-rustfix #![warn(clippy::manual_bits)] -#![allow(clippy::no_effect, path_statements, unused_must_use, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::useless_conversion, + path_statements, + unused_must_use, + clippy::unnecessary_operation +)] use std::mem::{size_of, size_of_val}; @@ -45,4 +51,9 @@ fn main() { size_of::() * 8; type Bool = bool; size_of::() * 8; + + let _: u32 = (size_of::() * 8) as u32; + let _: u32 = (size_of::() * 8).try_into().unwrap(); + let _ = (size_of::() * 8).pow(5); + let _ = &(size_of::() * 8); } diff --git a/tests/ui/manual_bits.stderr b/tests/ui/manual_bits.stderr index c4f5af2dcb0e..69c591a203d3 100644 --- a/tests/ui/manual_bits.stderr +++ b/tests/ui/manual_bits.stderr @@ -1,154 +1,178 @@ error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:9:5 + --> $DIR/manual_bits.rs:15:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` | = note: `-D clippy::manual-bits` implied by `-D warnings` -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:10:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` - -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:11:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` - -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:12:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` - -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:13:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` - -error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:14:5 - | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` - error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:16:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:17:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:18:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:19:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:20:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:21:5 + --> $DIR/manual_bits.rs:22:5 | -LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:23:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:24:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:25:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:26:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:27:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS` +LL | size_of::() * 8; + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:28:5 + --> $DIR/manual_bits.rs:29:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `i8::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:30:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i16::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:31:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i32::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:32:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `i64::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:33:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `i128::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits --> $DIR/manual_bits.rs:34:5 | -LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `isize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:35:5 + --> $DIR/manual_bits.rs:36:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using: `u8::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:37:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u16::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:38:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u32::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:39:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^ help: consider using: `u64::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:40:5 + | +LL | 8 * size_of::(); + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS as usize` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:41:5 | LL | 8 * size_of::(); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS` + | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `usize::BITS as usize` error: usage of `mem::size_of::()` to obtain the size of `T` in bits - --> $DIR/manual_bits.rs:45:5 + --> $DIR/manual_bits.rs:51:5 | LL | size_of::() * 8; - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS` + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `Word::BITS as usize` -error: aborting due to 25 previous errors +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:55:18 + | +LL | let _: u32 = (size_of::() * 8) as u32; + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:56:18 + | +LL | let _: u32 = (size_of::() * 8).try_into().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `u128::BITS` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:57:13 + | +LL | let _ = (size_of::() * 8).pow(5); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` + +error: usage of `mem::size_of::()` to obtain the size of `T` in bits + --> $DIR/manual_bits.rs:58:14 + | +LL | let _ = &(size_of::() * 8); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(u128::BITS as usize)` + +error: aborting due to 29 previous errors diff --git a/tests/ui/manual_non_exhaustive_enum.rs b/tests/ui/manual_non_exhaustive_enum.rs new file mode 100644 index 000000000000..f23c6d69b4c6 --- /dev/null +++ b/tests/ui/manual_non_exhaustive_enum.rs @@ -0,0 +1,78 @@ +#![warn(clippy::manual_non_exhaustive)] +#![allow(unused)] + +enum E { + A, + B, + #[doc(hidden)] + _C, +} + +// user forgot to remove the marker +#[non_exhaustive] +enum Ep { + A, + B, + #[doc(hidden)] + _C, +} + +// marker variant does not have doc hidden attribute, should be ignored +enum NoDocHidden { + A, + B, + _C, +} + +// name of variant with doc hidden does not start with underscore, should be ignored +enum NoUnderscore { + A, + B, + #[doc(hidden)] + C, +} + +// variant with doc hidden is not unit, should be ignored +enum NotUnit { + A, + B, + #[doc(hidden)] + _C(bool), +} + +// variant with doc hidden is the only one, should be ignored +enum OnlyMarker { + #[doc(hidden)] + _A, +} + +// variant with multiple markers, should be ignored +enum MultipleMarkers { + A, + #[doc(hidden)] + _B, + #[doc(hidden)] + _C, +} + +// already non_exhaustive and no markers, should be ignored +#[non_exhaustive] +enum NonExhaustive { + A, + B, +} + +// marked is used, don't lint +enum UsedHidden { + #[doc(hidden)] + _A, + B, + C, +} +fn foo(x: &mut UsedHidden) { + if matches!(*x, UsedHidden::B) { + *x = UsedHidden::_A; + } +} + +fn main() {} diff --git a/tests/ui/manual_non_exhaustive_enum.stderr b/tests/ui/manual_non_exhaustive_enum.stderr new file mode 100644 index 000000000000..317a45d2cbd5 --- /dev/null +++ b/tests/ui/manual_non_exhaustive_enum.stderr @@ -0,0 +1,41 @@ +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive_enum.rs:4:1 + | +LL | enum E { + | ^----- + | | + | _help: add the attribute: `#[non_exhaustive] enum E` + | | +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_^ + | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` +help: remove this variant + --> $DIR/manual_non_exhaustive_enum.rs:8:5 + | +LL | _C, + | ^^ + +error: this seems like a manual implementation of the non-exhaustive pattern + --> $DIR/manual_non_exhaustive_enum.rs:13:1 + | +LL | / enum Ep { +LL | | A, +LL | | B, +LL | | #[doc(hidden)] +LL | | _C, +LL | | } + | |_^ + | +help: remove this variant + --> $DIR/manual_non_exhaustive_enum.rs:17:5 + | +LL | _C, + | ^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/manual_non_exhaustive.rs b/tests/ui/manual_non_exhaustive_struct.rs similarity index 58% rename from tests/ui/manual_non_exhaustive.rs rename to tests/ui/manual_non_exhaustive_struct.rs index 7a788f485207..498eee4447b8 100644 --- a/tests/ui/manual_non_exhaustive.rs +++ b/tests/ui/manual_non_exhaustive_struct.rs @@ -1,69 +1,6 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] -mod enums { - enum E { - A, - B, - #[doc(hidden)] - _C, - } - - // user forgot to remove the marker - #[non_exhaustive] - enum Ep { - A, - B, - #[doc(hidden)] - _C, - } - - // marker variant does not have doc hidden attribute, should be ignored - enum NoDocHidden { - A, - B, - _C, - } - - // name of variant with doc hidden does not start with underscore, should be ignored - enum NoUnderscore { - A, - B, - #[doc(hidden)] - C, - } - - // variant with doc hidden is not unit, should be ignored - enum NotUnit { - A, - B, - #[doc(hidden)] - _C(bool), - } - - // variant with doc hidden is the only one, should be ignored - enum OnlyMarker { - #[doc(hidden)] - _A, - } - - // variant with multiple markers, should be ignored - enum MultipleMarkers { - A, - #[doc(hidden)] - _B, - #[doc(hidden)] - _C, - } - - // already non_exhaustive and no markers, should be ignored - #[non_exhaustive] - enum NonExhaustive { - A, - B, - } -} - mod structs { struct S { pub a: i32, diff --git a/tests/ui/manual_non_exhaustive.stderr b/tests/ui/manual_non_exhaustive_struct.stderr similarity index 53% rename from tests/ui/manual_non_exhaustive.stderr rename to tests/ui/manual_non_exhaustive_struct.stderr index 613c5e8ca1d4..e0766c17b758 100644 --- a/tests/ui/manual_non_exhaustive.stderr +++ b/tests/ui/manual_non_exhaustive_struct.stderr @@ -1,44 +1,5 @@ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:5:5 - | -LL | enum E { - | ^----- - | | - | _____help: add the attribute: `#[non_exhaustive] enum E` - | | -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_____^ - | - = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` -help: remove this variant - --> $DIR/manual_non_exhaustive.rs:9:9 - | -LL | _C, - | ^^ - -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:14:5 - | -LL | / enum Ep { -LL | | A, -LL | | B, -LL | | #[doc(hidden)] -LL | | _C, -LL | | } - | |_____^ - | -help: remove this variant - --> $DIR/manual_non_exhaustive.rs:18:9 - | -LL | _C, - | ^^ - -error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:68:5 + --> $DIR/manual_non_exhaustive_struct.rs:5:5 | LL | struct S { | ^------- @@ -51,14 +12,15 @@ LL | | _c: (), LL | | } | |_____^ | + = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` help: remove this field - --> $DIR/manual_non_exhaustive.rs:71:9 + --> $DIR/manual_non_exhaustive_struct.rs:8:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:76:5 + --> $DIR/manual_non_exhaustive_struct.rs:13:5 | LL | / struct Sp { LL | | pub a: i32, @@ -68,13 +30,13 @@ LL | | } | |_____^ | help: remove this field - --> $DIR/manual_non_exhaustive.rs:79:9 + --> $DIR/manual_non_exhaustive_struct.rs:16:9 | LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:117:5 + --> $DIR/manual_non_exhaustive_struct.rs:54:5 | LL | struct T(pub i32, pub i32, ()); | --------^^^^^^^^^^^^^^^^^^^^^^^ @@ -82,22 +44,22 @@ LL | struct T(pub i32, pub i32, ()); | help: add the attribute: `#[non_exhaustive] struct T` | help: remove this field - --> $DIR/manual_non_exhaustive.rs:117:32 + --> $DIR/manual_non_exhaustive_struct.rs:54:32 | LL | struct T(pub i32, pub i32, ()); | ^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> $DIR/manual_non_exhaustive.rs:121:5 + --> $DIR/manual_non_exhaustive_struct.rs:58:5 | LL | struct Tp(pub i32, pub i32, ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove this field - --> $DIR/manual_non_exhaustive.rs:121:33 + --> $DIR/manual_non_exhaustive_struct.rs:58:33 | LL | struct Tp(pub i32, pub i32, ()); | ^^ -error: aborting due to 6 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/manual_split_once.fixed b/tests/ui/manual_split_once.fixed index d5113df569a0..c7ca770434a3 100644 --- a/tests/ui/manual_split_once.fixed +++ b/tests/ui/manual_split_once.fixed @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -10,27 +10,25 @@ extern crate itertools; use itertools::Itertools; fn main() { - let _ = Some("key=value".split_once('=').map_or("key=value", |x| x.0)); let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); - let _ = "key=value".split_once('=').map_or("key=value", |x| x.0); let _ = "key=value".split_once('=').unwrap().1; let _ = "key=value".split_once('=').unwrap().1; let (_, _) = "key=value".split_once('=').unwrap(); let s = String::from("key=value"); - let _ = s.split_once('=').map_or(&*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; let s = Box::::from("key=value"); - let _ = s.split_once('=').map_or(&*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; let s = &"key=value"; - let _ = s.split_once('=').map_or(*s, |x| x.0); + let _ = s.split_once('=').unwrap().1; fn _f(s: &str) -> Option<&str> { - let _ = s.split_once("key=value").map_or(s, |x| x.0); - let _ = s.split_once("key=value")?.1; - let _ = s.split_once("key=value")?.1; + let _ = s.split_once('=')?.1; + let _ = s.split_once('=')?.1; + let _ = s.rsplit_once('=')?.0; + let _ = s.rsplit_once('=')?.0; None } @@ -38,19 +36,112 @@ fn main() { let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplitn(2, '=').next().unwrap(); let _ = "key=value".rsplit_once('=').unwrap().0; - let _ = "key=value".rsplit_once('=').map(|x| x.1); let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap(); + let _ = s.rsplit_once('=').map(|x| x.0); +} + +fn indirect() -> Option<()> { + let (l, r) = "a.b.c".split_once('.').unwrap(); + + + + let (l, r) = "a.b.c".split_once('.')?; + + + + let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + + + + let (l, r) = "a.b.c".rsplit_once('.')?; + + + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None } fn _msrv_1_51() { #![clippy::msrv = "1.51"] - // `str::split_once` was stabilized in 1.16. Do not lint this + // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } fn _msrv_1_52() { #![clippy::msrv = "1.52"] let _ = "key=value".split_once('=').unwrap().1; + + let (a, b) = "a.b.c".split_once('.').unwrap(); + + } diff --git a/tests/ui/manual_split_once.rs b/tests/ui/manual_split_once.rs index 80e02952dbd0..ee2848a251ee 100644 --- a/tests/ui/manual_split_once.rs +++ b/tests/ui/manual_split_once.rs @@ -2,7 +2,7 @@ #![feature(custom_inner_attributes)] #![warn(clippy::manual_split_once)] -#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::needless_splitn)] +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)] extern crate itertools; @@ -10,27 +10,25 @@ extern crate itertools; use itertools::Itertools; fn main() { - let _ = "key=value".splitn(2, '=').next(); let _ = "key=value".splitn(2, '=').nth(2); - let _ = "key=value".splitn(2, '=').next().unwrap(); - let _ = "key=value".splitn(2, '=').nth(0).unwrap(); let _ = "key=value".splitn(2, '=').nth(1).unwrap(); let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); let s = String::from("key=value"); - let _ = s.splitn(2, '=').next().unwrap(); + let _ = s.splitn(2, '=').nth(1).unwrap(); let s = Box::::from("key=value"); - let _ = s.splitn(2, '=').nth(0).unwrap(); + let _ = s.splitn(2, '=').nth(1).unwrap(); let s = &"key=value"; - let _ = s.splitn(2, '=').skip(0).next().unwrap(); + let _ = s.splitn(2, '=').skip(1).next().unwrap(); fn _f(s: &str) -> Option<&str> { - let _ = s.splitn(2, "key=value").next()?; - let _ = s.splitn(2, "key=value").nth(1)?; - let _ = s.splitn(2, "key=value").skip(1).next()?; + let _ = s.splitn(2, '=').nth(1)?; + let _ = s.splitn(2, '=').skip(1).next()?; + let _ = s.rsplitn(2, '=').nth(1)?; + let _ = s.rsplitn(2, '=').skip(1).next()?; None } @@ -38,19 +36,112 @@ fn main() { let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap(); // `rsplitn` gives the results in the reverse order of `rsplit_once` - let _ = "key=value".rsplitn(2, '=').next().unwrap(); let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); - let _ = "key=value".rsplitn(2, '=').nth(0); let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); + let _ = s.rsplitn(2, '=').nth(1); +} + +fn indirect() -> Option<()> { + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next().unwrap(); + let r = iter.next().unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".rsplitn(2, '.'); + let r = iter.next().unwrap(); + let l = iter.next().unwrap(); + + let mut iter = "a.b.c".rsplitn(2, '.'); + let r = iter.next()?; + let l = iter.next()?; + + // could lint, currently doesn't + + let mut iter = "a.b.c".splitn(2, '.'); + let other = 1; + let l = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let mut mut_binding = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let tuple = (iter.next()?, iter.next()?); + + // should not lint + + let mut missing_unwrap = "a.b.c".splitn(2, '.'); + let l = missing_unwrap.next(); + let r = missing_unwrap.next(); + + let mut mixed_unrap = "a.b.c".splitn(2, '.'); + let unwrap = mixed_unrap.next().unwrap(); + let question_mark = mixed_unrap.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let same_name = iter.next()?; + let same_name = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let shadows_existing = "d"; + let shadows_existing = iter.next()?; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let becomes_shadowed = iter.next()?; + let becomes_shadowed = "d"; + let r = iter.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + let r = iter.next()?; + let third_usage = iter.next()?; + + let mut n_three = "a.b.c".splitn(3, '.'); + let l = n_three.next()?; + let r = n_three.next()?; + + let mut iter = "a.b.c".splitn(2, '.'); + { + let in_block = iter.next()?; + } + let r = iter.next()?; + + let mut lacks_binding = "a.b.c".splitn(2, '.'); + let _ = lacks_binding.next()?; + let r = lacks_binding.next()?; + + let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~"); + let l = iter.next()?; + let r = iter.next()?; + + let mut assigned = ""; + let mut iter = "a.b.c".splitn(2, '.'); + let l = iter.next()?; + assigned = iter.next()?; + + None } fn _msrv_1_51() { #![clippy::msrv = "1.51"] - // `str::split_once` was stabilized in 1.16. Do not lint this + // `str::split_once` was stabilized in 1.52. Do not lint this let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } fn _msrv_1_52() { #![clippy::msrv = "1.52"] let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + + let mut iter = "a.b.c".splitn(2, '.'); + let a = iter.next().unwrap(); + let b = iter.next().unwrap(); } diff --git a/tests/ui/manual_split_once.stderr b/tests/ui/manual_split_once.stderr index af9c7a2d41bf..2563a6904b77 100644 --- a/tests/ui/manual_split_once.stderr +++ b/tests/ui/manual_split_once.stderr @@ -1,100 +1,213 @@ error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:13:13 + --> $DIR/manual_split_once.rs:14:13 | -LL | let _ = "key=value".splitn(2, '=').next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some("key=value".split_once('=').map_or("key=value", |x| x.0))` +LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` | = note: `-D clippy::manual-split-once` implied by `-D warnings` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:15:13 | -LL | let _ = "key=value".splitn(2, '=').next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:16:13 - | -LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').map_or("key=value", |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:17:13 - | -LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:18:13 - | LL | let _ = "key=value".splitn(2, '=').skip(1).next().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:19:18 + --> $DIR/manual_split_once.rs:16:18 | LL | let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=')` +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:19:13 + | +LL | let _ = s.splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` + error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:22:13 | -LL | let _ = s.splitn(2, '=').next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').nth(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` --> $DIR/manual_split_once.rs:25:13 | -LL | let _ = s.splitn(2, '=').nth(0).unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(&*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').skip(1).next().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').unwrap().1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:28:13 + --> $DIR/manual_split_once.rs:28:17 | -LL | let _ = s.splitn(2, '=').skip(0).next().unwrap(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=').map_or(*s, |x| x.0)` +LL | let _ = s.splitn(2, '=').nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:31:17 + --> $DIR/manual_split_once.rs:29:17 | -LL | let _ = s.splitn(2, "key=value").next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value").map_or(s, |x| x.0)` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:32:17 - | -LL | let _ = s.splitn(2, "key=value").nth(1)?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` - -error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:33:17 - | -LL | let _ = s.splitn(2, "key=value").skip(1).next()?; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once("key=value")?.1` +LL | let _ = s.splitn(2, '=').skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.split_once('=')?.1` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:42:13 + --> $DIR/manual_split_once.rs:30:17 + | +LL | let _ = s.rsplitn(2, '=').nth(1)?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:31:17 + | +LL | let _ = s.rsplitn(2, '=').skip(1).next()?; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=')?.0` + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:39:13 | LL | let _ = "key=value".rsplitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').unwrap().0` error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:43:13 - | -LL | let _ = "key=value".rsplitn(2, '=').nth(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|x| x.1)` - -error: manual implementation of `rsplit_once` - --> $DIR/manual_split_once.rs:44:18 + --> $DIR/manual_split_once.rs:40:18 | LL | let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".rsplit_once('=').map(|(x, y)| (y, x))` +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:41:13 + | +LL | let _ = s.rsplitn(2, '=').nth(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit_once('=').map(|x| x.0)` + error: manual implementation of `split_once` - --> $DIR/manual_split_once.rs:55:13 + --> $DIR/manual_split_once.rs:45:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let l = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let r = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `split_once` + | +LL | let (l, r) = "a.b.c".split_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let l = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let r = iter.next().unwrap(); +LL + + | + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:49:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let l = iter.next()?; + | --------------------- first usage here +LL | let r = iter.next()?; + | --------------------- second usage here + | +help: try `split_once` + | +LL | let (l, r) = "a.b.c".split_once('.')?; + | +help: remove the `iter` usages + | +LL - let l = iter.next()?; +LL + + | +help: remove the `iter` usages + | +LL - let r = iter.next()?; +LL + + | + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:53:5 + | +LL | let mut iter = "a.b.c".rsplitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let r = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let l = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `rsplit_once` + | +LL | let (l, r) = "a.b.c".rsplit_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let r = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let l = iter.next().unwrap(); +LL + + | + +error: manual implementation of `rsplit_once` + --> $DIR/manual_split_once.rs:57:5 + | +LL | let mut iter = "a.b.c".rsplitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let r = iter.next()?; + | --------------------- first usage here +LL | let l = iter.next()?; + | --------------------- second usage here + | +help: try `rsplit_once` + | +LL | let (l, r) = "a.b.c".rsplit_once('.')?; + | +help: remove the `iter` usages + | +LL - let r = iter.next()?; +LL + + | +help: remove the `iter` usages + | +LL - let l = iter.next()?; +LL + + | + +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:142:13 | LL | let _ = "key=value".splitn(2, '=').nth(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split_once('=').unwrap().1` -error: aborting due to 16 previous errors +error: manual implementation of `split_once` + --> $DIR/manual_split_once.rs:144:5 + | +LL | let mut iter = "a.b.c".splitn(2, '.'); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | let a = iter.next().unwrap(); + | ----------------------------- first usage here +LL | let b = iter.next().unwrap(); + | ----------------------------- second usage here + | +help: try `split_once` + | +LL | let (a, b) = "a.b.c".split_once('.').unwrap(); + | +help: remove the `iter` usages + | +LL - let a = iter.next().unwrap(); +LL + + | +help: remove the `iter` usages + | +LL - let b = iter.next().unwrap(); +LL + + | + +error: aborting due to 19 previous errors diff --git a/tests/ui/mistyped_literal_suffix.fixed b/tests/ui/mistyped_literal_suffix.fixed index 5d57638af434..a7b36d53cd26 100644 --- a/tests/ui/mistyped_literal_suffix.fixed +++ b/tests/ui/mistyped_literal_suffix.fixed @@ -5,7 +5,8 @@ unused_variables, overflowing_literals, clippy::excessive_precision, - clippy::inconsistent_digit_grouping + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings )] fn main() { @@ -25,5 +26,18 @@ fn main() { let fail28 = 241_251_235E723_f64; let ok29 = 42279.911_32; + // testing that the suggestion actually fits in its type + let fail30 = 127_i8; // should be i8 + let fail31 = 240_u8; // should be u8 + let ok32 = 360_8; // doesnt fit in either, should be ignored + let fail33 = 0x1234_i16; + let fail34 = 0xABCD_u16; + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_u64; // u64 + + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + let _ = 1.123_45E1_f32; } diff --git a/tests/ui/mistyped_literal_suffix.rs b/tests/ui/mistyped_literal_suffix.rs index 12171452885d..c97b31965c75 100644 --- a/tests/ui/mistyped_literal_suffix.rs +++ b/tests/ui/mistyped_literal_suffix.rs @@ -5,7 +5,8 @@ unused_variables, overflowing_literals, clippy::excessive_precision, - clippy::inconsistent_digit_grouping + clippy::inconsistent_digit_grouping, + clippy::unusual_byte_groupings )] fn main() { @@ -25,5 +26,18 @@ fn main() { let fail28 = 241251235E723_64; let ok29 = 42279.911_32; + // testing that the suggestion actually fits in its type + let fail30 = 127_8; // should be i8 + let fail31 = 240_8; // should be u8 + let ok32 = 360_8; // doesnt fit in either, should be ignored + let fail33 = 0x1234_16; + let fail34 = 0xABCD_16; + let ok35 = 0x12345_16; + let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + + // issue #6129 + let ok37 = 123_32.123; + let ok38 = 124_64.0; + let _ = 1.12345E1_32; } diff --git a/tests/ui/mistyped_literal_suffix.stderr b/tests/ui/mistyped_literal_suffix.stderr index d24543c26e4b..fb761d9bde45 100644 --- a/tests/ui/mistyped_literal_suffix.stderr +++ b/tests/ui/mistyped_literal_suffix.stderr @@ -1,5 +1,5 @@ error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:12:18 + --> $DIR/mistyped_literal_suffix.rs:13:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` @@ -7,64 +7,94 @@ LL | let fail14 = 2_32; = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:13:18 + --> $DIR/mistyped_literal_suffix.rs:14:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:14:18 + --> $DIR/mistyped_literal_suffix.rs:15:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:15:18 + --> $DIR/mistyped_literal_suffix.rs:16:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:18:18 + --> $DIR/mistyped_literal_suffix.rs:19:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:19:18 + --> $DIR/mistyped_literal_suffix.rs:20:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:22:18 + --> $DIR/mistyped_literal_suffix.rs:23:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:23:18 + --> $DIR/mistyped_literal_suffix.rs:24:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:24:18 + --> $DIR/mistyped_literal_suffix.rs:25:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:25:18 + --> $DIR/mistyped_literal_suffix.rs:26:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> $DIR/mistyped_literal_suffix.rs:28:13 + --> $DIR/mistyped_literal_suffix.rs:30:18 + | +LL | let fail30 = 127_8; // should be i8 + | ^^^^^ help: did you mean to write: `127_i8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:31:18 + | +LL | let fail31 = 240_8; // should be u8 + | ^^^^^ help: did you mean to write: `240_u8` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:33:18 + | +LL | let fail33 = 0x1234_16; + | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:34:18 + | +LL | let fail34 = 0xABCD_16; + | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:36:18 + | +LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` + +error: mistyped literal suffix + --> $DIR/mistyped_literal_suffix.rs:42:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` -error: aborting due to 11 previous errors +error: aborting due to 16 previous errors diff --git a/tests/ui/mut_from_ref.rs b/tests/ui/mut_from_ref.rs index a9a04c8f56b9..370dbd588216 100644 --- a/tests/ui/mut_from_ref.rs +++ b/tests/ui/mut_from_ref.rs @@ -5,7 +5,7 @@ struct Foo; impl Foo { fn this_wont_hurt_a_bit(&self) -> &mut Foo { - unimplemented!() + unsafe { unimplemented!() } } } @@ -15,29 +15,37 @@ trait Ouch { impl Ouch for Foo { fn ouch(x: &Foo) -> &mut Foo { - unimplemented!() + unsafe { unimplemented!() } } } fn fail(x: &u32) -> &mut u16 { - unimplemented!() + unsafe { unimplemented!() } } fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } // this is OK, because the result borrows y fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 { - unimplemented!() + unsafe { unimplemented!() } } // this is also OK, because the result could borrow y fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 { + unsafe { unimplemented!() } +} + +unsafe fn also_broken(x: &u32) -> &mut u32 { + unimplemented!() +} + +fn without_unsafe(x: &u32) -> &mut u32 { unimplemented!() } diff --git a/tests/ui/mut_from_ref.stderr b/tests/ui/mut_from_ref.stderr index 4787999920bc..b76d6a13ffb9 100644 --- a/tests/ui/mut_from_ref.stderr +++ b/tests/ui/mut_from_ref.stderr @@ -59,5 +59,17 @@ note: immutable borrow here LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ -error: aborting due to 5 previous errors +error: mutable borrow from immutable input(s) + --> $DIR/mut_from_ref.rs:44:35 + | +LL | unsafe fn also_broken(x: &u32) -> &mut u32 { + | ^^^^^^^^ + | +note: immutable borrow here + --> $DIR/mut_from_ref.rs:44:26 + | +LL | unsafe fn also_broken(x: &u32) -> &mut u32 { + | ^^^^ + +error: aborting due to 6 previous errors diff --git a/tests/ui/needless_for_each_fixable.fixed b/tests/ui/needless_for_each_fixable.fixed index f00f9ee4c331..c1685f7b6d7a 100644 --- a/tests/ui/needless_for_each_fixable.fixed +++ b/tests/ui/needless_for_each_fixable.fixed @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] -#![allow(unused, clippy::needless_return, clippy::match_single_binding)] +#![allow( + unused, + clippy::needless_return, + clippy::match_single_binding, + clippy::let_unit_value +)] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.rs b/tests/ui/needless_for_each_fixable.rs index 1bd400d348ba..ad17b0956fa9 100644 --- a/tests/ui/needless_for_each_fixable.rs +++ b/tests/ui/needless_for_each_fixable.rs @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::needless_for_each)] -#![allow(unused, clippy::needless_return, clippy::match_single_binding)] +#![allow( + unused, + clippy::needless_return, + clippy::match_single_binding, + clippy::let_unit_value +)] use std::collections::HashMap; diff --git a/tests/ui/needless_for_each_fixable.stderr b/tests/ui/needless_for_each_fixable.stderr index 6487e57266c7..08e995851d7a 100644 --- a/tests/ui/needless_for_each_fixable.stderr +++ b/tests/ui/needless_for_each_fixable.stderr @@ -1,5 +1,5 @@ error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:10:5 + --> $DIR/needless_for_each_fixable.rs:15:5 | LL | / v.iter().for_each(|elem| { LL | | acc += elem; @@ -15,7 +15,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:13:5 + --> $DIR/needless_for_each_fixable.rs:18:5 | LL | / v.into_iter().for_each(|elem| { LL | | acc += elem; @@ -30,7 +30,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:17:5 + --> $DIR/needless_for_each_fixable.rs:22:5 | LL | / [1, 2, 3].iter().for_each(|elem| { LL | | acc += elem; @@ -45,7 +45,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:22:5 + --> $DIR/needless_for_each_fixable.rs:27:5 | LL | / hash_map.iter().for_each(|(k, v)| { LL | | acc += k + v; @@ -60,7 +60,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:25:5 + --> $DIR/needless_for_each_fixable.rs:30:5 | LL | / hash_map.iter_mut().for_each(|(k, v)| { LL | | acc += *k + *v; @@ -75,7 +75,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:28:5 + --> $DIR/needless_for_each_fixable.rs:33:5 | LL | / hash_map.keys().for_each(|k| { LL | | acc += k; @@ -90,7 +90,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:31:5 + --> $DIR/needless_for_each_fixable.rs:36:5 | LL | / hash_map.values().for_each(|v| { LL | | acc += v; @@ -105,7 +105,7 @@ LL + } | error: needless use of `for_each` - --> $DIR/needless_for_each_fixable.rs:38:5 + --> $DIR/needless_for_each_fixable.rs:43:5 | LL | / my_vec().iter().for_each(|elem| { LL | | acc += elem; diff --git a/tests/ui/needless_late_init.rs b/tests/ui/needless_late_init.rs index 89e012c066fe..54e66b391b8d 100644 --- a/tests/ui/needless_late_init.rs +++ b/tests/ui/needless_late_init.rs @@ -1,4 +1,15 @@ -#![allow(unused)] +#![feature(let_chains)] +#![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)] + +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::rc::Rc; + +struct SignificantDrop; +impl std::ops::Drop for SignificantDrop { + fn drop(&mut self) { + println!("dropped"); + } +} fn main() { let a; @@ -17,13 +28,6 @@ fn main() { b = "five" } - let c; - if let Some(n) = Some(5) { - c = n; - } else { - c = -50; - } - let d; if true { let temp = 5; @@ -36,7 +40,7 @@ fn main() { if true { e = format!("{} {}", a, b); } else { - e = format!("{}", c); + e = format!("{}", n); } let f; @@ -52,7 +56,27 @@ fn main() { panic!(); } - println!("{}", a); + // Drop order only matters if both are significant + let x; + let y = SignificantDrop; + x = 1; + + let x; + let y = 1; + x = SignificantDrop; + + let x; + // types that should be considered insignificant + let y = 1; + let y = "2"; + let y = String::new(); + let y = vec![3.0]; + let y = HashMap::::new(); + let y = BTreeMap::::new(); + let y = HashSet::::new(); + let y = BTreeSet::::new(); + let y = Box::new(4); + x = SignificantDrop; } async fn in_async() -> &'static str { @@ -176,5 +200,32 @@ fn does_not_lint() { } in_macro!(); - println!("{}", x); + // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613 + let x; + if let Some(n) = Some("v") { + x = 1; + } else { + x = 2; + } + + let x; + if true && let Some(n) = Some("let chains too") { + x = 1; + } else { + x = 2; + } + + // ignore mut bindings + // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93 + // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100 + let mut x: usize; + x = 1; + x = 2; + x = 3; + + // should not move the declaration if `x` has a significant drop, and there + // is another binding with a significant drop between it and the first usage + let x; + let y = SignificantDrop; + x = SignificantDrop; } diff --git a/tests/ui/needless_late_init.stderr b/tests/ui/needless_late_init.stderr index ef79e635d2ae..d33a117b288c 100644 --- a/tests/ui/needless_late_init.stderr +++ b/tests/ui/needless_late_init.stderr @@ -1,5 +1,5 @@ -error: unneeded late initalization - --> $DIR/needless_late_init.rs:4:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:15:5 | LL | let a; | ^^^^^^ @@ -20,8 +20,8 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:13:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:24:5 | LL | let b; | ^^^^^^ @@ -41,29 +41,8 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:20:5 - | -LL | let c; - | ^^^^^^ - | -help: declare `c` here - | -LL | let c = if let Some(n) = Some(5) { - | +++++++ -help: remove the assignments from the branches - | -LL ~ n -LL | } else { -LL ~ -50 - | -help: add a semicolon after the `if` expression - | -LL | }; - | + - -error: unneeded late initalization - --> $DIR/needless_late_init.rs:27:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:31:5 | LL | let d; | ^^^^^^ @@ -83,8 +62,8 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:35:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:39:5 | LL | let e; | ^^^^^^ @@ -97,15 +76,15 @@ help: remove the assignments from the branches | LL ~ format!("{} {}", a, b) LL | } else { -LL ~ format!("{}", c) +LL ~ format!("{}", n) | help: add a semicolon after the `if` expression | LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:42:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:46:5 | LL | let f; | ^^^^^^ @@ -120,8 +99,8 @@ LL - 1 => f = "three", LL + 1 => "three", | -error: unneeded late initalization - --> $DIR/needless_late_init.rs:48:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:52:5 | LL | let g: usize; | ^^^^^^^^^^^^^ @@ -140,8 +119,50 @@ help: add a semicolon after the `if` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:63:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:60:5 + | +LL | let x; + | ^^^^^^ created here +LL | let y = SignificantDrop; +LL | x = 1; + | ^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = 1; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:64:5 + | +LL | let x; + | ^^^^^^ created here +LL | let y = 1; +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = SignificantDrop; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:68:5 + | +LL | let x; + | ^^^^^^ created here +... +LL | x = SignificantDrop; + | ^^^^^^^^^^^^^^^^^^^ initialised here + | +help: declare `x` here + | +LL | let x = SignificantDrop; + | ~~~~~ + +error: unneeded late initialization + --> $DIR/needless_late_init.rs:87:5 | LL | let a; | ^^^^^^ @@ -161,8 +182,8 @@ help: add a semicolon after the `match` expression LL | }; | + -error: unneeded late initalization - --> $DIR/needless_late_init.rs:80:5 +error: unneeded late initialization + --> $DIR/needless_late_init.rs:104:5 | LL | let a; | ^^^^^^ @@ -182,5 +203,5 @@ help: add a semicolon after the `match` expression LL | }; | + -error: aborting due to 9 previous errors +error: aborting due to 11 previous errors diff --git a/tests/ui/needless_late_init_fixable.fixed b/tests/ui/needless_late_init_fixable.fixed index b516f9d86b75..724477e8691d 100644 --- a/tests/ui/needless_late_init_fixable.fixed +++ b/tests/ui/needless_late_init_fixable.fixed @@ -15,11 +15,5 @@ fn main() { let d: usize = 1; - let mut e = 1; - e = 2; - - - let h = format!("{}", e); - - println!("{}", a); + let e = format!("{}", d); } diff --git a/tests/ui/needless_late_init_fixable.rs b/tests/ui/needless_late_init_fixable.rs index 75a4bc916dea..3e6bd3636727 100644 --- a/tests/ui/needless_late_init_fixable.rs +++ b/tests/ui/needless_late_init_fixable.rs @@ -14,12 +14,6 @@ fn main() { let d: usize; d = 1; - let mut e; - e = 1; - e = 2; - - let h; - h = format!("{}", e); - - println!("{}", a); + let e; + e = format!("{}", d); } diff --git a/tests/ui/needless_late_init_fixable.stderr b/tests/ui/needless_late_init_fixable.stderr index 3f3d4f5286b2..8c664309e3e8 100644 --- a/tests/ui/needless_late_init_fixable.stderr +++ b/tests/ui/needless_late_init_fixable.stderr @@ -1,8 +1,10 @@ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:6:5 | LL | let a; - | ^^^^^^ + | ^^^^^^ created here +LL | a = "zero"; + | ^^^^^^^^^^ initialised here | = note: `-D clippy::needless-late-init` implied by `-D warnings` help: declare `a` here @@ -10,60 +12,59 @@ help: declare `a` here LL | let a = "zero"; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:9:5 | LL | let b; - | ^^^^^^ + | ^^^^^^ created here +LL | let c; +LL | b = 1; + | ^^^^^ initialised here | help: declare `b` here | LL | let b = 1; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:10:5 | LL | let c; - | ^^^^^^ + | ^^^^^^ created here +LL | b = 1; +LL | c = 2; + | ^^^^^ initialised here | help: declare `c` here | LL | let c = 2; | ~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:14:5 | LL | let d: usize; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^ created here +LL | d = 1; + | ^^^^^ initialised here | help: declare `d` here | LL | let d: usize = 1; | ~~~~~~~~~~~~ -error: unneeded late initalization +error: unneeded late initialization --> $DIR/needless_late_init_fixable.rs:17:5 | -LL | let mut e; - | ^^^^^^^^^^ +LL | let e; + | ^^^^^^ created here +LL | e = format!("{}", d); + | ^^^^^^^^^^^^^^^^^^^^ initialised here | help: declare `e` here | -LL | let mut e = 1; - | ~~~~~~~~~ - -error: unneeded late initalization - --> $DIR/needless_late_init_fixable.rs:21:5 - | -LL | let h; - | ^^^^^^ - | -help: declare `h` here - | -LL | let h = format!("{}", e); +LL | let e = format!("{}", d); | ~~~~~ -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors diff --git a/tests/ui/needless_match.fixed b/tests/ui/needless_match.fixed index 9ccccaa1725a..b997e5316cf3 100644 --- a/tests/ui/needless_match.fixed +++ b/tests/ui/needless_match.fixed @@ -80,6 +80,18 @@ fn if_let_option() { } else { None }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) }; +} + +fn if_let_option_result() -> Result<(), ()> { + fn f(x: i32) -> Result, ()> { + Ok(Some(x)) + } + // Don't trigger + let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? }; + Ok(()) } fn if_let_result() { diff --git a/tests/ui/needless_match.rs b/tests/ui/needless_match.rs index d210ecff7f16..90482775a1ee 100644 --- a/tests/ui/needless_match.rs +++ b/tests/ui/needless_match.rs @@ -103,6 +103,18 @@ fn if_let_option() { } else { None }; + + // Don't trigger + let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) }; +} + +fn if_let_option_result() -> Result<(), ()> { + fn f(x: i32) -> Result, ()> { + Ok(Some(x)) + } + // Don't trigger + let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? }; + Ok(()) } fn if_let_result() { diff --git a/tests/ui/needless_match.stderr b/tests/ui/needless_match.stderr index 34c5226f0605..2d679631c6f0 100644 --- a/tests/ui/needless_match.stderr +++ b/tests/ui/needless_match.stderr @@ -72,19 +72,19 @@ LL | let _ = if let Some(a) = Some(1) { Some(a) } else { None }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:110:31 + --> $DIR/needless_match.rs:122:31 | LL | let _: Result = if let Err(e) = x { Err(e) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:111:31 + --> $DIR/needless_match.rs:123:31 | LL | let _: Result = if let Ok(val) = x { Ok(val) } else { x }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x` error: this if-let expression is unnecessary - --> $DIR/needless_match.rs:117:21 + --> $DIR/needless_match.rs:129:21 | LL | let _: Simple = if let Simple::A = x { | _____________________^ @@ -97,7 +97,7 @@ LL | | }; | |_____^ help: replace it with: `x` error: this match expression is unnecessary - --> $DIR/needless_match.rs:156:26 + --> $DIR/needless_match.rs:168:26 | LL | let _: Complex = match ce { | __________________________^ diff --git a/tests/ui/needless_option_as_deref.fixed b/tests/ui/needless_option_as_deref.fixed index c09b07db3dca..acd22c6bb433 100644 --- a/tests/ui/needless_option_as_deref.fixed +++ b/tests/ui/needless_option_as_deref.fixed @@ -16,6 +16,20 @@ fn main() { let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + for _ in 0..3 { + let _ = x.as_deref_mut(); + } + + let mut y = 0; + let mut x = Some(&mut y); + let mut closure = || { + let _ = x.as_deref_mut(); + }; + closure(); + closure(); + // #7846 let mut i = 0; let mut opt_vec = vec![Some(&mut i)]; diff --git a/tests/ui/needless_option_as_deref.rs b/tests/ui/needless_option_as_deref.rs index c3ba27ecccf2..61eda5052a21 100644 --- a/tests/ui/needless_option_as_deref.rs +++ b/tests/ui/needless_option_as_deref.rs @@ -16,6 +16,20 @@ fn main() { let _ = Some(Box::new(1)).as_deref(); let _ = Some(Box::new(1)).as_deref_mut(); + let mut y = 0; + let mut x = Some(&mut y); + for _ in 0..3 { + let _ = x.as_deref_mut(); + } + + let mut y = 0; + let mut x = Some(&mut y); + let mut closure = || { + let _ = x.as_deref_mut(); + }; + closure(); + closure(); + // #7846 let mut i = 0; let mut opt_vec = vec![Some(&mut i)]; diff --git a/tests/ui/needless_option_take.fixed b/tests/ui/needless_option_take.fixed new file mode 100644 index 000000000000..29691e81666f --- /dev/null +++ b/tests/ui/needless_option_take.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); +} diff --git a/tests/ui/needless_option_take.rs b/tests/ui/needless_option_take.rs new file mode 100644 index 000000000000..9f4109eb4635 --- /dev/null +++ b/tests/ui/needless_option_take.rs @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref().take(); +} diff --git a/tests/ui/needless_option_take.stderr b/tests/ui/needless_option_take.stderr new file mode 100644 index 000000000000..cb3bf015b369 --- /dev/null +++ b/tests/ui/needless_option_take.stderr @@ -0,0 +1,10 @@ +error: called `Option::take()` on a temporary value + --> $DIR/needless_option_take.rs:14:5 + | +LL | x.as_ref().take(); + | ^^^^^^^^^^^^^^^^^ help: try: `x.as_ref()` + | + = note: `-D clippy::needless-option-take` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/needless_splitn.fixed b/tests/ui/needless_splitn.fixed index f6a4b2f17d33..61f5fc4e679e 100644 --- a/tests/ui/needless_splitn.fixed +++ b/tests/ui/needless_splitn.fixed @@ -24,4 +24,24 @@ fn main() { let _ = str.rsplitn(2, '=').nth(1); let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap(); let (_, _) = str.rsplit('=').next_tuple().unwrap(); + + let _ = str.split('=').next(); + let _ = str.split('=').nth(3); + let _ = str.splitn(5, '=').nth(4); + let _ = str.splitn(5, '=').nth(5); +} + +fn _question_mark(s: &str) -> Option<()> { + let _ = s.split('=').next()?; + let _ = s.split('=').nth(0)?; + let _ = s.rsplit('=').next()?; + let _ = s.rsplit('=').nth(0)?; + + Some(()) +} + +fn _test_msrv() { + #![clippy::msrv = "1.51"] + // `manual_split_once` MSRV shouldn't apply to `needless_splitn` + let _ = "key=value".split('=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.rs b/tests/ui/needless_splitn.rs index 6ba32255bb2d..71d9a7077faa 100644 --- a/tests/ui/needless_splitn.rs +++ b/tests/ui/needless_splitn.rs @@ -24,4 +24,24 @@ fn main() { let _ = str.rsplitn(2, '=').nth(1); let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap(); let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); + + let _ = str.splitn(5, '=').next(); + let _ = str.splitn(5, '=').nth(3); + let _ = str.splitn(5, '=').nth(4); + let _ = str.splitn(5, '=').nth(5); +} + +fn _question_mark(s: &str) -> Option<()> { + let _ = s.splitn(2, '=').next()?; + let _ = s.splitn(2, '=').nth(0)?; + let _ = s.rsplitn(2, '=').next()?; + let _ = s.rsplitn(2, '=').nth(0)?; + + Some(()) +} + +fn _test_msrv() { + #![clippy::msrv = "1.51"] + // `manual_split_once` MSRV shouldn't apply to `needless_splitn` + let _ = "key=value".splitn(2, '=').nth(0).unwrap(); } diff --git a/tests/ui/needless_splitn.stderr b/tests/ui/needless_splitn.stderr index 66de2256554e..f112b29e7f20 100644 --- a/tests/ui/needless_splitn.stderr +++ b/tests/ui/needless_splitn.stderr @@ -36,5 +36,47 @@ error: unnecessary use of `rsplitn` LL | let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap(); | ^^^^^^^^^^^^^^^^^^^ help: try this: `str.rsplit('=')` -error: aborting due to 6 previous errors +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:28:13 + | +LL | let _ = str.splitn(5, '=').next(); + | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:29:13 + | +LL | let _ = str.splitn(5, '=').nth(3); + | ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:35:13 + | +LL | let _ = s.splitn(2, '=').next()?; + | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:36:13 + | +LL | let _ = s.splitn(2, '=').nth(0)?; + | ^^^^^^^^^^^^^^^^ help: try this: `s.split('=')` + +error: unnecessary use of `rsplitn` + --> $DIR/needless_splitn.rs:37:13 + | +LL | let _ = s.rsplitn(2, '=').next()?; + | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + +error: unnecessary use of `rsplitn` + --> $DIR/needless_splitn.rs:38:13 + | +LL | let _ = s.rsplitn(2, '=').nth(0)?; + | ^^^^^^^^^^^^^^^^^ help: try this: `s.rsplit('=')` + +error: unnecessary use of `splitn` + --> $DIR/needless_splitn.rs:46:13 + | +LL | let _ = "key=value".splitn(2, '=').nth(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')` + +error: aborting due to 13 previous errors diff --git a/tests/ui/new_without_default.rs b/tests/ui/new_without_default.rs index e94f99c95f48..538927b18055 100644 --- a/tests/ui/new_without_default.rs +++ b/tests/ui/new_without_default.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, clippy::missing_safety_doc)] +#![allow(dead_code, clippy::missing_safety_doc, clippy::extra_unused_lifetimes)] #![warn(clippy::new_without_default)] pub struct Foo; diff --git a/tests/ui/non_expressive_names.rs b/tests/ui/non_expressive_names.rs index 9937005d68d8..583096ac054a 100644 --- a/tests/ui/non_expressive_names.rs +++ b/tests/ui/non_expressive_names.rs @@ -1,5 +1,5 @@ #![warn(clippy::all)] -#![allow(unused, clippy::println_empty_string, non_snake_case)] +#![allow(unused, clippy::println_empty_string, non_snake_case, clippy::let_unit_value)] #[derive(Clone, Debug)] enum MaybeInst { diff --git a/tests/ui/numbered_fields.fixed b/tests/ui/numbered_fields.fixed index 1da97e968798..3710b3e9c81e 100644 --- a/tests/ui/numbered_fields.fixed +++ b/tests/ui/numbered_fields.fixed @@ -30,4 +30,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } diff --git a/tests/ui/numbered_fields.rs b/tests/ui/numbered_fields.rs index 08ec405a5606..2af84bc0642a 100644 --- a/tests/ui/numbered_fields.rs +++ b/tests/ui/numbered_fields.rs @@ -38,4 +38,9 @@ fn main() { // Ok because it's in macro let _ = tuple_struct_init!(); + + type Alias = TupleStruct; + + // Aliases can't be tuple constructed #8638 + let _ = Alias { 0: 0, 1: 1, 2: 2 }; } diff --git a/tests/ui/option_if_let_else.fixed b/tests/ui/option_if_let_else.fixed index 7790c816481d..e12e13a57f1f 100644 --- a/tests/ui/option_if_let_else.fixed +++ b/tests/ui/option_if_let_else.fixed @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow( + clippy::redundant_closure, + clippy::ref_option_ref, + clippy::equatable_if_let, + clippy::let_unit_value +)] fn bad1(string: Option<&str>) -> (bool, &str) { string.map_or((false, "hello"), |x| (true, x)) diff --git a/tests/ui/option_if_let_else.rs b/tests/ui/option_if_let_else.rs index 3d9f76ee4a6b..b5206fc26a9e 100644 --- a/tests/ui/option_if_let_else.rs +++ b/tests/ui/option_if_let_else.rs @@ -1,6 +1,11 @@ // run-rustfix #![warn(clippy::option_if_let_else)] -#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)] +#![allow( + clippy::redundant_closure, + clippy::ref_option_ref, + clippy::equatable_if_let, + clippy::let_unit_value +)] fn bad1(string: Option<&str>) -> (bool, &str) { if let Some(x) = string { diff --git a/tests/ui/option_if_let_else.stderr b/tests/ui/option_if_let_else.stderr index 546131ceb5b6..40aef977b989 100644 --- a/tests/ui/option_if_let_else.stderr +++ b/tests/ui/option_if_let_else.stderr @@ -1,5 +1,5 @@ error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:6:5 + --> $DIR/option_if_let_else.rs:11:5 | LL | / if let Some(x) = string { LL | | (true, x) @@ -11,19 +11,19 @@ LL | | } = note: `-D clippy::option-if-let-else` implied by `-D warnings` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:24:13 + --> $DIR/option_if_let_else.rs:29:13 | LL | let _ = if let Some(s) = *string { s.len() } else { 0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:25:13 + --> $DIR/option_if_let_else.rs:30:13 | LL | let _ = if let Some(s) = &num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:26:13 + --> $DIR/option_if_let_else.rs:31:13 | LL | let _ = if let Some(s) = &mut num { | _____________^ @@ -43,13 +43,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:32:13 + --> $DIR/option_if_let_else.rs:37:13 | LL | let _ = if let Some(ref s) = num { s } else { &0 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:33:13 + --> $DIR/option_if_let_else.rs:38:13 | LL | let _ = if let Some(mut s) = num { | _____________^ @@ -69,7 +69,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:39:13 + --> $DIR/option_if_let_else.rs:44:13 | LL | let _ = if let Some(ref mut s) = num { | _____________^ @@ -89,7 +89,7 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:48:5 + --> $DIR/option_if_let_else.rs:53:5 | LL | / if let Some(x) = arg { LL | | let y = x * x; @@ -108,7 +108,7 @@ LL + }) | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:61:13 + --> $DIR/option_if_let_else.rs:66:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -120,7 +120,7 @@ LL | | }; | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)` error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:70:13 + --> $DIR/option_if_let_else.rs:75:13 | LL | let _ = if let Some(x) = arg { | _____________^ @@ -143,7 +143,7 @@ LL ~ }, |x| x * x * x * x); | error: use Option::map_or_else instead of an if let/else - --> $DIR/option_if_let_else.rs:103:13 + --> $DIR/option_if_let_else.rs:108:13 | LL | / if let Some(idx) = s.find('.') { LL | | vec![s[..idx].to_string(), s[idx..].to_string()] @@ -153,13 +153,13 @@ LL | | } | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:127:13 + --> $DIR/option_if_let_else.rs:132:13 | LL | let _ = if let Some(x) = optional { x + 2 } else { 5 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:136:13 + --> $DIR/option_if_let_else.rs:141:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ @@ -181,13 +181,13 @@ LL ~ }); | error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:164:13 + --> $DIR/option_if_let_else.rs:169:13 | LL | let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)` error: use Option::map_or instead of an if let/else - --> $DIR/option_if_let_else.rs:168:13 + --> $DIR/option_if_let_else.rs:173:13 | LL | let _ = if let Some(x) = Some(0) { | _____________^ diff --git a/tests/ui/option_take_on_temporary.fixed b/tests/ui/option_take_on_temporary.fixed new file mode 100644 index 000000000000..29691e81666f --- /dev/null +++ b/tests/ui/option_take_on_temporary.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +fn main() { + println!("Testing non erroneous option_take_on_temporary"); + let mut option = Some(1); + let _ = Box::new(move || option.take().unwrap()); + + println!("Testing non erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); + + println!("Testing erroneous option_take_on_temporary"); + let x = Some(3); + x.as_ref(); +} diff --git a/tests/ui/or_then_unwrap.fixed b/tests/ui/or_then_unwrap.fixed index 6e0d5a87f680..844cc4b7a092 100644 --- a/tests/ui/or_then_unwrap.fixed +++ b/tests/ui/or_then_unwrap.fixed @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::or_then_unwrap)] -#![allow(clippy::map_identity)] +#![allow(clippy::map_identity, clippy::let_unit_value)] struct SomeStruct; impl SomeStruct { diff --git a/tests/ui/or_then_unwrap.rs b/tests/ui/or_then_unwrap.rs index e406a71d46d0..1528ef9be964 100644 --- a/tests/ui/or_then_unwrap.rs +++ b/tests/ui/or_then_unwrap.rs @@ -1,7 +1,7 @@ // run-rustfix #![warn(clippy::or_then_unwrap)] -#![allow(clippy::map_identity)] +#![allow(clippy::map_identity, clippy::let_unit_value)] struct SomeStruct; impl SomeStruct { diff --git a/tests/ui/panicking_macros.rs b/tests/ui/panicking_macros.rs index 12a0c776ae2f..041ef17fa683 100644 --- a/tests/ui/panicking_macros.rs +++ b/tests/ui/panicking_macros.rs @@ -1,4 +1,4 @@ -#![allow(clippy::assertions_on_constants, clippy::eq_op)] +#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::let_unit_value)] #![feature(inline_const)] #![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)] diff --git a/tests/ui/pub_use.rs b/tests/ui/pub_use.rs new file mode 100644 index 000000000000..65542bedec7b --- /dev/null +++ b/tests/ui/pub_use.rs @@ -0,0 +1,14 @@ +#![warn(clippy::pub_use)] +#![allow(unused_imports)] +#![no_main] + +pub mod outer { + mod inner { + pub struct Test {} + } + // should be linted + pub use inner::Test; +} + +// should not be linted +use std::fmt; diff --git a/tests/ui/pub_use.stderr b/tests/ui/pub_use.stderr new file mode 100644 index 000000000000..9ab710df818c --- /dev/null +++ b/tests/ui/pub_use.stderr @@ -0,0 +1,11 @@ +error: using `pub use` + --> $DIR/pub_use.rs:10:5 + | +LL | pub use inner::Test; + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::pub-use` implied by `-D warnings` + = help: move the exported item to a public module instead + +error: aborting due to previous error + diff --git a/tests/ui/redundant_pub_crate.fixed b/tests/ui/redundant_pub_crate.fixed index 25f2fd061b88..106947de68c1 100644 --- a/tests/ui/redundant_pub_crate.fixed +++ b/tests/ui/redundant_pub_crate.fixed @@ -104,4 +104,14 @@ mod m4 { pub use m4::*; +mod issue_8732 { + #[allow(unused_macros)] + macro_rules! some_macro { + () => {}; + } + + #[allow(unused_imports)] + pub(crate) use some_macro; // ok: macro exports are exempt +} + fn main() {} diff --git a/tests/ui/redundant_pub_crate.rs b/tests/ui/redundant_pub_crate.rs index 616286b4f39f..f96cfd318438 100644 --- a/tests/ui/redundant_pub_crate.rs +++ b/tests/ui/redundant_pub_crate.rs @@ -104,4 +104,14 @@ mod m4 { pub use m4::*; +mod issue_8732 { + #[allow(unused_macros)] + macro_rules! some_macro { + () => {}; + } + + #[allow(unused_imports)] + pub(crate) use some_macro; // ok: macro exports are exempt +} + fn main() {} diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed index 24a0c8122919..9c4079ad6d30 100644 --- a/tests/ui/rename.fixed +++ b/tests/ui/rename.fixed @@ -1,71 +1,70 @@ -//! Test for Clippy lint renames. +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + // run-rustfix -#![allow(dead_code)] -// allow the new lint name here, to test if the new name works -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::new_without_default)] +#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] -#![allow(clippy::bind_instead_of_map)] -#![allow(clippy::box_collection)] -#![allow(clippy::blocks_in_if_conditions)] -#![allow(clippy::map_unwrap_or)] -#![allow(clippy::unwrap_used)] -#![allow(clippy::expect_used)] +#![allow(clippy::disallowed_methods)] +#![allow(clippy::disallowed_types)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] -#![allow(clippy::invisible_characters)] -#![allow(clippy::single_char_add_str)] #![allow(clippy::match_result_ok)] -#![allow(clippy::disallowed_types)] -#![allow(clippy::disallowed_methods)] +#![allow(clippy::new_without_default)] +#![allow(clippy::bind_instead_of_map)] +#![allow(clippy::expect_used)] +#![allow(clippy::map_unwrap_or)] +#![allow(clippy::unwrap_used)] +#![allow(clippy::needless_borrow)] +#![allow(clippy::single_char_add_str)] +#![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] -// uplifted lints -#![allow(invalid_value)] -#![allow(array_into_iter)] -#![allow(unused_labels)] +#![allow(clippy::invisible_characters)] #![allow(drop_bounds)] -#![allow(temporary_cstring_as_ptr)] -#![allow(non_fmt_panics)] -#![allow(unknown_lints)] +#![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] +#![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] -// warn for the old lint name here, to test if the renaming worked -#![warn(clippy::module_name_repetitions)] -#![warn(clippy::new_without_default)] +#![allow(non_fmt_panics)] +#![allow(temporary_cstring_as_ptr)] +#![allow(unknown_lints)] +#![allow(unused_labels)] +#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::blocks_in_if_conditions)] +#![warn(clippy::box_collection)] #![warn(clippy::redundant_static_lifetimes)] #![warn(clippy::cognitive_complexity)] -#![warn(clippy::bind_instead_of_map)] -#![warn(clippy::box_collection)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::blocks_in_if_conditions)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::map_unwrap_or)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::unwrap_used)] -#![warn(clippy::expect_used)] -#![warn(clippy::expect_used)] +#![warn(clippy::disallowed_methods)] +#![warn(clippy::disallowed_types)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::for_loops_over_fallibles)] #![warn(clippy::useless_conversion)] -#![warn(clippy::invisible_characters)] -#![warn(clippy::single_char_add_str)] #![warn(clippy::match_result_ok)] -#![warn(clippy::disallowed_types)] -#![warn(clippy::disallowed_methods)] +#![warn(clippy::new_without_default)] +#![warn(clippy::bind_instead_of_map)] +#![warn(clippy::expect_used)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::unwrap_used)] #![warn(clippy::needless_borrow)] +#![warn(clippy::expect_used)] +#![warn(clippy::map_unwrap_or)] +#![warn(clippy::unwrap_used)] +#![warn(clippy::single_char_add_str)] +#![warn(clippy::module_name_repetitions)] #![warn(clippy::recursive_format_impl)] -// uplifted lints -#![warn(invalid_value)] -#![warn(array_into_iter)] -#![warn(unused_labels)] +#![warn(clippy::invisible_characters)] #![warn(drop_bounds)] -#![warn(temporary_cstring_as_ptr)] -#![warn(non_fmt_panics)] -#![warn(unknown_lints)] +#![warn(array_into_iter)] #![warn(invalid_atomic_ordering)] +#![warn(invalid_value)] #![warn(enum_intrinsics_non_enums)] +#![warn(non_fmt_panics)] +#![warn(temporary_cstring_as_ptr)] +#![warn(unknown_lints)] +#![warn(unused_labels)] fn main() {} diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs index ea64234c680d..e83e66b7fbd4 100644 --- a/tests/ui/rename.rs +++ b/tests/ui/rename.rs @@ -1,71 +1,70 @@ -//! Test for Clippy lint renames. +// This file was generated by `cargo dev update_lints`. +// Use that command to update this file and do not edit by hand. +// Manual edits will be overwritten. + // run-rustfix -#![allow(dead_code)] -// allow the new lint name here, to test if the new name works -#![allow(clippy::module_name_repetitions)] -#![allow(clippy::new_without_default)] +#![allow(clippy::blocks_in_if_conditions)] +#![allow(clippy::box_collection)] #![allow(clippy::redundant_static_lifetimes)] #![allow(clippy::cognitive_complexity)] -#![allow(clippy::bind_instead_of_map)] -#![allow(clippy::box_collection)] -#![allow(clippy::blocks_in_if_conditions)] -#![allow(clippy::map_unwrap_or)] -#![allow(clippy::unwrap_used)] -#![allow(clippy::expect_used)] +#![allow(clippy::disallowed_methods)] +#![allow(clippy::disallowed_types)] #![allow(clippy::for_loops_over_fallibles)] #![allow(clippy::useless_conversion)] -#![allow(clippy::invisible_characters)] -#![allow(clippy::single_char_add_str)] #![allow(clippy::match_result_ok)] -#![allow(clippy::disallowed_types)] -#![allow(clippy::disallowed_methods)] +#![allow(clippy::new_without_default)] +#![allow(clippy::bind_instead_of_map)] +#![allow(clippy::expect_used)] +#![allow(clippy::map_unwrap_or)] +#![allow(clippy::unwrap_used)] +#![allow(clippy::needless_borrow)] +#![allow(clippy::single_char_add_str)] +#![allow(clippy::module_name_repetitions)] #![allow(clippy::recursive_format_impl)] -// uplifted lints -#![allow(invalid_value)] -#![allow(array_into_iter)] -#![allow(unused_labels)] +#![allow(clippy::invisible_characters)] #![allow(drop_bounds)] -#![allow(temporary_cstring_as_ptr)] -#![allow(non_fmt_panics)] -#![allow(unknown_lints)] +#![allow(array_into_iter)] #![allow(invalid_atomic_ordering)] +#![allow(invalid_value)] #![allow(enum_intrinsics_non_enums)] -// warn for the old lint name here, to test if the renaming worked -#![warn(clippy::stutter)] -#![warn(clippy::new_without_default_derive)] -#![warn(clippy::const_static_lifetime)] -#![warn(clippy::cyclomatic_complexity)] -#![warn(clippy::option_and_then_some)] -#![warn(clippy::box_vec)] +#![allow(non_fmt_panics)] +#![allow(temporary_cstring_as_ptr)] +#![allow(unknown_lints)] +#![allow(unused_labels)] #![warn(clippy::block_in_if_condition_expr)] #![warn(clippy::block_in_if_condition_stmt)] -#![warn(clippy::option_map_unwrap_or)] -#![warn(clippy::option_map_unwrap_or_else)] -#![warn(clippy::result_map_unwrap_or_else)] -#![warn(clippy::option_unwrap_used)] -#![warn(clippy::result_unwrap_used)] -#![warn(clippy::option_expect_used)] -#![warn(clippy::result_expect_used)] +#![warn(clippy::box_vec)] +#![warn(clippy::const_static_lifetime)] +#![warn(clippy::cyclomatic_complexity)] +#![warn(clippy::disallowed_method)] +#![warn(clippy::disallowed_type)] #![warn(clippy::for_loop_over_option)] #![warn(clippy::for_loop_over_result)] #![warn(clippy::identity_conversion)] -#![warn(clippy::zero_width_space)] -#![warn(clippy::single_char_push_str)] #![warn(clippy::if_let_some_result)] -#![warn(clippy::disallowed_type)] -#![warn(clippy::disallowed_method)] +#![warn(clippy::new_without_default_derive)] +#![warn(clippy::option_and_then_some)] +#![warn(clippy::option_expect_used)] +#![warn(clippy::option_map_unwrap_or)] +#![warn(clippy::option_map_unwrap_or_else)] +#![warn(clippy::option_unwrap_used)] #![warn(clippy::ref_in_deref)] +#![warn(clippy::result_expect_used)] +#![warn(clippy::result_map_unwrap_or_else)] +#![warn(clippy::result_unwrap_used)] +#![warn(clippy::single_char_push_str)] +#![warn(clippy::stutter)] #![warn(clippy::to_string_in_display)] -// uplifted lints -#![warn(clippy::invalid_ref)] -#![warn(clippy::into_iter_on_array)] -#![warn(clippy::unused_label)] +#![warn(clippy::zero_width_space)] #![warn(clippy::drop_bounds)] -#![warn(clippy::temporary_cstring_as_ptr)] -#![warn(clippy::panic_params)] -#![warn(clippy::unknown_clippy_lints)] +#![warn(clippy::into_iter_on_array)] #![warn(clippy::invalid_atomic_ordering)] +#![warn(clippy::invalid_ref)] #![warn(clippy::mem_discriminant_non_enum)] +#![warn(clippy::panic_params)] +#![warn(clippy::temporary_cstring_as_ptr)] +#![warn(clippy::unknown_clippy_lints)] +#![warn(clippy::unused_label)] fn main() {} diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr index 8b132a783847..f811b10d0171 100644 --- a/tests/ui/rename.stderr +++ b/tests/ui/rename.stderr @@ -1,208 +1,208 @@ -error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` +error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` --> $DIR/rename.rs:35:9 | -LL | #![warn(clippy::stutter)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` +LL | #![warn(clippy::block_in_if_condition_expr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` | = note: `-D renamed-and-removed-lints` implied by `-D warnings` -error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` +error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` --> $DIR/rename.rs:36:9 | -LL | #![warn(clippy::new_without_default_derive)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` +LL | #![warn(clippy::block_in_if_condition_stmt)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` + +error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` + --> $DIR/rename.rs:37:9 + | +LL | #![warn(clippy::box_vec)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes` - --> $DIR/rename.rs:37:9 + --> $DIR/rename.rs:38:9 | LL | #![warn(clippy::const_static_lifetime)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes` error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity` - --> $DIR/rename.rs:38:9 + --> $DIR/rename.rs:39:9 | LL | #![warn(clippy::cyclomatic_complexity)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity` +error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` + --> $DIR/rename.rs:40:9 + | +LL | #![warn(clippy::disallowed_method)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` + +error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` + --> $DIR/rename.rs:41:9 + | +LL | #![warn(clippy::disallowed_type)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` + +error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` + --> $DIR/rename.rs:42:9 + | +LL | #![warn(clippy::for_loop_over_option)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + +error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` + --> $DIR/rename.rs:43:9 + | +LL | #![warn(clippy::for_loop_over_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` + +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` + --> $DIR/rename.rs:44:9 + | +LL | #![warn(clippy::identity_conversion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` + +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` + --> $DIR/rename.rs:45:9 + | +LL | #![warn(clippy::if_let_some_result)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` + +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default` + --> $DIR/rename.rs:46:9 + | +LL | #![warn(clippy::new_without_default_derive)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default` + error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map` - --> $DIR/rename.rs:39:9 + --> $DIR/rename.rs:47:9 | LL | #![warn(clippy::option_and_then_some)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map` -error: lint `clippy::box_vec` has been renamed to `clippy::box_collection` - --> $DIR/rename.rs:40:9 - | -LL | #![warn(clippy::box_vec)] - | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection` - -error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:41:9 - | -LL | #![warn(clippy::block_in_if_condition_expr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` - -error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions` - --> $DIR/rename.rs:42:9 - | -LL | #![warn(clippy::block_in_if_condition_stmt)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions` - -error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:43:9 - | -LL | #![warn(clippy::option_map_unwrap_or)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:44:9 - | -LL | #![warn(clippy::option_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` - --> $DIR/rename.rs:45:9 - | -LL | #![warn(clippy::result_map_unwrap_or_else)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` - -error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:46:9 - | -LL | #![warn(clippy::option_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` - -error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` - --> $DIR/rename.rs:47:9 - | -LL | #![warn(clippy::result_unwrap_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` - error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used` --> $DIR/rename.rs:48:9 | LL | #![warn(clippy::option_expect_used)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` -error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:49:9 | -LL | #![warn(clippy::result_expect_used)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` +LL | #![warn(clippy::option_map_unwrap_or)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` --> $DIR/rename.rs:50:9 | -LL | #![warn(clippy::for_loop_over_option)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` +LL | #![warn(clippy::option_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` -error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles` +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used` --> $DIR/rename.rs:51:9 | -LL | #![warn(clippy::for_loop_over_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles` - -error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion` - --> $DIR/rename.rs:52:9 - | -LL | #![warn(clippy::identity_conversion)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion` - -error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` - --> $DIR/rename.rs:53:9 - | -LL | #![warn(clippy::zero_width_space)] - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` - -error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` - --> $DIR/rename.rs:54:9 - | -LL | #![warn(clippy::single_char_push_str)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` - -error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok` - --> $DIR/rename.rs:55:9 - | -LL | #![warn(clippy::if_let_some_result)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok` - -error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types` - --> $DIR/rename.rs:56:9 - | -LL | #![warn(clippy::disallowed_type)] - | ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types` - -error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods` - --> $DIR/rename.rs:57:9 - | -LL | #![warn(clippy::disallowed_method)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods` +LL | #![warn(clippy::option_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow` - --> $DIR/rename.rs:58:9 + --> $DIR/rename.rs:52:9 | LL | #![warn(clippy::ref_in_deref)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow` +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used` + --> $DIR/rename.rs:53:9 + | +LL | #![warn(clippy::result_expect_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used` + +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or` + --> $DIR/rename.rs:54:9 + | +LL | #![warn(clippy::result_map_unwrap_or_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or` + +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used` + --> $DIR/rename.rs:55:9 + | +LL | #![warn(clippy::result_unwrap_used)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used` + +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str` + --> $DIR/rename.rs:56:9 + | +LL | #![warn(clippy::single_char_push_str)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str` + +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions` + --> $DIR/rename.rs:57:9 + | +LL | #![warn(clippy::stutter)] + | ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions` + error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl` - --> $DIR/rename.rs:59:9 + --> $DIR/rename.rs:58:9 | LL | #![warn(clippy::to_string_in_display)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl` -error: lint `clippy::invalid_ref` has been renamed to `invalid_value` - --> $DIR/rename.rs:61:9 +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters` + --> $DIR/rename.rs:59:9 | -LL | #![warn(clippy::invalid_ref)] - | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` - -error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` - --> $DIR/rename.rs:62:9 - | -LL | #![warn(clippy::into_iter_on_array)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` - -error: lint `clippy::unused_label` has been renamed to `unused_labels` - --> $DIR/rename.rs:63:9 - | -LL | #![warn(clippy::unused_label)] - | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` +LL | #![warn(clippy::zero_width_space)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters` error: lint `clippy::drop_bounds` has been renamed to `drop_bounds` - --> $DIR/rename.rs:64:9 + --> $DIR/rename.rs:60:9 | LL | #![warn(clippy::drop_bounds)] | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds` -error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` - --> $DIR/rename.rs:65:9 +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter` + --> $DIR/rename.rs:61:9 | -LL | #![warn(clippy::temporary_cstring_as_ptr)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` +LL | #![warn(clippy::into_iter_on_array)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter` + +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` + --> $DIR/rename.rs:62:9 + | +LL | #![warn(clippy::invalid_atomic_ordering)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` + +error: lint `clippy::invalid_ref` has been renamed to `invalid_value` + --> $DIR/rename.rs:63:9 + | +LL | #![warn(clippy::invalid_ref)] + | ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value` + +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` + --> $DIR/rename.rs:64:9 + | +LL | #![warn(clippy::mem_discriminant_non_enum)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` error: lint `clippy::panic_params` has been renamed to `non_fmt_panics` - --> $DIR/rename.rs:66:9 + --> $DIR/rename.rs:65:9 | LL | #![warn(clippy::panic_params)] | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics` +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr` + --> $DIR/rename.rs:66:9 + | +LL | #![warn(clippy::temporary_cstring_as_ptr)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr` + error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints` --> $DIR/rename.rs:67:9 | LL | #![warn(clippy::unknown_clippy_lints)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints` -error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering` +error: lint `clippy::unused_label` has been renamed to `unused_labels` --> $DIR/rename.rs:68:9 | -LL | #![warn(clippy::invalid_atomic_ordering)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering` - -error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums` - --> $DIR/rename.rs:69:9 - | -LL | #![warn(clippy::mem_discriminant_non_enum)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums` +LL | #![warn(clippy::unused_label)] + | ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels` error: aborting due to 34 previous errors diff --git a/tests/ui/rest_pat_in_fully_bound_structs.rs b/tests/ui/rest_pat_in_fully_bound_structs.rs index 38fc9969804f..086331af6b56 100644 --- a/tests/ui/rest_pat_in_fully_bound_structs.rs +++ b/tests/ui/rest_pat_in_fully_bound_structs.rs @@ -39,4 +39,19 @@ fn main() { // No lint foo!(a_struct); + + #[non_exhaustive] + struct B { + a: u32, + b: u32, + c: u64, + } + + let b_struct = B { a: 5, b: 42, c: 342 }; + + match b_struct { + B { a: 5, b: 42, .. } => {}, + B { a: 0, b: 0, c: 128, .. } => {}, // No Lint + _ => {}, + } } diff --git a/tests/ui/same_functions_in_if_condition.rs b/tests/ui/same_functions_in_if_condition.rs index 7f28f0257904..3d2295912c9f 100644 --- a/tests/ui/same_functions_in_if_condition.rs +++ b/tests/ui/same_functions_in_if_condition.rs @@ -1,3 +1,5 @@ +#![feature(adt_const_params)] +#![allow(incomplete_features)] #![warn(clippy::same_functions_in_if_condition)] #![allow(clippy::ifs_same_cond)] // This warning is different from `ifs_same_cond`. #![allow(clippy::if_same_then_else, clippy::comparison_chain)] // all empty blocks @@ -87,4 +89,21 @@ fn main() { "linux" }; println!("{}", os); + + #[derive(PartialEq, Eq)] + enum E { + A, + B, + } + fn generic() -> bool { + match P { + E::A => true, + E::B => false, + } + } + if generic::<{ E::A }>() { + println!("A"); + } else if generic::<{ E::B }>() { + println!("B"); + } } diff --git a/tests/ui/same_functions_in_if_condition.stderr b/tests/ui/same_functions_in_if_condition.stderr index 363a03846d23..71e82910ef75 100644 --- a/tests/ui/same_functions_in_if_condition.stderr +++ b/tests/ui/same_functions_in_if_condition.stderr @@ -1,72 +1,72 @@ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:29:15 + --> $DIR/same_functions_in_if_condition.rs:31:15 | LL | } else if function() { | ^^^^^^^^^^ | = note: `-D clippy::same-functions-in-if-condition` implied by `-D warnings` note: same as this - --> $DIR/same_functions_in_if_condition.rs:28:8 + --> $DIR/same_functions_in_if_condition.rs:30:8 | LL | if function() { | ^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:34:15 + --> $DIR/same_functions_in_if_condition.rs:36:15 | LL | } else if fn_arg(a) { | ^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:33:8 + --> $DIR/same_functions_in_if_condition.rs:35:8 | LL | if fn_arg(a) { | ^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:39:15 + --> $DIR/same_functions_in_if_condition.rs:41:15 | LL | } else if obj.method() { | ^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:38:8 + --> $DIR/same_functions_in_if_condition.rs:40:8 | LL | if obj.method() { | ^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:44:15 + --> $DIR/same_functions_in_if_condition.rs:46:15 | LL | } else if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:43:8 + --> $DIR/same_functions_in_if_condition.rs:45:8 | LL | if obj.method_arg(a) { | ^^^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:51:15 + --> $DIR/same_functions_in_if_condition.rs:53:15 | LL | } else if v.pop() == None { | ^^^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:49:8 + --> $DIR/same_functions_in_if_condition.rs:51:8 | LL | if v.pop() == None { | ^^^^^^^^^^^^^^^ error: this `if` has the same function call as a previous `if` - --> $DIR/same_functions_in_if_condition.rs:56:15 + --> $DIR/same_functions_in_if_condition.rs:58:15 | LL | } else if v.len() == 42 { | ^^^^^^^^^^^^^ | note: same as this - --> $DIR/same_functions_in_if_condition.rs:54:8 + --> $DIR/same_functions_in_if_condition.rs:56:8 | LL | if v.len() == 42 { | ^^^^^^^^^^^^^ diff --git a/tests/ui/shadow.rs b/tests/ui/shadow.rs index 0321f8c4cdf8..a394ef8f25c6 100644 --- a/tests/ui/shadow.rs +++ b/tests/ui/shadow.rs @@ -1,4 +1,5 @@ #![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)] +#![allow(clippy::let_unit_value)] fn shadow_same() { let x = 1; diff --git a/tests/ui/shadow.stderr b/tests/ui/shadow.stderr index f8b9221d5558..3bd41d062604 100644 --- a/tests/ui/shadow.stderr +++ b/tests/ui/shadow.stderr @@ -1,266 +1,266 @@ error: `x` is shadowed by itself in `x` - --> $DIR/shadow.rs:5:9 + --> $DIR/shadow.rs:6:9 | LL | let x = x; | ^ | = note: `-D clippy::shadow-same` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:4:9 + --> $DIR/shadow.rs:5:9 | LL | let x = 1; | ^ error: `mut x` is shadowed by itself in `&x` - --> $DIR/shadow.rs:6:13 + --> $DIR/shadow.rs:7:13 | LL | let mut x = &x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:5:9 + --> $DIR/shadow.rs:6:9 | LL | let x = x; | ^ error: `x` is shadowed by itself in `&mut x` - --> $DIR/shadow.rs:7:9 + --> $DIR/shadow.rs:8:9 | LL | let x = &mut x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:6:9 + --> $DIR/shadow.rs:7:9 | LL | let mut x = &x; | ^^^^^ error: `x` is shadowed by itself in `*x` - --> $DIR/shadow.rs:8:9 + --> $DIR/shadow.rs:9:9 | LL | let x = *x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:7:9 + --> $DIR/shadow.rs:8:9 | LL | let x = &mut x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:13:9 + --> $DIR/shadow.rs:14:9 | LL | let x = x.0; | ^ | = note: `-D clippy::shadow-reuse` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:12:9 + --> $DIR/shadow.rs:13:9 | LL | let x = ([[0]], ()); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:14:9 + --> $DIR/shadow.rs:15:9 | LL | let x = x[0]; | ^ | note: previous binding is here - --> $DIR/shadow.rs:13:9 + --> $DIR/shadow.rs:14:9 | LL | let x = x.0; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:15:10 + --> $DIR/shadow.rs:16:10 | LL | let [x] = x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:14:9 + --> $DIR/shadow.rs:15:9 | LL | let x = x[0]; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:16:9 + --> $DIR/shadow.rs:17:9 | LL | let x = Some(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:15:10 + --> $DIR/shadow.rs:16:10 | LL | let [x] = x; | ^ error: `x` is shadowed - --> $DIR/shadow.rs:17:9 + --> $DIR/shadow.rs:18:9 | LL | let x = foo(x); | ^ | note: previous binding is here - --> $DIR/shadow.rs:16:9 + --> $DIR/shadow.rs:17:9 | LL | let x = Some(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:19:9 | LL | let x = || x; | ^ | note: previous binding is here - --> $DIR/shadow.rs:17:9 + --> $DIR/shadow.rs:18:9 | LL | let x = foo(x); | ^ error: `x` is shadowed - --> $DIR/shadow.rs:19:9 + --> $DIR/shadow.rs:20:9 | LL | let x = Some(1).map(|_| x)?; | ^ | note: previous binding is here - --> $DIR/shadow.rs:18:9 + --> $DIR/shadow.rs:19:9 | LL | let x = || x; | ^ error: `y` is shadowed - --> $DIR/shadow.rs:21:9 + --> $DIR/shadow.rs:22:9 | LL | let y = match y { | ^ | note: previous binding is here - --> $DIR/shadow.rs:20:9 + --> $DIR/shadow.rs:21:9 | LL | let y = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:30:9 + --> $DIR/shadow.rs:31:9 | LL | let x = 2; | ^ | = note: `-D clippy::shadow-unrelated` implied by `-D warnings` note: previous binding is here - --> $DIR/shadow.rs:29:9 + --> $DIR/shadow.rs:30:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:35:13 + --> $DIR/shadow.rs:36:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:34:10 + --> $DIR/shadow.rs:35:10 | LL | fn f(x: u32) { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:40:14 + --> $DIR/shadow.rs:41:14 | LL | Some(x) => { | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:41:17 + --> $DIR/shadow.rs:42:17 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:40:14 + --> $DIR/shadow.rs:41:14 | LL | Some(x) => { | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:45:17 + --> $DIR/shadow.rs:46:17 | LL | if let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:46:20 + --> $DIR/shadow.rs:47:20 | LL | while let Some(x) = Some(1) {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:47:15 + --> $DIR/shadow.rs:48:15 | LL | let _ = |[x]: [u32; 1]| { | ^ | note: previous binding is here - --> $DIR/shadow.rs:37:9 + --> $DIR/shadow.rs:38:9 | LL | let x = 1; | ^ error: `x` shadows a previous, unrelated binding - --> $DIR/shadow.rs:48:13 + --> $DIR/shadow.rs:49:13 | LL | let x = 1; | ^ | note: previous binding is here - --> $DIR/shadow.rs:47:15 + --> $DIR/shadow.rs:48:15 | LL | let _ = |[x]: [u32; 1]| { | ^ error: `y` is shadowed - --> $DIR/shadow.rs:51:17 + --> $DIR/shadow.rs:52:17 | LL | if let Some(y) = y {} | ^ | note: previous binding is here - --> $DIR/shadow.rs:50:9 + --> $DIR/shadow.rs:51:9 | LL | let y = Some(1); | ^ error: `_b` shadows a previous, unrelated binding - --> $DIR/shadow.rs:87:9 + --> $DIR/shadow.rs:88:9 | LL | let _b = _a; | ^^ | note: previous binding is here - --> $DIR/shadow.rs:86:28 + --> $DIR/shadow.rs:87:28 | LL | pub async fn foo2(_a: i32, _b: i64) { | ^^ diff --git a/tests/ui/similar_names.rs b/tests/ui/similar_names.rs index 76f6ce9ee6b4..c21225d153bd 100644 --- a/tests/ui/similar_names.rs +++ b/tests/ui/similar_names.rs @@ -3,7 +3,8 @@ unused, clippy::println_empty_string, clippy::empty_loop, - clippy::diverging_sub_expression + clippy::diverging_sub_expression, + clippy::let_unit_value )] struct Foo { diff --git a/tests/ui/similar_names.stderr b/tests/ui/similar_names.stderr index faf572b0c6bc..6e7726938973 100644 --- a/tests/ui/similar_names.stderr +++ b/tests/ui/similar_names.stderr @@ -1,84 +1,84 @@ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:20:9 + --> $DIR/similar_names.rs:21:9 | LL | let bpple: i32; | ^^^^^ | = note: `-D clippy::similar-names` implied by `-D warnings` note: existing binding defined here - --> $DIR/similar_names.rs:18:9 + --> $DIR/similar_names.rs:19:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:22:9 + --> $DIR/similar_names.rs:23:9 | LL | let cpple: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:18:9 + --> $DIR/similar_names.rs:19:9 | LL | let apple: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:46:9 + --> $DIR/similar_names.rs:47:9 | LL | let bluby: i32; | ^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:45:9 + --> $DIR/similar_names.rs:46:9 | LL | let blubx: i32; | ^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:50:9 + --> $DIR/similar_names.rs:51:9 | LL | let coke: i32; | ^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:48:9 + --> $DIR/similar_names.rs:49:9 | LL | let cake: i32; | ^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:68:9 + --> $DIR/similar_names.rs:69:9 | LL | let xyzeabc: i32; | ^^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:66:9 + --> $DIR/similar_names.rs:67:9 | LL | let xyz1abc: i32; | ^^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:72:9 + --> $DIR/similar_names.rs:73:9 | LL | let parsee: i32; | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:70:9 + --> $DIR/similar_names.rs:71:9 | LL | let parser: i32; | ^^^^^^ error: binding's name is too similar to existing binding - --> $DIR/similar_names.rs:93:16 + --> $DIR/similar_names.rs:94:16 | LL | bpple: sprang, | ^^^^^^ | note: existing binding defined here - --> $DIR/similar_names.rs:92:16 + --> $DIR/similar_names.rs:93:16 | LL | apple: spring, | ^^^^^^ diff --git a/tests/ui/single_char_lifetime_names.rs b/tests/ui/single_char_lifetime_names.rs index 261d8bc7260c..69c5b236f7cf 100644 --- a/tests/ui/single_char_lifetime_names.rs +++ b/tests/ui/single_char_lifetime_names.rs @@ -1,4 +1,5 @@ #![warn(clippy::single_char_lifetime_names)] +#![allow(clippy::let_unit_value)] // Lifetimes should only be linted when they're introduced struct DiagnosticCtx<'a, 'b> diff --git a/tests/ui/single_char_lifetime_names.stderr b/tests/ui/single_char_lifetime_names.stderr index 013b64f46a8c..1438b3999dba 100644 --- a/tests/ui/single_char_lifetime_names.stderr +++ b/tests/ui/single_char_lifetime_names.stderr @@ -1,5 +1,5 @@ error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:4:22 + --> $DIR/single_char_lifetime_names.rs:5:22 | LL | struct DiagnosticCtx<'a, 'b> | ^^ @@ -8,7 +8,7 @@ LL | struct DiagnosticCtx<'a, 'b> = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:4:26 + --> $DIR/single_char_lifetime_names.rs:5:26 | LL | struct DiagnosticCtx<'a, 'b> | ^^ @@ -16,7 +16,7 @@ LL | struct DiagnosticCtx<'a, 'b> = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:13:6 + --> $DIR/single_char_lifetime_names.rs:14:6 | LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { | ^^ @@ -24,7 +24,7 @@ LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:13:10 + --> $DIR/single_char_lifetime_names.rs:14:10 | LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { | ^^ @@ -32,7 +32,7 @@ LL | impl<'a, 'b> DiagnosticCtx<'a, 'b> { = help: use a more informative name error: single-character lifetime names are likely uninformative - --> $DIR/single_char_lifetime_names.rs:33:15 + --> $DIR/single_char_lifetime_names.rs:34:15 | LL | fn split_once<'a>(base: &'a str, other: &'_ str) -> (&'a str, Option<&'a str>) { | ^^ diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs index b624a41a29b2..82387f3d80b7 100644 --- a/tests/ui/single_match_else.rs +++ b/tests/ui/single_match_else.rs @@ -1,7 +1,12 @@ +// aux-build: proc_macro_with_span.rs + #![warn(clippy::single_match_else)] #![allow(clippy::needless_return)] #![allow(clippy::no_effect)] +extern crate proc_macro_with_span; +use proc_macro_with_span::with_span; + enum ExprNode { ExprAddrOf, Butterflies, @@ -11,13 +16,22 @@ enum ExprNode { static NODE: ExprNode = ExprNode::Unicorns; fn unwrap_addr() -> Option<&'static ExprNode> { - match ExprNode::Butterflies { + let _ = match ExprNode::Butterflies { ExprNode::ExprAddrOf => Some(&NODE), _ => { let x = 5; None }, - } + }; + + // Don't lint + with_span!(span match ExprNode::Butterflies { + ExprNode::ExprAddrOf => Some(&NODE), + _ => { + let x = 5; + None + }, + }) } macro_rules! unwrap_addr { diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr index 21ea704b62ab..7756c6f204e6 100644 --- a/tests/ui/single_match_else.stderr +++ b/tests/ui/single_match_else.stderr @@ -1,22 +1,23 @@ error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let` - --> $DIR/single_match_else.rs:14:5 + --> $DIR/single_match_else.rs:19:13 | -LL | / match ExprNode::Butterflies { +LL | let _ = match ExprNode::Butterflies { + | _____________^ LL | | ExprNode::ExprAddrOf => Some(&NODE), LL | | _ => { LL | | let x = 5; LL | | None LL | | }, -LL | | } +LL | | }; | |_____^ | = note: `-D clippy::single-match-else` implied by `-D warnings` help: try this | -LL ~ if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { +LL ~ let _ = if let ExprNode::ExprAddrOf = ExprNode::Butterflies { Some(&NODE) } else { LL + let x = 5; LL + None -LL + } +LL ~ }; | error: aborting due to previous error diff --git a/tests/ui/stable_sort_primitive.stderr b/tests/ui/stable_sort_primitive.stderr index b8d22ed25046..c35e0c22ae89 100644 --- a/tests/ui/stable_sort_primitive.stderr +++ b/tests/ui/stable_sort_primitive.stderr @@ -5,7 +5,7 @@ LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | = note: `-D clippy::stable-sort-primitive` implied by `-D warnings` - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `bool` --> $DIR/stable_sort_primitive.rs:9:5 @@ -13,7 +13,7 @@ error: used `sort` on primitive type `bool` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `char` --> $DIR/stable_sort_primitive.rs:11:5 @@ -21,7 +21,7 @@ error: used `sort` on primitive type `char` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `str` --> $DIR/stable_sort_primitive.rs:13:5 @@ -29,7 +29,7 @@ error: used `sort` on primitive type `str` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `tuple` --> $DIR/stable_sort_primitive.rs:15:5 @@ -37,7 +37,7 @@ error: used `sort` on primitive type `tuple` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `array` --> $DIR/stable_sort_primitive.rs:17:5 @@ -45,7 +45,7 @@ error: used `sort` on primitive type `array` LL | vec.sort(); | ^^^^^^^^^^ help: try: `vec.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: used `sort` on primitive type `i32` --> $DIR/stable_sort_primitive.rs:19:5 @@ -53,7 +53,7 @@ error: used `sort` on primitive type `i32` LL | arr.sort(); | ^^^^^^^^^^ help: try: `arr.sort_unstable()` | - = note: an unstable sort would perform faster without any observable difference for this data type + = note: an unstable sort typically performs faster without any observable difference for this data type error: aborting due to 7 previous errors diff --git a/tests/ui/suspicious_else_formatting.rs b/tests/ui/suspicious_else_formatting.rs index fcd827a91c7f..21753e5dc6a4 100644 --- a/tests/ui/suspicious_else_formatting.rs +++ b/tests/ui/suspicious_else_formatting.rs @@ -1,7 +1,7 @@ // aux-build:proc_macro_suspicious_else_formatting.rs #![warn(clippy::suspicious_else_formatting)] -#![allow(clippy::if_same_then_else)] +#![allow(clippy::if_same_then_else, clippy::let_unit_value)] extern crate proc_macro_suspicious_else_formatting; use proc_macro_suspicious_else_formatting::DeriveBadSpan; diff --git a/tests/ui/to_digit_is_some.fixed b/tests/ui/to_digit_is_some.fixed index 19184df0becb..3c5e96427146 100644 --- a/tests/ui/to_digit_is_some.fixed +++ b/tests/ui/to_digit_is_some.fixed @@ -6,6 +6,6 @@ fn main() { let c = 'x'; let d = &c; - let _ = d.is_digit(10); - let _ = char::is_digit(c, 10); + let _ = d.is_digit(8); + let _ = char::is_digit(c, 8); } diff --git a/tests/ui/to_digit_is_some.rs b/tests/ui/to_digit_is_some.rs index 45a6728ebf57..4f247c06ceed 100644 --- a/tests/ui/to_digit_is_some.rs +++ b/tests/ui/to_digit_is_some.rs @@ -6,6 +6,6 @@ fn main() { let c = 'x'; let d = &c; - let _ = d.to_digit(10).is_some(); - let _ = char::to_digit(c, 10).is_some(); + let _ = d.to_digit(8).is_some(); + let _ = char::to_digit(c, 8).is_some(); } diff --git a/tests/ui/to_digit_is_some.stderr b/tests/ui/to_digit_is_some.stderr index 177d3ccd3e23..10a1b393a390 100644 --- a/tests/ui/to_digit_is_some.stderr +++ b/tests/ui/to_digit_is_some.stderr @@ -1,16 +1,16 @@ error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:9:13 | -LL | let _ = d.to_digit(10).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(10)` +LL | let _ = d.to_digit(8).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `d.is_digit(8)` | = note: `-D clippy::to-digit-is-some` implied by `-D warnings` error: use of `.to_digit(..).is_some()` --> $DIR/to_digit_is_some.rs:10:13 | -LL | let _ = char::to_digit(c, 10).is_some(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 10)` +LL | let _ = char::to_digit(c, 8).is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `char::is_digit(c, 8)` error: aborting due to 2 previous errors diff --git a/tests/ui/trait_duplication_in_bounds.rs b/tests/ui/trait_duplication_in_bounds.rs index f5ca91143af2..a21d4c5d637d 100644 --- a/tests/ui/trait_duplication_in_bounds.rs +++ b/tests/ui/trait_duplication_in_bounds.rs @@ -95,4 +95,7 @@ trait FooIter: Iterator { } } +// This should not lint +fn impl_trait(_: impl AsRef, _: impl AsRef) {} + fn main() {} diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index 6f8c8e47dfbf..d0a4cfb88370 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -67,5 +67,13 @@ LL | Self: Iterator, | = help: consider removing this trait bound -error: aborting due to 8 previous errors +error: this trait bound is already specified in the where clause + --> $DIR/trait_duplication_in_bounds.rs:99:23 + | +LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} + | ^^^^^^^^^^ + | + = help: consider removing this trait bound + +error: aborting due to 9 previous errors diff --git a/tests/ui/transmute_collection.rs b/tests/ui/transmute_collection.rs index cd5a7127791a..5a431bee04a4 100644 --- a/tests/ui/transmute_collection.rs +++ b/tests/ui/transmute_collection.rs @@ -1,7 +1,7 @@ #![warn(clippy::unsound_collection_transmute)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; -use std::mem::transmute; +use std::mem::{transmute, MaybeUninit}; fn main() { unsafe { @@ -43,5 +43,8 @@ fn main() { // wrong layout let _ = transmute::<_, HashMap>(HashMap::::new()); let _ = transmute::<_, HashMap>(HashMap::<[u8; 4], u32>::new()); + + let _ = transmute::<_, Vec>(Vec::>::new()); + let _ = transmute::<_, Vec<*mut u32>>(Vec::>::new()); } } diff --git a/tests/ui/trim_split_whitespace.fixed b/tests/ui/trim_split_whitespace.fixed new file mode 100644 index 000000000000..e4d352f7367e --- /dev/null +++ b/tests/ui/trim_split_whitespace.fixed @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + let _ = " A B C ".split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref + let s = DerefStr(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + + // Deref + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/tests/ui/trim_split_whitespace.rs b/tests/ui/trim_split_whitespace.rs new file mode 100644 index 000000000000..f98451a98371 --- /dev/null +++ b/tests/ui/trim_split_whitespace.rs @@ -0,0 +1,91 @@ +// run-rustfix +#![warn(clippy::trim_split_whitespace)] +#![allow(clippy::let_unit_value)] + +struct Custom; +impl Custom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStr(&'static str); +impl std::ops::Deref for DerefStr { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} + +struct DerefStrAndCustom(&'static str); +impl std::ops::Deref for DerefStrAndCustom { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustom { + fn trim(self) -> Self { + self + } + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomSplit(&'static str); +impl std::ops::Deref for DerefStrAndCustomSplit { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomSplit { + #[allow(dead_code)] + fn split_whitespace(self) {} +} + +struct DerefStrAndCustomTrim(&'static str); +impl std::ops::Deref for DerefStrAndCustomTrim { + type Target = str; + fn deref(&self) -> &Self::Target { + self.0 + } +} +impl DerefStrAndCustomTrim { + fn trim(self) -> Self { + self + } +} + +fn main() { + // &str + let _ = " A B C ".trim().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + + // String + let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + + // Custom + let _ = Custom.trim().split_whitespace(); // should not trigger lint + + // Deref + let s = DerefStr(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + + // Deref + custom impl + let s = DerefStrAndCustom(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint + + // Deref + only custom split_ws() impl + let s = DerefStrAndCustomSplit(" A B C "); + let _ = s.trim().split_whitespace(); // should trigger lint + // Expl: trim() is called on str (deref) and returns &str. + // Thus split_ws() is called on str as well and the custom impl on S is unused + + // Deref + only custom trim() impl + let s = DerefStrAndCustomTrim(" A B C "); + let _ = s.trim().split_whitespace(); // should not trigger lint +} diff --git a/tests/ui/trim_split_whitespace.stderr b/tests/ui/trim_split_whitespace.stderr new file mode 100644 index 000000000000..5ae7849e27d2 --- /dev/null +++ b/tests/ui/trim_split_whitespace.stderr @@ -0,0 +1,52 @@ +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:62:23 + | +LL | let _ = " A B C ".trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + | + = note: `-D clippy::trim-split-whitespace` implied by `-D warnings` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:63:23 + | +LL | let _ = " A B C ".trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:64:23 + | +LL | let _ = " A B C ".trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:67:37 + | +LL | let _ = (" A B C ").to_string().trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim_start` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:68:37 + | +LL | let _ = (" A B C ").to_string().trim_start().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^^^ help: remove `trim_start()` + +error: found call to `str::trim_end` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:69:37 + | +LL | let _ = (" A B C ").to_string().trim_end().split_whitespace(); // should trigger lint + | ^^^^^^^^^^^ help: remove `trim_end()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:76:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: found call to `str::trim` before `str::split_whitespace` + --> $DIR/trim_split_whitespace.rs:84:15 + | +LL | let _ = s.trim().split_whitespace(); // should trigger lint + | ^^^^^^^ help: remove `trim()` + +error: aborting due to 8 previous errors + diff --git a/tests/ui/type_repetition_in_bounds.rs b/tests/ui/type_repetition_in_bounds.rs index fc740ee11d6a..d11432f90461 100644 --- a/tests/ui/type_repetition_in_bounds.rs +++ b/tests/ui/type_repetition_in_bounds.rs @@ -79,4 +79,7 @@ where u: U, } +// This should not lint +fn impl_trait(_: impl AsRef, _: impl AsRef) {} + fn main() {} diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index 148c19c7d070..abc25e59496b 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -19,5 +19,13 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: aborting due to 2 previous errors +error: this type has already been used as a bound predicate + --> $DIR/type_repetition_in_bounds.rs:83:43 + | +LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} + | ^^^^^^^^^^ + | + = help: consider combining the bounds: `impl AsRef: AsRef + AsRef` + +error: aborting due to 3 previous errors diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index afa337c45f41..7be15b0b2dd3 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,6 +1,7 @@ // aux-build:proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks)] +#![allow(clippy::let_unit_value)] extern crate proc_macro_unsafe; diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 856a07fd3168..87d445bd7b86 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -1,5 +1,5 @@ error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:256:19 + --> $DIR/undocumented_unsafe_blocks.rs:257:19 | LL | /* Safety: */ unsafe {} | ^^^^^^^^^ @@ -8,7 +8,7 @@ LL | /* Safety: */ unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:260:5 + --> $DIR/undocumented_unsafe_blocks.rs:261:5 | LL | unsafe {} | ^^^^^^^^^ @@ -16,7 +16,7 @@ LL | unsafe {} = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:14 + --> $DIR/undocumented_unsafe_blocks.rs:265:14 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:29 + --> $DIR/undocumented_unsafe_blocks.rs:265:29 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:264:48 + --> $DIR/undocumented_unsafe_blocks.rs:265:48 | LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; | ^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:268:18 + --> $DIR/undocumented_unsafe_blocks.rs:269:18 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -48,7 +48,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:268:37 + --> $DIR/undocumented_unsafe_blocks.rs:269:37 | LL | let _ = (42, unsafe {}, "test", unsafe {}); | ^^^^^^^^^ @@ -56,7 +56,7 @@ LL | let _ = (42, unsafe {}, "test", unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:272:14 + --> $DIR/undocumented_unsafe_blocks.rs:273:14 | LL | let _ = *unsafe { &42 }; | ^^^^^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | let _ = *unsafe { &42 }; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:277:19 + --> $DIR/undocumented_unsafe_blocks.rs:278:19 | LL | let _ = match unsafe {} { | ^^^^^^^^^ @@ -72,7 +72,7 @@ LL | let _ = match unsafe {} { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:283:14 + --> $DIR/undocumented_unsafe_blocks.rs:284:14 | LL | let _ = &unsafe {}; | ^^^^^^^^^ @@ -80,7 +80,7 @@ LL | let _ = &unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:287:14 + --> $DIR/undocumented_unsafe_blocks.rs:288:14 | LL | let _ = [unsafe {}; 5]; | ^^^^^^^^^ @@ -88,7 +88,7 @@ LL | let _ = [unsafe {}; 5]; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:291:13 + --> $DIR/undocumented_unsafe_blocks.rs:292:13 | LL | let _ = unsafe {}; | ^^^^^^^^^ @@ -96,7 +96,7 @@ LL | let _ = unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:301:8 + --> $DIR/undocumented_unsafe_blocks.rs:302:8 | LL | t!(unsafe {}); | ^^^^^^^^^ @@ -104,7 +104,7 @@ LL | t!(unsafe {}); = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:307:13 + --> $DIR/undocumented_unsafe_blocks.rs:308:13 | LL | unsafe {} | ^^^^^^^^^ @@ -116,7 +116,7 @@ LL | t!(); = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:315:5 + --> $DIR/undocumented_unsafe_blocks.rs:316:5 | LL | unsafe {} // SAFETY: | ^^^^^^^^^ @@ -124,7 +124,7 @@ LL | unsafe {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:319:5 + --> $DIR/undocumented_unsafe_blocks.rs:320:5 | LL | unsafe { | ^^^^^^^^ @@ -132,7 +132,7 @@ LL | unsafe { = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:329:5 + --> $DIR/undocumented_unsafe_blocks.rs:330:5 | LL | unsafe {}; | ^^^^^^^^^ @@ -140,7 +140,7 @@ LL | unsafe {}; = help: consider adding a safety comment on the preceding line error: unsafe block missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:333:20 + --> $DIR/undocumented_unsafe_blocks.rs:334:20 | LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/uninit.rs b/tests/ui/uninit.rs index 1ed3883c1f06..dac5ce272c02 100644 --- a/tests/ui/uninit.rs +++ b/tests/ui/uninit.rs @@ -1,4 +1,5 @@ #![feature(stmt_expr_attributes)] +#![allow(clippy::let_unit_value)] use std::mem::{self, MaybeUninit}; diff --git a/tests/ui/uninit.stderr b/tests/ui/uninit.stderr index 85b64a8419ab..15ef2349489f 100644 --- a/tests/ui/uninit.stderr +++ b/tests/ui/uninit.stderr @@ -1,5 +1,5 @@ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:6:29 + --> $DIR/uninit.rs:7:29 | LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -7,13 +7,13 @@ LL | let _: usize = unsafe { MaybeUninit::uninit().assume_init() }; = note: `#[deny(clippy::uninit_assumed_init)]` on by default error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:9:31 + --> $DIR/uninit.rs:10:31 | LL | let _: [u8; 0] = unsafe { MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this call for this type may be undefined behavior - --> $DIR/uninit.rs:24:29 + --> $DIR/uninit.rs:25:29 | LL | let _: usize = unsafe { mem::MaybeUninit::uninit().assume_init() }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/unit_arg.rs b/tests/ui/unit_arg.rs index 535683729f68..38be87bddf19 100644 --- a/tests/ui/unit_arg.rs +++ b/tests/ui/unit_arg.rs @@ -7,7 +7,8 @@ clippy::unnecessary_wraps, clippy::or_fun_call, clippy::needless_question_mark, - clippy::self_named_constructors + clippy::self_named_constructors, + clippy::let_unit_value )] use std::fmt::Debug; diff --git a/tests/ui/unit_arg.stderr b/tests/ui/unit_arg.stderr index 5cfb367a7be8..394dee29dc96 100644 --- a/tests/ui/unit_arg.stderr +++ b/tests/ui/unit_arg.stderr @@ -1,5 +1,5 @@ error: passing a unit value to a function - --> $DIR/unit_arg.rs:56:5 + --> $DIR/unit_arg.rs:57:5 | LL | / foo({ LL | | 1; @@ -20,7 +20,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:59:5 + --> $DIR/unit_arg.rs:60:5 | LL | foo(foo(1)); | ^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:60:5 + --> $DIR/unit_arg.rs:61:5 | LL | / foo({ LL | | foo(1); @@ -54,7 +54,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:65:5 + --> $DIR/unit_arg.rs:66:5 | LL | / b.bar({ LL | | 1; @@ -74,7 +74,7 @@ LL ~ b.bar(()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:68:5 + --> $DIR/unit_arg.rs:69:5 | LL | taking_multiple_units(foo(0), foo(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:69:5 + --> $DIR/unit_arg.rs:70:5 | LL | / taking_multiple_units(foo(0), { LL | | foo(1); @@ -110,7 +110,7 @@ LL ~ taking_multiple_units((), ()); | error: passing unit values to a function - --> $DIR/unit_arg.rs:73:5 + --> $DIR/unit_arg.rs:74:5 | LL | / taking_multiple_units( LL | | { @@ -140,7 +140,7 @@ LL + foo(2); ... error: passing a unit value to a function - --> $DIR/unit_arg.rs:84:13 + --> $DIR/unit_arg.rs:85:13 | LL | None.or(Some(foo(2))); | ^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL ~ }); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:87:5 + --> $DIR/unit_arg.rs:88:5 | LL | foo(foo(())); | ^^^^^^^^^^^^ @@ -166,7 +166,7 @@ LL ~ foo(()); | error: passing a unit value to a function - --> $DIR/unit_arg.rs:124:5 + --> $DIR/unit_arg.rs:125:5 | LL | Some(foo(1)) | ^^^^^^^^^^^^ diff --git a/tests/ui/unit_hash.rs b/tests/ui/unit_hash.rs index 989916c239ba..43eb54eff477 100644 --- a/tests/ui/unit_hash.rs +++ b/tests/ui/unit_hash.rs @@ -1,4 +1,5 @@ #![warn(clippy::unit_hash)] +#![allow(clippy::let_unit_value)] use std::collections::hash_map::DefaultHasher; use std::hash::Hash; diff --git a/tests/ui/unit_hash.stderr b/tests/ui/unit_hash.stderr index da276296e028..050fa55a12be 100644 --- a/tests/ui/unit_hash.stderr +++ b/tests/ui/unit_hash.stderr @@ -1,5 +1,5 @@ error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:18:23 + --> $DIR/unit_hash.rs:19:23 | LL | Foo::Empty => ().hash(&mut state), | ^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` @@ -8,7 +8,7 @@ LL | Foo::Empty => ().hash(&mut state), = note: the implementation of `Hash` for `()` is a no-op error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:23:5 + --> $DIR/unit_hash.rs:24:5 | LL | res.hash(&mut state); | ^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` @@ -16,7 +16,7 @@ LL | res.hash(&mut state); = note: the implementation of `Hash` for `()` is a no-op error: this call to `hash` on the unit type will do nothing - --> $DIR/unit_hash.rs:26:5 + --> $DIR/unit_hash.rs:27:5 | LL | do_nothing().hash(&mut state); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `hash` or consider using: `0_u8.hash(&mut state)` diff --git a/tests/ui/unnecessary_owned_empty_strings.fixed b/tests/ui/unnecessary_owned_empty_strings.fixed new file mode 100644 index 000000000000..f95f91329a2f --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_strings.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::unnecessary_owned_empty_strings)] + +fn ref_str_argument(_value: &str) {} + +#[allow(clippy::ptr_arg)] +fn ref_string_argument(_value: &String) {} + +fn main() { + // should be linted + ref_str_argument(""); + + // should be linted + ref_str_argument(""); + + // should not be linted + ref_str_argument(""); + + // should not be linted + ref_string_argument(&String::new()); +} diff --git a/tests/ui/unnecessary_owned_empty_strings.rs b/tests/ui/unnecessary_owned_empty_strings.rs new file mode 100644 index 000000000000..0cbdc151ed9b --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_strings.rs @@ -0,0 +1,22 @@ +// run-rustfix + +#![warn(clippy::unnecessary_owned_empty_strings)] + +fn ref_str_argument(_value: &str) {} + +#[allow(clippy::ptr_arg)] +fn ref_string_argument(_value: &String) {} + +fn main() { + // should be linted + ref_str_argument(&String::new()); + + // should be linted + ref_str_argument(&String::from("")); + + // should not be linted + ref_str_argument(""); + + // should not be linted + ref_string_argument(&String::new()); +} diff --git a/tests/ui/unnecessary_owned_empty_strings.stderr b/tests/ui/unnecessary_owned_empty_strings.stderr new file mode 100644 index 000000000000..46bc4597b335 --- /dev/null +++ b/tests/ui/unnecessary_owned_empty_strings.stderr @@ -0,0 +1,16 @@ +error: usage of `&String::new()` for a function expecting a `&str` argument + --> $DIR/unnecessary_owned_empty_strings.rs:12:22 + | +LL | ref_str_argument(&String::new()); + | ^^^^^^^^^^^^^^ help: try: `""` + | + = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings` + +error: usage of `&String::from("")` for a function expecting a `&str` argument + --> $DIR/unnecessary_owned_empty_strings.rs:15:22 + | +LL | ref_str_argument(&String::from("")); + | ^^^^^^^^^^^^^^^^^ help: try: `""` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 38ba41ac54ec..7455e22d49b2 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -2,6 +2,7 @@ #![allow(clippy::ptr_arg)] #![warn(clippy::unnecessary_to_owned)] +#![feature(custom_inner_attributes)] use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr, OsString}; @@ -213,6 +214,17 @@ fn get_file_path(_file_type: &FileType) -> Result Result $DIR/unnecessary_to_owned.rs:150:64 + --> $DIR/unnecessary_to_owned.rs:151:64 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^ help: remove this | = note: `-D clippy::redundant-clone` implied by `-D warnings` note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:150:20 + --> $DIR/unnecessary_to_owned.rs:151:20 | LL | require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:151:40 + --> $DIR/unnecessary_to_owned.rs:152:40 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:151:21 + --> $DIR/unnecessary_to_owned.rs:152:21 | LL | require_os_str(&OsString::from("x").to_os_string()); | ^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:152:48 + --> $DIR/unnecessary_to_owned.rs:153:48 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:152:19 + --> $DIR/unnecessary_to_owned.rs:153:19 | LL | require_path(&std::path::PathBuf::from("x").to_path_buf()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant clone - --> $DIR/unnecessary_to_owned.rs:153:35 + --> $DIR/unnecessary_to_owned.rs:154:35 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^ help: remove this | note: this value is dropped without further use - --> $DIR/unnecessary_to_owned.rs:153:18 + --> $DIR/unnecessary_to_owned.rs:154:18 | LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:59:36 + --> $DIR/unnecessary_to_owned.rs:60:36 | LL | require_c_str(&Cow::from(c_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this @@ -56,427 +56,427 @@ LL | require_c_str(&Cow::from(c_str).into_owned()); = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:60:19 + --> $DIR/unnecessary_to_owned.rs:61:19 | LL | require_c_str(&c_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_os_string` - --> $DIR/unnecessary_to_owned.rs:62:20 + --> $DIR/unnecessary_to_owned.rs:63:20 | LL | require_os_str(&os_str.to_os_string()); | ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:63:38 + --> $DIR/unnecessary_to_owned.rs:64:38 | LL | require_os_str(&Cow::from(os_str).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:64:20 + --> $DIR/unnecessary_to_owned.rs:65:20 | LL | require_os_str(&os_str.to_owned()); | ^^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_path_buf` - --> $DIR/unnecessary_to_owned.rs:66:18 + --> $DIR/unnecessary_to_owned.rs:67:18 | LL | require_path(&path.to_path_buf()); | ^^^^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:67:34 + --> $DIR/unnecessary_to_owned.rs:68:34 | LL | require_path(&Cow::from(path).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:68:18 + --> $DIR/unnecessary_to_owned.rs:69:18 | LL | require_path(&path.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:70:17 + --> $DIR/unnecessary_to_owned.rs:71:17 | LL | require_str(&s.to_string()); | ^^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:71:30 + --> $DIR/unnecessary_to_owned.rs:72:30 | LL | require_str(&Cow::from(s).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:72:17 + --> $DIR/unnecessary_to_owned.rs:73:17 | LL | require_str(&s.to_owned()); | ^^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:73:17 + --> $DIR/unnecessary_to_owned.rs:74:17 | LL | require_str(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:75:19 + --> $DIR/unnecessary_to_owned.rs:76:19 | LL | require_slice(&slice.to_vec()); | ^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:76:36 + --> $DIR/unnecessary_to_owned.rs:77:36 | LL | require_slice(&Cow::from(slice).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:77:19 + --> $DIR/unnecessary_to_owned.rs:78:19 | LL | require_slice(&array.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:78:19 + --> $DIR/unnecessary_to_owned.rs:79:19 | LL | require_slice(&array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:79:19 + --> $DIR/unnecessary_to_owned.rs:80:19 | LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:80:19 + --> $DIR/unnecessary_to_owned.rs:81:19 | LL | require_slice(&x_ref.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `into_owned` - --> $DIR/unnecessary_to_owned.rs:82:42 + --> $DIR/unnecessary_to_owned.rs:83:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:83:15 + --> $DIR/unnecessary_to_owned.rs:84:15 | LL | require_x(&x_ref.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:85:25 + --> $DIR/unnecessary_to_owned.rs:86:25 | LL | require_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:86:26 + --> $DIR/unnecessary_to_owned.rs:87:26 | LL | require_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:87:24 + --> $DIR/unnecessary_to_owned.rs:88:24 | LL | require_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:88:23 + --> $DIR/unnecessary_to_owned.rs:89:23 | LL | require_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:89:25 + --> $DIR/unnecessary_to_owned.rs:90:25 | LL | require_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:91:30 + --> $DIR/unnecessary_to_owned.rs:92:30 | LL | require_impl_deref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:92:31 + --> $DIR/unnecessary_to_owned.rs:93:31 | LL | require_impl_deref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:93:29 + --> $DIR/unnecessary_to_owned.rs:94:29 | LL | require_impl_deref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:94:28 + --> $DIR/unnecessary_to_owned.rs:95:28 | LL | require_impl_deref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:95:30 + --> $DIR/unnecessary_to_owned.rs:96:30 | LL | require_impl_deref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:29 + --> $DIR/unnecessary_to_owned.rs:98:29 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:97:43 + --> $DIR/unnecessary_to_owned.rs:98:43 | LL | require_deref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:29 + --> $DIR/unnecessary_to_owned.rs:99:29 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:98:47 + --> $DIR/unnecessary_to_owned.rs:99:47 | LL | require_deref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:100:26 + --> $DIR/unnecessary_to_owned.rs:101:26 | LL | require_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:101:27 + --> $DIR/unnecessary_to_owned.rs:102:27 | LL | require_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:102:25 + --> $DIR/unnecessary_to_owned.rs:103:25 | LL | require_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:103:24 + --> $DIR/unnecessary_to_owned.rs:104:24 | LL | require_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:104:24 + --> $DIR/unnecessary_to_owned.rs:105:24 | LL | require_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:105:26 + --> $DIR/unnecessary_to_owned.rs:106:26 | LL | require_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:106:26 + --> $DIR/unnecessary_to_owned.rs:107:26 | LL | require_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:107:26 + --> $DIR/unnecessary_to_owned.rs:108:26 | LL | require_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:109:31 + --> $DIR/unnecessary_to_owned.rs:110:31 | LL | require_impl_as_ref_c_str(c_str.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `c_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:110:32 + --> $DIR/unnecessary_to_owned.rs:111:32 | LL | require_impl_as_ref_os_str(os_str.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `os_str` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:111:30 + --> $DIR/unnecessary_to_owned.rs:112:30 | LL | require_impl_as_ref_path(path.to_owned()); | ^^^^^^^^^^^^^^^ help: use: `path` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:112:29 + --> $DIR/unnecessary_to_owned.rs:113:29 | LL | require_impl_as_ref_str(s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:113:29 + --> $DIR/unnecessary_to_owned.rs:114:29 | LL | require_impl_as_ref_str(x.to_owned()); | ^^^^^^^^^^^^ help: use: `&x` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:114:31 + --> $DIR/unnecessary_to_owned.rs:115:31 | LL | require_impl_as_ref_slice(array.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:115:31 + --> $DIR/unnecessary_to_owned.rs:116:31 | LL | require_impl_as_ref_slice(array_ref.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:116:31 + --> $DIR/unnecessary_to_owned.rs:117:31 | LL | require_impl_as_ref_slice(slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:30 - | -LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); - | ^^^^^^^^^^^^ help: use: `s` - -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:118:44 - | -LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); - | ^^^^^^^^^^^^^^^^ help: use: `array` - error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:119:30 | -LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); +LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:119:44 | -LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` +LL | require_as_ref_str_slice(s.to_owned(), array.to_owned()); + | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:120:30 | -LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); +LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:120:44 | +LL | require_as_ref_str_slice(s.to_owned(), array_ref.to_owned()); + | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:121:30 + | +LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); + | ^^^^^^^^^^^^ help: use: `s` + +error: unnecessary use of `to_owned` + --> $DIR/unnecessary_to_owned.rs:121:44 + | LL | require_as_ref_str_slice(s.to_owned(), slice.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:30 + --> $DIR/unnecessary_to_owned.rs:122:30 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `array` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:121:48 + --> $DIR/unnecessary_to_owned.rs:122:48 | LL | require_as_ref_slice_str(array.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:30 + --> $DIR/unnecessary_to_owned.rs:123:30 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:122:52 + --> $DIR/unnecessary_to_owned.rs:123:52 | LL | require_as_ref_slice_str(array_ref.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:30 + --> $DIR/unnecessary_to_owned.rs:124:30 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^^^^^ help: use: `slice` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:123:48 + --> $DIR/unnecessary_to_owned.rs:124:48 | LL | require_as_ref_slice_str(slice.to_owned(), s.to_owned()); | ^^^^^^^^^^^^ help: use: `s` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:125:20 + --> $DIR/unnecessary_to_owned.rs:126:20 | LL | let _ = x.join(&x_ref.to_string()); | ^^^^^^^^^^^^^^^^^^ help: use: `x_ref` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:127:13 + --> $DIR/unnecessary_to_owned.rs:128:13 | LL | let _ = slice.to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:128:13 + --> $DIR/unnecessary_to_owned.rs:129:13 | LL | let _ = slice.to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:129:13 + --> $DIR/unnecessary_to_owned.rs:130:13 | LL | let _ = [std::path::PathBuf::new()][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:130:13 + --> $DIR/unnecessary_to_owned.rs:131:13 | LL | let _ = [std::path::PathBuf::new()][..].to_owned().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:132:13 + --> $DIR/unnecessary_to_owned.rs:133:13 | LL | let _ = IntoIterator::into_iter(slice.to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:133:13 + --> $DIR/unnecessary_to_owned.rs:134:13 | LL | let _ = IntoIterator::into_iter(slice.to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:134:13 + --> $DIR/unnecessary_to_owned.rs:135:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:135:13 + --> $DIR/unnecessary_to_owned.rs:136:13 | LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:196:14 + --> $DIR/unnecessary_to_owned.rs:197:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -491,11 +491,23 @@ LL - let path = match get_file_path(&t) { LL + let path = match get_file_path(t) { | +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:220:14 + | +LL | let _ = &["x"][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` + +error: unnecessary use of `to_vec` + --> $DIR/unnecessary_to_owned.rs:225:14 + | +LL | let _ = &["x"][..].to_vec().into_iter(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` + error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:260:24 + --> $DIR/unnecessary_to_owned.rs:272:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` -error: aborting due to 77 previous errors +error: aborting due to 79 previous errors diff --git a/tests/ui/unnested_or_patterns.fixed b/tests/ui/unnested_or_patterns.fixed index 46463a29e9b2..c223b5bc711b 100644 --- a/tests/ui/unnested_or_patterns.fixed +++ b/tests/ui/unnested_or_patterns.fixed @@ -6,10 +6,13 @@ #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { + // Should be ignored by this lint, as nesting requires more characters. + if let &0 | &2 = &0 {} + if let box (0 | 2) = Box::new(0) {} if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} - const C0: &u8 = &1; - if let &(0 | 2) | C0 = &0 {} + const C0: Option = Some(1); + if let Some(1 | 2) | C0 = None {} if let &mut (0 | 2) = &mut 0 {} if let x @ (0 | 2) = 0 {} if let (0, 1 | 2 | 3) = (0, 0) {} diff --git a/tests/ui/unnested_or_patterns.rs b/tests/ui/unnested_or_patterns.rs index 8ce0738bfc27..04cd11036e4e 100644 --- a/tests/ui/unnested_or_patterns.rs +++ b/tests/ui/unnested_or_patterns.rs @@ -6,10 +6,13 @@ #![allow(unreachable_patterns, irrefutable_let_patterns, unused_variables)] fn main() { + // Should be ignored by this lint, as nesting requires more characters. + if let &0 | &2 = &0 {} + if let box 0 | box 2 = Box::new(0) {} if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} - const C0: &u8 = &1; - if let &0 | C0 | &2 = &0 {} + const C0: Option = Some(1); + if let Some(1) | C0 | Some(2) = None {} if let &mut 0 | &mut 2 = &mut 0 {} if let x @ 0 | x @ 2 = 0 {} if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} diff --git a/tests/ui/unnested_or_patterns.stderr b/tests/ui/unnested_or_patterns.stderr index de424c3fdb8f..453c66cbba8f 100644 --- a/tests/ui/unnested_or_patterns.stderr +++ b/tests/ui/unnested_or_patterns.stderr @@ -1,5 +1,5 @@ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:9:12 + --> $DIR/unnested_or_patterns.rs:12:12 | LL | if let box 0 | box 2 = Box::new(0) {} | ^^^^^^^^^^^^^ @@ -11,7 +11,7 @@ LL | if let box (0 | 2) = Box::new(0) {} | ~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:10:12 + --> $DIR/unnested_or_patterns.rs:13:12 | LL | if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -22,18 +22,18 @@ LL | if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {} | ~~~~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:12:12 + --> $DIR/unnested_or_patterns.rs:15:12 | -LL | if let &0 | C0 | &2 = &0 {} - | ^^^^^^^^^^^^ +LL | if let Some(1) | C0 | Some(2) = None {} + | ^^^^^^^^^^^^^^^^^^^^^^ | help: nest the patterns | -LL | if let &(0 | 2) | C0 = &0 {} - | ~~~~~~~~~~~~~ +LL | if let Some(1 | 2) | C0 = None {} + | ~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:13:12 + --> $DIR/unnested_or_patterns.rs:16:12 | LL | if let &mut 0 | &mut 2 = &mut 0 {} | ^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | if let &mut (0 | 2) = &mut 0 {} | ~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:14:12 + --> $DIR/unnested_or_patterns.rs:17:12 | LL | if let x @ 0 | x @ 2 = 0 {} | ^^^^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | if let x @ (0 | 2) = 0 {} | ~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:15:12 + --> $DIR/unnested_or_patterns.rs:18:12 | LL | if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,7 @@ LL | if let (0, 1 | 2 | 3) = (0, 0) {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:16:12 + --> $DIR/unnested_or_patterns.rs:19:12 | LL | if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -77,7 +77,7 @@ LL | if let (1 | 2 | 3, 0) = (0, 0) {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:17:12 + --> $DIR/unnested_or_patterns.rs:20:12 | LL | if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,7 +88,7 @@ LL | if let (x, ..) | (x, 1 | 2) = (0, 1) {} | ~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:18:12 + --> $DIR/unnested_or_patterns.rs:21:12 | LL | if let [0] | [1] = [0] {} | ^^^^^^^^^ @@ -99,7 +99,7 @@ LL | if let [0 | 1] = [0] {} | ~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:19:12 + --> $DIR/unnested_or_patterns.rs:22:12 | LL | if let [x, 0] | [x, 1] = [0, 1] {} | ^^^^^^^^^^^^^^^ @@ -110,7 +110,7 @@ LL | if let [x, 0 | 1] = [0, 1] {} | ~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:20:12 + --> $DIR/unnested_or_patterns.rs:23:12 | LL | if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -121,7 +121,7 @@ LL | if let [x, 0 | 1 | 2] = [0, 1] {} | ~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:21:12 + --> $DIR/unnested_or_patterns.rs:24:12 | LL | if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,7 +132,7 @@ LL | if let [x, ..] | [x, 1 | 2] = [0, 1] {} | ~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:23:12 + --> $DIR/unnested_or_patterns.rs:26:12 | LL | if let TS(0, x) | TS(1, x) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^ @@ -143,7 +143,7 @@ LL | if let TS(0 | 1, x) = TS(0, 0) {} | ~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:24:12 + --> $DIR/unnested_or_patterns.rs:27:12 | LL | if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -154,7 +154,7 @@ LL | if let TS(1 | 2 | 3, 0) = TS(0, 0) {} | ~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:25:12 + --> $DIR/unnested_or_patterns.rs:28:12 | LL | if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +165,7 @@ LL | if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {} | ~~~~~~~~~~~~~~~~~~~~~~~~ error: unnested or-patterns - --> $DIR/unnested_or_patterns.rs:30:12 + --> $DIR/unnested_or_patterns.rs:33:12 | LL | if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/useless_attribute.fixed b/tests/ui/useless_attribute.fixed index ce58a80347b5..c23231a99e9f 100644 --- a/tests/ui/useless_attribute.fixed +++ b/tests/ui/useless_attribute.fixed @@ -57,6 +57,12 @@ pub use std::io::prelude::*; #[allow(clippy::enum_glob_use)] pub use std::cmp::Ordering::*; +// don't lint on clippy::redundant_pub_crate +mod c { + #[allow(clippy::redundant_pub_crate)] + pub(crate) struct S; +} + fn test_indented_attr() { #![allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.rs b/tests/ui/useless_attribute.rs index c82bb9ba07fd..7a7b198ea607 100644 --- a/tests/ui/useless_attribute.rs +++ b/tests/ui/useless_attribute.rs @@ -57,6 +57,12 @@ pub use std::io::prelude::*; #[allow(clippy::enum_glob_use)] pub use std::cmp::Ordering::*; +// don't lint on clippy::redundant_pub_crate +mod c { + #[allow(clippy::redundant_pub_crate)] + pub(crate) struct S; +} + fn test_indented_attr() { #[allow(clippy::almost_swapped)] use std::collections::HashSet; diff --git a/tests/ui/useless_attribute.stderr b/tests/ui/useless_attribute.stderr index d0194e4bbbe5..255d28763553 100644 --- a/tests/ui/useless_attribute.stderr +++ b/tests/ui/useless_attribute.stderr @@ -13,7 +13,7 @@ LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)` error: useless lint attribute - --> $DIR/useless_attribute.rs:61:5 + --> $DIR/useless_attribute.rs:67:5 | LL | #[allow(clippy::almost_swapped)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]` diff --git a/tests/ui/wildcard_imports.fixed b/tests/ui/wildcard_imports.fixed index 8402c33a4cd5..b6f47ae906b7 100644 --- a/tests/ui/wildcard_imports.fixed +++ b/tests/ui/wildcard_imports.fixed @@ -8,8 +8,7 @@ // FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] -#![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.rs b/tests/ui/wildcard_imports.rs index faaeaade9b02..eb404b7a3de5 100644 --- a/tests/ui/wildcard_imports.rs +++ b/tests/ui/wildcard_imports.rs @@ -8,8 +8,7 @@ // FIXME: We should likely add another edition 2021 test case for this lint #![warn(clippy::wildcard_imports)] -#![allow(unused)] -#![allow(clippy::unnecessary_wraps)] +#![allow(unused, clippy::unnecessary_wraps, clippy::let_unit_value)] #![warn(unused_imports)] extern crate wildcard_imports_helper; diff --git a/tests/ui/wildcard_imports.stderr b/tests/ui/wildcard_imports.stderr index 7534a65ec9bd..626c1754fc82 100644 --- a/tests/ui/wildcard_imports.stderr +++ b/tests/ui/wildcard_imports.stderr @@ -1,5 +1,5 @@ error: usage of wildcard import - --> $DIR/wildcard_imports.rs:17:5 + --> $DIR/wildcard_imports.rs:16:5 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` @@ -7,85 +7,85 @@ LL | use crate::fn_mod::*; = note: `-D clippy::wildcard-imports` implied by `-D warnings` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:18:5 + --> $DIR/wildcard_imports.rs:17:5 | LL | use crate::mod_mod::*; | ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:19:5 + --> $DIR/wildcard_imports.rs:18:5 | LL | use crate::multi_fn_mod::*; | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:21:5 + --> $DIR/wildcard_imports.rs:20:5 | LL | use crate::struct_mod::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:25:5 + --> $DIR/wildcard_imports.rs:24:5 | LL | use wildcard_imports_helper::inner::inner_for_self_import::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:26:5 + --> $DIR/wildcard_imports.rs:25:5 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:97:13 + --> $DIR/wildcard_imports.rs:96:13 | LL | use crate::fn_mod::*; | ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:103:75 + --> $DIR/wildcard_imports.rs:102:75 | LL | use wildcard_imports_helper::inner::inner_for_self_import::{self, *}; | ^ help: try: `inner_extern_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:104:13 + --> $DIR/wildcard_imports.rs:103:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:20 + --> $DIR/wildcard_imports.rs:114:20 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^ help: try: `inner::inner_foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:115:30 + --> $DIR/wildcard_imports.rs:114:30 | LL | use self::{inner::*, inner2::*}; | ^^^^^^^^^ help: try: `inner2::inner_bar` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:122:13 + --> $DIR/wildcard_imports.rs:121:13 | LL | use wildcard_imports_helper::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:151:9 + --> $DIR/wildcard_imports.rs:150:9 | LL | use crate::in_fn_test::*; | ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:160:9 + --> $DIR/wildcard_imports.rs:159:9 | LL | use crate:: in_fn_test:: * ; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:161:9 + --> $DIR/wildcard_imports.rs:160:9 | LL | use crate:: fn_mod:: | _________^ @@ -93,37 +93,37 @@ LL | | *; | |_________^ help: try: `crate:: fn_mod::foo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:172:13 + --> $DIR/wildcard_imports.rs:171:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:207:17 + --> $DIR/wildcard_imports.rs:206:17 | LL | use super::*; | ^^^^^^^^ help: try: `super::insidefoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:215:13 + --> $DIR/wildcard_imports.rs:214:13 | LL | use super_imports::*; | ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:224:17 + --> $DIR/wildcard_imports.rs:223:17 | LL | use super::super::*; | ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:233:13 + --> $DIR/wildcard_imports.rs:232:13 | LL | use super::super::super_imports::*; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo` error: usage of wildcard import - --> $DIR/wildcard_imports.rs:241:13 + --> $DIR/wildcard_imports.rs:240:13 | LL | use super::*; | ^^^^^^^^ help: try: `super::foofoo` diff --git a/tests/ui/wrong_self_convention.rs b/tests/ui/wrong_self_convention.rs index f8fee4b3ab2d..e3cc90ee222a 100644 --- a/tests/ui/wrong_self_convention.rs +++ b/tests/ui/wrong_self_convention.rs @@ -193,11 +193,6 @@ pub mod issue8142 { struct S; impl S { - // Should lint: is_ methods should only take &self, or no self at all. - fn is_still_buggy(&mut self) -> bool { - false - } - // Should not lint: "no self at all" is allowed. fn is_forty_two(x: u32) -> bool { x == 42 diff --git a/tests/ui/wrong_self_convention.stderr b/tests/ui/wrong_self_convention.stderr index 5493a99572e0..2e7ee51d7e11 100644 --- a/tests/ui/wrong_self_convention.stderr +++ b/tests/ui/wrong_self_convention.stderr @@ -31,7 +31,7 @@ LL | fn into_i32(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:38:15 | LL | fn is_i32(self) {} @@ -71,7 +71,7 @@ LL | pub fn into_i64(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:46:19 | LL | pub fn is_i64(self) {} @@ -111,7 +111,7 @@ LL | fn into_i32_ref(&self) {} | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:98:19 | LL | fn is_i32(self) {} @@ -143,7 +143,7 @@ LL | fn into_i32_ref(&self); | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` +error: methods called `is_*` usually take `self` by mutable reference or `self` by reference or no `self` --> $DIR/wrong_self_convention.rs:122:19 | LL | fn is_i32(self); @@ -191,13 +191,5 @@ LL | fn to_u64(self) -> u64 { | = help: consider choosing a less ambiguous name -error: methods called `is_*` usually take `self` by reference or no `self` - --> $DIR/wrong_self_convention.rs:197:27 - | -LL | fn is_still_buggy(&mut self) -> bool { - | ^^^^^^^^^ - | - = help: consider choosing a less ambiguous name - -error: aborting due to 25 previous errors +error: aborting due to 24 previous errors diff --git a/tests/ui/wrong_self_convention2.rs b/tests/ui/wrong_self_convention2.rs index a8fe83311337..0dcf4743e8b8 100644 --- a/tests/ui/wrong_self_convention2.rs +++ b/tests/ui/wrong_self_convention2.rs @@ -104,3 +104,13 @@ mod issue4546 { pub fn to_other_thingy(self: Pin<&Self>) {} } } + +mod issue_8480_8513 { + struct Cat(String); + + impl Cat { + fn is_animal(&mut self) -> bool { + todo!(); + } + } +} From ed8458f67a56b0f01dcdd693339c1aa2ee7fa6ff Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 15:20:07 +0100 Subject: [PATCH 448/536] (Partially) Revert "HACK: Move buggy lints to nursery" This reverts commit bb01aca86f66954b80798213711e8eadc4b26902. Partial: Keep regression tests --- clippy_lints/src/lib.register_nursery.rs | 2 -- clippy_lints/src/lib.register_pedantic.rs | 2 ++ clippy_lints/src/trait_bounds.rs | 4 ++-- tests/ui/trait_duplication_in_bounds.stderr | 10 +--------- tests/ui/type_repetition_in_bounds.stderr | 10 +--------- 5 files changed, 6 insertions(+), 22 deletions(-) diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index ec187563b3f6..18904a945389 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -28,8 +28,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), - LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), - LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR), LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(use_self::USE_SELF), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 2ee2c6e3358c..63232fd41130 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -84,6 +84,8 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), LintId::of(strings::STRING_ADD_ASSIGN), + LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), + LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS), LintId::of(transmute::TRANSMUTE_PTR_TO_PTR), LintId::of(types::LINKEDLIST), LintId::of(types::OPTION_OPTION), diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index 78e388a49af1..c0aca2d517cf 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -35,7 +35,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.38.0"] pub TYPE_REPETITION_IN_BOUNDS, - nursery, + pedantic, "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`" } @@ -65,7 +65,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.47.0"] pub TRAIT_DUPLICATION_IN_BOUNDS, - nursery, + pedantic, "Check if the same trait bounds are specified twice during a function declaration" } diff --git a/tests/ui/trait_duplication_in_bounds.stderr b/tests/ui/trait_duplication_in_bounds.stderr index d0a4cfb88370..6f8c8e47dfbf 100644 --- a/tests/ui/trait_duplication_in_bounds.stderr +++ b/tests/ui/trait_duplication_in_bounds.stderr @@ -67,13 +67,5 @@ LL | Self: Iterator, | = help: consider removing this trait bound -error: this trait bound is already specified in the where clause - --> $DIR/trait_duplication_in_bounds.rs:99:23 - | -LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} - | ^^^^^^^^^^ - | - = help: consider removing this trait bound - -error: aborting due to 9 previous errors +error: aborting due to 8 previous errors diff --git a/tests/ui/type_repetition_in_bounds.stderr b/tests/ui/type_repetition_in_bounds.stderr index abc25e59496b..148c19c7d070 100644 --- a/tests/ui/type_repetition_in_bounds.stderr +++ b/tests/ui/type_repetition_in_bounds.stderr @@ -19,13 +19,5 @@ LL | Self: Copy + Default + Ord, | = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord` -error: this type has already been used as a bound predicate - --> $DIR/type_repetition_in_bounds.rs:83:43 - | -LL | fn impl_trait(_: impl AsRef, _: impl AsRef) {} - | ^^^^^^^^^^ - | - = help: consider combining the bounds: `impl AsRef: AsRef + AsRef` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors From 6881a5a9703ff9f700c649105599324fe182ecc5 Mon Sep 17 00:00:00 2001 From: Federico Martinez Date: Thu, 5 May 2022 18:50:34 +0200 Subject: [PATCH 449/536] Added missing `### What it does` Adds the missing line to cast-slice-different-sizes lint documentation /closes 8781 --- clippy_lints/src/casts/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index b58252dcb942..9c3ebbe035bb 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -413,6 +413,7 @@ declare_clippy_lint! { } declare_clippy_lint! { + /// ### What it does /// Checks for `as` casts between raw pointers to slices with differently sized elements. /// /// ### Why is this bad? From 17bea3137b0b30b524c318e5773ff827442958b9 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Thu, 5 May 2022 13:24:38 -0400 Subject: [PATCH 450/536] Revert hack fixing #2475 added in commit 034c81b76145a0514916f34713671b16f26988df. It's no longer needed. --- clippy_lints/src/attrs.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 8b0e11cb802e..30dde2f0ef09 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -6,7 +6,7 @@ use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; -use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MacArgs, MacArgsEq, MetaItemKind, NestedMetaItem}; +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, @@ -586,21 +586,10 @@ impl EarlyLintPass for EarlyAttributes { fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { for attr in &item.attrs { - let attr_item = if let AttrKind::Normal(ref attr, _) = attr.kind { - attr - } else { - return; - }; - - if attr.style == AttrStyle::Outer { - if let MacArgs::Eq(_, MacArgsEq::Ast(expr)) = &attr_item.args - && !matches!(expr.kind, rustc_ast::ExprKind::Lit(..)) { - return; - } - if attr_item.args.inner_tokens().is_empty() || !is_present_in_source(cx, attr.span) { - return; - } - + if matches!(attr.kind, AttrKind::Normal(..)) + && attr.style == AttrStyle::Outer + && is_present_in_source(cx, attr.span) + { let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); let end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent()); From 4ed52bf13ecdc69a31e7be6a0bc9ba2a3e1a1932 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Thu, 5 May 2022 18:27:56 -0400 Subject: [PATCH 451/536] Use CamelCase; Fix filter ranges --- util/gh-pages/index.html | 8 +++---- util/gh-pages/script.js | 46 +++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index ec62b3682f23..11d99f39b558 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -435,7 +435,7 @@ Otherwise, have a great day =^.^= @@ -447,7 +447,7 @@ Otherwise, have a great day =^.^= -
  • +
  • 1. .0
  • @@ -536,4 +536,4 @@ Otherwise, have a great day =^.^= - \ No newline at end of file + diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index b04a93745749..8f6832aef539 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -136,13 +136,11 @@ }; $scope.themes = THEMES_DEFAULT; - const DEFAULT_VERSION_FILTERS = { - "≥": { enabled: false, minor_version: "" }, - "≤": { enabled: false, minor_version: "" }, - "=": { enabled: false, minor_version: "" }, + $scope.versionFilters = { + "≥": {enabled: false, minorVersion: ""}, + "≤": {enabled: false, minorVersion: ""}, + "=": {enabled: false, minorVersion: ""}, }; - // Weird workaround to get a copy of the object - $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); $scope.selectTheme = function (theme) { setTheme(theme, true); @@ -171,7 +169,9 @@ } $scope.clearVersionFilters = function () { - $scope.version_filters = JSON.parse(JSON.stringify(DEFAULT_VERSION_FILTERS)); + for (let filter in $scope.versionFilters) { + $scope.versionFilters[filter] = { enabled: false, minorVersion: "" }; + } } $scope.versionFilterCount = function(obj) { @@ -179,21 +179,21 @@ } $scope.byVersion = function(lint) { - function validate_version_str(ver) { + function validateVersionStr(ver) { return ver.length === 2 && !isNaN(ver); } - let filters = $scope.version_filters; + let filters = $scope.versionFilters; // Strip the "pre " prefix for pre 1.29.0 lints - let lint_version = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; - let lint_minor_verison = lint_version.substring(2, 4); + let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; + let lintMinorVersion = lintVersion.substring(2, 4); for (const filter in filters) { - let minor_version = filters[filter].minor_version; + let minorVersion = filters[filter].minorVersion; // Skip the work for version strings with invalid lengths or characters - if (!validate_version_str(minor_version)) { + if (!validateVersionStr(minorVersion)) { filters[filter].enabled = false; continue; } @@ -203,14 +203,14 @@ let result; switch (filter) { case "≥": - result = (lint_minor_verison >= minor_version); + result = (lintMinorVersion >= minorVersion); break; case "≤": - result = (lint_minor_verison <= minor_version); + result = (lintMinorVersion <= minorVersion); break; // "=" gets the highest priority, since all filters are inclusive case "=": - return (lint_minor_verison === minor_version); + return (lintMinorVersion === minorVersion); default: return true } @@ -219,19 +219,21 @@ return false; } - let cmp_filter; + let cmpFilter; if (filter === "≥") { - cmp_filter = "≤"; + cmpFilter = "≤"; } else { - cmp_filter = "≥"; + cmpFilter = "≥"; } - let cmp_minor_version = filters[cmp_filter].minor_version; - if (!filters[cmp_filter].enabled || !validate_version_str(cmp_minor_version)) { + let cmpMinorVersion = filters[cmpFilter].minorVersion; + if (!validateVersionStr(cmpMinorVersion)) { + filters[cmpFilter].enabled = false; return true; } - return (cmp_filter === "≥") ? (lint_minor_verison > minor_version) : (lint_minor_verison < minor_version); + filters[cmpFilter].enabled = true; + return (cmpFilter === "≥") ? (lintMinorVersion >= cmpMinorVersion) : (lintMinorVersion <= cmpMinorVersion); } return true; From 677b38c9185feab059d8c3ef3dffe49e67c26aba Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Thu, 5 May 2022 20:11:41 -0500 Subject: [PATCH 452/536] Suggest -Zunpretty=ast-tree instead of -Zast-json -Zast-json is being removed shortly: https://github.com/rust-lang/rust/pull/85993. ast-tree does essentially the same thing, and still works today even before that PR lands. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc663de8f792..022ba5d84148 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -67,7 +67,7 @@ and resolved paths. [`T-AST`] issues will generally need you to match against a predefined syntax structure. To figure out how this syntax structure is encoded in the AST, it is recommended to run -`rustc -Z ast-json` on an example of the structure and compare with the [nodes in the AST docs]. +`rustc -Z unpretty=ast-tree` on an example of the structure and compare with the [nodes in the AST docs]. Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. From 2aa63c95bd1590d5286bc79358397608e8d91fa4 Mon Sep 17 00:00:00 2001 From: Ariel Uy Date: Thu, 5 May 2022 21:13:17 -0700 Subject: [PATCH 453/536] Create RangeBounds struct For check_range_bounds return type. --- clippy_lints/src/ranges.rs | 76 ++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index 043299333b12..f26f3650cb35 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -207,7 +207,13 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { extract_msrv_attr!(LateContext); } -fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, expr: &Expr<'_>) { +fn check_possible_range_contains( + cx: &LateContext<'_>, + op: BinOpKind, + left: &Expr<'_>, + right: &Expr<'_>, + expr: &Expr<'_>, +) { if in_constant(cx, expr.hir_id) { return; } @@ -219,23 +225,19 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' _ => return, }; // value, name, order (higher/lower), inclusiveness - if let ( - Some((lval, lexpr, lid, name_span, lval_span, lord, linc)), - Some((rval, _, rid, _, rval_span, rord, rinc)), - ) = (check_range_bounds(cx, l), check_range_bounds(cx, r)) - { + if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) { // we only lint comparisons on the same name and with different // direction - if lid != rid || lord == rord { + if l.id != r.id || l.ord == r.ord { return; } - let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(lexpr), &lval, &rval); - if combine_and && ord == Some(rord) { + let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val); + if combine_and && ord == Some(r.ord) { // order lower bound and upper bound - let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less { - (lval_span, rval_span, linc, rinc) + let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less { + (l.val_span, r.val_span, l.inc, r.inc) } else { - (rval_span, lval_span, rinc, linc) + (r.val_span, l.val_span, r.inc, l.inc) }; // we only lint inclusive lower bounds if !l_inc { @@ -247,7 +249,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' ("Range", "..") }; let mut applicability = Applicability::MachineApplicable; - let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); let space = if lo.ends_with('.') { " " } else { "" }; @@ -260,13 +262,13 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name), applicability, ); - } else if !combine_and && ord == Some(lord) { + } else if !combine_and && ord == Some(l.ord) { // `!_.contains(_)` // order lower bound and upper bound - let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less { - (lval_span, rval_span, linc, rinc) + let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less { + (l.val_span, r.val_span, l.inc, r.inc) } else { - (rval_span, lval_span, rinc, linc) + (r.val_span, l.val_span, r.inc, l.inc) }; if l_inc { return; @@ -277,7 +279,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' ("RangeInclusive", "..=") }; let mut applicability = Applicability::MachineApplicable; - let name = snippet_with_applicability(cx, name_span, "_", &mut applicability); + let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability); let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability); let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability); let space = if lo.ends_with('.') { " " } else { "" }; @@ -294,10 +296,20 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<' } } -fn check_range_bounds<'a>( - cx: &'a LateContext<'_>, - ex: &'a Expr<'_>, -) -> Option<(Constant, &'a Expr<'a>, HirId, Span, Span, Ordering, bool)> { +struct RangeBounds<'a> { + val: Constant, + expr: &'a Expr<'a>, + id: HirId, + name_span: Span, + val_span: Span, + ord: Ordering, + inc: bool, +} + +// Takes a binary expression such as x <= 2 as input +// Breaks apart into various pieces, such as the value of the number, +// hir id of the variable, and direction/inclusiveness of the operator +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option> { if let ExprKind::Binary(ref op, l, r) = ex.kind { let (inclusive, ordering) = match op.node { BinOpKind::Gt => (false, Ordering::Greater), @@ -308,11 +320,27 @@ fn check_range_bounds<'a>( }; if let Some(id) = path_to_local(l) { if let Some((c, _)) = constant(cx, cx.typeck_results(), r) { - return Some((c, r, id, l.span, r.span, ordering, inclusive)); + return Some(RangeBounds { + val: c, + expr: r, + id, + name_span: l.span, + val_span: r.span, + ord: ordering, + inc: inclusive, + }); } } else if let Some(id) = path_to_local(r) { if let Some((c, _)) = constant(cx, cx.typeck_results(), l) { - return Some((c, l, id, r.span, l.span, ordering.reverse(), inclusive)); + return Some(RangeBounds { + val: c, + expr: l, + id, + name_span: r.span, + val_span: l.span, + ord: ordering.reverse(), + inc: inclusive, + }); } } } From 91a822c16228c57bf57961271ecb2d0d28112fa4 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Thu, 5 May 2022 21:39:54 -0400 Subject: [PATCH 454/536] Address `unnecessary_to_owned` false positive --- .../src/methods/unnecessary_to_owned.rs | 38 +++++++----- tests/ui/recursive_format_impl.stderr | 11 +--- tests/ui/unnecessary_to_owned.fixed | 61 ++++++++++++++++++- tests/ui/unnecessary_to_owned.rs | 61 ++++++++++++++++++- tests/ui/unnecessary_to_owned.stderr | 34 +++++------ 5 files changed, 160 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 02b882e8b55e..1f426979c4a0 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -65,13 +65,12 @@ fn check_addr_of_expr( if let Some(parent) = get_parent_expr(cx, expr); if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind; let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::>(); - if let Some(target_ty) = match adjustments[..] - { + if let // For matching uses of `Cow::from` [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Borrow(_), @@ -82,7 +81,7 @@ fn check_addr_of_expr( | [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Borrow(_), @@ -97,7 +96,7 @@ fn check_addr_of_expr( | [ Adjustment { kind: Adjust::Deref(None), - .. + target: referent_ty, }, Adjustment { kind: Adjust::Deref(Some(OverloadedDeref { .. })), @@ -107,17 +106,24 @@ fn check_addr_of_expr( kind: Adjust::Borrow(_), target: target_ty, }, - ] => Some(target_ty), - _ => None, - }; + ] = adjustments[..]; let receiver_ty = cx.typeck_results().expr_ty(receiver); - // Only flag cases where the receiver is copyable or the method is `Cow::into_owned`. This - // restriction is to ensure there is not overlap between `redundant_clone` and this lint. - if is_copy(cx, receiver_ty) || is_cow_into_owned(cx, method_name, method_def_id); + let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty); + let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); + // Only flag cases satisfying at least one of the following three conditions: + // * the referent and receiver types are distinct + // * the referent/receiver type is a copyable array + // * the method is `Cow::into_owned` + // This restriction is to ensure there is no overlap between `redundant_clone` and this + // lint. It also avoids the following false positive: + // https://github.com/rust-lang/rust-clippy/issues/8759 + // Arrays are a bit of a corner case. Non-copyable arrays are handled by + // `redundant_clone`, but copyable arrays are not. + if *referent_ty != receiver_ty + || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty)) + || is_cow_into_owned(cx, method_name, method_def_id); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty); - let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty); if receiver_ty == target_ty && n_target_refs >= n_receiver_refs { span_lint_and_sugg( cx, @@ -207,7 +213,11 @@ fn check_into_iter_call_arg( if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" }; + let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { + "copied" + } else { + "cloned" + }; // The next suggestion may be incorrect because the removal of the `to_owned`-like // function could cause the iterator to hold a reference to a resource that is used // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. diff --git a/tests/ui/recursive_format_impl.stderr b/tests/ui/recursive_format_impl.stderr index 6171696ed69d..1a717ac92d8b 100644 --- a/tests/ui/recursive_format_impl.stderr +++ b/tests/ui/recursive_format_impl.stderr @@ -6,15 +6,6 @@ LL | write!(f, "{}", self.to_string()) | = note: `-D clippy::recursive-format-impl` implied by `-D warnings` -error: unnecessary use of `to_string` - --> $DIR/recursive_format_impl.rs:61:50 - | -LL | Self::E(string) => write!(f, "E {}", string.to_string()), - | ^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings` - = note: this error originates in the macro `$crate::format_args` (in Nightly builds, run with -Z macro-backtrace for more info) - error: using `self` as `Display` in `impl Display` will cause infinite recursion --> $DIR/recursive_format_impl.rs:73:9 | @@ -87,5 +78,5 @@ LL | write!(f, "{}", &&**&&*self) | = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 11 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/unnecessary_to_owned.fixed b/tests/ui/unnecessary_to_owned.fixed index 7455e22d49b2..f4f76cd3dd49 100644 --- a/tests/ui/unnecessary_to_owned.fixed +++ b/tests/ui/unnecessary_to_owned.fixed @@ -78,10 +78,10 @@ fn main() { require_slice(array.as_ref()); require_slice(array_ref.as_ref()); require_slice(slice); - require_slice(x_ref); + require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. require_x(&Cow::::Owned(x.clone())); - require_x(x_ref); + require_x(&x_ref.to_owned()); // No longer flagged because of #8759. require_deref_c_str(c_str); require_deref_os_str(os_str); @@ -152,6 +152,7 @@ fn main() { require_os_str(&OsString::from("x")); require_path(&std::path::PathBuf::from("x")); require_str(&String::from("x")); + require_slice(&[String::from("x")]); } fn require_c_str(_: &CStr) {} @@ -272,3 +273,59 @@ mod issue_8507 { Box::new(build(y)) } } + +// https://github.com/rust-lang/rust-clippy/issues/8759 +mod issue_8759 { + #![allow(dead_code)] + + #[derive(Default)] + struct View {} + + impl std::borrow::ToOwned for View { + type Owned = View; + fn to_owned(&self) -> Self::Owned { + View {} + } + } + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} + +mod issue_8759_variant { + #![allow(dead_code)] + + #[derive(Clone, Default)] + struct View {} + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} diff --git a/tests/ui/unnecessary_to_owned.rs b/tests/ui/unnecessary_to_owned.rs index bbcd00ad2204..fe09a489ab0a 100644 --- a/tests/ui/unnecessary_to_owned.rs +++ b/tests/ui/unnecessary_to_owned.rs @@ -78,10 +78,10 @@ fn main() { require_slice(&array.to_owned()); require_slice(&array_ref.to_owned()); require_slice(&slice.to_owned()); - require_slice(&x_ref.to_owned()); + require_slice(&x_ref.to_owned()); // No longer flagged because of #8759. require_x(&Cow::::Owned(x.clone()).into_owned()); - require_x(&x_ref.to_owned()); + require_x(&x_ref.to_owned()); // No longer flagged because of #8759. require_deref_c_str(c_str.to_owned()); require_deref_os_str(os_str.to_owned()); @@ -152,6 +152,7 @@ fn main() { require_os_str(&OsString::from("x").to_os_string()); require_path(&std::path::PathBuf::from("x").to_path_buf()); require_str(&String::from("x").to_string()); + require_slice(&[String::from("x")].to_owned()); } fn require_c_str(_: &CStr) {} @@ -272,3 +273,59 @@ mod issue_8507 { Box::new(build(y.to_string())) } } + +// https://github.com/rust-lang/rust-clippy/issues/8759 +mod issue_8759 { + #![allow(dead_code)] + + #[derive(Default)] + struct View {} + + impl std::borrow::ToOwned for View { + type Owned = View; + fn to_owned(&self) -> Self::Owned { + View {} + } + } + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} + +mod issue_8759_variant { + #![allow(dead_code)] + + #[derive(Clone, Default)] + struct View {} + + #[derive(Default)] + struct RenderWindow { + default_view: View, + } + + impl RenderWindow { + fn default_view(&self) -> &View { + &self.default_view + } + fn set_view(&mut self, _view: &View) {} + } + + fn main() { + let mut rw = RenderWindow::default(); + rw.set_view(&rw.default_view().to_owned()); + } +} diff --git a/tests/ui/unnecessary_to_owned.stderr b/tests/ui/unnecessary_to_owned.stderr index f9713559e4f7..af7e7b41fb00 100644 --- a/tests/ui/unnecessary_to_owned.stderr +++ b/tests/ui/unnecessary_to_owned.stderr @@ -47,6 +47,18 @@ note: this value is dropped without further use LL | require_str(&String::from("x").to_string()); | ^^^^^^^^^^^^^^^^^ +error: redundant clone + --> $DIR/unnecessary_to_owned.rs:155:39 + | +LL | require_slice(&[String::from("x")].to_owned()); + | ^^^^^^^^^^^ help: remove this + | +note: this value is dropped without further use + --> $DIR/unnecessary_to_owned.rs:155:20 + | +LL | require_slice(&[String::from("x")].to_owned()); + | ^^^^^^^^^^^^^^^^^^^ + error: unnecessary use of `into_owned` --> $DIR/unnecessary_to_owned.rs:60:36 | @@ -151,24 +163,12 @@ error: unnecessary use of `to_owned` LL | require_slice(&slice.to_owned()); | ^^^^^^^^^^^^^^^^^ help: use: `slice` -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:81:19 - | -LL | require_slice(&x_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` - error: unnecessary use of `into_owned` --> $DIR/unnecessary_to_owned.rs:83:42 | LL | require_x(&Cow::::Owned(x.clone()).into_owned()); | ^^^^^^^^^^^^^ help: remove this -error: unnecessary use of `to_owned` - --> $DIR/unnecessary_to_owned.rs:84:15 - | -LL | require_x(&x_ref.to_owned()); - | ^^^^^^^^^^^^^^^^^ help: use: `x_ref` - error: unnecessary use of `to_owned` --> $DIR/unnecessary_to_owned.rs:86:25 | @@ -476,7 +476,7 @@ LL | let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owne | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:197:14 + --> $DIR/unnecessary_to_owned.rs:198:14 | LL | for t in file_types.to_vec() { | ^^^^^^^^^^^^^^^^^^^ @@ -492,22 +492,22 @@ LL + let path = match get_file_path(t) { | error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:220:14 + --> $DIR/unnecessary_to_owned.rs:221:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()` error: unnecessary use of `to_vec` - --> $DIR/unnecessary_to_owned.rs:225:14 + --> $DIR/unnecessary_to_owned.rs:226:14 | LL | let _ = &["x"][..].to_vec().into_iter(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()` error: unnecessary use of `to_string` - --> $DIR/unnecessary_to_owned.rs:272:24 + --> $DIR/unnecessary_to_owned.rs:273:24 | LL | Box::new(build(y.to_string())) | ^^^^^^^^^^^^^ help: use: `y` -error: aborting due to 79 previous errors +error: aborting due to 78 previous errors From 905a95171894826c58b1f731780f6df7b9b5eef0 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Fri, 6 May 2022 13:21:08 +0100 Subject: [PATCH 455/536] Pass through extra args in `cargo dev lint` --- clippy_dev/src/lint.rs | 5 ++++- clippy_dev/src/main.rs | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/clippy_dev/src/lint.rs b/clippy_dev/src/lint.rs index 1bc1a39542db..9e463aa741c7 100644 --- a/clippy_dev/src/lint.rs +++ b/clippy_dev/src/lint.rs @@ -13,7 +13,7 @@ fn exit_if_err(status: io::Result) { } } -pub fn run(path: &str) { +pub fn run<'a>(path: &str, args: impl Iterator) { let is_file = match fs::metadata(path) { Ok(metadata) => metadata.is_file(), Err(e) => { @@ -30,6 +30,7 @@ pub fn run(path: &str) { .args(["-Z", "no-codegen"]) .args(["--edition", "2021"]) .arg(path) + .args(args) .status(), ); } else { @@ -42,6 +43,8 @@ pub fn run(path: &str) { .expect("failed to create tempdir"); let status = Command::new(cargo_clippy_path()) + .arg("clippy") + .args(args) .current_dir(path) .env("CARGO_TARGET_DIR", target.as_ref()) .status(); diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index ebf8f38d4906..dcfaabbc204b 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -76,7 +76,8 @@ fn main() { }, ("lint", Some(matches)) => { let path = matches.value_of("path").unwrap(); - lint::run(path); + let args = matches.values_of("args").into_iter().flatten(); + lint::run(path, args); }, ("rename_lint", Some(matches)) => { let old_name = matches.value_of("old_name").unwrap(); @@ -278,11 +279,23 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { Lint a package directory: cargo dev lint tests/ui-cargo/wildcard_dependencies/fail cargo dev lint ~/my-project + + Run rustfix: + cargo dev lint ~/my-project -- --fix + + Set lint levels: + cargo dev lint file.rs -- -W clippy::pedantic + cargo dev lint ~/my-project -- -- -W clippy::pedantic "}) .arg( Arg::with_name("path") .required(true) .help("The path to a file or package directory to lint"), + ) + .arg( + Arg::with_name("args") + .multiple(true) + .help("Pass extra arguments to cargo/clippy-driver"), ), ) .subcommand( From 5d0ca74c753d72bf5b6dcfa532001cdd07aa0365 Mon Sep 17 00:00:00 2001 From: tamaron Date: Fri, 29 Apr 2022 18:34:58 +0900 Subject: [PATCH 456/536] Resolved conflicts --- .../src/undocumented_unsafe_blocks.rs | 64 ++++++++++- tests/ui/undocumented_unsafe_blocks.rs | 101 +++++++++++++++++- tests/ui/undocumented_unsafe_blocks.stderr | 50 ++++++++- 3 files changed, 212 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 465d8a914fb2..63652dba3980 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -2,12 +2,14 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; use rustc_data_structures::sync::Lrc; +use rustc_hir as hir; use rustc_hir::{Block, BlockCheckMode, UnsafeSource}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{BytePos, Pos, SyntaxContext}; +use rustc_span::{BytePos, Pos, Span, SyntaxContext}; +use std::rc::Rc; declare_clippy_lint! { /// ### What it does @@ -86,6 +88,66 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { ); } } + + fn check_mod(&mut self, cx: &LateContext<'_>, module: &'_ hir::Mod<'_>, mod_span: Span, hir_id: hir::HirId) { + let source_map = cx.sess().source_map(); + let mut item_and_spans: Vec<(&hir::Item<'_>, Span)> = Vec::new(); // (start, end, item) + + // Collect all items and their spans + for item_id in module.item_ids { + let item = cx.tcx.hir().item(*item_id); + item_and_spans.push((item, item.span)); + } + // Sort items by start position + item_and_spans.sort_by_key(|e| e.1.lo()); + + for (idx, (item, item_span)) in item_and_spans.iter().enumerate() { + if let hir::ItemKind::Impl(imple) = &item.kind + && imple.unsafety == hir::Unsafety::Unsafe + && !item_span.from_expansion() + && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, hir_id) + { + // Checks if the lines immediately preceding the impl contain a safety comment. + let impl_has_safety_comment = { + let span_before_impl = if idx == 0 { + // mod A { /* comment */ unsafe impl T {} } + // ^--------------------^ + todo!(); + //mod_span.until(module.spans) + } else { + // unsafe impl S {} /* comment */ unsafe impl T {} + // ^-------------^ + item_and_spans[idx - 1].1.between(*item_span) + }; + + if let Ok(start) = source_map.lookup_line(span_before_impl.lo()) + && let Ok(end) = source_map.lookup_line(span_before_impl.hi()) + && let Some(src) = start.sf.src.as_deref() + { + start.line < end.line && text_has_safety_comment( + src, + &start.sf.lines[start.line + 1 ..= end.line], + start.sf.start_pos.to_usize() + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } + }; + + if !impl_has_safety_comment { + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + *item_span, + "unsafe impl missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); + } + } + } + } } fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 7be15b0b2dd3..3044a92cbd04 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -1,7 +1,7 @@ // aux-build:proc_macro_unsafe.rs #![warn(clippy::undocumented_unsafe_blocks)] -#![allow(clippy::let_unit_value)] +#![allow(clippy::let_unit_value, clippy::missing_safety_doc)] extern crate proc_macro_unsafe; @@ -334,4 +334,103 @@ pub fn print_binary_tree() { println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); } +mod unsafe_impl_smoke_test { + unsafe trait A {} + + // error: no safety comment + unsafe impl A for () {} + + // Safety: ok + unsafe impl A for (i32) {} + + mod sub_mod { + // error: also works for the first item + unsafe impl B for (u32) {} + unsafe trait B {} + } + + #[rustfmt::skip] + mod sub_mod2 { + // + // SAFETY: ok + // + + unsafe impl B for (u32) {} + unsafe trait B {} + } +} + +mod unsafe_impl_from_macro { + unsafe trait T {} + + macro_rules! unsafe_impl { + ($t:ty) => { + unsafe impl T for $t {} + }; + } + // ok: from macro expanision + unsafe_impl!(()); + // ok: from macro expansion + unsafe_impl!(i32); +} + +#[rustfmt::skip] +mod unsafe_impl_valid_comment { + unsafe trait SaFety {} + // SaFety: + unsafe impl SaFety for () {} + + unsafe trait MultiLineComment {} + // The following impl is safe + // ... + // Safety: reason + unsafe impl MultiLineComment for () {} + + unsafe trait NoAscii {} + // 安全 SAFETY: 以下のコードは安全です + unsafe impl NoAscii for () {} + + unsafe trait InlineAndPrecedingComment {} + // SAFETY: + /* comment */ unsafe impl InlineAndPrecedingComment for () {} + + unsafe trait BuriedSafety {} + // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor + // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint + // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + // laborum. Safety: + // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi + // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio + // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl + // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus. + unsafe impl BuriedSafety for () {} + + unsafe trait MultiLineBlockComment {} + /* This is a description + * Safety: */ + unsafe impl MultiLineBlockComment for () {} +} + +#[rustfmt::skip] +mod unsafe_impl_invalid_comment { + unsafe trait NoComment {} + + unsafe impl NoComment for () {} + + unsafe trait InlineComment {} + + /* SAFETY: */ unsafe impl InlineComment for () {} + + unsafe trait TrailingComment {} + + unsafe impl TrailingComment for () {} // SAFETY: + + unsafe trait Interference {} + // SAFETY: + const BIG_NUMBER: i32 = 1000000; + unsafe impl Interference for () {} +} + fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 87d445bd7b86..80d68a038087 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -147,5 +147,53 @@ LL | println!("{}", unsafe { String::from_utf8_unchecked(vec![]) }); | = help: consider adding a safety comment on the preceding line -error: aborting due to 18 previous errors +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:341:5 + | +LL | unsafe impl A for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:348:9 + | +LL | unsafe impl B for (u32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:420:5 + | +LL | unsafe impl NoComment for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:424:19 + | +LL | /* SAFETY: */ unsafe impl InlineComment for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:428:5 + | +LL | unsafe impl TrailingComment for () {} // SAFETY: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:433:5 + | +LL | unsafe impl Interference for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 24 previous errors From c09778dd21771eee53c32f53a570c53757d46ca2 Mon Sep 17 00:00:00 2001 From: Miguel Guarniz Date: Fri, 29 Apr 2022 13:11:22 -0400 Subject: [PATCH 457/536] use def_span and def_kind queries instead of calling tcx.hir() methods Signed-off-by: Miguel Guarniz --- clippy_lints/src/same_name_method.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/same_name_method.rs b/clippy_lints/src/same_name_method.rs index f63925a2f143..c5c174cc8f61 100644 --- a/clippy_lints/src/same_name_method.rs +++ b/clippy_lints/src/same_name_method.rs @@ -51,14 +51,14 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { let mut map = FxHashMap::::default(); for id in cx.tcx.hir().items() { - if matches!(cx.tcx.hir().def_kind(id.def_id), DefKind::Impl) + if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl) && let item = cx.tcx.hir().item(id) && let ItemKind::Impl(Impl { - items, - of_trait, - self_ty, - .. - }) = &item.kind + items, + of_trait, + self_ty, + .. + }) = &item.kind && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { if !map.contains_key(res) { From 41c7e4d3822c1a4d3d93fe64cb2d70d13d0687b5 Mon Sep 17 00:00:00 2001 From: Preston From Date: Fri, 18 Feb 2022 00:02:22 -0600 Subject: [PATCH 458/536] Lint for significant drops who may have surprising lifetimes #1 author Preston From 1645164142 -0600 committer Preston From 1650005351 -0600 --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_nursery.rs | 1 + clippy_lints/src/lib.rs | 2 + .../src/significant_drop_in_scrutinee.rs | 408 ++++++++++++++ clippy_utils/src/attrs.rs | 1 + tests/ui/significant_drop_in_scrutinee.rs | 526 ++++++++++++++++++ tests/ui/significant_drop_in_scrutinee.stderr | 261 +++++++++ 8 files changed, 1201 insertions(+) create mode 100644 clippy_lints/src/significant_drop_in_scrutinee.rs create mode 100644 tests/ui/significant_drop_in_scrutinee.rs create mode 100644 tests/ui/significant_drop_in_scrutinee.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 751f9fccd88d..d25ad0ac6fa1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3696,6 +3696,7 @@ Released 2018-09-13 [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive +[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 5768edc50188..c888a5feda2a 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -475,6 +475,7 @@ store.register_lints(&[ size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, stable_sort_primitive::STABLE_SORT_PRIMITIVE, + significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE, strings::STRING_ADD, strings::STRING_ADD_ASSIGN, strings::STRING_FROM_UTF8_AS_BYTES, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index ec187563b3f6..d43c7e03533c 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -25,6 +25,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE), LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE), LintId::of(regex::TRIVIAL_REGEX), + LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE), LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS), LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3bb821a14829..09071a255c5e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -364,6 +364,7 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; +mod significant_drop_in_scrutinee; mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; @@ -874,6 +875,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion)); + store.register_late_pass(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee)); store.register_late_pass(|| Box::new(dbg_macro::DbgMacro)); let cargo_ignore_publish = conf.cargo_ignore_publish; store.register_late_pass(move || { diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs new file mode 100644 index 000000000000..94ae0c8f5a65 --- /dev/null +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -0,0 +1,408 @@ +use crate::FxHashSet; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::get_attr; +use clippy_utils::source::{indent_of, snippet}; +use rustc_errors::{Applicability, Diagnostic}; +use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{Ty, TypeAndMut}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Check for temporaries returned from function calls in a match scrutinee that have the + /// `clippy::has_significant_drop` attribute. + /// + /// ### Why is this bad? + /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have + /// an important side-effect, such as unlocking a mutex, making it important for users to be + /// able to accurately understand their lifetimes. When a temporary is returned in a function + /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may + /// be surprising. + /// + /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a + /// function call that returns a `MutexGuard` and then tries to lock again in one of the match + /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of + /// the match block and thus will not unlock. + /// + /// ### Example + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// + /// let mutex = Mutex::new(State {}); + /// + /// match mutex.lock().unwrap().foo() { + /// true => { + /// mutex.lock().unwrap().bar(); // Deadlock! + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// + /// ``` + /// Use instead: + /// ```rust + /// # use std::sync::Mutex; + /// + /// # struct State {} + /// + /// # impl State { + /// # fn foo(&self) -> bool { + /// # true + /// # } + /// + /// # fn bar(&self) {} + /// # } + /// + /// let mutex = Mutex::new(State {}); + /// + /// let is_foo = mutex.lock().unwrap().foo(); + /// match is_foo { + /// true => { + /// mutex.lock().unwrap().bar(); + /// } + /// false => {} + /// }; + /// + /// println!("All done!"); + /// ``` + #[clippy::version = "1.60.0"] + pub SIGNIFICANT_DROP_IN_SCRUTINEE, + nursery, + "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime" +} + +declare_lint_pass!(SignificantDropInScrutinee => [SIGNIFICANT_DROP_IN_SCRUTINEE]); + +impl<'tcx> LateLintPass<'tcx> for SignificantDropInScrutinee { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let Some(suggestions) = has_significant_drop_in_scrutinee(cx, expr) { + for found in suggestions { + span_lint_and_then( + cx, + SIGNIFICANT_DROP_IN_SCRUTINEE, + found.found_span, + "temporary with significant drop in match scrutinee", + |diag| set_diagnostic(diag, cx, expr, found), + ) + } + } + } +} + +fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) { + if found.lint_suggestion == LintSuggestion::MoveAndClone { + // If our suggestion is to move and clone, then we want to leave it to the user to + // decide how to address this lint, since it may be that cloning is inappropriate. + // Therefore, we won't to emit a suggestion. + return; + } + + let original = snippet(cx, found.found_span, ".."); + let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0)); + + let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy { + format!("let value = *{};\n{}", original, trailing_indent) + } else if found.is_unit_return_val { + // If the return value of the expression to be moved is unit, then we don't need to + // capture the result in a temporary -- we can just replace it completely with `()`. + format!("{};\n{}", original, trailing_indent) + } else { + format!("let value = {};\n{}", original, trailing_indent) + }; + + let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly { + "try moving the temporary above the match" + } else { + "try moving the temporary above the match and create a copy" + }; + + let scrutinee_replacement = if found.is_unit_return_val { + "()".to_owned() + } else { + "value".to_owned() + }; + + diag.multipart_suggestion( + suggestion_message, + vec![ + (expr.span.shrink_to_lo(), replacement), + (found.found_span, scrutinee_replacement), + ], + Applicability::MaybeIncorrect, + ); +} + +/// If the expression is an ExprKind::Match, check if the scrutinee has a significant drop that may +/// have a surprising lifetime. +fn has_significant_drop_in_scrutinee<'tcx, 'a>( + cx: &'a LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option> { + let mut helper = SigDropHelper::new(cx); + match expr.kind { + ExprKind::Match(match_expr, _, _) => helper.find_sig_drop(match_expr), + _ => None, + } +} + +struct SigDropHelper<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + is_chain_end: bool, + seen_types: FxHashSet>, + has_significant_drop: bool, + current_sig_drop: Option, + sig_drop_spans: Option>, + special_handling_for_binary_op: bool, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +enum LintSuggestion { + MoveOnly, + MoveAndDerefToCopy, + MoveAndClone, +} + +#[derive(Clone, Copy)] +struct FoundSigDrop { + found_span: Span, + is_unit_return_val: bool, + lint_suggestion: LintSuggestion, +} + +impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> { + SigDropHelper { + cx, + is_chain_end: true, + seen_types: FxHashSet::default(), + has_significant_drop: false, + current_sig_drop: None, + sig_drop_spans: None, + special_handling_for_binary_op: false, + } + } + + fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option> { + self.visit_expr(match_expr); + + // If sig drop spans is empty but we found a significant drop, it means that we didn't find + // a type that was trivially copyable as we moved up the chain after finding a significant + // drop, so move the entire scrutinee. + if self.has_significant_drop && self.sig_drop_spans.is_none() { + self.try_setting_current_suggestion(match_expr, true); + self.move_current_suggestion(); + } + + self.sig_drop_spans.take() + } + + /// This will try to set the current suggestion (so it can be moved into the suggestions vec + /// later). If allow_move_and_clone is false, the suggestion *won't* be set -- this gives us + /// an opportunity to look for another type in the chain that will be trivially copyable. + /// However, if we are at the the end of the chain, we want to accept whatever is there. (The + /// suggestion won't actually be output, but the diagnostic message will be output, so the user + /// can determine the best way to handle the lint.) + fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) { + if self.current_sig_drop.is_some() { + return; + } + let ty = self.get_type(expr); + if ty.is_ref() { + // We checked that the type was ref, so builtin_deref will return Some TypeAndMut, + // but let's avoid any chance of an ICE + if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) { + if ty.is_trivially_pure_clone_copy() { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveAndDerefToCopy, + }); + } else if allow_move_and_clone { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveAndClone, + }); + } + } + } else if ty.is_trivially_pure_clone_copy() { + self.current_sig_drop.replace(FoundSigDrop { + found_span: expr.span, + is_unit_return_val: false, + lint_suggestion: LintSuggestion::MoveOnly, + }); + } + } + + fn move_current_suggestion(&mut self) { + if let Some(current) = self.current_sig_drop.take() { + self.sig_drop_spans.get_or_insert_with(Vec::new).push(current); + } + } + + fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> { + self.cx.typeck_results().expr_ty(ex) + } + + fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool { + !self.seen_types.insert(ty) + } + + fn visit_exprs_for_binary_ops( + &mut self, + left: &'tcx Expr<'_>, + right: &'tcx Expr<'_>, + is_unit_return_val: bool, + span: Span, + ) { + self.special_handling_for_binary_op = true; + self.visit_expr(left); + self.visit_expr(right); + + // If either side had a significant drop, suggest moving the entire scrutinee to avoid + // unnecessary copies and to simplify cases where both sides have significant drops. + if self.has_significant_drop { + self.current_sig_drop.replace(FoundSigDrop { + found_span: span, + is_unit_return_val, + lint_suggestion: LintSuggestion::MoveOnly, + }); + } + + self.special_handling_for_binary_op = false; + } + + fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + if let Some(adt) = ty.ty_adt_def() { + if get_attr(cx.sess(), cx.tcx.get_attrs(adt.did()), "has_significant_drop").count() > 0 { + return true; + } + } + + match ty.kind() { + rustc_middle::ty::Adt(a, b) => { + for f in a.all_fields() { + let ty = f.ty(cx.tcx, b); + if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) { + return true; + } + } + + for generic_arg in b.iter() { + if let GenericArgKind::Type(ty) = generic_arg.unpack() { + if self.has_sig_drop_attr(cx, ty) { + return true; + } + } + } + false + }, + rustc_middle::ty::Array(ty, _) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::RawPtr(TypeAndMut { ty, .. }) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Ref(_, ty, _) => self.has_sig_drop_attr(cx, *ty), + rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty), + _ => false, + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> { + fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { + if !self.is_chain_end && self.has_sig_drop_attr(self.cx, self.get_type(ex)) { + self.has_significant_drop = true; + return; + } + self.is_chain_end = false; + + match ex.kind { + ExprKind::MethodCall(_, [ref expr, ..], _) => { + self.visit_expr(expr) + } + ExprKind::Binary(_, left, right) => { + self.visit_exprs_for_binary_ops(left, right, false, ex.span); + } + ExprKind::Assign(left, right, _) => { + self.visit_exprs_for_binary_ops(left, right, true, ex.span); + } + ExprKind::AssignOp(_, left, right) => { + self.visit_exprs_for_binary_ops(left, right, true, ex.span); + } + ExprKind::Tup(exprs) => { + for expr in exprs { + self.visit_expr(expr); + if self.has_significant_drop { + // We may have not have set current_sig_drop if all the suggestions were + // MoveAndClone, so add this tuple item's full expression in that case. + if self.current_sig_drop.is_none() { + self.try_setting_current_suggestion(expr, true); + } + + // Now we are guaranteed to have something, so add it to the final vec. + self.move_current_suggestion(); + } + // Reset `has_significant_drop` after each tuple expression so we can look for + // additional cases. + self.has_significant_drop = false; + } + if self.sig_drop_spans.is_some() { + self.has_significant_drop = true; + } + } + ExprKind::Box(..) | + ExprKind::Array(..) | + ExprKind::Call(..) | + ExprKind::Unary(..) | + ExprKind::If(..) | + ExprKind::Match(..) | + ExprKind::Field(..) | + ExprKind::Index(..) | + ExprKind::Ret(..) | + ExprKind::Repeat(..) | + ExprKind::Yield(..) | + ExprKind::MethodCall(..) => walk_expr(self, ex), + ExprKind::AddrOf(_, _, _) | + ExprKind::Block(_, _) | + ExprKind::Break(_, _) | + ExprKind::Cast(_, _) | + // Don't want to check the closure itself, only invocation, which is covered by MethodCall + ExprKind::Closure(_, _, _, _, _) | + ExprKind::ConstBlock(_) | + ExprKind::Continue(_) | + ExprKind::DropTemps(_) | + ExprKind::Err | + ExprKind::InlineAsm(_) | + ExprKind::Let(_) | + ExprKind::Lit(_) | + ExprKind::Loop(_, _, _, _) | + ExprKind::Path(_) | + ExprKind::Struct(_, _, _) | + ExprKind::Type(_, _) => { + return; + } + } + + // Once a significant temporary has been found, we need to go back up at least 1 level to + // find the span to extract for replacement, so the temporary gets dropped. However, for + // binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to + // simplify cases where both sides have significant drops. + if self.has_significant_drop && !self.special_handling_for_binary_op { + self.try_setting_current_suggestion(ex, false); + } + } +} diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 25a84d166508..7f448175e326 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -22,6 +22,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[ ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")), ("dump", DeprecationStatus::None), ("msrv", DeprecationStatus::None), + ("has_significant_drop", DeprecationStatus::None), ]; pub struct LimitStack { diff --git a/tests/ui/significant_drop_in_scrutinee.rs b/tests/ui/significant_drop_in_scrutinee.rs new file mode 100644 index 000000000000..c4a3301e7226 --- /dev/null +++ b/tests/ui/significant_drop_in_scrutinee.rs @@ -0,0 +1,526 @@ +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934 +// // run-rustfix + +#![warn(clippy::significant_drop_in_scrutinee)] +#![allow(clippy::single_match)] +#![allow(clippy::match_single_binding)] +#![allow(unused_assignments)] +#![allow(dead_code)] + +use std::ops::Deref; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Mutex, MutexGuard}; + +struct State {} + +impl State { + fn foo(&self) -> bool { + true + } + + fn bar(&self) {} +} + +fn should_not_trigger_lint_with_mutex_guard_outside_match() { + let mutex = Mutex::new(State {}); + + // Should not trigger lint because the temporary should drop at the `;` on line before the match + let is_foo = mutex.lock().unwrap().foo(); + match is_foo { + true => { + mutex.lock().unwrap().bar(); + } + false => {} + }; +} + +fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() { + let mutex = Mutex::new(State {}); + + // Should not trigger lint because the scrutinee is explicitly returning the MutexGuard, + // so its lifetime should not be surprising. + match mutex.lock() { + Ok(guard) => { + guard.foo(); + mutex.lock().unwrap().bar(); + } + _ => {} + }; +} + +fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() { + let mutex = Mutex::new(State {}); + + // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it + // is preserved until the end of the match, but there is no clear indication that this is the + // case. + match mutex.lock().unwrap().foo() { + true => { + mutex.lock().unwrap().bar(); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_insignificant_drop() { + // Should not trigger lint because there are no temporaries whose drops have a significant + // side effect. + match 1u64.to_string().is_empty() { + true => { + println!("It was empty") + } + false => { + println!("It was not empty") + } + } +} + +struct StateWithMutex { + m: Mutex, +} + +struct MutexGuardWrapper<'a> { + mg: MutexGuard<'a, u64>, +} + +impl<'a> MutexGuardWrapper<'a> { + fn get_the_value(&self) -> u64 { + *self.mg.deref() + } +} + +struct MutexGuardWrapperWrapper<'a> { + mg: MutexGuardWrapper<'a>, +} + +impl<'a> MutexGuardWrapperWrapper<'a> { + fn get_the_value(&self) -> u64 { + *self.mg.mg.deref() + } +} + +impl StateWithMutex { + fn lock_m(&self) -> MutexGuardWrapper<'_> { + MutexGuardWrapper { + mg: self.m.lock().unwrap(), + } + } + + fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> { + MutexGuardWrapperWrapper { + mg: MutexGuardWrapper { + mg: self.m.lock().unwrap(), + }, + } + } + + fn foo(&self) -> bool { + true + } + + fn bar(&self) {} +} + +fn should_trigger_lint_with_wrapped_mutex() { + let s = StateWithMutex { m: Mutex::new(1) }; + + // Should trigger lint because a temporary contains a type with a significant drop and its + // lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that + // the temporary contains such a type, making it potentially even more surprising. + match s.lock_m().get_the_value() { + 1 => { + println!("Got 1. Is it still 1?"); + println!("{}", s.lock_m().get_the_value()); + } + 2 => { + println!("Got 2. Is it still 2?"); + println!("{}", s.lock_m().get_the_value()); + } + _ => {} + } + println!("All done!"); +} + +fn should_trigger_lint_with_double_wrapped_mutex() { + let s = StateWithMutex { m: Mutex::new(1) }; + + // Should trigger lint because a temporary contains a type which further contains a type with a + // significant drop and its lifetime is not obvious. Additionally, it is not obvious from + // looking at the scrutinee that the temporary contains such a type, making it potentially even + // more surprising. + match s.lock_m_m().get_the_value() { + 1 => { + println!("Got 1. Is it still 1?"); + println!("{}", s.lock_m().get_the_value()); + } + 2 => { + println!("Got 2. Is it still 2?"); + println!("{}", s.lock_m().get_the_value()); + } + _ => {} + } + println!("All done!"); +} + +struct Counter { + i: AtomicU64, +} + +#[clippy::has_significant_drop] +struct CounterWrapper<'a> { + counter: &'a Counter, +} + +impl<'a> CounterWrapper<'a> { + fn new(counter: &Counter) -> CounterWrapper { + counter.i.fetch_add(1, Ordering::Relaxed); + CounterWrapper { counter } + } +} + +impl<'a> Drop for CounterWrapper<'a> { + fn drop(&mut self) { + self.counter.i.fetch_sub(1, Ordering::Relaxed); + } +} + +impl Counter { + fn temp_increment(&self) -> Vec { + vec![CounterWrapper::new(self), CounterWrapper::new(self)] + } +} + +fn should_trigger_lint_for_vec() { + let counter = Counter { i: AtomicU64::new(0) }; + + // Should trigger lint because the temporary in the scrutinee returns a collection of types + // which have significant drops. The types with significant drops are also non-obvious when + // reading the expression in the scrutinee. + match counter.temp_increment().len() { + 2 => { + let current_count = counter.i.load(Ordering::Relaxed); + println!("Current count {}", current_count); + assert_eq!(current_count, 0); + } + 1 => {} + 3 => {} + _ => {} + }; +} + +struct StateWithField { + s: String, +} + +// Should trigger lint only on the type in the tuple which is created using a temporary +// with a significant drop. Additionally, this test ensures that the format of the tuple +// is preserved correctly in the suggestion. +fn should_trigger_lint_for_tuple_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + { + match (mutex1.lock().unwrap().s.len(), true) { + (3, _) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + println!("done"); + } + (_, _) => {} + }; + + match (true, mutex1.lock().unwrap().s.len(), true) { + (_, 3, _) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + println!("done"); + } + (_, _, _) => {} + }; + + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + (3, _, 3) => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + (_, _, _) => {} + }; + + let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() }); + match mutex3.lock().unwrap().s.as_str() { + "three" => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + _ => {} + }; + + + match (true, mutex3.lock().unwrap().s.as_str()) { + (_, "three") => { + println!("started"); + mutex1.lock().unwrap().s.len(); + mutex2.lock().unwrap().s.len(); + println!("done"); + } + (_, _) => {} + }; + } +} + +// Should trigger lint when either side of a binary operation creates a temporary with a +// significant drop. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() { + let mutex = Mutex::new(StateWithField { s: "state".to_owned() }); + + match mutex.lock().unwrap().s.len() > 1 { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; + + match 1 < mutex.lock().unwrap().s.len() { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; +} + +// Should trigger lint when both sides of a binary operation creates a temporary with a +// significant drop. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() { + let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "statewithfield".to_owned() }); + + match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { + true => { + println!("{} < {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); + } + false => {} + }; + + match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { + true => { + println!("{} >= {}", mutex1.lock().unwrap().s.len(), mutex2.lock().unwrap().s.len()); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_closure_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + let get_mutex_guard = || mutex1.lock().unwrap().s.len(); + + // Should not trigger lint because the temporary with a significant drop will be dropped + // at the end of the closure, so the MutexGuard will be unlocked and not have a potentially + // surprising lifetime. + match get_mutex_guard() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_trigger_lint_for_return_from_closure_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + + let get_mutex_guard = || mutex1.lock().unwrap(); + + // Should trigger lint because the temporary with a significant drop is returned from the + // closure but not used directly in any match arms, so it has a potentially surprising lifetime. + match get_mutex_guard().s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_trigger_lint_for_return_from_match_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + + let i = 100; + + // Should trigger lint because the nested match within the scrutinee returns a temporary with a + // significant drop is but not used directly in any match arms, so it has a potentially + // surprising lifetime. + match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => { + println!("nothing to do here"); + } + }; +} + +fn should_trigger_lint_for_return_from_if_in_scrutinee() { + let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() }); + let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() }); + + let i = 100; + + // Should trigger lint because the nested if-expression within the scrutinee returns a temporary + // with a significant drop is but not used directly in any match arms, so it has a potentially + // surprising lifetime. + match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { + true => { + mutex1.lock().unwrap().s.len(); + } + false => {} + }; +} + +fn should_not_trigger_lint_for_if_in_scrutinee() { + let mutex = Mutex::new(StateWithField { s: "state".to_owned() }); + + let i = 100; + + // Should not trigger the lint because the temporary with a significant drop *is* dropped within + // the body of the if-expression nested within the match scrutinee, and therefore does not have + // a potentially surprising lifetime. + match if i > 1 { mutex.lock().unwrap().s.len() > 1 } else { false } { + true => { + mutex.lock().unwrap().s.len(); + } + false => {} + }; +} + +struct StateWithBoxedMutexGuard { + u: Mutex, +} + +impl StateWithBoxedMutexGuard { + fn new() -> StateWithBoxedMutexGuard { + StateWithBoxedMutexGuard { u: Mutex::new(42) } + } + fn lock(&self) -> Box> { + Box::new(self.u.lock().unwrap()) + } +} + +fn should_trigger_lint_for_boxed_mutex_guard() { + let s = StateWithBoxedMutexGuard::new(); + + // Should trigger lint because a temporary Box holding a type with a significant drop in a match + // scrutinee may have a potentially surprising lifetime. + match s.lock().deref().deref() { + 0 | 1 => println!("Value was less than 2"), + _ => println!("Value is {}", s.lock().deref()), + }; +} + +struct StateStringWithBoxedMutexGuard { + s: Mutex, +} + +impl StateStringWithBoxedMutexGuard { + fn new() -> StateStringWithBoxedMutexGuard { + StateStringWithBoxedMutexGuard { s: Mutex::new("A String".to_owned()) } + } + fn lock(&self) -> Box> { + Box::new(self.s.lock().unwrap()) + } +} + +fn should_trigger_lint_for_boxed_mutex_guard_holding_string() { + let s = StateStringWithBoxedMutexGuard::new(); + + let matcher = String::from("A String"); + + // Should trigger lint because a temporary Box holding a type with a significant drop in a match + // scrutinee may have a potentially surprising lifetime. + match s.lock().deref().deref() { + matcher => println!("Value is {}", s.lock().deref()), + _ => println!("Value was not a match"), + }; +} + + +struct StateWithIntField { + i: u64, +} + +// Should trigger lint when either side of an assign expression contains a temporary with a +// significant drop, because the temporary's lifetime will be extended to the end of the match. +// To avoid potential unnecessary copies or creating references that would trigger the significant +// drop problem, the lint recommends moving the entire binary operation. +fn should_trigger_lint_in_assign_expr() { + let mutex = Mutex::new(StateWithIntField { i: 10 }); + + let mut i = 100; + + match mutex.lock().unwrap().i = i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match i = mutex.lock().unwrap().i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match mutex.lock().unwrap().i += 1 { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; + + match i += mutex.lock().unwrap().i { + _ => { + println!("{}", mutex.lock().unwrap().i); + } + }; +} + +#[derive(Debug)] +enum RecursiveEnum { + Foo(Option>) +} + +#[derive(Debug)] +enum GenericRecursiveEnum { + Foo(T, Option>>) +} + +fn should_not_cause_stack_overflow() { + // Test that when a type recursively contains itself, a stack overflow does not occur when + // checking sub-types for significant drops. + let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None)))); + match f { + RecursiveEnum::Foo(Some(f)) => { + println!("{:?}", f) + } + RecursiveEnum::Foo(f) => { + println!("{:?}", f) + } + } + + let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None)))); + match f { + GenericRecursiveEnum::Foo(i, Some(f)) => { + println!("{} {:?}", i, f) + } + GenericRecursiveEnum::Foo(i, f) => { + println!("{} {:?}", i, f) + } + } +} + +fn main() {} diff --git a/tests/ui/significant_drop_in_scrutinee.stderr b/tests/ui/significant_drop_in_scrutinee.stderr new file mode 100644 index 000000000000..c442e93f539a --- /dev/null +++ b/tests/ui/significant_drop_in_scrutinee.stderr @@ -0,0 +1,261 @@ +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:57:11 + | +LL | match mutex.lock().unwrap().foo() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::significant-drop-in-scrutinee` implied by `-D warnings` +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().foo(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:130:11 + | +LL | match s.lock_m().get_the_value() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = s.lock_m().get_the_value(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:151:11 + | +LL | match s.lock_m_m().get_the_value() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = s.lock_m_m().get_the_value(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:199:11 + | +LL | match counter.temp_increment().len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = counter.temp_increment().len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:222:16 + | +LL | match (mutex1.lock().unwrap().s.len(), true) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (value, true) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:231:22 + | +LL | match (true, mutex1.lock().unwrap().s.len(), true) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (true, value, true) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:241:16 + | +LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len(); +LL ~ match (value, true, mutex2.lock().unwrap().s.len()) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:241:54 + | +LL | match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex2.lock().unwrap().s.len(); +LL ~ match (mutex1.lock().unwrap().s.len(), true, value) { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:252:15 + | +LL | match mutex3.lock().unwrap().s.as_str() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:263:22 + | +LL | match (true, mutex3.lock().unwrap().s.as_str()) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:282:11 + | +LL | match mutex.lock().unwrap().s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:289:11 + | +LL | match 1 < mutex.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = 1 < mutex.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:305:11 + | +LL | match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:312:11 + | +LL | match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:343:11 + | +LL | match get_mutex_guard().s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = get_mutex_guard().s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:360:11 + | +LL | match match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = match i { 100 => mutex1.lock().unwrap(), _ => mutex2.lock().unwrap() }.s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:379:11 + | +LL | match if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ let value = if i > 1 { mutex1.lock().unwrap() } else { mutex2.lock().unwrap() }.s.len() > 1; +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:421:11 + | +LL | match s.lock().deref().deref() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match and create a copy + | +LL ~ let value = *s.lock().deref().deref(); +LL ~ match value { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:447:11 + | +LL | match s.lock().deref().deref() { + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:467:11 + | +LL | match mutex.lock().unwrap().i = i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ mutex.lock().unwrap().i = i; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:473:11 + | +LL | match i = mutex.lock().unwrap().i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ i = mutex.lock().unwrap().i; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:479:11 + | +LL | match mutex.lock().unwrap().i += 1 { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ mutex.lock().unwrap().i += 1; +LL ~ match () { + | + +error: temporary with significant drop in match scrutinee + --> $DIR/significant_drop_in_scrutinee.rs:485:11 + | +LL | match i += mutex.lock().unwrap().i { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try moving the temporary above the match + | +LL ~ i += mutex.lock().unwrap().i; +LL ~ match () { + | + +error: aborting due to 23 previous errors + From 8d8588941ea4dfcbd67b4682ef40d7b409df63e6 Mon Sep 17 00:00:00 2001 From: tamaron Date: Sat, 7 May 2022 18:18:57 +0900 Subject: [PATCH 459/536] fix --- .../src/undocumented_unsafe_blocks.rs | 216 +++++++++++------- tests/ui/undocumented_unsafe_blocks.rs | 17 +- tests/ui/undocumented_unsafe_blocks.stderr | 22 +- 3 files changed, 161 insertions(+), 94 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 63652dba3980..09efb12f5daa 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,19 +1,18 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; +use clippy_utils::{get_parent_node, is_lint_allowed}; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; -use rustc_hir::{Block, BlockCheckMode, UnsafeSource}; +use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{BytePos, Pos, Span, SyntaxContext}; -use std::rc::Rc; declare_clippy_lint! { /// ### What it does - /// Checks for `unsafe` blocks without a `// SAFETY: ` comment + /// Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment /// explaining why the unsafe operations performed inside /// the block are safe. /// @@ -36,7 +35,7 @@ declare_clippy_lint! { /// ``` /// /// ### Why is this bad? - /// Undocumented unsafe blocks can make it difficult to + /// Undocumented unsafe blocks and impls can make it difficult to /// read and maintain code, as well as uncover unsoundness /// and bugs. /// @@ -68,7 +67,7 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) - && !is_unsafe_from_proc_macro(cx, block) + && !is_unsafe_from_proc_macro(cx, block.span) && !block_has_safety_comment(cx, block) { let source_map = cx.tcx.sess.source_map(); @@ -89,70 +88,36 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } } - fn check_mod(&mut self, cx: &LateContext<'_>, module: &'_ hir::Mod<'_>, mod_span: Span, hir_id: hir::HirId) { - let source_map = cx.sess().source_map(); - let mut item_and_spans: Vec<(&hir::Item<'_>, Span)> = Vec::new(); // (start, end, item) + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if let hir::ItemKind::Impl(imple) = item.kind + && imple.unsafety == hir::Unsafety::Unsafe + && !in_external_macro(cx.tcx.sess, item.span) + && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) + && !is_unsafe_from_proc_macro(cx, item.span) + && !item_has_safety_comment(cx, item) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(item.span) { + source_map.span_until_char(item.span, '\n') + } else { + item.span + }; - // Collect all items and their spans - for item_id in module.item_ids { - let item = cx.tcx.hir().item(*item_id); - item_and_spans.push((item, item.span)); - } - // Sort items by start position - item_and_spans.sort_by_key(|e| e.1.lo()); - - for (idx, (item, item_span)) in item_and_spans.iter().enumerate() { - if let hir::ItemKind::Impl(imple) = &item.kind - && imple.unsafety == hir::Unsafety::Unsafe - && !item_span.from_expansion() - && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, hir_id) - { - // Checks if the lines immediately preceding the impl contain a safety comment. - let impl_has_safety_comment = { - let span_before_impl = if idx == 0 { - // mod A { /* comment */ unsafe impl T {} } - // ^--------------------^ - todo!(); - //mod_span.until(module.spans) - } else { - // unsafe impl S {} /* comment */ unsafe impl T {} - // ^-------------^ - item_and_spans[idx - 1].1.between(*item_span) - }; - - if let Ok(start) = source_map.lookup_line(span_before_impl.lo()) - && let Ok(end) = source_map.lookup_line(span_before_impl.hi()) - && let Some(src) = start.sf.src.as_deref() - { - start.line < end.line && text_has_safety_comment( - src, - &start.sf.lines[start.line + 1 ..= end.line], - start.sf.start_pos.to_usize() - ) - } else { - // Problem getting source text. Pretend a comment was found. - true - } - }; - - if !impl_has_safety_comment { - span_lint_and_help( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - *item_span, - "unsafe impl missing a safety comment", - None, - "consider adding a safety comment on the preceding line", - ); - } - } + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe impl missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); } } } -fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { +fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); - let file_pos = source_map.lookup_byte_offset(block.span.lo()); + let file_pos = source_map.lookup_byte_offset(span.lo()); file_pos .sf .src @@ -162,7 +127,7 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool { } /// Checks if the lines immediately preceding the block contain a safety comment. -fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { +fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool { // This intentionally ignores text before the start of a function so something like: // ``` // // SAFETY: reason @@ -171,13 +136,85 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. + span_from_macro_expansion_has_safety_comment(cx, block.span) || span_in_body_has_safety_comment(cx, block.span) +} + +/// Checks if the lines immediately preceding the item contain a safety comment. +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { + if span_from_macro_expansion_has_safety_comment(cx, item.span) || span_in_body_has_safety_comment(cx, item.span) { + return true; + } + + if item.span.ctxt() == SyntaxContext::root() { + if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { + let mut span_before_item = None; + let mut hi = false; + if let Node::Item(parent_item) = parent_node { + if let ItemKind::Mod(parent_mod) = &parent_item.kind { + for (idx, item_id) in parent_mod.item_ids.iter().enumerate() { + if *item_id == item.item_id() { + if idx == 0 { + // mod A { /* comment */ unsafe impl T {} ... } + // ^------------------------------------------^ gets this span + // ^---------------------^ finally checks the text in this range + hi = false; + span_before_item = Some(parent_item.span); + } else { + let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]); + // some_item /* comment */ unsafe impl T {} + // ^-------^ gets this span + // ^---------------^ finally checks the text in this range + hi = true; + span_before_item = Some(prev_item.span); + } + break; + } + } + } + } + let span_before_item = span_before_item.unwrap(); + + let source_map = cx.sess().source_map(); + if let Some(item_span) = walk_span_to_context(item.span, SyntaxContext::root()) + && let Some(span_before_item) = walk_span_to_context(span_before_item, SyntaxContext::root()) + && let Ok(unsafe_line) = source_map.lookup_line(item_span.lo()) + && let Ok(line_before_unsafe) = source_map.lookup_line(if hi { + span_before_item.hi() + } else { + span_before_item.lo() + }) + && Lrc::ptr_eq(&unsafe_line.sf, &line_before_unsafe.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + line_before_unsafe.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[line_before_unsafe.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } + } else { + // No parent node. Pretend a comment was found. + true + } + } else { + false + } +} + +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); - let ctxt = block.span.ctxt(); - if ctxt != SyntaxContext::root() { - // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block. + let ctxt = span.ctxt(); + if ctxt == SyntaxContext::root() { + false + } else { + // From a macro expansion. Get the text from the start of the macro declaration to start of the + // unsafe block. // macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; } // ^--------------------------------------------^ - if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo()) && Lrc::ptr_eq(&unsafe_line.sf, ¯o_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() @@ -191,24 +228,35 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool { // Problem getting source text. Pretend a comment was found. true } - } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo()) + } +} + +fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { + let source_map = cx.sess().source_map(); + let ctxt = span.ctxt(); + if ctxt == SyntaxContext::root() && let Some(body) = cx.enclosing_body - && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) - && let Ok(body_line) = source_map.lookup_line(body_span.lo()) - && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() { - // Get the text from the start of function body to the unsafe block. - // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } - // ^-------------^ - body_line.line < unsafe_line.line && text_has_safety_comment( - src, - &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + if let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root()) + && let Ok(body_line) = source_map.lookup_line(body_span.lo()) + && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + // Get the text from the start of function body to the unsafe block. + // fn foo() { some_stuff; unsafe { stuff }; other_stuff; } + // ^-------------^ + body_line.line < unsafe_line.line && text_has_safety_comment( + src, + &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) + } else { + // Problem getting source text. Pretend a comment was found. + true + } } else { - // Problem getting source text. Pretend a comment was found. - true + false } } diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index 3044a92cbd04..fd64c60384d6 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -363,15 +363,22 @@ mod unsafe_impl_smoke_test { mod unsafe_impl_from_macro { unsafe trait T {} - macro_rules! unsafe_impl { + macro_rules! no_safety_comment { ($t:ty) => { unsafe impl T for $t {} }; } - // ok: from macro expanision - unsafe_impl!(()); - // ok: from macro expansion - unsafe_impl!(i32); + // error + no_safety_comment!(()); + + macro_rules! with_safety_comment { + ($t:ty) => { + // SAFETY: + unsafe impl T for $t {} + }; + } + // ok + with_safety_comment!((i32)); } #[rustfmt::skip] diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 80d68a038087..3a1f647b06d8 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -164,7 +164,19 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:420:5 + --> $DIR/undocumented_unsafe_blocks.rs:368:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(()); + | ---------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:427:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -172,7 +184,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:424:19 + --> $DIR/undocumented_unsafe_blocks.rs:431:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -180,7 +192,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:428:5 + --> $DIR/undocumented_unsafe_blocks.rs:435:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -188,12 +200,12 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:433:5 + --> $DIR/undocumented_unsafe_blocks.rs:440:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 24 previous errors +error: aborting due to 25 previous errors From bca3d8a6b5f04fe2a6ca95ff22d07ee73ad80604 Mon Sep 17 00:00:00 2001 From: flip1995 Date: Thu, 5 May 2022 15:50:11 +0100 Subject: [PATCH 460/536] Track if a where bound comes from a impl Trait desugar With #93803 `impl Trait` function arguments get desugared to hidden where bounds. However, Clippy needs to know if a bound was originally a impl Trait or an actual bound. This adds a field to the `WhereBoundPredicate` struct to keep track of this information during HIR lowering. --- clippy_lints/src/lifetimes.rs | 6 +++--- clippy_lints/src/trait_bounds.rs | 6 +++++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index ab5d3fa7b6d9..51d5b510ab93 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -9,8 +9,8 @@ use rustc_hir::intravisit::{ use rustc_hir::FnRetTy::Return; use rustc_hir::{ BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem, - ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier, - TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, PredicateOrigin, + TraitBoundModifier, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter as middle_nested_filter; @@ -145,7 +145,7 @@ fn check_fn_inner<'tcx>( .filter(|param| matches!(param.kind, GenericParamKind::Type { .. })); for typ in types { for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) { - if pred.in_where_clause { + if pred.origin == PredicateOrigin::WhereClause { // has_where_lifetimes checked that this predicate contains no lifetime. continue; } diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index c0aca2d517cf..911da3997ae4 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -8,7 +8,8 @@ use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::{ - GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate, + GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath, TraitItem, Ty, TyKind, + WherePredicate, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; @@ -95,6 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { for predicate in item.generics.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if bound_predicate.origin != PredicateOrigin::ImplTrait; if !bound_predicate.span.from_expansion(); if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(PathSegment { @@ -168,6 +170,7 @@ impl TraitBounds { for bound in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref p) = bound; + if p.origin != PredicateOrigin::ImplTrait; if p.bounds.len() as u64 <= self.max_trait_bounds; if !p.span.from_expansion(); if let Some(ref v) = map.insert( @@ -223,6 +226,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) { for predicate in gen.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if bound_predicate.origin != PredicateOrigin::ImplTrait; if !bound_predicate.span.from_expansion(); if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; if let Some(segment) = segments.first(); From 03960ebab26a1b1d2d43540f47aca6670bdc1e54 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 7 May 2022 16:49:19 +0200 Subject: [PATCH 461/536] Replace `#[allow]` with `#[expect]` in Clippy --- clippy_lints/src/booleans.rs | 6 ++--- clippy_lints/src/cognitive_complexity.rs | 4 ++-- clippy_lints/src/default.rs | 2 +- clippy_lints/src/default_numeric_fallback.rs | 1 - clippy_lints/src/dereference.rs | 4 ++-- clippy_lints/src/doc.rs | 6 ++--- clippy_lints/src/double_comparison.rs | 2 +- clippy_lints/src/entry.rs | 4 ++-- clippy_lints/src/enum_clike.rs | 2 +- clippy_lints/src/enum_variants.rs | 2 +- clippy_lints/src/eq_op.rs | 3 +-- clippy_lints/src/floating_point_arithmetic.rs | 2 +- clippy_lints/src/implicit_hasher.rs | 2 +- clippy_lints/src/implicit_return.rs | 4 ++-- clippy_lints/src/int_plus_one.rs | 2 +- clippy_lints/src/lib.rs | 3 ++- clippy_lints/src/literal_representation.rs | 3 +-- clippy_lints/src/loops/mod.rs | 1 - clippy_lints/src/loops/needless_range_loop.rs | 2 +- .../src/loops/while_let_on_iterator.rs | 2 +- clippy_lints/src/macro_use.rs | 3 +-- clippy_lints/src/manual_map.rs | 2 +- clippy_lints/src/manual_non_exhaustive.rs | 4 ++-- clippy_lints/src/matches/match_same_arms.rs | 6 ++--- .../src/matches/match_single_binding.rs | 2 +- clippy_lints/src/matches/match_wild_enum.rs | 3 +-- .../src/matches/redundant_pattern_match.rs | 2 +- clippy_lints/src/misc.rs | 2 +- clippy_lints/src/needless_pass_by_value.rs | 2 +- clippy_lints/src/neg_multiply.rs | 1 - clippy_lints/src/new_without_default.rs | 1 - clippy_lints/src/non_expressive_names.rs | 2 +- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/pattern_type_mismatch.rs | 1 - clippy_lints/src/ptr.rs | 2 +- clippy_lints/src/redundant_clone.rs | 2 +- clippy_lints/src/reference.rs | 5 ++--- clippy_lints/src/regex.rs | 1 - clippy_lints/src/uninit_vec.rs | 2 +- clippy_lints/src/unused_unit.rs | 2 +- clippy_lints/src/useless_conversion.rs | 2 +- clippy_lints/src/vec.rs | 5 ++--- clippy_lints/src/write.rs | 3 --- clippy_utils/src/ast_utils.rs | 2 +- clippy_utils/src/attrs.rs | 1 - clippy_utils/src/consts.rs | 2 +- clippy_utils/src/eager_or_lazy.rs | 2 +- clippy_utils/src/hir_utils.rs | 8 +++---- clippy_utils/src/lib.rs | 6 ++--- clippy_utils/src/paths.rs | 22 +++++++++---------- clippy_utils/src/source.rs | 2 +- clippy_utils/src/sugg.rs | 4 +--- clippy_utils/src/ty.rs | 2 +- clippy_utils/src/usage.rs | 1 - 54 files changed, 73 insertions(+), 93 deletions(-) diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs index f7449c8dc72e..0adb6327164e 100644 --- a/clippy_lints/src/booleans.rs +++ b/clippy_lints/src/booleans.rs @@ -137,7 +137,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { } for (n, expr) in self.terminals.iter().enumerate() { if eq_expr_value(self.cx, e, expr) { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] return Ok(Bool::Term(n as u8)); } @@ -149,7 +149,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { if eq_expr_value(self.cx, e_lhs, expr_lhs); if eq_expr_value(self.cx, e_rhs, expr_rhs); then { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); } } @@ -157,7 +157,7 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> { let n = self.terminals.len(); self.terminals.push(e); if n < 32 { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] Ok(Bool::Term(n as u8)) } else { Err("too many literals".to_owned()) diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs index 2bf7f8689054..317c4bfb3226 100644 --- a/clippy_lints/src/cognitive_complexity.rs +++ b/clippy_lints/src/cognitive_complexity.rs @@ -48,7 +48,7 @@ impl CognitiveComplexity { impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]); impl CognitiveComplexity { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] fn check<'tcx>( &mut self, cx: &LateContext<'tcx>, @@ -70,7 +70,7 @@ impl CognitiveComplexity { let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) { returns } else { - #[allow(clippy::integer_division)] + #[expect(clippy::integer_division)] (returns / 2) }; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index f7e4bc24321c..243dfd3a4618 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -110,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for Default { } } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index f3996e5b44d7..3d9f9ed41ce1 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -116,7 +116,6 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> { } impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { - #[allow(clippy::too_many_lines)] fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { match &expr.kind { ExprKind::Call(func, args) => { diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index fe3911983421..ea4c0207bb01 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -168,7 +168,7 @@ struct RefPat { } impl<'tcx> LateLintPass<'tcx> for Dereferencing { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // Skip path expressions from deref calls. e.g. `Deref::deref(e)` if Some(expr.hir_id) == self.skip_expr.take() { @@ -580,7 +580,7 @@ fn find_adjustments<'tcx>( } } -#[allow(clippy::needless_pass_by_value)] +#[expect(clippy::needless_pass_by_value)] fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) { match state { State::DerefMethod { diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index b3fd8af4730d..aaec88f50c77 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -198,7 +198,7 @@ declare_clippy_lint! { "presence of `fn main() {` in code examples" } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Clone)] pub struct DocMarkdown { valid_idents: FxHashSet, @@ -373,7 +373,7 @@ fn lint_for_missing_headers<'tcx>( /// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we /// need to keep track of /// the spans but this function is inspired from the later. -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] #[must_use] pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) { // one-line comments lose their prefix @@ -428,7 +428,7 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs /// We don't want the parser to choke on intra doc links. Since we don't /// actually care about rendering them, just pretend that all broken links are /// point to a fake address. - #[allow(clippy::unnecessary_wraps)] // we're following a type signature + #[expect(clippy::unnecessary_wraps)] // we're following a type signature fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> { Some(("fake".into(), "fake".into())) } diff --git a/clippy_lints/src/double_comparison.rs b/clippy_lints/src/double_comparison.rs index 176092e5b280..be95375789d5 100644 --- a/clippy_lints/src/double_comparison.rs +++ b/clippy_lints/src/double_comparison.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]); impl<'tcx> DoubleComparisons { - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] fn check_binop(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) { let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) { (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => { diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 1ae2e20c1e06..6c5ed5dca2d2 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(HashMapPass => [MAP_ENTRY]); impl<'tcx> LateLintPass<'tcx> for HashMapPass { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) { Some(higher::If { cond, then, r#else }) => (cond, then, r#else), @@ -319,7 +319,7 @@ struct Insertion<'tcx> { /// `or_insert_with`. /// * Determine if there's any sub-expression that can't be placed in a closure. /// * Determine if there's only a single insert statement. `or_insert` can be used in this case. -#[allow(clippy::struct_excessive_bools)] +#[expect(clippy::struct_excessive_bools)] struct InsertSearcher<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, /// The map expression used in the contains call. diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index e2a5430da08c..43b405c9a8eb 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]); impl<'tcx> LateLintPass<'tcx> for UnportableVariant { - #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap, clippy::cast_sign_loss)] + #[expect(clippy::cast_possible_wrap)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if cx.tcx.data_layout.pointer_size.bits() != 64 { return; diff --git a/clippy_lints/src/enum_variants.rs b/clippy_lints/src/enum_variants.rs index 346d03ca5568..e029b8e85379 100644 --- a/clippy_lints/src/enum_variants.rs +++ b/clippy_lints/src/enum_variants.rs @@ -240,7 +240,7 @@ impl LateLintPass<'_> for EnumVariantNames { assert!(last.is_some()); } - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { let item_name = item.ident.name.as_str(); let item_camel = to_camel_case(item_name); diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 51c811b304ca..afb5d32f9533 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -72,7 +72,7 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); impl<'tcx> LateLintPass<'tcx> for EqOp { - #[allow(clippy::similar_names, clippy::too_many_lines)] + #[expect(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if_chain! { if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| { @@ -138,7 +138,6 @@ impl<'tcx> LateLintPass<'tcx> for EqOp { }, }; if let Some(trait_id) = trait_id { - #[allow(clippy::match_same_arms)] match (&left.kind, &right.kind) { // do not suggest to dereference literals (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {}, diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs index 79ce53f7a5f2..42503c26de1d 100644 --- a/clippy_lints/src/floating_point_arithmetic.rs +++ b/clippy_lints/src/floating_point_arithmetic.rs @@ -215,7 +215,7 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) { // converted to an integer without loss of precision. For now we only check // ranges [-16777215, 16777216) for type f32 as whole number floats outside // this range are lossy and ambiguous. -#[allow(clippy::cast_possible_truncation)] +#[expect(clippy::cast_possible_truncation)] fn get_integer_from_float_constant(value: &Constant) -> Option { match value { F32(num) if num.fract() == 0.0 => { diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs index feb1b1014b18..4f9680f60fe8 100644 --- a/clippy_lints/src/implicit_hasher.rs +++ b/clippy_lints/src/implicit_hasher.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]); impl<'tcx> LateLintPass<'tcx> for ImplicitHasher { - #[allow(clippy::cast_possible_truncation, clippy::too_many_lines)] + #[expect(clippy::cast_possible_truncation, clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { use rustc_span::BytePos; diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index d650d6e9a858..647947d5d30d 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -164,7 +164,7 @@ fn lint_implicit_returns( }) .visit_block(block); if add_return { - #[allow(clippy::option_if_let_else)] + #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { lint_return(cx, span); LintLocation::Parent @@ -196,7 +196,7 @@ fn lint_implicit_returns( _ => { - #[allow(clippy::option_if_let_else)] + #[expect(clippy::option_if_let_else)] if let Some(span) = call_site_span { lint_return(cx, span); LintLocation::Parent diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index 3716d36ad881..8db7b307ddb7 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -52,7 +52,7 @@ enum Side { } impl IntPlusOne { - #[allow(clippy::cast_sign_loss)] + #[expect(clippy::cast_sign_loss)] fn check_lit(lit: &Lit, target_value: i128) -> bool { if let LitKind::Int(value, ..) = lit.kind { return value == (target_value as u128); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3bb821a14829..092c981140ef 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -8,6 +8,7 @@ #![feature(iter_intersperse)] #![feature(let_chains)] #![feature(let_else)] +#![feature(lint_reasons)] #![feature(once_cell)] #![feature(rustc_private)] #![feature(stmt_expr_attributes)] @@ -472,7 +473,7 @@ pub fn read_conf(sess: &Session) -> Conf { /// Register all lints and lint groups with the rustc plugin registry /// /// Used in `./src/driver.rs`. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { register_removed_non_tool_lints(store); diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs index 269d3c62eafc..9998712b8527 100644 --- a/clippy_lints/src/literal_representation.rs +++ b/clippy_lints/src/literal_representation.rs @@ -204,7 +204,6 @@ impl WarningType { } } -#[allow(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct LiteralDigitGrouping { lint_fraction_readability: bool, @@ -432,7 +431,7 @@ impl LiteralDigitGrouping { } } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct DecimalLiteralRepresentation { threshold: u64, diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs index f029067d3671..75d771f992a8 100644 --- a/clippy_lints/src/loops/mod.rs +++ b/clippy_lints/src/loops/mod.rs @@ -620,7 +620,6 @@ declare_lint_pass!(Loops => [ ]); impl<'tcx> LateLintPass<'tcx> for Loops { - #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let for_loop = higher::ForLoop::hir(expr); if let Some(higher::ForLoop { diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs index 6ed141fa4a5a..09f9c05b4fce 100644 --- a/clippy_lints/src/loops/needless_range_loop.rs +++ b/clippy_lints/src/loops/needless_range_loop.rs @@ -19,7 +19,7 @@ use std::mem; /// Checks for looping over a range and then indexing a sequence with it. /// The iteratee must be a range literal. -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index 20a8294a0d1a..82760607ba29 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -239,7 +239,7 @@ fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tc v.uses_iter } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool { struct AfterLoopVisitor<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 76c5cfadc2c1..da806918be06 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -49,7 +49,7 @@ impl MacroRefData { } #[derive(Default)] -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct MacroUseImports { /// the actual import path used and the span of the attribute above it. imports: Vec<(String, Span)>, @@ -135,7 +135,6 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { self.push_unique_macro_pat_ty(cx, ty.span); } } - #[allow(clippy::too_many_lines)] fn check_crate_post(&mut self, cx: &LateContext<'_>) { let mut used = FxHashMap::default(); let mut check_dup = vec![]; diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 8475e367b09f..230ae029ed9d 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); impl<'tcx> LateLintPass<'tcx> for ManualMap { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { Some(IfLetOrMatch::IfLet(scrutinee, pat, body, Some(r#else))) => (scrutinee, pat, body, None, r#else), diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index b8d620d81713..004e36ae13c3 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -62,7 +62,7 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveStruct { msrv: Option, } @@ -76,7 +76,7 @@ impl ManualNonExhaustiveStruct { impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveEnum { msrv: Option, constructed_enum_variants: FxHashSet<(DefId, DefId)>, diff --git a/clippy_lints/src/matches/match_same_arms.rs b/clippy_lints/src/matches/match_same_arms.rs index 9b7344fb8b0b..a96a7fe55f3a 100644 --- a/clippy_lints/src/matches/match_same_arms.rs +++ b/clippy_lints/src/matches/match_same_arms.rs @@ -16,7 +16,7 @@ use std::collections::hash_map::Entry; use super::MATCH_SAME_ARMS; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 { let mut h = SpanlessHash::new(cx); @@ -225,9 +225,9 @@ fn iter_matching_struct_fields<'a>( Iter(left.iter(), right.iter()) } -#[allow(clippy::similar_names)] +#[expect(clippy::similar_names)] impl<'a> NormalizedPat<'a> { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn from_pat(cx: &LateContext<'_>, arena: &'a DroplessArena, pat: &'a Pat<'_>) -> Self { match pat.kind { PatKind::Wild | PatKind::Binding(.., None) => Self::Wild, diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 39fe54648fbc..028e8c297fbd 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -8,7 +8,7 @@ use rustc_lint::LateContext; use super::MATCH_SINGLE_BINDING; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 93bf0dc62e07..a3a26d9c3e16 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -10,7 +10,7 @@ use rustc_span::sym; use super::{MATCH_WILDCARD_FOR_SINGLE_VARIANTS, WILDCARD_ENUM_MATCH_ARM}; -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { let ty = cx.typeck_results().expr_ty(ex).peel_refs(); let adt_def = match ty.kind() { @@ -56,7 +56,6 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { recurse_or_patterns(arm.pat, |pat| { let path = match &peel_hir_pat_refs(pat).0.kind { PatKind::Path(path) => { - #[allow(clippy::match_same_arms)] let id = match cx.qpath_res(path, pat.hir_id) { Res::Def( DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst, diff --git a/clippy_lints/src/matches/redundant_pattern_match.rs b/clippy_lints/src/matches/redundant_pattern_match.rs index 37b67647efe9..1a8b9d15f370 100644 --- a/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/clippy_lints/src/matches/redundant_pattern_match.rs @@ -340,7 +340,7 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op } } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn find_good_method_for_match<'a>( cx: &LateContext<'_>, arms: &[Arm<'_>], diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index ac82dd306a52..2566a3c1f86a 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -548,7 +548,7 @@ fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _)) } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { #[derive(Default)] struct EqImpl { diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 4034079a90c0..38960103d5e0 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -70,7 +70,7 @@ macro_rules! need { } impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_fn( &mut self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/neg_multiply.rs b/clippy_lints/src/neg_multiply.rs index 6ba9ba0753d4..707f3b2181ac 100644 --- a/clippy_lints/src/neg_multiply.rs +++ b/clippy_lints/src/neg_multiply.rs @@ -34,7 +34,6 @@ declare_clippy_lint! { declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]); -#[allow(clippy::match_same_arms)] impl<'tcx> LateLintPass<'tcx> for NegMultiply { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, left, right) = e.kind { diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 2f733f221d57..6e7627639eb8 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -58,7 +58,6 @@ pub struct NewWithoutDefault { impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]); impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { - #[allow(clippy::too_many_lines)] fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if let hir::ItemKind::Impl(hir::Impl { of_trait: None, diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index e3bc40c4b491..7f6b535c7b16 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -191,7 +191,7 @@ impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> { } } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_ident(&mut self, ident: Ident) { let interned_name = ident.name.as_str(); if interned_name.chars().any(char::is_uppercase) { diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index 9af3059a37f9..c5b8b8103a13 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -124,7 +124,7 @@ impl<'tcx> PassByRefOrValue { // Cap the calculated bit width at 32-bits to reduce // portability problems between 32 and 64-bit targets let bit_width = cmp::min(bit_width, 32); - #[allow(clippy::integer_division)] + #[expect(clippy::integer_division)] let byte_width = bit_width / 8; // Use a limit of 2 times the register byte width byte_width * 2 diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs index be319ee110d2..a4d265111f9a 100644 --- a/clippy_lints/src/pattern_type_mismatch.rs +++ b/clippy_lints/src/pattern_type_mismatch.rs @@ -163,7 +163,6 @@ enum Level { Lower, } -#[allow(rustc::usage_of_ty_tykind)] fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> { let mut result = None; pat.walk(|p| { diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index c35eeeac67a3..86460c1b27e3 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -514,7 +514,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 1507c75ff612..954e702a1f88 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -71,7 +71,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]); impl<'tcx> LateLintPass<'tcx> for RedundantClone { - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] fn check_fn( &mut self, cx: &LateContext<'tcx>, diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 811a7bb9c153..f789cec6d6ac 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -54,13 +54,12 @@ impl EarlyLintPass for DerefAddrOf { then { let mut applicability = Applicability::MachineApplicable; let sugg = if e.span.from_expansion() { - #[allow(clippy::option_if_let_else)] if let Some(macro_source) = snippet_opt(cx, e.span) { // Remove leading whitespace from the given span // e.g: ` $visitor` turns into `$visitor` let trim_leading_whitespaces = |span| { snippet_opt(cx, span).and_then(|snip| { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] snip.find(|c: char| !c.is_whitespace()).map(|pos| { span.lo() + BytePos(pos as u32) }) @@ -68,7 +67,7 @@ impl EarlyLintPass for DerefAddrOf { }; let mut generate_snippet = |pattern: &str| { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] macro_source.rfind(pattern).map(|pattern_pos| { let rpos = pattern_pos + pattern.len(); let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32)); diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index a92097e1d24c..78ca7622f4a1 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -79,7 +79,6 @@ impl<'tcx> LateLintPass<'tcx> for Regex { } } -#[allow(clippy::cast_possible_truncation)] // truncation very unlikely here #[must_use] fn str_span(base: Span, c: regex_syntax::ast::Span, offset: u8) -> Span { let offset = u32::from(offset); diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 6d909c34690d..9f4c5555f11b 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -98,7 +98,7 @@ fn handle_uninit_vec_pair<'tcx>( // Check T of Vec if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) { // FIXME: #7698, false positive of the internal lints - #[allow(clippy::collapsible_span_lint_calls)] + #[expect(clippy::collapsible_span_lint_calls)] span_lint_and_then( cx, UNINIT_VEC, diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs index bfd17a687499..52585e59566c 100644 --- a/clippy_lints/src/unused_unit.rs +++ b/clippy_lints/src/unused_unit.rs @@ -130,7 +130,7 @@ fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) { snippet_opt(cx, span.with_hi(ty.span.hi())).map_or((ty.span, Applicability::MaybeIncorrect), |fn_source| { position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| { ( - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)), Applicability::MachineApplicable, ) diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs index abd8a3623703..4a3b5383c892 100644 --- a/clippy_lints/src/useless_conversion.rs +++ b/clippy_lints/src/useless_conversion.rs @@ -41,7 +41,7 @@ pub struct UselessConversion { impl_lint_pass!(UselessConversion => [USELESS_CONVERSION]); -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] impl<'tcx> LateLintPass<'tcx> for UselessConversion { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if e.span.from_expansion() { diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs index 79e7410c3a83..ba1ff65479d6 100644 --- a/clippy_lints/src/vec.rs +++ b/clippy_lints/src/vec.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; -#[allow(clippy::module_name_repetitions)] +#[expect(clippy::module_name_repetitions)] #[derive(Copy, Clone)] pub struct UselessVec { pub too_large_for_stack: u64, @@ -83,7 +83,7 @@ impl UselessVec { let snippet = match *vec_args { higher::VecArgs::Repeat(elem, len) => { if let Some((Constant::Int(len_constant), _)) = constant(cx, cx.typeck_results(), len) { - #[allow(clippy::cast_possible_truncation)] + #[expect(clippy::cast_possible_truncation)] if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack { return; } @@ -110,7 +110,6 @@ impl UselessVec { }, higher::VecArgs::Vec(args) => { if let Some(last) = args.iter().last() { - #[allow(clippy::cast_possible_truncation)] if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack { return; } diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index 54b93a20a057..d2493c055a51 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -342,8 +342,6 @@ impl EarlyLintPass for Write { if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) { if fmt_str.symbol == kw::Empty { let mut applicability = Applicability::MachineApplicable; - // FIXME: remove this `#[allow(...)]` once the issue #5822 gets fixed - #[allow(clippy::option_if_let_else)] let suggestion = if let Some(e) = expr { snippet_with_applicability(cx, e.span, "v", &mut applicability) } else { @@ -528,7 +526,6 @@ impl Write { /// ```rust,ignore /// (Some("string to write: {}"), Some(buf)) /// ``` - #[allow(clippy::too_many_lines)] fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option, Option) { let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None); let expr = if is_write { diff --git a/clippy_utils/src/ast_utils.rs b/clippy_utils/src/ast_utils.rs index 7919800483f5..b379f8c06c60 100644 --- a/clippy_utils/src/ast_utils.rs +++ b/clippy_utils/src/ast_utils.rs @@ -242,7 +242,7 @@ pub fn eq_item(l: &Item, r: &Item, mut eq_kind: impl FnMut(&K, &K) -> b eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind) } -#[allow(clippy::too_many_lines)] // Just a big match statement +#[expect(clippy::too_many_lines)] // Just a big match statement pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool { use ItemKind::*; match (l, r) { diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 25a84d166508..5bbc2b5b0ebf 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -5,7 +5,6 @@ use rustc_span::sym; use std::str::FromStr; /// Deprecation status of attributes known by Clippy. -#[allow(dead_code)] pub enum DeprecationStatus { /// Attribute is deprecated Deprecated, diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 9785c0a75fb0..720dfe72237c 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -350,7 +350,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } - #[allow(clippy::cast_possible_wrap)] + #[expect(clippy::cast_possible_wrap)] fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option { use self::Constant::{Bool, Int}; match *o { diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs index a6ef6d79fc02..1a784b6cdda4 100644 --- a/clippy_utils/src/eager_or_lazy.rs +++ b/clippy_utils/src/eager_or_lazy.rs @@ -96,7 +96,7 @@ fn fn_eagerness<'tcx>( } } -#[allow(clippy::too_many_lines)] +#[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { struct V<'cx, 'tcx> { cx: &'cx LateContext<'tcx>, diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f4da625f1e30..951e630080fc 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -64,7 +64,6 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> { } } - #[allow(dead_code)] pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool { self.inter_expr().eq_block(left, right) } @@ -194,7 +193,7 @@ impl HirEqInterExpr<'_, '_, '_> { res } - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool { if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() { return false; @@ -359,7 +358,7 @@ impl HirEqInterExpr<'_, '_, '_> { } } - #[allow(clippy::similar_names)] + #[expect(clippy::similar_names)] fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool { match (left, right) { (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => { @@ -405,7 +404,6 @@ impl HirEqInterExpr<'_, '_, '_> { left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r)) } - #[allow(clippy::similar_names)] pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool { match (&left.kind, &right.kind) { (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec), @@ -560,7 +558,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { std::mem::discriminant(&b.rules).hash(&mut self.s); } - #[allow(clippy::too_many_lines)] + #[expect(clippy::too_many_lines)] pub fn hash_expr(&mut self, e: &Expr<'_>) { let simple_const = self .maybe_typeck_results diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7d46952d9718..b25e9d1a6c55 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -2,6 +2,7 @@ #![feature(control_flow_enum)] #![feature(let_else)] #![feature(let_chains)] +#![feature(lint_reasons)] #![feature(once_cell)] #![feature(rustc_private)] #![recursion_limit = "512"] @@ -35,7 +36,6 @@ extern crate rustc_typeck; #[macro_use] pub mod sym_helper; -#[allow(clippy::module_name_repetitions)] pub mod ast_utils; pub mod attrs; pub mod comparisons; @@ -1561,14 +1561,14 @@ pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 { Integer::from_int_ty(&tcx, ity).size().bits() } -#[allow(clippy::cast_possible_wrap)] +#[expect(clippy::cast_possible_wrap)] /// Turn a constant int byte representation into an i128 pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 { let amt = 128 - int_bits(tcx, ity); ((u as i128) << amt) >> amt } -#[allow(clippy::cast_sign_loss)] +#[expect(clippy::cast_sign_loss)] /// clip unused bytes pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 { let amt = 128 - int_bits(tcx, ity); diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 60971fb716db..9b9cbff2d146 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -43,9 +43,9 @@ pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"]; pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"]; pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"]; pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"]; pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"]; pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"]; @@ -63,7 +63,7 @@ pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"]; pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"]; pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"]; pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"]; #[cfg(feature = "internal")] pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"]; @@ -117,17 +117,17 @@ pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"]; pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"]; pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"]; pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"]; /// Preferably use the diagnostic item `sym::Result` where possible pub const RESULT: [&str; 3] = ["core", "result", "Result"]; @@ -169,9 +169,9 @@ pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"]; -#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"]; pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"]; pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"]; diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs index c69a3d8d2a15..04ef2f57447c 100644 --- a/clippy_utils/src/source.rs +++ b/clippy_utils/src/source.rs @@ -137,7 +137,7 @@ pub fn position_before_rarrow(s: &str) -> Option { } /// Reindent a multiline string with possibility of ignoring the first line. -#[allow(clippy::needless_pass_by_value)] +#[expect(clippy::needless_pass_by_value)] pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option) -> Cow<'_, str> { let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' '); let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t'); diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 18915553e61c..855d3657dd40 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -50,7 +50,7 @@ impl Display for Sugg<'_> { } } -#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method +#[expect(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method impl<'a> Sugg<'a> { /// Prepare a suggestion from an expression. pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option { @@ -318,7 +318,6 @@ impl<'a> Sugg<'a> { /// Convenience method to create the `..` or `...` /// suggestion. - #[allow(dead_code)] pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> { match limit { ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end), @@ -886,7 +885,6 @@ impl<'tcx> DerefDelegate<'_, 'tcx> { impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {} - #[allow(clippy::too_many_lines)] fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) { if let PlaceBase::Local(id) = cmt.place.base { let map = self.cx.tcx.hir(); diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 901e3e5390c5..5767a573a275 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -603,7 +603,7 @@ impl core::ops::Add for EnumValue { } /// Attempts to read the given constant as though it were an an enum value. -#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] +#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)] pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option { if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) { match tcx.type_of(id).kind() { diff --git a/clippy_utils/src/usage.rs b/clippy_utils/src/usage.rs index 4236e3aae2fb..abba9b005582 100644 --- a/clippy_utils/src/usage.rs +++ b/clippy_utils/src/usage.rs @@ -44,7 +44,6 @@ struct MutVarsDelegate { } impl<'tcx> MutVarsDelegate { - #[allow(clippy::similar_names)] fn update(&mut self, cat: &PlaceWithHirId<'tcx>) { match cat.place.base { PlaceBase::Local(id) => { From bdfea1c095d0a98e759eeb680d34d2d6da51132c Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sun, 8 May 2022 07:13:14 -0400 Subject: [PATCH 462/536] Pass msrvs by copy --- clippy_lints/src/approx_const.rs | 4 +--- clippy_lints/src/attrs.rs | 2 +- clippy_lints/src/borrow_as_ptr.rs | 2 +- clippy_lints/src/casts/cast_abs_to_unsigned.rs | 4 ++-- clippy_lints/src/casts/cast_lossless.rs | 6 +++--- clippy_lints/src/casts/cast_slice_different_sizes.rs | 4 ++-- clippy_lints/src/casts/mod.rs | 10 +++++----- clippy_lints/src/casts/ptr_as_ptr.rs | 4 ++-- clippy_lints/src/checked_conversions.rs | 2 +- clippy_lints/src/from_over_into.rs | 2 +- clippy_lints/src/if_then_some_else_none.rs | 2 +- clippy_lints/src/index_refutable_slice.rs | 2 +- clippy_lints/src/manual_bits.rs | 2 +- clippy_lints/src/manual_non_exhaustive.rs | 4 ++-- clippy_lints/src/manual_strip.rs | 2 +- clippy_lints/src/map_clone.rs | 2 +- clippy_lints/src/matches/mod.rs | 4 ++-- clippy_lints/src/mem_replace.rs | 2 +- clippy_lints/src/methods/cloned_instead_of_copied.rs | 6 +++--- clippy_lints/src/methods/err_expect.rs | 4 ++-- clippy_lints/src/methods/filter_map_next.rs | 4 ++-- clippy_lints/src/methods/is_digit_ascii_radix.rs | 4 ++-- clippy_lints/src/methods/map_unwrap_or.rs | 4 ++-- clippy_lints/src/methods/mod.rs | 8 ++++---- clippy_lints/src/methods/option_as_ref_deref.rs | 4 ++-- clippy_lints/src/methods/str_splitn.rs | 4 ++-- clippy_lints/src/methods/unnecessary_to_owned.rs | 6 +++--- clippy_lints/src/missing_const_for_fn.rs | 4 ++-- clippy_lints/src/ranges.rs | 2 +- clippy_lints/src/redundant_field_names.rs | 2 +- clippy_lints/src/redundant_static_lifetimes.rs | 2 +- clippy_lints/src/unnested_or_patterns.rs | 8 ++++---- clippy_lints/src/use_self.rs | 6 +++--- clippy_utils/src/lib.rs | 4 ++-- clippy_utils/src/qualify_min_const_fn.rs | 8 ++++---- doc/adding_lints.md | 2 +- 36 files changed, 70 insertions(+), 72 deletions(-) diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index e109ee0009ee..da1b646f4777 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -87,9 +87,7 @@ impl ApproxConstant { let s = s.as_str(); if s.parse::().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { - if is_approx_const(constant, s, min_digits) - && msrv.as_ref().map_or(true, |msrv| meets_msrv(self.msrv.as_ref(), msrv)) - { + if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) { span_lint_and_help( cx, APPROX_CONSTANT, diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 30dde2f0ef09..3de91f3d24a9 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -613,7 +613,7 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option) { if_chain! { - if meets_msrv(msrv.as_ref(), &msrvs::TOOL_ATTRIBUTES); + if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES); // check cfg_attr if attr.has_name(sym::cfg_attr); if let Some(items) = attr.meta_item_list(); diff --git a/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs index 9f8eb488c29b..0993adbae2e6 100644 --- a/clippy_lints/src/borrow_as_ptr.rs +++ b/clippy_lints/src/borrow_as_ptr.rs @@ -57,7 +57,7 @@ impl BorrowAsPtr { impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) { + if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { return; } diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index e9b0f1f672de..6bac6bf83f8e 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -16,10 +16,10 @@ pub(super) fn check( cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) { if_chain! { - if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS); + if meets_msrv(msrv, msrvs::UNSIGNED_ABS); if cast_from.is_integral(); if cast_to.is_integral(); if cast_from.is_signed(); diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 7717c1e9e31d..938458e30cad 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -16,7 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { return; @@ -68,7 +68,7 @@ fn should_lint( expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: &Option, + msrv: Option, ) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). if in_constant(cx, expr.hir_id) { @@ -95,7 +95,7 @@ fn should_lint( }; !is_isize_or_usize(cast_from) && from_nbits < to_nbits }, - (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv.as_ref(), &msrvs::FROM_BOOL) => true, + (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true, (_, _) => { matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) }, diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index 2238668abca7..bb09a6708a01 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -8,9 +8,9 @@ use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Option) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) { + if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) { return; } diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 9c3ebbe035bb..108e119c1041 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -532,7 +532,7 @@ impl_lint_pass!(Casts => [ impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !in_external_macro(cx.sess(), expr.span) { - ptr_as_ptr::check(cx, expr, &self.msrv); + ptr_as_ptr::check(cx, expr, self.msrv); } if expr.span.from_expansion() { @@ -562,9 +562,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } } @@ -572,8 +572,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_ref_to_mut::check(cx, expr); cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); - ptr_as_ptr::check(cx, expr, &self.msrv); - cast_slice_different_sizes::check(cx, expr, &self.msrv); + ptr_as_ptr::check(cx, expr, self.msrv); + cast_slice_different_sizes::check(cx, expr, self.msrv); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index fb04f93fbcf9..46d45d09661a 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -12,8 +12,8 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option) { - if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option) { + if !meets_msrv(msrv, msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 31cc3698592b..e23428e216d0 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -57,7 +57,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::TRY_FROM) { + if !meets_msrv(self.msrv, msrvs::TRY_FROM) { return; } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index c2f52605151e..5d25c1d06341 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -55,7 +55,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { + if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) { return; } diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 9525c163ece1..b8d227855d97 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -57,7 +57,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { + if !meets_msrv(self.msrv, msrvs::BOOL_THEN) { return; } diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 8a84513b7792..07b0604f78d7 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if meets_msrv(self.msrv.as_ref(), &msrvs::SLICE_PATTERNS); + if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS); let found_slices = find_slice_values(cx, let_pat); if !found_slices.is_empty(); diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index ac3d9447b6bd..60bbcde4f1de 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -48,7 +48,7 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]); impl<'tcx> LateLintPass<'tcx> for ManualBits { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) { + if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) { return; } diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index b8d620d81713..14f39067299c 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -98,7 +98,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustiveStruct { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { return; } @@ -150,7 +150,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::NON_EXHAUSTIVE) { + if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { return; } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index aacabf303a70..dfb3efc4e28b 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -68,7 +68,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { + if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { return; } diff --git a/clippy_lints/src/map_clone.rs b/clippy_lints/src/map_clone.rs index ceb66947d02c..a13d191375bf 100644 --- a/clippy_lints/src/map_clone.rs +++ b/clippy_lints/src/map_clone.rs @@ -144,7 +144,7 @@ impl MapClone { fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) { let mut applicability = Applicability::MachineApplicable; - let (message, sugg_method) = if is_copy && meets_msrv(self.msrv.as_ref(), &msrvs::ITERATOR_COPIED) { + let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) { ("you are using an explicit closure for copying elements", "copied") } else { ("you are using an explicit closure for cloning elements", "cloned") diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 401ecef460c3..3d8391bce2b2 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -658,7 +658,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } if !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { - if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) + if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) { match_same_arms::check(cx, arms); @@ -685,7 +685,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { match_wild_err_arm::check(cx, ex, arms); wild_in_or_pats::check(cx, arms); } else { - if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) { + if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { match_like_matches::check(cx, expr); } redundant_pattern_match::check(cx, expr); diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 054937e3e36b..41073d40f3d7 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -254,7 +254,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) { + if meets_msrv(self.msrv, msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index 6d30bb5a278b..e9aeab2d5b62 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -10,16 +10,16 @@ use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<&RustcVersion>) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option` -> `T` ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, &msrvs::OPTION_COPIED) => + if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) => { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) => { + _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => { match get_iterator_item_ty(cx, recv_ty) { // ::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index be9d4ad94fb8..570a1b87358d 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -13,7 +13,7 @@ pub(super) fn check( cx: &LateContext<'_>, _expr: &rustc_hir::Expr<'_>, recv: &rustc_hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, expect_span: Span, err_span: Span, ) { @@ -21,7 +21,7 @@ pub(super) fn check( if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if meets_msrv(msrv, &msrvs::EXPECT_ERR); + if meets_msrv(msrv, msrvs::EXPECT_ERR); // Grabs the `Result` type let result_type = cx.typeck_results().expr_ty(recv); diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index f0d69a1f42e7..38ec4d8e3ab8 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -14,10 +14,10 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, &msrvs::ITERATOR_FIND_MAP) { + if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index ad333df2f2d5..aa176dcc8b4a 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -15,9 +15,9 @@ pub(super) fn check<'tcx>( expr: &'tcx Expr<'_>, self_arg: &'tcx Expr<'_>, radix: &'tcx Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) { - if !meets_msrv(msrv, &msrvs::IS_ASCII_DIGIT) { + if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) { return; } diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 9ec84e76519a..4a8e7ce4ddbb 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -19,13 +19,13 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - if is_result && !meets_msrv(msrv, &msrvs::RESULT_MAP_OR_ELSE) { + if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) { return false; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index f3be71f6b8bb..5b073f6f3b8f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2306,7 +2306,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - check_methods(cx, expr, self.msrv.as_ref()); + check_methods(cx, expr, self.msrv); match expr.kind { hir::ExprKind::Call(func, args) => { @@ -2322,7 +2322,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args); single_char_pattern::check(cx, expr, method_call.ident.name, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv.as_ref()); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -2506,7 +2506,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { } #[allow(clippy::too_many_lines)] -fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { +fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option) { if let Some((name, [recv, args @ ..], span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { @@ -2534,7 +2534,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); }, Some(("take", [take_self_arg, take_arg], _)) => { - if meets_msrv(msrv, &msrvs::STR_REPEAT) { + if meets_msrv(msrv, msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index ba2d2914315f..b50a173d8359 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -19,9 +19,9 @@ pub(super) fn check<'tcx>( as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, is_mut: bool, - msrv: Option<&RustcVersion>, + msrv: Option, ) { - if !meets_msrv(msrv, &msrvs::OPTION_AS_DEREF) { + if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) { return; } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 52891eeed069..fc375763542a 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -24,7 +24,7 @@ pub(super) fn check( self_arg: &Expr<'_>, pat_arg: &Expr<'_>, count: u128, - msrv: Option<&RustcVersion>, + msrv: Option, ) { if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; @@ -34,7 +34,7 @@ pub(super) fn check( IterUsageKind::Nth(n) => count > n + 1, IterUsageKind::NextTuple => count > 2, }; - let manual = count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE); + let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE); match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 1f426979c4a0..97c4feb3122a 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -26,7 +26,7 @@ pub fn check<'tcx>( expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>], - msrv: Option<&RustcVersion>, + msrv: Option, ) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); @@ -198,7 +198,7 @@ fn check_into_iter_call_arg( expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -213,7 +213,7 @@ fn check_into_iter_call_arg( if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } - let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, &msrvs::ITERATOR_COPIED) { + let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 06209bfe7b08..16d65966c100 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::CONST_IF_MATCH) { + if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) { return; } @@ -146,7 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv.as_ref()) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { cx.tcx.sess.span_err(span, err.as_ref()); } diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index f26f3650cb35..a47dc26f6034 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -193,7 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for Ranges { check_range_zip_with_len(cx, path, args, expr.span); }, ExprKind::Binary(ref op, l, r) => { - if meets_msrv(self.msrv.as_ref(), &msrvs::RANGE_CONTAINS) { + if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr); } }, diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 40a62fd6d201..40b03068f6c7 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -51,7 +51,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::FIELD_INIT_SHORTHAND) { + if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index ea5064217abe..2d26c49252fa 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -99,7 +99,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv.as_ref(), &msrvs::STATIC_IN_CONST) { + if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index ae431aac83b8..04e2f301bfd8 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -61,13 +61,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -75,13 +75,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) { + if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index 138f8bccb3f5..66f7748e9e08 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -198,7 +198,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { if_chain! { if !hir_ty.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, in_body, @@ -225,7 +225,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id); then {} else { return; } @@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { if_chain! { if !pat.span.from_expansion(); - if meets_msrv(self.msrv.as_ref(), &msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind; if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _)); diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7d46952d9718..ed511004ea07 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -117,8 +117,8 @@ pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option) -> Opt None } -pub fn meets_msrv(msrv: Option<&RustcVersion>, lint_msrv: &RustcVersion) -> bool { - msrv.map_or(true, |msrv| msrv.meets(*lint_msrv)) +pub fn meets_msrv(msrv: Option, lint_msrv: RustcVersion) -> bool { + msrv.map_or(true, |msrv| msrv.meets(lint_msrv)) } #[macro_export] diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs index 75808b1b1746..6a8ed4e7bd73 100644 --- a/clippy_utils/src/qualify_min_const_fn.rs +++ b/clippy_utils/src/qualify_min_const_fn.rs @@ -19,7 +19,7 @@ use std::borrow::Cow; type McfResult = Result<(), (Span, Cow<'static, str>)>; -pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<&RustcVersion>) -> McfResult { +pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option) -> McfResult { let def_id = body.source.def_id(); let mut current = def_id; loop { @@ -269,7 +269,7 @@ fn check_terminator<'a, 'tcx>( tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, terminator: &Terminator<'tcx>, - msrv: Option<&RustcVersion>, + msrv: Option, ) -> McfResult { let span = terminator.source_info.span; match &terminator.kind { @@ -353,7 +353,7 @@ fn check_terminator<'a, 'tcx>( } } -fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> bool { +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option) -> bool { tcx.is_const_fn(def_id) && tcx.lookup_const_stability(def_id).map_or(true, |const_stab| { if let rustc_attr::StabilityLevel::Stable { since } = const_stab.level { @@ -362,7 +362,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<&RustcVersion>) -> b // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262. crate::meets_msrv( msrv, - &RustcVersion::parse(since.as_str()) + RustcVersion::parse(since.as_str()) .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"), ) } else { diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 307cf2f3a904..5607e2264a65 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -432,7 +432,7 @@ The project's MSRV can then be matched against the feature MSRV in the LintPass using the `meets_msrv` utility function. ``` rust -if !meets_msrv(self.msrv.as_ref(), &msrvs::STR_STRIP_PREFIX) { +if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { return; } ``` From 597f61bbe3a861c3d77b784cc701e6a975fbe842 Mon Sep 17 00:00:00 2001 From: "Samuel E. Moelius III" Date: Sun, 8 May 2022 06:44:58 -0400 Subject: [PATCH 463/536] Optionally allow `expect` and `unwrap` in tests --- clippy_lints/src/lib.rs | 11 +- clippy_lints/src/methods/expect_used.rs | 7 +- clippy_lints/src/methods/mod.rs | 376 +++++++++--------- clippy_lints/src/methods/unwrap_used.rs | 7 +- clippy_lints/src/utils/conf.rs | 8 + tests/ui-toml/expect_used/clippy.toml | 1 + tests/ui-toml/expect_used/expect_used.rs | 29 ++ tests/ui-toml/expect_used/expect_used.stderr | 19 + .../toml_unknown_key/conf_unknown_key.stderr | 2 +- tests/ui-toml/unwrap_used/clippy.toml | 1 + tests/ui-toml/unwrap_used/unwrap_used.rs | 74 ++++ tests/ui-toml/unwrap_used/unwrap_used.stderr | 197 +++++++++ 12 files changed, 547 insertions(+), 185 deletions(-) create mode 100644 tests/ui-toml/expect_used/clippy.toml create mode 100644 tests/ui-toml/expect_used/expect_used.rs create mode 100644 tests/ui-toml/expect_used/expect_used.stderr create mode 100644 tests/ui-toml/unwrap_used/clippy.toml create mode 100644 tests/ui-toml/unwrap_used/unwrap_used.rs create mode 100644 tests/ui-toml/unwrap_used/unwrap_used.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3bb821a14829..6970757b56d5 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -582,8 +582,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; + let allow_expect_in_tests = conf.allow_expect_in_tests; + let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv))); - store.register_late_pass(move || Box::new(methods::Methods::new(avoid_breaking_exported_api, msrv))); + store.register_late_pass(move || { + Box::new(methods::Methods::new( + avoid_breaking_exported_api, + msrv, + allow_expect_in_tests, + allow_unwrap_in_tests, + )) + }); store.register_late_pass(move || Box::new(matches::Matches::new(msrv))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); diff --git a/clippy_lints/src/methods/expect_used.rs b/clippy_lints/src/methods/expect_used.rs index 55be513c5bb1..fbc3348f1855 100644 --- a/clippy_lints/src/methods/expect_used.rs +++ b/clippy_lints/src/methods/expect_used.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_in_test_function; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,7 +8,7 @@ use rustc_span::sym; use super::EXPECT_USED; /// lint use of `expect()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) { @@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr None }; + if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + return; + } + if let Some((lint, kind, none_value)) = mess { span_lint_and_help( cx, diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 5b073f6f3b8f..e452614ce17f 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2200,14 +2200,23 @@ declare_clippy_lint! { pub struct Methods { avoid_breaking_exported_api: bool, msrv: Option, + allow_expect_in_tests: bool, + allow_unwrap_in_tests: bool, } impl Methods { #[must_use] - pub fn new(avoid_breaking_exported_api: bool, msrv: Option) -> Self { + pub fn new( + avoid_breaking_exported_api: bool, + msrv: Option, + allow_expect_in_tests: bool, + allow_unwrap_in_tests: bool, + ) -> Self { Self { avoid_breaking_exported_api, msrv, + allow_expect_in_tests, + allow_unwrap_in_tests, } } } @@ -2306,7 +2315,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { return; } - check_methods(cx, expr, self.msrv); + self.check_methods(cx, expr); match expr.kind { hir::ExprKind::Call(func, args) => { @@ -2505,196 +2514,201 @@ impl<'tcx> LateLintPass<'tcx> for Methods { extract_msrv_attr!(LateContext); } -#[allow(clippy::too_many_lines)] -fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option) { - if let Some((name, [recv, args @ ..], span)) = method_call(expr) { - match (name, args) { - ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { - zst_offset::check(cx, expr, recv); - }, - ("and_then", [arg]) => { - let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); - let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); - if !biom_option_linted && !biom_result_linted { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); - } - }, - ("as_deref" | "as_deref_mut", []) => { - needless_option_as_deref::check(cx, expr, recv, name); - }, - ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), - ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), - ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), - ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), - ("collect", []) => match method_call(recv) { - Some((name @ ("cloned" | "copied"), [recv2], _)) => { - iter_cloned_collect::check(cx, name, expr, recv2); +impl Methods { + #[allow(clippy::too_many_lines)] + fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if let Some((name, [recv, args @ ..], span)) = method_call(expr) { + match (name, args) { + ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { + zst_offset::check(cx, expr, recv); }, - Some(("map", [m_recv, m_arg], _)) => { - map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); - }, - Some(("take", [take_self_arg, take_arg], _)) => { - if meets_msrv(msrv, msrvs::STR_REPEAT) { - manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + ("and_then", [arg]) => { + let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg); + let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg); + if !biom_option_linted && !biom_result_linted { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, - _ => {}, - }, - (name @ "count", args @ []) => match method_call(recv) { - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { - iter_count::check(cx, expr, recv2, name2); + ("as_deref" | "as_deref_mut", []) => { + needless_option_as_deref::check(cx, expr, recv, name); }, - Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), - _ => {}, - }, - ("drain", [arg]) => { - iter_with_drain::check(cx, expr, recv, span, arg); - }, - ("expect", [_]) => match method_call(recv) { - Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), - Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span), - _ => expect_used::check(cx, expr, recv), - }, - ("extend", [arg]) => { - string_extend_chars::check(cx, expr, recv, arg); - extend_with_drain::check(cx, expr, recv, arg); - }, - ("filter_map", [arg]) => { - unnecessary_filter_map::check(cx, expr, arg, name); - filter_map_identity::check(cx, expr, arg, span); - }, - ("find_map", [arg]) => { - unnecessary_filter_map::check(cx, expr, arg, name); - }, - ("flat_map", [arg]) => { - flat_map_identity::check(cx, expr, arg, span); - flat_map_option::check(cx, expr, arg, span); - }, - (name @ "flatten", args @ []) => match method_call(recv) { - Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - _ => {}, - }, - ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), - ("for_each", [_]) => { - if let Some(("inspect", [_, _], span2)) = method_call(recv) { - inspect_for_each::check(cx, expr, span2); - } - }, - ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), - ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, msrv), - ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), - ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), - ("join", [join_arg]) => { - if let Some(("collect", _, span)) = method_call(recv) { - unnecessary_join::check(cx, expr, recv, join_arg, span); - } - }, - ("last", args @ []) | ("skip", args @ [_]) => { - if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv2, name, args); - } - } - }, - (name @ ("map" | "map_err"), [m_arg]) => { - if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { - match (name, args) { - ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), - ("filter", [f_arg]) => { - filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); - }, - ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true), - _ => {}, - } - } - map_identity::check(cx, expr, recv, m_arg, name, span); - }, - ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), - (name @ "next", args @ []) => { - if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { - match (name2, args2) { - ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, msrv), - ("iter", []) => iter_next_slice::check(cx, expr, recv2), - ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), - ("skip_while", [_]) => skip_while_next::check(cx, expr), - _ => {}, - } - } - }, - ("nth", args @ [n_arg]) => match method_call(recv) { - Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), - Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), - Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), - Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), - _ => iter_nth_zero::check(cx, expr, recv, n_arg), - }, - ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), - ("or_else", [arg]) => { - if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); - } - }, - ("splitn" | "rsplitn", [count_arg, pat_arg]) => { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { - suspicious_splitn::check(cx, name, expr, recv, count); - str_splitn::check(cx, name, expr, recv, pat_arg, count, msrv); - } - }, - ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { - if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { - suspicious_splitn::check(cx, name, expr, recv, count); - } - }, - ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), - ("take", args @ [_arg]) => { - if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv2, name, args); - } - } - }, - ("take", []) => needless_option_take::check(cx, expr, recv), - ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { - implicit_clone::check(cx, name, expr, recv); - }, - ("unwrap", []) => { - match method_call(recv) { - Some(("get", [recv, get_arg], _)) => { - get_unwrap::check(cx, expr, recv, get_arg, false); + ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), + ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), + ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv), + ("collect", []) => match method_call(recv) { + Some((name @ ("cloned" | "copied"), [recv2], _)) => { + iter_cloned_collect::check(cx, name, expr, recv2); }, - Some(("get_mut", [recv, get_arg], _)) => { - get_unwrap::check(cx, expr, recv, get_arg, true); + Some(("map", [m_recv, m_arg], _)) => { + map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv); }, - Some(("or", [recv, or_arg], or_span)) => { - or_then_unwrap::check(cx, expr, recv, or_arg, or_span); + Some(("take", [take_self_arg, take_arg], _)) => { + if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); + } }, _ => {}, - } - unwrap_used::check(cx, expr, recv); - }, - ("unwrap_or", [u_arg]) => match method_call(recv) { - Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { - manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, - Some(("map", [m_recv, m_arg], span)) => { - option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); + (name @ "count", args @ []) => match method_call(recv) { + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { + iter_count::check(cx, expr, recv2, name2); + }, + Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), + _ => {}, + }, + ("drain", [arg]) => { + iter_with_drain::check(cx, expr, recv, span, arg); + }, + ("expect", [_]) => match method_call(recv) { + Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), + Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + _ => expect_used::check(cx, expr, recv, self.allow_expect_in_tests), + }, + ("extend", [arg]) => { + string_extend_chars::check(cx, expr, recv, arg); + extend_with_drain::check(cx, expr, recv, arg); + }, + ("filter_map", [arg]) => { + unnecessary_filter_map::check(cx, expr, arg, name); + filter_map_identity::check(cx, expr, arg, span); + }, + ("find_map", [arg]) => { + unnecessary_filter_map::check(cx, expr, arg, name); + }, + ("flat_map", [arg]) => { + flat_map_identity::check(cx, expr, arg, span); + flat_map_option::check(cx, expr, arg, span); + }, + (name @ "flatten", args @ []) => match method_call(recv) { + Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span), + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + _ => {}, + }, + ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), + ("for_each", [_]) => { + if let Some(("inspect", [_, _], span2)) = method_call(recv) { + inspect_for_each::check(cx, expr, span2); + } + }, + ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"), + ("is_file", []) => filetype_is_file::check(cx, expr, recv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), + ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), + ("join", [join_arg]) => { + if let Some(("collect", _, span)) = method_call(recv) { + unnecessary_join::check(cx, expr, recv, join_arg, span); + } + }, + ("last", args @ []) | ("skip", args @ [_]) => { + if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv2, name, args); + } + } + }, + (name @ ("map" | "map_err"), [m_arg]) => { + if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { + match (name, args) { + ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv), + ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + ("filter", [f_arg]) => { + filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); + }, + ("find", [f_arg]) => { + filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true); + }, + _ => {}, + } + } + map_identity::check(cx, expr, recv, m_arg, name, span); + }, + ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), + (name @ "next", args @ []) => { + if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) { + match (name2, args2) { + ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), + ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + ("iter", []) => iter_next_slice::check(cx, expr, recv2), + ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), + ("skip_while", [_]) => skip_while_next::check(cx, expr), + _ => {}, + } + } + }, + ("nth", args @ [n_arg]) => match method_call(recv) { + Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), + Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args), + Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), + Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), + _ => iter_nth_zero::check(cx, expr, recv, n_arg), + }, + ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"), + ("or_else", [arg]) => { + if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) { + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); + } + }, + ("splitn" | "rsplitn", [count_arg, pat_arg]) => { + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + suspicious_splitn::check(cx, name, expr, recv, count); + str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); + } + }, + ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { + if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { + suspicious_splitn::check(cx, name, expr, recv, count); + } + }, + ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), + ("take", args @ [_arg]) => { + if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) { + if let ("cloned", []) = (name2, args2) { + iter_overeager_cloned::check(cx, expr, recv2, name, args); + } + } + }, + ("take", []) => needless_option_take::check(cx, expr, recv), + ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { + implicit_clone::check(cx, name, expr, recv); + }, + ("unwrap", []) => { + match method_call(recv) { + Some(("get", [recv, get_arg], _)) => { + get_unwrap::check(cx, expr, recv, get_arg, false); + }, + Some(("get_mut", [recv, get_arg], _)) => { + get_unwrap::check(cx, expr, recv, get_arg, true); + }, + Some(("or", [recv, or_arg], or_span)) => { + or_then_unwrap::check(cx, expr, recv, or_arg, or_span); + }, + _ => {}, + } + unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests); + }, + ("unwrap_or", [u_arg]) => match method_call(recv) { + Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { + manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); + }, + Some(("map", [m_recv, m_arg], span)) => { + option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span); + }, + _ => {}, + }, + ("unwrap_or_else", [u_arg]) => match method_call(recv) { + Some(("map", [recv, map_arg], _)) + if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {}, + _ => { + unwrap_or_else_default::check(cx, expr, recv, u_arg); + unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); + }, }, _ => {}, - }, - ("unwrap_or_else", [u_arg]) => match method_call(recv) { - Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {}, - _ => { - unwrap_or_else_default::check(cx, expr, recv, u_arg); - unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); - }, - }, - _ => {}, + } } } } diff --git a/clippy_lints/src/methods/unwrap_used.rs b/clippy_lints/src/methods/unwrap_used.rs index 44676d78c607..5c761014927c 100644 --- a/clippy_lints/src/methods/unwrap_used.rs +++ b/clippy_lints/src/methods/unwrap_used.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; +use clippy_utils::is_in_test_function; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; @@ -7,7 +8,7 @@ use rustc_span::sym; use super::UNWRAP_USED; /// lint use of `unwrap()` for `Option`s and `Result`s -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) { let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) { @@ -18,6 +19,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr None }; + if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { + return; + } + if let Some((lint, kind, none_value)) = mess { span_lint_and_help( cx, diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 74b0168a1794..bdcd76d153fb 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -316,6 +316,14 @@ define_Conf! { /// /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes (max_include_file_size: u64 = 1_000_000), + /// Lint: EXPECT_USED. + /// + /// Whether `expect` should be allowed in test functions + (allow_expect_in_tests: bool = false), + /// Lint: UNWRAP_USED. + /// + /// Whether `unwrap` should be allowed in test functions + (allow_unwrap_in_tests: bool = false), } /// Search for the configuration file. diff --git a/tests/ui-toml/expect_used/clippy.toml b/tests/ui-toml/expect_used/clippy.toml new file mode 100644 index 000000000000..6933b8164195 --- /dev/null +++ b/tests/ui-toml/expect_used/clippy.toml @@ -0,0 +1 @@ +allow-expect-in-tests = true diff --git a/tests/ui-toml/expect_used/expect_used.rs b/tests/ui-toml/expect_used/expect_used.rs new file mode 100644 index 000000000000..22dcd3ae9d69 --- /dev/null +++ b/tests/ui-toml/expect_used/expect_used.rs @@ -0,0 +1,29 @@ +// compile-flags: --test +#![warn(clippy::expect_used)] + +fn expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +fn expect_result() { + let res: Result = Ok(0); + let _ = res.expect(""); +} + +fn main() { + expect_option(); + expect_result(); +} + +#[test] +fn test_expect_option() { + let opt = Some(0); + let _ = opt.expect(""); +} + +#[test] +fn test_expect_result() { + let res: Result = Ok(0); + let _ = res.expect(""); +} diff --git a/tests/ui-toml/expect_used/expect_used.stderr b/tests/ui-toml/expect_used/expect_used.stderr new file mode 100644 index 000000000000..9cb2199ed21c --- /dev/null +++ b/tests/ui-toml/expect_used/expect_used.stderr @@ -0,0 +1,19 @@ +error: used `expect()` on `an Option` value + --> $DIR/expect_used.rs:6:13 + | +LL | let _ = opt.expect(""); + | ^^^^^^^^^^^^^^ + | + = note: `-D clippy::expect-used` implied by `-D warnings` + = help: if this value is an `None`, it will panic + +error: used `expect()` on `a Result` value + --> $DIR/expect_used.rs:11:13 + | +LL | let _ = res.expect(""); + | ^^^^^^^^^^^^^^ + | + = help: if this value is an `Err`, it will panic + +error: aborting due to 2 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 8701809b4daa..65277dd03e83 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -1,4 +1,4 @@ -error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `third-party` at line 5 column 1 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `avoid-breaking-exported-api`, `msrv`, `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `disallowed-types`, `unreadable-literal-lint-fractions`, `upper-case-acronyms-aggressive`, `cargo-ignore-publish`, `standard-macro-braces`, `enforced-import-renames`, `allowed-scripts`, `enable-raw-pointer-heuristic-for-send`, `max-suggested-slice-pattern-length`, `await-holding-invalid-types`, `max-include-file-size`, `allow-expect-in-tests`, `allow-unwrap-in-tests`, `third-party` at line 5 column 1 error: aborting due to previous error diff --git a/tests/ui-toml/unwrap_used/clippy.toml b/tests/ui-toml/unwrap_used/clippy.toml new file mode 100644 index 000000000000..154626ef4e81 --- /dev/null +++ b/tests/ui-toml/unwrap_used/clippy.toml @@ -0,0 +1 @@ +allow-unwrap-in-tests = true diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs new file mode 100644 index 000000000000..a639de079715 --- /dev/null +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -0,0 +1,74 @@ +// compile-flags: --test + +#![allow(unused_mut, clippy::from_iter_instead_of_collect)] +#![warn(clippy::unwrap_used)] +#![deny(clippy::get_unwrap)] + +use std::collections::BTreeMap; +use std::collections::HashMap; +use std::collections::VecDeque; +use std::iter::FromIterator; + +struct GetFalsePositive { + arr: [u32; 3], +} + +impl GetFalsePositive { + fn get(&self, pos: usize) -> Option<&u32> { + self.arr.get(pos) + } + fn get_mut(&mut self, pos: usize) -> Option<&mut u32> { + self.arr.get_mut(pos) + } +} + +fn main() { + let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let mut some_slice = &mut [0, 1, 2, 3]; + let mut some_vec = vec![0, 1, 2, 3]; + let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect(); + let mut some_hashmap: HashMap = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut some_btreemap: BTreeMap = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]); + let mut false_positive = GetFalsePositive { arr: [0, 1, 2] }; + + { + // Test `get().unwrap()` + let _ = boxed_slice.get(1).unwrap(); + let _ = some_slice.get(0).unwrap(); + let _ = some_vec.get(0).unwrap(); + let _ = some_vecdeque.get(0).unwrap(); + let _ = some_hashmap.get(&1).unwrap(); + let _ = some_btreemap.get(&1).unwrap(); + #[allow(clippy::unwrap_used)] + let _ = false_positive.get(0).unwrap(); + // Test with deref + let _: u8 = *boxed_slice.get(1).unwrap(); + } + + { + // Test `get_mut().unwrap()` + *boxed_slice.get_mut(0).unwrap() = 1; + *some_slice.get_mut(0).unwrap() = 1; + *some_vec.get_mut(0).unwrap() = 1; + *some_vecdeque.get_mut(0).unwrap() = 1; + // Check false positives + #[allow(clippy::unwrap_used)] + { + *some_hashmap.get_mut(&1).unwrap() = 'b'; + *some_btreemap.get_mut(&1).unwrap() = 'b'; + *false_positive.get_mut(0).unwrap() = 1; + } + } + + { + // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()` + let _ = some_vec.get(0..1).unwrap().to_vec(); + let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + } +} + +#[test] +fn test() { + let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]); + let _ = boxed_slice.get(1).unwrap(); +} diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr new file mode 100644 index 000000000000..6c3adc5395f7 --- /dev/null +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -0,0 +1,197 @@ +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:36:17 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + | +note: the lint level is defined here + --> $DIR/unwrap_used.rs:5:9 + | +LL | #![deny(clippy::get_unwrap)] + | ^^^^^^^^^^^^^^^^^^ + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:36:17 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unwrap-used` implied by `-D warnings` + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:37:17 + | +LL | let _ = some_slice.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:37:17 + | +LL | let _ = some_slice.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:38:17 + | +LL | let _ = some_vec.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:38:17 + | +LL | let _ = some_vec.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:39:17 + | +LL | let _ = some_vecdeque.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:39:17 + | +LL | let _ = some_vecdeque.get(0).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:40:17 + | +LL | let _ = some_hashmap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:40:17 + | +LL | let _ = some_hashmap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:41:17 + | +LL | let _ = some_btreemap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:41:17 + | +LL | let _ = some_btreemap.get(&1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:45:21 + | +LL | let _: u8 = *boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:45:22 + | +LL | let _: u8 = *boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:50:9 + | +LL | *boxed_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:50:10 + | +LL | *boxed_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:51:9 + | +LL | *some_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:51:10 + | +LL | *some_slice.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:52:9 + | +LL | *some_vec.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:52:10 + | +LL | *some_vec.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:53:9 + | +LL | *some_vecdeque.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:53:10 + | +LL | *some_vecdeque.get_mut(0).unwrap() = 1; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:65:17 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:65:17 + | +LL | let _ = some_vec.get(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:66:17 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` + +error: used `unwrap()` on `an Option` value + --> $DIR/unwrap_used.rs:66:17 + | +LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise + --> $DIR/unwrap_used.rs:73:13 + | +LL | let _ = boxed_slice.get(1).unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` + +error: aborting due to 27 previous errors + From 21e4765e58b0f92bc266c5370ecec270c0b2206c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 31 Mar 2022 21:49:50 +0200 Subject: [PATCH 464/536] Test `expect` attribute for tool lints, clippy edition (RFC 2383) --- tests/ui/expect_tool_lint_rfc_2383.rs | 142 ++++++++++++++++++++++ tests/ui/expect_tool_lint_rfc_2383.stderr | 40 ++++++ 2 files changed, 182 insertions(+) create mode 100644 tests/ui/expect_tool_lint_rfc_2383.rs create mode 100644 tests/ui/expect_tool_lint_rfc_2383.stderr diff --git a/tests/ui/expect_tool_lint_rfc_2383.rs b/tests/ui/expect_tool_lint_rfc_2383.rs new file mode 100644 index 000000000000..28b37f96e911 --- /dev/null +++ b/tests/ui/expect_tool_lint_rfc_2383.rs @@ -0,0 +1,142 @@ +// check-pass +#![feature(lint_reasons)] +//! This file tests the `#[expect]` attribute implementation for tool lints. The same +//! file is used to test clippy and rustdoc. Any changes to this file should be synced +//! to the other test files as well. +//! +//! Expectations: +//! * rustc: only rustc lint expectations are emitted +//! * clippy: rustc and Clippy's expectations are emitted +//! * rustdoc: only rustdoc lint expectations are emitted +//! +//! This test can't cover every lint from Clippy, rustdoc and potentially other +//! tools that will be developed. This therefore only tests a small subset of lints +#![expect(rustdoc::missing_crate_level_docs)] + +mod rustc_ok { + //! See + + #[expect(dead_code)] + pub fn rustc_lints() { + let x = 42.0; + + #[expect(illegal_floating_point_literal_pattern)] + match x { + 5.0 => {} + 6.0 => {} + _ => {} + } + } +} + +mod rustc_warn { + //! See + + #[expect(dead_code)] + pub fn rustc_lints() { + let x = 42; + + #[expect(illegal_floating_point_literal_pattern)] + match x { + 5 => {} + 6 => {} + _ => {} + } + } +} + +pub mod rustdoc_ok { + //! See + + #[expect(rustdoc::broken_intra_doc_links)] + /// I want to link to [`Nonexistent`] but it doesn't exist! + pub fn foo() {} + + #[expect(rustdoc::invalid_html_tags)] + ///

    + pub fn bar() {} + + #[expect(rustdoc::bare_urls)] + /// http://example.org + pub fn baz() {} +} + +pub mod rustdoc_warn { + //! See + + #[expect(rustdoc::broken_intra_doc_links)] + /// I want to link to [`bar`] but it doesn't exist! + pub fn foo() {} + + #[expect(rustdoc::invalid_html_tags)] + ///

    + pub fn bar() {} + + #[expect(rustdoc::bare_urls)] + /// + pub fn baz() {} +} + +mod clippy_ok { + //! See + + #[expect(clippy::almost_swapped)] + fn foo() { + let mut a = 0; + let mut b = 9; + a = b; + b = a; + } + + #[expect(clippy::bytes_nth)] + fn bar() { + let _ = "Hello".bytes().nth(3); + } + + #[expect(clippy::if_same_then_else)] + fn baz() { + let _ = if true { 42 } else { 42 }; + } + + #[expect(clippy::logic_bug)] + fn burger() { + let a = false; + let b = true; + + if a && b || a {} + } +} + +mod clippy_warn { + //! See + + #[expect(clippy::almost_swapped)] + fn foo() { + let mut a = 0; + let mut b = 9; + a = b; + } + + #[expect(clippy::bytes_nth)] + fn bar() { + let _ = "Hello".as_bytes().get(3); + } + + #[expect(clippy::if_same_then_else)] + fn baz() { + let _ = if true { 33 } else { 42 }; + } + + #[expect(clippy::logic_bug)] + fn burger() { + let a = false; + let b = true; + let c = false; + + if a && b || c {} + } +} + +fn main() { + rustc_warn::rustc_lints(); +} diff --git a/tests/ui/expect_tool_lint_rfc_2383.stderr b/tests/ui/expect_tool_lint_rfc_2383.stderr new file mode 100644 index 000000000000..db29e85a8219 --- /dev/null +++ b/tests/ui/expect_tool_lint_rfc_2383.stderr @@ -0,0 +1,40 @@ +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:35:14 + | +LL | #[expect(dead_code)] + | ^^^^^^^^^ + | + = note: `-D unfulfilled-lint-expectations` implied by `-D warnings` + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:39:18 + | +LL | #[expect(illegal_floating_point_literal_pattern)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:113:14 + | +LL | #[expect(clippy::almost_swapped)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:120:14 + | +LL | #[expect(clippy::bytes_nth)] + | ^^^^^^^^^^^^^^^^^ + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:125:14 + | +LL | #[expect(clippy::if_same_then_else)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: this lint expectation is unfulfilled + --> $DIR/expect_tool_lint_rfc_2383.rs:130:14 + | +LL | #[expect(clippy::logic_bug)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors + From 5e4f0922911536f80d9591180fa604229ac13939 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 9 May 2022 14:06:46 +0100 Subject: [PATCH 465/536] use let chains in bit_mask.rs --- clippy_lints/src/bit_mask.rs | 40 ++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/clippy_lints/src/bit_mask.rs b/clippy_lints/src/bit_mask.rs index ca4af66cad16..dc7e400fdc28 100644 --- a/clippy_lints/src/bit_mask.rs +++ b/clippy_lints/src/bit_mask.rs @@ -1,7 +1,6 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -130,23 +129,24 @@ impl<'tcx> LateLintPass<'tcx> for BitMask { } } } - if_chain! { - if let ExprKind::Binary(op, left, right) = &e.kind; - if BinOpKind::Eq == op.node; - if let ExprKind::Binary(op1, left1, right1) = &left.kind; - if BinOpKind::BitAnd == op1.node; - if let ExprKind::Lit(lit) = &right1.kind; - if let LitKind::Int(n, _) = lit.node; - if let ExprKind::Lit(lit1) = &right.kind; - if let LitKind::Int(0, _) = lit1.node; - if n.leading_zeros() == n.count_zeros(); - if n > u128::from(self.verbose_bit_mask_threshold); - then { - span_lint_and_then(cx, - VERBOSE_BIT_MASK, - e.span, - "bit mask could be simplified with a call to `trailing_zeros`", - |diag| { + + if let ExprKind::Binary(op, left, right) = &e.kind + && BinOpKind::Eq == op.node + && let ExprKind::Binary(op1, left1, right1) = &left.kind + && BinOpKind::BitAnd == op1.node + && let ExprKind::Lit(lit) = &right1.kind + && let LitKind::Int(n, _) = lit.node + && let ExprKind::Lit(lit1) = &right.kind + && let LitKind::Int(0, _) = lit1.node + && n.leading_zeros() == n.count_zeros() + && n > u128::from(self.verbose_bit_mask_threshold) + { + span_lint_and_then( + cx, + VERBOSE_BIT_MASK, + e.span, + "bit mask could be simplified with a call to `trailing_zeros`", + |diag| { let sugg = Sugg::hir(cx, left1, "...").maybe_par(); diag.span_suggestion( e.span, @@ -154,8 +154,8 @@ impl<'tcx> LateLintPass<'tcx> for BitMask { format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()), Applicability::MaybeIncorrect, ); - }); - } + }, + ); } } } From c9d88ef962a5a9ae3ef6a9b111533e831dcd145c Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 9 May 2022 14:23:16 +0100 Subject: [PATCH 466/536] Recommend let chains over if_chain in docs --- CONTRIBUTING.md | 8 ++++---- doc/adding_lints.md | 4 ++-- doc/common_tools_writing_lints.md | 26 +++++++++++--------------- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 022ba5d84148..6ab2bd59137f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,7 +69,7 @@ and resolved paths. To figure out how this syntax structure is encoded in the AST, it is recommended to run `rustc -Z unpretty=ast-tree` on an example of the structure and compare with the [nodes in the AST docs]. Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting]. -But we can make it nest-less by using [if_chain] macro, [like this][nest-less]. +But we can make it nest-less by using [let chains], [like this][nest-less]. [`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`] first. Sometimes they are only somewhat involved code wise, but not difficult per-se. @@ -87,9 +87,9 @@ an AST expression). `match_def_path()` in Clippy's `utils` module can also be us [`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium [`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty [nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/ -[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/mem_forget.rs#L29-L43 -[if_chain]: https://docs.rs/if_chain/*/if_chain -[nest-less]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/bit_mask.rs#L124-L150 +[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/mem_forget.rs#L31-L45 +[let chains]: https://github.com/rust-lang/rust/pull/94927 +[nest-less]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/bit_mask.rs#L133-L159 ## Writing code diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 5607e2264a65..4dc94d9f5a5b 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -656,7 +656,7 @@ Here are some pointers to things you are likely going to need for every lint: * [Clippy utils][utils] - Various helper functions. Maybe the function you need is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc) * [Clippy diagnostics][diagnostics] -* [The `if_chain` macro][if_chain] +* [Let chains][let-chains] * [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro] * [`Span`][span] * [`Applicability`][applicability] @@ -684,7 +684,7 @@ don't hesitate to ask on [Zulip] or in the issue/PR. [`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html [`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html [`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html -[if_chain]: https://docs.rs/if_chain/*/if_chain/ +[let-chains]: https://github.com/rust-lang/rust/pull/94927 [from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion [in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html [span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md index 828bf4cbef94..131ac3c3611e 100644 --- a/doc/common_tools_writing_lints.md +++ b/doc/common_tools_writing_lints.md @@ -62,16 +62,14 @@ Starting with an `expr`, you can check whether it is calling a specific method ` ```rust impl<'tcx> LateLintPass<'tcx> for MyStructLint { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if_chain! { - // Check our expr is calling a method - if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind; + // Check our expr is calling a method + if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind // Check the name of this method is `some_method` - if path.ident.name == sym!(some_method); + && path.ident.name == sym!(some_method) // Optionally, check the type of the self argument. // - See "Checking for a specific type" - then { + { // ... - } } } } @@ -165,18 +163,16 @@ use clippy_utils::{is_type_diagnostic_item, return_ty}; impl<'tcx> LateLintPass<'tcx> for MyTypeImpl { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) { - if_chain! { - // Check if item is a method/function - if let ImplItemKind::Fn(ref signature, _) = impl_item.kind; + // Check if item is a method/function + if let ImplItemKind::Fn(ref signature, _) = impl_item.kind // Check the method is named `some_method` - if impl_item.ident.name == sym!(some_method); + && impl_item.ident.name == sym!(some_method) // We can also check it has a parameter `self` - if signature.decl.implicit_self.has_implicit_self(); + && signature.decl.implicit_self.has_implicit_self() // We can go further and even check if its return type is `String` - if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)); - then { - // ... - } + && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type)) + { + // ... } } } From 47e9afa20bf5b4d1828e2b8c3e5fa049388e664f Mon Sep 17 00:00:00 2001 From: SparrowLii Date: Mon, 9 May 2022 21:48:57 +0800 Subject: [PATCH 467/536] fix clippy --- clippy_lints/src/collapsible_match.rs | 4 ++-- clippy_lints/src/entry.rs | 4 ++-- clippy_lints/src/only_used_in_recursion.rs | 2 +- clippy_lints/src/utils/author.rs | 10 +++++----- clippy_utils/src/hir_utils.rs | 6 ++++-- 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/collapsible_match.rs b/clippy_lints/src/collapsible_match.rs index cc354b50afa3..826eb0ae6b13 100644 --- a/clippy_lints/src/collapsible_match.rs +++ b/clippy_lints/src/collapsible_match.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_s use if_chain::if_chain; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, Guard, HirId, Pat, PatKind}; +use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::Span; @@ -109,7 +109,7 @@ fn check_arm<'tcx>( (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), }; // the binding must not be used in the if guard - if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(_, e))| !is_local_used(cx, *e, binding_id)); + if outer_guard.map_or(true, |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)); // ...or anywhere in the inner expression if match inner { IfLetOrMatch::IfLet(_, _, body, els) => { diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 1ae2e20c1e06..d3d3ed2c2357 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -11,7 +11,7 @@ use rustc_errors::Applicability; use rustc_hir::{ hir_id::HirIdSet, intravisit::{walk_expr, Visitor}, - Block, Expr, ExprKind, Guard, HirId, Pat, Stmt, StmtKind, UnOp, + Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -478,7 +478,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> { let mut is_map_used = self.is_map_used; for arm in arms { self.visit_pat(arm.pat); - if let Some(Guard::If(guard) | Guard::IfLet(_, guard)) = arm.guard { + if let Some(Guard::If(guard) | Guard::IfLet(&Let { init: guard, .. })) = arm.guard { self.visit_non_tail_expr(guard); } is_map_used |= self.visit_cond_arm(arm.body); diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs index beb812793f81..d66698f8adc6 100644 --- a/clippy_lints/src/only_used_in_recursion.rs +++ b/clippy_lints/src/only_used_in_recursion.rs @@ -596,7 +596,7 @@ impl<'tcx> SideEffectVisit<'tcx> { let mut vars = std::mem::take(&mut self.ret_vars); let _ = arm.guard.as_ref().map(|guard| { self.visit_expr(match guard { - Guard::If(expr) | Guard::IfLet(_, expr) => expr, + Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr, }); vars.append(&mut self.ret_vars); }); diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index ff5be825b781..3f4d0fd199d0 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -315,11 +315,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { out!("if let Some(Guard::If({expr})) = {arm}.guard;"); self.expr(expr); }, - Some(hir::Guard::IfLet(pat, expr)) => { - bind!(self, pat, expr); - out!("if let Some(Guard::IfLet({pat}, {expr}) = {arm}.guard;"); - self.pat(pat); - self.expr(expr); + Some(hir::Guard::IfLet(let_expr)) => { + bind!(self, let_expr); + out!("if let Some(Guard::IfLet({let_expr}) = {arm}.guard;"); + self.pat(field!(let_expr.pat)); + self.expr(field!(let_expr.init)); }, } self.expr(field!(arm.body)); diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index f4da625f1e30..aa21f15ee5d9 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -301,7 +301,9 @@ impl HirEqInterExpr<'_, '_, '_> { fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool { match (left, right) { (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r), - (Guard::IfLet(lp, le), Guard::IfLet(rp, re)) => self.eq_pat(lp, rp) && self.eq_expr(le, re), + (Guard::IfLet(l), Guard::IfLet(r)) => { + self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init) + }, _ => false, } } @@ -894,7 +896,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { pub fn hash_guard(&mut self, g: &Guard<'_>) { match g { - Guard::If(expr) | Guard::IfLet(_, expr) => { + Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => { self.hash_expr(expr); }, } From 0f1544f15ed774f25388c0420997e52a56d19d06 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 9 May 2022 11:41:25 -0400 Subject: [PATCH 468/536] Reduce unnecessary work in `cmp_owned` --- clippy_lints/src/misc.rs | 46 +++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index ac82dd306a52..18a98fc6f840 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -20,8 +20,8 @@ use rustc_span::symbol::sym; use clippy_utils::consts::{constant, Constant}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats, - last_path_segment, match_any_def_paths, path_def_id, paths, unsext, SpanlessEq, + get_item_name, get_parent_expr, in_constant, is_integer_const, iter_input_pats, last_path_segment, + match_any_def_paths, path_def_id, paths, unsext, SpanlessEq, }; declare_clippy_lint! { @@ -569,33 +569,30 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: }) } - let (arg_ty, snip) = match expr.kind { - ExprKind::MethodCall(.., args, _) if args.len() == 1 => { - if_chain!( - if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); - if is_diag_trait_item(cx, expr_def_id, sym::ToString) - || is_diag_trait_item(cx, expr_def_id, sym::ToOwned); - then { - (cx.typeck_results().expr_ty(&args[0]), snippet(cx, args[0].span, "..")) - } else { - return; - } - ) + let typeck = cx.typeck_results(); + let (arg, arg_span) = match expr.kind { + ExprKind::MethodCall(.., [arg], _) + if typeck + .type_dependent_def_id(expr.hir_id) + .and_then(|id| cx.tcx.trait_of_item(id)) + .map_or(false, |id| { + matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned)) + }) => + { + (arg, arg.span) }, - ExprKind::Call(path, [arg]) => { + ExprKind::Call(path, [arg]) if path_def_id(cx, path) .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .is_some() - { - (cx.typeck_results().expr_ty(arg), snippet(cx, arg.span, "..")) - } else { - return; - } + .is_some() => + { + (arg, arg.span) }, _ => return, }; - let other_ty = cx.typeck_results().expr_ty(other); + let arg_ty = typeck.expr_ty(arg); + let other_ty = typeck.expr_ty(other); let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default(); let with_deref = arg_ty @@ -627,13 +624,14 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: return; } + let arg_snip = snippet(cx, arg_span, ".."); let expr_snip; let eq_impl; if with_deref.is_implemented() { - expr_snip = format!("*{}", snip); + expr_snip = format!("*{}", arg_snip); eq_impl = with_deref; } else { - expr_snip = snip.to_string(); + expr_snip = arg_snip.to_string(); eq_impl = without_deref; }; From 993b4016db6684eeac1d5e9e986d18bd7f2fe1a3 Mon Sep 17 00:00:00 2001 From: Jason Newcomb Date: Mon, 9 May 2022 12:29:31 -0400 Subject: [PATCH 469/536] Don't lint `cmp_owned` when `From::from` results in a copy type. --- clippy_lints/src/misc.rs | 8 +++++-- tests/ui/cmp_owned/without_suggestion.rs | 22 ++++++++++++++++++++ tests/ui/cmp_owned/without_suggestion.stderr | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 18a98fc6f840..27a15b106fb8 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::implements_trait; +use clippy_utils::ty::{implements_trait, is_copy}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -584,7 +584,11 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: ExprKind::Call(path, [arg]) if path_def_id(cx, path) .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .is_some() => + .map_or(false, |idx| match idx { + 0 => true, + 1 => !is_copy(cx, typeck.expr_ty(expr)), + _ => false, + }) => { (arg, arg.span) }, diff --git a/tests/ui/cmp_owned/without_suggestion.rs b/tests/ui/cmp_owned/without_suggestion.rs index f44a3901fb48..ae0862257eb7 100644 --- a/tests/ui/cmp_owned/without_suggestion.rs +++ b/tests/ui/cmp_owned/without_suggestion.rs @@ -9,6 +9,10 @@ fn main() { let x = &&Baz; let y = &Baz; y.to_owned() == **x; + + let x = 0u32; + let y = U32Wrapper(x); + let _ = U32Wrapper::from(x) == y; } struct Foo; @@ -51,3 +55,21 @@ impl std::borrow::Borrow for Bar { &FOO } } + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct U32Wrapper(u32); +impl From for U32Wrapper { + fn from(x: u32) -> Self { + Self(x) + } +} +impl PartialEq for U32Wrapper { + fn eq(&self, other: &u32) -> bool { + self.0 == *other + } +} +impl PartialEq for u32 { + fn eq(&self, other: &U32Wrapper) -> bool { + *self == other.0 + } +} diff --git a/tests/ui/cmp_owned/without_suggestion.stderr b/tests/ui/cmp_owned/without_suggestion.stderr index 2ea3d8fac0d1..d2dd14d8edbb 100644 --- a/tests/ui/cmp_owned/without_suggestion.stderr +++ b/tests/ui/cmp_owned/without_suggestion.stderr @@ -13,7 +13,7 @@ LL | y.to_owned() == **x; | ^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating error: this creates an owned instance just for comparison - --> $DIR/without_suggestion.rs:18:9 + --> $DIR/without_suggestion.rs:22:9 | LL | self.to_owned() == *other | ^^^^^^^^^^^^^^^^^^^^^^^^^ try implementing the comparison without allocating From 6edf0b4f3613c59cac2401c35b1b6218fd518623 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Mon, 9 May 2022 20:30:06 +0100 Subject: [PATCH 470/536] Ignore unfulfilled_lint_expectations in metadata collection --- tests/dogfood.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/dogfood.rs b/tests/dogfood.rs index eb97d1933d5d..9da80518ce91 100644 --- a/tests/dogfood.rs +++ b/tests/dogfood.rs @@ -21,7 +21,7 @@ fn dogfood_clippy() { // "" is the root package for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] { - run_clippy_for_package(package); + run_clippy_for_package(package, &[]); } } @@ -38,7 +38,7 @@ fn run_metadata_collection_lint() { // Run collection as is std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); - run_clippy_for_package("clippy_lints"); + run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); // Check if cargo caching got in the way if let Ok(file) = File::open(metadata_output_path) { @@ -61,10 +61,10 @@ fn run_metadata_collection_lint() { .unwrap(); // Running the collection again - run_clippy_for_package("clippy_lints"); + run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]); } -fn run_clippy_for_package(project: &str) { +fn run_clippy_for_package(project: &str, args: &[&str]) { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH); @@ -76,6 +76,7 @@ fn run_clippy_for_package(project: &str) { .arg("--all-targets") .arg("--all-features") .arg("--") + .args(args) .args(&["-D", "clippy::all"]) .args(&["-D", "clippy::pedantic"]) .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir From fe84ff336055412f0df8a2e625eaf46c6701e574 Mon Sep 17 00:00:00 2001 From: nsunderland1 Date: Fri, 6 May 2022 00:10:11 -0700 Subject: [PATCH 471/536] New lint: [`derive_partial_eq_without_eq`] --- CHANGELOG.md | 1 + clippy_dev/src/update_lints.rs | 6 +- clippy_lints/src/checked_conversions.rs | 2 +- clippy_lints/src/derive.rs | 72 +++++++++++++- clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_style.rs | 1 + clippy_lints/src/loops/utils.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/methods/str_splitn.rs | 2 +- clippy_utils/src/numeric_literal.rs | 2 +- tests/ui/absurd-extreme-comparisons.rs | 2 +- tests/ui/assign_ops2.rs | 2 +- .../ui/cmp_owned/asymmetric_partial_eq.fixed | 2 +- tests/ui/cmp_owned/asymmetric_partial_eq.rs | 2 +- tests/ui/cmp_owned/with_suggestion.fixed | 4 +- tests/ui/cmp_owned/with_suggestion.rs | 4 +- tests/ui/cmp_owned/without_suggestion.rs | 4 +- tests/ui/crashes/ice-6254.rs | 1 + tests/ui/crashes/ice-6254.stderr | 2 +- tests/ui/derive_hash_xor_eq.rs | 2 + tests/ui/derive_hash_xor_eq.stderr | 16 +-- tests/ui/derive_partial_eq_without_eq.fixed | 98 +++++++++++++++++++ tests/ui/derive_partial_eq_without_eq.rs | 98 +++++++++++++++++++ tests/ui/derive_partial_eq_without_eq.stderr | 46 +++++++++ tests/ui/equatable_if_let.fixed | 2 +- tests/ui/equatable_if_let.rs | 2 +- tests/ui/unit_cmp.rs | 6 +- tests/ui/unit_cmp.stderr | 12 +-- 29 files changed, 359 insertions(+), 38 deletions(-) create mode 100644 tests/ui/derive_partial_eq_without_eq.fixed create mode 100644 tests/ui/derive_partial_eq_without_eq.rs create mode 100644 tests/ui/derive_partial_eq_without_eq.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 751f9fccd88d..0608c360d302 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3350,6 +3350,7 @@ Released 2018-09-13 [`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls [`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq [`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord +[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq [`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods [`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs index 1a6a4336da27..e9cc4f299431 100644 --- a/clippy_dev/src/update_lints.rs +++ b/clippy_dev/src/update_lints.rs @@ -17,7 +17,7 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html"; -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum UpdateMode { Check, Change, @@ -372,7 +372,7 @@ fn exit_with_failure() { } /// Lint data parsed from the Clippy source code. -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] struct Lint { name: String, group: String, @@ -414,7 +414,7 @@ impl Lint { } } -#[derive(Clone, PartialEq, Debug)] +#[derive(Clone, PartialEq, Eq, Debug)] struct DeprecatedLint { name: String, reason: String, diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index e23428e216d0..28c77ea40ef7 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -123,7 +123,7 @@ struct Conversion<'a> { } /// The kind of conversion that is checked -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] enum ConversionType { SignedToUnsigned, SignedToSigned, diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 557e101494e3..a4757ebd8c79 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,8 +1,9 @@ -use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::paths; use clippy_utils::ty::{implements_trait, is_copy}; use clippy_utils::{is_automatically_derived, is_lint_allowed, match_def_path}; use if_chain::if_chain; +use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety, @@ -156,11 +157,44 @@ declare_clippy_lint! { "deriving `serde::Deserialize` on a type that has methods using `unsafe`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for types that derive `PartialEq` and could implement `Eq`. + /// + /// ### Why is this bad? + /// If a type `T` derives `PartialEq` and all of its members implement `Eq`, + /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used + /// in APIs that require `Eq` types. It also allows structs containing `T` to derive + /// `Eq` themselves. + /// + /// ### Example + /// ```rust + /// #[derive(PartialEq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + /// Use instead: + /// ```rust + /// #[derive(PartialEq, Eq)] + /// struct Foo { + /// i_am_eq: i32, + /// i_am_eq_too: Vec, + /// } + /// ``` + #[clippy::version = "1.62.0"] + pub DERIVE_PARTIAL_EQ_WITHOUT_EQ, + style, + "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`" +} + declare_lint_pass!(Derive => [ EXPL_IMPL_CLONE_ON_COPY, DERIVE_HASH_XOR_EQ, DERIVE_ORD_XOR_PARTIAL_ORD, - UNSAFE_DERIVE_DESERIALIZE + UNSAFE_DERIVE_DESERIALIZE, + DERIVE_PARTIAL_EQ_WITHOUT_EQ ]); impl<'tcx> LateLintPass<'tcx> for Derive { @@ -179,6 +213,7 @@ impl<'tcx> LateLintPass<'tcx> for Derive { if is_automatically_derived { check_unsafe_derive_deserialize(cx, item, trait_ref, ty); + check_partial_eq_without_eq(cx, item.span, trait_ref, ty); } else { check_copy_clone(cx, item, trait_ref, ty); } @@ -419,3 +454,36 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { self.cx.tcx.hir() } } + +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) { + if_chain! { + if let ty::Adt(adt, substs) = ty.kind(); + if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); + if let Some(def_id) = trait_ref.trait_def_id(); + if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); + if !implements_trait(cx, ty, eq_trait_def_id, substs); + then { + // If all of our fields implement `Eq`, we can implement `Eq` too + for variant in adt.variants() { + for field in &variant.fields { + let ty = field.ty(cx.tcx, substs); + + if !implements_trait(cx, ty, eq_trait_def_id, substs) { + return; + } + } + } + + span_lint_and_sugg( + cx, + DERIVE_PARTIAL_EQ_WITHOUT_EQ, + span.ctxt().outer_expn_data().call_site, + "you are deriving `PartialEq` and can implement `Eq`", + "consider deriving `Eq` as well", + "PartialEq, Eq".to_string(), + Applicability::MachineApplicable, + ) + } + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index e68718f9fdf9..0f43b320f76a 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -46,6 +46,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 5768edc50188..4e302b10a0fd 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -113,6 +113,7 @@ store.register_lints(&[ derivable_impls::DERIVABLE_IMPLS, derive::DERIVE_HASH_XOR_EQ, derive::DERIVE_ORD_XOR_PARTIAL_ORD, + derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ, derive::EXPL_IMPL_CLONE_ON_COPY, derive::UNSAFE_DERIVE_DESERIALIZE, disallowed_methods::DISALLOWED_METHODS, diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index d183c0449cd5..62f26d821a0d 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -16,6 +16,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), + LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ), LintId::of(disallowed_methods::DISALLOWED_METHODS), LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), diff --git a/clippy_lints/src/loops/utils.rs b/clippy_lints/src/loops/utils.rs index 772d251b620a..4801a84eb92c 100644 --- a/clippy_lints/src/loops/utils.rs +++ b/clippy_lints/src/loops/utils.rs @@ -13,7 +13,7 @@ use rustc_span::symbol::{sym, Symbol}; use rustc_typeck::hir_ty_to_ty; use std::iter::Iterator; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] enum IncrementVisitorVarState { Initial, // Not examined yet IncrOnce, // Incremented exactly once, may be a loop counter diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index e452614ce17f..cfeee4d0c709 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -2835,7 +2835,7 @@ const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [ ShouldImplTraitCase::new("std::ops::Sub", "sub", 2, FN_HEADER, SelfKind::Value, OutType::Any, true), ]; -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] enum SelfKind { Value, Ref, diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index fc375763542a..90651a6ba045 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -271,7 +271,7 @@ enum IterUsageKind { NextTuple, } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Eq)] enum UnwrapKind { Unwrap, QuestionMark, diff --git a/clippy_utils/src/numeric_literal.rs b/clippy_utils/src/numeric_literal.rs index b92d42e83232..3fb5415ce029 100644 --- a/clippy_utils/src/numeric_literal.rs +++ b/clippy_utils/src/numeric_literal.rs @@ -1,7 +1,7 @@ use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind}; use std::iter; -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum Radix { Binary, Octal, diff --git a/tests/ui/absurd-extreme-comparisons.rs b/tests/ui/absurd-extreme-comparisons.rs index d205b383d1ff..f682b280c1b8 100644 --- a/tests/ui/absurd-extreme-comparisons.rs +++ b/tests/ui/absurd-extreme-comparisons.rs @@ -37,7 +37,7 @@ fn main() { use std::cmp::{Ordering, PartialEq, PartialOrd}; -#[derive(PartialEq, PartialOrd)] +#[derive(PartialEq, Eq, PartialOrd)] pub struct U(u64); impl PartialEq for U { diff --git a/tests/ui/assign_ops2.rs b/tests/ui/assign_ops2.rs index 4703a8c77778..f6d3a8fa3f0d 100644 --- a/tests/ui/assign_ops2.rs +++ b/tests/ui/assign_ops2.rs @@ -24,7 +24,7 @@ fn main() { use std::ops::{Mul, MulAssign}; -#[derive(Copy, Clone, Debug, PartialEq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Wrap(i64); impl Mul for Wrap { diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed index 3305ac9bf8b6..abd059c23080 100644 --- a/tests/ui/cmp_owned/asymmetric_partial_eq.fixed +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.fixed @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, clippy::redundant_clone)] // See #5700 +#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700 // Define the types in each module to avoid trait impls leaking between modules. macro_rules! impl_types { diff --git a/tests/ui/cmp_owned/asymmetric_partial_eq.rs b/tests/ui/cmp_owned/asymmetric_partial_eq.rs index 88bc2f51dd66..020ef5f840bd 100644 --- a/tests/ui/cmp_owned/asymmetric_partial_eq.rs +++ b/tests/ui/cmp_owned/asymmetric_partial_eq.rs @@ -1,5 +1,5 @@ // run-rustfix -#![allow(unused, clippy::redundant_clone)] // See #5700 +#![allow(unused, clippy::redundant_clone, clippy::derive_partial_eq_without_eq)] // See #5700 // Define the types in each module to avoid trait impls leaking between modules. macro_rules! impl_types { diff --git a/tests/ui/cmp_owned/with_suggestion.fixed b/tests/ui/cmp_owned/with_suggestion.fixed index 05fb96339e33..b28c4378e33c 100644 --- a/tests/ui/cmp_owned/with_suggestion.fixed +++ b/tests/ui/cmp_owned/with_suggestion.fixed @@ -45,7 +45,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { @@ -61,7 +61,7 @@ impl std::borrow::Borrow for Bar { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { diff --git a/tests/ui/cmp_owned/with_suggestion.rs b/tests/ui/cmp_owned/with_suggestion.rs index 0a02825ed82f..c1089010fe10 100644 --- a/tests/ui/cmp_owned/with_suggestion.rs +++ b/tests/ui/cmp_owned/with_suggestion.rs @@ -45,7 +45,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { @@ -61,7 +61,7 @@ impl std::borrow::Borrow for Bar { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { diff --git a/tests/ui/cmp_owned/without_suggestion.rs b/tests/ui/cmp_owned/without_suggestion.rs index f44a3901fb48..738d082339a1 100644 --- a/tests/ui/cmp_owned/without_suggestion.rs +++ b/tests/ui/cmp_owned/without_suggestion.rs @@ -26,7 +26,7 @@ impl ToOwned for Foo { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Baz; impl ToOwned for Baz { @@ -36,7 +36,7 @@ impl ToOwned for Baz { } } -#[derive(PartialEq)] +#[derive(PartialEq, Eq)] struct Bar; impl PartialEq for Bar { diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs index c19eca43884a..a2a60a169153 100644 --- a/tests/ui/crashes/ice-6254.rs +++ b/tests/ui/crashes/ice-6254.rs @@ -2,6 +2,7 @@ // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())', // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5 +#[allow(clippy::derive_partial_eq_without_eq)] #[derive(PartialEq)] struct Foo(i32); const FOO_REF_REF: &&Foo = &&Foo(42); diff --git a/tests/ui/crashes/ice-6254.stderr b/tests/ui/crashes/ice-6254.stderr index 95ebf23d8181..f37ab2e9b0c7 100644 --- a/tests/ui/crashes/ice-6254.stderr +++ b/tests/ui/crashes/ice-6254.stderr @@ -1,5 +1,5 @@ error: to use a constant of type `Foo` in a pattern, `Foo` must be annotated with `#[derive(PartialEq, Eq)]` - --> $DIR/ice-6254.rs:12:9 + --> $DIR/ice-6254.rs:13:9 | LL | FOO_REF_REF => {}, | ^^^^^^^^^^^ diff --git a/tests/ui/derive_hash_xor_eq.rs b/tests/ui/derive_hash_xor_eq.rs index 10abe22d53e5..813ddc566464 100644 --- a/tests/ui/derive_hash_xor_eq.rs +++ b/tests/ui/derive_hash_xor_eq.rs @@ -1,3 +1,5 @@ +#![allow(clippy::derive_partial_eq_without_eq)] + #[derive(PartialEq, Hash)] struct Foo; diff --git a/tests/ui/derive_hash_xor_eq.stderr b/tests/ui/derive_hash_xor_eq.stderr index b383072ca4db..e5184bd1407c 100644 --- a/tests/ui/derive_hash_xor_eq.stderr +++ b/tests/ui/derive_hash_xor_eq.stderr @@ -1,12 +1,12 @@ error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:10:10 + --> $DIR/derive_hash_xor_eq.rs:12:10 | LL | #[derive(Hash)] | ^^^^ | = note: `#[deny(clippy::derive_hash_xor_eq)]` on by default note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:13:1 + --> $DIR/derive_hash_xor_eq.rs:15:1 | LL | / impl PartialEq for Bar { LL | | fn eq(&self, _: &Bar) -> bool { @@ -17,13 +17,13 @@ LL | | } = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are deriving `Hash` but have implemented `PartialEq` explicitly - --> $DIR/derive_hash_xor_eq.rs:19:10 + --> $DIR/derive_hash_xor_eq.rs:21:10 | LL | #[derive(Hash)] | ^^^^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:22:1 + --> $DIR/derive_hash_xor_eq.rs:24:1 | LL | / impl PartialEq for Baz { LL | | fn eq(&self, _: &Baz) -> bool { @@ -34,7 +34,7 @@ LL | | } = note: this error originates in the derive macro `Hash` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:31:1 + --> $DIR/derive_hash_xor_eq.rs:33:1 | LL | / impl std::hash::Hash for Bah { LL | | fn hash(&self, _: &mut H) {} @@ -42,14 +42,14 @@ LL | | } | |_^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:28:10 + --> $DIR/derive_hash_xor_eq.rs:30:10 | LL | #[derive(PartialEq)] | ^^^^^^^^^ = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) error: you are implementing `Hash` explicitly but have derived `PartialEq` - --> $DIR/derive_hash_xor_eq.rs:49:5 + --> $DIR/derive_hash_xor_eq.rs:51:5 | LL | / impl Hash for Foo3 { LL | | fn hash(&self, _: &mut H) {} @@ -57,7 +57,7 @@ LL | | } | |_____^ | note: `PartialEq` implemented here - --> $DIR/derive_hash_xor_eq.rs:46:14 + --> $DIR/derive_hash_xor_eq.rs:48:14 | LL | #[derive(PartialEq)] | ^^^^^^^^^ diff --git a/tests/ui/derive_partial_eq_without_eq.fixed b/tests/ui/derive_partial_eq_without_eq.fixed new file mode 100644 index 000000000000..7d4d1b3b6490 --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.fixed @@ -0,0 +1,98 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::derive_partial_eq_without_eq)] + +// Don't warn on structs that aren't PartialEq +struct NotPartialEq { + foo: u32, + bar: String, +} + +// Eq can be derived but is missing +#[derive(Debug, PartialEq, Eq)] +struct MissingEq { + foo: u32, + bar: String, +} + +// Eq is derived +#[derive(PartialEq, Eq)] +struct NotMissingEq { + foo: u32, + bar: String, +} + +// Eq is manually implemented +#[derive(PartialEq)] +struct ManualEqImpl { + foo: u32, + bar: String, +} + +impl Eq for ManualEqImpl {} + +// Cannot be Eq because f32 isn't Eq +#[derive(PartialEq)] +struct CannotBeEq { + foo: u32, + bar: f32, +} + +// Don't warn if PartialEq is manually implemented +struct ManualPartialEqImpl { + foo: u32, + bar: String, +} + +impl PartialEq for ManualPartialEqImpl { + fn eq(&self, other: &Self) -> bool { + self.foo == other.foo && self.bar == other.bar + } +} + +// Generic fields should be properly checked for Eq-ness +#[derive(PartialEq)] +struct GenericNotEq { + foo: T, + bar: U, +} + +#[derive(PartialEq, Eq)] +struct GenericEq { + foo: T, + bar: U, +} + +#[derive(PartialEq, Eq)] +struct TupleStruct(u32); + +#[derive(PartialEq, Eq)] +struct GenericTupleStruct(T); + +#[derive(PartialEq)] +struct TupleStructNotEq(f32); + +#[derive(PartialEq, Eq)] +enum Enum { + Foo(u32), + Bar { a: String, b: () }, +} + +#[derive(PartialEq, Eq)] +enum GenericEnum { + Foo(T), + Bar { a: U, b: V }, +} + +#[derive(PartialEq)] +enum EnumNotEq { + Foo(u32), + Bar { a: String, b: f32 }, +} + +// Ensure that rustfix works properly when `PartialEq` has other derives on either side +#[derive(Debug, PartialEq, Eq, Clone)] +struct RustFixWithOtherDerives; + +fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.rs b/tests/ui/derive_partial_eq_without_eq.rs new file mode 100644 index 000000000000..ab4e1df1ca40 --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.rs @@ -0,0 +1,98 @@ +// run-rustfix + +#![allow(unused)] +#![warn(clippy::derive_partial_eq_without_eq)] + +// Don't warn on structs that aren't PartialEq +struct NotPartialEq { + foo: u32, + bar: String, +} + +// Eq can be derived but is missing +#[derive(Debug, PartialEq)] +struct MissingEq { + foo: u32, + bar: String, +} + +// Eq is derived +#[derive(PartialEq, Eq)] +struct NotMissingEq { + foo: u32, + bar: String, +} + +// Eq is manually implemented +#[derive(PartialEq)] +struct ManualEqImpl { + foo: u32, + bar: String, +} + +impl Eq for ManualEqImpl {} + +// Cannot be Eq because f32 isn't Eq +#[derive(PartialEq)] +struct CannotBeEq { + foo: u32, + bar: f32, +} + +// Don't warn if PartialEq is manually implemented +struct ManualPartialEqImpl { + foo: u32, + bar: String, +} + +impl PartialEq for ManualPartialEqImpl { + fn eq(&self, other: &Self) -> bool { + self.foo == other.foo && self.bar == other.bar + } +} + +// Generic fields should be properly checked for Eq-ness +#[derive(PartialEq)] +struct GenericNotEq { + foo: T, + bar: U, +} + +#[derive(PartialEq)] +struct GenericEq { + foo: T, + bar: U, +} + +#[derive(PartialEq)] +struct TupleStruct(u32); + +#[derive(PartialEq)] +struct GenericTupleStruct(T); + +#[derive(PartialEq)] +struct TupleStructNotEq(f32); + +#[derive(PartialEq)] +enum Enum { + Foo(u32), + Bar { a: String, b: () }, +} + +#[derive(PartialEq)] +enum GenericEnum { + Foo(T), + Bar { a: U, b: V }, +} + +#[derive(PartialEq)] +enum EnumNotEq { + Foo(u32), + Bar { a: String, b: f32 }, +} + +// Ensure that rustfix works properly when `PartialEq` has other derives on either side +#[derive(Debug, PartialEq, Clone)] +struct RustFixWithOtherDerives; + +fn main() {} diff --git a/tests/ui/derive_partial_eq_without_eq.stderr b/tests/ui/derive_partial_eq_without_eq.stderr new file mode 100644 index 000000000000..bf55165890a5 --- /dev/null +++ b/tests/ui/derive_partial_eq_without_eq.stderr @@ -0,0 +1,46 @@ +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:13:17 + | +LL | #[derive(Debug, PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + | + = note: `-D clippy::derive-partial-eq-without-eq` implied by `-D warnings` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:61:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:67:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:70:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:76:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:82:10 + | +LL | #[derive(PartialEq)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: you are deriving `PartialEq` and can implement `Eq` + --> $DIR/derive_partial_eq_without_eq.rs:95:17 + | +LL | #[derive(Debug, PartialEq, Clone)] + | ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq` + +error: aborting due to 7 previous errors + diff --git a/tests/ui/equatable_if_let.fixed b/tests/ui/equatable_if_let.fixed index 88918d9671e4..47bf25e409bd 100644 --- a/tests/ui/equatable_if_let.fixed +++ b/tests/ui/equatable_if_let.fixed @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, dead_code)] +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)] #![warn(clippy::equatable_if_let)] use std::cmp::Ordering; diff --git a/tests/ui/equatable_if_let.rs b/tests/ui/equatable_if_let.rs index 9a7ab75ef450..d498bca2455b 100644 --- a/tests/ui/equatable_if_let.rs +++ b/tests/ui/equatable_if_let.rs @@ -1,6 +1,6 @@ // run-rustfix -#![allow(unused_variables, dead_code)] +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)] #![warn(clippy::equatable_if_let)] use std::cmp::Ordering; diff --git a/tests/ui/unit_cmp.rs b/tests/ui/unit_cmp.rs index 8d3a4eed82e3..3d271104361b 100644 --- a/tests/ui/unit_cmp.rs +++ b/tests/ui/unit_cmp.rs @@ -1,5 +1,9 @@ #![warn(clippy::unit_cmp)] -#![allow(clippy::no_effect, clippy::unnecessary_operation)] +#![allow( + clippy::no_effect, + clippy::unnecessary_operation, + clippy::derive_partial_eq_without_eq +)] #[derive(PartialEq)] pub struct ContainsUnit(()); // should be fine diff --git a/tests/ui/unit_cmp.stderr b/tests/ui/unit_cmp.stderr index 824506a4257e..41cf19ae685e 100644 --- a/tests/ui/unit_cmp.stderr +++ b/tests/ui/unit_cmp.stderr @@ -1,5 +1,5 @@ error: ==-comparison of unit values detected. This will always be true - --> $DIR/unit_cmp.rs:12:8 + --> $DIR/unit_cmp.rs:16:8 | LL | if { | ________^ @@ -12,7 +12,7 @@ LL | | } {} = note: `-D clippy::unit-cmp` implied by `-D warnings` error: >-comparison of unit values detected. This will always be false - --> $DIR/unit_cmp.rs:18:8 + --> $DIR/unit_cmp.rs:22:8 | LL | if { | ________^ @@ -23,7 +23,7 @@ LL | | } {} | |_____^ error: `assert_eq` of unit values detected. This will always succeed - --> $DIR/unit_cmp.rs:24:5 + --> $DIR/unit_cmp.rs:28:5 | LL | / assert_eq!( LL | | { @@ -35,7 +35,7 @@ LL | | ); | |_____^ error: `debug_assert_eq` of unit values detected. This will always succeed - --> $DIR/unit_cmp.rs:32:5 + --> $DIR/unit_cmp.rs:36:5 | LL | / debug_assert_eq!( LL | | { @@ -47,7 +47,7 @@ LL | | ); | |_____^ error: `assert_ne` of unit values detected. This will always fail - --> $DIR/unit_cmp.rs:41:5 + --> $DIR/unit_cmp.rs:45:5 | LL | / assert_ne!( LL | | { @@ -59,7 +59,7 @@ LL | | ); | |_____^ error: `debug_assert_ne` of unit values detected. This will always fail - --> $DIR/unit_cmp.rs:49:5 + --> $DIR/unit_cmp.rs:53:5 | LL | / debug_assert_ne!( LL | | { From 107ee4067414dc02da091ce49fb491695bbb52f0 Mon Sep 17 00:00:00 2001 From: lcnr Date: Tue, 26 Apr 2022 14:04:23 +0200 Subject: [PATCH 472/536] update clippy --- clippy_lints/src/derivable_impls.rs | 6 +++--- clippy_lints/src/derive.rs | 10 +++++----- clippy_lints/src/functions/must_use.rs | 8 ++++---- clippy_lints/src/manual_non_exhaustive.rs | 3 +-- clippy_lints/src/matches/match_wild_enum.rs | 3 +-- clippy_lints/src/new_without_default.rs | 2 +- clippy_lints/src/partialeq_ne_impl.rs | 4 +--- clippy_lints/src/significant_drop_in_scrutinee.rs | 2 +- clippy_utils/src/attrs.rs | 7 ++----- clippy_utils/src/lib.rs | 15 ++------------- clippy_utils/src/ty.rs | 12 ++++++------ 11 files changed, 27 insertions(+), 45 deletions(-) diff --git a/clippy_lints/src/derivable_impls.rs b/clippy_lints/src/derivable_impls.rs index 14098340745b..34a5f8444dea 100644 --- a/clippy_lints/src/derivable_impls.rs +++ b/clippy_lints/src/derivable_impls.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{is_automatically_derived, is_default_equivalent, peel_blocks}; +use clippy_utils::{is_default_equivalent, peel_blocks}; use rustc_hir::{ def::{DefKind, Res}, Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind, @@ -71,8 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { self_ty, .. }) = item.kind; - if let attrs = cx.tcx.hir().attrs(item.hir_id()); - if !is_automatically_derived(attrs); + if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); if !item.span.from_expansion(); if let Some(def_id) = trait_ref.trait_def_id(); if cx.tcx.is_diagnostic_item(sym::Default, def_id); @@ -81,6 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls { if let ImplItemKind::Fn(_, b) = &impl_item.kind; if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def(); + if let attrs = cx.tcx.hir().attrs(item.hir_id()); if !attrs.iter().any(|attr| attr.doc_str().is_some()); if let child_attrs = cx.tcx.hir().attrs(impl_item_hir); if !child_attrs.iter().any(|attr| attr.doc_str().is_some()); diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index 557e101494e3..545bc7d21033 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then}; use clippy_utils::paths; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{is_automatically_derived, is_lint_allowed, match_def_path}; +use clippy_utils::{is_lint_allowed, match_def_path}; use if_chain::if_chain; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ @@ -171,8 +171,8 @@ impl<'tcx> LateLintPass<'tcx> for Derive { }) = item.kind { let ty = cx.tcx.type_of(item.def_id); - let attrs = cx.tcx.hir().attrs(item.hir_id()); - let is_automatically_derived = is_automatically_derived(attrs); + let is_automatically_derived = + cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); @@ -201,7 +201,7 @@ fn check_hash_peq<'tcx>( then { // Look for the PartialEq implementations for `ty` cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { - let peq_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); + let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); if peq_is_automatically_derived == hash_is_automatically_derived { return; @@ -255,7 +255,7 @@ fn check_ord_partial_ord<'tcx>( then { // Look for the PartialOrd implementations for `ty` cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { - let partial_ord_is_automatically_derived = is_automatically_derived(cx.tcx.get_attrs(impl_id)); + let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); if partial_ord_is_automatically_derived == ord_is_automatically_derived { return; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 38e943d2eb87..6672a6cb0b58 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -13,13 +13,13 @@ use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method}; +use clippy_utils::{match_def_path, return_ty, trait_ref_of_method}; use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); + let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -44,7 +44,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Imp let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); + let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.def_id).is_none() { @@ -67,7 +67,7 @@ pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Tr let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); let attrs = cx.tcx.hir().attrs(item.hir_id()); - let attr = must_use_attr(attrs); + let attr = cx.tcx.get_attr(item.def_id.to_def_id(), sym::must_use); if let Some(attr) = attr { check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr); } else if let hir::TraitFn::Provided(eid) = *eid { diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index b8d620d81713..09164690700e 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,4 +1,3 @@ -use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::{is_lint_allowed, meets_msrv, msrvs}; @@ -161,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { let id = cx.tcx.hir().local_def_id(v.id); (matches!(v.data, hir::VariantData::Unit(_)) && v.ident.as_str().starts_with('_') - && is_doc_hidden(cx.tcx.get_attrs(id.to_def_id()))) + && cx.tcx.is_doc_hidden(id.to_def_id())) .then(|| (id, v.span)) }); if let Some((id, span)) = iter.next() diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs index 93bf0dc62e07..fc45ccee1852 100644 --- a/clippy_lints/src/matches/match_wild_enum.rs +++ b/clippy_lints/src/matches/match_wild_enum.rs @@ -193,6 +193,5 @@ impl<'a> CommonPrefixSearcher<'a> { } fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool { - let attrs = cx.tcx.get_attrs(variant_def.def_id); - clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs) + cx.tcx.is_doc_hidden(variant_def.def_id) || cx.tcx.has_attr(variant_def.def_id, sym::unstable) } diff --git a/clippy_lints/src/new_without_default.rs b/clippy_lints/src/new_without_default.rs index 2f733f221d57..2bdccb425071 100644 --- a/clippy_lints/src/new_without_default.rs +++ b/clippy_lints/src/new_without_default.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault { // can't be implemented for unsafe new return; } - if clippy_utils::is_doc_hidden(cx.tcx.hir().attrs(id)) { + if cx.tcx.is_doc_hidden(impl_item.def_id) { // shouldn't be implemented when it is hidden in docs return; } diff --git a/clippy_lints/src/partialeq_ne_impl.rs b/clippy_lints/src/partialeq_ne_impl.rs index 1469cb434c00..09ac514d014e 100644 --- a/clippy_lints/src/partialeq_ne_impl.rs +++ b/clippy_lints/src/partialeq_ne_impl.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_hir; -use clippy_utils::is_automatically_derived; use if_chain::if_chain; use rustc_hir::{Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -37,8 +36,7 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { if_chain! { if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), items: impl_items, .. }) = item.kind; - let attrs = cx.tcx.hir().attrs(item.hir_id()); - if !is_automatically_derived(attrs); + if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived); if let Some(eq_trait) = cx.tcx.lang_items().eq_trait(); if trait_ref.path.res.def_id() == eq_trait; then { diff --git a/clippy_lints/src/significant_drop_in_scrutinee.rs b/clippy_lints/src/significant_drop_in_scrutinee.rs index 94ae0c8f5a65..f300acf0fb2b 100644 --- a/clippy_lints/src/significant_drop_in_scrutinee.rs +++ b/clippy_lints/src/significant_drop_in_scrutinee.rs @@ -290,7 +290,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> { fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { if let Some(adt) = ty.ty_adt_def() { - if get_attr(cx.sess(), cx.tcx.get_attrs(adt.did()), "has_significant_drop").count() > 0 { + if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 { return true; } } diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs index 7f448175e326..904b1a05ccc3 100644 --- a/clippy_utils/src/attrs.rs +++ b/clippy_utils/src/attrs.rs @@ -1,6 +1,7 @@ -use rustc_ast::{ast, attr}; +use rustc_ast::ast; use rustc_errors::Applicability; use rustc_session::Session; +use rustc_ast::attr; use rustc_span::sym; use std::str::FromStr; @@ -158,7 +159,3 @@ pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool { .any(|l| attr::list_contains_name(&l, sym::hidden)) } -/// Return true if the attributes contain `#[unstable]` -pub fn is_unstable(attrs: &[ast::Attribute]) -> bool { - attrs.iter().any(|attr| attr.has_name(sym::unstable)) -} diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 7d46952d9718..98a073d122e3 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -66,7 +66,7 @@ use std::lazy::SyncOnceCell; use std::sync::{Mutex, MutexGuard}; use if_chain::if_chain; -use rustc_ast::ast::{self, Attribute, LitKind}; +use rustc_ast::ast::{self, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unhash::UnhashMap; use rustc_hir as hir; @@ -1472,12 +1472,6 @@ pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx> } } -/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d -/// implementations have. -pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool { - has_attr(attrs, sym::automatically_derived) -} - pub fn is_self(slf: &Param<'_>) -> bool { if let PatKind::Binding(.., name, _) = slf.pat.kind { name.name == kw::SelfLower @@ -1724,11 +1718,6 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t None } -// Finds the `#[must_use]` attribute, if any -pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> { - attrs.iter().find(|a| a.has_name(sym::must_use)) -} - // check if expr is calling method or function with #[must_use] attribute pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let did = match expr.kind { @@ -1745,7 +1734,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { _ => None, }; - did.map_or(false, |did| must_use_attr(cx.tcx.get_attrs(did)).is_some()) + did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use)) } /// Checks if an expression represents the identity function diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 901e3e5390c5..7f14a306d0e5 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -22,7 +22,7 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::query::normalize::AtExt; use std::iter; -use crate::{match_def_path, must_use_attr, path_res, paths}; +use crate::{match_def_path, path_res, paths}; // Checks if the given type implements copy. pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { @@ -178,18 +178,18 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { // Returns whether the type has #[must_use] attribute pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { match ty.kind() { - ty::Adt(adt, _) => must_use_attr(cx.tcx.get_attrs(adt.did())).is_some(), - ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(), + ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use), + ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use), ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => { // for the Array case we don't need to care for the len == 0 case // because we don't want to lint functions returning empty arrays is_must_use_ty(cx, *ty) }, ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)), - ty::Opaque(ref def_id, _) => { + ty::Opaque(def_id, _) => { for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) { if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() { - if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() { + if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { return true; } } @@ -199,7 +199,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { ty::Dynamic(binder, _) => { for predicate in binder.iter() { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() { + if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) { return true; } } From feb6d8cf30ffe4341d2bd41c35554a85951e4440 Mon Sep 17 00:00:00 2001 From: yonip23 Date: Sun, 8 May 2022 22:20:25 +0300 Subject: [PATCH 473/536] introduce rc_clone_in_vec_init lint --- CHANGELOG.md | 1 + clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_lints/src/rc_clone_in_vec_init.rs | 90 +++++++++++++++++++++ tests/ui/rc_clone_in_vec_init/arc.rs | 49 +++++++++++ tests/ui/rc_clone_in_vec_init/arc.stderr | 30 +++++++ tests/ui/rc_clone_in_vec_init/rc.rs | 50 ++++++++++++ tests/ui/rc_clone_in_vec_init/rc.stderr | 30 +++++++ 10 files changed, 255 insertions(+) create mode 100644 clippy_lints/src/rc_clone_in_vec_init.rs create mode 100644 tests/ui/rc_clone_in_vec_init/arc.rs create mode 100644 tests/ui/rc_clone_in_vec_init/arc.stderr create mode 100644 tests/ui/rc_clone_in_vec_init/rc.rs create mode 100644 tests/ui/rc_clone_in_vec_init/rc.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 751f9fccd88d..3a274ae1d9c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3642,6 +3642,7 @@ Released 2018-09-13 [`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero [`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len [`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer +[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init [`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex [`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl [`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index e68718f9fdf9..b608ef9c6930 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -263,6 +263,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(ranges::MANUAL_RANGE_CONTAINS), LintId::of(ranges::RANGE_ZIP_WITH_LEN), LintId::of(ranges::REVERSED_EMPTY_RANGES), + LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL), LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 5768edc50188..9224d9d51131 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -446,6 +446,7 @@ store.register_lints(&[ ranges::RANGE_PLUS_ONE, ranges::RANGE_ZIP_WITH_LEN, ranges::REVERSED_EMPTY_RANGES, + rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, redundant_clone::REDUNDANT_CLONE, redundant_closure_call::REDUNDANT_CLOSURE_CALL, redundant_else::REDUNDANT_ELSE, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 7a881bfe3991..940bf7ace5e8 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -26,6 +26,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(methods::SUSPICIOUS_MAP), LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(octal_escapes::OCTAL_ESCAPES), + LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), ]) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 3bb821a14829..3c3cb51c8e39 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -345,6 +345,7 @@ mod ptr_offset_with_cast; mod pub_use; mod question_mark; mod ranges; +mod rc_clone_in_vec_init; mod redundant_clone; mod redundant_closure_call; mod redundant_else; @@ -890,6 +891,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: let max_include_file_size = conf.max_include_file_size; store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); + store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs new file mode 100644 index 000000000000..6391890b0433 --- /dev/null +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -0,0 +1,90 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::higher::VecArgs; +use clippy_utils::last_path_segment; +use clippy_utils::macros::{root_macro_call_first_node, MacroCall}; +use rustc_hir::{Expr, ExprKind, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::{sym, Symbol}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]` + /// + /// ### Why is this bad? + /// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc` + /// is a bit misleading, as it will create references to the same pointer, rather + /// than different instances. + /// + /// ### Example + /// ```rust + /// let v = vec![std::sync::Arc::new("some data".to_string()); 100]; + /// // or + /// let v = vec![std::rc::Rc::new("some data".to_string()); 100]; + /// ``` + /// Use instead: + /// ```rust + /// + /// // Initialize each value separately: + /// let mut data = Vec::with_capacity(100); + /// for _ in 0..100 { + /// data.push(std::rc::Rc::new("some data".to_string())); + /// } + /// + /// // Or if you want clones of the same reference, + /// // Create the reference beforehand to clarify that + /// // it should be cloned for each value + /// let data = std::rc::Rc::new("some data".to_string()); + /// let v = vec![data; 100]; + /// ``` + #[clippy::version = "1.62.0"] + pub RC_CLONE_IN_VEC_INIT, + suspicious, + "initializing `Arc` or `Rc` in `vec![elem; len]`" +} +declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); + +impl LateLintPass<'_> for RcCloneInVecInit { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; + let Some(VecArgs::Repeat(elem, _)) = VecArgs::hir(cx, expr) else { return; }; + let Some(symbol) = new_reference_call(cx, elem) else { return; }; + + emit_lint(cx, symbol, ¯o_call); + } +} + +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, macro_call: &MacroCall) { + let symbol_name = symbol.as_str(); + + span_lint_and_then( + cx, + RC_CLONE_IN_VEC_INIT, + macro_call.span, + &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), + |diag| { + diag.note(format!("each element will point to the same `{symbol_name}` instance")); + diag.help(format!( + "if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" + )); + diag.help("or if not, initialize each element individually"); + }, + ); +} + +/// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new` +fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { + if_chain! { + if let ExprKind::Call(func, _args) = expr.kind; + if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind; + if let TyKind::Path(ref ty_path) = ty.kind; + if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id(); + if last_path_segment(func_path).ident.name == sym::new; + + then { + return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc); + } + } + + None +} diff --git a/tests/ui/rc_clone_in_vec_init/arc.rs b/tests/ui/rc_clone_in_vec_init/arc.rs new file mode 100644 index 000000000000..9f4e27dfa624 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/arc.rs @@ -0,0 +1,49 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::sync::{Arc, Mutex}; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![Arc::new("x".to_string()); 2]; +} + +fn should_warn_complex_case() { + let v = vec![ + std::sync::Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; +} + +fn should_not_warn_custom_arc() { + #[derive(Clone)] + struct Arc; + + impl Arc { + fn new() -> Self { + Arc + } + } + + let v = vec![Arc::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_arc() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(std::sync::Arc::new({ + let y = 3; + dbg!(y); + y + })); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Arc::new("x".to_string())]; +} diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr new file mode 100644 index 000000000000..19e4727cb987 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -0,0 +1,30 @@ +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:7:13 + | +LL | let v = vec![Arc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Arc` instance + = help: if this is intentional, consider extracting the `Arc` initialization to a variable + = help: or if not, initialize each element individually + +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:11:13 + | +LL | let v = vec![ + | _____________^ +LL | | std::sync::Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Arc` instance + = help: if this is intentional, consider extracting the `Arc` initialization to a variable + = help: or if not, initialize each element individually + +error: aborting due to 2 previous errors + diff --git a/tests/ui/rc_clone_in_vec_init/rc.rs b/tests/ui/rc_clone_in_vec_init/rc.rs new file mode 100644 index 000000000000..5e2834aa9023 --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/rc.rs @@ -0,0 +1,50 @@ +#![warn(clippy::rc_clone_in_vec_init)] +use std::rc::Rc; +use std::sync::Mutex; + +fn main() {} + +fn should_warn_simple_case() { + let v = vec![Rc::new("x".to_string()); 2]; +} + +fn should_warn_complex_case() { + let v = vec![ + std::rc::Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; +} + +fn should_not_warn_custom_arc() { + #[derive(Clone)] + struct Rc; + + impl Rc { + fn new() -> Self { + Rc + } + } + + let v = vec![Rc::new(); 2]; +} + +fn should_not_warn_vec_from_elem_but_not_rc() { + let v = vec![String::new(); 2]; + let v1 = vec![1; 2]; + let v2 = vec![ + Box::new(std::rc::Rc::new({ + let y = 3; + dbg!(y); + y + })); + 2 + ]; +} + +fn should_not_warn_vec_macro_but_not_from_elem() { + let v = vec![Rc::new("x".to_string())]; +} diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr new file mode 100644 index 000000000000..50ffcca9676a --- /dev/null +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -0,0 +1,30 @@ +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:8:13 + | +LL | let v = vec![Rc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` + = note: each element will point to the same `Rc` instance + = help: if this is intentional, consider extracting the `Rc` initialization to a variable + = help: or if not, initialize each element individually + +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:12:13 + | +LL | let v = vec![ + | _____________^ +LL | | std::rc::Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Rc` instance + = help: if this is intentional, consider extracting the `Rc` initialization to a variable + = help: or if not, initialize each element individually + +error: aborting due to 2 previous errors + From aaf87c3871c16d087aac4d09acdb15a5d6331acd Mon Sep 17 00:00:00 2001 From: tamaron Date: Tue, 10 May 2022 23:08:18 +0900 Subject: [PATCH 474/536] fix --- .../src/undocumented_unsafe_blocks.rs | 103 ++++++++++++------ tests/ui/undocumented_unsafe_blocks.rs | 49 ++++++++- tests/ui/undocumented_unsafe_blocks.stderr | 68 +++++++++++- 3 files changed, 177 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 09efb12f5daa..1992f61bdb43 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -140,6 +140,7 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> boo } /// Checks if the lines immediately preceding the item contain a safety comment. +#[allow(clippy::collapsible_match)] fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { if span_from_macro_expansion_has_safety_comment(cx, item.span) || span_in_body_has_safety_comment(cx, item.span) { return true; @@ -147,48 +148,51 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { if item.span.ctxt() == SyntaxContext::root() { if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { - let mut span_before_item = None; - let mut hi = false; - if let Node::Item(parent_item) = parent_node { - if let ItemKind::Mod(parent_mod) = &parent_item.kind { - for (idx, item_id) in parent_mod.item_ids.iter().enumerate() { - if *item_id == item.item_id() { - if idx == 0 { - // mod A { /* comment */ unsafe impl T {} ... } - // ^------------------------------------------^ gets this span - // ^---------------------^ finally checks the text in this range - hi = false; - span_before_item = Some(parent_item.span); - } else { - let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]); - // some_item /* comment */ unsafe impl T {} - // ^-------^ gets this span - // ^---------------^ finally checks the text in this range - hi = true; - span_before_item = Some(prev_item.span); - } - break; - } + let comment_start; + match parent_node { + Node::Crate(parent_mod) => { + comment_start = comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item); + }, + Node::Item(parent_item) => { + if let ItemKind::Mod(parent_mod) = &parent_item.kind { + comment_start = comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item); + } else { + // Doesn't support impls in this position. Pretend a comment was found. + return true; } - } + }, + Node::Stmt(stmt) => { + if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) { + match stmt_parent { + Node::Block(block) => { + comment_start = walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo); + }, + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + }, + } + } else { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + } + }, + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return true; + }, } - let span_before_item = span_before_item.unwrap(); let source_map = cx.sess().source_map(); - if let Some(item_span) = walk_span_to_context(item.span, SyntaxContext::root()) - && let Some(span_before_item) = walk_span_to_context(span_before_item, SyntaxContext::root()) - && let Ok(unsafe_line) = source_map.lookup_line(item_span.lo()) - && let Ok(line_before_unsafe) = source_map.lookup_line(if hi { - span_before_item.hi() - } else { - span_before_item.lo() - }) - && Lrc::ptr_eq(&unsafe_line.sf, &line_before_unsafe.sf) + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) && let Some(src) = unsafe_line.sf.src.as_deref() { - line_before_unsafe.line < unsafe_line.line && text_has_safety_comment( + comment_start_line.line < unsafe_line.line && text_has_safety_comment( src, - &unsafe_line.sf.lines[line_before_unsafe.line + 1..=unsafe_line.line], + &unsafe_line.sf.lines[comment_start_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos.to_usize(), ) } else { @@ -204,6 +208,35 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { } } +fn comment_start_before_impl_in_mod( + cx: &LateContext<'_>, + parent_mod: &hir::Mod<'_>, + parent_mod_span: Span, + imple: &hir::Item<'_>, +) -> Option { + parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| { + if *item_id == imple.item_id() { + if idx == 0 { + // mod A { /* comment */ unsafe impl T {} ... } + // ^------------------------------------------^ returns the start of this span + // ^---------------^ finally checks comments in this range + if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) { + return Some(sp.lo()); + } + } else { + // some_item /* comment */ unsafe impl T {} + // ^-------^ returns the end of this span + // ^---------------^ finally checks comments in this range + let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]); + if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) { + return Some(sp.hi()); + } + } + } + None + }) +} + fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); diff --git a/tests/ui/undocumented_unsafe_blocks.rs b/tests/ui/undocumented_unsafe_blocks.rs index fd64c60384d6..33b6a82f9d2c 100644 --- a/tests/ui/undocumented_unsafe_blocks.rs +++ b/tests/ui/undocumented_unsafe_blocks.rs @@ -344,7 +344,7 @@ mod unsafe_impl_smoke_test { unsafe impl A for (i32) {} mod sub_mod { - // error: also works for the first item + // error: unsafe impl B for (u32) {} unsafe trait B {} } @@ -363,24 +363,51 @@ mod unsafe_impl_smoke_test { mod unsafe_impl_from_macro { unsafe trait T {} + // error macro_rules! no_safety_comment { ($t:ty) => { unsafe impl T for $t {} }; } - // error + + // ok no_safety_comment!(()); + // ok macro_rules! with_safety_comment { ($t:ty) => { // SAFETY: unsafe impl T for $t {} }; } + // ok with_safety_comment!((i32)); } +mod unsafe_impl_macro_and_not_macro { + unsafe trait T {} + + // error + macro_rules! no_safety_comment { + ($t:ty) => { + unsafe impl T for $t {} + }; + } + + // ok + no_safety_comment!(()); + + // error + unsafe impl T for (i32) {} + + // ok + no_safety_comment!(u32); + + // error + unsafe impl T for (bool) {} +} + #[rustfmt::skip] mod unsafe_impl_valid_comment { unsafe trait SaFety {} @@ -440,4 +467,22 @@ mod unsafe_impl_invalid_comment { unsafe impl Interference for () {} } +unsafe trait ImplInFn {} + +fn impl_in_fn() { + // error + unsafe impl ImplInFn for () {} + + // SAFETY: ok + unsafe impl ImplInFn for (i32) {} +} + +unsafe trait CrateRoot {} + +// error +unsafe impl CrateRoot for () {} + +// SAFETY: ok +unsafe impl CrateRoot for (i32) {} + fn main() {} diff --git a/tests/ui/undocumented_unsafe_blocks.stderr b/tests/ui/undocumented_unsafe_blocks.stderr index 3a1f647b06d8..b79949e9d06d 100644 --- a/tests/ui/undocumented_unsafe_blocks.stderr +++ b/tests/ui/undocumented_unsafe_blocks.stderr @@ -164,7 +164,7 @@ LL | unsafe impl B for (u32) {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:368:13 + --> $DIR/undocumented_unsafe_blocks.rs:369:13 | LL | unsafe impl T for $t {} | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -176,7 +176,47 @@ LL | no_safety_comment!(()); = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:427:5 + --> $DIR/undocumented_unsafe_blocks.rs:394:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(()); + | ---------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:402:5 + | +LL | unsafe impl T for (i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:394:13 + | +LL | unsafe impl T for $t {} + | ^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | no_safety_comment!(u32); + | ----------------------- in this macro invocation + | + = help: consider adding a safety comment on the preceding line + = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:408:5 + | +LL | unsafe impl T for (bool) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:454:5 | LL | unsafe impl NoComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -184,7 +224,7 @@ LL | unsafe impl NoComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:431:19 + --> $DIR/undocumented_unsafe_blocks.rs:458:19 | LL | /* SAFETY: */ unsafe impl InlineComment for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -192,7 +232,7 @@ LL | /* SAFETY: */ unsafe impl InlineComment for () {} = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:435:5 + --> $DIR/undocumented_unsafe_blocks.rs:462:5 | LL | unsafe impl TrailingComment for () {} // SAFETY: | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -200,12 +240,28 @@ LL | unsafe impl TrailingComment for () {} // SAFETY: = help: consider adding a safety comment on the preceding line error: unsafe impl missing a safety comment - --> $DIR/undocumented_unsafe_blocks.rs:440:5 + --> $DIR/undocumented_unsafe_blocks.rs:467:5 | LL | unsafe impl Interference for () {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding a safety comment on the preceding line -error: aborting due to 25 previous errors +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:474:5 + | +LL | unsafe impl ImplInFn for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: unsafe impl missing a safety comment + --> $DIR/undocumented_unsafe_blocks.rs:483:1 + | +LL | unsafe impl CrateRoot for () {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider adding a safety comment on the preceding line + +error: aborting due to 31 previous errors From 1926217b86d0894dd5f1dc2c9c41aaed4282fd6c Mon Sep 17 00:00:00 2001 From: tamaron Date: Wed, 11 May 2022 00:27:12 +0900 Subject: [PATCH 475/536] chore --- clippy_lints/src/undocumented_unsafe_blocks.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 1992f61bdb43..bde7613b48ee 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -219,7 +219,7 @@ fn comment_start_before_impl_in_mod( if idx == 0 { // mod A { /* comment */ unsafe impl T {} ... } // ^------------------------------------------^ returns the start of this span - // ^---------------^ finally checks comments in this range + // ^---------------------^ finally checks comments in this range if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) { return Some(sp.lo()); } From 631b4ee7a26e4e68469a607376005eb4064b3e6f Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Tue, 10 May 2022 14:21:51 -0400 Subject: [PATCH 476/536] Fix redundant_allocation warning for Rc> --- .../src/types/redundant_allocation.rs | 22 ++++++++++++++++--- tests/ui/redundant_allocation.rs | 18 +++++++++++++++ tests/ui/redundant_allocation.stderr | 11 +++++++++- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 10d2ae2eb1db..5fdf17314953 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; +use rustc_hir::{self as hir, def, def_id::DefId, PrimTy, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; @@ -54,8 +54,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(ty) => { - // Box> is smaller than Box because of wide pointers - if matches!(ty.kind, TyKind::TraitObject(..)) { + if alloc_makes_pointer_thin(cx, ty) { return false; } ty.span @@ -110,3 +109,20 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ } true } + +/// Returns `true` if the allocation would make `hir_ty` go from fat to thin. +fn alloc_makes_pointer_thin(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) -> bool { + match &hir_ty.kind { + TyKind::TraitObject(..) => true, + TyKind::Path(ty_qpath) => { + let ty_res = cx.qpath_res(ty_qpath, hir_ty.hir_id); + if let def::Res::PrimTy(prim_ty) = ty_res { + if matches!(prim_ty, PrimTy::Str) { + return true; + } + } + false + }, + _ => false, + } +} diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index 80f94e5f3cbb..b830a3771d5c 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -97,4 +97,22 @@ mod box_dyn { pub fn test_rc_box(_: Rc>>) {} } +// https://github.com/rust-lang/rust-clippy/issues/8604 +mod box_str { + use std::boxed::Box; + use std::rc::Rc; + use std::sync::Arc; + + struct S { + a: Box>, + b: Rc>, + c: Arc>, + } + + pub fn test_box(_: Box>) {} + pub fn test_rc(_: Rc>) {} + pub fn test_arc(_: Arc>) {} + pub fn test_rc_box(_: Rc>>) {} +} + fn main() {} diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index c3b10e5f5e67..ae213cb8975a 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -143,5 +143,14 @@ LL | pub fn test_rc_box(_: Rc>>) {} = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation = help: consider using just `Rc>` or `Box>` -error: aborting due to 16 previous errors +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:115:27 + | +LL | pub fn test_rc_box(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: aborting due to 17 previous errors From f3e01c4f6a12ad1bd70813875ed84dbe8c830df7 Mon Sep 17 00:00:00 2001 From: yonip23 Date: Wed, 11 May 2022 00:46:20 +0300 Subject: [PATCH 477/536] add suggestions to rc_clone_in_vec_init --- clippy_lints/src/rc_clone_in_vec_init.rs | 102 ++++++++++++++++++++--- tests/ui/rc_clone_in_vec_init/arc.stderr | 34 +++++++- tests/ui/rc_clone_in_vec_init/rc.stderr | 34 +++++++- 3 files changed, 152 insertions(+), 18 deletions(-) diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index 6391890b0433..aa575d7e68bf 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -1,11 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::last_path_segment; -use clippy_utils::macros::{root_macro_call_first_node, MacroCall}; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::source::{indent_of, snippet}; +use rustc_errors::{Applicability, Diagnostic}; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::{sym, Symbol}; +use rustc_span::{sym, Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -47,27 +49,107 @@ declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]); impl LateLintPass<'_> for RcCloneInVecInit { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; - let Some(VecArgs::Repeat(elem, _)) = VecArgs::hir(cx, expr) else { return; }; + let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; let Some(symbol) = new_reference_call(cx, elem) else { return; }; + let lint_span = macro_call.span; + let symbol_name = symbol.as_str(); + let len_snippet = snippet(cx, len.span, ".."); + let elem_snippet = elem_snippet(cx, elem, symbol_name); + let indentation = indent_of(cx, lint_span).unwrap_or(0); + let lint_suggestions = + construct_lint_suggestions(lint_span, symbol_name, &elem_snippet, len_snippet.as_ref(), indentation); - emit_lint(cx, symbol, ¯o_call); + emit_lint(cx, symbol, lint_span, &lint_suggestions); } } -fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, macro_call: &MacroCall) { +struct LintSuggestion { + span: Span, + message: String, + suggestion: String, + applicability: Applicability, +} + +impl LintSuggestion { + fn span_suggestion(&self, diag: &mut Diagnostic) { + diag.span_suggestion(self.span, &self.message, &self.suggestion, self.applicability); + } +} + +fn construct_lint_suggestions( + span: Span, + symbol_name: &str, + elem_snippet: &str, + len_snippet: &str, + indentation: usize, +) -> Vec { + let indentation = " ".repeat(indentation); + let loop_init_suggestion = loop_init_suggestion(elem_snippet, len_snippet, &indentation); + let extract_suggestion = extract_suggestion(elem_snippet, len_snippet, &indentation); + + vec![ + LintSuggestion { + span, + message: format!("consider initializing each `{symbol_name}` element individually"), + suggestion: loop_init_suggestion, + applicability: Applicability::Unspecified, + }, + LintSuggestion { + span, + message: format!( + "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" + ), + suggestion: extract_suggestion, + applicability: Applicability::Unspecified, + }, + ] +} + +fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { + let mut elem_snippet = snippet(cx, elem.span, "..").to_string(); + if elem_snippet.contains('\n') { + let reference_initialization = format!("{symbol_name}::new"); + // This string must be found in `elem_snippet`, otherwise we won't be constructing the snippet in + // the first place. + let reference_initialization_end = + elem_snippet.find(&reference_initialization).unwrap() + reference_initialization.len(); + elem_snippet.replace_range(reference_initialization_end.., ".."); + } + elem_snippet +} + +fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { + format!( + r#"{{ +{indent}{indent}let mut v = Vec::with_capacity({len}); +{indent}{indent}(0..{len}).for_each(|_| v.push({elem})); +{indent}{indent}v +{indent}}}"# + ) +} + +fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { + format!( + "{{ +{indent}{indent}let data = {elem}; +{indent}{indent}vec![data; {len}] +{indent}}}" + ) +} + +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, lint_suggestions: &[LintSuggestion]) { let symbol_name = symbol.as_str(); span_lint_and_then( cx, RC_CLONE_IN_VEC_INIT, - macro_call.span, + lint_span, &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), |diag| { diag.note(format!("each element will point to the same `{symbol_name}` instance")); - diag.help(format!( - "if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" - )); - diag.help("or if not, initialize each element individually"); + lint_suggestions + .iter() + .for_each(|suggestion| suggestion.span_suggestion(diag)); }, ); } diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr index 19e4727cb987..3de96c6f1758 100644 --- a/tests/ui/rc_clone_in_vec_init/arc.stderr +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -6,8 +6,21 @@ LL | let v = vec![Arc::new("x".to_string()); 2]; | = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` = note: each element will point to the same `Arc` instance - = help: if this is intentional, consider extracting the `Arc` initialization to a variable - = help: or if not, initialize each element individually +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | error: calling `Arc::new` in `vec![elem; len]` --> $DIR/arc.rs:11:13 @@ -23,8 +36,21 @@ LL | | ]; | |_____^ | = note: each element will point to the same `Arc` instance - = help: if this is intentional, consider extracting the `Arc` initialization to a variable - = help: or if not, initialize each element individually +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(std::sync::Arc::new..)); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = std::sync::Arc::new..; +LL + vec![data; 2] +LL ~ }; + | error: aborting due to 2 previous errors diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr index 50ffcca9676a..e05f024cf9de 100644 --- a/tests/ui/rc_clone_in_vec_init/rc.stderr +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -6,8 +6,21 @@ LL | let v = vec![Rc::new("x".to_string()); 2]; | = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings` = note: each element will point to the same `Rc` instance - = help: if this is intentional, consider extracting the `Rc` initialization to a variable - = help: or if not, initialize each element individually +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | error: calling `Rc::new` in `vec![elem; len]` --> $DIR/rc.rs:12:13 @@ -23,8 +36,21 @@ LL | | ]; | |_____^ | = note: each element will point to the same `Rc` instance - = help: if this is intentional, consider extracting the `Rc` initialization to a variable - = help: or if not, initialize each element individually +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(std::rc::Rc::new..)); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = std::rc::Rc::new..; +LL + vec![data; 2] +LL ~ }; + | error: aborting due to 2 previous errors From 554dc41ceac0f4a625fc344e9248e7e1dae11378 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Wed, 20 Apr 2022 10:14:11 -0400 Subject: [PATCH 478/536] Fix `match_single_binding` for assign expressions --- .../src/matches/match_single_binding.rs | 173 ++++++++++++------ tests/ui/match_single_binding.fixed | 13 ++ tests/ui/match_single_binding.rs | 14 ++ tests/ui/match_single_binding.stderr | 38 +++- tests/ui/match_single_binding2.stderr | 4 +- 5 files changed, 178 insertions(+), 64 deletions(-) diff --git a/clippy_lints/src/matches/match_single_binding.rs b/clippy_lints/src/matches/match_single_binding.rs index 028e8c297fbd..a59711d4cace 100644 --- a/clippy_lints/src/matches/match_single_binding.rs +++ b/clippy_lints/src/matches/match_single_binding.rs @@ -1,15 +1,22 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, snippet_block, snippet_with_applicability}; +use clippy_utils::macros::HirNode; +use clippy_utils::source::{indent_of, snippet, snippet_block, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, is_refutable, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, Expr, ExprKind, Local, Node, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; +use rustc_span::Span; use super::MATCH_SINGLE_BINDING; +enum AssignmentExpr { + Assign { span: Span, match_span: Span }, + Local { span: Span, pat_span: Span }, +} + #[expect(clippy::too_many_lines)] -pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) { +pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'a>) { if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) { return; } @@ -42,61 +49,59 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e let mut applicability = Applicability::MaybeIncorrect; match arms[0].pat.kind { PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => { - // If this match is in a local (`let`) stmt - let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) { - ( - parent_let_node.span, + let (target_span, sugg) = match opt_parent_assign_span(cx, ex) { + Some(AssignmentExpr::Assign { span, match_span }) => { + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &*snippet_body, + &mut applicability, + Some(span), + ); + + span_lint_and_sugg( + cx, + MATCH_SINGLE_BINDING, + span.to(match_span), + "this assignment could be simplified", + "consider removing the `match` expression", + sugg, + applicability, + ); + + return; + }, + Some(AssignmentExpr::Local { span, pat_span }) => ( + span, format!( "let {} = {};\n{}let {} = {};", snippet_with_applicability(cx, bind_names, "..", &mut applicability), snippet_with_applicability(cx, matched_vars, "..", &mut applicability), " ".repeat(indent_of(cx, expr.span).unwrap_or(0)), - snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability), + snippet_with_applicability(cx, pat_span, "..", &mut applicability), snippet_body ), - ) - } else { - // If we are in closure, we need curly braces around suggestion - let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); - let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string()); - if let Some(parent_expr) = get_parent_expr(cx, expr) { - if let ExprKind::Closure(..) = parent_expr.kind { - cbrace_end = format!("\n{}}}", indent); - // Fix body indent due to the closure - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); - } - } - // If the parent is already an arm, and the body is another match statement, - // we need curly braces around suggestion - let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id); - if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { - if let ExprKind::Match(..) = arm.body.kind { - cbrace_end = format!("\n{}}}", indent); - // Fix body indent due to the match - indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); - cbrace_start = format!("{{\n{}", indent); - } - } - ( - expr.span, - format!( - "{}let {} = {};\n{}{}{}", - cbrace_start, - snippet_with_applicability(cx, bind_names, "..", &mut applicability), - snippet_with_applicability(cx, matched_vars, "..", &mut applicability), - indent, - snippet_body, - cbrace_end - ), - ) + ), + None => { + let sugg = sugg_with_curlies( + cx, + (ex, expr), + (bind_names, matched_vars), + &*snippet_body, + &mut applicability, + None, + ); + (expr.span, sugg) + }, }; + span_lint_and_sugg( cx, MATCH_SINGLE_BINDING, target_span, "this match could be written as a `let` statement", - "consider using `let` statement", + "consider using a `let` statement", sugg, applicability, ); @@ -110,6 +115,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e indent, snippet_body ); + span_lint_and_sugg( cx, MATCH_SINGLE_BINDING, @@ -135,15 +141,76 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e } } -/// Returns true if the `ex` match expression is in a local (`let`) statement -fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> { +/// Returns true if the `ex` match expression is in a local (`let`) or assign expression +fn opt_parent_assign_span<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option { let map = &cx.tcx.hir(); - if_chain! { - if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)); - if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id)); - then { - return Some(parent_let_expr); - } + + if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id)) { + return match map.find(map.get_parent_node(parent_arm_expr.hir_id)) { + Some(Node::Local(parent_let_expr)) => Some(AssignmentExpr::Local { + span: parent_let_expr.span, + pat_span: parent_let_expr.pat.span(), + }), + Some(Node::Expr(Expr { + kind: ExprKind::Assign(parent_assign_expr, match_expr, _), + .. + })) => Some(AssignmentExpr::Assign { + span: parent_assign_expr.span, + match_span: match_expr.span, + }), + _ => None, + }; } + None } + +fn sugg_with_curlies<'a>( + cx: &LateContext<'a>, + (ex, match_expr): (&Expr<'a>, &Expr<'a>), + (bind_names, matched_vars): (Span, Span), + snippet_body: &str, + applicability: &mut Applicability, + assignment: Option, +) -> String { + let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0)); + + let (mut cbrace_start, mut cbrace_end) = (String::new(), String::new()); + if let Some(parent_expr) = get_parent_expr(cx, match_expr) { + if let ExprKind::Closure(..) = parent_expr.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the closure + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } + + // If the parent is already an arm, and the body is another match statement, + // we need curly braces around suggestion + let parent_node_id = cx.tcx.hir().get_parent_node(match_expr.hir_id); + if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) { + if let ExprKind::Match(..) = arm.body.kind { + cbrace_end = format!("\n{}}}", indent); + // Fix body indent due to the match + indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0)); + cbrace_start = format!("{{\n{}", indent); + } + } + + let assignment_str = assignment.map_or_else(String::new, |span| { + let mut s = snippet(cx, span, "..").to_string(); + s.push_str(" = "); + s + }); + + format!( + "{}let {} = {};\n{}{}{}{}", + cbrace_start, + snippet_with_applicability(cx, bind_names, "..", applicability), + snippet_with_applicability(cx, matched_vars, "..", applicability), + indent, + assignment_str, + snippet_body, + cbrace_end + ) +} diff --git a/tests/ui/match_single_binding.fixed b/tests/ui/match_single_binding.fixed index b8dc8179f7d7..de46e6cff55b 100644 --- a/tests/ui/match_single_binding.fixed +++ b/tests/ui/match_single_binding.fixed @@ -111,3 +111,16 @@ fn main() { let x = 1; println!("Not an array index start"); } + +#[allow(dead_code)] +fn issue_8723() { + let (mut val, idx) = ("a b", 1); + + let (pre, suf) = val.split_at(idx); + val = { + println!("{}", pre); + suf + }; + + let _ = val; +} diff --git a/tests/ui/match_single_binding.rs b/tests/ui/match_single_binding.rs index fe63dcd63f2b..eea64fcb292b 100644 --- a/tests/ui/match_single_binding.rs +++ b/tests/ui/match_single_binding.rs @@ -126,3 +126,17 @@ fn main() { _ => println!("Not an array index start"), } } + +#[allow(dead_code)] +fn issue_8723() { + let (mut val, idx) = ("a b", 1); + + val = match val.split_at(idx) { + (pre, suf) => { + println!("{}", pre); + suf + }, + }; + + let _ = val; +} diff --git a/tests/ui/match_single_binding.stderr b/tests/ui/match_single_binding.stderr index d939291f53c4..5d4e7314b213 100644 --- a/tests/ui/match_single_binding.stderr +++ b/tests/ui/match_single_binding.stderr @@ -9,7 +9,7 @@ LL | | } | |_____^ | = note: `-D clippy::match-single-binding` implied by `-D warnings` -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + { @@ -25,7 +25,7 @@ LL | | (x, y, z) => println!("{} {} {}", x, y, z), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (x, y, z) = (a, b, c); LL + println!("{} {} {}", x, y, z); @@ -88,7 +88,7 @@ LL | | Point { x, y } => println!("Coords: ({}, {})", x, y), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x, y } = p; LL + println!("Coords: ({}, {})", x, y); @@ -102,7 +102,7 @@ LL | | Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x: x1, y: y1 } = p; LL + println!("Coords: ({}, {})", x1, y1); @@ -116,7 +116,7 @@ LL | | ref r => println!("Got a reference to {}", r), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let ref r = x; LL + println!("Got a reference to {}", r); @@ -130,7 +130,7 @@ LL | | ref mut mr => println!("Got a mutable reference to {}", mr), LL | | } | |_____^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let ref mut mr = x; LL + println!("Got a mutable reference to {}", mr); @@ -144,7 +144,7 @@ LL | | Point { x, y } => x * y, LL | | }; | |______^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let Point { x, y } = coords(); LL + let product = x * y; @@ -159,7 +159,7 @@ LL | | unwrapped => unwrapped, LL | | }) | |_________^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ .map(|i| { LL + let unwrapped = i.unwrap(); @@ -176,5 +176,25 @@ LL | | _ => println!("Not an array index start"), LL | | } | |_____^ help: consider using the match body instead: `println!("Not an array index start");` -error: aborting due to 12 previous errors +error: this assignment could be simplified + --> $DIR/match_single_binding.rs:134:5 + | +LL | / val = match val.split_at(idx) { +LL | | (pre, suf) => { +LL | | println!("{}", pre); +LL | | suf +LL | | }, +LL | | }; + | |_____^ + | +help: consider removing the `match` expression + | +LL ~ let (pre, suf) = val.split_at(idx); +LL + val = { +LL + println!("{}", pre); +LL + suf +LL ~ }; + | + +error: aborting due to 13 previous errors diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr index d34933194660..22bf7d8be4a2 100644 --- a/tests/ui/match_single_binding2.stderr +++ b/tests/ui/match_single_binding2.stderr @@ -8,7 +8,7 @@ LL | | }, | |_____________^ | = note: `-D clippy::match-single-binding` implied by `-D warnings` -help: consider using `let` statement +help: consider using a `let` statement | LL ~ Some((iter, _item)) => { LL + let (min, max) = iter.size_hint(); @@ -24,7 +24,7 @@ LL | | (a, b) => println!("a {:?} and b {:?}", a, b), LL | | } | |_____________^ | -help: consider using `let` statement +help: consider using a `let` statement | LL ~ let (a, b) = get_tup(); LL + println!("a {:?} and b {:?}", a, b); From cf8ea64d9dd9f8925ce328523e075be5f18efeaf Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 8 May 2022 01:17:58 -0400 Subject: [PATCH 479/536] Introduce EarlyBinder --- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_lints/src/future_not_send.rs | 4 ++-- clippy_lints/src/mut_reference.rs | 4 ++-- clippy_lints/src/transmute/transmute_undefined_repr.rs | 4 ++-- clippy_utils/src/consts.rs | 4 ++-- clippy_utils/src/ty.rs | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 1b19868e4c70..b7776b3f0c38 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; +use rustc_middle::ty::{self, ClosureKind, EarlyBinder, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(body.value.hir_id); - let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs); + let call_ty = EarlyBinder(cx.tcx.type_of(method_def_id)).subst(cx.tcx, substs); if check_sig(cx, closure_ty, call_ty); then { span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index 43911a313d5a..5c46d6c7df70 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -5,7 +5,7 @@ use rustc_hir::{Body, FnDecl, HirId}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{Opaque, PredicateKind::Trait}; +use rustc_middle::ty::{EarlyBinder, Opaque, PredicateKind::Trait}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt; @@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { let preds = cx.tcx.explicit_item_bounds(id); let mut is_future = false; for &(p, _span) in preds { - let p = p.subst(cx.tcx, subst); + let p = EarlyBinder(p).subst(cx.tcx, subst); if let Some(trait_pred) = p.to_opt_poly_trait_pred() { if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() { is_future = true; diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index 5c3e505c06c4..a323737ae404 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, Ty}; +use rustc_middle::ty::{self, EarlyBinder, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::iter; @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { ExprKind::MethodCall(path, arguments, _) => { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); - let method_type = cx.tcx.type_of(def_id).subst(cx.tcx, substs); + let method_type = EarlyBinder(cx.tcx.type_of(def_id)).subst(cx.tcx, substs); check_arguments(cx, arguments, method_type, path.ident.as_str(), "method"); }, _ => (), diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index f5e21267a897..0bb577b7620b 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; +use rustc_middle::ty::{self, EarlyBinder, IntTy, Ty, TypeAndMut, UintTy}; use rustc_span::Span; #[allow(clippy::too_many_lines)] @@ -307,7 +307,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> .non_enum_variant() .fields .iter() - .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs)); + .map(|f| EarlyBinder(cx.tcx.type_of(f.did)).subst(cx.tcx, substs)); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { return ReducedTy::TypeErasure; }; diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index fdb822c3e5b6..a80c7ee49295 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -9,7 +9,7 @@ use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, use rustc_lint::LateContext; use rustc_middle::mir::interpret::Scalar; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt}; +use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; @@ -420,7 +420,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { let substs = if self.substs.is_empty() { substs } else { - substs.subst(self.lcx.tcx, self.substs) + EarlyBinder(substs).subst(self.lcx.tcx, self.substs) }; let result = self diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index 901e3e5390c5..b46a7b86a759 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{ - self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, + self, AdtDef, Binder, EarlyBinder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -520,7 +520,7 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option Some(ExprFnSig::Closure(subs.as_closure().sig())), - ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.fn_sig(id).subst(cx.tcx, subs))), + ty::FnDef(id, subs) => Some(ExprFnSig::Sig(EarlyBinder(cx.tcx.fn_sig(id)).subst(cx.tcx, subs))), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)), ty::Dynamic(bounds, _) => { let lang_items = cx.tcx.lang_items(); From ae0216d55700274166a853cee1daba59196d4bff Mon Sep 17 00:00:00 2001 From: Koichi ITO Date: Wed, 11 May 2022 01:03:52 +0900 Subject: [PATCH 480/536] Use the traits added to the Rust 2021 Edition prelude Follow up https://github.com/rust-lang/rust/pull/96861. This PR uses the traits added to the Rust 2021 Edition prelude. > The `TryInto`, `TryFrom` and `FromIterator` traits are now part of the prelude. https://doc.rust-lang.org/edition-guide/rust-2021/prelude.html --- clippy_lints/src/checked_conversions.rs | 1 - clippy_lints/src/enum_clike.rs | 1 - clippy_lints/src/excessive_bools.rs | 2 - clippy_lints/src/fallible_impl_from.rs | 1 - clippy_lints/src/index_refutable_slice.rs | 1 - clippy_lints/src/methods/mod.rs | 2 - clippy_lints/src/redundant_clone.rs | 1 - clippy_lints/src/regex.rs | 1 - clippy_lints/src/tabs_in_doc_comments.rs | 1 - clippy_utils/src/consts.rs | 1 - clippy_utils/src/sugg.rs | 1 - tests/ui-toml/unwrap_used/unwrap_used.rs | 1 - tests/ui-toml/unwrap_used/unwrap_used.stderr | 54 +++++++++---------- .../proc_macro_suspicious_else_formatting.rs | 1 - tests/ui/checked_conversions.fixed | 2 - tests/ui/checked_conversions.rs | 2 - tests/ui/checked_conversions.stderr | 32 +++++------ .../ui/crashes/auxiliary/proc_macro_crash.rs | 1 - tests/ui/from_iter_instead_of_collect.fixed | 1 - tests/ui/from_iter_instead_of_collect.rs | 1 - tests/ui/from_iter_instead_of_collect.stderr | 30 +++++------ tests/ui/get_unwrap.fixed | 1 - tests/ui/get_unwrap.rs | 1 - tests/ui/get_unwrap.stderr | 52 +++++++++--------- tests/ui/infinite_iter.rs | 1 - tests/ui/infinite_iter.stderr | 2 +- tests/ui/manual_str_repeat.fixed | 2 +- tests/ui/manual_str_repeat.rs | 2 +- tests/ui/map_err.rs | 1 - tests/ui/map_err.stderr | 2 +- tests/ui/methods.rs | 1 - tests/ui/methods.stderr | 4 +- tests/ui/useless_conversion_try.rs | 2 - tests/ui/useless_conversion_try.stderr | 18 +++---- 34 files changed, 99 insertions(+), 128 deletions(-) diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 28c77ea40ef7..7eeaaa019214 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -30,7 +30,6 @@ declare_clippy_lint! { /// Could be written: /// /// ```rust - /// # use std::convert::TryFrom; /// # let foo = 1; /// # let _ = /// i32::try_from(foo).is_ok() diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs index 43b405c9a8eb..10be245b3629 100644 --- a/clippy_lints/src/enum_clike.rs +++ b/clippy_lints/src/enum_clike.rs @@ -8,7 +8,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, IntTy, UintTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/excessive_bools.rs b/clippy_lints/src/excessive_bools.rs index 245a4fc12fd4..7a81fb37e841 100644 --- a/clippy_lints/src/excessive_bools.rs +++ b/clippy_lints/src/excessive_bools.rs @@ -4,8 +4,6 @@ use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; -use std::convert::TryInto; - declare_clippy_lint! { /// ### What it does /// Checks for excessive diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 088d9996516e..9f868df3ad06 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -32,7 +32,6 @@ declare_clippy_lint! { /// // Good /// struct Foo(i32); /// - /// use std::convert::TryFrom; /// impl TryFrom for Foo { /// type Error = (); /// fn try_from(s: String) -> Result { diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 07b0604f78d7..9ce5b8e17a9a 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -14,7 +14,6 @@ use rustc_middle::ty; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::Ident, Span}; -use std::convert::TryInto; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index cfeee4d0c709..3e07961fcb33 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1776,8 +1776,6 @@ declare_clippy_lint! { /// /// ### Example /// ```rust - /// use std::iter::FromIterator; - /// /// let five_fives = std::iter::repeat(5).take(5); /// /// let v = Vec::from_iter(five_fives); diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 954e702a1f88..9904617353f8 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -19,7 +19,6 @@ use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, Ge use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; use rustc_span::sym; -use std::convert::TryFrom; use std::ops::ControlFlow; macro_rules! unwrap_or_continue { diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs index 78ca7622f4a1..67129299e2f9 100644 --- a/clippy_lints/src/regex.rs +++ b/clippy_lints/src/regex.rs @@ -7,7 +7,6 @@ use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does diff --git a/clippy_lints/src/tabs_in_doc_comments.rs b/clippy_lints/src/tabs_in_doc_comments.rs index 15543b6a2627..e223aea297fc 100644 --- a/clippy_lints/src/tabs_in_doc_comments.rs +++ b/clippy_lints/src/tabs_in_doc_comments.rs @@ -4,7 +4,6 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::{BytePos, Span}; -use std::convert::TryFrom; declare_clippy_lint! { /// ### What it does diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs index 720dfe72237c..75fab624fdae 100644 --- a/clippy_utils/src/consts.rs +++ b/clippy_utils/src/consts.rs @@ -13,7 +13,6 @@ use rustc_middle::ty::{self, FloatTy, ScalarInt, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_span::symbol::Symbol; use std::cmp::Ordering::{self, Equal}; -use std::convert::TryInto; use std::hash::{Hash, Hasher}; use std::iter; diff --git a/clippy_utils/src/sugg.rs b/clippy_utils/src/sugg.rs index 855d3657dd40..1de73a80ad20 100644 --- a/clippy_utils/src/sugg.rs +++ b/clippy_utils/src/sugg.rs @@ -17,7 +17,6 @@ use rustc_middle::ty; use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext}; use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use std::borrow::Cow; -use std::convert::TryInto; use std::fmt::{Display, Write as _}; use std::iter; use std::ops::{Add, Neg, Not, Sub}; diff --git a/tests/ui-toml/unwrap_used/unwrap_used.rs b/tests/ui-toml/unwrap_used/unwrap_used.rs index a639de079715..74d0d7c2650d 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.rs +++ b/tests/ui-toml/unwrap_used/unwrap_used.rs @@ -7,7 +7,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::VecDeque; -use std::iter::FromIterator; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr index 6c3adc5395f7..6bcfa0a8b564 100644 --- a/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -1,5 +1,5 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:36:17 + --> $DIR/unwrap_used.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` @@ -11,7 +11,7 @@ LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:36:17 + --> $DIR/unwrap_used.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,13 +20,13 @@ LL | let _ = boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:37:17 + --> $DIR/unwrap_used.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:37:17 + --> $DIR/unwrap_used.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,13 +34,13 @@ LL | let _ = some_slice.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:38:17 + --> $DIR/unwrap_used.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:38:17 + --> $DIR/unwrap_used.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,13 +48,13 @@ LL | let _ = some_vec.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:39:17 + --> $DIR/unwrap_used.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:39:17 + --> $DIR/unwrap_used.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,13 +62,13 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:40:17 + --> $DIR/unwrap_used.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:40:17 + --> $DIR/unwrap_used.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,13 +76,13 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:41:17 + --> $DIR/unwrap_used.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:41:17 + --> $DIR/unwrap_used.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,13 +90,13 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:45:21 + --> $DIR/unwrap_used.rs:44:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:45:22 + --> $DIR/unwrap_used.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,13 +104,13 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:50:9 + --> $DIR/unwrap_used.rs:49:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:50:10 + --> $DIR/unwrap_used.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,13 +118,13 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:51:9 + --> $DIR/unwrap_used.rs:50:9 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:51:10 + --> $DIR/unwrap_used.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,13 +132,13 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:52:9 + --> $DIR/unwrap_used.rs:51:9 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:52:10 + --> $DIR/unwrap_used.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,13 +146,13 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:53:9 + --> $DIR/unwrap_used.rs:52:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:53:10 + --> $DIR/unwrap_used.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -160,13 +160,13 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:65:17 + --> $DIR/unwrap_used.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:65:17 + --> $DIR/unwrap_used.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -174,13 +174,13 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:66:17 + --> $DIR/unwrap_used.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` error: used `unwrap()` on `an Option` value - --> $DIR/unwrap_used.rs:66:17 + --> $DIR/unwrap_used.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -188,7 +188,7 @@ LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/unwrap_used.rs:73:13 + --> $DIR/unwrap_used.rs:72:13 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` diff --git a/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs index 26c88489b03c..a2ef0fe827c0 100644 --- a/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs +++ b/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs @@ -5,7 +5,6 @@ extern crate proc_macro; use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree}; -use std::iter::FromIterator; fn read_ident(iter: &mut token_stream::IntoIter) -> Ident { match iter.next() { diff --git a/tests/ui/checked_conversions.fixed b/tests/ui/checked_conversions.fixed index 12290db3dcf5..0983d393b560 100644 --- a/tests/ui/checked_conversions.fixed +++ b/tests/ui/checked_conversions.fixed @@ -7,8 +7,6 @@ )] #![warn(clippy::checked_conversions)] -use std::convert::TryFrom; - // Positive tests // Signed to unsigned diff --git a/tests/ui/checked_conversions.rs b/tests/ui/checked_conversions.rs index 895a301e8212..7d26ace47fdf 100644 --- a/tests/ui/checked_conversions.rs +++ b/tests/ui/checked_conversions.rs @@ -7,8 +7,6 @@ )] #![warn(clippy::checked_conversions)] -use std::convert::TryFrom; - // Positive tests // Signed to unsigned diff --git a/tests/ui/checked_conversions.stderr b/tests/ui/checked_conversions.stderr index 18518def0acb..2e518040561c 100644 --- a/tests/ui/checked_conversions.stderr +++ b/tests/ui/checked_conversions.stderr @@ -1,5 +1,5 @@ error: checked cast can be simplified - --> $DIR/checked_conversions.rs:17:13 + --> $DIR/checked_conversions.rs:15:13 | LL | let _ = value <= (u32::max_value() as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` @@ -7,91 +7,91 @@ LL | let _ = value <= (u32::max_value() as i64) && value >= 0; = note: `-D clippy::checked-conversions` implied by `-D warnings` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:18:13 + --> $DIR/checked_conversions.rs:16:13 | LL | let _ = value <= (u32::MAX as i64) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:22:13 + --> $DIR/checked_conversions.rs:20:13 | LL | let _ = value <= i64::from(u16::max_value()) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:23:13 + --> $DIR/checked_conversions.rs:21:13 | LL | let _ = value <= i64::from(u16::MAX) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:27:13 + --> $DIR/checked_conversions.rs:25:13 | LL | let _ = value <= (u8::max_value() as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:28:13 + --> $DIR/checked_conversions.rs:26:13 | LL | let _ = value <= (u8::MAX as isize) && value >= 0; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u8::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:34:13 + --> $DIR/checked_conversions.rs:32:13 | LL | let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:35:13 + --> $DIR/checked_conversions.rs:33:13 | LL | let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:39:13 + --> $DIR/checked_conversions.rs:37:13 | LL | let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:40:13 + --> $DIR/checked_conversions.rs:38:13 | LL | let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:46:13 + --> $DIR/checked_conversions.rs:44:13 | LL | let _ = value <= i32::max_value() as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:47:13 + --> $DIR/checked_conversions.rs:45:13 | LL | let _ = value <= i32::MAX as u32; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `i32::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:51:13 + --> $DIR/checked_conversions.rs:49:13 | LL | let _ = value <= isize::max_value() as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:52:13 + --> $DIR/checked_conversions.rs:50:13 | LL | let _ = value <= isize::MAX as usize && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `isize::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:56:13 + --> $DIR/checked_conversions.rs:54:13 | LL | let _ = value <= u16::max_value() as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` error: checked cast can be simplified - --> $DIR/checked_conversions.rs:57:13 + --> $DIR/checked_conversions.rs:55:13 | LL | let _ = value <= u16::MAX as u32 && value as i32 == 5; | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u16::try_from(value).is_ok()` diff --git a/tests/ui/crashes/auxiliary/proc_macro_crash.rs b/tests/ui/crashes/auxiliary/proc_macro_crash.rs index ed8e7a708a5e..5ff2af7cd825 100644 --- a/tests/ui/crashes/auxiliary/proc_macro_crash.rs +++ b/tests/ui/crashes/auxiliary/proc_macro_crash.rs @@ -12,7 +12,6 @@ extern crate proc_macro; use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree}; -use std::iter::FromIterator; #[proc_macro] pub fn macro_test(input_stream: TokenStream) -> TokenStream { diff --git a/tests/ui/from_iter_instead_of_collect.fixed b/tests/ui/from_iter_instead_of_collect.fixed index 12db43b53436..403c3b3e4438 100644 --- a/tests/ui/from_iter_instead_of_collect.fixed +++ b/tests/ui/from_iter_instead_of_collect.fixed @@ -4,7 +4,6 @@ #![allow(unused_imports)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; -use std::iter::FromIterator; struct Foo(Vec); diff --git a/tests/ui/from_iter_instead_of_collect.rs b/tests/ui/from_iter_instead_of_collect.rs index f5ec190e0cdc..fefc7b01a65b 100644 --- a/tests/ui/from_iter_instead_of_collect.rs +++ b/tests/ui/from_iter_instead_of_collect.rs @@ -4,7 +4,6 @@ #![allow(unused_imports)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; -use std::iter::FromIterator; struct Foo(Vec); diff --git a/tests/ui/from_iter_instead_of_collect.stderr b/tests/ui/from_iter_instead_of_collect.stderr index 8f08ac8c3ff4..8aa3c3c01f81 100644 --- a/tests/ui/from_iter_instead_of_collect.stderr +++ b/tests/ui/from_iter_instead_of_collect.stderr @@ -1,5 +1,5 @@ error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:19:9 + --> $DIR/from_iter_instead_of_collect.rs:18:9 | LL | >::from_iter(iter.into_iter().copied()) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter.into_iter().copied().collect::()` @@ -7,85 +7,85 @@ LL | >::from_iter(iter.into_iter().copied()) = note: `-D clippy::from-iter-instead-of-collect` implied by `-D warnings` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:25:13 + --> $DIR/from_iter_instead_of_collect.rs:24:13 | LL | let _ = Vec::from_iter(iter_expr); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `iter_expr.collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:27:13 + --> $DIR/from_iter_instead_of_collect.rs:26:13 | LL | let _ = HashMap::::from_iter(vec![5, 5, 5, 5].iter().enumerate()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `vec![5, 5, 5, 5].iter().enumerate().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:32:19 + --> $DIR/from_iter_instead_of_collect.rs:31:19 | LL | assert_eq!(a, Vec::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:33:19 + --> $DIR/from_iter_instead_of_collect.rs:32:19 | LL | assert_eq!(a, Vec::::from_iter(0..3)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:35:17 + --> $DIR/from_iter_instead_of_collect.rs:34:17 | LL | let mut b = VecDeque::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:38:17 + --> $DIR/from_iter_instead_of_collect.rs:37:17 | LL | let mut b = VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:43:21 + --> $DIR/from_iter_instead_of_collect.rs:42:21 | LL | let mut b = collections::VecDeque::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:48:14 + --> $DIR/from_iter_instead_of_collect.rs:47:14 | LL | let bm = BTreeMap::from_iter(values.iter().cloned()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `values.iter().cloned().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:49:19 + --> $DIR/from_iter_instead_of_collect.rs:48:19 | LL | let mut bar = BTreeMap::from_iter(bm.range(0..2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `bm.range(0..2).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:52:19 + --> $DIR/from_iter_instead_of_collect.rs:51:19 | LL | let mut bts = BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:56:17 + --> $DIR/from_iter_instead_of_collect.rs:55:17 | LL | let _ = collections::BTreeSet::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:57:17 + --> $DIR/from_iter_instead_of_collect.rs:56:17 | LL | let _ = collections::BTreeSet::::from_iter(0..3); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `(0..3).collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:60:15 + --> $DIR/from_iter_instead_of_collect.rs:59:15 | LL | for _i in Vec::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` error: usage of `FromIterator::from_iter` - --> $DIR/from_iter_instead_of_collect.rs:61:15 + --> $DIR/from_iter_instead_of_collect.rs:60:15 | LL | for _i in Vec::<&i32>::from_iter([1, 2, 3].iter()) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `.collect()` instead of `::from_iter()`: `[1, 2, 3].iter().collect::>()` diff --git a/tests/ui/get_unwrap.fixed b/tests/ui/get_unwrap.fixed index c3a36dcabd1a..8f165d675890 100644 --- a/tests/ui/get_unwrap.fixed +++ b/tests/ui/get_unwrap.fixed @@ -7,7 +7,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::VecDeque; -use std::iter::FromIterator; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui/get_unwrap.rs b/tests/ui/get_unwrap.rs index d77a202aa39c..786749daa746 100644 --- a/tests/ui/get_unwrap.rs +++ b/tests/ui/get_unwrap.rs @@ -7,7 +7,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::VecDeque; -use std::iter::FromIterator; struct GetFalsePositive { arr: [u32; 3], diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr index cb5f44fbd59e..ea8fec527351 100644 --- a/tests/ui/get_unwrap.stderr +++ b/tests/ui/get_unwrap.stderr @@ -1,5 +1,5 @@ error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:36:17 + --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]` @@ -11,7 +11,7 @@ LL | #![deny(clippy::get_unwrap)] | ^^^^^^^^^^^^^^^^^^ error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:36:17 + --> $DIR/get_unwrap.rs:35:17 | LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,13 +20,13 @@ LL | let _ = boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:37:17 + --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:37:17 + --> $DIR/get_unwrap.rs:36:17 | LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -34,13 +34,13 @@ LL | let _ = some_slice.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:38:17 + --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:38:17 + --> $DIR/get_unwrap.rs:37:17 | LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -48,13 +48,13 @@ LL | let _ = some_vec.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:39:17 + --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:39:17 + --> $DIR/get_unwrap.rs:38:17 | LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -62,13 +62,13 @@ LL | let _ = some_vecdeque.get(0).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:40:17 + --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:40:17 + --> $DIR/get_unwrap.rs:39:17 | LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -76,13 +76,13 @@ LL | let _ = some_hashmap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:41:17 + --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:41:17 + --> $DIR/get_unwrap.rs:40:17 | LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -90,13 +90,13 @@ LL | let _ = some_btreemap.get(&1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:45:21 + --> $DIR/get_unwrap.rs:44:21 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:45:22 + --> $DIR/get_unwrap.rs:44:22 | LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -104,13 +104,13 @@ LL | let _: u8 = *boxed_slice.get(1).unwrap(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:50:9 + --> $DIR/get_unwrap.rs:49:9 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:50:10 + --> $DIR/get_unwrap.rs:49:10 | LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -118,13 +118,13 @@ LL | *boxed_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:51:9 + --> $DIR/get_unwrap.rs:50:9 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:51:10 + --> $DIR/get_unwrap.rs:50:10 | LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -132,13 +132,13 @@ LL | *some_slice.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:52:9 + --> $DIR/get_unwrap.rs:51:9 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:52:10 + --> $DIR/get_unwrap.rs:51:10 | LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -146,13 +146,13 @@ LL | *some_vec.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:53:9 + --> $DIR/get_unwrap.rs:52:9 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:53:10 + --> $DIR/get_unwrap.rs:52:10 | LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -160,13 +160,13 @@ LL | *some_vecdeque.get_mut(0).unwrap() = 1; = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:65:17 + --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:65:17 + --> $DIR/get_unwrap.rs:64:17 | LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -174,13 +174,13 @@ LL | let _ = some_vec.get(0..1).unwrap().to_vec(); = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise - --> $DIR/get_unwrap.rs:66:17 + --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]` error: used `unwrap()` on `an Option` value - --> $DIR/get_unwrap.rs:66:17 + --> $DIR/get_unwrap.rs:65:17 | LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/infinite_iter.rs b/tests/ui/infinite_iter.rs index 1fe688977659..a1e5fad0c621 100644 --- a/tests/ui/infinite_iter.rs +++ b/tests/ui/infinite_iter.rs @@ -51,7 +51,6 @@ fn main() { mod finite_collect { use std::collections::HashSet; - use std::iter::FromIterator; struct C; impl FromIterator for C { diff --git a/tests/ui/infinite_iter.stderr b/tests/ui/infinite_iter.stderr index 5f5e7ac9f253..ba277e36339a 100644 --- a/tests/ui/infinite_iter.stderr +++ b/tests/ui/infinite_iter.stderr @@ -98,7 +98,7 @@ LL | (0..).all(|x| x == 24); // maybe infinite iter | ^^^^^^^^^^^^^^^^^^^^^^ error: infinite iteration detected - --> $DIR/infinite_iter.rs:64:31 + --> $DIR/infinite_iter.rs:63:31 | LL | let _: HashSet = (0..).collect(); // Infinite iter | ^^^^^^^^^^^^^^^ diff --git a/tests/ui/manual_str_repeat.fixed b/tests/ui/manual_str_repeat.fixed index dc140257f321..0704ba2f933e 100644 --- a/tests/ui/manual_str_repeat.fixed +++ b/tests/ui/manual_str_repeat.fixed @@ -4,7 +4,7 @@ #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; -use std::iter::{repeat, FromIterator}; +use std::iter::repeat; fn main() { let _: String = "test".repeat(10); diff --git a/tests/ui/manual_str_repeat.rs b/tests/ui/manual_str_repeat.rs index 0d69c989b2ed..f522be439aa0 100644 --- a/tests/ui/manual_str_repeat.rs +++ b/tests/ui/manual_str_repeat.rs @@ -4,7 +4,7 @@ #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; -use std::iter::{repeat, FromIterator}; +use std::iter::repeat; fn main() { let _: String = std::iter::repeat("test").take(10).collect(); diff --git a/tests/ui/map_err.rs b/tests/ui/map_err.rs index 00e037843f8c..bb35ab1a14ef 100644 --- a/tests/ui/map_err.rs +++ b/tests/ui/map_err.rs @@ -1,6 +1,5 @@ #![warn(clippy::map_err_ignore)] #![allow(clippy::unnecessary_wraps)] -use std::convert::TryFrom; use std::error::Error; use std::fmt; diff --git a/tests/ui/map_err.stderr b/tests/ui/map_err.stderr index 37e87e64de28..c035840521e4 100644 --- a/tests/ui/map_err.stderr +++ b/tests/ui/map_err.stderr @@ -1,5 +1,5 @@ error: `map_err(|_|...` wildcard pattern discards the original error - --> $DIR/map_err.rs:23:32 + --> $DIR/map_err.rs:22:32 | LL | println!("{:?}", x.map_err(|_| Errors::Ignored)); | ^^^ diff --git a/tests/ui/methods.rs b/tests/ui/methods.rs index 977ce54327b3..9805097084d3 100644 --- a/tests/ui/methods.rs +++ b/tests/ui/methods.rs @@ -26,7 +26,6 @@ use std::collections::BTreeMap; use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; -use std::iter::FromIterator; use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; diff --git a/tests/ui/methods.stderr b/tests/ui/methods.stderr index b63672dd6fdb..6be38b24fbda 100644 --- a/tests/ui/methods.stderr +++ b/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> $DIR/methods.rs:104:5 + --> $DIR/methods.rs:103:5 | LL | / fn new() -> i32 { LL | | 0 @@ -9,7 +9,7 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> $DIR/methods.rs:125:13 + --> $DIR/methods.rs:124:13 | LL | let _ = v.iter().filter(|&x| { | _____________^ diff --git a/tests/ui/useless_conversion_try.rs b/tests/ui/useless_conversion_try.rs index 3787ea991445..39f54c27bee1 100644 --- a/tests/ui/useless_conversion_try.rs +++ b/tests/ui/useless_conversion_try.rs @@ -1,7 +1,5 @@ #![deny(clippy::useless_conversion)] -use std::convert::{TryFrom, TryInto}; - fn test_generic(val: T) -> T { let _ = T::try_from(val).unwrap(); val.try_into().unwrap() diff --git a/tests/ui/useless_conversion_try.stderr b/tests/ui/useless_conversion_try.stderr index 2e0d9129bfb3..b691c13f7dbb 100644 --- a/tests/ui/useless_conversion_try.stderr +++ b/tests/ui/useless_conversion_try.stderr @@ -1,5 +1,5 @@ error: useless conversion to the same type: `T` - --> $DIR/useless_conversion_try.rs:6:13 + --> $DIR/useless_conversion_try.rs:4:13 | LL | let _ = T::try_from(val).unwrap(); | ^^^^^^^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | #![deny(clippy::useless_conversion)] = help: consider removing `T::try_from()` error: useless conversion to the same type: `T` - --> $DIR/useless_conversion_try.rs:7:5 + --> $DIR/useless_conversion_try.rs:5:5 | LL | val.try_into().unwrap() | ^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | val.try_into().unwrap() = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:29:21 + --> $DIR/useless_conversion_try.rs:27:21 | LL | let _: String = "foo".to_string().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | let _: String = "foo".to_string().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:30:21 + --> $DIR/useless_conversion_try.rs:28:21 | LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | let _: String = TryFrom::try_from("foo".to_string()).unwrap(); = help: consider removing `TryFrom::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:31:13 + --> $DIR/useless_conversion_try.rs:29:13 | LL | let _ = String::try_from("foo".to_string()).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | let _ = String::try_from("foo".to_string()).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:32:13 + --> $DIR/useless_conversion_try.rs:30:13 | LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | let _ = String::try_from(format!("A: {:04}", 123)).unwrap(); = help: consider removing `String::try_from()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:33:21 + --> $DIR/useless_conversion_try.rs:31:21 | LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -60,7 +60,7 @@ LL | let _: String = format!("Hello {}", "world").try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:34:21 + --> $DIR/useless_conversion_try.rs:32:21 | LL | let _: String = "".to_owned().try_into().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL | let _: String = "".to_owned().try_into().unwrap(); = help: consider removing `.try_into()` error: useless conversion to the same type: `std::string::String` - --> $DIR/useless_conversion_try.rs:35:27 + --> $DIR/useless_conversion_try.rs:33:27 | LL | let _: String = match String::from("_").try_into() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 31825326e5bb0a19bfc2f1c2b862787c43c0ee5f Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Wed, 11 May 2022 18:51:14 +0200 Subject: [PATCH 481/536] Bless clippy. --- tests/ui/crashes/ice-6252.stderr | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/ui/crashes/ice-6252.stderr b/tests/ui/crashes/ice-6252.stderr index c8239897f3ab..d930d486fde6 100644 --- a/tests/ui/crashes/ice-6252.stderr +++ b/tests/ui/crashes/ice-6252.stderr @@ -30,7 +30,15 @@ LL | const VAL: T; LL | impl TypeVal for Multiply where N: TypeVal {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `VAL` in implementation -error: aborting due to 3 previous errors +error: constant expression depends on a generic parameter + --> $DIR/ice-6252.rs:13:9 + | +LL | [1; >::VAL]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this may fail depending on what value the parameter takes + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0046, E0412. For more information about an error, try `rustc --explain E0046`. From 344888a5820c2ae2d7c88ecf14bcaffb60ca2bc5 Mon Sep 17 00:00:00 2001 From: yonip23 Date: Wed, 11 May 2022 23:11:52 +0300 Subject: [PATCH 482/536] fix review comments --- clippy_lints/src/rc_clone_in_vec_init.rs | 70 ++++++++++-------------- tests/ui/rc_clone_in_vec_init/arc.rs | 9 +++ tests/ui/rc_clone_in_vec_init/arc.stderr | 36 +++++++++++- tests/ui/rc_clone_in_vec_init/rc.rs | 9 +++ tests/ui/rc_clone_in_vec_init/rc.stderr | 36 +++++++++++- 5 files changed, 114 insertions(+), 46 deletions(-) diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index aa575d7e68bf..7a7a3f558ca0 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs; use clippy_utils::last_path_segment; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::{indent_of, snippet}; -use rustc_errors::{Applicability, Diagnostic}; +use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -51,70 +51,53 @@ impl LateLintPass<'_> for RcCloneInVecInit { let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; }; let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; }; let Some(symbol) = new_reference_call(cx, elem) else { return; }; - let lint_span = macro_call.span; - let symbol_name = symbol.as_str(); - let len_snippet = snippet(cx, len.span, ".."); - let elem_snippet = elem_snippet(cx, elem, symbol_name); - let indentation = indent_of(cx, lint_span).unwrap_or(0); - let lint_suggestions = - construct_lint_suggestions(lint_span, symbol_name, &elem_snippet, len_snippet.as_ref(), indentation); - emit_lint(cx, symbol, lint_span, &lint_suggestions); + emit_lint(cx, symbol, macro_call.span, elem, len); } } struct LintSuggestion { - span: Span, message: String, - suggestion: String, - applicability: Applicability, -} - -impl LintSuggestion { - fn span_suggestion(&self, diag: &mut Diagnostic) { - diag.span_suggestion(self.span, &self.message, &self.suggestion, self.applicability); - } + snippet: String, } fn construct_lint_suggestions( + cx: &LateContext<'_>, span: Span, symbol_name: &str, - elem_snippet: &str, - len_snippet: &str, - indentation: usize, + elem: &Expr<'_>, + len: &Expr<'_>, ) -> Vec { + let len_snippet = snippet(cx, len.span, ".."); + let elem_snippet = elem_snippet(cx, elem, symbol_name); + let indentation = indent_of(cx, span).unwrap_or(0); let indentation = " ".repeat(indentation); - let loop_init_suggestion = loop_init_suggestion(elem_snippet, len_snippet, &indentation); - let extract_suggestion = extract_suggestion(elem_snippet, len_snippet, &indentation); + let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); + let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); vec![ LintSuggestion { - span, message: format!("consider initializing each `{symbol_name}` element individually"), - suggestion: loop_init_suggestion, - applicability: Applicability::Unspecified, + snippet: loop_init_suggestion, }, LintSuggestion { - span, message: format!( "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" ), - suggestion: extract_suggestion, - applicability: Applicability::Unspecified, + snippet: extract_suggestion, }, ] } fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { - let mut elem_snippet = snippet(cx, elem.span, "..").to_string(); + let elem_snippet = snippet(cx, elem.span, "..").to_string(); if elem_snippet.contains('\n') { - let reference_initialization = format!("{symbol_name}::new"); - // This string must be found in `elem_snippet`, otherwise we won't be constructing the snippet in - // the first place. - let reference_initialization_end = - elem_snippet.find(&reference_initialization).unwrap() + reference_initialization.len(); - elem_snippet.replace_range(reference_initialization_end.., ".."); + let reference_creation = format!("{symbol_name}::new"); + let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap(); + + return format!("{code_until_reference_creation}{reference_creation}(..)"); } + elem_snippet } @@ -137,7 +120,7 @@ fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { ) } -fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, lint_suggestions: &[LintSuggestion]) { +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) { let symbol_name = symbol.as_str(); span_lint_and_then( @@ -146,10 +129,17 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, lint_suggest lint_span, &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), |diag| { + let suggestions = construct_lint_suggestions(cx, lint_span, symbol_name, elem, len); + diag.note(format!("each element will point to the same `{symbol_name}` instance")); - lint_suggestions - .iter() - .for_each(|suggestion| suggestion.span_suggestion(diag)); + suggestions.iter().for_each(|suggestion| { + diag.span_suggestion( + lint_span, + &suggestion.message, + &suggestion.snippet, + Applicability::Unspecified, + ); + }); }, ); } diff --git a/tests/ui/rc_clone_in_vec_init/arc.rs b/tests/ui/rc_clone_in_vec_init/arc.rs index 9f4e27dfa624..bef2c67a1a54 100644 --- a/tests/ui/rc_clone_in_vec_init/arc.rs +++ b/tests/ui/rc_clone_in_vec_init/arc.rs @@ -16,6 +16,15 @@ fn should_warn_complex_case() { })); 2 ]; + + let v1 = vec![ + Arc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; } fn should_not_warn_custom_arc() { diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr index 3de96c6f1758..387580c24310 100644 --- a/tests/ui/rc_clone_in_vec_init/arc.stderr +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -40,17 +40,47 @@ help: consider initializing each `Arc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(std::sync::Arc::new..)); +LL + (0..2).for_each(|_| v.push(std::sync::Arc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Arc` initialization to a variable | LL ~ let v = { -LL + let data = std::sync::Arc::new..; +LL + let data = std::sync::Arc::new(..); LL + vec![data; 2] LL ~ }; | -error: aborting due to 2 previous errors +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:20:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Arc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Arc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 3 previous errors diff --git a/tests/ui/rc_clone_in_vec_init/rc.rs b/tests/ui/rc_clone_in_vec_init/rc.rs index 5e2834aa9023..79c23cafa2c1 100644 --- a/tests/ui/rc_clone_in_vec_init/rc.rs +++ b/tests/ui/rc_clone_in_vec_init/rc.rs @@ -17,6 +17,15 @@ fn should_warn_complex_case() { })); 2 ]; + + let v1 = vec![ + Rc::new(Mutex::new({ + let x = 1; + dbg!(x); + x + })); + 2 + ]; } fn should_not_warn_custom_arc() { diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr index e05f024cf9de..4ce53eecbbd8 100644 --- a/tests/ui/rc_clone_in_vec_init/rc.stderr +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -40,17 +40,47 @@ help: consider initializing each `Rc` element individually | LL ~ let v = { LL + let mut v = Vec::with_capacity(2); -LL + (0..2).for_each(|_| v.push(std::rc::Rc::new..)); +LL + (0..2).for_each(|_| v.push(std::rc::Rc::new(..))); LL + v LL ~ }; | help: or if this is intentional, consider extracting the `Rc` initialization to a variable | LL ~ let v = { -LL + let data = std::rc::Rc::new..; +LL + let data = std::rc::Rc::new(..); LL + vec![data; 2] LL ~ }; | -error: aborting due to 2 previous errors +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:21:14 + | +LL | let v1 = vec![ + | ______________^ +LL | | Rc::new(Mutex::new({ +LL | | let x = 1; +LL | | dbg!(x); +... | +LL | | 2 +LL | | ]; + | |_____^ + | + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v1 = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new(..))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v1 = { +LL + let data = Rc::new(..); +LL + vec![data; 2] +LL ~ }; + | + +error: aborting due to 3 previous errors From a791205e76fe9619f90d4869ab54e4213847b8fe Mon Sep 17 00:00:00 2001 From: yonip23 Date: Wed, 11 May 2022 23:16:49 +0300 Subject: [PATCH 483/536] fix clippy warning --- clippy_lints/src/rc_clone_in_vec_init.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index 7a7a3f558ca0..ee119e8c4a4f 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -92,6 +92,8 @@ fn construct_lint_suggestions( fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { let elem_snippet = snippet(cx, elem.span, "..").to_string(); if elem_snippet.contains('\n') { + // This string must be found in `elem_snippet`, otherwise we won't be constructing + // the snippet in the first place. let reference_creation = format!("{symbol_name}::new"); let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap(); @@ -132,14 +134,14 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< let suggestions = construct_lint_suggestions(cx, lint_span, symbol_name, elem, len); diag.note(format!("each element will point to the same `{symbol_name}` instance")); - suggestions.iter().for_each(|suggestion| { + for suggestion in suggestions { diag.span_suggestion( lint_span, &suggestion.message, &suggestion.snippet, Applicability::Unspecified, ); - }); + } }, ); } From 9e66f2d05844fb6e6972f347629bd77e08fee8d4 Mon Sep 17 00:00:00 2001 From: tamaron Date: Thu, 12 May 2022 12:33:05 +0900 Subject: [PATCH 484/536] fix --- clippy_lints/src/undocumented_unsafe_blocks.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index bde7613b48ee..5a8677f90be4 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -142,20 +142,19 @@ fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> boo /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { - if span_from_macro_expansion_has_safety_comment(cx, item.span) || span_in_body_has_safety_comment(cx, item.span) { + if span_from_macro_expansion_has_safety_comment(cx, item.span) { return true; } if item.span.ctxt() == SyntaxContext::root() { if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { - let comment_start; - match parent_node { + let comment_start = match parent_node { Node::Crate(parent_mod) => { - comment_start = comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item); + comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) }, Node::Item(parent_item) => { if let ItemKind::Mod(parent_mod) = &parent_item.kind { - comment_start = comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item); + comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item) } else { // Doesn't support impls in this position. Pretend a comment was found. return true; @@ -164,16 +163,14 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { Node::Stmt(stmt) => { if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) { match stmt_parent { - Node::Block(block) => { - comment_start = walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo); - }, + Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), _ => { // Doesn't support impls in this position. Pretend a comment was found. return true; }, } } else { - // Doesn't support impls in this position. Pretend a comment was found. + // Problem getting the parent node. Pretend a comment was found. return true; } }, @@ -181,7 +178,7 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { // Doesn't support impls in this position. Pretend a comment was found. return true; }, - } + }; let source_map = cx.sess().source_map(); if let Some(comment_start) = comment_start From dc23b5d661f828ddace3ccc65c5a53b0f43c1dd4 Mon Sep 17 00:00:00 2001 From: yonip23 Date: Thu, 12 May 2022 10:06:15 +0300 Subject: [PATCH 485/536] fix indentation + test --- clippy_lints/src/rc_clone_in_vec_init.rs | 10 ++++---- tests/ui/rc_clone_in_vec_init/arc.rs | 10 ++++++++ tests/ui/rc_clone_in_vec_init/arc.stderr | 29 +++++++++++++++++++++--- tests/ui/rc_clone_in_vec_init/rc.rs | 10 ++++++++ tests/ui/rc_clone_in_vec_init/rc.stderr | 29 +++++++++++++++++++++--- 5 files changed, 77 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index ee119e8c4a4f..ca12509bdc7f 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -106,9 +106,9 @@ fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> Str fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { format!( r#"{{ -{indent}{indent}let mut v = Vec::with_capacity({len}); -{indent}{indent}(0..{len}).for_each(|_| v.push({elem})); -{indent}{indent}v +{indent} let mut v = Vec::with_capacity({len}); +{indent} (0..{len}).for_each(|_| v.push({elem})); +{indent} v {indent}}}"# ) } @@ -116,8 +116,8 @@ fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String { fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String { format!( "{{ -{indent}{indent}let data = {elem}; -{indent}{indent}vec![data; {len}] +{indent} let data = {elem}; +{indent} vec![data; {len}] {indent}}}" ) } diff --git a/tests/ui/rc_clone_in_vec_init/arc.rs b/tests/ui/rc_clone_in_vec_init/arc.rs index bef2c67a1a54..384060e6eae5 100644 --- a/tests/ui/rc_clone_in_vec_init/arc.rs +++ b/tests/ui/rc_clone_in_vec_init/arc.rs @@ -7,6 +7,16 @@ fn should_warn_simple_case() { let v = vec![Arc::new("x".to_string()); 2]; } +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Arc::new("x".to_string()); 2]; + } + } +} + fn should_warn_complex_case() { let v = vec![ std::sync::Arc::new(Mutex::new({ diff --git a/tests/ui/rc_clone_in_vec_init/arc.stderr b/tests/ui/rc_clone_in_vec_init/arc.stderr index 387580c24310..ce84186c8e30 100644 --- a/tests/ui/rc_clone_in_vec_init/arc.stderr +++ b/tests/ui/rc_clone_in_vec_init/arc.stderr @@ -23,7 +23,30 @@ LL ~ }; | error: calling `Arc::new` in `vec![elem; len]` - --> $DIR/arc.rs:11:13 + --> $DIR/arc.rs:15:21 + | +LL | let v = vec![Arc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Arc` instance +help: consider initializing each `Arc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Arc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Arc` initialization to a variable + | +LL ~ let v = { +LL + let data = Arc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Arc::new` in `vec![elem; len]` + --> $DIR/arc.rs:21:13 | LL | let v = vec![ | _____________^ @@ -53,7 +76,7 @@ LL ~ }; | error: calling `Arc::new` in `vec![elem; len]` - --> $DIR/arc.rs:20:14 + --> $DIR/arc.rs:30:14 | LL | let v1 = vec![ | ______________^ @@ -82,5 +105,5 @@ LL + vec![data; 2] LL ~ }; | -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/rc_clone_in_vec_init/rc.rs b/tests/ui/rc_clone_in_vec_init/rc.rs index 79c23cafa2c1..0394457fe170 100644 --- a/tests/ui/rc_clone_in_vec_init/rc.rs +++ b/tests/ui/rc_clone_in_vec_init/rc.rs @@ -8,6 +8,16 @@ fn should_warn_simple_case() { let v = vec![Rc::new("x".to_string()); 2]; } +fn should_warn_simple_case_with_big_indentation() { + if true { + let k = 1; + dbg!(k); + if true { + let v = vec![Rc::new("x".to_string()); 2]; + } + } +} + fn should_warn_complex_case() { let v = vec![ std::rc::Rc::new(Mutex::new({ diff --git a/tests/ui/rc_clone_in_vec_init/rc.stderr b/tests/ui/rc_clone_in_vec_init/rc.stderr index 4ce53eecbbd8..0f5cc0cf98fe 100644 --- a/tests/ui/rc_clone_in_vec_init/rc.stderr +++ b/tests/ui/rc_clone_in_vec_init/rc.stderr @@ -23,7 +23,30 @@ LL ~ }; | error: calling `Rc::new` in `vec![elem; len]` - --> $DIR/rc.rs:12:13 + --> $DIR/rc.rs:16:21 + | +LL | let v = vec![Rc::new("x".to_string()); 2]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: each element will point to the same `Rc` instance +help: consider initializing each `Rc` element individually + | +LL ~ let v = { +LL + let mut v = Vec::with_capacity(2); +LL + (0..2).for_each(|_| v.push(Rc::new("x".to_string()))); +LL + v +LL ~ }; + | +help: or if this is intentional, consider extracting the `Rc` initialization to a variable + | +LL ~ let v = { +LL + let data = Rc::new("x".to_string()); +LL + vec![data; 2] +LL ~ }; + | + +error: calling `Rc::new` in `vec![elem; len]` + --> $DIR/rc.rs:22:13 | LL | let v = vec![ | _____________^ @@ -53,7 +76,7 @@ LL ~ }; | error: calling `Rc::new` in `vec![elem; len]` - --> $DIR/rc.rs:21:14 + --> $DIR/rc.rs:31:14 | LL | let v1 = vec![ | ______________^ @@ -82,5 +105,5 @@ LL + vec![data; 2] LL ~ }; | -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From 8708a261a1e297323060a8c71789e482b2d3725f Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 7 May 2022 22:10:56 +0100 Subject: [PATCH 486/536] Some lintcheck cleanup --- lintcheck/Cargo.toml | 5 +- lintcheck/lintcheck_crates.toml | 2 +- lintcheck/src/config.rs | 140 ++++++++++ lintcheck/src/main.rs | 458 ++++++++------------------------ 4 files changed, 258 insertions(+), 347 deletions(-) create mode 100644 lintcheck/src/config.rs diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml index c694037021a5..e63f65ce2f75 100644 --- a/lintcheck/Cargo.toml +++ b/lintcheck/Cargo.toml @@ -6,16 +6,15 @@ readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-clippy" categories = ["development-tools"] -edition = "2018" +edition = "2021" publish = false [dependencies] +cargo_metadata = "0.14" clap = "2.33" flate2 = "1.0" -fs_extra = "1.2" rayon = "1.5.1" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" tar = "0.4" toml = "0.5" ureq = "2.2" diff --git a/lintcheck/lintcheck_crates.toml b/lintcheck/lintcheck_crates.toml index dfee28f1a871..4fbae8614ca3 100644 --- a/lintcheck/lintcheck_crates.toml +++ b/lintcheck/lintcheck_crates.toml @@ -24,7 +24,7 @@ unicode-xid = {name = "unicode-xid", versions = ['0.2.1']} anyhow = {name = "anyhow", versions = ['1.0.38']} async-trait = {name = "async-trait", versions = ['0.1.42']} cxx = {name = "cxx", versions = ['1.0.32']} -ryu = {name = "ryu", version = ['1.0.5']} +ryu = {name = "ryu", versions = ['1.0.5']} serde_yaml = {name = "serde_yaml", versions = ['0.8.17']} thiserror = {name = "thiserror", versions = ['1.0.24']} # some embark crates, there are other interesting crates but diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs new file mode 100644 index 000000000000..de32b4843601 --- /dev/null +++ b/lintcheck/src/config.rs @@ -0,0 +1,140 @@ +use clap::{App, Arg, ArgMatches}; +use std::env; +use std::path::PathBuf; + +fn get_clap_config<'a>() -> ArgMatches<'a> { + App::new("lintcheck") + .about("run clippy on a set of crates and check output") + .arg( + Arg::with_name("only") + .takes_value(true) + .value_name("CRATE") + .long("only") + .help("Only process a single crate of the list"), + ) + .arg( + Arg::with_name("crates-toml") + .takes_value(true) + .value_name("CRATES-SOURCES-TOML-PATH") + .long("crates-toml") + .help("Set the path for a crates.toml where lintcheck should read the sources from"), + ) + .arg( + Arg::with_name("threads") + .takes_value(true) + .value_name("N") + .short("j") + .long("jobs") + .help("Number of threads to use, 0 automatic choice"), + ) + .arg( + Arg::with_name("fix") + .long("--fix") + .help("Runs cargo clippy --fix and checks if all suggestions apply"), + ) + .arg( + Arg::with_name("filter") + .long("--filter") + .takes_value(true) + .multiple(true) + .value_name("clippy_lint_name") + .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"), + ) + .arg( + Arg::with_name("markdown") + .long("--markdown") + .help("Change the reports table to use markdown links"), + ) + .get_matches() +} + +#[derive(Debug)] +pub(crate) struct LintcheckConfig { + /// max number of jobs to spawn (default 1) + pub max_jobs: usize, + /// we read the sources to check from here + pub sources_toml_path: PathBuf, + /// we save the clippy lint results here + pub lintcheck_results_path: PathBuf, + /// Check only a specified package + pub only: Option, + /// whether to just run --fix and not collect all the warnings + pub fix: bool, + /// A list of lints that this lintcheck run should focus on + pub lint_filter: Vec, + /// Indicate if the output should support markdown syntax + pub markdown: bool, +} + +impl LintcheckConfig { + pub fn new() -> Self { + let clap_config = get_clap_config(); + + // first, check if we got anything passed via the LINTCHECK_TOML env var, + // if not, ask clap if we got any value for --crates-toml + // if not, use the default "lintcheck/lintcheck_crates.toml" + let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { + clap_config + .value_of("crates-toml") + .clone() + .unwrap_or("lintcheck/lintcheck_crates.toml") + .to_string() + }); + + let markdown = clap_config.is_present("markdown"); + let sources_toml_path = PathBuf::from(sources_toml); + + // for the path where we save the lint results, get the filename without extension (so for + // wasd.toml, use "wasd"...) + let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); + let lintcheck_results_path = PathBuf::from(format!( + "lintcheck-logs/{}_logs.{}", + filename.display(), + if markdown { "md" } else { "txt" } + )); + + // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and + // use half of that for the physical core count + // by default use a single thread + let max_jobs = match clap_config.value_of("threads") { + Some(threads) => { + let threads: usize = threads + .parse() + .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads)); + if threads == 0 { + // automatic choice + // Rayon seems to return thread count so half that for core count + (rayon::current_num_threads() / 2) as usize + } else { + threads + } + }, + // no -j passed, use a single thread + None => 1, + }; + + let lint_filter: Vec = clap_config + .values_of("filter") + .map(|iter| { + iter.map(|lint_name| { + let mut filter = lint_name.replace('_', "-"); + if !filter.starts_with("clippy::") { + filter.insert_str(0, "clippy::"); + } + filter + }) + .collect() + }) + .unwrap_or_default(); + + LintcheckConfig { + max_jobs, + sources_toml_path, + lintcheck_results_path, + only: clap_config.value_of("only").map(String::from), + fix: clap_config.is_present("fix"), + lint_filter, + markdown, + } + } +} diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs index 816efbdaedf3..dff9d27db0a6 100644 --- a/lintcheck/src/main.rs +++ b/lintcheck/src/main.rs @@ -7,23 +7,25 @@ #![allow(clippy::collapsible_else_if)] -use std::ffi::OsStr; +mod config; + +use config::LintcheckConfig; + +use std::collections::HashMap; +use std::env; use std::fmt::Write as _; +use std::fs::write; +use std::io::ErrorKind; +use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; -use std::{collections::HashMap, io::ErrorKind}; -use std::{ - env, - fs::write, - path::{Path, PathBuf}, - thread, - time::Duration, -}; +use std::thread; +use std::time::Duration; -use clap::{App, Arg, ArgMatches}; +use cargo_metadata::diagnostic::DiagnosticLevel; +use cargo_metadata::Message; use rayon::prelude::*; use serde::{Deserialize, Serialize}; -use serde_json::Value; use walkdir::{DirEntry, WalkDir}; #[cfg(not(windows))] @@ -93,37 +95,67 @@ struct Crate { #[derive(Debug)] struct ClippyWarning { crate_name: String, - crate_version: String, file: String, - line: String, - column: String, - linttype: String, + line: usize, + column: usize, + lint_type: String, message: String, is_ice: bool, } #[allow(unused)] impl ClippyWarning { + fn new(cargo_message: Message, krate: &Crate) -> Option { + let diag = match cargo_message { + Message::CompilerMessage(message) => message.message, + _ => return None, + }; + + let lint_type = diag.code?.code; + if !(lint_type.contains("clippy") || diag.message.contains("clippy")) + || diag.message.contains("could not read cargo metadata") + { + return None; + } + + let span = diag.spans.into_iter().find(|span| span.is_primary)?; + + let file = match Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) { + Ok(stripped) => format!("$CARGO_HOME/{}", stripped.display()), + Err(_) => format!( + "target/lintcheck/sources/{}-{}/{}", + krate.name, krate.version, span.file_name + ), + }; + + Some(Self { + crate_name: krate.name.clone(), + file, + line: span.line_start, + column: span.column_start, + lint_type, + message: diag.message, + is_ice: diag.level == DiagnosticLevel::Ice, + }) + } + fn to_output(&self, markdown: bool) -> String { - let file = format!("{}-{}/{}", &self.crate_name, &self.crate_version, &self.file); - let file_with_pos = format!("{}:{}:{}", &file, &self.line, &self.column); + let file_with_pos = format!("{}:{}:{}", &self.file, &self.line, &self.column); if markdown { - let lint = format!("`{}`", self.linttype); + let lint = format!("`{}`", self.lint_type); + + let mut file = self.file.clone(); + if !file.starts_with('$') { + file.insert_str(0, "../"); + } let mut output = String::from("| "); - let _ = write!( - output, - "[`{}`](../target/lintcheck/sources/{}#L{})", - file_with_pos, file, self.line - ); + let _ = write!(output, "[`{}`]({}#L{})", file_with_pos, file, self.line); let _ = write!(output, r#" | {:<50} | "{}" |"#, lint, self.message); output.push('\n'); output } else { - format!( - "target/lintcheck/sources/{} {} \"{}\"\n", - file_with_pos, self.linttype, self.message - ) + format!("{} {} \"{}\"\n", file_with_pos, self.lint_type, self.message) } } } @@ -278,18 +310,17 @@ impl Crate { &self, cargo_clippy_path: &Path, target_dir_index: &AtomicUsize, - thread_limit: usize, total_crates_to_lint: usize, - fix: bool, + config: &LintcheckConfig, lint_filter: &Vec, ) -> Vec { // advance the atomic index by one let index = target_dir_index.fetch_add(1, Ordering::SeqCst); // "loop" the index within 0..thread_limit - let thread_index = index % thread_limit; + let thread_index = index % config.max_jobs; let perc = (index * 100) / total_crates_to_lint; - if thread_limit == 1 { + if config.max_jobs == 1 { println!( "{}/{} {}% Linting {} {}", index, total_crates_to_lint, perc, &self.name, &self.version @@ -305,7 +336,7 @@ impl Crate { let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir"); - let mut args = if fix { + let mut args = if config.fix { vec!["--fix", "--"] } else { vec!["--", "--message-format=json", "--"] @@ -356,7 +387,7 @@ impl Crate { ); } - if fix { + if config.fix { if let Some(stderr) = stderr .lines() .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate")) @@ -371,127 +402,15 @@ impl Crate { return Vec::new(); } - let output_lines = stdout.lines(); - let warnings: Vec = output_lines - .into_iter() - // get all clippy warnings and ICEs - .filter(|line| filter_clippy_warnings(&line)) - .map(|json_msg| parse_json_message(json_msg, &self)) + // get all clippy warnings and ICEs + let warnings: Vec = Message::parse_stream(stdout.as_bytes()) + .filter_map(|msg| ClippyWarning::new(msg.unwrap(), &self)) .collect(); warnings } } -#[derive(Debug)] -struct LintcheckConfig { - /// max number of jobs to spawn (default 1) - max_jobs: usize, - /// we read the sources to check from here - sources_toml_path: PathBuf, - /// we save the clippy lint results here - lintcheck_results_path: PathBuf, - /// whether to just run --fix and not collect all the warnings - fix: bool, - /// A list of lints that this lintcheck run should focus on - lint_filter: Vec, - /// Indicate if the output should support markdown syntax - markdown: bool, -} - -impl LintcheckConfig { - fn from_clap(clap_config: &ArgMatches) -> Self { - // first, check if we got anything passed via the LINTCHECK_TOML env var, - // if not, ask clap if we got any value for --crates-toml - // if not, use the default "lintcheck/lintcheck_crates.toml" - let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| { - clap_config - .value_of("crates-toml") - .clone() - .unwrap_or("lintcheck/lintcheck_crates.toml") - .to_string() - }); - - let markdown = clap_config.is_present("markdown"); - let sources_toml_path = PathBuf::from(sources_toml); - - // for the path where we save the lint results, get the filename without extension (so for - // wasd.toml, use "wasd"...) - let filename: PathBuf = sources_toml_path.file_stem().unwrap().into(); - let lintcheck_results_path = PathBuf::from(format!( - "lintcheck-logs/{}_logs.{}", - filename.display(), - if markdown { "md" } else { "txt" } - )); - - // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and - // use half of that for the physical core count - // by default use a single thread - let max_jobs = match clap_config.value_of("threads") { - Some(threads) => { - let threads: usize = threads - .parse() - .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads)); - if threads == 0 { - // automatic choice - // Rayon seems to return thread count so half that for core count - (rayon::current_num_threads() / 2) as usize - } else { - threads - } - }, - // no -j passed, use a single thread - None => 1, - }; - let fix: bool = clap_config.is_present("fix"); - let lint_filter: Vec = clap_config - .values_of("filter") - .map(|iter| { - iter.map(|lint_name| { - let mut filter = lint_name.replace('_', "-"); - if !filter.starts_with("clippy::") { - filter.insert_str(0, "clippy::"); - } - filter - }) - .collect() - }) - .unwrap_or_default(); - - LintcheckConfig { - max_jobs, - sources_toml_path, - lintcheck_results_path, - fix, - lint_filter, - markdown, - } - } -} - -/// takes a single json-formatted clippy warnings and returns true (we are interested in that line) -/// or false (we aren't) -fn filter_clippy_warnings(line: &str) -> bool { - // we want to collect ICEs because clippy might have crashed. - // these are summarized later - if line.contains("internal compiler error: ") { - return true; - } - // in general, we want all clippy warnings - // however due to some kind of bug, sometimes there are absolute paths - // to libcore files inside the message - // or we end up with cargo-metadata output (https://github.com/rust-lang/rust-clippy/issues/6508) - - // filter out these message to avoid unnecessary noise in the logs - if line.contains("clippy::") - && !(line.contains("could not read cargo metadata") - || (line.contains(".rustup") && line.contains("toolchains"))) - { - return true; - } - false -} - /// Builds clippy inside the repo to make sure we have a clippy executable we can use. fn build_clippy() { let status = Command::new("cargo") @@ -527,10 +446,8 @@ fn read_crates(toml_path: &Path) -> Vec { path: PathBuf::from(path), options: tk.options.clone(), }); - } - - // if we have multiple versions, save each one - if let Some(ref versions) = tk.versions { + } else if let Some(ref versions) = tk.versions { + // if we have multiple versions, save each one versions.iter().for_each(|ver| { crate_sources.push(CrateSource::CratesIo { name: tk.name.clone(), @@ -538,16 +455,18 @@ fn read_crates(toml_path: &Path) -> Vec { options: tk.options.clone(), }); }) - } - // otherwise, we should have a git source - if tk.git_url.is_some() && tk.git_hash.is_some() { + } else if tk.git_url.is_some() && tk.git_hash.is_some() { + // otherwise, we should have a git source crate_sources.push(CrateSource::Git { name: tk.name.clone(), url: tk.git_url.clone().unwrap(), commit: tk.git_hash.clone().unwrap(), options: tk.options.clone(), }); + } else { + panic!("Invalid crate source: {tk:?}"); } + // if we have a version as well as a git data OR only one git data, something is funky if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some()) || tk.git_hash.is_some() != tk.git_url.is_some() @@ -568,57 +487,13 @@ fn read_crates(toml_path: &Path) -> Vec { crate_sources } -/// Parse the json output of clippy and return a `ClippyWarning` -fn parse_json_message(json_message: &str, krate: &Crate) -> ClippyWarning { - let jmsg: Value = serde_json::from_str(&json_message).unwrap_or_else(|e| panic!("Failed to parse json:\n{:?}", e)); - - let file: String = jmsg["message"]["spans"][0]["file_name"] - .to_string() - .trim_matches('"') - .into(); - - let file = if file.contains(".cargo") { - // if we deal with macros, a filename may show the origin of a macro which can be inside a dep from - // the registry. - // don't show the full path in that case. - - // /home/matthias/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-1.0.63/src/custom_keyword.rs - let path = PathBuf::from(file); - let mut piter = path.iter(); - // consume all elements until we find ".cargo", so that "/home/matthias" is skipped - let _: Option<&OsStr> = piter.find(|x| x == &std::ffi::OsString::from(".cargo")); - // collect the remaining segments - let file = piter.collect::(); - format!("{}", file.display()) - } else { - file - }; - - ClippyWarning { - crate_name: krate.name.to_string(), - crate_version: krate.version.to_string(), - file, - line: jmsg["message"]["spans"][0]["line_start"] - .to_string() - .trim_matches('"') - .into(), - column: jmsg["message"]["spans"][0]["text"][0]["highlight_start"] - .to_string() - .trim_matches('"') - .into(), - linttype: jmsg["message"]["code"]["code"].to_string().trim_matches('"').into(), - message: jmsg["message"]["message"].to_string().trim_matches('"').into(), - is_ice: json_message.contains("internal compiler error: "), - } -} - /// Generate a short list of occurring lints-types and their count fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) { // count lint type occurrences let mut counter: HashMap<&String, usize> = HashMap::new(); clippy_warnings .iter() - .for_each(|wrn| *counter.entry(&wrn.linttype).or_insert(0) += 1); + .for_each(|wrn| *counter.entry(&wrn.lint_type).or_insert(0) += 1); // collect into a tupled list for sorting let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect(); @@ -667,22 +542,14 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool { logs_modified < clippy_modified } -/// lintchecks `main()` function -/// -/// # Panics -/// -/// This function panics if the clippy binaries don't exist -/// or if lintcheck is executed from the wrong directory (aka none-repo-root) -pub fn main() { +fn main() { // assert that we launch lintcheck from the repo root (via cargo lintcheck) if std::fs::metadata("lintcheck/Cargo.toml").is_err() { eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively."); std::process::exit(3); } - let clap_config = &get_clap_config(); - - let config = LintcheckConfig::from_clap(clap_config); + let config = LintcheckConfig::new(); println!("Compiling clippy..."); build_clippy(); @@ -736,76 +603,46 @@ pub fn main() { }) .collect(); - let clippy_warnings: Vec = if let Some(only_one_crate) = clap_config.value_of("only") { - // if we don't have the specified crate in the .toml, throw an error - if !crates.iter().any(|krate| { - let name = match krate { - CrateSource::CratesIo { name, .. } | CrateSource::Git { name, .. } | CrateSource::Path { name, .. } => { - name - }, - }; - name == only_one_crate - }) { - eprintln!( - "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml", - only_one_crate - ); - std::process::exit(1); - } + let crates: Vec = crates + .into_iter() + .filter(|krate| { + if let Some(only_one_crate) = &config.only { + let name = match krate { + CrateSource::CratesIo { name, .. } + | CrateSource::Git { name, .. } + | CrateSource::Path { name, .. } => name, + }; - // only check a single crate that was passed via cmdline - crates - .into_iter() - .map(|krate| krate.download_and_extract()) - .filter(|krate| krate.name == only_one_crate) - .flat_map(|krate| { - krate.run_clippy_lints(&cargo_clippy_path, &AtomicUsize::new(0), 1, 1, config.fix, &lint_filter) - }) - .collect() - } else { - if config.max_jobs > 1 { - // run parallel with rayon + name == only_one_crate + } else { + true + } + }) + .map(|krate| krate.download_and_extract()) + .collect(); - // Ask rayon for thread count. Assume that half of that is the number of physical cores - // Use one target dir for each core so that we can run N clippys in parallel. - // We need to use different target dirs because cargo would lock them for a single build otherwise, - // killing the parallelism. However this also means that deps will only be reused half/a - // quarter of the time which might result in a longer wall clock runtime + if crates.is_empty() { + eprintln!( + "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml", + config.only.unwrap(), + ); + std::process::exit(1); + } - // This helps when we check many small crates with dep-trees that don't have a lot of branches in - // order to achieve some kind of parallelism + // run parallel with rayon - // by default, use a single thread - let num_cpus = config.max_jobs; - let num_crates = crates.len(); + // This helps when we check many small crates with dep-trees that don't have a lot of branches in + // order to achieve some kind of parallelism - // check all crates (default) - crates - .into_par_iter() - .map(|krate| krate.download_and_extract()) - .flat_map(|krate| { - krate.run_clippy_lints( - &cargo_clippy_path, - &counter, - num_cpus, - num_crates, - config.fix, - &lint_filter, - ) - }) - .collect() - } else { - // run sequential - let num_crates = crates.len(); - crates - .into_iter() - .map(|krate| krate.download_and_extract()) - .flat_map(|krate| { - krate.run_clippy_lints(&cargo_clippy_path, &counter, 1, num_crates, config.fix, &lint_filter) - }) - .collect() - } - }; + rayon::ThreadPoolBuilder::new() + .num_threads(config.max_jobs) + .build_global() + .unwrap(); + + let clippy_warnings: Vec = crates + .par_iter() + .flat_map(|krate| krate.run_clippy_lints(&cargo_clippy_path, &counter, crates.len(), &config, &lint_filter)) + .collect(); // if we are in --fix mode, don't change the log files, terminate here if config.fix { @@ -837,7 +674,7 @@ pub fn main() { text.push_str("| file | lint | message |\n"); text.push_str("| --- | --- | --- |\n"); } - write!(text, "{}", all_msgs.join("")); + write!(text, "{}", all_msgs.join("")).unwrap(); text.push_str("\n\n### ICEs:\n"); for (cratename, msg) in ices.iter() { let _ = write!(text, "{}: '{}'", cratename, msg); @@ -949,75 +786,10 @@ fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) { }); } -fn get_clap_config<'a>() -> ArgMatches<'a> { - App::new("lintcheck") - .about("run clippy on a set of crates and check output") - .arg( - Arg::with_name("only") - .takes_value(true) - .value_name("CRATE") - .long("only") - .help("only process a single crate of the list"), - ) - .arg( - Arg::with_name("crates-toml") - .takes_value(true) - .value_name("CRATES-SOURCES-TOML-PATH") - .long("crates-toml") - .help("set the path for a crates.toml where lintcheck should read the sources from"), - ) - .arg( - Arg::with_name("threads") - .takes_value(true) - .value_name("N") - .short("j") - .long("jobs") - .help("number of threads to use, 0 automatic choice"), - ) - .arg( - Arg::with_name("fix") - .long("--fix") - .help("runs cargo clippy --fix and checks if all suggestions apply"), - ) - .arg( - Arg::with_name("filter") - .long("--filter") - .takes_value(true) - .multiple(true) - .value_name("clippy_lint_name") - .help("apply a filter to only collect specified lints, this also overrides `allow` attributes"), - ) - .arg( - Arg::with_name("markdown") - .long("--markdown") - .help("change the reports table to use markdown links"), - ) - .get_matches() -} - /// Returns the path to the Clippy project directory -/// -/// # Panics -/// -/// Panics if the current directory could not be retrieved, there was an error reading any of the -/// Cargo.toml files or ancestor directory is the clippy root directory #[must_use] -pub fn clippy_project_root() -> PathBuf { - let current_dir = std::env::current_dir().unwrap(); - for path in current_dir.ancestors() { - let result = std::fs::read_to_string(path.join("Cargo.toml")); - if let Err(err) = &result { - if err.kind() == std::io::ErrorKind::NotFound { - continue; - } - } - - let content = result.unwrap(); - if content.contains("[package]\nname = \"clippy\"") { - return path.to_path_buf(); - } - } - panic!("error: Can't determine root of project. Please run inside a Clippy working dir."); +fn clippy_project_root() -> &'static Path { + Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap() } #[test] From 0def44a1c15e624a6e51206048895021f5b1ee92 Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Thu, 12 May 2022 19:06:23 -0400 Subject: [PATCH 487/536] Catch other thinning allocations using Ty --- .../src/types/redundant_allocation.rs | 23 +++--------- tests/ui/redundant_allocation.rs | 27 +++++++++++--- tests/ui/redundant_allocation.stderr | 35 ++++++++++++++++--- 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 5fdf17314953..6c231fa25101 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -2,9 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_errors::Applicability; -use rustc_hir::{self as hir, def, def_id::DefId, PrimTy, QPath, TyKind}; +use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind}; use rustc_lint::LateContext; use rustc_span::symbol::sym; +use rustc_typeck::hir_ty_to_ty; use super::{utils, REDUNDANT_ALLOCATION}; @@ -54,7 +55,8 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(ty) => { - if alloc_makes_pointer_thin(cx, ty) { + // Reallocation of a fat pointer causes it to become thin + if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) { return false; } ty.span @@ -109,20 +111,3 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ } true } - -/// Returns `true` if the allocation would make `hir_ty` go from fat to thin. -fn alloc_makes_pointer_thin(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) -> bool { - match &hir_ty.kind { - TyKind::TraitObject(..) => true, - TyKind::Path(ty_qpath) => { - let ty_res = cx.qpath_res(ty_qpath, hir_ty.hir_id); - if let def::Res::PrimTy(prim_ty) = ty_res { - if matches!(prim_ty, PrimTy::Str) { - return true; - } - } - false - }, - _ => false, - } -} diff --git a/tests/ui/redundant_allocation.rs b/tests/ui/redundant_allocation.rs index b830a3771d5c..cf7d8c6e349a 100644 --- a/tests/ui/redundant_allocation.rs +++ b/tests/ui/redundant_allocation.rs @@ -98,21 +98,38 @@ mod box_dyn { } // https://github.com/rust-lang/rust-clippy/issues/8604 -mod box_str { +mod box_fat_ptr { use std::boxed::Box; + use std::path::Path; use std::rc::Rc; use std::sync::Arc; + pub struct DynSized { + foo: [usize], + } + struct S { a: Box>, b: Rc>, c: Arc>, + + e: Box>, + f: Box>, + g: Box>, } - pub fn test_box(_: Box>) {} - pub fn test_rc(_: Rc>) {} - pub fn test_arc(_: Arc>) {} - pub fn test_rc_box(_: Rc>>) {} + pub fn test_box_str(_: Box>) {} + pub fn test_rc_str(_: Rc>) {} + pub fn test_arc_str(_: Arc>) {} + + pub fn test_box_slice(_: Box>) {} + pub fn test_box_path(_: Box>) {} + pub fn test_box_custom(_: Box>) {} + + pub fn test_rc_box_str(_: Rc>>) {} + pub fn test_rc_box_slice(_: Rc>>) {} + pub fn test_rc_box_path(_: Rc>>) {} + pub fn test_rc_box_custom(_: Rc>>) {} } fn main() {} diff --git a/tests/ui/redundant_allocation.stderr b/tests/ui/redundant_allocation.stderr index ae213cb8975a..fab1b069fcbc 100644 --- a/tests/ui/redundant_allocation.stderr +++ b/tests/ui/redundant_allocation.stderr @@ -144,13 +144,40 @@ LL | pub fn test_rc_box(_: Rc>>) {} = help: consider using just `Rc>` or `Box>` error: usage of `Rc>>` - --> $DIR/redundant_allocation.rs:115:27 + --> $DIR/redundant_allocation.rs:129:31 | -LL | pub fn test_rc_box(_: Rc>>) {} - | ^^^^^^^^^^^^^^^^^ +LL | pub fn test_rc_box_str(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^ | = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation = help: consider using just `Rc>` or `Box>` -error: aborting due to 17 previous errors +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:130:33 + | +LL | pub fn test_rc_box_slice(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:131:32 + | +LL | pub fn test_rc_box_path(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: usage of `Rc>>` + --> $DIR/redundant_allocation.rs:132:34 + | +LL | pub fn test_rc_box_custom(_: Rc>>) {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `Box>` is already on the heap, `Rc>>` makes an extra allocation + = help: consider using just `Rc>` or `Box>` + +error: aborting due to 20 previous errors From ed3744b957d318f7b22b1a2d53b867f7da42746d Mon Sep 17 00:00:00 2001 From: yonip23 Date: Fri, 13 May 2022 02:20:28 +0300 Subject: [PATCH 488/536] inline construct_lint_suggestions --- clippy_lints/src/rc_clone_in_vec_init.rs | 61 ++++++++---------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/rc_clone_in_vec_init.rs b/clippy_lints/src/rc_clone_in_vec_init.rs index ca12509bdc7f..110f58f3734d 100644 --- a/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/clippy_lints/src/rc_clone_in_vec_init.rs @@ -56,39 +56,6 @@ impl LateLintPass<'_> for RcCloneInVecInit { } } -struct LintSuggestion { - message: String, - snippet: String, -} - -fn construct_lint_suggestions( - cx: &LateContext<'_>, - span: Span, - symbol_name: &str, - elem: &Expr<'_>, - len: &Expr<'_>, -) -> Vec { - let len_snippet = snippet(cx, len.span, ".."); - let elem_snippet = elem_snippet(cx, elem, symbol_name); - let indentation = indent_of(cx, span).unwrap_or(0); - let indentation = " ".repeat(indentation); - let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); - let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); - - vec![ - LintSuggestion { - message: format!("consider initializing each `{symbol_name}` element individually"), - snippet: loop_init_suggestion, - }, - LintSuggestion { - message: format!( - "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" - ), - snippet: extract_suggestion, - }, - ] -} - fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String { let elem_snippet = snippet(cx, elem.span, "..").to_string(); if elem_snippet.contains('\n') { @@ -131,17 +98,27 @@ fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr< lint_span, &format!("calling `{symbol_name}::new` in `vec![elem; len]`"), |diag| { - let suggestions = construct_lint_suggestions(cx, lint_span, symbol_name, elem, len); + let len_snippet = snippet(cx, len.span, ".."); + let elem_snippet = elem_snippet(cx, elem, symbol_name); + let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0)); + let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); + let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation); diag.note(format!("each element will point to the same `{symbol_name}` instance")); - for suggestion in suggestions { - diag.span_suggestion( - lint_span, - &suggestion.message, - &suggestion.snippet, - Applicability::Unspecified, - ); - } + diag.span_suggestion( + lint_span, + format!("consider initializing each `{symbol_name}` element individually"), + loop_init_suggestion, + Applicability::Unspecified, + ); + diag.span_suggestion( + lint_span, + format!( + "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable" + ), + extract_suggestion, + Applicability::Unspecified, + ); }, ); } From 4e5f69cc869f734d39f74a5b0d9250eb5fc101ce Mon Sep 17 00:00:00 2001 From: ydah <13041216+ydah@users.noreply.github.com> Date: Fri, 13 May 2022 14:20:25 +0900 Subject: [PATCH 489/536] Tweak some words improved representation This PR has implemented improved representation. - Use "lib" instead of "lifb" - Use "triggered" instead of "triggere" - Use "blacklisted_name" instead of "blackisted_name" - Use "stabilization" instead of "stabilisation" - Use "behavior" instead of "behaviour" - Use "target" instead of "tartet" - Use "checked_add" instead of "chcked_add" - Use "anti-pattern" instead of "antipattern" - Use "suggestion" instead of "suggesttion" - Use "example" instead of "exampel" - Use "Cheat Sheet" instead of "Cheatsheet" --- clippy_dev/src/main.rs | 2 +- clippy_dev/src/new_lint.rs | 4 ++-- clippy_lints/src/assign_ops.rs | 2 +- clippy_lints/src/casts/cast_slice_different_sizes.rs | 2 +- clippy_lints/src/casts/mod.rs | 2 +- clippy_lints/src/default_union_representation.rs | 4 ++-- clippy_lints/src/derive.rs | 4 ++-- clippy_lints/src/main_recursion.rs | 2 +- clippy_lints/src/methods/mod.rs | 2 +- clippy_lints/src/mutable_debug_assertion.rs | 2 +- clippy_lints/src/needless_bitwise_bool.rs | 4 ++-- clippy_lints/src/pass_by_ref_or_value.rs | 2 +- clippy_lints/src/redundant_clone.rs | 2 +- clippy_lints/src/transmute/mod.rs | 4 ++-- doc/adding_lints.md | 4 ++-- tests/ui/blacklisted_name.rs | 4 ++-- 16 files changed, 23 insertions(+), 23 deletions(-) diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs index dcfaabbc204b..d5cd7ca96c0c 100644 --- a/clippy_dev/src/main.rs +++ b/clippy_dev/src/main.rs @@ -124,7 +124,7 @@ fn get_clap_config<'a>() -> ArgMatches<'a> { * the lint count in README.md is correct\n \ * the changelog contains markdown link references at the bottom\n \ * all lint groups include the correct lints\n \ - * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \ + * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \ * all lints are registered in the lint store", ) .arg(Arg::with_name("print-only").long("print-only").help( diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs index 10f67d301f88..07d196387887 100644 --- a/clippy_dev/src/new_lint.rs +++ b/clippy_dev/src/new_lint.rs @@ -133,7 +133,7 @@ fn to_camel_case(name: &str) -> String { .collect() } -fn get_stabilisation_version() -> String { +fn get_stabilization_version() -> String { fn parse_manifest(contents: &str) -> Option { let version = contents .lines() @@ -199,7 +199,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String { }, }; - let version = get_stabilisation_version(); + let version = get_stabilization_version(); let lint_name = lint.name; let category = lint.category; let name_camel = to_camel_case(lint.name); diff --git a/clippy_lints/src/assign_ops.rs b/clippy_lints/src/assign_ops.rs index 12c1bddf79d5..4c2d3366483a 100644 --- a/clippy_lints/src/assign_ops.rs +++ b/clippy_lints/src/assign_ops.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { /// ### Known problems /// Clippy cannot know for sure if `a op= a op b` should have /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both. - /// If `a op= a op b` is really the correct behaviour it should be + /// If `a op= a op b` is really the correct behavior it should be /// written as `a = a op a op b` as it's less confusing. /// /// ### Example diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index bb09a6708a01..027c660ce3b2 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -121,7 +121,7 @@ fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Optio let to_slice_ty = get_raw_slice_ty_mut(cast_to)?; // If the expression that makes up the source of this cast is itself a cast, recursively - // call `expr_cast_chain_tys` and update the end type with the final tartet type. + // call `expr_cast_chain_tys` and update the end type with the final target type. // Otherwise, this cast is not immediately nested, just construct the info for this cast if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) { Some(CastChainInfo { diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 108e119c1041..daf3b7b4ce4f 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -306,7 +306,7 @@ declare_clippy_lint! { /// Checks for casts of `&T` to `&mut T` anywhere in the code. /// /// ### Why is this bad? - /// It’s basically guaranteed to be undefined behaviour. + /// It’s basically guaranteed to be undefined behavior. /// `UnsafeCell` is the only way to obtain aliasable data that is considered /// mutable. /// diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs index 9b5da0bd8a66..d559ad423df5 100644 --- a/clippy_lints/src/default_union_representation.rs +++ b/clippy_lints/src/default_union_representation.rs @@ -25,7 +25,7 @@ declare_clippy_lint! { /// /// fn main() { /// let _x: u32 = unsafe { - /// Foo { a: 0_i32 }.b // Undefined behaviour: `b` is allowed to be padding + /// Foo { a: 0_i32 }.b // Undefined behavior: `b` is allowed to be padding /// }; /// } /// ``` @@ -39,7 +39,7 @@ declare_clippy_lint! { /// /// fn main() { /// let _x: u32 = unsafe { - /// Foo { a: 0_i32 }.b // Now defined behaviour, this is just an i32 -> u32 transmute + /// Foo { a: 0_i32 }.b // Now defined behavior, this is just an i32 -> u32 transmute /// }; /// } /// ``` diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index a4757ebd8c79..fbbc5bf78a59 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -102,8 +102,8 @@ declare_clippy_lint! { /// types. /// /// ### Why is this bad? - /// To avoid surprising behaviour, these traits should - /// agree and the behaviour of `Copy` cannot be overridden. In almost all + /// To avoid surprising behavior, these traits should + /// agree and the behavior of `Copy` cannot be overridden. In almost all /// situations a `Copy` type should have a `Clone` implementation that does /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]` /// gets you. diff --git a/clippy_lints/src/main_recursion.rs b/clippy_lints/src/main_recursion.rs index fad8fa467d4b..20333c150e3d 100644 --- a/clippy_lints/src/main_recursion.rs +++ b/clippy_lints/src/main_recursion.rs @@ -12,7 +12,7 @@ declare_clippy_lint! { /// /// ### Why is this bad? /// Apart from special setups (which we could detect following attributes like #![no_std]), - /// recursing into main() seems like an unintuitive antipattern we should be able to detect. + /// recursing into main() seems like an unintuitive anti-pattern we should be able to detect. /// /// ### Example /// ```no_run diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 3e07961fcb33..35fc452ed7cf 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -1563,7 +1563,7 @@ declare_clippy_lint! { #[clippy::version = "1.39.0"] pub MANUAL_SATURATING_ARITHMETIC, style, - "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`" + "`.checked_add/sub(x).unwrap_or(MAX/MIN)`" } declare_clippy_lint! { diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 4ba68c8eacd2..8dba60f3a585 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -16,7 +16,7 @@ declare_clippy_lint! { /// ### Why is this bad? /// In release builds `debug_assert!` macros are optimized out by the /// compiler. - /// Therefore mutating something in a `debug_assert!` macro results in different behaviour + /// Therefore mutating something in a `debug_assert!` macro results in different behavior /// between a release and debug build. /// /// ### Example diff --git a/clippy_lints/src/needless_bitwise_bool.rs b/clippy_lints/src/needless_bitwise_bool.rs index 95395e2e136d..623d22bc9bdf 100644 --- a/clippy_lints/src/needless_bitwise_bool.rs +++ b/clippy_lints/src/needless_bitwise_bool.rs @@ -53,7 +53,7 @@ fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { false } -fn suggesstion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { +fn suggestion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let ExprKind::Binary(ref op, left, right) = expr.kind { if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) { let op_snippet = match op.node { @@ -75,7 +75,7 @@ impl LateLintPass<'_> for NeedlessBitwiseBool { expr.span, "use of bitwise operator instead of lazy operator between booleans", |diag| { - if let Some(sugg) = suggesstion_snippet(cx, expr) { + if let Some(sugg) = suggestion_snippet(cx, expr) { diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/clippy_lints/src/pass_by_ref_or_value.rs b/clippy_lints/src/pass_by_ref_or_value.rs index c5b8b8103a13..e3ded716341f 100644 --- a/clippy_lints/src/pass_by_ref_or_value.rs +++ b/clippy_lints/src/pass_by_ref_or_value.rs @@ -52,7 +52,7 @@ declare_clippy_lint! { /// to a function that needs the memory address. For further details, refer to /// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953) /// that explains a real case in which this false positive - /// led to an **undefined behaviour** introduced with unsafe code. + /// led to an **undefined behavior** introduced with unsafe code. /// /// ### Example /// diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 9904617353f8..b19f9aff6118 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -632,7 +632,7 @@ impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> { } /// Collect possible borrowed for every `&mut` local. -/// For exampel, `_1 = &mut _2` generate _1: {_2,...} +/// For example, `_1 = &mut _2` generate _1: {_2,...} /// Known Problems: not sure all borrowed are tracked struct PossibleOriginVisitor<'a, 'tcx> { possible_origin: TransitiveRelation, diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 342f23f030cd..d2a040beb0cf 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -27,7 +27,7 @@ declare_clippy_lint! { /// architecture. /// /// ### Why is this bad? - /// It's basically guaranteed to be undefined behaviour. + /// It's basically guaranteed to be undefined behavior. /// /// ### Known problems /// When accessing C, users might want to store pointer @@ -40,7 +40,7 @@ declare_clippy_lint! { #[clippy::version = "pre 1.29.0"] pub WRONG_TRANSMUTE, correctness, - "transmutes that are confusing at best, undefined behaviour at worst and always useless" + "transmutes that are confusing at best, undefined behavior at worst and always useless" } // FIXME: Move this to `complexity` again, after #5343 is fixed diff --git a/doc/adding_lints.md b/doc/adding_lints.md index 4dc94d9f5a5b..e8f0c338fd58 100644 --- a/doc/adding_lints.md +++ b/doc/adding_lints.md @@ -28,7 +28,7 @@ because that's clearly a non-descriptive name. - [Debugging](#debugging) - [PR Checklist](#pr-checklist) - [Adding configuration to a lint](#adding-configuration-to-a-lint) - - [Cheatsheet](#cheatsheet) + - [Cheat Sheet](#cheat-sheet) ## Setup @@ -649,7 +649,7 @@ in the following steps: with the configuration value and a rust file that should be linted by Clippy. The test can otherwise be written as usual. -## Cheatsheet +## Cheat Sheet Here are some pointers to things you are likely going to need for every lint: diff --git a/tests/ui/blacklisted_name.rs b/tests/ui/blacklisted_name.rs index 57d7139fef56..27df732a0880 100644 --- a/tests/ui/blacklisted_name.rs +++ b/tests/ui/blacklisted_name.rs @@ -46,10 +46,10 @@ fn issue_1647_ref_mut() { mod tests { fn issue_7305() { - // `blackisted_name` lint should not be triggered inside of the test code. + // `blacklisted_name` lint should not be triggered inside of the test code. let foo = 0; - // Check that even in nested functions warning is still not triggere. + // Check that even in nested functions warning is still not triggered. fn nested() { let foo = 0; } From ad7338e5659f5711e08502906147e22b44e41a3e Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Fri, 13 May 2022 05:55:50 -0400 Subject: [PATCH 490/536] Comment why `hir_ty_to_ty` is safe in `check_ty` --- clippy_lints/src/types/redundant_allocation.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs index 6c231fa25101..a1312fcda0b7 100644 --- a/clippy_lints/src/types/redundant_allocation.rs +++ b/clippy_lints/src/types/redundant_allocation.rs @@ -55,7 +55,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_ }; let inner_span = match qpath_generic_tys(inner_qpath).next() { Some(ty) => { - // Reallocation of a fat pointer causes it to become thin + // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use + // here because `mod.rs` guarantees this lint is only run on types outside of bodies and + // is not run on locals. if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx.at(ty.span), cx.param_env) { return false; } From b622f56d398155d3456887ed0025b391fed8b42c Mon Sep 17 00:00:00 2001 From: Miguel Guarniz Date: Sat, 7 May 2022 14:51:05 -0400 Subject: [PATCH 491/536] remove TestItemNamesVisitor Signed-off-by: Miguel Guarniz --- clippy_utils/src/lib.rs | 58 ++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 98a073d122e3..6db7f247a992 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -74,11 +74,10 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; -use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk}; use rustc_hir::{ def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl, - ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, + HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, }; @@ -2068,35 +2067,6 @@ pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool { false } -struct TestItemNamesVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - names: Vec, -} - -impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> { - fn visit_item(&mut self, item: &Item<'_>) { - if let ItemKind::Const(ty, _body) = item.kind { - if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { - // We could also check for the type name `test::TestDescAndFn` - if let Res::Def(DefKind::Struct, _) = path.res { - let has_test_marker = self - .tcx - .hir() - .attrs(item.hir_id()) - .iter() - .any(|a| a.has_name(sym::rustc_test_marker)); - if has_test_marker { - self.names.push(item.ident.name); - } - } - } - } - } - fn visit_trait_item(&mut self, _: &TraitItem<'_>) {} - fn visit_impl_item(&mut self, _: &ImplItem<'_>) {} - fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {} -} - static TEST_ITEM_NAMES_CACHE: SyncOnceCell>>> = SyncOnceCell::new(); fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool { @@ -2105,10 +2075,28 @@ fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn( match map.entry(module) { Entry::Occupied(entry) => f(entry.get()), Entry::Vacant(entry) => { - let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() }; - tcx.hir().visit_item_likes_in_module(module, &mut visitor); - visitor.names.sort_unstable(); - f(&*entry.insert(visitor.names)) + let mut names = Vec::new(); + for id in tcx.hir().module_items(module) { + if matches!(tcx.def_kind(id.def_id), DefKind::Const) + && let item = tcx.hir().item(id) + && let ItemKind::Const(ty, _body) = item.kind { + if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind { + // We could also check for the type name `test::TestDescAndFn` + if let Res::Def(DefKind::Struct, _) = path.res { + let has_test_marker = tcx + .hir() + .attrs(item.hir_id()) + .iter() + .any(|a| a.has_name(sym::rustc_test_marker)); + if has_test_marker { + names.push(item.ident.name); + } + } + } + } + } + names.sort_unstable(); + f(&*entry.insert(names)) }, } } From cc0607a5b184901c9f7dacebd60838235c745390 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 8 May 2022 15:12:56 -0400 Subject: [PATCH 492/536] Add bound_type_of --- clippy_lints/src/eta_reduction.rs | 4 ++-- clippy_lints/src/mut_reference.rs | 4 ++-- clippy_lints/src/transmute/transmute_undefined_repr.rs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index b7776b3f0c38..530d6d4de35f 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, ClosureKind, EarlyBinder, Ty, TypeFoldable}; +use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if check_inputs(cx, body.params, args); let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(body.value.hir_id); - let call_ty = EarlyBinder(cx.tcx.type_of(method_def_id)).subst(cx.tcx, substs); + let call_ty = cx.tcx.bound_type_of(method_def_id).subst(cx.tcx, substs); if check_sig(cx, closure_ty, call_ty); then { span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs index a323737ae404..9d8f8999ce40 100644 --- a/clippy_lints/src/mut_reference.rs +++ b/clippy_lints/src/mut_reference.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::{self, EarlyBinder, Ty}; +use rustc_middle::ty::{self, Ty}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::iter; @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { ExprKind::MethodCall(path, arguments, _) => { let def_id = cx.typeck_results().type_dependent_def_id(e.hir_id).unwrap(); let substs = cx.typeck_results().node_substs(e.hir_id); - let method_type = EarlyBinder(cx.tcx.type_of(def_id)).subst(cx.tcx, substs); + let method_type = cx.tcx.bound_type_of(def_id).subst(cx.tcx, substs); check_arguments(cx, arguments, method_type, path.ident.as_str(), "method"); }, _ => (), diff --git a/clippy_lints/src/transmute/transmute_undefined_repr.rs b/clippy_lints/src/transmute/transmute_undefined_repr.rs index 0bb577b7620b..be6277332db4 100644 --- a/clippy_lints/src/transmute/transmute_undefined_repr.rs +++ b/clippy_lints/src/transmute/transmute_undefined_repr.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::is_c_void; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, EarlyBinder, IntTy, Ty, TypeAndMut, UintTy}; +use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy}; use rustc_span::Span; #[allow(clippy::too_many_lines)] @@ -307,7 +307,7 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> .non_enum_variant() .fields .iter() - .map(|f| EarlyBinder(cx.tcx.type_of(f.did)).subst(cx.tcx, substs)); + .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs)); let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else { return ReducedTy::TypeErasure; }; From 6dab55cfae21ecc91f42b899a80fb91b72ab8e32 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Sun, 8 May 2022 15:43:18 -0400 Subject: [PATCH 493/536] Add bound_fn_sig --- clippy_utils/src/ty.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs index b46a7b86a759..8d99f3002b87 100644 --- a/clippy_utils/src/ty.rs +++ b/clippy_utils/src/ty.rs @@ -13,7 +13,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::interpret::{ConstValue, Scalar}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst}; use rustc_middle::ty::{ - self, AdtDef, Binder, EarlyBinder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, + self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr, }; use rustc_span::symbol::Ident; use rustc_span::{sym, Span, Symbol, DUMMY_SP}; @@ -520,7 +520,7 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option Some(ExprFnSig::Closure(subs.as_closure().sig())), - ty::FnDef(id, subs) => Some(ExprFnSig::Sig(EarlyBinder(cx.tcx.fn_sig(id)).subst(cx.tcx, subs))), + ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))), ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)), ty::Dynamic(bounds, _) => { let lang_items = cx.tcx.lang_items(); From 1f79a442e561521d0efae15da3da8acf17b580c2 Mon Sep 17 00:00:00 2001 From: Alex Macleod Date: Sat, 14 May 2022 19:35:16 +0100 Subject: [PATCH 494/536] Add `duplicate_mod` lint --- CHANGELOG.md | 1 + clippy_lints/src/duplicate_mod.rs | 102 ++++++++++++++++++ clippy_lints/src/lib.register_all.rs | 1 + clippy_lints/src/lib.register_lints.rs | 1 + clippy_lints/src/lib.register_suspicious.rs | 1 + clippy_lints/src/lib.rs | 2 + clippy_utils/src/diagnostics.rs | 2 +- tests/ui-cargo/duplicate_mod/fail/Cargo.toml | 5 + tests/ui-cargo/duplicate_mod/fail/src/a.rs | 1 + tests/ui-cargo/duplicate_mod/fail/src/b.rs | 1 + tests/ui-cargo/duplicate_mod/fail/src/c.rs | 1 + .../fail/src/from_other_module.rs | 1 + tests/ui-cargo/duplicate_mod/fail/src/main.rs | 16 +++ .../duplicate_mod/fail/src/main.stderr | 42 ++++++++ .../fail/src/other_module/mod.rs | 2 + 15 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 clippy_lints/src/duplicate_mod.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/Cargo.toml create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/a.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/b.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/c.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/main.rs create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/main.stderr create mode 100644 tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 47aaae9c1986..e5988a19c147 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3363,6 +3363,7 @@ Released 2018-09-13 [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy [`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref +[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else diff --git a/clippy_lints/src/duplicate_mod.rs b/clippy_lints/src/duplicate_mod.rs new file mode 100644 index 000000000000..c6c7b959d4f4 --- /dev/null +++ b/clippy_lints/src/duplicate_mod.rs @@ -0,0 +1,102 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind}; +use rustc_errors::MultiSpan; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; +use rustc_session::{declare_tool_lint, impl_lint_pass}; +use rustc_span::{FileName, Span}; +use std::collections::BTreeMap; +use std::path::PathBuf; + +declare_clippy_lint! { + /// ### What it does + /// Checks for files that are included as modules multiple times. + /// + /// ### Why is this bad? + /// Loading a file as a module more than once causes it to be compiled + /// multiple times, taking longer and putting duplicate content into the + /// module tree. + /// + /// ### Example + /// ```rust,ignore + /// // lib.rs + /// mod a; + /// mod b; + /// ``` + /// ```rust,ignore + /// // a.rs + /// #[path = "./b.rs"] + /// mod b; + /// ``` + /// + /// Use instead: + /// + /// ```rust,ignore + /// // lib.rs + /// mod a; + /// mod b; + /// ``` + /// ```rust,ignore + /// // a.rs + /// use crate::b; + /// ``` + #[clippy::version = "1.62.0"] + pub DUPLICATE_MOD, + suspicious, + "file loaded as module multiple times" +} + +#[derive(PartialOrd, Ord, PartialEq, Eq)] +struct Modules { + local_path: PathBuf, + spans: Vec, +} + +#[derive(Default)] +pub struct DuplicateMod { + /// map from the canonicalized path to `Modules`, `BTreeMap` to make the + /// order deterministic for tests + modules: BTreeMap, +} + +impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]); + +impl EarlyLintPass for DuplicateMod { + fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { + if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans)) = &item.kind + && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span) + && let Some(local_path) = real.into_local_path() + && let Ok(absolute_path) = local_path.canonicalize() + { + let modules = self.modules.entry(absolute_path).or_insert(Modules { + local_path, + spans: Vec::new(), + }); + modules.spans.push(item.span_with_attributes()); + } + } + + fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) { + for Modules { local_path, spans } in self.modules.values() { + if spans.len() < 2 { + continue; + } + + let mut multi_span = MultiSpan::from_spans(spans.clone()); + let (&first, duplicates) = spans.split_first().unwrap(); + + multi_span.push_span_label(first, "first loaded here"); + for &duplicate in duplicates { + multi_span.push_span_label(duplicate, "loaded again here"); + } + + span_lint_and_help( + cx, + DUPLICATE_MOD, + multi_span, + &format!("file is loaded as a module multiple times: `{}`", local_path.display()), + None, + "replace all but one `mod` item with `use` items", + ); + } + } +} diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index fca5e109509d..78653d932c44 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -60,6 +60,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(drop_forget_ref::FORGET_NON_DROP), LintId::of(drop_forget_ref::FORGET_REF), LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS), + LintId::of(duplicate_mod::DUPLICATE_MOD), LintId::of(duration_subsec::DURATION_SUBSEC), LintId::of(entry::MAP_ENTRY), LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 7489f53fd3c3..155de0a0b089 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -133,6 +133,7 @@ store.register_lints(&[ drop_forget_ref::FORGET_NON_DROP, drop_forget_ref::FORGET_REF, drop_forget_ref::UNDROPPED_MANUALLY_DROPS, + duplicate_mod::DUPLICATE_MOD, duration_subsec::DURATION_SUBSEC, else_if_without_else::ELSE_IF_WITHOUT_ELSE, empty_drop::EMPTY_DROP, diff --git a/clippy_lints/src/lib.register_suspicious.rs b/clippy_lints/src/lib.register_suspicious.rs index 940bf7ace5e8..5df2dd34368a 100644 --- a/clippy_lints/src/lib.register_suspicious.rs +++ b/clippy_lints/src/lib.register_suspicious.rs @@ -14,6 +14,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec! LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF), LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::FORGET_NON_DROP), + LintId::of(duplicate_mod::DUPLICATE_MOD), LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE), LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS), LintId::of(format_impl::PRINT_IN_FORMAT_IMPL), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1c502ef1d445..27cb5bf70877 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -211,6 +211,7 @@ mod doc; mod double_comparison; mod double_parens; mod drop_forget_ref; +mod duplicate_mod; mod duration_subsec; mod else_if_without_else; mod empty_drop; @@ -902,6 +903,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); + store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs index 68b5151a6487..04afe5ac3733 100644 --- a/clippy_utils/src/diagnostics.rs +++ b/clippy_utils/src/diagnostics.rs @@ -77,7 +77,7 @@ pub fn span_lint(cx: &T, lint: &'static Lint, sp: impl Into( cx: &'a T, lint: &'static Lint, - span: Span, + span: impl Into, msg: &str, help_span: Option, help: &str, diff --git a/tests/ui-cargo/duplicate_mod/fail/Cargo.toml b/tests/ui-cargo/duplicate_mod/fail/Cargo.toml new file mode 100644 index 000000000000..bf3c817de1c4 --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/Cargo.toml @@ -0,0 +1,5 @@ +[package] +name = "duplicate_mod" +edition = "2021" +publish = false +version = "0.1.0" diff --git a/tests/ui-cargo/duplicate_mod/fail/src/a.rs b/tests/ui-cargo/duplicate_mod/fail/src/a.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/a.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/b.rs b/tests/ui-cargo/duplicate_mod/fail/src/b.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/b.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/c.rs b/tests/ui-cargo/duplicate_mod/fail/src/c.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/c.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs b/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/from_other_module.rs @@ -0,0 +1 @@ + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.rs b/tests/ui-cargo/duplicate_mod/fail/src/main.rs new file mode 100644 index 000000000000..79b343da2470 --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.rs @@ -0,0 +1,16 @@ +mod a; + +mod b; +#[path = "b.rs"] +mod b2; + +mod c; +#[path = "c.rs"] +mod c2; +#[path = "c.rs"] +mod c3; + +mod from_other_module; +mod other_module; + +fn main() {} diff --git a/tests/ui-cargo/duplicate_mod/fail/src/main.stderr b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr new file mode 100644 index 000000000000..00d7739c8a2e --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/main.stderr @@ -0,0 +1,42 @@ +error: file is loaded as a module multiple times: `$DIR/b.rs` + --> $DIR/main.rs:3:1 + | +LL | mod b; + | ^^^^^^ first loaded here +LL | / #[path = "b.rs"] +LL | | mod b2; + | |_______^ loaded again here + | + = note: `-D clippy::duplicate-mod` implied by `-D warnings` + = help: replace all but one `mod` item with `use` items + +error: file is loaded as a module multiple times: `$DIR/c.rs` + --> $DIR/main.rs:7:1 + | +LL | mod c; + | ^^^^^^ first loaded here +LL | / #[path = "c.rs"] +LL | | mod c2; + | |_______^ loaded again here +LL | / #[path = "c.rs"] +LL | | mod c3; + | |_______^ loaded again here + | + = help: replace all but one `mod` item with `use` items + +error: file is loaded as a module multiple times: `$DIR/from_other_module.rs` + --> $DIR/main.rs:13:1 + | +LL | mod from_other_module; + | ^^^^^^^^^^^^^^^^^^^^^^ first loaded here + | + ::: $DIR/other_module/mod.rs:1:1 + | +LL | / #[path = "../from_other_module.rs"] +LL | | mod m; + | |______^ loaded again here + | + = help: replace all but one `mod` item with `use` items + +error: aborting due to 3 previous errors + diff --git a/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs b/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs new file mode 100644 index 000000000000..36ce7286aded --- /dev/null +++ b/tests/ui-cargo/duplicate_mod/fail/src/other_module/mod.rs @@ -0,0 +1,2 @@ +#[path = "../from_other_module.rs"] +mod m; From a30587e0fe5bc35f4ca9ea885b27bc93dbfced89 Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sat, 14 May 2022 20:08:10 -0400 Subject: [PATCH 495/536] Switch input to `type=number`; Separate version validation out of `byVersion` --- util/gh-pages/index.html | 13 ++--- util/gh-pages/script.js | 108 +++++++++++++++++++-------------------- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 11d99f39b558..8bba9640a774 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -291,7 +291,7 @@ Otherwise, have a great day =^.^= border: 1px solid var(--theme-popup-border); } - #version-filter-selector .checkbox { + #version-filter-selector .item { display: flex; } @@ -440,23 +440,24 @@ Otherwise, have a great day =^.^= diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js index 8f6832aef539..f72fe8c7f8bb 100644 --- a/util/gh-pages/script.js +++ b/util/gh-pages/script.js @@ -137,9 +137,9 @@ $scope.themes = THEMES_DEFAULT; $scope.versionFilters = { - "≥": {enabled: false, minorVersion: ""}, - "≤": {enabled: false, minorVersion: ""}, - "=": {enabled: false, minorVersion: ""}, + "≥": {enabled: false, minorVersion: null }, + "≤": {enabled: false, minorVersion: null }, + "=": {enabled: false, minorVersion: null }, }; $scope.selectTheme = function (theme) { @@ -170,7 +170,7 @@ $scope.clearVersionFilters = function () { for (let filter in $scope.versionFilters) { - $scope.versionFilters[filter] = { enabled: false, minorVersion: "" }; + $scope.versionFilters[filter] = { enabled: false, minorVersion: null }; } } @@ -178,62 +178,62 @@ return Object.values(obj).filter(x => x.enabled).length; } - $scope.byVersion = function(lint) { - function validateVersionStr(ver) { - return ver.length === 2 && !isNaN(ver); + $scope.updateVersionFilters = function() { + for (const filter in $scope.versionFilters) { + let minorVersion = $scope.versionFilters[filter].minorVersion; + + // 1.29.0 and greater + if (minorVersion && minorVersion > 28) { + $scope.versionFilters[filter].enabled = true; + } + + $scope.versionFilters[filter].enabled = false; } + } + $scope.byVersion = function(lint) { let filters = $scope.versionFilters; - - // Strip the "pre " prefix for pre 1.29.0 lints - let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; - let lintMinorVersion = lintVersion.substring(2, 4); - for (const filter in filters) { - let minorVersion = filters[filter].minorVersion; + if (filters[filter].enabled) { + let minorVersion = filters[filter].minorVersion; - // Skip the work for version strings with invalid lengths or characters - if (!validateVersionStr(minorVersion)) { - filters[filter].enabled = false; - continue; + // Strip the "pre " prefix for pre 1.29.0 lints + let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version; + let lintMinorVersion = lintVersion.substring(2, 4); + + let result; + switch (filter) { + case "≥": + result = (lintMinorVersion >= minorVersion); + break; + case "≤": + result = (lintMinorVersion <= minorVersion); + break; + // "=" gets the highest priority, since all filters are inclusive + case "=": + return (lintMinorVersion == minorVersion); + default: + return true + } + + if (!result) { + return false; + } + + let cmpFilter; + if (filter === "≥") { + cmpFilter = "≤"; + } else { + cmpFilter = "≥"; + } + + if (filters[cmpFilter].enabled) { + let cmpMinorVersion = filters[cmpFilter].minorVersion; + result = (cmpFilter === "≥") ? (lintMinorVersion >= cmpMinorVersion) : (lintMinorVersion <= cmpMinorVersion); + } + + return result; } - - filters[filter].enabled = true; - - let result; - switch (filter) { - case "≥": - result = (lintMinorVersion >= minorVersion); - break; - case "≤": - result = (lintMinorVersion <= minorVersion); - break; - // "=" gets the highest priority, since all filters are inclusive - case "=": - return (lintMinorVersion === minorVersion); - default: - return true - } - - if (!result) { - return false; - } - - let cmpFilter; - if (filter === "≥") { - cmpFilter = "≤"; - } else { - cmpFilter = "≥"; - } - - let cmpMinorVersion = filters[cmpFilter].minorVersion; - if (!validateVersionStr(cmpMinorVersion)) { - filters[cmpFilter].enabled = false; - return true; - } - - filters[cmpFilter].enabled = true; - return (cmpFilter === "≥") ? (lintMinorVersion >= cmpMinorVersion) : (lintMinorVersion <= cmpMinorVersion); } return true; From b81d7039704d2b9f84c7a90eee923a4a7a5304ac Mon Sep 17 00:00:00 2001 From: Serial <69764315+Serial-ATA@users.noreply.github.com> Date: Sun, 15 May 2022 11:30:00 -0400 Subject: [PATCH 496/536] Use early returns --- util/gh-pages/index.html | 7 ++++--- util/gh-pages/script.js | 22 +++++++++------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html index 8bba9640a774..2f586077af54 100644 --- a/util/gh-pages/index.html +++ b/util/gh-pages/index.html @@ -110,6 +110,7 @@ Otherwise, have a great day =^.^= left: auto; } + #version-filter-count { display: none; } @@ -291,7 +292,7 @@ Otherwise, have a great day =^.^= border: 1px solid var(--theme-popup-border); } - #version-filter-selector .item { + #version-filter-selector .checkbox { display: flex; } @@ -440,14 +441,14 @@ Otherwise, have a great day =^.^=