Make Analyzer, VM and dart2js accept URI strings as part-of library identifier.

R=brianwilkerson@google.com, floitsch@google.com, hausner@google.com, johnniwinther@google.com, sigmund@google.com

Review-Url: https://codereview.chromium.org/2640853005 .
This commit is contained in:
Lasse R.H. Nielsen 2017-03-01 12:15:11 +01:00
parent bb72d5e8a0
commit ef097edad6
19 changed files with 173 additions and 22 deletions

View file

@ -474,7 +474,8 @@ class LibraryAnalyzer {
}
}
if (hasPartDirective && libraryNameNode == null) {
if (hasPartDirective && libraryNameNode == null &&
!_context.analysisOptions.enableUriInPartOf) {
libraryErrorReporter.reportErrorForOffset(
ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART, 0, 0);
}

View file

@ -1422,7 +1422,7 @@ class AnalysisOptionsImpl implements AnalysisOptions {
List<String> _excludePatterns;
@override
bool enableUriInPartOf = false;
bool enableUriInPartOf = true;
@override
bool generateImplicitErrors = true;
@ -1686,7 +1686,7 @@ class AnalysisOptionsImpl implements AnalysisOptions {
enableStrictCallChecks = false;
enableSuperMixins = false;
enableTiming = false;
enableUriInPartOf = false;
enableUriInPartOf = true;
_errorProcessors = null;
_excludePatterns = null;
finerGrainedInvalidation = false;

View file

@ -208,7 +208,7 @@ class Parser {
* A flag indicating whether the parser is to allow URI's in part-of
* directives.
*/
bool _enableUriInPartOf = false;
bool _enableUriInPartOf = true;
/**
* A flag indicating whether parser is to parse function bodies.

View file

@ -1368,7 +1368,7 @@ class _SummarizeAstVisitor extends RecursiveAstVisitor {
@override
void visitPartOfDirective(PartOfDirective node) {
isCoreLibrary = node.libraryName.name == 'dart.core';
isCoreLibrary = node.libraryName?.name == 'dart.core';
isPartOf = true;
}

View file

@ -1626,7 +1626,8 @@ class BuildLibraryElementTask extends SourceBasedAnalysisTask {
}
}
}
if (hasPartDirective && libraryNameNode == null) {
if (hasPartDirective && libraryNameNode == null &&
!context.analysisOptions.enableUriInPartOf) {
errors.add(new AnalysisError(librarySource, 0, 0,
ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART));
}

View file

@ -3419,7 +3419,8 @@ class Foo {
void test_nonIdentifierLibraryName_partOf() {
CompilationUnit unit = parseCompilationUnit(
"part of 'lib';", [ParserErrorCode.NON_IDENTIFIER_LIBRARY_NAME]);
"part of 3;", [ParserErrorCode.MISSING_NAME_IN_PART_OF_DIRECTIVE,
ParserErrorCode.UNEXPECTED_TOKEN]);
expect(unit, isNotNull);
}

View file

@ -766,8 +766,13 @@ library lib;
Source partSource = addSource("/part.dart", "part of 'lib';");
context.parseCompilationUnit(librarySource);
List<AnalysisError> errors = context.computeErrors(partSource);
expect(errors, isNotNull);
expect(errors.length > 0, isTrue);
if (context.analysisOptions.enableUriInPartOf) {
// TODO(28522)
// Should report that 'lib' isn't the correct URI.
} else {
expect(errors, isNotNull);
expect(errors.length > 0, isTrue);
}
}
void test_computeErrors_dart_some() {

View file

@ -414,9 +414,14 @@ part of lib;
AnalysisResult libResult = await driver.getResult(lib);
List<AnalysisError> errors = libResult.errors;
expect(errors, hasLength(1));
expect(errors[0].errorCode,
ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART);
if (libResult.unit.element.context.analysisOptions.enableUriInPartOf) {
// TODO(28522): Should cause an error for wrong library name.
expect(errors, hasLength(0));
} else {
expect(errors, hasLength(1));
expect(errors[0].errorCode,
ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART);
}
}
test_analyze_resolveDirectives_error_partOfDifferentLibrary_byName() async {

View file

@ -870,8 +870,37 @@ part of my_lib;
part of my_lib;
'''
});
_assertErrorsWithCodes(
[ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART]);
if (context.analysisOptions.enableUriInPartOf) {
// TODO(28522)
// Should report that names are wrong.
} else {
_assertErrorsWithCodes(
[ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART]);
AnalysisError error = errorListener.errors[0];
}
}
test_perform_error_missingLibraryDirectiveWithPart_noCommon() {
_performBuildTask({
'/lib.dart': '''
part 'partA.dart';
part 'partB.dart';
''',
'/partA.dart': '''
part of libA;
''',
'/partB.dart': '''
part of libB;
'''
});
if (context.analysisOptions.enableUriInPartOf) {
// TODO(28522)
// Should report that names are wrong.
} else {
_assertErrorsWithCodes(
[ResolverErrorCode.MISSING_LIBRARY_DIRECTIVE_WITH_PART]);
AnalysisError error = errorListener.errors[0];
}
}
test_perform_error_partDoesNotExist() {

View file

@ -292,6 +292,7 @@ enum MessageKind {
LIBRARY_NOT_FOUND,
LIBRARY_NOT_SUPPORTED,
LIBRARY_TAG_MUST_BE_FIRST,
LIBRARY_URI_MISMATCH,
MAIN_HAS_PART_OF,
MAIN_NOT_A_FUNCTION,
MAIN_WITH_EXTRA_PARAMETER,
@ -2073,6 +2074,26 @@ main() {}
""",
'part.dart': """
part of lib.bar;
"""
}
]),
MessageKind.LIBRARY_URI_MISMATCH: const MessageTemplate(
MessageKind.LIBRARY_URI_MISMATCH,
"Expected URI of library '#{libraryUri}'.",
howToFix: "Try changing the directive to 'part of "
"\"#{libraryUri}\";'.",
examples: const [
const {
'main.dart': """
library lib.foo;
part 'part.dart';
main() {}
""",
'part.dart': """
part of "main.dart";
"""
}
]),

View file

@ -767,6 +767,20 @@ class CompilationUnitElementX extends ElementX
}
partTag = tag;
LibraryName libraryTag = library.libraryTag;
Expression libraryReference = tag.name;
if (libraryReference is LiteralString) {
// Name is a URI. Resolve and compare to library's URI.
String content = libraryReference.dartString.slowToString();
Uri uri = this.script.readableUri.resolve(content);
Uri expectedUri = library.canonicalUri;
if (uri != expectedUri) {
// Consider finding a relative URI reference for the error message.
reporter.reportWarningMessage(tag.name,
MessageKind.LIBRARY_URI_MISMATCH, {'libraryUri': expectedUri});
}
return;
}
String actualName = tag.name.toString();
if (libraryTag != null) {
String expectedName = libraryTag.name.toString();

View file

@ -344,8 +344,13 @@ class Parser {
assert(optional('part', token));
assert(optional('of', token.next));
Token partKeyword = token;
token = parseQualified(token.next.next, IdentifierContext.partName,
IdentifierContext.partNameContinuation);
token = token.next.next;
if (token.isIdentifier()) {
token = parseQualified(token, IdentifierContext.partName,
IdentifierContext.partNameContinuation);
} else {
token = parseLiteralStringOrRecoverExpression(token);
}
Token semicolon = token;
token = expect(';', token);
listener.endPartOf(partKeyword, semicolon);

View file

@ -6386,12 +6386,16 @@ void Parser::ParsePartHeader() {
ReportError("'part of' expected");
}
ConsumeToken();
// The VM is not required to check that the library name matches the
// name of the current library, so we ignore it.
ExpectIdentifier("library name expected");
while (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("malformed library name");
// The VM is not required to check that the library name or URI matches the
// name or URI of the current library, so we ignore them.
if (CurrentToken() == Token::kSTRING) {
ParseStringLiteral(false);
} else {
ExpectIdentifier("library name expected");
while (CurrentToken() == Token::kPERIOD) {
ConsumeToken();
ExpectIdentifier("malformed library name");
}
}
ExpectSemicolon();
}

View file

@ -0,0 +1,9 @@
// Copyright (c) 2017, 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 file.
part of "part_of_uri2_test.dart";
// Refer to declaration in library and other part.
const bar1 = "part1";
const baz1 = "$foo${bar2}1";

View file

@ -0,0 +1,9 @@
// Copyright (c) 2017, 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 file.
part of part_of_uri2;
// Refer to declaration in library and other part.
const bar2 = "part2";
const baz2 = "$foo${bar1}2";

View file

@ -0,0 +1,15 @@
// Copyright (c) 2017, 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 file.
library part_of_uri2;
part "part_of_uri2_part.dart"; // declares bar1, baz1, uses URI.
part "part_of_uri2_part2.dart"; // declares bar2, baz2, uses id.
const foo = 'foo';
const qux = "$baz1$baz2";
main() {
if (!identical(qux, "foopart21foopart12")) throw "Fail: $qux";
}

View file

@ -0,0 +1,9 @@
// Copyright (c) 2017, 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 file.
part of "part_of_uri_test.dart";
// Refer to declaration in library and other part.
const bar1 = "part1";
const baz1 = "$foo${bar2}1";

View file

@ -0,0 +1,9 @@
// Copyright (c) 2017, 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 file.
part of "part_of_uri_test.dart";
// Refer to declaration in library and other part.
const bar2 = "part2";
const baz2 = "$foo${bar1}2";

View file

@ -0,0 +1,14 @@
// Copyright (c) 2017, 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 file.
// No library declaration
part "part_of_uri_part.dart"; // declares bar1, baz1
part "part_of_uri_part2.dart"; // declares bar2, baz2
const foo = 'foo';
const qux = "$baz1$baz2";
main() {
if (!identical(qux, "foopart21foopart12")) throw "Fail: $qux";
}