about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2023-03-19 15:33:55 +0530
committerGitHub <noreply@github.com>2023-03-19 15:33:55 +0530
commite9c25b4ad5a78e7a125efec5bc050abe1eff293e (patch)
tree855c7fe74f2fa461ac372a448f4e7ee4c0e90ed1
parent993b775849bdc75eac5cd3b7d459bcbaa9514ed8 (diff)
parent97915ab943001b5a74df4b3e22e7c0a469433fb9 (diff)
downloadrust-e9c25b4ad5a78e7a125efec5bc050abe1eff293e.tar.gz
rust-e9c25b4ad5a78e7a125efec5bc050abe1eff293e.zip
Rollup merge of #105793 - lukas-code:circular-deps, r=Mark-Simulacrum
Add note for mismatched types because of circular dependencies

If you have crate A with a dependency on crate B, and crate B with a dev-dependency on A, then you might see "mismatched types" errors on types that seem to be equal. This PR adds a note that explains that the types are different, because crate B is compiled twice, one time with `cfg(test)` and one time without.

I haven't found a good way to create circular dependencies in UI tests, so I abused the incremental tests instead. As a bonus, incremental tests support "cpass" now.

related to https://github.com/rust-lang/rust/issues/22750
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs21
-rw-r--r--src/tools/compiletest/src/runtest.rs50
-rw-r--r--tests/incremental/auxiliary/circular-dependencies-aux.rs10
-rw-r--r--tests/incremental/circular-dependencies.rs37
4 files changed, 96 insertions, 22 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index de1a2e6a577..fd16363a1db 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -615,9 +615,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
 
         let report_path_match = |err: &mut Diagnostic, did1: DefId, did2: DefId| {
-            // Only external crates, if either is from a local
-            // module we could have false positives
-            if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
+            // Only report definitions from different crates. If both definitions
+            // are from a local module we could have false positives, e.g.
+            // let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
+            if did1.krate != did2.krate {
                 let abs_path =
                     |def_id| AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]);
 
@@ -629,10 +630,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 };
                 if same_path().unwrap_or(false) {
                     let crate_name = self.tcx.crate_name(did1.krate);
-                    err.note(&format!(
-                        "perhaps two different versions of crate `{}` are being used?",
-                        crate_name
-                    ));
+                    let msg = if did1.is_local() || did2.is_local() {
+                        format!(
+                            "the crate `{crate_name}` is compiled multiple times, possibly with different configurations"
+                        )
+                    } else {
+                        format!(
+                            "perhaps two different versions of crate `{crate_name}` are being used?"
+                        )
+                    };
+                    err.note(msg);
                 }
             }
         };
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index a127875b55d..4d9010d3c4b 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -278,13 +278,15 @@ impl<'test> TestCx<'test> {
             Incremental => {
                 let revision =
                     self.revision.expect("incremental tests require a list of revisions");
-                if revision.starts_with("rpass") || revision.starts_with("rfail") {
+                if revision.starts_with("cpass")
+                    || revision.starts_with("rpass")
+                    || revision.starts_with("rfail")
+                {
                     true
                 } else if revision.starts_with("cfail") {
-                    // FIXME: would be nice if incremental revs could start with "cpass"
                     pm.is_some()
                 } else {
-                    panic!("revision name must begin with rpass, rfail, or cfail");
+                    panic!("revision name must begin with cpass, rpass, rfail, or cfail");
                 }
             }
             mode => panic!("unimplemented for mode {:?}", mode),
@@ -384,6 +386,20 @@ impl<'test> TestCx<'test> {
         }
     }
 
+    fn run_cpass_test(&self) {
+        let emit_metadata = self.should_emit_metadata(self.pass_mode());
+        let proc_res = self.compile_test(WillExecute::No, emit_metadata);
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        // FIXME(#41968): Move this check to tidy?
+        if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
+            self.fatal("compile-pass tests with expected warnings should be moved to ui/");
+        }
+    }
+
     fn run_rpass_test(&self) {
         let emit_metadata = self.should_emit_metadata(self.pass_mode());
         let should_run = self.run_if_enabled();
@@ -393,17 +409,15 @@ impl<'test> TestCx<'test> {
             self.fatal_proc_rec("compilation failed!", &proc_res);
         }
 
+        // FIXME(#41968): Move this check to tidy?
+        if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
+            self.fatal("run-pass tests with expected warnings should be moved to ui/");
+        }
+
         if let WillExecute::Disabled = should_run {
             return;
         }
 
