Add the ability to associate a URL with a diagnostic

Change-Id: I06f694311130a36b1db4b4baf2e7d46d999f07b7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/97554
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson 2019-03-22 13:32:05 +00:00 committed by commit-bot@chromium.org
parent 87c553bf3c
commit 11a148c85e
12 changed files with 176 additions and 36 deletions

View file

@ -2891,6 +2891,11 @@ a:focus, a:hover {
<p>
The name, as a string, of the error code associated with this error.
</p>
</dd><dt class="field"><b>url: String<span style="color:#999999"> (optional)</span></b></dt><dd>
<p>
The URL of a page containing documentation associated with this error.
</p>
</dd><dt class="field"><b>hasFix: bool<span style="color:#999999"> (optional)</span></b></dt><dd>
<p>

View file

@ -39,34 +39,6 @@ List<AnalysisError> doAnalysisError_listFromEngine(
analysisOptions, lineInfo, errors, newAnalysisError_fromEngine);
}
/**
* Translates engine errors through the ErrorProcessor.
*/
List<T> mapEngineErrors<T>(
engine.AnalysisOptions analysisOptions,
engine.LineInfo lineInfo,
List<engine.AnalysisError> errors,
T Function(engine.LineInfo lineInfo, engine.AnalysisError error,
[engine.ErrorSeverity errorSeverity])
constructor) {
List<T> serverErrors = <T>[];
for (engine.AnalysisError error in errors) {
ErrorProcessor processor =
ErrorProcessor.getProcessor(analysisOptions, error);
if (processor != null) {
engine.ErrorSeverity severity = processor.severity;
// Errors with null severity are filtered out.
if (severity != null) {
// Specified severities override.
serverErrors.add(constructor(lineInfo, error, severity));
}
} else {
serverErrors.add(constructor(lineInfo, error));
}
}
return serverErrors;
}
/**
* Adds [edit] to the file containing the given [element].
*/
@ -103,6 +75,34 @@ String getReturnTypeString(engine.Element element) {
}
}
/**
* Translates engine errors through the ErrorProcessor.
*/
List<T> mapEngineErrors<T>(
engine.AnalysisOptions analysisOptions,
engine.LineInfo lineInfo,
List<engine.AnalysisError> errors,
T Function(engine.LineInfo lineInfo, engine.AnalysisError error,
[engine.ErrorSeverity errorSeverity])
constructor) {
List<T> serverErrors = <T>[];
for (engine.AnalysisError error in errors) {
ErrorProcessor processor =
ErrorProcessor.getProcessor(analysisOptions, error);
if (processor != null) {
engine.ErrorSeverity severity = processor.severity;
// Errors with null severity are filtered out.
if (severity != null) {
// Specified severities override.
serverErrors.add(constructor(lineInfo, error, severity));
}
} else {
serverErrors.add(constructor(lineInfo, error));
}
}
return serverErrors;
}
/**
* Construct based on error information from the analyzer engine.
*
@ -140,8 +140,9 @@ AnalysisError newAnalysisError_fromEngine(
String code = errorCode.name.toLowerCase();
String correction = error.correction;
bool fix = hasFix(error.errorCode);
String url = errorCode.url;
return new AnalysisError(severity, type, location, message, code,
correction: correction, hasFix: fix);
correction: correction, hasFix: fix, url: url);
}
/**

View file

@ -20,4 +20,5 @@ const String START_COLUMN = 'startColumn';
const String START_LINE = 'startLine';
const String SUBSCRIPTIONS = 'subscriptions';
const String TYPE = 'type';
const String URL = 'url';
const String VERSION = 'version';

View file

@ -34,6 +34,7 @@ final Matcher isAddContentOverlay = new LazyMatcher(() => new MatchesJsonObject(
* "message": String
* "correction": optional String
* "code": String
* "url": optional String
* "hasFix": optional bool
* }
*/
@ -46,6 +47,7 @@ final Matcher isAnalysisError =
"code": isString
}, optionalFields: {
"correction": isString,
"url": isString,
"hasFix": isBool
}));

View file

