diff --git a/README.md b/README.md index 79302b4..4210103 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # scip-dart -Experimental spike on a [scip](https://github.com/sourcegraph/scip) indexer for [dart](https://github.com/dart-lang) +Implementation of a [scip](https://github.com/sourcegraph/scip) indexer for [dart](https://github.com/dart-lang) Designed to be a replacement for [lsif_indexer](https://github.com/Workiva/lsif_indexer), with better coverage and reliability diff --git a/lib/src/relationship_generator.dart b/lib/src/relationship_generator.dart new file mode 100644 index 0000000..65efd6b --- /dev/null +++ b/lib/src/relationship_generator.dart @@ -0,0 +1,53 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:scip_dart/src/gen/scip.pbserver.dart'; +import 'package:scip_dart/src/symbol_generator.dart'; + +List? relationshipsFor( + Declaration node, + Element element, + SymbolGenerator symbolGenerator, +) { + // Only classes have relationships. Mixins and enums do not support + // inheritance themselves and therefore do not have any relationships + // to define. + if (element is ClassElement) { + // All things inherit from Object, if they have one supertype + // there are no relationships + if (element.allSupertypes.length <= 1) return null; + + return element.allSupertypes + .where((type) => !type.isDartCoreObject) + .map((type) => Relationship( + symbol: symbolGenerator.symbolFor(type.element), + isImplementation: true, + )) + .toList(); + } + + // Since mixins do not support inheritance, we only care about + // methods that exist on classes + if (node is MethodDeclaration && node.parent is ClassDeclaration) { + final parentNode = node.parent as ClassDeclaration; + final parentElement = parentNode.declaredElement as ClassElement; + + // retrieve all of the methods and accessors of every parent type that + // has the same name of [node]. These are the elements that this [node] + // are overriding + final referencingElements = parentElement.allSupertypes + .map((type) => [...type.methods, ...type.accessors]) + .expand((type) => type) + .where((type) => type.name == node.name.toString()); + + if (referencingElements.isNotEmpty) { + return referencingElements + .map((type) => Relationship( + symbol: symbolGenerator.symbolFor(type), + isImplementation: true, + isReference: true)) + .toList(); + } + } + + return null; +} diff --git a/lib/src/scip_visitor.dart b/lib/src/scip_visitor.dart index fede0e0..9a5a8cc 100644 --- a/lib/src/scip_visitor.dart +++ b/lib/src/scip_visitor.dart @@ -6,7 +6,8 @@ import 'package:package_config/package_config.dart'; import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:scip_dart/src/metadata.dart'; import 'package:scip_dart/src/gen/scip.pb.dart'; -import 'package:scip_dart/src/symbol.dart'; +import 'package:scip_dart/src/relationship_generator.dart'; +import 'package:scip_dart/src/symbol_generator.dart'; import 'package:scip_dart/src/utils.dart'; List globalExternalSymbols = []; @@ -61,7 +62,10 @@ class ScipVisitor extends GeneralizingAstVisitor { if (node.declaredElement == null) return; final element = node.declaredElement!; - _registerAsDefinition(element); + _registerAsDefinition( + element, + relationships: relationshipsFor(node, element, _symbolGenerator), + ); } void _visitNormalFormalParameter(NormalFormalParameter node) { @@ -160,13 +164,17 @@ class ScipVisitor extends GeneralizingAstVisitor { /// /// This adds both a symbol, and an occurrence for the element and it's /// name - void _registerAsDefinition(Element element) { + void _registerAsDefinition( + Element element, { + List? relationships, + }) { final symbol = _symbolGenerator.symbolFor(element); if (symbol != null) { final meta = getSymbolMetadata(element); symbols.add(SymbolInformation( symbol: symbol, documentation: meta.documentation, + relationships: relationships, )); occurrences.add(Occurrence( diff --git a/lib/src/symbol.dart b/lib/src/symbol_generator.dart similarity index 99% rename from lib/src/symbol.dart rename to lib/src/symbol_generator.dart index 31dfaef..a57f4b3 100644 --- a/lib/src/symbol.dart +++ b/lib/src/symbol_generator.dart @@ -20,10 +20,7 @@ class SymbolGenerator { /// Use [] Map _localElementRegistry = {}; - SymbolGenerator( - this._packageConfig, - this._pubspec, - ); + SymbolGenerator(this._packageConfig, this._pubspec); /// For a given `Element` returns the scip symbol form. /// diff --git a/snapshots/input/basic-project/lib/relationships.dart b/snapshots/input/basic-project/lib/relationships.dart new file mode 100644 index 0000000..bd028ee --- /dev/null +++ b/snapshots/input/basic-project/lib/relationships.dart @@ -0,0 +1,19 @@ +abstract class Mammal { + String get hierarchy; +} + +abstract class Animal extends Mammal { + String sound() => 'NOISE!'; +} + +mixin SwimAction { + void execute() => print('swimming...'); +} + +class Dog extends Animal with SwimAction { + @override + String sound() => 'woof'; + + @override + String get hierarchy => 'dog.animal.mammal'; +} \ No newline at end of file diff --git a/snapshots/output/basic-project/lib/more.dart b/snapshots/output/basic-project/lib/more.dart index 94af943..28b9dbe 100755 --- a/snapshots/output/basic-project/lib/more.dart +++ b/snapshots/output/basic-project/lib/more.dart @@ -35,6 +35,7 @@ class Animal with SleepMixin { // ^^^^^^ definition scip-dart pub dart_test 1.0.0 lib/more.dart/Animal# // documentation ```dart +// relationship scip-dart pub dart_test 1.0.0 lib/more.dart/SleepMixin# implementation // ^^^^^^^^^^ reference scip-dart pub dart_test 1.0.0 lib/more.dart/SleepMixin# String name; // ^^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/string.dart/String# @@ -102,6 +103,7 @@ // ^^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/string.dart/String# // ^^^^^^^^ definition scip-dart pub dart_test 1.0.0 lib/more.dart/Animal#toString(). // documentation ```dart +// relationship scip-dart pub dart:core 2.18.0 dart:core/object.dart/Object#toString(). implementation reference return '$name the $type'; // ^^^^ reference scip-dart pub dart_test 1.0.0 lib/more.dart/Animal#name. // ^^^^ reference scip-dart pub dart_test 1.0.0 lib/more.dart/Animal#type. diff --git a/snapshots/output/basic-project/lib/relationships.dart b/snapshots/output/basic-project/lib/relationships.dart new file mode 100755 index 0000000..29b0f41 --- /dev/null +++ b/snapshots/output/basic-project/lib/relationships.dart @@ -0,0 +1,54 @@ + abstract class Mammal { +// definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/ +// ^^^^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/Mammal# +// documentation ```dart + String get hierarchy; +// ^^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/string.dart/String# +// ^^^^^^^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/Mammal#hierarchy. +// documentation ```dart + } + + abstract class Animal extends Mammal { +// ^^^^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/Animal# +// documentation ```dart +// relationship scip-dart pub dart_test 1.0.0 lib/relationships.dart/Mammal# implementation +// ^^^^^^ reference scip-dart pub dart_test 1.0.0 lib/relationships.dart/Mammal# + String sound() => 'NOISE!'; +// ^^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/string.dart/String# +// ^^^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/Animal#sound(). +// documentation ```dart + } + + mixin SwimAction { +// ^^^^^^^^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/SwimAction# +// documentation ```dart + void execute() => print('swimming...'); +// ^^^^^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/SwimAction#execute(). +// documentation ```dart +// ^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/print.dart/print(). + } + + class Dog extends Animal with SwimAction { +// ^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/Dog# +// documentation ```dart +// relationship scip-dart pub dart_test 1.0.0 lib/relationships.dart/Animal# implementation +// relationship scip-dart pub dart_test 1.0.0 lib/relationships.dart/Mammal# implementation +// relationship scip-dart pub dart_test 1.0.0 lib/relationships.dart/SwimAction# implementation +// ^^^^^^ reference scip-dart pub dart_test 1.0.0 lib/relationships.dart/Animal# +// ^^^^^^^^^^ reference scip-dart pub dart_test 1.0.0 lib/relationships.dart/SwimAction# + @override +// ^^^^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/annotations.dart/override. + String sound() => 'woof'; +// ^^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/string.dart/String# +// ^^^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/Dog#sound(). +// documentation ```dart +// relationship scip-dart pub dart_test 1.0.0 lib/relationships.dart/Animal#sound(). implementation reference + + @override +// ^^^^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/annotations.dart/override. + String get hierarchy => 'dog.animal.mammal'; +// ^^^^^^ reference scip-dart pub dart:core 2.18.0 dart:core/string.dart/String# +// ^^^^^^^^^ definition scip-dart pub dart_test 1.0.0 lib/relationships.dart/Dog#hierarchy. +// documentation ```dart +// relationship scip-dart pub dart_test 1.0.0 lib/relationships.dart/Mammal#hierarchy. implementation reference + }