Add put/getSourceErrorsInLibrary to IncrementalCache.

R=brianwilkerson@google.com, paulberry@google.com
BUG=

Review URL: https://codereview.chromium.org/2042883002 .
This commit is contained in:
Konstantin Shcheglov 2016-06-06 13:27:25 -07:00
parent 49dec47c97
commit ed8154a53c
6 changed files with 532 additions and 1 deletions

View file

@ -11,6 +11,7 @@ import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/source/error_processor.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/type.dart';
import 'package:analyzer/src/dart/scanner/scanner.dart' show ScannerErrorCode;
import 'package:analyzer/src/generated/generated/shared_messages.dart'
@ -21,7 +22,6 @@ import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/task/model.dart';
import 'package:analyzer/task/model.dart';
import 'package:source_span/source_span.dart';
import 'package:analyzer/src/dart/element/element.dart';
/**
* The descriptor used to associate error processors with analysis contexts in
@ -122,6 +122,12 @@ class AnalysisError {
}
}
/**
* Initialize a newly created analysis error with given values.
*/
AnalysisError.forValues(this.source, this.offset, this.length, this.errorCode,
this._message, this._correction);
/**
* Return the template used to create the correction to be displayed for this
* error, or `null` if there is no correction information for this error. The
@ -3022,6 +3028,11 @@ abstract class ErrorCode {
ScannerErrorCode.UNTERMINATED_STRING_LITERAL,
];
/**
* The lazy initialized map from [uniqueName] to the [ErrorCode] instance.
*/
static HashMap<String, ErrorCode> _uniqueNameToCodeMap;
/**
* An empty list of error codes.
*/
@ -3070,6 +3081,20 @@ abstract class ErrorCode {
@override
String toString() => uniqueName;
/**
* Return the [ErrorCode] with the given [uniqueName], or `null` if not
* found.
*/
static ErrorCode byUniqueName(String uniqueName) {
if (_uniqueNameToCodeMap == null) {
_uniqueNameToCodeMap = new HashMap<String, ErrorCode>();
for (ErrorCode errorCode in values) {
_uniqueNameToCodeMap[errorCode.uniqueName] = errorCode;
}
}
return _uniqueNameToCodeMap[uniqueName];
}
}
/**

View file

@ -141,6 +141,198 @@ class _UnlinkedParamKindReader extends fb.Reader<idl.UnlinkedParamKind> {
}
}
class CacheAnalysisErrorBuilder extends Object with _CacheAnalysisErrorMixin implements idl.CacheAnalysisError {
bool _finished = false;
String _correction;
String _errorCodeUniqueName;
int _length;
String _message;
int _offset;
@override
String get correction => _correction ??= '';
/**
* The correction to be displayed for this error, or `null` if there is no
* correction information for this error. The correction should indicate how
* the user can fix the error.
*/
void set correction(String _value) {
assert(!_finished);
_correction = _value;
}
@override
String get errorCodeUniqueName => _errorCodeUniqueName ??= '';
/**
* The unique name of the error code.
*/
void set errorCodeUniqueName(String _value) {
assert(!_finished);
_errorCodeUniqueName = _value;
}
@override
int get length => _length ??= 0;
/**
* Length of the error range.
*/
void set length(int _value) {
assert(!_finished);
assert(_value == null || _value >= 0);
_length = _value;
}
@override
String get message => _message ??= '';
/**
* The message to be displayed for this error. The message should indicate
* what is wrong and why it is wrong.
*/
void set message(String _value) {
assert(!_finished);
_message = _value;
}
@override
int get offset => _offset ??= 0;
/**
* Offset of the error range relative to the beginning of the file.
*/
void set offset(int _value) {
assert(!_finished);
assert(_value == null || _value >= 0);
_offset = _value;
}
CacheAnalysisErrorBuilder({String correction, String errorCodeUniqueName, int length, String message, int offset})
: _correction = correction,
_errorCodeUniqueName = errorCodeUniqueName,
_length = length,
_message = message,
_offset = offset;
/**
* Flush [informative] data recursively.
*/
void flushInformative() {
}
fb.Offset finish(fb.Builder fbBuilder) {
assert(!_finished);
_finished = true;
fb.Offset offset_correction;
fb.Offset offset_errorCodeUniqueName;
fb.Offset offset_message;
if (_correction != null) {
offset_correction = fbBuilder.writeString(_correction);
}
if (_errorCodeUniqueName != null) {
offset_errorCodeUniqueName = fbBuilder.writeString(_errorCodeUniqueName);
}
if (_message != null) {
offset_message = fbBuilder.writeString(_message);
}
fbBuilder.startTable();
if (offset_correction != null) {
fbBuilder.addOffset(4, offset_correction);
}
if (offset_errorCodeUniqueName != null) {
fbBuilder.addOffset(0, offset_errorCodeUniqueName);
}
if (_length != null && _length != 0) {
fbBuilder.addUint32(2, _length);
}
if (offset_message != null) {
fbBuilder.addOffset(3, offset_message);
}
if (_offset != null && _offset != 0) {
fbBuilder.addUint32(1, _offset);
}
return fbBuilder.endTable();
}
}
class _CacheAnalysisErrorReader extends fb.TableReader<_CacheAnalysisErrorImpl> {
const _CacheAnalysisErrorReader();
@override
_CacheAnalysisErrorImpl createObject(fb.BufferContext bc, int offset) => new _CacheAnalysisErrorImpl(bc, offset);
}
class _CacheAnalysisErrorImpl extends Object with _CacheAnalysisErrorMixin implements idl.CacheAnalysisError {
final fb.BufferContext _bc;
final int _bcOffset;
_CacheAnalysisErrorImpl(this._bc, this._bcOffset);
String _correction;
String _errorCodeUniqueName;
int _length;
String _message;
int _offset;
@override
String get correction {
_correction ??= const fb.StringReader().vTableGet(_bc, _bcOffset, 4, '');
return _correction;
}
@override
String get errorCodeUniqueName {
_errorCodeUniqueName ??= const fb.StringReader().vTableGet(_bc, _bcOffset, 0, '');
return _errorCodeUniqueName;
}
@override
int get length {
_length ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 2, 0);
return _length;
}
@override
String get message {
_message ??= const fb.StringReader().vTableGet(_bc, _bcOffset, 3, '');
return _message;
}
@override
int get offset {
_offset ??= const fb.Uint32Reader().vTableGet(_bc, _bcOffset, 1, 0);
return _offset;
}
}
abstract class _CacheAnalysisErrorMixin implements idl.CacheAnalysisError {
@override
Map<String, Object> toJson() {
Map<String, Object> _result = <String, Object>{};
if (correction != '') _result["correction"] = correction;
if (errorCodeUniqueName != '') _result["errorCodeUniqueName"] = errorCodeUniqueName;
if (length != 0) _result["length"] = length;
if (message != '') _result["message"] = message;
if (offset != 0) _result["offset"] = offset;
return _result;
}
@override
Map<String, Object> toMap() => {
"correction": correction,
"errorCodeUniqueName": errorCodeUniqueName,
"length": length,
"message": message,
"offset": offset,
};
@override
String toString() => convert.JSON.encode(toJson());
}
class CacheSourceContentBuilder extends Object with _CacheSourceContentMixin implements idl.CacheSourceContent {
bool _finished = false;
@ -316,6 +508,96 @@ abstract class _CacheSourceContentMixin implements idl.CacheSourceContent {
String toString() => convert.JSON.encode(toJson());
}
class CacheSourceErrorsInLibraryBuilder extends Object with _CacheSourceErrorsInLibraryMixin implements idl.CacheSourceErrorsInLibrary {
bool _finished = false;
List<CacheAnalysisErrorBuilder> _errors;
@override
List<CacheAnalysisErrorBuilder> get errors => _errors ??= <CacheAnalysisErrorBuilder>[];
/**
* The list of errors in the source in the library.
*/
void set errors(List<CacheAnalysisErrorBuilder> _value) {
assert(!_finished);
_errors = _value;
}
CacheSourceErrorsInLibraryBuilder({List<CacheAnalysisErrorBuilder> errors})
: _errors = errors;
/**
* Flush [informative] data recursively.
*/
void flushInformative() {
_errors?.forEach((b) => b.flushInformative());
}
List<int> toBuffer() {
fb.Builder fbBuilder = new fb.Builder();
return fbBuilder.finish(finish(fbBuilder), "CSEL");
}
fb.Offset finish(fb.Builder fbBuilder) {
assert(!_finished);
_finished = true;
fb.Offset offset_errors;
if (!(_errors == null || _errors.isEmpty)) {
offset_errors = fbBuilder.writeList(_errors.map((b) => b.finish(fbBuilder)).toList());
}
fbBuilder.startTable();
if (offset_errors != null) {
fbBuilder.addOffset(0, offset_errors);
}
return fbBuilder.endTable();
}
}
idl.CacheSourceErrorsInLibrary readCacheSourceErrorsInLibrary(List<int> buffer) {
fb.BufferContext rootRef = new fb.BufferContext.fromBytes(buffer);
return const _CacheSourceErrorsInLibraryReader().read(rootRef, 0);
}
class _CacheSourceErrorsInLibraryReader extends fb.TableReader<_CacheSourceErrorsInLibraryImpl> {
const _CacheSourceErrorsInLibraryReader();
@override
_CacheSourceErrorsInLibraryImpl createObject(fb.BufferContext bc, int offset) => new _CacheSourceErrorsInLibraryImpl(bc, offset);
}
class _CacheSourceErrorsInLibraryImpl extends Object with _CacheSourceErrorsInLibraryMixin implements idl.CacheSourceErrorsInLibrary {
final fb.BufferContext _bc;
final int _bcOffset;
_CacheSourceErrorsInLibraryImpl(this._bc, this._bcOffset);
List<idl.CacheAnalysisError> _errors;
@override
List<idl.CacheAnalysisError> get errors {
_errors ??= const fb.ListReader<idl.CacheAnalysisError>(const _CacheAnalysisErrorReader()).vTableGet(_bc, _bcOffset, 0, const <idl.CacheAnalysisError>[]);
return _errors;
}
}
abstract class _CacheSourceErrorsInLibraryMixin implements idl.CacheSourceErrorsInLibrary {
@override
Map<String, Object> toJson() {
Map<String, Object> _result = <String, Object>{};
if (errors.isNotEmpty) _result["errors"] = errors.map((_value) => _value.toJson()).toList();
return _result;
}
@override
Map<String, Object> toMap() => {
"errors": errors,
};
@override
String toString() => convert.JSON.encode(toJson());
}
class CodeRangeBuilder extends Object with _CodeRangeMixin implements idl.CodeRange {
bool _finished = false;

View file

@ -790,6 +790,39 @@ enum UnlinkedParamKind : byte {
named
}
/**
* Information about an analysis error in a source.
*/
table CacheAnalysisError {
/**
* The correction to be displayed for this error, or `null` if there is no
* correction information for this error. The correction should indicate how
* the user can fix the error.
*/
correction:string (id: 4);
/**
* The unique name of the error code.
*/
errorCodeUniqueName:string (id: 0);
/**
* Length of the error range.
*/
length:uint (id: 2);
/**
* The message to be displayed for this error. The message should indicate
* what is wrong and why it is wrong.
*/
message:string (id: 3);
/**
* Offset of the error range relative to the beginning of the file.
*/
offset:uint (id: 1);
}
/**
* Information about a source that depends only on its content.
*/
@ -818,6 +851,17 @@ table CacheSourceContent {
partUris:[string] (id: 3);
}
/**
* Errors of a source in a library, which depends on the import/export closure
* of the containing library and the source.
*/
table CacheSourceErrorsInLibrary {
/**
* The list of errors in the source in the library.
*/
errors:[CacheAnalysisError] (id: 0);
}
/**
* Information about an element code range.
*/

View file

@ -57,6 +57,44 @@ import 'format.dart' as generated;
*/
const informative = null;
/**
* Information about an analysis error in a source.
*/
abstract class CacheAnalysisError extends base.SummaryClass {
/**
* The correction to be displayed for this error, or `null` if there is no
* correction information for this error. The correction should indicate how
* the user can fix the error.
*/
@Id(4)
String get correction;
/**
* The unique name of the error code.
*/
@Id(0)
String get errorCodeUniqueName;
/**
* Length of the error range.
*/
@Id(2)
int get length;
/**
* The message to be displayed for this error. The message should indicate
* what is wrong and why it is wrong.
*/
@Id(3)
String get message;
/**
* Offset of the error range relative to the beginning of the file.
*/
@Id(1)
int get offset;
}
/**
* Information about a source that depends only on its content.
*/
@ -93,6 +131,21 @@ abstract class CacheSourceContent extends base.SummaryClass {
List<String> get partUris;
}
/**
* Errors of a source in a library, which depends on the import/export closure
* of the containing library and the source.
*/
@TopLevel('CSEL')
abstract class CacheSourceErrorsInLibrary extends base.SummaryClass {
factory CacheSourceErrorsInLibrary.fromBuffer(List<int> buffer) =>
generated.readCacheSourceErrorsInLibrary(buffer);
/**
* The list of errors in the source in the library.
*/
@Id(0)
List<CacheAnalysisError> get errors;
}
/**
* Kind of a source in the cache.
*/

View file

@ -8,6 +8,7 @@ import 'dart:core' hide Resource;
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary/format.dart';
import 'package:analyzer/src/summary/idl.dart';
@ -158,6 +159,28 @@ class IncrementalCache {
}
}
/**
* Return cached errors in the given [source] in the context of the given
* [librarySource], or `null` if the cache does not have this information.
*/
List<AnalysisError> getSourceErrorsInLibrary(
Source librarySource, Source source) {
try {
String key = _getSourceErrorsKey(librarySource, source);
List<int> bytes = storage.get(key);
if (bytes == null) {
return null;
}
CacheSourceErrorsInLibrary errorsObject =
new CacheSourceErrorsInLibrary.fromBuffer(bytes);
return errorsObject.errors
.map((e) => _convertErrorFromCached(source, e))
.toList();
} catch (e) {
return null;
}
}
/**
* Return the kind of the given [source], or `null` if unknown.
*/
@ -188,6 +211,19 @@ class IncrementalCache {
storage.put(key, bytes);
}
/**
* Associate the given [errors] with the [source] in the [librarySource].
*/
void putSourceErrorsInLibrary(
Source librarySource, Source source, List<AnalysisError> errors) {
CacheSourceErrorsInLibraryBuilder builder =
new CacheSourceErrorsInLibraryBuilder(
errors: errors.map(_convertErrorToCached).toList());
String key = _getSourceErrorsKey(librarySource, source);
List<int> bytes = builder.toBuffer();
storage.put(key, bytes);
}
/**
* Fill the whole source closure of the library with the given
* [librarySource]. It includes defining units and parts of the library and
@ -235,6 +271,33 @@ class IncrementalCache {
return digest.bytes;
}
/**
* Return the [AnalysisError] for the given [cachedError].
*/
AnalysisError _convertErrorFromCached(
Source source, CacheAnalysisError cachedError) {
ErrorCode errorCode = _getErrorCode(cachedError);
return new AnalysisError.forValues(
source,
cachedError.offset,
cachedError.length,
errorCode,
cachedError.message,
cachedError.correction);
}
/**
* Return the [CacheAnalysisError] for the given [error].
*/
CacheAnalysisError _convertErrorToCached(AnalysisError error) {
return new CacheAnalysisErrorBuilder(
errorCodeUniqueName: error.errorCode.uniqueName,
offset: error.offset,
length: error.length,
message: error.message,
correction: error.correction);
}
/**
* Get the content based information about the given [source], maybe `null`
* if the information is not in the cache.
@ -262,6 +325,18 @@ class IncrementalCache {
return '$hashStr.content';
}
/**
* Return the [ErrorCode] of the given [error], throws if not found.
*/
ErrorCode _getErrorCode(CacheAnalysisError error) {
String uniqueName = error.errorCodeUniqueName;
ErrorCode errorCode = ErrorCode.byUniqueName(uniqueName);
if (errorCode != null) {
return errorCode;
}
throw new StateError('Unable to find ErrorCode: $uniqueName');
}
/**
* Get the bundle for the given key.
*/
@ -327,6 +402,18 @@ class IncrementalCache {
});
}
/**
* Return the key for errors in the [source] in the [librarySource].
*/
String _getSourceErrorsKey(Source librarySource, Source source) {
List<int> hash = _computeSaltedMD5OfBytes((ByteConversionSink byteSink) {
byteSink.add(_getLibraryClosureHash(librarySource));
byteSink.add(_getSourceContentHash(source));
});
String hashStr = hex.encode(hash);
return '$hashStr.errorsInLibrary';
}
/**
* Return a source representing the URI that results from resolving the given
* (possibly relative) [containedUri] against the URI associated with the

View file

@ -3,10 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/generated/error.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/summary/incremental_cache.dart';
import 'package:unittest/unittest.dart';
import '../../generated/test_support.dart';
import '../../reflective_tests.dart';
import '../abstract_single_unit.dart';
@ -119,6 +121,44 @@ main() {}
expect(bundles, isNotNull);
}
void test_getSourceErrorsInLibrary_library() {
verifyNoTestUnitErrors = false;
putTestLibrary(r'''
main() {
int unusedVar = 42;
}
''');
List<AnalysisError> computedErrors = context.computeErrors(testSource);
cache.putSourceErrorsInLibrary(testSource, testSource, computedErrors);
List<AnalysisError> readErrors =
cache.getSourceErrorsInLibrary(testSource, testSource);
new GatheringErrorListener()
..addAll(readErrors)
..assertErrors(computedErrors);
}
void test_getSourceErrorsInLibrary_part() {
verifyNoTestUnitErrors = false;
Source partSource = addSource(
'/foo.dart',
r'''
main() {
int unusedVar = 42;
}
''');
putTestLibrary(r'''
library lib;
part 'foo.dart';
''');
List<AnalysisError> computedErrors = context.computeErrors(partSource);
cache.putSourceErrorsInLibrary(testSource, partSource, computedErrors);
List<AnalysisError> readErrors =
cache.getSourceErrorsInLibrary(testSource, partSource);
new GatheringErrorListener()
..addAll(readErrors)
..assertErrors(computedErrors);
}
void test_getSourceKind_library() {
putTestLibrary(r'''
main() {}