[CFE] Fix dangling reference to part imported as library

Before this CL:
If a library (say lib.dart) imports a file (say part.dart) that's
actually a part (of some other library (say main.dart)) an error is
given, but the "fake library" shortly representing the part is still
"linked" into the libraries dependency list.
This means that the component returned from a compilation is different
from the combination of libraries one can get to if walking all
LibraryDependency's. This also means that if we serialize the component
we can't load it back in (https://github.com/dart-lang/sdk/issues/46706).
It furthermore means that we have two different entities with the same
fileUri and importUri.

This CL rewrites a such import of a part to instead be an import of the
"parent", i.e. if a library lib.dart imports part.dart that's a part of
main.dart an error is issued, and the import is rewritten to be an import
of main.dart instead. The same thing happens to exports.

This alleviates the problems (i.e. no crashing if trying to load the dill,
no referenec to a otherwise non-existing library etc), and has the added
benefit of probably being what the user wants: Anything defined in
part.dart (that isn't private) is still in scope from lib.dart.

Change-Id: I17aca9db4edbf25b23d3ef301cf65589eeeb1635
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/213344
Reviewed-by: Johnni Winther <johnniwinther@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
This commit is contained in:
Jens Johansen
2021-09-15 06:20:03 +00:00
committed by commit-bot@chromium.org
parent fd2d014112
commit 17503caa05
14 changed files with 403 additions and 4 deletions
@@ -1067,6 +1067,17 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
export.exporter.addProblem(
messagePartExport, export.charOffset, "export".length, null,
context: context);
if (library != null) {
// Recovery: Export the main library instead.
export.exported = library;
SourceLibraryBuilder exporter =
export.exporter as SourceLibraryBuilder;
for (Export export2 in exporter.exports) {
if (export2.exported == this) {
export2.exported = library;
}
}
}
}
}
}
@@ -1228,6 +1239,12 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
part.scope.becomePartOf(scope);
// TODO(ahe): Include metadata from part?
// Recovery: Take on all exporters (i.e. if a library has erroneously
// exported the part it has (in validatePart) been recovered to import the
// main library (this) instead --- to make it complete (and set up scopes
// correctly) the exporters in this has to be updated too).
exporters.addAll(part.exporters);
nativeMethods.addAll(part.nativeMethods);
boundlessTypeVariables.addAll(part.boundlessTypeVariables);
// Check that the targets are different. This is not normally a problem
@@ -1245,6 +1262,11 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
_implicitlyTypedFields!.addAll(partImplicitlyTypedFields);
}
}
if (library != part.library) {
// Mark the part library as synthetic as it's not an actual library
// (anymore).
part.library.isSynthetic = true;
}
return true;
} else {
assert(part is DillLibraryBuilder);
@@ -1273,15 +1295,21 @@ class SourceLibraryBuilder extends LibraryBuilderImpl {
void addImportsToScope() {
bool explicitCoreImport = this == loader.coreLibrary;
for (Import import in imports) {
if (import.imported == loader.coreLibrary) {
explicitCoreImport = true;
}
if (import.imported?.isPart ?? false) {
addProblem(
templatePartOfInLibrary.withArguments(import.imported!.fileUri),
import.charOffset,
noLength,
fileUri);
if (import.imported?.partOfLibrary != null) {
// Recovery: Rewrite to import the "part owner" library.
// Note that the part will not have a partOfLibrary if it claims to be
// a part, but isn't mentioned as a part by the (would-be) "parent".
import.imported = import.imported?.partOfLibrary;
}
}
if (import.imported == loader.coreLibrary) {
explicitCoreImport = true;
}
import.finalizeImports(this);
}
@@ -0,0 +1,38 @@
# Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE.md file.
type: newworld
worlds:
- entry: main.dart
errors: true
sources:
main.dart: |
import 'lib.dart';
part 'part.dart';
void main() {}
part.dart: |
part of "main.dart";
void partMethod() {}
lib.dart: |
import 'part.dart';
import 'part.dart';
import 'part.dart';
void libMethod() {
partMethod();
}
expectedLibraryCount: 2
- entry: main.dart
invalidate:
- lib.dart
sources:
main.dart: |
import 'lib.dart';
part 'part.dart';
void main() {}
part.dart: |
part of "main.dart";
void partMethod() {}
lib.dart: |
void libMethod() {}
expectedLibraryCount: 2
@@ -0,0 +1,37 @@
main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
//
// Problems in library:
//
// org-dartlang-test:///lib.dart:1:1: Error: Can't import 'org-dartlang-test:///part.dart', because it has a 'part of' declaration.
// Try removing the 'part of' declaration, or using 'org-dartlang-test:///part.dart' as a part.
// import 'part.dart';
// ^
//
// org-dartlang-test:///lib.dart:2:1: Error: Can't import 'org-dartlang-test:///part.dart', because it has a 'part of' declaration.
// Try removing the 'part of' declaration, or using 'org-dartlang-test:///part.dart' as a part.
// import 'part.dart';
// ^
//
// org-dartlang-test:///lib.dart:3:1: Error: Can't import 'org-dartlang-test:///part.dart', because it has a 'part of' declaration.
// Try removing the 'part of' declaration, or using 'org-dartlang-test:///part.dart' as a part.
// import 'part.dart';
// ^
//
import "org-dartlang-test:///main.dart";
import "org-dartlang-test:///main.dart";
import "org-dartlang-test:///main.dart";
static method libMethod() → void {
main::partMethod();
}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart";
part part.dart;
static method main() → void {}
static method /* from org-dartlang-test:///part.dart */ partMethod() → void {}
}
@@ -0,0 +1,13 @@
main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
static method libMethod() → void {}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart";
part part.dart;
static method main() → void {}
static method /* from org-dartlang-test:///part.dart */ partMethod() → void {}
}
@@ -0,0 +1,41 @@
# Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE.md file.
type: newworld
worlds:
- entry: main.dart
errors: true
sources:
main.dart: |
import 'lib.dart' as lib;
part 'part.dart';
void main() {
lib.partMethod();
}
part.dart: |
part of "main.dart";
void partMethod() {}
lib.dart: |
export 'part.dart';
export 'part.dart';
export 'part.dart';
void libMethod() {}
expectedLibraryCount: 2
- entry: main.dart
invalidate:
- lib.dart
sources:
main.dart: |
import 'lib.dart' as lib;
part 'part.dart';
void main() {
lib.partMethod();
}
part.dart: |
part of "main.dart";
void partMethod() {}
lib.dart: |
void libMethod() {}
void partMethod() {}
expectedLibraryCount: 2
@@ -0,0 +1,39 @@
main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
//
// Problems in library:
//
// org-dartlang-test:///lib.dart:1:1: Error: Can't export this file because it contains a 'part of' declaration.
// export 'part.dart';
// ^^^^^^
// org-dartlang-test:///part.dart: Context: This is the file that can't be exported.
//
// org-dartlang-test:///lib.dart:2:1: Error: Can't export this file because it contains a 'part of' declaration.
// export 'part.dart';
// ^^^^^^
// org-dartlang-test:///part.dart: Context: This is the file that can't be exported.
//
// org-dartlang-test:///lib.dart:3:1: Error: Can't export this file because it contains a 'part of' declaration.
// export 'part.dart';
// ^^^^^^
// org-dartlang-test:///part.dart: Context: This is the file that can't be exported.
//
additionalExports = (main::main,
main::partMethod)
export "org-dartlang-test:///main.dart";
export "org-dartlang-test:///main.dart";
export "org-dartlang-test:///main.dart";
static method libMethod() → void {}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart" as lib;
part part.dart;
static method main() → void {
main::partMethod();
}
static method /* from org-dartlang-test:///part.dart */ partMethod() → void {}
}
@@ -0,0 +1,16 @@
main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
static method libMethod() → void {}
static method partMethod() → void {}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart" as lib;
part part.dart;
static method main() → void {
lib::partMethod();
}
static method /* from org-dartlang-test:///part.dart */ partMethod() → void {}
}
@@ -0,0 +1,32 @@
# Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE.md file.
type: newworld
worlds:
- entry: main.dart
errors: true
sources:
main.dart: |
import 'lib.dart' as lib;
void main() {}
part.dart: |
// Notice that main.dart didn't claim this as a part!
part of "main.dart";
void partMethod() {}
lib.dart: |
import 'part.dart';
import 'part.dart';
import 'part.dart';
void libMethod() {}
expectedLibraryCount: 3
- entry: main.dart
invalidate:
- lib.dart
sources:
main.dart: |
import 'lib.dart' as lib;
void main() {}
lib.dart: |
void libMethod() {}
expectedLibraryCount: 2
@@ -0,0 +1,45 @@
main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
//
// Problems in library:
//
// org-dartlang-test:///lib.dart:1:1: Error: Can't import 'org-dartlang-test:///part.dart', because it has a 'part of' declaration.
// Try removing the 'part of' declaration, or using 'org-dartlang-test:///part.dart' as a part.
// import 'part.dart';
// ^
//
// org-dartlang-test:///lib.dart:2:1: Error: Can't import 'org-dartlang-test:///part.dart', because it has a 'part of' declaration.
// Try removing the 'part of' declaration, or using 'org-dartlang-test:///part.dart' as a part.
// import 'part.dart';
// ^
//
// org-dartlang-test:///lib.dart:3:1: Error: Can't import 'org-dartlang-test:///part.dart', because it has a 'part of' declaration.
// Try removing the 'part of' declaration, or using 'org-dartlang-test:///part.dart' as a part.
// import 'part.dart';
// ^
//
import "org-dartlang-test:///part.dart";
import "org-dartlang-test:///part.dart";
import "org-dartlang-test:///part.dart";
static method libMethod() → void {}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart" as lib;
static method main() → void {}
}
library from "org-dartlang-test:///part.dart" as part {
//
// Problems in library:
//
// org-dartlang-test:///part.dart:1:1: Error: This part doesn't have a containing library.
// Try removing the 'part of' declaration.
// // Notice that main.dart didn't claim this as a part!
// ^
//
static method partMethod() → void {}
}
@@ -0,0 +1,11 @@
main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
static method libMethod() → void {}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart" as lib;
static method main() → void {}
}
@@ -0,0 +1,37 @@
# Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
# for details. All rights reserved. Use of this source code is governed by a
# BSD-style license that can be found in the LICENSE.md file.
type: newworld
worlds:
- entry: main.dart
errors: true
sources:
main.dart: |
import 'lib.dart' as lib;
void main() {
lib.partMethod();
}
part.dart: |
// Notice that main.dart didn't claim this as a part!
part of "main.dart";
void partMethod() {}
lib.dart: |
export 'part.dart';
export 'part.dart';
export 'part.dart';
void libMethod() {}
expectedLibraryCount: 3
- entry: main.dart
invalidate:
- lib.dart
sources:
main.dart: |
import 'lib.dart' as lib;
void main() {
lib.partMethod();
}
lib.dart: |
void libMethod() {}
void partMethod() {}
expectedLibraryCount: 2
@@ -0,0 +1,48 @@
main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
//
// Problems in library:
//
// org-dartlang-test:///lib.dart:1:1: Error: Can't export this file because it contains a 'part of' declaration.
// export 'part.dart';
// ^^^^^^
// org-dartlang-test:///part.dart: Context: This is the file that can't be exported.
//
// org-dartlang-test:///lib.dart:2:1: Error: Can't export this file because it contains a 'part of' declaration.
// export 'part.dart';
// ^^^^^^
// org-dartlang-test:///part.dart: Context: This is the file that can't be exported.
//
// org-dartlang-test:///lib.dart:3:1: Error: Can't export this file because it contains a 'part of' declaration.
// export 'part.dart';
// ^^^^^^
// org-dartlang-test:///part.dart: Context: This is the file that can't be exported.
//
additionalExports = (part::partMethod)
export "org-dartlang-test:///part.dart";
export "org-dartlang-test:///part.dart";
export "org-dartlang-test:///part.dart";
static method libMethod() → void {}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart" as lib;
static method main() → void {
part::partMethod();
}
}
library from "org-dartlang-test:///part.dart" as part {
//
// Problems in library:
//
// org-dartlang-test:///part.dart:1:1: Error: This part doesn't have a containing library.
// Try removing the 'part of' declaration.
// // Notice that main.dart didn't claim this as a part!
// ^
//
static method partMethod() → void {}
}
@@ -0,0 +1,14 @@
main = main::main;
library from "org-dartlang-test:///lib.dart" as lib {
static method libMethod() → void {}
static method partMethod() → void {}
}
library from "org-dartlang-test:///main.dart" as main {
import "org-dartlang-test:///lib.dart" as lib;
static method main() → void {
lib::partMethod();
}
}
+1 -1
View File
@@ -1126,7 +1126,7 @@ class BinaryPrinter implements Visitor<void>, BinarySink {
writeOffset(node.fileOffset);
writeByte(node.flags);
writeAnnotationList(node.annotations);
writeLibraryReference(node.targetLibrary, allowNull: true);
writeLibraryReference(node.targetLibrary, allowNull: false);
writeStringReference(node.name ?? '');
writeNodeList(node.combinators);
}