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

Side by Side Diff: pkg/analysis_server/lib/src/services/kythe/kythe_visitors.dart

Issue 2987193002: Some initial work for the Dart Kythe indexer support in the analysis server (Closed)
Patch Set: Created 3 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
« no previous file with comments | « no previous file | pkg/analysis_server/lib/src/services/kythe/schema.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
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.
4
5 import 'dart:convert';
6 import 'dart:io';
7
8 import 'package:analyzer/dart/ast/ast.dart';
9 import 'package:analyzer/dart/ast/standard_resolution_map.dart';
10 import 'package:analyzer/dart/ast/syntactic_entity.dart';
11 import 'package:analyzer/dart/ast/token.dart';
12 import 'package:analyzer/dart/ast/visitor.dart';
13 import 'package:analyzer/dart/element/element.dart';
14 import 'package:analyzer/dart/element/type.dart';
15 import 'package:analyzer/dart/element/visitor.dart';
16 import 'package:analyzer/src/dart/resolver/inheritance_manager.dart';
17
18 import 'schema.dart' as schema;
19
20 const int _notFound = -1;
21
22 /// Computes analysis of the given compilation [unit].
23 ///
24 /// [unit] is the compilation unit to be analyzed; it is assumed to exist in the
25 /// given [corpus] and to have the given text [contents]. Analysis results are
26 /// returned as a list of Kythe [pb_Entry] objects.
27 List<pb_Entry> computeIndex(
28 String corpus, CompilationUnit unit, String contents) {
29 final List<pb_Entry> entries = [];
30 var visitor = new KytheDartVisitor(
31 entries,
32 corpus,
33 new InheritanceManager(
34 resolutionMap.elementDeclaredByCompilationUnit(unit).library),
35 contents);
36 unit.accept(visitor);
37 return entries;
38 }
39
40 /// Outputs analysis of the given compilation [unit] to the parent process.
41 ///
42 /// [unit] is the compilation unit to be analyzed; it is assumed to exist in the
43 /// given [corpus] and to have the given text [contents]. Analysis results are
44 /// sent do the parent process as raw Kythe [Entry] objects.
45 void writeOutIndex(CompilationUnit unit, String corpus, String contents) {
46 List<pb_Entry> entries = computeIndex(corpus, unit, contents);
47 for (pb_Entry e in entries) {
48 assert(e.source != null);
49 if (e.edgeKind == "") {
50 assert(e.target.toString() == "");
51 }
52 _sendToParentProcess(e);
53 }
54 }
55
56 /// Given some [ConstructorElement], this method returns '<class-name>' as the
57 /// name of the constructor, unless the constructor is a named constructor
58 /// in which '<class-name>.<constructor-name>' is returned.
59 String _computeConstructorElementName(ConstructorElement element) {
60 assert(element != null);
61 var name = element.enclosingElement.name;
62 var constructorName = element.name;
63 if (!constructorName.isEmpty) {
64 name = name + '.' + constructorName;
65 }
66 return name;
67 }
68
69 /// Create an anchor signature of the form '<start>-<end>'.
70 String _getAnchorSignature(int start, int end) {
71 return '$start-$end';
72 }
73
74 String _getPath(Element e) {
75 // TODO(jwren) figure out what source generates a e != null, but
76 // e.source == null to ensure that it is not a bug somewhere in the stack.
77 if (e == null || e.source == null) {
78 // null sometimes when the element is used to generate the node type
79 // "dynamic"
80 return '';
81 }
82 var path = e.source.fullName;
83 assert(path.lastIndexOf('CORPUS_NAME') != -1);
84 return path.substring(path.lastIndexOf('CORPUS_NAME') + 12);
85 }
86
87 /// If a non-null element is passed, the [SignatureElementVisitor] is used to
88 /// generate and return a [String] signature, otherwise
89 /// [schema.DYNAMIC_KIND] is returned.
90 String _getSignature(Element element, String nodeKind, String corpus) {
91 assert(nodeKind != schema.ANCHOR_KIND); // Call _getAnchorSignature instead
92 if (element == null) {
93 return schema.DYNAMIC_KIND;
94 }
95 if (element is CompilationUnitElement) {
96 return _getPath(element);
97 }
98 return '$nodeKind:${element.accept(SignatureElementVisitor.instance)}';
99 }
100
101 /// Send a [Reply] message to the parent process via standard output.
102 ///
103 /// The message is formatted as a base-128 encoded length followed by the
104 /// serialized message data. The base-128 encoding is in little-endian order,
105 /// with the high bit set on all bytes but the last. This was chosen since
106 /// it's the same as the base-128 encoding used by protobufs, so it allows a
107 /// modest amount of code reuse. Also it parallels the format used by
108 /// [messageGrouper].
109 void _sendToParentProcess(pb_Entry entry) {
110 var rawMessage = entry.writeToBuffer();
111 var encodedLength = (new CodedBufferWriter(rawMessage.length)).toBuffer();
112 stdout..add(encodedLength)..add(rawMessage);
113 }
114
115 // TODO(jwren) This method simply serves to provide the WORKSPACE relative path
116 // for sources in Elements, it needs to be written in a more robust way.
117 /// This visitor writes out Kythe facts and edges as specified by the Kythe
118 /// Schema here https://kythe.io/docs/schema/. This visitor handles all nodes,
119 /// facts and edges.
120 class KytheDartVisitor extends GeneralizingAstVisitor with OutputUtils {
121 final List<pb_Entry> entries;
122 final String corpus;
123 final InheritanceManager _inheritanceManager;
124 String _enclosingFilePath = '';
125 Element _enclosingElement;
126 ClassElement _enclosingClassElement;
127 pb_VName _enclosingVName;
128 pb_VName _enclosingFileVName;
129 pb_VName _enclosingClassVName;
130 final String _contents;
131
132 KytheDartVisitor(
133 this.entries, this.corpus, this._inheritanceManager, this._contents);
134
135 @override
136 String get enclosingFilePath => _enclosingFilePath;
137
138 @override
139 visitAnnotation(Annotation node) {
140 // TODO(jwren) To get the full set of cross refs correct, additional ref
141 // edges are needed, example: from "A" in "A.namedConstructor()"
142
143 var start = node.name.offset;
144 var end = node.name.end;
145 if (node.constructorName != null) {
146 end = node.constructorName.end;
147 }
148 var refVName = _handleRefEdge(
149 node.element,
150 const <String>[schema.REF_EDGE],
151 start: start,
152 end: end,
153 );
154 if (refVName != null) {
155 var parentNode = node.parent;
156 if (parentNode is Declaration) {
157 Element parentElement = parentNode.element;
158 if (parentNode is TopLevelVariableDeclaration) {
159 _handleVariableDeclarationListAnnotations(
160 parentNode.variables, refVName);
161 } else if (parentNode is FieldDeclaration) {
162 _handleVariableDeclarationListAnnotations(
163 parentNode.fields, refVName);
164 } else if (parentElement != null) {
165 var parentVName =
166 _vNameFromElement(parentElement, _getNodeKind(parentElement));
167 addEdge(parentVName, schema.ANNOTATED_BY_EDGE, refVName);
168 } else {
169 // parentAstNode is not a variable declaration node and
170 // parentElement == null
171 assert(false);
172 }
173 } else {
174 // parentAstNode is not a Declaration
175 // TODO(jwren) investigate
176 // throw new Exception('parentAstNode.runtimeType = ${parentAstNode.runtime Type}');
177 // assert(false);
178 }
179 }
180
181 // visit children
182 _safelyVisit(node.arguments);
183 }
184
185 @override
186 visitAssignmentExpression(AssignmentExpression node) {
187 //
188 // operator
189 // NOTE: usage node only written out if assignment is not the '=' operator,
190 // we are looking for an operator such as +=, -=, *=, /=
191 //
192 Token operator = node.operator;
193 MethodElement element = node.bestElement;
194 if (operator.type != TokenType.EQ && element != null) {
195 // method
196 _vNameFromElement(element, schema.FUNCTION_KIND);
197
198 // anchor- ref/call
199 _handleRefCallEdge(element,
200 syntacticEntity: node.operator, enclosingTarget: _enclosingVName);
201
202 // TODO (jwren) Add function type information
203 }
204 // visit children
205 _safelyVisit(node.leftHandSide);
206 _safelyVisit(node.rightHandSide);
207 }
208
209 @override
210 visitBinaryExpression(BinaryExpression node) {
211 //
212 // operators such as +, -, *, /
213 //
214 MethodElement element = node.bestElement;
215 if (element != null) {
216 // method
217 _vNameFromElement(element, schema.FUNCTION_KIND);
218
219 // anchor- ref/call
220 _handleRefCallEdge(element,
221 syntacticEntity: node.operator, enclosingTarget: _enclosingVName);
222
223 // TODO (jwren) Add function type information
224 }
225 // visit children
226 _safelyVisit(node.leftOperand);
227 _safelyVisit(node.rightOperand);
228 }
229
230 @override
231 visitClassDeclaration(ClassDeclaration node) {
232 return _withEnclosingElement(node.element, () {
233 // record/ class node
234 addNodeAndFacts(schema.RECORD_KIND,
235 nodeVName: _enclosingClassVName,
236 subKind: schema.CLASS_SUBKIND,
237 completeFact: schema.DEFINITION);
238
239 // anchor- defines/binding
240 addAnchorEdgesContainingEdge(
241 syntacticEntity: node.name,
242 edges: [
243 schema.DEFINES_BINDING_EDGE,
244 ],
245 target: _enclosingClassVName,
246 enclosingTarget: _enclosingFileVName);
247
248 // anchor- defines
249 addAnchorEdgesContainingEdge(
250 syntacticEntity: node,
251 edges: [
252 schema.DEFINES_EDGE,
253 ],
254 target: _enclosingClassVName);
255
256 // extends
257 var supertype = _enclosingClassElement.supertype;
258 if (supertype?.element != null) {
259 var recordSupertypeVName =
260 _vNameFromElement(supertype.element, schema.RECORD_KIND);
261 addEdge(
262 _enclosingClassVName, schema.EXTENDS_EDGE, recordSupertypeVName);
263 }
264
265 // implements
266 var interfaces = _enclosingClassElement.interfaces;
267 for (var interface in interfaces) {
268 if (interface.element != null) {
269 var recordInterfaceVName =
270 _vNameFromElement(interface.element, schema.RECORD_KIND);
271 addEdge(
272 _enclosingClassVName, schema.EXTENDS_EDGE, recordInterfaceVName);
273 }
274 }
275
276 // mixins
277 var mixins = _enclosingClassElement.mixins;
278 for (var mixin in mixins) {
279 if (mixin.element != null) {
280 var recordMixinVName =
281 _vNameFromElement(mixin.element, schema.RECORD_KIND);
282 addEdge(_enclosingClassVName, schema.EXTENDS_EDGE, recordMixinVName);
283 }
284 }
285
286 // TODO (jwren) type parameters
287
288 // visit children
289 _safelyVisit(node.documentationComment);
290 _safelyVisitList(node.metadata);
291 _safelyVisit(node.extendsClause);
292 _safelyVisit(node.implementsClause);
293 _safelyVisit(node.withClause);
294 _safelyVisit(node.nativeClause);
295 _safelyVisitList(node.members);
296 _safelyVisit(node.typeParameters);
297 });
298 }
299
300 @override
301 visitClassTypeAlias(ClassTypeAlias node) {
302 return _withEnclosingElement(node.element, () {
303 // record/ class node
304 addNodeAndFacts(schema.RECORD_KIND,
305 nodeVName: _enclosingClassVName,
306 subKind: schema.CLASS_SUBKIND,
307 completeFact: schema.DEFINITION);
308
309 // anchor
310 addAnchorEdgesContainingEdge(
311 syntacticEntity: node.name,
312 edges: [
313 schema.DEFINES_BINDING_EDGE,
314 ],
315 target: _enclosingClassVName,
316 enclosingTarget: _enclosingFileVName);
317
318 //
319 // superclass
320 // The super type is not in an ExtendsClause (as is the case with
321 // ClassDeclarations) and super.visitClassTypeAlias is not sufficient.
322 //
323 _handleRefEdge(
324 node.superclass.name.bestElement,
325 const <String>[schema.REF_EDGE],
326 syntacticEntity: node.superclass,
327 );
328 // TODO(jwren) refactor the following lines into a method that can be used
329 // by visitClassDeclaration()
330 // extends
331 var recordSupertypeVName = _vNameFromElement(
332 node.superclass.name.bestElement, schema.RECORD_KIND);
333 addEdge(_enclosingClassVName, schema.EXTENDS_EDGE, recordSupertypeVName);
334
335 // implements
336 var interfaces = _enclosingClassElement.interfaces;
337 for (var interface in interfaces) {
338 if (interface.element != null) {
339 var recordInterfaceVName =
340 _vNameFromElement(interface.element, schema.RECORD_KIND);
341 addEdge(
342 _enclosingClassVName, schema.EXTENDS_EDGE, recordInterfaceVName);
343 }
344 }
345
346 // mixins
347 var mixins = _enclosingClassElement.mixins;
348 for (var mixin in mixins) {
349 if (mixin.element != null) {
350 var recordMixinVName =
351 _vNameFromElement(mixin.element, schema.RECORD_KIND);
352 addEdge(_enclosingClassVName, schema.EXTENDS_EDGE, recordMixinVName);
353 }
354 }
355
356 // visit children
357 _safelyVisit(node.documentationComment);
358 _safelyVisitList(node.metadata);
359 _safelyVisit(node.typeParameters);
360 _safelyVisit(node.withClause);
361 _safelyVisit(node.implementsClause);
362 });
363 }
364
365 @override
366 visitCompilationUnit(CompilationUnit node) {
367 _enclosingFilePath = _getPath(node.element);
368 return _withEnclosingElement(node.element, () {
369 addFact(_enclosingFileVName, schema.NODE_KIND_FACT,
370 _encode(schema.FILE_KIND));
371 addFact(_enclosingFileVName, schema.TEXT_FACT, _encode(_contents));
372 addFact(_enclosingFileVName, schema.TEXT_ENCODING_FACT,
373 _encode(schema.DEFAULT_TEXT_ENCODING));
374
375 // handle LibraryDirective:
376
377 // A "package" VName in Kythe, schema.PACKAGE_KIND, is a Dart "library".
378
379 // Don't use visitLibraryDirective as this won't generate a package
380 // VName for libraries that don't have a library directive.
381 var libraryElement =
382 resolutionMap.elementDeclaredByCompilationUnit(node).library;
383 if (libraryElement.definingCompilationUnit == node.element) {
384 LibraryDirective libraryDirective;
385 for (var directive in node.directives) {
386 if (directive is LibraryDirective) {
387 libraryDirective = directive;
388 break;
389 }
390 }
391
392 var start = 0;
393 var end = 0;
394 if (libraryDirective != null) {
395 start = libraryDirective.name.offset;
396 end = libraryDirective.name.end;
397 }
398
399 // package node
400 var packageVName = addNodeAndFacts(schema.PACKAGE_KIND,
401 element: libraryElement, completeFact: schema.DEFINITION);
402
403 // anchor
404 addAnchorEdgesContainingEdge(
405 start: start,
406 end: end,
407 edges: [
408 schema.DEFINES_BINDING_EDGE,
409 ],
410 target: packageVName,
411 enclosingTarget: _enclosingFileVName);
412 }
413
414 super.visitCompilationUnit(node);
415 });
416 }
417
418 @override
419 visitConstructorDeclaration(ConstructorDeclaration node) {
420 return _withEnclosingElement(node.element, () {
421 // function/ constructor node
422 var constructorVName = addNodeAndFacts(schema.FUNCTION_KIND,
423 element: node.element,
424 subKind: schema.CONSTRUCTOR_SUBKIND,
425 completeFact: schema.DEFINITION);
426
427 // anchor
428 var start = node.returnType.offset;
429 var end = node.returnType.end;
430 if (node.name != null) {
431 end = node.name.end;
432 }
433 addAnchorEdgesContainingEdge(
434 start: start,
435 end: end,
436 edges: [
437 schema.DEFINES_BINDING_EDGE,
438 ],
439 target: constructorVName,
440 enclosingTarget: _enclosingClassVName);
441
442 // function type
443 addFunctionType(node.element, node.parameters, constructorVName,
444 returnNode: node.returnType);
445
446 // TODO(jwren) handle implicit constructor case
447 // TODO(jwren) handle redirected constructor case
448
449 // visit children
450 _safelyVisit(node.documentationComment);
451 _safelyVisitList(node.metadata);
452 _safelyVisit(node.parameters);
453 _safelyVisitList(node.initializers);
454 _safelyVisit(node.body);
455 });
456 }
457
458 @override
459 visitEnumConstantDeclaration(EnumConstantDeclaration node) {
460 // constant node
461 var constDeclVName =
462 addNodeAndFacts(schema.CONSTANT_KIND, element: node.element);
463
464 // anchor- defines/binding, defines
465 addAnchorEdgesContainingEdge(
466 syntacticEntity: node.name,
467 edges: [
468 schema.DEFINES_BINDING_EDGE,
469 schema.DEFINES_EDGE,
470 ],
471 target: constDeclVName,
472 enclosingTarget: _enclosingClassVName);
473
474 // no children
475 }
476
477 @override
478 visitEnumDeclaration(EnumDeclaration node) {
479 return _withEnclosingElement(node.element, () {
480 // record/ enum node
481 addNodeAndFacts(schema.RECORD_KIND,
482 nodeVName: _enclosingClassVName,
483 subKind: schema.ENUM_CLASS_SUBKIND,
484 completeFact: schema.DEFINITION);
485
486 // anchor- defines/binding
487 addAnchorEdgesContainingEdge(
488 syntacticEntity: node.name,
489 edges: [
490 schema.DEFINES_BINDING_EDGE,
491 ],
492 target: _enclosingClassVName,
493 enclosingTarget: _enclosingFileVName);
494
495 // anchor- defines
496 addAnchorEdgesContainingEdge(
497 syntacticEntity: node,
498 edges: [
499 schema.DEFINES_EDGE,
500 ],
501 target: _enclosingClassVName);
502
503 // visit children
504 _safelyVisitList(node.constants);
505 });
506 }
507
508 @override
509 visitFieldFormalParameter(FieldFormalParameter node) {
510 // identifier
511 // Specified as Element, not var, so that the type can be changed in the
512 // if-block.
513 Element element = node.element;
514 if (element is FieldFormalParameterElement) {
515 element = (element as FieldFormalParameterElement).field;
516 }
517 _handleRefEdge(
518 element,
519 const <String>[schema.REF_EDGE],
520 syntacticEntity: node.identifier,
521 );
522
523 // visit children
524 _safelyVisit(node.documentationComment);
525 _safelyVisitList(node.metadata);
526 _safelyVisit(node.type);
527 _safelyVisit(node.typeParameters);
528 _safelyVisit(node.parameters);
529 }
530
531 @override
532 visitFunctionDeclaration(FunctionDeclaration node) {
533 return _withEnclosingElement(node.element, () {
534 // function node
535 var functionVName = addNodeAndFacts(schema.FUNCTION_KIND,
536 element: node.element, completeFact: schema.DEFINITION);
537
538 // anchor- defines/binding
539 addAnchorEdgesContainingEdge(
540 syntacticEntity: node.name,
541 edges: [
542 schema.DEFINES_BINDING_EDGE,
543 ],
544 target: functionVName,
545 enclosingTarget: _enclosingFileVName);
546
547 // anchor- defines
548 addAnchorEdgesContainingEdge(
549 syntacticEntity: node,
550 edges: [
551 schema.DEFINES_EDGE,
552 ],
553 target: functionVName);
554
555 // function type
556 addFunctionType(
557 node.element, node.functionExpression.parameters, functionVName,
558 returnNode: node.returnType);
559
560 _safelyVisit(node.documentationComment);
561 _safelyVisitList(node.metadata);
562 _safelyVisit(node.returnType);
563 _safelyVisit(node.functionExpression);
564 });
565 }
566
567 @override
568 visitFunctionExpression(FunctionExpression node) {
569 return _withEnclosingElement(
570 node.element, () => super.visitFunctionExpression(node));
571 }
572
573 @override
574 visitFunctionTypeAlias(FunctionTypeAlias node) {
575 //
576 // return type
577 //
578 var returnType = node.returnType;
579 if (returnType is TypeName) {
580 _handleRefEdge(
581 returnType.name?.bestElement,
582 const <String>[schema.REF_EDGE],
583 syntacticEntity: returnType.name,
584 );
585 } else if (returnType is GenericFunctionType) {
586 // TODO(jwren): add support for generic function types.
587 throw new UnimplementedError();
588 } else if (returnType != null) {
589 throw new StateError(
590 'Unexpected TypeAnnotation subtype: ${returnType.runtimeType}');
591 }
592
593 // visit children
594 _safelyVisit(node.documentationComment);
595 _safelyVisitList(node.metadata);
596 _safelyVisit(node.typeParameters);
597 _safelyVisit(node.parameters);
598 }
599
600 @override
601 visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
602 // TODO(jwren) Missing graph coverage on FunctionTypedFormalParameters
603 // visit children
604 _safelyVisit(node.documentationComment);
605 _safelyVisitList(node.metadata);
606 _safelyVisit(node.identifier);
607 _safelyVisit(node.typeParameters);
608 _safelyVisit(node.parameters);
609 }
610
611 @override
612 visitImportDirective(ImportDirective node) {
613 // uri
614 _handleUriReference(node.uri, node.uriElement);
615
616 // prefix
617 var prefixIdentifier = node.prefix;
618
619 if (prefixIdentifier != null) {
620 // variable
621 var variableVName = addNodeAndFacts(schema.VARIABLE_KIND,
622 element: prefixIdentifier.staticElement,
623 completeFact: schema.DEFINITION);
624
625 // anchor
626 addAnchorEdgesContainingEdge(
627 syntacticEntity: prefixIdentifier,
628 edges: [schema.DEFINES_BINDING_EDGE],
629 target: variableVName,
630 enclosingTarget: _enclosingVName);
631 }
632
633 // visit children
634 _safelyVisit(node.documentationComment);
635 _safelyVisitList(node.metadata);
636 _safelyVisitList(node.combinators);
637 _safelyVisitList(node.configurations);
638 _safelyVisit(node.uri);
639 }
640
641 @override
642 visitIndexExpression(IndexExpression node) {
643 //
644 // index method ref/call
645 //
646 var element = node.bestElement;
647 var start = node.leftBracket.offset;
648 var end = node.rightBracket.end;
649
650 // anchor- ref/call
651 _handleRefCallEdge(element,
652 start: start, end: end, enclosingTarget: _enclosingVName);
653
654 // visit children
655 _safelyVisit(node.target);
656 _safelyVisit(node.index);
657 }
658
659 @override
660 visitInstanceCreationExpression(InstanceCreationExpression node) {
661 //
662 // constructorName
663 //
664 var constructorName = node.constructorName;
665 var constructorElement =
666 resolutionMap.staticElementForConstructorReference(constructorName);
667 if (constructorElement != null) {
668 // anchor- ref/call
669 _handleRefCallEdge(constructorElement,
670 syntacticEntity: constructorName, enclosingTarget: _enclosingVName);
671
672 // Now write out a ref edge from the same anchor (constructorName) to the
673 // enclosing class of the called constructor, this will make the
674 // invocation of a constructor discoverable when someone inquires about
675 // references to the class.
676 //
677 // We can't call _handleRefEdge as the anchor node has already been
678 // written out.
679 var enclosingEltVName = _vNameFromElement(
680 constructorElement.enclosingElement, schema.RECORD_KIND);
681 var anchorVName =
682 _vNameAnchor(constructorName.offset, constructorName.end);
683 addEdge(anchorVName, schema.REF_EDGE, enclosingEltVName);
684
685 // TODO(jwren): investigate
686 // assert (element.enclosingElement != null);
687 }
688 // visit children
689 _safelyVisitList(constructorName.type.typeArguments?.arguments);
690 _safelyVisit(node.argumentList);
691 }
692
693 @override
694 visitMethodDeclaration(MethodDeclaration node) {
695 return _withEnclosingElement(node.element, () {
696 // function node
697 var methodVName = addNodeAndFacts(schema.FUNCTION_KIND,
698 element: node.element, completeFact: schema.DEFINITION);
699
700 // anchor- defines/binding
701 addAnchorEdgesContainingEdge(
702 syntacticEntity: node.name,
703 edges: [
704 schema.DEFINES_BINDING_EDGE,
705 ],
706 target: methodVName,
707 enclosingTarget: _enclosingClassVName);
708
709 // anchor- defines
710 addAnchorEdgesContainingEdge(
711 syntacticEntity: node,
712 edges: [
713 schema.DEFINES_EDGE,
714 ],
715 target: methodVName);
716
717 // function type
718 addFunctionType(node.element, node.parameters, methodVName,
719 returnNode: node.returnType);
720
721 // override edges
722 List<ExecutableElement> overriddenList =
723 _inheritanceManager.lookupOverrides(_enclosingClassElement,
724 resolutionMap.elementDeclaredByMethodDeclaration(node).name);
725 for (ExecutableElement overridden in overriddenList) {
726 if (overridden is MultiplyInheritedExecutableElement) {
727 for (ExecutableElement elt in overridden.inheritedElements) {
728 addEdge(methodVName, schema.OVERRIDES_EDGE,
729 _vNameFromElement(elt, schema.FUNCTION_KIND));
730 }
731 } else {
732 addEdge(methodVName, schema.OVERRIDES_EDGE,
733 _vNameFromElement(overridden, schema.FUNCTION_KIND));
734 }
735 }
736
737 // visit children
738 _safelyVisit(node.documentationComment);
739 _safelyVisitList(node.metadata);
740 _safelyVisit(node.returnType);
741 _safelyVisit(node.typeParameters);
742 _safelyVisit(node.parameters);
743 _safelyVisit(node.body);
744 });
745 }
746
747 @override
748 visitMethodInvocation(MethodInvocation node) {
749 var element = node.methodName?.bestElement;
750
751 // anchor- ref/call
752 _handleRefCallEdge(element, syntacticEntity: node.methodName);
753
754 // visit children
755 _safelyVisit(node.target);
756 _safelyVisit(node.typeArguments);
757 _safelyVisit(node.argumentList);
758 }
759
760 @override
761 visitSimpleFormalParameter(SimpleFormalParameter node) {
762 // parameter node
763 var paramVName = addNodeAndFacts(schema.VARIABLE_KIND,
764 element: node.element,
765 subKind: schema.LOCAL_PARAMETER_SUBKIND,
766 completeFact: schema.DEFINITION);
767
768 // The anchor and anchor edges generation are broken into two cases, the
769 // first case is "method(parameter_name) ...", where the the parameter
770 // character range only includes a parameter name. The second case is for
771 // parameter declarations which are prefixed with a type, 'var', or
772 // 'dynamic', as in "method(var parameter_name) ...".
773 //
774 // With the first case a single anchor range is created, for the second
775 // case an anchor is created on parameter_name, as well as the range
776 // including any prefixes.
777 if (node.offset == node.identifier.offset &&
778 node.length == node.identifier.length) {
779 // anchor- defines/binding, defines
780 addAnchorEdgesContainingEdge(
781 syntacticEntity: node.identifier,
782 edges: [
783 schema.DEFINES_BINDING_EDGE,
784 schema.DEFINES_EDGE,
785 ],
786 target: paramVName,
787 enclosingTarget: _enclosingVName);
788 } else {
789 // anchor- defines/binding
790 addAnchorEdgesContainingEdge(
791 syntacticEntity: node.identifier,
792 edges: [
793 schema.DEFINES_BINDING_EDGE,
794 ],
795 target: paramVName,
796 enclosingTarget: _enclosingVName);
797
798 // anchor- defines
799 addAnchorEdgesContainingEdge(
800 syntacticEntity: node,
801 edges: [
802 schema.DEFINES_EDGE,
803 ],
804 target: paramVName);
805 }
806
807 // type
808 addEdge(
809 paramVName,
810 schema.TYPED_EDGE,
811 _vNameFromType(
812 resolutionMap.elementDeclaredByFormalParameter(node).type));
813
814 // visit children
815 _safelyVisit(node.documentationComment);
816 _safelyVisitList(node.metadata);
817 _safelyVisit(node.type);
818 }
819
820 @override
821 visitSimpleIdentifier(SimpleIdentifier node) {
822 // Most simple identifiers are "ref" edges. In cases some cases, there may
823 // be other ref/* edges.
824
825 if (node.getAncestor((node) => node is CommentReference) != null) {
826 // The identifier is in a comment, add just the "ref" edge.
827 _handleRefEdge(
828 node.bestElement,
829 const <String>[schema.REF_EDGE],
830 syntacticEntity: node,
831 );
832 } else if (node.inDeclarationContext()) {
833 // The node is in a declaration context, and should have
834 // "ref/defines/binding" edge as well as the default "ref" edge.
835 _handleRefEdge(
836 node.bestElement,
837 const <String>[schema.DEFINES_BINDING_EDGE, schema.REF_EDGE],
838 syntacticEntity: node,
839 );
840 } else {
841 _handleRefCallEdge(node.bestElement, syntacticEntity: node);
842 }
843
844 // no children to visit
845 }
846
847 @override
848 visitSuperExpression(SuperExpression node) {
849 _handleThisOrSuper(node);
850 }
851
852 @override
853 visitThisExpression(ThisExpression node) {
854 _handleThisOrSuper(node);
855 }
856
857 @override
858 visitUriBasedDirective(UriBasedDirective node) {
859 _handleUriReference(node.uri, node.uriElement);
860
861 // visit children
862 super.visitUriBasedDirective(node);
863 }
864
865 @override
866 visitVariableDeclaration(VariableDeclaration node) {
867 // level variable
868 var isLocal = _enclosingVName != _enclosingClassVName &&
869 _enclosingVName != _enclosingFileVName;
870
871 // variable
872 var variableVName = addNodeAndFacts(schema.VARIABLE_KIND,
873 element: node.element,
874 subKind: isLocal ? schema.LOCAL_SUBKIND : schema.FIELD_SUBKIND,
875 completeFact: schema.DEFINITION);
876
877 // anchor
878 addAnchorEdgesContainingEdge(
879 syntacticEntity: node.name,
880 edges: [
881 schema.DEFINES_BINDING_EDGE,
882 ],
883 target: variableVName,
884 enclosingTarget: _enclosingVName);
885
886 // type
887 addEdge(
888 variableVName,
889 schema.TYPED_EDGE,
890 _vNameFromType(
891 resolutionMap.elementDeclaredByVariableDeclaration(node).type));
892
893 // visit children
894 _safelyVisit(node.initializer);
895 }
896
897 Element _findNonSyntheticElement(Element element) {
898 if (element == null || !element.isSynthetic) {
899 return element;
900 }
901 if (element is PropertyAccessorElement) {
902 if (!element.variable.isSynthetic) {
903 return element.variable;
904 } else if (element.correspondingGetter != null &&
905 !element.correspondingGetter.isSynthetic) {
906 return element.correspondingGetter;
907 } else if (element.correspondingSetter != null &&
908 !element.correspondingSetter.isSynthetic) {
909 return element.correspondingSetter;
910 }
911 }
912 return null;
913 }
914
915 String _getNodeKind(Element e) {
916 if (e is FieldElement && e.isEnumConstant) {
917 // FieldElement is a kind of VariableElement, so this test case must be
918 // before the e is VariableElement check.
919 return schema.CONSTANT_KIND;
920 } else if (e is VariableElement || e is PrefixElement) {
921 return schema.VARIABLE_KIND;
922 } else if (e is ExecutableElement) {
923 return schema.FUNCTION_KIND;
924 } else if (e is ClassElement || e is TypeParameterElement) {
925 // TODO(jwren): this should be using absvar instead, see
926 // https://kythe.io/docs/schema/#absvar
927 return schema.RECORD_KIND;
928 }
929 return null;
930 }
931
932 _handleRefCallEdge(
933 Element element, {
934 SyntacticEntity syntacticEntity: null,
935 start: _notFound,
936 end: _notFound,
937 pb_VName enclosingTarget: null,
938 }) {
939 if (element is ExecutableElement &&
940 _enclosingVName != _enclosingFileVName) {
941 _handleRefEdge(
942 element,
943 const <String>[schema.REF_CALL_EDGE, schema.REF_EDGE],
944 syntacticEntity: syntacticEntity,
945 start: start,
946 end: end,
947 enclosingTarget: enclosingTarget,
948 enclosingAnchor: _enclosingVName,
949 );
950 } else {
951 _handleRefEdge(
952 element,
953 const <String>[schema.REF_EDGE],
954 syntacticEntity: syntacticEntity,
955 start: start,
956 end: end,
957 enclosingTarget: enclosingTarget,
958 );
959 }
960 }
961
962 /// This is a convenience method for adding ref edges. If the [start] and
963 /// [end] offsets are provided, they are used, otherwise the offsets are
964 /// computed by using the [syntacticEntity].
965 /// The list of edges is assumed to be non-empty, and are added from the
966 /// anchor to the target generated using the passed [Element].
967 /// The created [pb_VName] is returned, if not `null` is returned.
968 pb_VName _handleRefEdge(
969 Element element,
970 List<String> refEdgeTypes, {
971 SyntacticEntity syntacticEntity: null,
972 start: _notFound,
973 end: _notFound,
974 pb_VName enclosingTarget: null,
975 pb_VName enclosingAnchor: null,
976 }) {
977 assert(refEdgeTypes.isNotEmpty);
978 element = _findNonSyntheticElement(element);
979 if (element == null) {
980 return null;
981 }
982
983 // vname
984 var nodeKind = _getNodeKind(element);
985 if (nodeKind == null || nodeKind.isEmpty) {
986 return null;
987 }
988 var vName = _vNameFromElement(element, nodeKind);
989 assert(vName != null);
990
991 // anchor
992 addAnchorEdgesContainingEdge(
993 start: start,
994 end: end,
995 syntacticEntity: syntacticEntity,
996 edges: refEdgeTypes,
997 target: vName,
998 enclosingTarget: enclosingTarget,
999 enclosingAnchor: enclosingAnchor,
1000 );
1001
1002 return vName;
1003 }
1004
1005 void _handleThisOrSuper(Expression thisOrSuperNode) {
1006 DartType type = thisOrSuperNode.staticType;
1007 if (type != null && type.element != null) {
1008 // Expected SuperExpression.staticType to return the type of the
1009 // supertype, but it returns the type of the enclosing class (same as
1010 // ThisExpression), do some additional work to correct assumption:
1011 if (thisOrSuperNode is SuperExpression && type.element is ClassElement) {
1012 DartType supertype = (type.element as ClassElement).supertype;
1013 if (supertype != null) {
1014 type = supertype;
1015 }
1016 }
1017 // vname
1018 var vName = _vNameFromElement(type.element, schema.RECORD_KIND);
1019
1020 // anchor
1021 var anchorVName = addAnchorEdgesContainingEdge(
1022 syntacticEntity: thisOrSuperNode,
1023 edges: [schema.REF_EDGE],
1024 target: vName);
1025
1026 // childof from the anchor
1027 addEdge(anchorVName, schema.CHILD_OF_EDGE, _enclosingVName);
1028 }
1029
1030 // no children to visit
1031 }
1032
1033 /// Add a "ref/imports" edge from the passed [uriNode] location to the
1034 /// [referencedElement] [Element]. If the passed element is null, the edge is
1035 /// not written out.
1036 void _handleUriReference(StringLiteral uriNode, Element referencedElement) {
1037 if (referencedElement != null) {
1038 var start = uriNode.offset;
1039 var end = uriNode.end;
1040
1041 // The following is the expected and common case.
1042 // The contents between the quotes is used as the location to work well
1043 // with CodeSearch.
1044 if (uriNode is SimpleStringLiteral) {
1045 start = uriNode.contentsOffset;
1046 end = uriNode.contentsEnd;
1047 }
1048
1049 // package node
1050 var packageVName =
1051 _vNameFromElement(referencedElement, schema.PACKAGE_KIND);
1052
1053 // anchor
1054 addAnchorEdgesContainingEdge(
1055 start: start,
1056 end: end,
1057 edges: [schema.REF_IMPORTS_EDGE],
1058 target: packageVName,
1059 enclosingTarget: _enclosingFileVName);
1060 }
1061 }
1062
1063 _handleVariableDeclarationListAnnotations(
1064 VariableDeclarationList variableDeclarationList, pb_VName refVName) {
1065 assert(refVName != null);
1066 for (var varDecl in variableDeclarationList.variables) {
1067 if (varDecl.element != null) {
1068 var parentVName =
1069 _vNameFromElement(varDecl.element, schema.VARIABLE_KIND);
1070 addEdge(parentVName, schema.ANNOTATED_BY_EDGE, refVName);
1071 } else {
1072 // The element out of the VarDeclarationList is null
1073 assert(false);
1074 }
1075 }
1076 }
1077
1078 /// If the given [node] is not `null`, accept this visitor.
1079 void _safelyVisit(AstNode node) {
1080 if (node != null) {
1081 node.accept(this);
1082 }
1083 }
1084
1085 /// If the given [nodeList] is not `null`, accept this visitor.
1086 void _safelyVisitList(NodeList nodeList) {
1087 if (nodeList != null) {
1088 nodeList.accept(this);
1089 }
1090 }
1091
1092 _withEnclosingElement(Element element, f()) {
1093 Element outerEnclosingElement = _enclosingElement;
1094 Element outerEnclosingClassElement = _enclosingClassElement;
1095 var outerEnclosingVName = _enclosingVName;
1096 var outerEnclosingClassVName = _enclosingClassVName;
1097 try {
1098 _enclosingElement = element;
1099 if (element is CompilationUnitElement) {
1100 _enclosingFileVName = _enclosingVName = _vNameFile();
1101 } else if (element is ClassElement) {
1102 _enclosingClassElement = element;
1103 _enclosingClassVName = _enclosingVName =
1104 _vNameFromElement(_enclosingClassElement, schema.RECORD_KIND);
1105 } else if (element is MethodElement ||
1106 element is FunctionElement ||
1107 element is ConstructorElement) {
1108 _enclosingVName =
1109 _vNameFromElement(_enclosingElement, schema.FUNCTION_KIND);
1110 }
1111 return f();
1112 } finally {
1113 _enclosingElement = outerEnclosingElement;
1114 _enclosingClassElement = outerEnclosingClassElement;
1115 _enclosingClassVName = outerEnclosingClassVName;
1116 _enclosingVName = outerEnclosingVName;
1117 }
1118 }
1119 }
1120
1121 /// This class is meant to be a mixin to concrete visitor methods to walk the
1122 /// [Element] or [AstNode]s produced by the Dart Analyzer to output Kythe
1123 /// [pb_Entry] protos.
1124 abstract class OutputUtils {
1125 /// A set of [String]s which have already had a name [pb_VName] created.
1126 final Set<String> nameNodes = new Set<String>();
1127 String get corpus;
1128 pb_VName get dynamicBuiltin => _vName(schema.DYNAMIC_KIND, '', '', '');
1129
1130 String get enclosingFilePath;
1131
1132 List<pb_Entry> get entries;
1133 pb_VName get fnBuiltin => _vName(schema.FN_BUILTIN, '', '', '');
1134 pb_VName get voidBuiltin => _vName(schema.VOID_BUILTIN, '', '', '');
1135
1136 /// This is a convenience method for adding anchors. If the [start] and [end]
1137 /// offsets are provided, they are used, otherwise the offsets are computed by
1138 /// using the [syntacticEntity]. If a non-empty list of edges is provided, as
1139 /// well as a target, then this method also adds the edges from the anchor to
1140 /// target. The anchor [pb_VName] is returned.
1141 ///
1142 /// If a [target] and [enclosingTarget] are provided, a childof edge is
1143 /// written out from the target to the enclosing target.
1144 ///
1145 /// If an [enclosingAnchor] is provided a childof edge is written out from the
1146 /// anchor to the enclosing anchor. In cases where ref/call is an edge, this
1147 /// is required to generate the callgraph.
1148 ///
1149 /// Finally, for all anchors, a childof edge with a target of the enclosing
1150 /// file is written out.
1151 pb_VName addAnchorEdgesContainingEdge({
1152 SyntacticEntity syntacticEntity: null,
1153 int start: _notFound,
1154 int end: _notFound,
1155 List<String> edges: const [],
1156 pb_VName target: null,
1157 pb_VName enclosingTarget: null,
1158 pb_VName enclosingAnchor: null,
1159 }) {
1160 if (start == _notFound && end == _notFound) {
1161 if (syntacticEntity != null) {
1162 start = syntacticEntity.offset;
1163 end = syntacticEntity.end;
1164 } else {
1165 throw new Exception('Offset positions were not provided when calling '
1166 'addAnchorEdgesContainingEdge');
1167 }
1168 }
1169 // TODO(jwren) investigate
1170 // assert(start < end);
1171 var anchorVName = _vNameAnchor(start, end);
1172 addFact(anchorVName, schema.NODE_KIND_FACT, _encode(schema.ANCHOR_KIND));
1173 addFact(anchorVName, schema.ANCHOR_START_FACT, _encodeInt(start));
1174 addFact(anchorVName, schema.ANCHOR_END_FACT, _encodeInt(end));
1175 if (target != null) {
1176 for (String edge in edges) {
1177 addEdge(anchorVName, edge, target);
1178 }
1179 if (enclosingTarget != null) {
1180 addEdge(target, schema.CHILD_OF_EDGE, enclosingTarget);
1181 }
1182 }
1183 // If provided, write out the childof edge to the enclosing anchor
1184 if (enclosingAnchor != null) {
1185 addEdge(anchorVName, schema.CHILD_OF_EDGE, enclosingAnchor);
1186 }
1187
1188 // Assert that if ref/call is one of the edges, that and enclosing anchor
1189 // was provided for the callgraph.
1190 // Documentation at http://kythe.io/docs/schema/callgraph.html
1191 if (edges.contains(schema.REF_CALL_EDGE)) {
1192 assert(enclosingAnchor != null);
1193 }
1194
1195 // Finally add the childof edge to the enclosing file VName.
1196 addEdge(anchorVName, schema.CHILD_OF_EDGE, _vNameFile());
1197 return anchorVName;
1198 }
1199
1200 /// TODO(jwren): for cases where the target is a name, we need the same kind
1201 /// of logic as [addNameFact] to prevent the edge from being written out.
1202 /// This is a convenience method for visitors to add an edge Entry.
1203 pb_Entry addEdge(pb_VName source, String edgeKind, pb_VName target,
1204 {int ordinalIntValue: _notFound}) {
1205 if (ordinalIntValue == _notFound) {
1206 return addEntry(source, edgeKind, target, "/", new List<int>());
1207 } else {
1208 return addEntry(source, edgeKind, target, schema.ORDINAL,
1209 _encodeInt(ordinalIntValue));
1210 }
1211 }
1212
1213 pb_Entry addEntry(pb_VName source, String edgeKind, pb_VName target,
1214 String factName, List<int> factValue) {
1215 assert(source != null);
1216 assert(factName != null);
1217 assert(factValue != null);
1218 // factValue may be an empty array, the fact may be that a file text or
1219 // document text is empty
1220 var entry = pb_Entry.create()
1221 ..source = source
1222 ..factName = factName
1223 ..factValue = factValue;
1224 if (edgeKind != null && edgeKind.isNotEmpty) {
1225 entry.edgeKind = edgeKind;
1226 entry.target = target;
1227 }
1228 entries.add(entry);
1229 return entry;
1230 }
1231
1232 /// This is a convenience method for visitors to add a fact [pb_Entry].
1233 pb_Entry addFact(pb_VName source, String factName, List<int> factValue) {
1234 return addEntry(source, null, null, factName, factValue);
1235 }
1236
1237 /// This is a convenience method for adding function types.
1238 pb_VName addFunctionType(
1239 Element functionElement,
1240 FormalParameterList paramNodes,
1241 pb_VName functionVName, {
1242 AstNode returnNode: null,
1243 }) {
1244 var i = 0;
1245 var funcTypeVName =
1246 addNodeAndFacts(schema.TAPP_KIND, element: functionElement);
1247 addEdge(funcTypeVName, schema.PARAM_EDGE, fnBuiltin, ordinalIntValue: i++);
1248
1249 var returnTypeVName;
1250 if (returnNode is TypeName) {
1251 // MethodDeclaration and FunctionDeclaration both return a TypeName from
1252 // returnType
1253 if (resolutionMap.typeForTypeName(returnNode).isVoid) {
1254 returnTypeVName = voidBuiltin;
1255 } else {
1256 returnTypeVName =
1257 _vNameFromElement(returnNode.name.bestElement, schema.TAPP_KIND);
1258 }
1259 } else if (returnNode is Identifier) {
1260 // ConstructorDeclaration returns an Identifier from returnType
1261 if (resolutionMap.bestTypeForExpression(returnNode).isVoid) {
1262 returnTypeVName = voidBuiltin;
1263 } else {
1264 returnTypeVName =
1265 _vNameFromElement(returnNode.bestElement, schema.TAPP_KIND);
1266 }
1267 }
1268 // else: return type is null, void, unresolved.
1269
1270 if (returnTypeVName != null) {
1271 addEdge(funcTypeVName, schema.PARAM_EDGE, returnTypeVName,
1272 ordinalIntValue: i++);
1273 }
1274
1275 if (paramNodes != null) {
1276 for (FormalParameter paramNode in paramNodes.parameters) {
1277 var paramTypeVName = dynamicBuiltin;
1278 if (!resolutionMap
1279 .elementDeclaredByFormalParameter(paramNode)
1280 .type
1281 .isDynamic) {
1282 paramTypeVName = _vNameFromElement(
1283 resolutionMap
1284 .elementDeclaredByFormalParameter(paramNode)
1285 .type
1286 .element,
1287 schema.TAPP_KIND);
1288 }
1289 addEdge(funcTypeVName, schema.PARAM_EDGE, paramTypeVName,
1290 ordinalIntValue: i++);
1291 }
1292 }
1293 addEdge(functionVName, schema.TYPED_EDGE, funcTypeVName);
1294 return funcTypeVName;
1295 }
1296
1297 /// This is a convenience method for adding nodes with facts.
1298 /// If an [pb_VName] is passed, it is used, otherwise an element is required
1299 /// which is used to create a [pb_VName]. Either [nodeVName] must be non-null or
1300 /// [element] must be non-null. Other optional parameters if passed are then
1301 /// used to set the associated facts on the [pb_VName]. This method does not
1302 /// currently guarantee that the inputs to these fact kinds are valid for the
1303 /// associated nodeKind- if a non-null, then it will set.
1304 pb_VName addNodeAndFacts(String nodeKind,
1305 {Element element: null,
1306 pb_VName nodeVName: null,
1307 String subKind: null,
1308 String completeFact: null}) {
1309 if (nodeVName == null) {
1310 nodeVName = _vNameFromElement(element, nodeKind);
1311 }
1312 addFact(nodeVName, schema.NODE_KIND_FACT, _encode(nodeKind));
1313 if (subKind != null) {
1314 addFact(nodeVName, schema.SUBKIND_FACT, _encode(subKind));
1315 }
1316 if (completeFact != null) {
1317 addFact(nodeVName, schema.COMPLETE_FACT, _encode(completeFact));
1318 }
1319 return nodeVName;
1320 }
1321
1322 List<int> _encode(String str) {
1323 return UTF8.encode(str);
1324 }
1325
1326 List<int> _encodeInt(int i) {
1327 return UTF8.encode(i.toString());
1328 }
1329
1330 /// Given all parameters for a [pb_VName] this method creates and returns a
1331 /// [pb_VName].
1332 pb_VName _vName(String signature, String corpus, String root, String path,
1333 [String language = schema.DART_LANG]) {
1334 return pb_VName.create()
1335 ..signature = signature
1336 ..corpus = corpus
1337 ..root = root
1338 ..path = path
1339 ..language = language;
1340 }
1341
1342 /// Returns an anchor [pb_VName] corresponding to the given start and end
1343 /// offsets.
1344 pb_VName _vNameAnchor(int start, int end) {
1345 return _vName(
1346 _getAnchorSignature(start, end), corpus, '', enclosingFilePath);
1347 }
1348
1349 /// Return the [pb_VName] for this file.
1350 pb_VName _vNameFile() {
1351 // file vnames, the signature and language are not set
1352 return _vName('', corpus, '', enclosingFilePath, '');
1353 }
1354
1355 /// Given some [Element] and Kythe node kind, this method generates and
1356 /// returns the [pb_VName].
1357 pb_VName _vNameFromElement(Element e, String nodeKind) {
1358 assert(nodeKind != schema.FILE_KIND);
1359 // general case
1360 return _vName(_getSignature(e, nodeKind, corpus), corpus, '', _getPath(e));
1361 }
1362
1363 /// Returns a [pb_VName] corresponding to the given [DartType].
1364 pb_VName _vNameFromType(DartType type) {
1365 if (type == null || type.isDynamic) {
1366 return dynamicBuiltin;
1367 } else if (type.isVoid) {
1368 return voidBuiltin;
1369 } else if (type.element is ClassElement) {
1370 return _vNameFromElement(type.element, schema.RECORD_KIND);
1371 } else {
1372 return dynamicBuiltin;
1373 }
1374 }
1375 }
1376
1377 class CodedBufferWriter {
1378 CodedBufferWriter(var v);
1379 toBuffer() {}
1380 }
1381
1382 class pb_Entry {
1383 var source, edgeKind, target, factName, factValue;
1384 static pb_Entry create() => new pb_Entry();
1385 writeToBuffer() {}
1386 }
1387
1388 class pb_VName {
1389 var signature, corpus, root, path, language;
1390 static pb_VName create() => new pb_VName();
1391 }
1392
1393 /// This visitor class should be used by [_getSignature].
1394 ///
1395 /// This visitor is an [GeneralizingElementVisitor] which builds up a [String]
1396 /// signature for a given [Element], uniqueness is guaranteed within the
1397 /// enclosing file.
1398 class SignatureElementVisitor extends GeneralizingElementVisitor<StringBuffer> {
1399 static SignatureElementVisitor instance = new SignatureElementVisitor();
1400
1401 @override
1402 StringBuffer visitCompilationUnitElement(CompilationUnitElement e) {
1403 return new StringBuffer();
1404 }
1405
1406 @override
1407 StringBuffer visitElement(Element e) {
1408 assert(e is! MultiplyInheritedExecutableElement);
1409 var enclosingElt = e.enclosingElement;
1410 var buffer = enclosingElt.accept(this);
1411 if (buffer.isNotEmpty) {
1412 buffer.write('#');
1413 }
1414 if (e is MethodElement && e.name == '-' && e.parameters.length == 1) {
1415 buffer.write('unary-');
1416 } else if (e is ConstructorElement) {
1417 buffer.write(_computeConstructorElementName(e));
1418 } else {
1419 buffer.write(e.name);
1420 }
1421 if (enclosingElt is ExecutableElement) {
1422 buffer..write('@')..write(e.nameOffset - enclosingElt.nameOffset);
1423 }
1424 return buffer;
1425 }
1426
1427 @override
1428 StringBuffer visitLibraryElement(LibraryElement e) {
1429 return new StringBuffer('library:${e.displayName}');
1430 }
1431
1432 @override
1433 StringBuffer visitTypeParameterElement(TypeParameterElement e) {
1434 // It is legal to have a named constructor with the same name as a type
1435 // parameter. So we distinguish them by using '.' between the class (or
1436 // typedef) name and the type parameter name.
1437 return e.enclosingElement.accept(this)..write('.')..write(e.name);
1438 }
1439 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/lib/src/services/kythe/schema.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698