-        // FIXME(#41968): Move this check to tidy?
-        let expected_errors = errors::load_errors(&self.testpaths.file, self.revision);
-        assert!(
-            expected_errors.is_empty(),
-            "run-pass tests with expected warnings should be moved to ui/"
-        );
-
         let proc_res = self.exec_compiled_test();
         if !proc_res.status.success() {
             self.fatal_proc_rec("test run failed!", &proc_res);
@@ -2913,10 +2927,11 @@ impl<'test> TestCx<'test> {
     fn run_incremental_test(&self) {
         // Basic plan for a test incremental/foo/bar.rs:
         // - load list of revisions rpass1, cfail2, rpass3
-        //   - each should begin with `rpass`, `cfail`, or `rfail`
-        //   - if `rpass`, expect compile and execution to succeed
+        //   - each should begin with `cpass`, `rpass`, `cfail`, or `rfail`
+        //   - if `cpass`, expect compilation to succeed, don't execute
+        //   - if `rpass`, expect compilation and execution to succeed
         //   - if `cfail`, expect compilation to fail
-        //   - if `rfail`, expect execution to fail
+        //   - if `rfail`, expect compilation to succeed and execution to fail
         // - create a directory build/foo/bar.incremental
         // - compile foo/bar.rs with -C incremental=.../foo/bar.incremental and -C rpass1
         //   - because name of revision starts with "rpass", expect success
@@ -2940,7 +2955,12 @@ impl<'test> TestCx<'test> {
             print!("revision={:?} props={:#?}", revision, self.props);
         }
 
-        if revision.starts_with("rpass") {
+        if revision.starts_with("cpass") {
+            if self.props.should_ice {
+                self.fatal("can only use should-ice in cfail tests");
+            }
+            self.run_cpass_test();
+        } else if revision.starts_with("rpass") {
             if self.props.should_ice {
                 self.fatal("can only use should-ice in cfail tests");
             }
@@ -2953,7 +2973,7 @@ impl<'test> TestCx<'test> {
         } else if revision.starts_with("cfail") {
             self.run_cfail_test();
         } else {
-            self.fatal("revision name must begin with rpass, rfail, or cfail");
+            self.fatal("revision name must begin with cpass, rpass, rfail, or cfail");
         }
     }
 
diff --git a/tests/incremental/auxiliary/circular-dependencies-aux.rs b/tests/incremental/auxiliary/circular-dependencies-aux.rs
new file mode 100644
index 00000000000..0e74eb1b2f2
--- /dev/null
+++ b/tests/incremental/auxiliary/circular-dependencies-aux.rs
@@ -0,0 +1,10 @@
+// edition: 2021
+// compile-flags: --crate-type lib --extern circular_dependencies={{build-base}}/circular-dependencies/libcircular_dependencies.rmeta --emit dep-info,metadata
+
+use circular_dependencies::Foo;
+
+pub fn consume_foo(_: Foo) {}
+
+pub fn produce_foo() -> Foo {
+    Foo
+}
diff --git a/tests/incremental/circular-dependencies.rs b/tests/incremental/circular-dependencies.rs
new file mode 100644
index 00000000000..10673066a9d
--- /dev/null
+++ b/tests/incremental/circular-dependencies.rs
@@ -0,0 +1,37 @@
+// ignore-tidy-linelength
+// revisions: cpass1 cfail2
+// edition: 2021
+// [cpass1] compile-flags: --crate-type lib --emit dep-info,metadata
+// [cfail2] aux-build: circular-dependencies-aux.rs
+// [cfail2] compile-flags: --test --extern aux={{build-base}}/circular-dependencies/auxiliary/libcircular_dependencies_aux.rmeta -L dependency={{build-base}}/circular-dependencies
+
+pub struct Foo;
+//[cfail2]~^ NOTE `Foo` is defined in the current crate
+//[cfail2]~| NOTE `Foo` is defined in the current crate
+//[cfail2]~| NOTE `circular_dependencies::Foo` is defined in crate `circular_dependencies`
+//[cfail2]~| NOTE `circular_dependencies::Foo` is defined in crate `circular_dependencies`
+
+pub fn consume_foo(_: Foo) {}
+//[cfail2]~^ NOTE function defined here
+
+pub fn produce_foo() -> Foo {
+    Foo
+}
+
+#[test]
+fn test() {
+    aux::consume_foo(produce_foo());
+    //[cfail2]~^ ERROR mismatched types [E0308]
+    //[cfail2]~| NOTE expected `circular_dependencies::Foo`, found `Foo`
+    //[cfail2]~| NOTE arguments to this function are incorrect
+    //[cfail2]~| NOTE `Foo` and `circular_dependencies::Foo` have similar names, but are actually distinct types
+    //[cfail2]~| NOTE the crate `circular_dependencies` is compiled multiple times, possibly with different configurations
+    //[cfail2]~| NOTE function defined here
+
+    consume_foo(aux::produce_foo());
+    //[cfail2]~^ ERROR mismatched types [E0308]
+    //[cfail2]~| NOTE expected `Foo`, found `circular_dependencies::Foo`
+    //[cfail2]~| NOTE arguments to this function are incorrect
+    //[cfail2]~| NOTE `circular_dependencies::Foo` and `Foo` have similar names, but are actually distinct types
+    //[cfail2]~| NOTE the crate `circular_dependencies` is compiled multiple times, possibly with different configurations
+}