Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(287)

Side by Side Diff: pkg/analysis_server/lib/src/services/correction/status.dart

Issue 485083004: Make RefactoringStatus a collection of generated RefactoringProblems. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library services.status; 5 library services.status;
6 6
7 import 'package:analysis_server/src/protocol2.dart' hide Element;
8 import 'package:analysis_server/src/services/correction/source_range.dart';
7 import 'package:analysis_server/src/services/search/search_engine.dart'; 9 import 'package:analysis_server/src/services/search/search_engine.dart';
8 import 'package:analysis_server/src/services/correction/source_range.dart';
9 import 'package:analyzer/src/generated/ast.dart'; 10 import 'package:analyzer/src/generated/ast.dart';
10 import 'package:analyzer/src/generated/element.dart'; 11 import 'package:analyzer/src/generated/element.dart';
11 import 'package:analyzer/src/generated/engine.dart'; 12 import 'package:analyzer/src/generated/engine.dart';
12 import 'package:analyzer/src/generated/java_core.dart';
13 import 'package:analyzer/src/generated/source.dart'; 13 import 'package:analyzer/src/generated/source.dart';
14 14
15 15
16 /** 16 /**
17 * Creates a new [Location].
18 */
19 Location createLocation(AnalysisContext context, Source source,
Brian Wilkerson 2014/08/22 14:24:04 I'd like this better if these were factory methods
scheglov 2014/08/22 14:46:13 Agree. It's the next step. I decided to do my firs
20 SourceRange range) {
21 int startLine = 0;
22 int startColumn = 0;
23 {
24 LineInfo lineInfo = context.getLineInfo(source);
25 if (lineInfo != null) {
26 LineInfo_Location offsetLocation = lineInfo.getLocation(range.offset);
27 startLine = offsetLocation.lineNumber;
28 startColumn = offsetLocation.columnNumber;
29 }
30 }
31 return new Location(
32 source.fullName,
33 range.offset,
34 range.length,
35 startLine,
36 startColumn);
37 }
38
39
40 /**
41 * Creates a new [Location] for the given [Element].
42 */
43 Location createLocation_forElement(Element element) {
44 AnalysisContext context = element.context;
45 Source source = element.source;
46 SourceRange range = rangeElementName(element);
47 return createLocation(context, source, range);
48 }
49
50
51 /**
52 * Creates a new [Location] for the given [SearchMatch].
53 */
54 Location createLocation_forMatch(SearchMatch match) {
55 Element enclosingElement = match.element;
56 return createLocation(
57 enclosingElement.context,
58 enclosingElement.source,
59 match.sourceRange);
60 }
61
62
63 /**
64 * Creates a new [Location] for the given [AstNode].
65 */
66 Location createLocation_forNode(AstNode node) {
67 CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit);
68 CompilationUnitElement unitElement = unit.element;
69 AnalysisContext context = unitElement.context;
70 Source source = unitElement.source;
71 SourceRange range = rangeNode(node);
72 return createLocation(context, source, range);
73 }
74
75
76 /**
77 * Creates a new [Location] for the given [CompilationUnit].
78 */
79 Location createLocation_forUnit(CompilationUnit unit, SourceRange range) {
80 CompilationUnitElement unitElement = unit.element;
81 AnalysisContext context = unitElement.context;
82 Source source = unitElement.source;
83 return createLocation(context, source, range);
84 }
85
86
87 RefactoringProblemSeverity _maxSeverity(RefactoringProblemSeverity a,
Brian Wilkerson 2014/08/22 14:24:04 And this should be an instance method on Refactori
scheglov 2014/08/22 14:46:13 Do you mean a *static* method? The initial values
88 RefactoringProblemSeverity b) {
89 if (a == null) {
90 return b;
Brian Wilkerson 2014/08/22 14:24:04 For consistency we should return 'a' if 'b' is nul
scheglov 2014/08/22 14:46:13 Done.
91 } else if (a == RefactoringProblemSeverity.INFO) {
92 return b;
93 } else if (a == RefactoringProblemSeverity.WARNING) {
94 if (b == RefactoringProblemSeverity.ERROR ||
95 b == RefactoringProblemSeverity.FATAL) {
96 return b;
97 }
98 } else if (a == RefactoringProblemSeverity.ERROR) {
99 if (b == RefactoringProblemSeverity.FATAL) {
100 return b;
101 }
102 }
103 return a;
104 }
105
106 /**
17 * An outcome of a condition checking operation. 107 * An outcome of a condition checking operation.
18 */ 108 */
19 class RefactoringStatus { 109 class RefactoringStatus {
20 /** 110 /**
21 * The current severity of this [RefactoringStatus] - the maximum of the 111 * The current severity of this [RefactoringStatus] - the maximum of the
22 * severities of its [entries]. 112 * severities of its [entries].
23 */ 113 */
24 RefactoringStatusSeverity _severity = RefactoringStatusSeverity.OK; 114 RefactoringProblemSeverity _severity = null;
25 115
26 /** 116 /**
27 * A list of [RefactoringStatusEntry]s. 117 * A list of [RefactoringProblem]s.
28 */ 118 */
29 final List<RefactoringStatusEntry> entries = []; 119 final List<RefactoringProblem> problems = [];
30 120
31 /** 121 /**
32 * Creates a new OK [RefactoringStatus]. 122 * Creates a new OK [RefactoringStatus].
33 */ 123 */
34 RefactoringStatus(); 124 RefactoringStatus();
35 125
36 /** 126 /**
37 * Creates a new [RefactoringStatus] with the ERROR severity. 127 * Creates a new [RefactoringStatus] with the ERROR severity.
38 */ 128 */
39 factory RefactoringStatus.error(String msg, 129 factory RefactoringStatus.error(String msg, [Location location]) {
40 [RefactoringStatusContext context]) {
41 RefactoringStatus status = new RefactoringStatus(); 130 RefactoringStatus status = new RefactoringStatus();
42 status.addError(msg, context); 131 status.addError(msg, location);
43 return status; 132 return status;
44 } 133 }
45 134
46 /** 135 /**
47 * Creates a new [RefactoringStatus] with the FATAL severity. 136 * Creates a new [RefactoringStatus] with the FATAL severity.
48 */ 137 */
49 factory RefactoringStatus.fatal(String msg, 138 factory RefactoringStatus.fatal(String msg, [Location location]) {
50 [RefactoringStatusContext context]) {
51 RefactoringStatus status = new RefactoringStatus(); 139 RefactoringStatus status = new RefactoringStatus();
52 status.addFatalError(msg, context); 140 status.addFatalError(msg, location);
53 return status; 141 return status;
54 } 142 }
55 143
56 /** 144 /**
57 * Creates a new [RefactoringStatus] with the WARNING severity. 145 * Creates a new [RefactoringStatus] with the WARNING severity.
58 */ 146 */
59 factory RefactoringStatus.warning(String msg, 147 factory RefactoringStatus.warning(String msg, [Location location]) {
60 [RefactoringStatusContext context]) {
61 RefactoringStatus status = new RefactoringStatus(); 148 RefactoringStatus status = new RefactoringStatus();
62 status.addWarning(msg, context); 149 status.addWarning(msg, location);
63 return status; 150 return status;
64 } 151 }
65 152
66 /** 153 /**
67 * Returns the first [RefactoringStatusEntry] with the highest severity. 154 * Returns `true` if the severity is FATAL or ERROR.
155 */
156 bool get hasError {
157 return _severity == RefactoringProblemSeverity.FATAL ||
158 _severity == RefactoringProblemSeverity.ERROR;
159 }
160
161 /**
162 * Returns `true` if the severity is FATAL.
163 */
164 bool get hasFatalError => _severity == RefactoringProblemSeverity.FATAL;
165
166 /**
167 * Returns `true` if the severity is WARNING.
168 */
169 bool get hasWarning => _severity == RefactoringProblemSeverity.WARNING;
170
171 /**
172 * Return `true` if the severity is `OK`.
173 */
174 bool get isOK => _severity == null;
175
176 /**
177 * Returns the message of the [RefactoringProblem] with highest severity;
178 * may be `null` if no problems.
179 */
180 String get message {
181 RefactoringProblem problem = this.problem;
182 if (problem == null) {
183 return null;
184 }
185 return problem.message;
186 }
187
188 /**
189 * Returns the first [RefactoringProblem] with the highest severity.
68 * 190 *
69 * If there is more than one entry with the highest severity then there is no 191 * If there is more than one problem with the highest severity then there is n o
70 * guarantee as to which will be returned. 192 * guarantee as to which will be returned.
Paul Berry 2014/08/22 12:36:03 If you don't want to make a guarantee as to which
scheglov 2014/08/22 14:46:13 Acknowledged.
71 * 193 *
72 * Returns `null` if no entries. 194 * Returns `null` if no entries.
73 */ 195 */
74 RefactoringStatusEntry get entryWithHighestSeverity { 196 RefactoringProblem get problem {
75 for (RefactoringStatusEntry entry in entries) { 197 for (RefactoringProblem problem in problems) {
76 if (entry.severity == _severity) { 198 if (problem.severity == _severity) {
77 return entry; 199 return problem;
78 } 200 }
79 } 201 }
80 return null; 202 return null;
81 } 203 }
82 204
83 /** 205 /**
84 * Returns `true` if the severity is FATAL or ERROR. 206 * Returns the current severity of this [RefactoringStatus].
85 */ 207 */
86 bool get hasError => 208 RefactoringProblemSeverity get severity => _severity;
87 _severity == RefactoringStatusSeverity.FATAL ||
88 _severity == RefactoringStatusSeverity.ERROR;
89 209
90 /** 210 /**
91 * Returns `true` if the severity is FATAL. 211 * Adds an ERROR problem with the given message and location.
92 */ 212 */
93 bool get hasFatalError => _severity == RefactoringStatusSeverity.FATAL; 213 void addError(String msg, [Location location]) {
94 214 _addProblem(
95 /** 215 new RefactoringProblem(
96 * Returns `true` if the severity is WARNING. 216 RefactoringProblemSeverity.ERROR,
97 */ 217 msg,
98 bool get hasWarning => _severity == RefactoringStatusSeverity.WARNING; 218 location: location));
99
100 /**
101 * Return `true` if the severity is `OK`.
102 */
103 bool get isOK => _severity == RefactoringStatusSeverity.OK;
104
105 /**
106 * Returns the message of the [RefactoringStatusEntry] with highest severity;
107 * may be `null` if no entries.
108 */
109 String get message {
110 RefactoringStatusEntry entry = entryWithHighestSeverity;
111 if (entry == null) {
112 return null;
113 }
114 return entry.message;
115 } 219 }
116 220
117 /** 221 /**
118 * Returns the current severity of this [RefactoringStatus]. 222 * Adds a FATAL problem with the given message and location.
119 */ 223 */
120 RefactoringStatusSeverity get severity => _severity; 224 void addFatalError(String msg, [Location location]) {
121 225 _addProblem(
122 /** 226 new RefactoringProblem(
123 * Adds an ERROR entry with the given message and status. 227 RefactoringProblemSeverity.FATAL,
124 */ 228 msg,
125 void addError(String msg, [RefactoringStatusContext context]) { 229 location: location));
126 _addEntry(
127 new RefactoringStatusEntry(RefactoringStatusSeverity.ERROR, msg, context ));
128 } 230 }
129 231
130 /** 232 /**
131 * Adds a FATAL entry with the given message and status.
132 */
133 void addFatalError(String msg, [RefactoringStatusContext context]) {
134 _addEntry(
135 new RefactoringStatusEntry(RefactoringStatusSeverity.FATAL, msg, context ));
136 }
137
138 /**
139 * Merges [other] into this [RefactoringStatus]. 233 * Merges [other] into this [RefactoringStatus].
140 * 234 *
141 * The [other]'s entries are added to this. 235 * The [other]'s entries are added to this.
142 * 236 *
143 * The resulting severity is the more severe of this and [other] severities. 237 * The resulting severity is the more severe of this and [other] severities.
144 * 238 *
145 * Merging with `null` is allowed - it has no effect. 239 * Merging with `null` is allowed - it has no effect.
146 */ 240 */
147 void addStatus(RefactoringStatus other) { 241 void addStatus(RefactoringStatus other) {
148 if (other == null) { 242 if (other == null) {
149 return; 243 return;
150 } 244 }
151 entries.addAll(other.entries); 245 problems.addAll(other.problems);
152 _severity = RefactoringStatusSeverity._max(_severity, other.severity); 246 _severity = _maxSeverity(_severity, other.severity);
153 } 247 }
154 248
155 /** 249 /**
156 * Adds a WARNING entry with the given message and status. 250 * Adds a WARNING problem with the given message and location.
157 */ 251 */
158 void addWarning(String msg, [RefactoringStatusContext context]) { 252 void addWarning(String msg, [Location location]) {
159 _addEntry( 253 _addProblem(
160 new RefactoringStatusEntry(RefactoringStatusSeverity.WARNING, msg, conte xt)); 254 new RefactoringProblem(
161 } 255 RefactoringProblemSeverity.WARNING,
162 256 msg,
163 /** 257 location: location));
164 * Returns a copy of this [RefactoringStatus] with ERROR replaced with FATAL.
165 */
166 RefactoringStatus escalateErrorToFatal() {
167 RefactoringStatus result = new RefactoringStatus();
168 for (RefactoringStatusEntry entry in entries) {
169 if (entry.severity == RefactoringStatusSeverity.ERROR) {
170 entry = new RefactoringStatusEntry(
171 RefactoringStatusSeverity.FATAL,
172 entry.message,
173 entry.context);
174 }
175 result._addEntry(entry);
176 }
177 return result;
178 } 258 }
179 259
180 @override 260 @override
181 String toString() { 261 String toString() {
182 StringBuffer sb = new StringBuffer(); 262 StringBuffer sb = new StringBuffer();
183 sb.write("<"); 263 sb.write("<");
184 sb.write(_severity.name); 264 if (_severity == null) {
265 sb.write('OK');
266 } else {
267 sb.write(_severity.name);
268 }
185 if (!isOK) { 269 if (!isOK) {
186 sb.write("\n"); 270 sb.write("\n");
187 for (RefactoringStatusEntry entry in entries) { 271 for (RefactoringProblem problem in problems) {
188 sb.write("\t"); 272 sb.write("\t");
189 sb.write(entry); 273 sb.write(problem);
190 sb.write("\n"); 274 sb.write("\n");
191 } 275 }
192 } 276 }
193 sb.write(">"); 277 sb.write(">");
194 return sb.toString(); 278 return sb.toString();
195 } 279 }
196 280
197 /** 281 /**
198 * Adds the given [RefactoringStatusEntry] and updates [severity]. 282 * Adds the given [RefactoringProblem] and updates [severity].
199 */ 283 */
200 void _addEntry(RefactoringStatusEntry entry) { 284 void _addProblem(RefactoringProblem problem) {
201 entries.add(entry); 285 problems.add(problem);
202 _severity = RefactoringStatusSeverity._max(_severity, entry.severity); 286 // update maximum severity
287 RefactoringProblemSeverity severity = problem.severity;
288 _severity = _maxSeverity(_severity, severity);
203 } 289 }
204 } 290 }
205
206
207 /**
208 * [RefactoringStatusContext] can be used to annotate [RefactoringStatusEntry]s
209 * with additional information typically presented in the user interface.
210 */
211 class RefactoringStatusContext {
212 /**
213 * The [AnalysisContext] in which this status occurs.
214 */
215 final AnalysisContext context;
216
217 /**
218 * The [Source] in which this status occurs.
219 */
220 final Source source;
221
222 /**
223 * The [SourceRange] with specific location where this status occurs.
224 */
225 final SourceRange range;
226
227 /**
228 * Creates a new [RefactoringStatusContext].
229 */
230 RefactoringStatusContext(this.context, this.source, this.range);
231
232 /**
233 * Creates a new [RefactoringStatusContext] for the given [Element].
234 */
235 factory RefactoringStatusContext.forElement(Element element) {
236 AnalysisContext context = element.context;
237 Source source = element.source;
238 SourceRange range = rangeElementName(element);
239 return new RefactoringStatusContext(context, source, range);
240 }
241
242 /**
243 * Creates a new [RefactoringStatusContext] for the given [SearchMatch].
244 */
245 factory RefactoringStatusContext.forMatch(SearchMatch match) {
246 Element enclosingElement = match.element;
247 return new RefactoringStatusContext(
248 enclosingElement.context,
249 enclosingElement.source,
250 match.sourceRange);
251 }
252
253 /**
254 * Creates a new [RefactoringStatusContext] for the given [AstNode].
255 */
256 factory RefactoringStatusContext.forNode(AstNode node) {
257 CompilationUnit unit = node.getAncestor((node) => node is CompilationUnit);
258 CompilationUnitElement unitElement = unit.element;
259 AnalysisContext context = unitElement.context;
260 Source source = unitElement.source;
261 SourceRange range = rangeNode(node);
262 return new RefactoringStatusContext(context, source, range);
263 }
264
265 /**
266 * Creates a new [RefactoringStatusContext] for the given [CompilationUnit].
267 */
268 factory RefactoringStatusContext.forUnit(CompilationUnit unit,
269 SourceRange range) {
270 CompilationUnitElement unitElement = unit.element;
271 AnalysisContext context = unitElement.context;
272 Source source = unitElement.source;
273 return new RefactoringStatusContext(context, source, range);
274 }
275
276 @override
277 String toString() {
278 JavaStringBuilder builder = new JavaStringBuilder();
279 builder.append("[source=");
280 builder.append(source);
281 builder.append(", range=");
282 builder.append(range);
283 builder.append("]");
284 return builder.toString();
285 }
286 }
287
288
289 /**
290 * An immutable object representing an entry in a [RefactoringStatus].
291 *
292 * A [RefactoringStatusEntry] consists of a severity, a message and a context.
293 */
294 class RefactoringStatusEntry {
295 /**
296 * The severity level.
297 */
298 final RefactoringStatusSeverity severity;
299
300 /**
301 * The message of the status entry.
302 */
303 final String message;
304
305 /**
306 * The [RefactoringStatusContext] which can be used to show more detailed
307 * information regarding this status entry in the UI.
308 *
309 * May be `null` indicating that no context is available.
310 */
311 final RefactoringStatusContext context;
312
313 RefactoringStatusEntry(this.severity, this.message, [this.context]);
314
315 /**
316 * Returns whether the entry represents an error or not.
317 */
318 bool get isError => severity == RefactoringStatusSeverity.ERROR;
319
320 /**
321 * Returns whether the entry represents a fatal error or not.
322 */
323 bool get isFatalError => severity == RefactoringStatusSeverity.FATAL;
324
325 /**
326 * Returns whether the entry represents a warning or not.
327 */
328 bool get isWarning => severity == RefactoringStatusSeverity.WARNING;
329
330 @override
331 String toString() {
332 if (context != null) {
333 return "${severity}: ${message}; Context: ${context}";
334 } else {
335 return "${severity}: ${message}";
336 }
337 }
338 }
339
340
341 /**
342 * Severity of [RefactoringStatus].
343 */
344 class RefactoringStatusSeverity {
345 /**
346 * The severity indicating the nominal case.
347 */
348 static const OK = const RefactoringStatusSeverity('OK', 0);
349
350 /**
351 * The severity indicating a warning.
352 *
353 * Use this severity if the refactoring can be performed, but you assume that
354 * the user could not be aware of problems or confusions resulting from the
355 * execution.
356 */
357 static const WARNING = const RefactoringStatusSeverity('WARNING', 2);
358
359 /**
360 * The severity indicating an error.
361 *
362 * Use this severity if the refactoring can be performed, but the refactoring
363 * will not be behavior preserving and/or the partial execution will lead to
364 * an inconsistent state (e.g. compile errors).
365 */
366 static const ERROR = const RefactoringStatusSeverity('ERROR', 3);
367
368 /**
369 * The severity indicating a fatal error.
370 *
371 * Use this severity if the refactoring cannot be performed, and execution
372 * would lead to major problems. Note that this completely blocks the user
373 * from performing this refactoring.
374 *
375 * It is often preferable to use an [ERROR] status and allow a partial
376 * execution (e.g. if just one reference to a refactored element cannot be
377 * updated).
378 */
379 static const FATAL = const RefactoringStatusSeverity('FATAL', 4);
380
381 final String name;
382 final int ordinal;
383
384 const RefactoringStatusSeverity(this.name, this.ordinal);
385
386 @override
387 String toString() => name;
388
389 /**
390 * Returns the most severe [RefactoringStatusSeverity].
391 */
392 static RefactoringStatusSeverity _max(RefactoringStatusSeverity a,
393 RefactoringStatusSeverity b) {
394 if (b.ordinal > a.ordinal) {
395 return b;
396 }
397 return a;
398 }
399 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698