aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir_ty/src/_match.rs790
-rw-r--r--crates/ra_ide/src/ssr.rs74
-rw-r--r--crates/rust-analyzer/src/config.rs5
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs6
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap86
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap86
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs213
-rw-r--r--crates/rust-analyzer/src/main_loop.rs20
-rw-r--r--editors/code/package.json18
9 files changed, 830 insertions, 468 deletions
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
index fff257193..02a7a61f1 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -362,7 +362,12 @@ impl PatStack {
362 cx: &MatchCheckCtx, 362 cx: &MatchCheckCtx,
363 constructor: &Constructor, 363 constructor: &Constructor,
364 ) -> MatchCheckResult<Option<PatStack>> { 364 ) -> MatchCheckResult<Option<PatStack>> {
365 let result = match (self.head().as_pat(cx), constructor) { 365 if self.is_empty() {
366 return Ok(None);
367 }
368
369 let head_pat = self.head().as_pat(cx);
370 let result = match (head_pat, constructor) {
366 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { 371 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
367 if ellipsis.is_some() { 372 if ellipsis.is_some() {
368 // If there are ellipsis here, we should add the correct number of 373 // If there are ellipsis here, we should add the correct number of
@@ -531,7 +536,7 @@ impl Matrix {
531 } 536 }
532 537
533 fn heads(&self) -> Vec<PatIdOrWild> { 538 fn heads(&self) -> Vec<PatIdOrWild> {
534 self.0.iter().map(|p| p.head()).collect() 539 self.0.iter().flat_map(|p| p.get_head()).collect()
535 } 540 }
536 541
537 /// Computes `D(self)` for each contained PatStack. 542 /// Computes `D(self)` for each contained PatStack.
@@ -837,194 +842,193 @@ mod tests {
837 842
838 pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; 843 pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB};
839 844
840 pub(super) fn check_diagnostic_message(content: &str) -> String { 845 pub(super) fn check_diagnostic_message(ra_fixture: &str) -> String {
841 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().0 846 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0
842 } 847 }
843 848
844 pub(super) fn check_diagnostic(content: &str) { 849 pub(super) fn check_diagnostic(ra_fixture: &str) {
845 let diagnostic_count = 850 let diagnostic_count =
846 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1; 851 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
847 852
848 assert_eq!(1, diagnostic_count, "no diagnostic reported"); 853 assert_eq!(1, diagnostic_count, "no diagnostic reported");
849 } 854 }
850 855
851 pub(super) fn check_no_diagnostic(content: &str) { 856 pub(super) fn check_no_diagnostic(ra_fixture: &str) {
852 let diagnostic_count = 857 let diagnostic_count =
853 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1; 858 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
854 859
855 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); 860 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one");
856 } 861 }
857 862
858 #[test] 863 #[test]
859 fn empty_tuple_no_arms_diagnostic_message() { 864 fn empty_tuple_no_arms_diagnostic_message() {
860 let content = r"
861 fn test_fn() {
862 match () {
863 }
864 }
865 ";
866
867 assert_snapshot!( 865 assert_snapshot!(
868 check_diagnostic_message(content), 866 check_diagnostic_message(r"
867 fn test_fn() {
868 match () {
869 }
870 }
871 "),
869 @"\"()\": Missing match arm\n" 872 @"\"()\": Missing match arm\n"
870 ); 873 );
871 } 874 }
872 875
873 #[test] 876 #[test]
874 fn empty_tuple_no_arms() { 877 fn empty_tuple_no_arms() {
875 let content = r" 878 check_diagnostic(
879 r"
876 fn test_fn() { 880 fn test_fn() {
877 match () { 881 match () {
878 } 882 }
879 } 883 }
880 "; 884 ",
881 885 );
882 check_diagnostic(content);
883 } 886 }
884 887
885 #[test] 888 #[test]
886 fn empty_tuple_wild() { 889 fn empty_tuple_wild() {
887 let content = r" 890 check_no_diagnostic(
891 r"
888 fn test_fn() { 892 fn test_fn() {
889 match () { 893 match () {
890 _ => {} 894 _ => {}
891 } 895 }
892 } 896 }
893 "; 897 ",
894 898 );
895 check_no_diagnostic(content);
896 } 899 }
897 900
898 #[test] 901 #[test]
899 fn empty_tuple_no_diagnostic() { 902 fn empty_tuple_no_diagnostic() {
900 let content = r" 903 check_no_diagnostic(
904 r"
901 fn test_fn() { 905 fn test_fn() {
902 match () { 906 match () {
903 () => {} 907 () => {}
904 } 908 }
905 } 909 }
906 "; 910 ",
907 911 );
908 check_no_diagnostic(content);
909 } 912 }
910 913
911 #[test] 914 #[test]
912 fn tuple_of_empty_tuple_no_arms() { 915 fn tuple_of_empty_tuple_no_arms() {
913 let content = r" 916 check_diagnostic(
917 r"
914 fn test_fn() { 918 fn test_fn() {
915 match (()) { 919 match (()) {
916 } 920 }
917 } 921 }
918 "; 922 ",
919 923 );
920 check_diagnostic(content);
921 } 924 }
922 925
923 #[test] 926 #[test]
924 fn tuple_of_empty_tuple_no_diagnostic() { 927 fn tuple_of_empty_tuple_no_diagnostic() {
925 let content = r" 928 check_no_diagnostic(
929 r"
926 fn test_fn() { 930 fn test_fn() {
927 match (()) { 931 match (()) {
928 (()) => {} 932 (()) => {}
929 } 933 }
930 } 934 }
931 "; 935 ",
932 936 );
933 check_no_diagnostic(content);
934 } 937 }
935 938
936 #[test] 939 #[test]
937 fn tuple_of_two_empty_tuple_no_arms() { 940 fn tuple_of_two_empty_tuple_no_arms() {
938 let content = r" 941 check_diagnostic(
942 r"
939 fn test_fn() { 943 fn test_fn() {
940 match ((), ()) { 944 match ((), ()) {
941 } 945 }
942 } 946 }
943 "; 947 ",
944 948 );
945 check_diagnostic(content);
946 } 949 }
947 950
948 #[test] 951 #[test]
949 fn tuple_of_two_empty_tuple_no_diagnostic() { 952 fn tuple_of_two_empty_tuple_no_diagnostic() {
950 let content = r" 953 check_no_diagnostic(
954 r"
951 fn test_fn() { 955 fn test_fn() {
952 match ((), ()) { 956 match ((), ()) {
953 ((), ()) => {} 957 ((), ()) => {}
954 } 958 }
955 } 959 }
956 "; 960 ",
957 961 );
958 check_no_diagnostic(content);
959 } 962 }
960 963
961 #[test] 964 #[test]
962 fn bool_no_arms() { 965 fn bool_no_arms() {
963 let content = r" 966 check_diagnostic(
967 r"
964 fn test_fn() { 968 fn test_fn() {
965 match false { 969 match false {
966 } 970 }
967 } 971 }
968 "; 972 ",
969 973 );
970 check_diagnostic(content);
971 } 974 }
972 975
973 #[test] 976 #[test]
974 fn bool_missing_arm() { 977 fn bool_missing_arm() {
975 let content = r" 978 check_diagnostic(
979 r"
976 fn test_fn() { 980 fn test_fn() {
977 match false { 981 match false {
978 true => {} 982 true => {}
979 } 983 }
980 } 984 }
981 "; 985 ",
982 986 );
983 check_diagnostic(content);
984 } 987 }
985 988
986 #[test] 989 #[test]
987 fn bool_no_diagnostic() { 990 fn bool_no_diagnostic() {
988 let content = r" 991 check_no_diagnostic(
992 r"
989 fn test_fn() { 993 fn test_fn() {
990 match false { 994 match false {
991 true => {} 995 true => {}
992 false => {} 996 false => {}
993 } 997 }
994 } 998 }
995 "; 999 ",
996 1000 );
997 check_no_diagnostic(content);
998 } 1001 }
999 1002
1000 #[test] 1003 #[test]
1001 fn tuple_of_bools_no_arms() { 1004 fn tuple_of_bools_no_arms() {
1002 let content = r" 1005 check_diagnostic(
1006 r"
1003 fn test_fn() { 1007 fn test_fn() {
1004 match (false, true) { 1008 match (false, true) {
1005 } 1009 }
1006 } 1010 }
1007 "; 1011 ",
1008 1012 );
1009 check_diagnostic(content);
1010 } 1013 }
1011 1014
1012 #[test] 1015 #[test]
1013 fn tuple_of_bools_missing_arms() { 1016 fn tuple_of_bools_missing_arms() {
1014 let content = r" 1017 check_diagnostic(
1018 r"
1015 fn test_fn() { 1019 fn test_fn() {
1016 match (false, true) { 1020 match (false, true) {
1017 (true, true) => {}, 1021 (true, true) => {},
1018 } 1022 }
1019 } 1023 }
1020 "; 1024 ",
1021 1025 );
1022 check_diagnostic(content);
1023 } 1026 }
1024 1027
1025 #[test] 1028 #[test]
1026 fn tuple_of_bools_missing_arm() { 1029 fn tuple_of_bools_missing_arm() {
1027 let content = r" 1030 check_diagnostic(
1031 r"
1028 fn test_fn() { 1032 fn test_fn() {
1029 match (false, true) { 1033 match (false, true) {
1030 (false, true) => {}, 1034 (false, true) => {},
@@ -1032,14 +1036,14 @@ mod tests {
1032 (true, false) => {}, 1036 (true, false) => {},
1033 } 1037 }
1034 } 1038 }
1035 "; 1039 ",
1036 1040 );
1037 check_diagnostic(content);
1038 } 1041 }
1039 1042
1040 #[test] 1043 #[test]
1041 fn tuple_of_bools_with_wilds() { 1044 fn tuple_of_bools_with_wilds() {
1042 let content = r" 1045 check_no_diagnostic(
1046 r"
1043 fn test_fn() { 1047 fn test_fn() {
1044 match (false, true) { 1048 match (false, true) {
1045 (false, _) => {}, 1049 (false, _) => {},
@@ -1047,14 +1051,14 @@ mod tests {
1047 (_, true) => {}, 1051 (_, true) => {},
1048 } 1052 }
1049 } 1053 }
1050 "; 1054 ",
1051 1055 );
1052 check_no_diagnostic(content);
1053 } 1056 }
1054 1057
1055 #[test] 1058 #[test]
1056 fn tuple_of_bools_no_diagnostic() { 1059 fn tuple_of_bools_no_diagnostic() {
1057 let content = r" 1060 check_no_diagnostic(
1061 r"
1058 fn test_fn() { 1062 fn test_fn() {
1059 match (false, true) { 1063 match (false, true) {
1060 (true, true) => {}, 1064 (true, true) => {},
@@ -1063,27 +1067,27 @@ mod tests {
1063 (false, false) => {}, 1067 (false, false) => {},
1064 } 1068 }
1065 } 1069 }
1066 "; 1070 ",
1067 1071 );
1068 check_no_diagnostic(content);
1069 } 1072 }
1070 1073
1071 #[test] 1074 #[test]
1072 fn tuple_of_bools_binding_missing_arms() { 1075 fn tuple_of_bools_binding_missing_arms() {
1073 let content = r" 1076 check_diagnostic(
1077 r"
1074 fn test_fn() { 1078 fn test_fn() {
1075 match (false, true) { 1079 match (false, true) {
1076 (true, _x) => {}, 1080 (true, _x) => {},
1077 } 1081 }
1078 } 1082 }
1079 "; 1083 ",
1080 1084 );
1081 check_diagnostic(content);
1082 } 1085 }
1083 1086
1084 #[test] 1087 #[test]
1085 fn tuple_of_bools_binding_no_diagnostic() { 1088 fn tuple_of_bools_binding_no_diagnostic() {
1086 let content = r" 1089 check_no_diagnostic(
1090 r"
1087 fn test_fn() { 1091 fn test_fn() {
1088 match (false, true) { 1092 match (false, true) {
1089 (true, _x) => {}, 1093 (true, _x) => {},
@@ -1091,80 +1095,80 @@ mod tests {
1091 (false, false) => {}, 1095 (false, false) => {},
1092 } 1096 }
1093 } 1097 }
1094 "; 1098 ",
1095 1099 );
1096 check_no_diagnostic(content);
1097 } 1100 }
1098 1101
1099 #[test] 1102 #[test]
1100 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() { 1103 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
1101 let content = r" 1104 check_no_diagnostic(
1105 r"
1102 fn test_fn() { 1106 fn test_fn() {
1103 match (false, true, false) { 1107 match (false, true, false) {
1104 (false, ..) => {}, 1108 (false, ..) => {},
1105 (true, ..) => {}, 1109 (true, ..) => {},
1106 } 1110 }
1107 } 1111 }
1108 "; 1112 ",
1109 1113 );
1110 check_no_diagnostic(content);
1111 } 1114 }
1112 1115
1113 #[test] 1116 #[test]
1114 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() { 1117 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
1115 let content = r" 1118 check_no_diagnostic(
1119 r"
1116 fn test_fn() { 1120 fn test_fn() {
1117 match (false, true, false) { 1121 match (false, true, false) {
1118 (.., false) => {}, 1122 (.., false) => {},
1119 (.., true) => {}, 1123 (.., true) => {},
1120 } 1124 }
1121 } 1125 }
1122 "; 1126 ",
1123 1127 );
1124 check_no_diagnostic(content);
1125 } 1128 }
1126 1129
1127 #[test] 1130 #[test]
1128 fn tuple_of_bools_with_ellipsis_no_diagnostic() { 1131 fn tuple_of_bools_with_ellipsis_no_diagnostic() {
1129 let content = r" 1132 check_no_diagnostic(
1133 r"
1130 fn test_fn() { 1134 fn test_fn() {
1131 match (false, true, false) { 1135 match (false, true, false) {
1132 (..) => {}, 1136 (..) => {},
1133 } 1137 }
1134 } 1138 }
1135 "; 1139 ",
1136 1140 );
1137 check_no_diagnostic(content);
1138 } 1141 }
1139 1142
1140 #[test] 1143 #[test]
1141 fn tuple_of_tuple_and_bools_no_arms() { 1144 fn tuple_of_tuple_and_bools_no_arms() {
1142 let content = r" 1145 check_diagnostic(
1146 r"
1143 fn test_fn() { 1147 fn test_fn() {
1144 match (false, ((), false)) { 1148 match (false, ((), false)) {
1145 } 1149 }
1146 } 1150 }
1147 "; 1151 ",
1148 1152 );
1149 check_diagnostic(content);
1150 } 1153 }
1151 1154
1152 #[test] 1155 #[test]
1153 fn tuple_of_tuple_and_bools_missing_arms() { 1156 fn tuple_of_tuple_and_bools_missing_arms() {
1154 let content = r" 1157 check_diagnostic(
1158 r"
1155 fn test_fn() { 1159 fn test_fn() {
1156 match (false, ((), false)) { 1160 match (false, ((), false)) {
1157 (true, ((), true)) => {}, 1161 (true, ((), true)) => {},
1158 } 1162 }
1159 } 1163 }
1160 "; 1164 ",
1161 1165 );
1162 check_diagnostic(content);
1163 } 1166 }
1164 1167
1165 #[test] 1168 #[test]
1166 fn tuple_of_tuple_and_bools_no_diagnostic() { 1169 fn tuple_of_tuple_and_bools_no_diagnostic() {
1167 let content = r" 1170 check_no_diagnostic(
1171 r"
1168 fn test_fn() { 1172 fn test_fn() {
1169 match (false, ((), false)) { 1173 match (false, ((), false)) {
1170 (true, ((), true)) => {}, 1174 (true, ((), true)) => {},
@@ -1173,27 +1177,27 @@ mod tests {
1173 (false, ((), false)) => {}, 1177 (false, ((), false)) => {},
1174 } 1178 }
1175 } 1179 }
1176 "; 1180 ",
1177 1181 );
1178 check_no_diagnostic(content);
1179 } 1182 }
1180 1183
1181 #[test] 1184 #[test]
1182 fn tuple_of_tuple_and_bools_wildcard_missing_arms() { 1185 fn tuple_of_tuple_and_bools_wildcard_missing_arms() {
1183 let content = r" 1186 check_diagnostic(
1187 r"
1184 fn test_fn() { 1188 fn test_fn() {
1185 match (false, ((), false)) { 1189 match (false, ((), false)) {
1186 (true, _) => {}, 1190 (true, _) => {},
1187 } 1191 }
1188 } 1192 }
1189 "; 1193 ",
1190 1194 );
1191 check_diagnostic(content);
1192 } 1195 }
1193 1196
1194 #[test] 1197 #[test]
1195 fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() { 1198 fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() {
1196 let content = r" 1199 check_no_diagnostic(
1200 r"
1197 fn test_fn() { 1201 fn test_fn() {
1198 match (false, ((), false)) { 1202 match (false, ((), false)) {
1199 (true, ((), true)) => {}, 1203 (true, ((), true)) => {},
@@ -1201,14 +1205,14 @@ mod tests {
1201 (false, _) => {}, 1205 (false, _) => {},
1202 } 1206 }
1203 } 1207 }
1204 "; 1208 ",
1205 1209 );
1206 check_no_diagnostic(content);
1207 } 1210 }
1208 1211
1209 #[test] 1212 #[test]
1210 fn enum_no_arms() { 1213 fn enum_no_arms() {
1211 let content = r" 1214 check_diagnostic(
1215 r"
1212 enum Either { 1216 enum Either {
1213 A, 1217 A,
1214 B, 1218 B,
@@ -1217,14 +1221,14 @@ mod tests {
1217 match Either::A { 1221 match Either::A {
1218 } 1222 }
1219 } 1223 }
1220 "; 1224 ",
1221 1225 );
1222 check_diagnostic(content);
1223 } 1226 }
1224 1227
1225 #[test] 1228 #[test]
1226 fn enum_missing_arms() { 1229 fn enum_missing_arms() {
1227 let content = r" 1230 check_diagnostic(
1231 r"
1228 enum Either { 1232 enum Either {
1229 A, 1233 A,
1230 B, 1234 B,
@@ -1234,14 +1238,14 @@ mod tests {
1234 Either::A => {}, 1238 Either::A => {},
1235 } 1239 }
1236 } 1240 }
1237 "; 1241 ",
1238 1242 );
1239 check_diagnostic(content);
1240 } 1243 }
1241 1244
1242 #[test] 1245 #[test]
1243 fn enum_no_diagnostic() { 1246 fn enum_no_diagnostic() {
1244 let content = r" 1247 check_no_diagnostic(
1248 r"
1245 enum Either { 1249 enum Either {
1246 A, 1250 A,
1247 B, 1251 B,
@@ -1252,14 +1256,14 @@ mod tests {
1252 Either::B => {}, 1256 Either::B => {},
1253 } 1257 }
1254 } 1258 }
1255 "; 1259 ",
1256 1260 );
1257 check_no_diagnostic(content);
1258 } 1261 }
1259 1262
1260 #[test] 1263 #[test]
1261 fn enum_ref_missing_arms() { 1264 fn enum_ref_missing_arms() {
1262 let content = r" 1265 check_diagnostic(
1266 r"
1263 enum Either { 1267 enum Either {
1264 A, 1268 A,
1265 B, 1269 B,
@@ -1269,14 +1273,14 @@ mod tests {
1269 Either::A => {}, 1273 Either::A => {},
1270 } 1274 }
1271 } 1275 }
1272 "; 1276 ",
1273 1277 );
1274 check_diagnostic(content);
1275 } 1278 }
1276 1279
1277 #[test] 1280 #[test]
1278 fn enum_ref_no_diagnostic() { 1281 fn enum_ref_no_diagnostic() {
1279 let content = r" 1282 check_no_diagnostic(
1283 r"
1280 enum Either { 1284 enum Either {
1281 A, 1285 A,
1282 B, 1286 B,
@@ -1287,14 +1291,14 @@ mod tests {
1287 Either::B => {}, 1291 Either::B => {},
1288 } 1292 }
1289 } 1293 }
1290 "; 1294 ",
1291 1295 );
1292 check_no_diagnostic(content);
1293 } 1296 }
1294 1297
1295 #[test] 1298 #[test]
1296 fn enum_containing_bool_no_arms() { 1299 fn enum_containing_bool_no_arms() {
1297 let content = r" 1300 check_diagnostic(
1301 r"
1298 enum Either { 1302 enum Either {
1299 A(bool), 1303 A(bool),
1300 B, 1304 B,
@@ -1303,14 +1307,14 @@ mod tests {
1303 match Either::B { 1307 match Either::B {
1304 } 1308 }
1305 } 1309 }
1306 "; 1310 ",
1307 1311 );
1308 check_diagnostic(content);
1309 } 1312 }
1310 1313
1311 #[test] 1314 #[test]
1312 fn enum_containing_bool_missing_arms() { 1315 fn enum_containing_bool_missing_arms() {
1313 let content = r" 1316 check_diagnostic(
1317 r"
1314 enum Either { 1318 enum Either {
1315 A(bool), 1319 A(bool),
1316 B, 1320 B,
@@ -1321,14 +1325,14 @@ mod tests {
1321 Either::B => (), 1325 Either::B => (),
1322 } 1326 }
1323 } 1327 }
1324 "; 1328 ",
1325 1329 );
1326 check_diagnostic(content);
1327 } 1330 }
1328 1331
1329 #[test] 1332 #[test]
1330 fn enum_containing_bool_no_diagnostic() { 1333 fn enum_containing_bool_no_diagnostic() {
1331 let content = r" 1334 check_no_diagnostic(
1335 r"
1332 enum Either { 1336 enum Either {
1333 A(bool), 1337 A(bool),
1334 B, 1338 B,
@@ -1340,14 +1344,14 @@ mod tests {
1340 Either::B => (), 1344 Either::B => (),
1341 } 1345 }
1342 } 1346 }
1343 "; 1347 ",
1344 1348 );
1345 check_no_diagnostic(content);
1346 } 1349 }
1347 1350
1348 #[test] 1351 #[test]
1349 fn enum_containing_bool_with_wild_no_diagnostic() { 1352 fn enum_containing_bool_with_wild_no_diagnostic() {
1350 let content = r" 1353 check_no_diagnostic(
1354 r"
1351 enum Either { 1355 enum Either {
1352 A(bool), 1356 A(bool),
1353 B, 1357 B,
@@ -1358,14 +1362,14 @@ mod tests {
1358 _ => (), 1362 _ => (),
1359 } 1363 }
1360 } 1364 }
1361 "; 1365 ",
1362 1366 );
1363 check_no_diagnostic(content);
1364 } 1367 }
1365 1368
1366 #[test] 1369 #[test]
1367 fn enum_containing_bool_with_wild_2_no_diagnostic() { 1370 fn enum_containing_bool_with_wild_2_no_diagnostic() {
1368 let content = r" 1371 check_no_diagnostic(
1372 r"
1369 enum Either { 1373 enum Either {
1370 A(bool), 1374 A(bool),
1371 B, 1375 B,
@@ -1376,14 +1380,14 @@ mod tests {
1376 Either::B => (), 1380 Either::B => (),
1377 } 1381 }
1378 } 1382 }
1379 "; 1383 ",
1380 1384 );
1381 check_no_diagnostic(content);
1382 } 1385 }
1383 1386
1384 #[test] 1387 #[test]
1385 fn enum_different_sizes_missing_arms() { 1388 fn enum_different_sizes_missing_arms() {
1386 let content = r" 1389 check_diagnostic(
1390 r"
1387 enum Either { 1391 enum Either {
1388 A(bool), 1392 A(bool),
1389 B(bool, bool), 1393 B(bool, bool),
@@ -1394,14 +1398,14 @@ mod tests {
1394 Either::B(false, _) => (), 1398 Either::B(false, _) => (),
1395 } 1399 }
1396 } 1400 }
1397 "; 1401 ",
1398 1402 );
1399 check_diagnostic(content);
1400 } 1403 }
1401 1404
1402 #[test] 1405 #[test]
1403 fn enum_different_sizes_no_diagnostic() { 1406 fn enum_different_sizes_no_diagnostic() {
1404 let content = r" 1407 check_no_diagnostic(
1408 r"
1405 enum Either { 1409 enum Either {
1406 A(bool), 1410 A(bool),
1407 B(bool, bool), 1411 B(bool, bool),
@@ -1413,14 +1417,14 @@ mod tests {
1413 Either::B(false, _) => (), 1417 Either::B(false, _) => (),
1414 } 1418 }
1415 } 1419 }
1416 "; 1420 ",
1417 1421 );
1418 check_no_diagnostic(content);
1419 } 1422 }
1420 1423
1421 #[test] 1424 #[test]
1422 fn or_no_diagnostic() { 1425 fn or_no_diagnostic() {
1423 let content = r" 1426 check_no_diagnostic(
1427 r"
1424 enum Either { 1428 enum Either {
1425 A(bool), 1429 A(bool),
1426 B(bool, bool), 1430 B(bool, bool),
@@ -1432,14 +1436,14 @@ mod tests {
1432 Either::B(false, _) => (), 1436 Either::B(false, _) => (),
1433 } 1437 }
1434 } 1438 }
1435 "; 1439 ",
1436 1440 );
1437 check_no_diagnostic(content);
1438 } 1441 }
1439 1442
1440 #[test] 1443 #[test]
1441 fn tuple_of_enum_no_diagnostic() { 1444 fn tuple_of_enum_no_diagnostic() {
1442 let content = r" 1445 check_no_diagnostic(
1446 r"
1443 enum Either { 1447 enum Either {
1444 A(bool), 1448 A(bool),
1445 B(bool, bool), 1449 B(bool, bool),
@@ -1456,14 +1460,16 @@ mod tests {
1456 (Either::B(_, _), Either2::D) => (), 1460 (Either::B(_, _), Either2::D) => (),
1457 } 1461 }
1458 } 1462 }
1459 "; 1463 ",
1460 1464 );
1461 check_no_diagnostic(content);
1462 } 1465 }
1463 1466
1464 #[test] 1467 #[test]
1465 fn mismatched_types() { 1468 fn mismatched_types() {
1466 let content = r" 1469 // Match statements with arms that don't match the
1470 // expression pattern do not fire this diagnostic.
1471 check_no_diagnostic(
1472 r"
1467 enum Either { 1473 enum Either {
1468 A, 1474 A,
1469 B, 1475 B,
@@ -1478,47 +1484,47 @@ mod tests {
1478 Either2::D => (), 1484 Either2::D => (),
1479 } 1485 }
1480 } 1486 }
1481 "; 1487 ",
1482 1488 );
1483 // Match statements with arms that don't match the
1484 // expression pattern do not fire this diagnostic.
1485 check_no_diagnostic(content);
1486 } 1489 }
1487 1490
1488 #[test] 1491 #[test]
1489 fn mismatched_types_with_different_arity() { 1492 fn mismatched_types_with_different_arity() {
1490 let content = r" 1493 // Match statements with arms that don't match the
1494 // expression pattern do not fire this diagnostic.
1495 check_no_diagnostic(
1496 r"
1491 fn test_fn() { 1497 fn test_fn() {
1492 match (true, false) { 1498 match (true, false) {
1493 (true, false, true) => (), 1499 (true, false, true) => (),
1494 (true) => (), 1500 (true) => (),
1495 } 1501 }
1496 } 1502 }
1497 "; 1503 ",
1498 1504 );
1499 // Match statements with arms that don't match the
1500 // expression pattern do not fire this diagnostic.
1501 check_no_diagnostic(content);
1502 } 1505 }
1503 1506
1504 #[test] 1507 #[test]
1505 fn malformed_match_arm_tuple_missing_pattern() { 1508 fn malformed_match_arm_tuple_missing_pattern() {
1506 let content = r" 1509 // Match statements with arms that don't match the
1510 // expression pattern do not fire this diagnostic.
1511 check_no_diagnostic(
1512 r"
1507 fn test_fn() { 1513 fn test_fn() {
1508 match (0) { 1514 match (0) {
1509 () => (), 1515 () => (),
1510 } 1516 }
1511 } 1517 }
1512 "; 1518 ",
1513 1519 );
1514 // Match statements with arms that don't match the
1515 // expression pattern do not fire this diagnostic.
1516 check_no_diagnostic(content);
1517 } 1520 }
1518 1521
1519 #[test] 1522 #[test]
1520 fn malformed_match_arm_tuple_enum_missing_pattern() { 1523 fn malformed_match_arm_tuple_enum_missing_pattern() {
1521 let content = r" 1524 // We are testing to be sure we don't panic here when the match
1525 // arm `Either::B` is missing its pattern.
1526 check_no_diagnostic(
1527 r"
1522 enum Either { 1528 enum Either {
1523 A, 1529 A,
1524 B(u32), 1530 B(u32),
@@ -1529,32 +1535,30 @@ mod tests {
1529 Either::B() => (), 1535 Either::B() => (),
1530 } 1536 }
1531 } 1537 }
1532 "; 1538 ",
1533 1539 );
1534 // We are testing to be sure we don't panic here when the match
1535 // arm `Either::B` is missing its pattern.
1536 check_no_diagnostic(content);
1537 } 1540 }
1538 1541
1539 #[test] 1542 #[test]
1540 fn enum_not_in_scope() { 1543 fn enum_not_in_scope() {
1541 let content = r" 1544 // The enum is not in scope so we don't perform exhaustiveness
1545 // checking, but we want to be sure we don't panic here (and
1546 // we don't create a diagnostic).
1547 check_no_diagnostic(
1548 r"
1542 fn test_fn() { 1549 fn test_fn() {
1543 match Foo::Bar { 1550 match Foo::Bar {
1544 Foo::Baz => (), 1551 Foo::Baz => (),
1545 } 1552 }
1546 } 1553 }
1547 "; 1554 ",
1548 1555 );
1549 // The enum is not in scope so we don't perform exhaustiveness
1550 // checking, but we want to be sure we don't panic here (and
1551 // we don't create a diagnostic).
1552 check_no_diagnostic(content);
1553 } 1556 }
1554 1557
1555 #[test] 1558 #[test]
1556 fn expr_diverges() { 1559 fn expr_diverges() {
1557 let content = r" 1560 check_no_diagnostic(
1561 r"
1558 enum Either { 1562 enum Either {
1559 A, 1563 A,
1560 B, 1564 B,
@@ -1565,14 +1569,14 @@ mod tests {
1565 Either::B => (), 1569 Either::B => (),
1566 } 1570 }
1567 } 1571 }
1568 "; 1572 ",
1569 1573 );
1570 check_no_diagnostic(content);
1571 } 1574 }
1572 1575
1573 #[test] 1576 #[test]
1574 fn expr_loop_with_break() { 1577 fn expr_loop_with_break() {
1575 let content = r" 1578 check_no_diagnostic(
1579 r"
1576 enum Either { 1580 enum Either {
1577 A, 1581 A,
1578 B, 1582 B,
@@ -1583,14 +1587,14 @@ mod tests {
1583 Either::B => (), 1587 Either::B => (),
1584 } 1588 }
1585 } 1589 }
1586 "; 1590 ",
1587 1591 );
1588 check_no_diagnostic(content);
1589 } 1592 }
1590 1593
1591 #[test] 1594 #[test]
1592 fn expr_partially_diverges() { 1595 fn expr_partially_diverges() {
1593 let content = r" 1596 check_no_diagnostic(
1597 r"
1594 enum Either<T> { 1598 enum Either<T> {
1595 A(T), 1599 A(T),
1596 B, 1600 B,
@@ -1604,14 +1608,14 @@ mod tests {
1604 Either::B => 0, 1608 Either::B => 0,
1605 } 1609 }
1606 } 1610 }
1607 "; 1611 ",
1608 1612 );
1609 check_no_diagnostic(content);
1610 } 1613 }
1611 1614
1612 #[test] 1615 #[test]
1613 fn enum_record_no_arms() { 1616 fn enum_record_no_arms() {
1614 let content = r" 1617 check_diagnostic(
1618 r"
1615 enum Either { 1619 enum Either {
1616 A { foo: bool }, 1620 A { foo: bool },
1617 B, 1621 B,
@@ -1621,14 +1625,14 @@ mod tests {
1621 match a { 1625 match a {
1622 } 1626 }
1623 } 1627 }
1624 "; 1628 ",
1625 1629 );
1626 check_diagnostic(content);
1627 } 1630 }
1628 1631
1629 #[test] 1632 #[test]
1630 fn enum_record_missing_arms() { 1633 fn enum_record_missing_arms() {
1631 let content = r" 1634 check_diagnostic(
1635 r"
1632 enum Either { 1636 enum Either {
1633 A { foo: bool }, 1637 A { foo: bool },
1634 B, 1638 B,
@@ -1639,14 +1643,14 @@ mod tests {
1639 Either::A { foo: true } => (), 1643 Either::A { foo: true } => (),
1640 } 1644 }
1641 } 1645 }
1642 "; 1646 ",
1643 1647 );
1644 check_diagnostic(content);
1645 } 1648 }
1646 1649
1647 #[test] 1650 #[test]
1648 fn enum_record_no_diagnostic() { 1651 fn enum_record_no_diagnostic() {
1649 let content = r" 1652 check_no_diagnostic(
1653 r"
1650 enum Either { 1654 enum Either {
1651 A { foo: bool }, 1655 A { foo: bool },
1652 B, 1656 B,
@@ -1659,14 +1663,17 @@ mod tests {
1659 Either::B => (), 1663 Either::B => (),
1660 } 1664 }
1661 } 1665 }
1662 "; 1666 ",
1663 1667 );
1664 check_no_diagnostic(content);
1665 } 1668 }
1666 1669
1667 #[test] 1670 #[test]
1668 fn enum_record_missing_field_no_diagnostic() { 1671 fn enum_record_missing_field_no_diagnostic() {
1669 let content = r" 1672 // When `Either::A` is missing a struct member, we don't want
1673 // to fire the missing match arm diagnostic. This should fire
1674 // some other diagnostic.
1675 check_no_diagnostic(
1676 r"
1670 enum Either { 1677 enum Either {
1671 A { foo: bool }, 1678 A { foo: bool },
1672 B, 1679 B,
@@ -1678,17 +1685,16 @@ mod tests {
1678 Either::B => (), 1685 Either::B => (),
1679 } 1686 }
1680 } 1687 }
1681 "; 1688 ",
1682 1689 );
1683 // When `Either::A` is missing a struct member, we don't want
1684 // to fire the missing match arm diagnostic. This should fire
1685 // some other diagnostic.
1686 check_no_diagnostic(content);
1687 } 1690 }
1688 1691
1689 #[test] 1692 #[test]
1690 fn enum_record_missing_field_missing_match_arm() { 1693 fn enum_record_missing_field_missing_match_arm() {
1691 let content = r" 1694 // Even though `Either::A` is missing fields, we still want to fire
1695 // the missing arm diagnostic here, since we know `Either::B` is missing.
1696 check_diagnostic(
1697 r"
1692 enum Either { 1698 enum Either {
1693 A { foo: bool }, 1699 A { foo: bool },
1694 B, 1700 B,
@@ -1699,16 +1705,14 @@ mod tests {
1699 Either::A { } => (), 1705 Either::A { } => (),
1700 } 1706 }
1701 } 1707 }
1702 "; 1708 ",
1703 1709 );
1704 // Even though `Either::A` is missing fields, we still want to fire
1705 // the missing arm diagnostic here, since we know `Either::B` is missing.
1706 check_diagnostic(content);
1707 } 1710 }
1708 1711
1709 #[test] 1712 #[test]
1710 fn enum_record_no_diagnostic_wild() { 1713 fn enum_record_no_diagnostic_wild() {
1711 let content = r" 1714 check_no_diagnostic(
1715 r"
1712 enum Either { 1716 enum Either {
1713 A { foo: bool }, 1717 A { foo: bool },
1714 B, 1718 B,
@@ -1720,14 +1724,14 @@ mod tests {
1720 Either::B => (), 1724 Either::B => (),
1721 } 1725 }
1722 } 1726 }
1723 "; 1727 ",
1724 1728 );
1725 check_no_diagnostic(content);
1726 } 1729 }
1727 1730
1728 #[test] 1731 #[test]
1729 fn enum_record_fields_out_of_order_missing_arm() { 1732 fn enum_record_fields_out_of_order_missing_arm() {
1730 let content = r" 1733 check_diagnostic(
1734 r"
1731 enum Either { 1735 enum Either {
1732 A { foo: bool, bar: () }, 1736 A { foo: bool, bar: () },
1733 B, 1737 B,
@@ -1739,14 +1743,14 @@ mod tests {
1739 Either::A { foo: true, bar: () } => (), 1743 Either::A { foo: true, bar: () } => (),
1740 } 1744 }
1741 } 1745 }
1742 "; 1746 ",
1743 1747 );
1744 check_diagnostic(content);
1745 } 1748 }
1746 1749
1747 #[test] 1750 #[test]
1748 fn enum_record_fields_out_of_order_no_diagnostic() { 1751 fn enum_record_fields_out_of_order_no_diagnostic() {
1749 let content = r" 1752 check_no_diagnostic(
1753 r"
1750 enum Either { 1754 enum Either {
1751 A { foo: bool, bar: () }, 1755 A { foo: bool, bar: () },
1752 B, 1756 B,
@@ -1759,89 +1763,89 @@ mod tests {
1759 Either::B => (), 1763 Either::B => (),
1760 } 1764 }
1761 } 1765 }
1762 "; 1766 ",
1763 1767 );
1764 check_no_diagnostic(content);
1765 } 1768 }
1766 1769
1767 #[test] 1770 #[test]
1768 fn enum_record_ellipsis_missing_arm() { 1771 fn enum_record_ellipsis_missing_arm() {
1769 let content = r" 1772 check_diagnostic(
1770 enum Either { 1773 r"
1771 A { foo: bool, bar: bool }, 1774 enum Either {
1772 B, 1775 A { foo: bool, bar: bool },
1773 } 1776 B,
1774 fn test_fn() { 1777 }
1775 match Either::B { 1778 fn test_fn() {
1776 Either::A { foo: true, .. } => (), 1779 match Either::B {
1777 Either::B => (), 1780 Either::A { foo: true, .. } => (),
1778 } 1781 Either::B => (),
1779 } 1782 }
1780 "; 1783 }
1781 1784 ",
1782 check_diagnostic(content); 1785 );
1783 } 1786 }
1784 1787
1785 #[test] 1788 #[test]
1786 fn enum_record_ellipsis_no_diagnostic() { 1789 fn enum_record_ellipsis_no_diagnostic() {
1787 let content = r" 1790 check_no_diagnostic(
1788 enum Either { 1791 r"
1789 A { foo: bool, bar: bool }, 1792 enum Either {
1790 B, 1793 A { foo: bool, bar: bool },
1791 } 1794 B,
1792 fn test_fn() { 1795 }
1793 let a = Either::A { foo: true }; 1796 fn test_fn() {
1794 match a { 1797 let a = Either::A { foo: true };
1795 Either::A { foo: true, .. } => (), 1798 match a {
1796 Either::A { foo: false, .. } => (), 1799 Either::A { foo: true, .. } => (),
1797 Either::B => (), 1800 Either::A { foo: false, .. } => (),
1798 } 1801 Either::B => (),
1799 } 1802 }
1800 "; 1803 }
1801 1804 ",
1802 check_no_diagnostic(content); 1805 );
1803 } 1806 }
1804 1807
1805 #[test] 1808 #[test]
1806 fn enum_record_ellipsis_all_fields_missing_arm() { 1809 fn enum_record_ellipsis_all_fields_missing_arm() {
1807 let content = r" 1810 check_diagnostic(
1808 enum Either { 1811 r"
1809 A { foo: bool, bar: bool }, 1812 enum Either {
1810 B, 1813 A { foo: bool, bar: bool },
1811 } 1814 B,
1812 fn test_fn() { 1815 }
1813 let a = Either::B; 1816 fn test_fn() {
1814 match a { 1817 let a = Either::B;
1815 Either::A { .. } => (), 1818 match a {
1816 } 1819 Either::A { .. } => (),
1817 } 1820 }
1818 "; 1821 }
1819 1822 ",
1820 check_diagnostic(content); 1823 );
1821 } 1824 }
1822 1825
1823 #[test] 1826 #[test]
1824 fn enum_record_ellipsis_all_fields_no_diagnostic() { 1827 fn enum_record_ellipsis_all_fields_no_diagnostic() {
1825 let content = r" 1828 check_no_diagnostic(
1826 enum Either { 1829 r"
1827 A { foo: bool, bar: bool }, 1830 enum Either {
1828 B, 1831 A { foo: bool, bar: bool },
1829 } 1832 B,
1830 fn test_fn() { 1833 }
1831 let a = Either::B; 1834 fn test_fn() {
1832 match a { 1835 let a = Either::B;
1833 Either::A { .. } => (), 1836 match a {
1834 Either::B => (), 1837 Either::A { .. } => (),
1835 } 1838 Either::B => (),
1836 } 1839 }
1837 "; 1840 }
1838 1841 ",
1839 check_no_diagnostic(content); 1842 );
1840 } 1843 }
1841 1844
1842 #[test] 1845 #[test]
1843 fn enum_tuple_partial_ellipsis_no_diagnostic() { 1846 fn enum_tuple_partial_ellipsis_no_diagnostic() {
1844 let content = r" 1847 check_no_diagnostic(
1848 r"
1845 enum Either { 1849 enum Either {
1846 A(bool, bool, bool, bool), 1850 A(bool, bool, bool, bool),
1847 B, 1851 B,
@@ -1855,14 +1859,14 @@ mod tests {
1855 Either::B => {}, 1859 Either::B => {},
1856 } 1860 }
1857 } 1861 }
1858 "; 1862 ",
1859 1863 );
1860 check_no_diagnostic(content);
1861 } 1864 }
1862 1865
1863 #[test] 1866 #[test]
1864 fn enum_tuple_partial_ellipsis_2_no_diagnostic() { 1867 fn enum_tuple_partial_ellipsis_2_no_diagnostic() {
1865 let content = r" 1868 check_no_diagnostic(
1869 r"
1866 enum Either { 1870 enum Either {
1867 A(bool, bool, bool, bool), 1871 A(bool, bool, bool, bool),
1868 B, 1872 B,
@@ -1876,14 +1880,14 @@ mod tests {
1876 Either::B => {}, 1880 Either::B => {},
1877 } 1881 }
1878 } 1882 }
1879 "; 1883 ",
1880 1884 );
1881 check_no_diagnostic(content);
1882 } 1885 }
1883 1886
1884 #[test] 1887 #[test]
1885 fn enum_tuple_partial_ellipsis_missing_arm() { 1888 fn enum_tuple_partial_ellipsis_missing_arm() {
1886 let content = r" 1889 check_diagnostic(
1890 r"
1887 enum Either { 1891 enum Either {
1888 A(bool, bool, bool, bool), 1892 A(bool, bool, bool, bool),
1889 B, 1893 B,
@@ -1896,14 +1900,14 @@ mod tests {
1896 Either::B => {}, 1900 Either::B => {},
1897 } 1901 }
1898 } 1902 }
1899 "; 1903 ",
1900 1904 );
1901 check_diagnostic(content);
1902 } 1905 }
1903 1906
1904 #[test] 1907 #[test]
1905 fn enum_tuple_partial_ellipsis_2_missing_arm() { 1908 fn enum_tuple_partial_ellipsis_2_missing_arm() {
1906 let content = r" 1909 check_diagnostic(
1910 r"
1907 enum Either { 1911 enum Either {
1908 A(bool, bool, bool, bool), 1912 A(bool, bool, bool, bool),
1909 B, 1913 B,
@@ -1916,14 +1920,14 @@ mod tests {
1916 Either::B => {}, 1920 Either::B => {},
1917 } 1921 }
1918 } 1922 }
1919 "; 1923 ",
1920 1924 );
1921 check_diagnostic(content);
1922 } 1925 }
1923 1926
1924 #[test] 1927 #[test]
1925 fn enum_tuple_ellipsis_no_diagnostic() { 1928 fn enum_tuple_ellipsis_no_diagnostic() {
1926 let content = r" 1929 check_no_diagnostic(
1930 r"
1927 enum Either { 1931 enum Either {
1928 A(bool, bool, bool, bool), 1932 A(bool, bool, bool, bool),
1929 B, 1933 B,
@@ -1934,51 +1938,51 @@ mod tests {
1934 Either::B => {}, 1938 Either::B => {},
1935 } 1939 }
1936 } 1940 }
1937 "; 1941 ",
1938 1942 );
1939 check_no_diagnostic(content);
1940 } 1943 }
1941 1944
1942 #[test] 1945 #[test]
1943 fn enum_never() { 1946 fn enum_never() {
1944 let content = r" 1947 check_no_diagnostic(
1948 r"
1945 enum Never {} 1949 enum Never {}
1946 1950
1947 fn test_fn(never: Never) { 1951 fn test_fn(never: Never) {
1948 match never {} 1952 match never {}
1949 } 1953 }
1950 "; 1954 ",
1951 1955 );
1952 check_no_diagnostic(content);
1953 } 1956 }
1954 1957
1955 #[test] 1958 #[test]
1956 fn type_never() { 1959 fn type_never() {
1957 let content = r" 1960 check_no_diagnostic(
1961 r"
1958 fn test_fn(never: !) { 1962 fn test_fn(never: !) {
1959 match never {} 1963 match never {}
1960 } 1964 }
1961 "; 1965 ",
1962 1966 );
1963 check_no_diagnostic(content);
1964 } 1967 }
1965 1968
1966 #[test] 1969 #[test]
1967 fn enum_never_ref() { 1970 fn enum_never_ref() {
1968 let content = r" 1971 check_no_diagnostic(
1972 r"
1969 enum Never {} 1973 enum Never {}
1970 1974
1971 fn test_fn(never: &Never) { 1975 fn test_fn(never: &Never) {
1972 match never {} 1976 match never {}
1973 } 1977 }
1974 "; 1978 ",
1975 1979 );
1976 check_no_diagnostic(content);
1977 } 1980 }
1978 1981
1979 #[test] 1982 #[test]
1980 fn expr_diverges_missing_arm() { 1983 fn expr_diverges_missing_arm() {
1981 let content = r" 1984 check_no_diagnostic(
1985 r"
1982 enum Either { 1986 enum Either {
1983 A, 1987 A,
1984 B, 1988 B,
@@ -1988,9 +1992,27 @@ mod tests {
1988 Either::A => (), 1992 Either::A => (),
1989 } 1993 }
1990 } 1994 }
1991 "; 1995 ",
1996 );
1997 }
1998
1999 #[test]
2000 fn or_pattern_panic() {
2001 check_no_diagnostic(
2002 r"
2003 pub enum Category {
2004 Infinity,
2005 Zero,
2006 }
1992 2007
1993 check_no_diagnostic(content); 2008 fn panic(a: Category, b: Category) {
2009 match (a, b) {
2010 (Category::Zero | Category::Infinity, _) => {}
2011 (_, Category::Zero | Category::Infinity) => {}
2012 }
2013 }
2014 ",
2015 );
1994 } 2016 }
1995} 2017}
1996 2018
@@ -2010,23 +2032,26 @@ mod false_negatives {
2010 2032
2011 #[test] 2033 #[test]
2012 fn integers() { 2034 fn integers() {
2013 let content = r" 2035 // This is a false negative.
2036 // We don't currently check integer exhaustiveness.
2037 check_no_diagnostic(
2038 r"
2014 fn test_fn() { 2039 fn test_fn() {
2015 match 5 { 2040 match 5 {
2016 10 => (), 2041 10 => (),
2017 11..20 => (), 2042 11..20 => (),
2018 } 2043 }
2019 } 2044 }
2020 "; 2045 ",
2021 2046 );
2022 // This is a false negative.
2023 // We don't currently check integer exhaustiveness.
2024 check_no_diagnostic(content);
2025 } 2047 }
2026 2048
2027 #[test] 2049 #[test]
2028 fn internal_or() { 2050 fn internal_or() {
2029 let content = r" 2051 // This is a false negative.
2052 // We do not currently handle patterns with internal `or`s.
2053 check_no_diagnostic(
2054 r"
2030 fn test_fn() { 2055 fn test_fn() {
2031 enum Either { 2056 enum Either {
2032 A(bool), 2057 A(bool),
@@ -2036,16 +2061,18 @@ mod false_negatives {
2036 Either::A(true | false) => (), 2061 Either::A(true | false) => (),
2037 } 2062 }
2038 } 2063 }
2039 "; 2064 ",
2040 2065 );
2041 // This is a false negative.
2042 // We do not currently handle patterns with internal `or`s.
2043 check_no_diagnostic(content);
2044 } 2066 }
2045 2067
2046 #[test] 2068 #[test]
2047 fn expr_loop_missing_arm() { 2069 fn expr_loop_missing_arm() {
2048 let content = r" 2070 // This is a false negative.
2071 // We currently infer the type of `loop { break Foo::A }` to `!`, which
2072 // causes us to skip the diagnostic since `Either::A` doesn't type check
2073 // with `!`.
2074 check_diagnostic(
2075 r"
2049 enum Either { 2076 enum Either {
2050 A, 2077 A,
2051 B, 2078 B,
@@ -2055,48 +2082,46 @@ mod false_negatives {
2055 Either::A => (), 2082 Either::A => (),
2056 } 2083 }
2057 } 2084 }
2058 "; 2085 ",
2059 2086 );
2060 // This is a false negative.
2061 // We currently infer the type of `loop { break Foo::A }` to `!`, which
2062 // causes us to skip the diagnostic since `Either::A` doesn't type check
2063 // with `!`.
2064 check_diagnostic(content);
2065 } 2087 }
2066 2088
2067 #[test] 2089 #[test]
2068 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { 2090 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
2069 let content = r" 2091 // This is a false negative.
2092 // We don't currently handle tuple patterns with ellipsis.
2093 check_no_diagnostic(
2094 r"
2070 fn test_fn() { 2095 fn test_fn() {
2071 match (false, true, false) { 2096 match (false, true, false) {
2072 (false, ..) => {}, 2097 (false, ..) => {},
2073 } 2098 }
2074 } 2099 }
2075 "; 2100 ",
2076 2101 );
2077 // This is a false negative.
2078 // We don't currently handle tuple patterns with ellipsis.
2079 check_no_diagnostic(content);
2080 } 2102 }
2081 2103
2082 #[test] 2104 #[test]
2083 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { 2105 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
2084 let content = r" 2106 // This is a false negative.
2107 // We don't currently handle tuple patterns with ellipsis.
2108 check_no_diagnostic(
2109 r"
2085 fn test_fn() { 2110 fn test_fn() {
2086 match (false, true, false) { 2111 match (false, true, false) {
2087 (.., false) => {}, 2112 (.., false) => {},
2088 } 2113 }
2089 } 2114 }
2090 "; 2115 ",
2091 2116 );
2092 // This is a false negative.
2093 // We don't currently handle tuple patterns with ellipsis.
2094 check_no_diagnostic(content);
2095 } 2117 }
2096 2118
2097 #[test] 2119 #[test]
2098 fn struct_missing_arm() { 2120 fn struct_missing_arm() {
2099 let content = r" 2121 // This is a false negative.
2122 // We don't currently handle structs.
2123 check_no_diagnostic(
2124 r"
2100 struct Foo { 2125 struct Foo {
2101 a: bool, 2126 a: bool,
2102 } 2127 }
@@ -2105,10 +2130,7 @@ mod false_negatives {
2105 Foo { a: true } => {}, 2130 Foo { a: true } => {},
2106 } 2131 }
2107 } 2132 }
2108 "; 2133 ",
2109 2134 );
2110 // This is a false negative.
2111 // We don't currently handle structs.
2112 check_no_diagnostic(content);
2113 } 2135 }
2114} 2136}
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 93e9aee1d..762aab962 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -27,11 +27,11 @@ impl std::error::Error for SsrError {}
27// 27//
28// Search and replace with named wildcards that will match any expression. 28// Search and replace with named wildcards that will match any expression.
29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. 29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
30// A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. 30// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
31// Available via the command `rust-analyzer.ssr`. 31// Available via the command `rust-analyzer.ssr`.
32// 32//
33// ```rust 33// ```rust
34// // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] 34// // Using structural search replace command [foo($a, $b) ==>> ($a).foo($b)]
35// 35//
36// // BEFORE 36// // BEFORE
37// String::from(foo(y + 5, z)) 37// String::from(foo(y + 5, z))
@@ -79,7 +79,7 @@ struct SsrPattern {
79 vars: Vec<Var>, 79 vars: Vec<Var>,
80} 80}
81 81
82/// represents an `$var` in an SSR query 82/// Represents a `$var` in an SSR query.
83#[derive(Debug, Clone, PartialEq, Eq, Hash)] 83#[derive(Debug, Clone, PartialEq, Eq, Hash)]
84struct Var(String); 84struct Var(String);
85 85
@@ -122,8 +122,7 @@ impl FromStr for SsrQuery {
122 let mut pattern = it.next().expect("something").to_string(); 122 let mut pattern = it.next().expect("something").to_string();
123 123
124 for part in it.map(split_by_var) { 124 for part in it.map(split_by_var) {
125 let (var, var_type, remainder) = part?; 125 let (var, remainder) = part?;
126 is_expr(var_type)?;
127 let new_var = create_name(var, &mut vars)?; 126 let new_var = create_name(var, &mut vars)?;
128 pattern.push_str(new_var); 127 pattern.push_str(new_var);
129 pattern.push_str(remainder); 128 pattern.push_str(remainder);
@@ -166,15 +165,11 @@ fn traverse(node: &SyntaxNode, go: &mut impl FnMut(&SyntaxNode) -> bool) {
166 } 165 }
167} 166}
168 167
169fn split_by_var(s: &str) -> Result<(&str, &str, &str), SsrError> { 168fn split_by_var(s: &str) -> Result<(&str, &str), SsrError> {
170 let end_of_name = s.find(':').ok_or_else(|| SsrError("Use $<name>:expr".into()))?; 169 let end_of_name = s.find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or_else(|| s.len());
171 let name = &s[0..end_of_name]; 170 let name = &s[..end_of_name];
172 is_name(name)?; 171 is_name(name)?;
173 let type_begin = end_of_name + 1; 172 Ok((name, &s[end_of_name..]))
174 let type_length =
175 s[type_begin..].find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or_else(|| s.len());
176 let type_name = &s[type_begin..type_begin + type_length];
177 Ok((name, type_name, &s[type_begin + type_length..]))
178} 173}
179 174
180fn is_name(s: &str) -> Result<(), SsrError> { 175fn is_name(s: &str) -> Result<(), SsrError> {
@@ -185,14 +180,6 @@ fn is_name(s: &str) -> Result<(), SsrError> {
185 } 180 }
186} 181}
187 182
188fn is_expr(s: &str) -> Result<(), SsrError> {
189 if s == "expr" {
190 Ok(())
191 } else {
192 Err(SsrError("Only $<name>:expr is supported".into()))
193 }
194}
195
196fn replace_in_template(template: String, var: &str, new_var: &str) -> String { 183fn replace_in_template(template: String, var: &str, new_var: &str) -> String {
197 let name = format!("${}", var); 184 let name = format!("${}", var);
198 template.replace(&name, new_var) 185 template.replace(&name, new_var)
@@ -450,7 +437,7 @@ mod tests {
450 437
451 #[test] 438 #[test]
452 fn parser_happy_case() { 439 fn parser_happy_case() {
453 let result: SsrQuery = "foo($a:expr, $b:expr) ==>> bar($b, $a)".parse().unwrap(); 440 let result: SsrQuery = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap();
454 assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)"); 441 assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)");
455 assert_eq!(result.pattern.vars.len(), 2); 442 assert_eq!(result.pattern.vars.len(), 2);
456 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a"); 443 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
@@ -477,30 +464,9 @@ mod tests {
477 } 464 }
478 465
479 #[test] 466 #[test]
480 fn parser_no_pattern_type() {
481 assert_eq!(parse_error_text("foo($a) ==>>"), "Parse error: Use $<name>:expr");
482 }
483
484 #[test]
485 fn parser_invalid_name() {
486 assert_eq!(
487 parse_error_text("foo($a+:expr) ==>>"),
488 "Parse error: Name can contain only alphanumerics and _"
489 );
490 }
491
492 #[test]
493 fn parser_invalid_type() {
494 assert_eq!(
495 parse_error_text("foo($a:ident) ==>>"),
496 "Parse error: Only $<name>:expr is supported"
497 );
498 }
499
500 #[test]
501 fn parser_repeated_name() { 467 fn parser_repeated_name() {
502 assert_eq!( 468 assert_eq!(
503 parse_error_text("foo($a:expr, $a:expr) ==>>"), 469 parse_error_text("foo($a, $a) ==>>"),
504 "Parse error: Name `a` repeats more than once" 470 "Parse error: Name `a` repeats more than once"
505 ); 471 );
506 } 472 }
@@ -517,7 +483,7 @@ mod tests {
517 483
518 #[test] 484 #[test]
519 fn parse_match_replace() { 485 fn parse_match_replace() {
520 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap(); 486 let query: SsrQuery = "foo($x) ==>> bar($x)".parse().unwrap();
521 let input = "fn main() { foo(1+2); }"; 487 let input = "fn main() { foo(1+2); }";
522 488
523 let code = SourceFile::parse(input).tree(); 489 let code = SourceFile::parse(input).tree();
@@ -549,7 +515,7 @@ mod tests {
549 #[test] 515 #[test]
550 fn ssr_function_to_method() { 516 fn ssr_function_to_method() {
551 assert_ssr_transform( 517 assert_ssr_transform(
552 "my_function($a:expr, $b:expr) ==>> ($a).my_method($b)", 518 "my_function($a, $b) ==>> ($a).my_method($b)",
553 "loop { my_function( other_func(x, y), z + w) }", 519 "loop { my_function( other_func(x, y), z + w) }",
554 "loop { (other_func(x, y)).my_method(z + w) }", 520 "loop { (other_func(x, y)).my_method(z + w) }",
555 ) 521 )
@@ -558,7 +524,7 @@ mod tests {
558 #[test] 524 #[test]
559 fn ssr_nested_function() { 525 fn ssr_nested_function() {
560 assert_ssr_transform( 526 assert_ssr_transform(
561 "foo($a:expr, $b:expr, $c:expr) ==>> bar($c, baz($a, $b))", 527 "foo($a, $b, $c) ==>> bar($c, baz($a, $b))",
562 "fn main { foo (x + value.method(b), x+y-z, true && false) }", 528 "fn main { foo (x + value.method(b), x+y-z, true && false) }",
563 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }", 529 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }",
564 ) 530 )
@@ -567,7 +533,7 @@ mod tests {
567 #[test] 533 #[test]
568 fn ssr_expected_spacing() { 534 fn ssr_expected_spacing() {
569 assert_ssr_transform( 535 assert_ssr_transform(
570 "foo($x:expr) + bar() ==>> bar($x)", 536 "foo($x) + bar() ==>> bar($x)",
571 "fn main() { foo(5) + bar() }", 537 "fn main() { foo(5) + bar() }",
572 "fn main() { bar(5) }", 538 "fn main() { bar(5) }",
573 ); 539 );
@@ -576,7 +542,7 @@ mod tests {
576 #[test] 542 #[test]
577 fn ssr_with_extra_space() { 543 fn ssr_with_extra_space() {
578 assert_ssr_transform( 544 assert_ssr_transform(
579 "foo($x:expr ) + bar() ==>> bar($x)", 545 "foo($x ) + bar() ==>> bar($x)",
580 "fn main() { foo( 5 ) +bar( ) }", 546 "fn main() { foo( 5 ) +bar( ) }",
581 "fn main() { bar(5) }", 547 "fn main() { bar(5) }",
582 ); 548 );
@@ -585,7 +551,7 @@ mod tests {
585 #[test] 551 #[test]
586 fn ssr_keeps_nested_comment() { 552 fn ssr_keeps_nested_comment() {
587 assert_ssr_transform( 553 assert_ssr_transform(
588 "foo($x:expr) ==>> bar($x)", 554 "foo($x) ==>> bar($x)",
589 "fn main() { foo(other(5 /* using 5 */)) }", 555 "fn main() { foo(other(5 /* using 5 */)) }",
590 "fn main() { bar(other(5 /* using 5 */)) }", 556 "fn main() { bar(other(5 /* using 5 */)) }",
591 ) 557 )
@@ -594,7 +560,7 @@ mod tests {
594 #[test] 560 #[test]
595 fn ssr_keeps_comment() { 561 fn ssr_keeps_comment() {
596 assert_ssr_transform( 562 assert_ssr_transform(
597 "foo($x:expr) ==>> bar($x)", 563 "foo($x) ==>> bar($x)",
598 "fn main() { foo(5 /* using 5 */) }", 564 "fn main() { foo(5 /* using 5 */) }",
599 "fn main() { bar(5)/* using 5 */ }", 565 "fn main() { bar(5)/* using 5 */ }",
600 ) 566 )
@@ -603,7 +569,7 @@ mod tests {
603 #[test] 569 #[test]
604 fn ssr_struct_lit() { 570 fn ssr_struct_lit() {
605 assert_ssr_transform( 571 assert_ssr_transform(
606 "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)", 572 "foo{a: $a, b: $b} ==>> foo::new($a, $b)",
607 "fn main() { foo{b:2, a:1} }", 573 "fn main() { foo{b:2, a:1} }",
608 "fn main() { foo::new(1, 2) }", 574 "fn main() { foo::new(1, 2) }",
609 ) 575 )
@@ -612,7 +578,7 @@ mod tests {
612 #[test] 578 #[test]
613 fn ssr_call_and_method_call() { 579 fn ssr_call_and_method_call() {
614 assert_ssr_transform( 580 assert_ssr_transform(
615 "foo::<'a>($a:expr, $b:expr)) ==>> foo2($a, $b)", 581 "foo::<'a>($a, $b)) ==>> foo2($a, $b)",
616 "fn main() { get().bar.foo::<'a>(1); }", 582 "fn main() { get().bar.foo::<'a>(1); }",
617 "fn main() { foo2(get().bar, 1); }", 583 "fn main() { foo2(get().bar, 1); }",
618 ) 584 )
@@ -621,7 +587,7 @@ mod tests {
621 #[test] 587 #[test]
622 fn ssr_method_call_and_call() { 588 fn ssr_method_call_and_call() {
623 assert_ssr_transform( 589 assert_ssr_transform(
624 "$o:expr.foo::<i32>($a:expr)) ==>> $o.foo2($a)", 590 "$o.foo::<i32>($a)) ==>> $o.foo2($a)",
625 "fn main() { X::foo::<i32>(x, 1); }", 591 "fn main() { X::foo::<i32>(x, 1); }",
626 "fn main() { x.foo2(1); }", 592 "fn main() { x.foo2(1); }",
627 ) 593 )
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 0e7a937a0..0df7427cb 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -9,6 +9,7 @@
9 9
10use std::{ffi::OsString, path::PathBuf}; 10use std::{ffi::OsString, path::PathBuf};
11 11
12use crate::diagnostics::DiagnosticsConfig;
12use lsp_types::ClientCapabilities; 13use lsp_types::ClientCapabilities;
13use ra_flycheck::FlycheckConfig; 14use ra_flycheck::FlycheckConfig;
14use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig}; 15use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
@@ -20,6 +21,7 @@ pub struct Config {
20 pub client_caps: ClientCapsConfig, 21 pub client_caps: ClientCapsConfig,
21 22
22 pub publish_diagnostics: bool, 23 pub publish_diagnostics: bool,
24 pub diagnostics: DiagnosticsConfig,
23 pub lru_capacity: Option<usize>, 25 pub lru_capacity: Option<usize>,
24 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, 26 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
25 pub files: FilesConfig, 27 pub files: FilesConfig,
@@ -136,6 +138,7 @@ impl Default for Config {
136 138
137 with_sysroot: true, 139 with_sysroot: true,
138 publish_diagnostics: true, 140 publish_diagnostics: true,
141 diagnostics: DiagnosticsConfig::default(),
139 lru_capacity: None, 142 lru_capacity: None,
140 proc_macro_srv: None, 143 proc_macro_srv: None,
141 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() }, 144 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
@@ -184,6 +187,8 @@ impl Config {
184 187
185 set(value, "/withSysroot", &mut self.with_sysroot); 188 set(value, "/withSysroot", &mut self.with_sysroot);
186 set(value, "/diagnostics/enable", &mut self.publish_diagnostics); 189 set(value, "/diagnostics/enable", &mut self.publish_diagnostics);
190 set(value, "/diagnostics/warningsAsInfo", &mut self.diagnostics.warnings_as_info);
191 set(value, "/diagnostics/warningsAsHint", &mut self.diagnostics.warnings_as_hint);
187 set(value, "/lruCapacity", &mut self.lru_capacity); 192 set(value, "/lruCapacity", &mut self.lru_capacity);
188 self.files.watcher = match get(value, "/files/watcher") { 193 self.files.watcher = match get(value, "/files/watcher") {
189 Some("client") => FilesWatcher::Client, 194 Some("client") => FilesWatcher::Client,
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 25856c543..290609e7f 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -11,6 +11,12 @@ use crate::lsp_ext;
11pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; 11pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
12 12
13#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
14pub struct DiagnosticsConfig {
15 pub warnings_as_info: Vec<String>,
16 pub warnings_as_hint: Vec<String>,
17}
18
19#[derive(Debug, Default, Clone)]
14pub struct DiagnosticCollection { 20pub struct DiagnosticCollection {
15 pub native: HashMap<FileId, Vec<Diagnostic>>, 21 pub native: HashMap<FileId, Vec<Diagnostic>>,
16 pub check: HashMap<FileId, Vec<Diagnostic>>, 22 pub check: HashMap<FileId, Vec<Diagnostic>>,
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap
new file mode 100644
index 000000000..f0273315e
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap
@@ -0,0 +1,86 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
8 uri: "file:///test/driver/subcommand/repl.rs",
9 range: Range {
10 start: Position {
11 line: 290,
12 character: 8,
13 },
14 end: Position {
15 line: 290,
16 character: 11,
17 },
18 },
19 },
20 diagnostic: Diagnostic {
21 range: Range {
22 start: Position {
23 line: 290,
24 character: 8,
25 },
26 end: Position {
27 line: 290,
28 character: 11,
29 },
30 },
31 severity: Some(
32 Hint,
33 ),
34 code: Some(
35 String(
36 "unused_variables",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
43 related_information: None,
44 tags: Some(
45 [
46 Unnecessary,
47 ],
48 ),
49 },
50 fixes: [
51 CodeAction {
52 title: "consider prefixing with an underscore",
53 id: None,
54 group: None,
55 kind: Some(
56 "quickfix",
57 ),
58 command: None,
59 edit: Some(
60 SnippetWorkspaceEdit {
61 changes: Some(
62 {
63 "file:///test/driver/subcommand/repl.rs": [
64 TextEdit {
65 range: Range {
66 start: Position {
67 line: 290,
68 character: 8,
69 },
70 end: Position {
71 line: 290,
72 character: 11,
73 },
74 },
75 new_text: "_foo",
76 },
77 ],
78 },
79 ),
80 document_changes: None,
81 },
82 ),
83 },
84 ],
85 },
86]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap
new file mode 100644
index 000000000..85fd050fd
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap
@@ -0,0 +1,86 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
8 uri: "file:///test/driver/subcommand/repl.rs",
9 range: Range {
10 start: Position {
11 line: 290,
12 character: 8,
13 },
14 end: Position {
15 line: 290,
16 character: 11,
17 },
18 },
19 },
20 diagnostic: Diagnostic {
21 range: Range {
22 start: Position {
23 line: 290,
24 character: 8,
25 },
26 end: Position {
27 line: 290,
28 character: 11,
29 },
30 },
31 severity: Some(
32 Information,
33 ),
34 code: Some(
35 String(
36 "unused_variables",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
43 related_information: None,
44 tags: Some(
45 [
46 Unnecessary,
47 ],
48 ),
49 },
50 fixes: [
51 CodeAction {
52 title: "consider prefixing with an underscore",
53 id: None,
54 group: None,
55 kind: Some(
56 "quickfix",
57 ),
58 command: None,
59 edit: Some(
60 SnippetWorkspaceEdit {
61 changes: Some(
62 {
63 "file:///test/driver/subcommand/repl.rs": [
64 TextEdit {
65 range: Range {
66 start: Position {
67 line: 290,
68 character: 8,
69 },
70 end: Position {
71 line: 290,
72 character: 11,
73 },
74 },
75 new_text: "_foo",
76 },
77 ],
78 },
79 ),
80 document_changes: None,
81 },
82 ),
83 },
84 ],
85 },
86]
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 24ff9b280..ba74f15f3 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -9,14 +9,24 @@ use lsp_types::{
9use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; 9use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
10use stdx::format_to; 10use stdx::format_to;
11 11
12use super::DiagnosticsConfig;
12use crate::{lsp_ext, to_proto::url_from_abs_path}; 13use crate::{lsp_ext, to_proto::url_from_abs_path};
13 14
14/// Converts a Rust level string to a LSP severity 15/// Determines the LSP severity from a diagnostic
15fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { 16fn map_diagnostic_to_severity(
16 let res = match val { 17 config: &DiagnosticsConfig,
18 val: &ra_flycheck::Diagnostic,
19) -> Option<DiagnosticSeverity> {
20 let res = match val.level {
17 DiagnosticLevel::Ice => DiagnosticSeverity::Error, 21 DiagnosticLevel::Ice => DiagnosticSeverity::Error,
18 DiagnosticLevel::Error => DiagnosticSeverity::Error, 22 DiagnosticLevel::Error => DiagnosticSeverity::Error,
19 DiagnosticLevel::Warning => DiagnosticSeverity::Warning, 23 DiagnosticLevel::Warning => match &val.code {
24 Some(code) if config.warnings_as_hint.contains(&code.code) => DiagnosticSeverity::Hint,
25 Some(code) if config.warnings_as_info.contains(&code.code) => {
26 DiagnosticSeverity::Information
27 }
28 _ => DiagnosticSeverity::Warning,
29 },
20 DiagnosticLevel::Note => DiagnosticSeverity::Information, 30 DiagnosticLevel::Note => DiagnosticSeverity::Information,
21 DiagnosticLevel::Help => DiagnosticSeverity::Hint, 31 DiagnosticLevel::Help => DiagnosticSeverity::Hint,
22 DiagnosticLevel::Unknown => return None, 32 DiagnosticLevel::Unknown => return None,
@@ -172,6 +182,7 @@ pub(crate) struct MappedRustDiagnostic {
172/// 182///
173/// If the diagnostic has no primary span this will return `None` 183/// If the diagnostic has no primary span this will return `None`
174pub(crate) fn map_rust_diagnostic_to_lsp( 184pub(crate) fn map_rust_diagnostic_to_lsp(
185 config: &DiagnosticsConfig,
175 rd: &ra_flycheck::Diagnostic, 186 rd: &ra_flycheck::Diagnostic,
176 workspace_root: &Path, 187 workspace_root: &Path,
177) -> Vec<MappedRustDiagnostic> { 188) -> Vec<MappedRustDiagnostic> {
@@ -180,7 +191,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
180 return Vec::new(); 191 return Vec::new();
181 } 192 }
182 193
183 let severity = map_level_to_severity(rd.level); 194 let severity = map_diagnostic_to_severity(config, rd);
184 195
185 let mut source = String::from("rustc"); 196 let mut source = String::from("rustc");
186 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 197 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -328,7 +339,7 @@ mod tests {
328 ); 339 );
329 340
330 let workspace_root = Path::new("/test/"); 341 let workspace_root = Path::new("/test/");
331 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 342 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
332 insta::assert_debug_snapshot!(diag); 343 insta::assert_debug_snapshot!(diag);
333 } 344 }
334 345
@@ -410,7 +421,183 @@ mod tests {
410 ); 421 );
411 422
412 let workspace_root = Path::new("/test/"); 423 let workspace_root = Path::new("/test/");
413 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 424 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
425 insta::assert_debug_snapshot!(diag);
426 }
427
428 #[test]
429 #[cfg(not(windows))]
430 fn snap_rustc_unused_variable_as_info() {
431 let diag = parse_diagnostic(
432 r##"{
433 "message": "unused variable: `foo`",
434 "code": {
435 "code": "unused_variables",
436 "explanation": null
437 },
438 "level": "warning",
439 "spans": [
440 {
441 "file_name": "driver/subcommand/repl.rs",
442 "byte_start": 9228,
443 "byte_end": 9231,
444 "line_start": 291,
445 "line_end": 291,
446 "column_start": 9,
447 "column_end": 12,
448 "is_primary": true,
449 "text": [
450 {
451 "text": " let foo = 42;",
452 "highlight_start": 9,
453 "highlight_end": 12
454 }
455 ],
456 "label": null,
457 "suggested_replacement": null,
458 "suggestion_applicability": null,
459 "expansion": null
460 }
461 ],
462 "children": [
463 {
464 "message": "#[warn(unused_variables)] on by default",
465 "code": null,
466 "level": "note",
467 "spans": [],
468 "children": [],
469 "rendered": null
470 },
471 {
472 "message": "consider prefixing with an underscore",
473 "code": null,
474 "level": "help",
475 "spans": [
476 {
477 "file_name": "driver/subcommand/repl.rs",
478 "byte_start": 9228,
479 "byte_end": 9231,
480 "line_start": 291,
481 "line_end": 291,
482 "column_start": 9,
483 "column_end": 12,
484 "is_primary": true,
485 "text": [
486 {
487 "text": " let foo = 42;",
488 "highlight_start": 9,
489 "highlight_end": 12
490 }
491 ],
492 "label": null,
493 "suggested_replacement": "_foo",
494 "suggestion_applicability": "MachineApplicable",
495 "expansion": null
496 }
497 ],
498 "children": [],
499 "rendered": null
500 }
501 ],
502 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
503 }"##,
504 );
505
506 let config = DiagnosticsConfig {
507 warnings_as_info: vec!["unused_variables".to_string()],
508 ..DiagnosticsConfig::default()
509 };
510
511 let workspace_root = Path::new("/test/");
512 let diag = map_rust_diagnostic_to_lsp(&config, &diag, workspace_root);
513 insta::assert_debug_snapshot!(diag);
514 }
515
516 #[test]
517 #[cfg(not(windows))]
518 fn snap_rustc_unused_variable_as_hint() {
519 let diag = parse_diagnostic(
520 r##"{
521 "message": "unused variable: `foo`",
522 "code": {
523 "code": "unused_variables",
524 "explanation": null
525 },
526 "level": "warning",
527 "spans": [
528 {
529 "file_name": "driver/subcommand/repl.rs",
530 "byte_start": 9228,
531 "byte_end": 9231,
532 "line_start": 291,
533 "line_end": 291,
534 "column_start": 9,
535 "column_end": 12,
536 "is_primary": true,
537 "text": [
538 {
539 "text": " let foo = 42;",
540 "highlight_start": 9,
541 "highlight_end": 12
542 }
543 ],
544 "label": null,
545 "suggested_replacement": null,
546 "suggestion_applicability": null,
547 "expansion": null
548 }
549 ],
550 "children": [
551 {
552 "message": "#[warn(unused_variables)] on by default",
553 "code": null,
554 "level": "note",
555 "spans": [],
556 "children": [],
557 "rendered": null
558 },
559 {
560 "message": "consider prefixing with an underscore",
561 "code": null,
562 "level": "help",
563 "spans": [
564 {
565 "file_name": "driver/subcommand/repl.rs",
566 "byte_start": 9228,
567 "byte_end": 9231,
568 "line_start": 291,
569 "line_end": 291,
570 "column_start": 9,
571 "column_end": 12,
572 "is_primary": true,
573 "text": [
574 {
575 "text": " let foo = 42;",
576 "highlight_start": 9,
577 "highlight_end": 12
578 }
579 ],
580 "label": null,
581 "suggested_replacement": "_foo",
582 "suggestion_applicability": "MachineApplicable",
583 "expansion": null
584 }
585 ],
586 "children": [],
587 "rendered": null
588 }
589 ],
590 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
591 }"##,
592 );
593
594 let config = DiagnosticsConfig {
595 warnings_as_hint: vec!["unused_variables".to_string()],
596 ..DiagnosticsConfig::default()
597 };
598
599 let workspace_root = Path::new("/test/");
600 let diag = map_rust_diagnostic_to_lsp(&config, &diag, workspace_root);
414 insta::assert_debug_snapshot!(diag); 601 insta::assert_debug_snapshot!(diag);
415 } 602 }
416 603
@@ -534,7 +721,7 @@ mod tests {
534 ); 721 );
535 722
536 let workspace_root = Path::new("/test/"); 723 let workspace_root = Path::new("/test/");
537 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 724 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
538 insta::assert_debug_snapshot!(diag); 725 insta::assert_debug_snapshot!(diag);
539 } 726 }
540 727
@@ -654,7 +841,7 @@ mod tests {
654 ); 841 );
655 842
656 let workspace_root = Path::new("/test/"); 843 let workspace_root = Path::new("/test/");
657 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 844 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
658 insta::assert_debug_snapshot!(diag); 845 insta::assert_debug_snapshot!(diag);
659 } 846 }
660 847
@@ -697,7 +884,7 @@ mod tests {
697 ); 884 );
698 885
699 let workspace_root = Path::new("/test/"); 886 let workspace_root = Path::new("/test/");
700 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 887 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
701 insta::assert_debug_snapshot!(diag); 888 insta::assert_debug_snapshot!(diag);
702 } 889 }
703 890
@@ -968,7 +1155,7 @@ mod tests {
968 ); 1155 );
969 1156
970 let workspace_root = Path::new("/test/"); 1157 let workspace_root = Path::new("/test/");
971 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 1158 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
972 insta::assert_debug_snapshot!(diag); 1159 insta::assert_debug_snapshot!(diag);
973 } 1160 }
974 1161
@@ -1197,7 +1384,7 @@ mod tests {
1197 ); 1384 );
1198 1385
1199 let workspace_root = Path::new("/test/"); 1386 let workspace_root = Path::new("/test/");
1200 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 1387 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
1201 insta::assert_debug_snapshot!(diag); 1388 insta::assert_debug_snapshot!(diag);
1202 } 1389 }
1203 1390
@@ -1330,7 +1517,7 @@ mod tests {
1330 ); 1517 );
1331 1518
1332 let workspace_root = Path::new("/test/"); 1519 let workspace_root = Path::new("/test/");
1333 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 1520 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
1334 insta::assert_debug_snapshot!(diag); 1521 insta::assert_debug_snapshot!(diag);
1335 } 1522 }
1336} 1523}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index cc9bb1726..80cfd3c28 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -669,14 +669,11 @@ fn apply_document_changes(
669 mut line_index: Cow<'_, LineIndex>, 669 mut line_index: Cow<'_, LineIndex>,
670 content_changes: Vec<TextDocumentContentChangeEvent>, 670 content_changes: Vec<TextDocumentContentChangeEvent>,
671) { 671) {
672 // Remove when https://github.com/rust-analyzer/rust-analyzer/issues/4263 is fixed.
673 let backup_text = old_text.clone();
674 let backup_changes = content_changes.clone();
675
676 // The changes we got must be applied sequentially, but can cross lines so we 672 // The changes we got must be applied sequentially, but can cross lines so we
677 // have to keep our line index updated. 673 // have to keep our line index updated.
678 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we 674 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
679 // remember the last valid line in the index and only rebuild it if needed. 675 // remember the last valid line in the index and only rebuild it if needed.
676 // The VFS will normalize the end of lines to `\n`.
680 enum IndexValid { 677 enum IndexValid {
681 All, 678 All,
682 UpToLineExclusive(u64), 679 UpToLineExclusive(u64),
@@ -700,19 +697,7 @@ fn apply_document_changes(
700 } 697 }
701 index_valid = IndexValid::UpToLineExclusive(range.start.line); 698 index_valid = IndexValid::UpToLineExclusive(range.start.line);
702 let range = from_proto::text_range(&line_index, range); 699 let range = from_proto::text_range(&line_index, range);
703 let mut text = old_text.to_owned(); 700 old_text.replace_range(Range::<usize>::from(range), &change.text);
704 match std::panic::catch_unwind(move || {
705 text.replace_range(Range::<usize>::from(range), &change.text);
706 text
707 }) {
708 Ok(t) => *old_text = t,
709 Err(e) => {
710 eprintln!("Bug in incremental text synchronization. Please report the following output on https://github.com/rust-analyzer/rust-analyzer/issues/4263");
711 dbg!(&backup_text);
712 dbg!(&backup_changes);
713 std::panic::resume_unwind(e);
714 }
715 }
716 } 701 }
717 None => { 702 None => {
718 *old_text = change.text; 703 *old_text = change.text;
@@ -734,6 +719,7 @@ fn on_check_task(
734 719
735 CheckTask::AddDiagnostic { workspace_root, diagnostic } => { 720 CheckTask::AddDiagnostic { workspace_root, diagnostic } => {
736 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 721 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
722 &global_state.config.diagnostics,
737 &diagnostic, 723 &diagnostic,
738 &workspace_root, 724 &workspace_root,
739 ); 725 );
diff --git a/editors/code/package.json b/editors/code/package.json
index e2027970d..3acc375f6 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -525,6 +525,24 @@
525 "markdownDescription": "Internal config for debugging, disables loading of sysroot crates", 525 "markdownDescription": "Internal config for debugging, disables loading of sysroot crates",
526 "type": "boolean", 526 "type": "boolean",
527 "default": true 527 "default": true
528 },
529 "rust-analyzer.diagnostics.warningsAsInfo": {
530 "type": "array",
531 "uniqueItems": true,
532 "items": {
533 "type": "string"
534 },
535 "description": "List of warnings that should be displayed with info severity.\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the problems panel.",
536 "default": []
537 },
538 "rust-analyzer.diagnostics.warningsAsHint": {
539 "type": "array",
540 "uniqueItems": true,
541 "items": {
542 "type": "string"
543 },
544 "description": "List of warnings warnings that should be displayed with hint severity.\nThe warnings will be indicated by faded text or three dots in code and will not show up in te problems panel.",
545 "default": []
528 } 546 }
529 } 547 }
530 }, 548 },