Reject deprecated language features.

Or rather, provide the following options which do:

  --reject-deprecated-language-features
    Reject deprecated language features.  Without this option, the
    compiler will accept language features that are no longer valid
    according to The Dart Programming Language Specification, version
    0.12, M1.

  --report-sdk-use-of-deprecated-language-features
    Report use of deprecated features in Dart platform libraries.
    Without this option, the compiler will silently accept use of
    deprecated language features from these libraries.  The option
    --reject-deprecated-language-features controls if these usages are
    reported as errors or warnings.

Review URL: https://codereview.chromium.org//11416004

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@15007 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
ahe@google.com 2012-11-16 11:41:05 +00:00
parent 42b89c9f77
commit f5c804380f
15 changed files with 132 additions and 41 deletions

View file

@ -35,9 +35,14 @@ class Compiler extends leg.Compiler {
emitJavaScript: !hasOption(options, '--output-type=dart'),
disallowUnsafeEval: hasOption(options, '--disallow-unsafe-eval'),
analyzeAll: hasOption(options, '--analyze-all'),
rejectDeprecatedFeatures:
hasOption(options, '--reject-deprecated-language-features'),
checkDeprecationInSdk:
hasOption(options,
'--report-sdk-use-of-deprecated-language-features'),
strips: getStrips(options),
enableConcreteTypeInference:
hasOption(options, '--enable-concrete-type-inference')) {
hasOption(options, '--enable-concrete-type-inference')) {
if (!libraryRoot.path.endsWith("/")) {
throw new ArgumentError("libraryRoot must end with a /");
}

View file

@ -113,6 +113,8 @@ abstract class Compiler implements DiagnosticListener {
final bool enableConcreteTypeInference;
final bool analyzeAll;
final bool enableNativeLiveTypeAnalysis;
final bool rejectDeprecatedFeatures;
final bool checkDeprecationInSdk;
bool disableInlining = false;
@ -225,6 +227,8 @@ abstract class Compiler implements DiagnosticListener {
bool generateSourceMap: true,
bool disallowUnsafeEval: false,
this.analyzeAll: false,
this.rejectDeprecatedFeatures: false,
this.checkDeprecationInSdk: false,
List<String> strips: const []})
: libraries = new Map<String, LibraryElement>(),
progress = new Stopwatch() {
@ -341,12 +345,10 @@ abstract class Compiler implements DiagnosticListener {
try {
runCompiler(uri);
} on CompilerCancelledException catch (exception) {
log(exception.toString());
log('compilation failed');
log('Error: $exception');
return false;
}
tracer.close();
log('compilation succeeded');
return true;
}
@ -399,12 +401,11 @@ abstract class Compiler implements DiagnosticListener {
LibraryElement scanBuiltinLibrary(String filename);
void initializeSpecialClasses() {
bool coreLibValid = true;
final List missingClasses = [];
ClassElement lookupSpecialClass(SourceString name) {
ClassElement result = coreLibrary.find(name);
if (result == null) {
log('core library class $name missing');
coreLibValid = false;
missingClasses.add(name.slowToString());
}
return result;
}
@ -424,8 +425,8 @@ abstract class Compiler implements DiagnosticListener {
dynamicClass = lookupSpecialClass(const SourceString('Dynamic_'));
nullClass = lookupSpecialClass(const SourceString('Null'));
types = new Types(this, dynamicClass);
if (!coreLibValid) {
cancel('core library does not contain required classes');
if (!missingClasses.isEmpty) {
cancel('core library does not contain required classes: $missingClasses');
}
}
@ -767,6 +768,21 @@ abstract class Compiler implements DiagnosticListener {
reportDiagnostic(span, "$message", kind);
}
void onDeprecatedFeature(Spannable span, String feature) {
if (currentElement == null)
throw new SpannableAssertionFailure(span, feature);
if (!checkDeprecationInSdk &&
currentElement.getLibrary().isPlatformLibrary) {
return;
}
var kind = rejectDeprecatedFeatures
? api.Diagnostic.ERROR : api.Diagnostic.WARNING;
var message = rejectDeprecatedFeatures
? MessageKind.DEPRECATED_FEATURE_ERROR.error([feature])
: MessageKind.DEPRECATED_FEATURE_WARNING.error([feature]);
reportMessage(spanFromSpannable(span), message, kind);
}
void reportDiagnostic(SourceSpan span, String message, api.Diagnostic kind);
SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) {

View file

@ -165,6 +165,10 @@ void compile(List<String> argv) {
new OptionHandler('--disallow-unsafe-eval', passThrough),
new OptionHandler('--analyze-all', passThrough),
new OptionHandler('--enable-native-live-type-analysis', passThrough),
new OptionHandler('--reject-deprecated-language-features', passThrough),
new OptionHandler('--report-sdk-use-of-deprecated-language-features',
passThrough),
// The following two options must come last.
new OptionHandler('-.*', (String argument) {
helpAndFail('Error: Unknown option "$argument".');
@ -418,7 +422,22 @@ be removed in a future version:
Disables dynamic generation of code in the generated output. This is
necessary to satisfy CSP restrictions (see http://www.w3.org/TR/CSP/).
This flag is not continuously tested. Please report breakages and we
will fix them as soon as possible.''');
will fix them as soon as possible.
--reject-deprecated-language-features
Reject deprecated language features. Without this option, the
compiler will accept language features that are no longer valid
according to The Dart Programming Language Specification, version
0.12, M1.
--report-sdk-use-of-deprecated-language-features
Report use of deprecated features in Dart platform libraries.
Without this option, the compiler will silently accept use of
deprecated language features from these libraries. The option
--reject-deprecated-language-features controls if these usages are
reported as errors or warnings.
'''.trim());
}
void helpAndExit(bool verbose) {

View file

@ -19,4 +19,6 @@ abstract class DiagnosticListener {
SourceSpan spanFromNode(Node node, [Uri uri]);
void reportMessage(SourceSpan span, Diagnostic message, api.Diagnostic kind);
void onDeprecatedFeature(Spannable span, String feature);
}

View file

@ -2,7 +2,7 @@
// 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 org_dartlang_js_helper;
library _js_helper;
import 'dart:collection';

View file

@ -192,13 +192,16 @@ class NativeEnqueuerBase implements NativeEnqueuer {
}
bool isNativeMethod(Element element) {
if (!element.getLibrary().canUseNative) return false;
// Native method?
Node node = element.parseNode(compiler);
if (node is! FunctionExpression) return false;
node = node.body;
Token token = node.getBeginToken();
if (token.stringValue == 'native') return true;
return false;
return compiler.withCurrentElement(element, () {
Node node = element.parseNode(compiler);
if (node is! FunctionExpression) return false;
node = node.body;
Token token = node.getBeginToken();
if (identical(token.stringValue, 'native')) return true;
return false;
});
}
void registerFieldLoad(Element field) {

View file

@ -1048,12 +1048,14 @@ class TypeResolver {
if (send != null) {
typeName = send.selector;
}
if (identical(typeName.source.stringValue, 'void')) {
String stringValue = typeName.source.stringValue;
if (identical(stringValue, 'void')) {
return compiler.types.voidType.element;
} else if (
// TODO(aprelev@gmail.com): Remove deprecated Dynamic keyword support.
identical(typeName.source.stringValue, 'Dynamic')
|| identical(typeName.source.stringValue, 'dynamic')) {
} else if (identical(stringValue, 'Dynamic')) {
// TODO(aprelev@gmail.com): Remove deprecated Dynamic keyword support.
compiler.onDeprecatedFeature(typeName, 'Dynamic');
return compiler.dynamicClass;
} else if (identical(stringValue, 'dynamic')) {
return compiler.dynamicClass;
} else if (send != null) {
Element e = scope.lookup(send.receiver.asIdentifier().source);
@ -2926,13 +2928,17 @@ class SignatureResolver extends CommonResolverVisitor<Element> {
}
} else {
if (element.isGetter()) {
if (!element.getLibrary().isPlatformLibrary) {
// TODO(ahe): Remove the isPlatformLibrary check.
if (!identical(formalParameters.getEndToken().next.stringValue, 'native')) {
// TODO(ahe): Remove the check for native keyword.
if (!identical(formalParameters.getEndToken().next.stringValue,
// TODO(ahe): Remove the check for native keyword.
'native')) {
if (compiler.rejectDeprecatedFeatures &&
// TODO(ahe): Remove isPlatformLibrary check.
!element.getLibrary().isPlatformLibrary) {
compiler.reportMessage(compiler.spanFromNode(formalParameters),
MessageKind.EXTRA_FORMALS.error([]),
Diagnostic.WARNING);
Diagnostic.ERROR);
} else {
compiler.onDeprecatedFeature(formalParameters, 'getter parameters');
}
}
}

View file

@ -605,6 +605,8 @@ class ParserError {
toString() => reason;
}
typedef int IdGenerator();
/**
* A parser event listener designed to work with [PartialParser]. It
* builds elements representing the top-level declarations found in
@ -612,7 +614,7 @@ class ParserError {
* [compilationUnitElement].
*/
class ElementListener extends Listener {
Function idGenerator;
final IdGenerator idGenerator;
final DiagnosticListener listener;
final CompilationUnitElement compilationUnitElement;
final StringValidator stringValidator;
@ -623,10 +625,9 @@ class ElementListener extends Listener {
Link<MetadataAnnotation> metadata = const Link<MetadataAnnotation>();
ElementListener(DiagnosticListener listener,
CompilationUnitElement this.compilationUnitElement,
int idGenerator())
this.compilationUnitElement,
this.idGenerator)
: this.listener = listener,
this.idGenerator = idGenerator,
stringValidator = new StringValidator(listener),
interpolationScope = const Link<StringQuoting>();
@ -791,6 +792,7 @@ class ElementListener extends Listener {
pushElement(new PartialClassElement(
name.source, interfaceKeyword, endToken, compilationUnitElement, id));
rejectBuiltInIdentifier(name);
listener.onDeprecatedFeature(interfaceKeyword, 'interface declarations');
}
void endFunctionTypeAlias(Token typedefKeyword, Token endToken) {
@ -1005,8 +1007,9 @@ class ElementListener extends Listener {
metadata = metadata.prepend(annotation);
}
// TODO(ahe): Remove this method.
void addScriptTag(ScriptTag tag) {
// TODO(ahe): Remove this method.
listener.onDeprecatedFeature(tag, '# tags');
addLibraryTag(tag.toLibraryTag());
}
@ -1338,6 +1341,9 @@ class NodeListener extends ElementListener {
NodeList arguments = new NodeList.singleton(argument);
pushNode(new Send(receiver, new Operator(token), arguments));
}
if (identical(tokenString, '===') || identical(tokenString, '!==')) {
listener.onDeprecatedFeature(token, tokenString);
}
}
void beginCascade(Token token) {

View file

@ -21,7 +21,7 @@ class SpannableAssertionFailure {
final String message;
SpannableAssertionFailure(this.node, this.message);
String toString() => 'compiler crashed.';
String toString() => 'Compiler crashed: $message.';
}
/// Writes the characters of [iterator] on [buffer]. The characters

View file

@ -243,9 +243,20 @@ class MessageKind {
static const MISSING_FORMALS = const MessageKind(
"Error: Formal parameters are missing.");
// TODO(ahe): Change the message below when it becomes an error.
static const EXTRA_FORMALS = const MessageKind(
"Warning: Formal parameters will not be allowed here in M1.");
"Error: Formal parameters are not allowed here.");
// TODO(ahe): This message is hard to localize. This is acceptable,
// as it will be removed when we ship Dart version 1.0.
static const DEPRECATED_FEATURE_WARNING = const MessageKind(
"Warning: deprecated language feature, #{1}, "
"will be removed in a future Dart milestone.");
// TODO(ahe): This message is hard to localize. This is acceptable,
// as it will be removed when we ship Dart version 1.0.
static const DEPRECATED_FEATURE_ERROR = const MessageKind(
"Error: #{1} are not legal "
"due to option --reject-deprecated-language-features.");
static const CONSTRUCTOR_WITH_RETURN_TYPE = const MessageKind(
"Error: cannot have return type for constructor.");

View file

@ -21,11 +21,13 @@ import "mock_compiler.dart";
import "parser_helper.dart";
String compile(String code, {String entry: 'main',
String coreSource: DEFAULT_CORELIB,
bool enableTypeAssertions: false,
bool minify: false,
bool analyzeAll: false}) {
MockCompiler compiler =
new MockCompiler(enableTypeAssertions: enableTypeAssertions,
coreSource: coreSource,
enableMinification: minify);
compiler.parseScript(code);
lego.Element element = compiler.mainApp.find(buildSourceString(entry));

View file

@ -66,8 +66,7 @@ const String DEFAULT_CORELIB = r'''
class Object {}
class Type {}
class Function {}
interface List default ListImplementation { List([length]);}
class ListImplementation { factory List([length]) => null; }
class List {}
abstract class Map {}
class Closure {}
class Null {}

View file

@ -604,8 +604,8 @@ testClassHierarchy() {
compiler.errors[0].message.kind);
compiler = new MockCompiler();
compiler.parseScript("""interface A extends B {}
interface B extends A {}
compiler.parseScript("""abstract class A extends B {}
abstract class B extends A {}
class C implements A {}
main() { return new C(); }""");
mainElement = compiler.mainApp.find(MAIN);

View file

@ -124,7 +124,7 @@ testOperators() {
analyze("{ bool b = (1 ${op} false); }", MessageKind.NOT_ASSIGNABLE);
analyze("{ bool b = (true ${op} 2); }", MessageKind.NOT_ASSIGNABLE);
}
for (final op in ['>', '<', '<=', '>=', '==', '!=', '===', '!==']) {
for (final op in ['>', '<', '<=', '>=', '==', '!=']) {
analyze("{ bool b = 1 ${op} 2; }");
analyze("{ int i = 1 ${op} 2; }", MessageKind.NOT_ASSIGNABLE);
analyze("{ int i; bool b = (i = true) ${op} 2; }",

View file

@ -198,8 +198,30 @@ main(value) {
REMOVED,
];
// TODO(ahe): It would probably be better if this test used the real
// core library sources, as its purpose is to detect failure to
// optimize fixed-sized arrays.
const String DEFAULT_CORELIB_WITH_LIST_INTERFACE = r'''
print(var obj) {}
abstract class num {}
abstract class int extends num { }
abstract class double extends num { }
class bool {}
class String {}
class Object {}
class Type {}
class Function {}
interface List default ListImplementation { List([length]);}
class ListImplementation { factory List([length]) => null; }
abstract class Map {}
class Closure {}
class Null {}
class Dynamic_ {}
bool identical(Object a, Object b) {}''';
expect(String code, int kind) {
String generated = compile(code);
String generated =
compile(code, coreSource: DEFAULT_CORELIB_WITH_LIST_INTERFACE);
switch (kind) {
case REMOVED:
Expect.isTrue(!generated.contains('ioore'));