@ -67,6 +67,31 @@ class AnalysisErrorTest {
});
}
void test_fromEngine_hasUrl() {
engineError = new MockAnalysisError(
source,
new MockErrorCode(url: 'http://codes.dartlang.org/TEST_ERROR'),
10,
20,
'my message');
AnalysisError error = newAnalysisError_fromEngine(lineInfo, engineError);
expect(error.toJson(), {
SEVERITY: 'ERROR',
TYPE: 'COMPILE_TIME_ERROR',
LOCATION: {
FILE: 'foo.dart',
OFFSET: 10,
LENGTH: 20,
START_LINE: 3,
START_COLUMN: 2
},
MESSAGE: 'my message',
CODE: 'test_error',
URL: 'http://codes.dartlang.org/TEST_ERROR',
HAS_FIX: false
});
}
void test_fromEngine_noCorrection() {
engineError.correction = null;
AnalysisError error = newAnalysisError_fromEngine(lineInfo, engineError);
@ -213,3 +238,41 @@ class MockAnalysisError implements engine.AnalysisError {
MockAnalysisError(
this.source, this.errorCode, this.offset, this.length, this.message);
}
class MockErrorCode implements engine.ErrorCode {
@override
engine.ErrorType type;
@override
engine.ErrorSeverity errorSeverity;
@override
String name;
@override
String url;
MockErrorCode(
{this.type: engine.ErrorType.COMPILE_TIME_ERROR,
this.errorSeverity: engine.ErrorSeverity.ERROR,
this.name: 'TEST_ERROR',
this.url});
@override
String get correction {
throw new StateError('Unexpected invocation of correction');
}
@override
bool get isUnresolvedIdentifier => false;
@override
String get message {
throw new StateError('Unexpected invocation of message');
}
@override
String get uniqueName {
throw new StateError('Unexpected invocation of uniqueName');
}
}

View file

@ -68,6 +68,11 @@ public class AnalysisError {
*/
private final String code;
/**
* The URL of a page containing documentation associated with this error.
*/
private final String url;
/**
* A hint to indicate to interested clients that this error has an associated fix (or fixes). The
* absence of this field implies there are not known to be fixes. Note that since the operation to
@ -82,13 +87,14 @@ public class AnalysisError {
/**
* Constructor for {@link AnalysisError}.
*/
public AnalysisError(String severity, String type, Location location, String message, String correction, String code, Boolean hasFix) {
public AnalysisError(String severity, String type, Location location, String message, String correction, String code, String url, Boolean hasFix) {
this.severity = severity;
this.type = type;
this.location = location;
this.message = message;
this.correction = correction;
this.code = code;
this.url = url;
this.hasFix = hasFix;
}
@ -103,6 +109,7 @@ public class AnalysisError {
ObjectUtilities.equals(other.message, message) &&
ObjectUtilities.equals(other.correction, correction) &&
ObjectUtilities.equals(other.code, code) &&
ObjectUtilities.equals(other.url, url) &&
ObjectUtilities.equals(other.hasFix, hasFix);
}
return false;
@ -115,8 +122,9 @@ public class AnalysisError {
String message = jsonObject.get("message").getAsString();
String correction = jsonObject.get("correction") == null ? null : jsonObject.get("correction").getAsString();
String code = jsonObject.get("code").getAsString();
String url = jsonObject.get("url") == null ? null : jsonObject.get("url").getAsString();
Boolean hasFix = jsonObject.get("hasFix") == null ? null : jsonObject.get("hasFix").getAsBoolean();
return new AnalysisError(severity, type, location, message, correction, code, hasFix);
return new AnalysisError(severity, type, location, message, correction, code, url, hasFix);
}
public static List<AnalysisError> fromJsonArray(JsonArray jsonArray) {
@ -189,6 +197,13 @@ public class AnalysisError {
return type;
}
/**
* The URL of a page containing documentation associated with this error.
*/
public String getUrl() {
return url;
}
@Override
public int hashCode() {
HashCodeBuilder builder = new HashCodeBuilder();
@ -198,6 +213,7 @@ public class AnalysisError {
builder.append(message);
builder.append(correction);
builder.append(code);
builder.append(url);
builder.append(hasFix);
return builder.toHashCode();
}
@ -212,6 +228,9 @@ public class AnalysisError {
jsonObject.addProperty("correction", correction);
}
jsonObject.addProperty("code", code);
if (url != null) {
jsonObject.addProperty("url", url);
}
if (hasFix != null) {
jsonObject.addProperty("hasFix", hasFix);
}
@ -234,6 +253,8 @@ public class AnalysisError {
builder.append(correction + ", ");
builder.append("code=");
builder.append(code + ", ");
builder.append("url=");
builder.append(url + ", ");
builder.append("hasFix=");
builder.append(hasFix);
builder.append("]");

View file

@ -63,6 +63,9 @@ class MockErrorCode implements ErrorCode {
@override
String name;
@override
String url;
MockErrorCode(this.type, this.errorSeverity, this.name);
@override

View file

@ -900,6 +900,11 @@ a:focus, a:hover {
<p>
The name, as a string, of the error code associated with this error.
</p>
</dd><dt class="field"><b>url: String<span style="color:#999999"> (optional)</span></b></dt><dd>
<p>
The URL of a page containing documentation associated with this error.
</p>
</dd><dt class="field"><b>hasFix: bool<span style="color:#999999"> (optional)</span></b></dt><dd>
<p>

View file

@ -102,6 +102,7 @@ class AddContentOverlay implements HasToJson {
* "message": String
* "correction": optional String
* "code": String
* "url": optional String
* "hasFix": optional bool
* }
*
@ -120,6 +121,8 @@ class AnalysisError implements HasToJson {
String _code;
String _url;
bool _hasFix;
/**
@ -205,6 +208,18 @@ class AnalysisError implements HasToJson {
this._code = value;
}
/**
* The URL of a page containing documentation associated with this error.
*/
String get url => _url;
/**
* The URL of a page containing documentation associated with this error.
*/
void set url(String value) {
this._url = value;
}
/**
* A hint to indicate to interested clients that this error has an associated
* fix (or fixes). The absence of this field implies there are not known to
@ -233,13 +248,14 @@ class AnalysisError implements HasToJson {
AnalysisError(AnalysisErrorSeverity severity, AnalysisErrorType type,
Location location, String message, String code,
{String correction, bool hasFix}) {
{String correction, String url, bool hasFix}) {
this.severity = severity;
this.type = type;
this.location = location;
this.message = message;
this.correction = correction;
this.code = code;
this.url = url;
this.hasFix = hasFix;
}
@ -288,12 +304,16 @@ class AnalysisError implements HasToJson {
} else {
throw jsonDecoder.mismatch(jsonPath, "code");
}
String url;
if (json.containsKey("url")) {
url = jsonDecoder.decodeString(jsonPath + ".url", json["url"]);
}
bool hasFix;
if (json.containsKey("hasFix")) {
hasFix = jsonDecoder.decodeBool(jsonPath + ".hasFix", json["hasFix"]);
}
return new AnalysisError(severity, type, location, message, code,
correction: correction, hasFix: hasFix);
correction: correction, url: url, hasFix: hasFix);
} else {
throw jsonDecoder.mismatch(jsonPath, "AnalysisError", json);
}
@ -310,6 +330,9 @@ class AnalysisError implements HasToJson {
result["correction"] = correction;
}
result["code"] = code;
if (url != null) {
result["url"] = url;
}
if (hasFix != null) {
result["hasFix"] = hasFix;
}
@ -328,6 +351,7 @@ class AnalysisError implements HasToJson {
message == other.message &&
correction == other.correction &&
code == other.code &&
url == other.url &&
hasFix == other.hasFix;
}
return false;
@ -342,6 +366,7 @@ class AnalysisError implements HasToJson {
hash = JenkinsSmiHash.combine(hash, message.hashCode);
hash = JenkinsSmiHash.combine(hash, correction.hashCode);
hash = JenkinsSmiHash.combine(hash, code.hashCode);
hash = JenkinsSmiHash.combine(hash, url.hashCode);
hash = JenkinsSmiHash.combine(hash, hasFix.hashCode);
return JenkinsSmiHash.finish(hash);
}

View file

@ -34,6 +34,7 @@ final Matcher isAddContentOverlay = new LazyMatcher(() => new MatchesJsonObject(
* "message": String
* "correction": optional String
* "code": String
* "url": optional String
* "hasFix": optional bool
* }
*/
@ -46,6 +47,7 @@ final Matcher isAnalysisError =
"code": isString
}, optionalFields: {
"correction": isString,
"url": isString,
"hasFix": isBool
}));

View file

@ -95,6 +95,12 @@
The name, as a string, of the error code associated with this error.
</p>
</field>
<field name="url" optional="true">
<ref>String</ref>
<p>
The URL of a page containing documentation associated with this error.
</p>
</field>
<field name="hasFix" optional="true">
<ref>bool</ref>
<p>

View file

@ -1,4 +1,4 @@
// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
// Copyright (c) 2016, 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.
@ -25,6 +25,12 @@ abstract class ErrorCode {
*/
final String correction;
/**
* The URL of a page containing documentation for errors with this code, or
* `null` if there is no known documentation.
*/
final String url;
/**
* Whether this error is caused by an unresolved identifier.
*/
@ -36,7 +42,7 @@ abstract class ErrorCode {
* template. The correction associated with the error will be created from the
* given [correction] template.
*/
const ErrorCode(this.name, this.message, [this.correction])
const ErrorCode(this.name, this.message, [this.correction, this.url])
: isUnresolvedIdentifier = false;
/**
@ -46,7 +52,7 @@ abstract class ErrorCode {
* given [correction] template.
*/
const ErrorCode.temporary(this.name, this.message,
{this.correction, this.isUnresolvedIdentifier: false});
{this.correction, this.isUnresolvedIdentifier: false, this.url});
/**
* The severity of the error.