Skip to content

Commit 3375875

Browse files
authored
Use analyzer line number library (#2034)
* Fix line number cache and add tests * dartfmt * remove accidental minus 0 * Review comments and lint fixup for modern Dart * Change approach -- delete dartdoc's line number cache implementation * Cleanups, add more tests * Check for synthetic constructors instead of default * Add testing for a completely empty library file. * Review comments
1 parent 451c718 commit 3375875

10 files changed

+151
-96
lines changed

lib/src/line_number_cache.dart

-57
This file was deleted.

lib/src/markdown_processor.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -771,8 +771,11 @@ String _linkDocReference(String codeRef, Warnable warnable,
771771
}
772772
} else {
773773
if (result.warn) {
774+
// Avoid claiming documentation is inherited when it comes from the
775+
// current element.
774776
warnable.warn(PackageWarning.unresolvedDocReference,
775-
message: codeRef, referredFrom: warnable.documentationFrom);
777+
message: codeRef,
778+
referredFrom: warnable.documentationIsLocal ? null : warnable.documentationFrom);
776779
}
777780
return '<code>${htmlEscape.convert(codeRef)}</code>';
778781
}

lib/src/model.dart

+75-28
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import 'package:analyzer/dart/element/type.dart';
2525
import 'package:analyzer/dart/element/visitor.dart';
2626
import 'package:analyzer/file_system/file_system.dart' as file_system;
2727
import 'package:analyzer/file_system/physical_file_system.dart';
28+
import 'package:analyzer/source/line_info.dart';
2829
import 'package:analyzer/src/context/builder.dart';
2930
import 'package:analyzer/src/dart/analysis/byte_store.dart';
3031
import 'package:analyzer/src/dart/analysis/driver.dart';
@@ -50,7 +51,6 @@ import 'package:crypto/crypto.dart';
5051
import 'package:dartdoc/src/dartdoc_options.dart';
5152
import 'package:dartdoc/src/element_type.dart';
5253
import 'package:dartdoc/src/io_utils.dart';
53-
import 'package:dartdoc/src/line_number_cache.dart';
5454
import 'package:dartdoc/src/logging.dart';
5555
import 'package:dartdoc/src/markdown_processor.dart' show Documentation;
5656
import 'package:dartdoc/src/model_utils.dart' as utils;
@@ -1390,6 +1390,17 @@ class Constructor extends ModelElement
13901390
ConstructorElement element, Library library, PackageGraph packageGraph)
13911391
: super(element, library, packageGraph, null);
13921392

1393+
@override
1394+
CharacterLocation get characterLocation {
1395+
if (element.isSynthetic) {
1396+
// Make warnings for a synthetic constructor refer to somewhere reasonable
1397+
// since a synthetic constructor has no definition independent of the
1398+
// parent class.
1399+
return enclosingElement.characterLocation;
1400+
}
1401+
return super.characterLocation;
1402+
}
1403+
13931404
@override
13941405
// TODO(jcollins-g): Revisit this when dart-lang/sdk#31517 is implemented.
13951406
List<TypeParameter> get typeParameters =>
@@ -2057,7 +2068,7 @@ class Field extends ModelElement
20572068
}
20582069

20592070
/// Mixin for top-level variables and fields (aka properties)
2060-
abstract class GetterSetterCombo implements ModelElement {
2071+
mixin GetterSetterCombo on ModelElement {
20612072
Accessor get getter;
20622073

20632074
Iterable<Accessor> get allAccessors sync* {
@@ -2114,6 +2125,20 @@ abstract class GetterSetterCombo implements ModelElement {
21142125
return const HtmlEscape(HtmlEscapeMode.unknown).convert(result);
21152126
}
21162127

2128+
@override
2129+
CharacterLocation get characterLocation {
2130+
// Handle all synthetic possibilities. Ordinarily, warnings for
2131+
// explicit setters/getters will be handled by those objects, but
2132+
// if a warning comes up for an enclosing synthetic field we have to
2133+
// put it somewhere. So pick an accessor.
2134+
if (element.isSynthetic) {
2135+
if (hasExplicitGetter) return getter.characterLocation;
2136+
if (hasExplicitSetter) return setter.characterLocation;
2137+
assert(false, 'Field and accessors can not all be synthetic');
2138+
}
2139+
return super.characterLocation;
2140+
}
2141+
21172142
String get constantValue => linkifyConstantValue(constantValueBase);
21182143

21192144
String get constantValueTruncated =>
@@ -2367,6 +2392,18 @@ class Library extends ModelElement with Categorization, TopLevelContainer {
23672392

23682393
List<Class> get allClasses => _allClasses;
23692394

2395+
@override
2396+
CharacterLocation get characterLocation {
2397+
if (element.nameOffset == -1) {
2398+
assert(isAnonymous, 'Only anonymous libraries are allowed to have no declared location');
2399+
return CharacterLocation(1, 1);
2400+
}
2401+
return super.characterLocation;
2402+
}
2403+
2404+
@override
2405+
CompilationUnitElement get compilationUnitElement => (element as LibraryElement).definingCompilationUnit;
2406+
23702407
@override
23712408
Iterable<Class> get classes {
23722409
return _allClasses
@@ -2917,6 +2954,19 @@ class Method extends ModelElement
29172954
}).toList();
29182955
}
29192956

2957+
@override
2958+
CharacterLocation get characterLocation {
2959+
if (enclosingElement is Enum && name == 'toString') {
2960+
// The toString() method on Enums is special, treated as not having
2961+
// a definition location by the analyzer yet not being inherited, either.
2962+
// Just directly override our location with the Enum definition --
2963+
// this is OK because Enums can not inherit from each other nor
2964+
// have their definitions split between files.
2965+
return enclosingElement.characterLocation;
2966+
}
2967+
return super.characterLocation;
2968+
}
2969+
29202970
@override
29212971
ModelElement get enclosingElement {
29222972
if (_enclosingClass == null) {
@@ -3078,7 +3128,7 @@ abstract class Privacy {
30783128
/// ModelElement will reference itself as part of the "wrong" [Library]
30793129
/// from the public interface perspective.
30803130
abstract class ModelElement extends Canonicalization
3081-
with Privacy, Warnable, Nameable, SourceCodeMixin, Indexable
3131+
with Privacy, Warnable, Locatable, Nameable, SourceCodeMixin, Indexable
30823132
implements Comparable, Documentable {
30833133
final Element _element;
30843134

@@ -3715,8 +3765,8 @@ abstract class ModelElement extends Canonicalization
37153765
@override
37163766
String get location {
37173767
// Call nothing from here that can emit warnings or you'll cause stack overflows.
3718-
if (lineAndColumn != null) {
3719-
return "(${path.toUri(sourceFileName)}:${lineAndColumn.item1}:${lineAndColumn.item2})";
3768+
if (characterLocation != null) {
3769+
return "(${path.toUri(sourceFileName)}:${characterLocation.toString()})";
37203770
}
37213771
return "(${path.toUri(sourceFileName)})";
37223772
}
@@ -3751,19 +3801,25 @@ abstract class ModelElement extends Canonicalization
37513801

37523802
String get sourceFileName => element.source.fullName;
37533803

3754-
Tuple2<int, int> _lineAndColumn;
3755-
bool _isLineNumberComputed = false;
3804+
CharacterLocation _characterLocation;
3805+
bool _characterLocationIsSet = false;
37563806

37573807
@override
3758-
Tuple2<int, int> get lineAndColumn {
3759-
// TODO(jcollins-g): implement lineAndColumn for explicit fields
3760-
if (!_isLineNumberComputed) {
3761-
_lineAndColumn = lineNumberCache.lineAndColumn(
3762-
element.source.fullName, element.nameOffset);
3808+
CharacterLocation get characterLocation {
3809+
if (!_characterLocationIsSet) {
3810+
LineInfo lineInfo = compilationUnitElement.lineInfo;
3811+
_characterLocationIsSet = true;
3812+
assert(element.nameOffset >= 0, 'Invalid location data for element: $fullyQualifiedName');
3813+
assert(lineInfo != null, 'No lineInfo data available for element: $fullyQualifiedName');
3814+
if (element.nameOffset >= 0) {
3815+
_characterLocation = lineInfo?.getLocation(element.nameOffset);
3816+
}
37633817
}
3764-
return _lineAndColumn;
3818+
return _characterLocation;
37653819
}
37663820

3821+
CompilationUnitElement get compilationUnitElement => element.getAncestor((e) => e is CompilationUnitElement);
3822+
37673823
bool get hasAnnotations => annotations.isNotEmpty;
37683824

37693825
@override
@@ -4191,8 +4247,8 @@ abstract class ModelElement extends Canonicalization
41914247
warn(PackageWarning.toolError, message: message),
41924248
content: basicMatch[2],
41934249
environment: {
4194-
'SOURCE_LINE': lineAndColumn?.item1?.toString(),
4195-
'SOURCE_COLUMN': lineAndColumn?.item2?.toString(),
4250+
'SOURCE_LINE': characterLocation?.lineNumber.toString(),
4251+
'SOURCE_COLUMN': characterLocation?.columnNumber.toString(),
41964252
'SOURCE_PATH': (sourceFileName == null ||
41974253
package?.packagePath == null)
41984254
? null
@@ -5153,13 +5209,6 @@ class PackageGraph {
51535209
warnable = warnable.enclosingElement;
51545210
}
51555211
}
5156-
if (warnable is Accessor) {
5157-
// This might be part of a Field, if so, assign this warning to the field
5158-
// rather than the Accessor.
5159-
if ((warnable as Accessor).enclosingCombo != null) {
5160-
warnable = (warnable as Accessor).enclosingCombo;
5161-
}
5162-
}
51635212
} else {
51645213
// If we don't have an element, we need a message to disambiguate.
51655214
assert(message != null);
@@ -5224,9 +5273,6 @@ class PackageGraph {
52245273
break;
52255274
case PackageWarning.unresolvedDocReference:
52265275
warningMessage = "unresolved doc reference [${message}]";
5227-
if (referredFrom == null) {
5228-
referredFrom = warnable.documentationFrom;
5229-
}
52305276
referredFromPrefix = 'in documentation inherited from';
52315277
break;
52325278
case PackageWarning.unknownMacro:
@@ -5273,14 +5319,14 @@ class PackageGraph {
52735319
List<String> messageParts = [warningMessage];
52745320
if (warnable != null) {
52755321
messageParts
5276-
.add("${warnablePrefix} ${warnableName}: ${warnable.location ?? ''}");
5322+
.add("$warnablePrefix $warnableName: $warnable.location");
52775323
}
52785324
if (referredFrom != null) {
52795325
for (Locatable referral in referredFrom) {
52805326
if (referral != warnable) {
52815327
var referredFromStrings = _safeWarnableName(referral);
52825328
messageParts.add(
5283-
"${referredFromPrefix} ${referredFromStrings}: ${referral.location ?? ''}");
5329+
"$referredFromPrefix $referredFromStrings: ${referral.location}");
52845330
}
52855331
}
52865332
}
@@ -5957,6 +6003,7 @@ abstract class MarkdownFileDocumentation
59576003
class Category extends Nameable
59586004
with
59596005
Warnable,
6006+
Locatable,
59606007
Canonicalization,
59616008
MarkdownFileDocumentation,
59626009
LibraryContainer,
@@ -6510,7 +6557,7 @@ class Parameter extends ModelElement implements EnclosedElement {
65106557
abstract class SourceCodeMixin implements Documentable {
65116558
ModelNode get modelNode;
65126559

6513-
Tuple2<int, int> get lineAndColumn;
6560+
CharacterLocation get characterLocation;
65146561

65156562
Element get element;
65166563

lib/src/source_linker.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class SourceLinker {
8888
excludes: config.linkToSourceExcludes,
8989
// TODO(jcollins-g): disallow defaulting? Some elements come back without
9090
// a line number right now.
91-
lineNumber: element.lineAndColumn?.item1 ?? 1,
91+
lineNumber: element.characterLocation?.lineNumber ?? 1,
9292
sourceFileName: element.sourceFileName,
9393
revision: config.linkToSourceRevision,
9494
root: config.linkToSourceRoot,

lib/src/warnings.dart

+3
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ abstract class Warnable implements Canonicalization {
208208
abstract class Locatable {
209209
List<Locatable> get documentationFrom;
210210

211+
/// True if documentationFrom contains only one item, [this].
212+
bool get documentationIsLocal => documentationFrom.length == 1 && identical(documentationFrom.first, this);
213+
211214
String get fullyQualifiedName;
212215

213216
String get href;

test/dartdoc_integration_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void main() {
5656
expect(outputLines, contains(matches('^ warning:')));
5757
expect(outputLines.last, matches(r'^found \d+ warnings and \d+ errors'));
5858
expect(outputDir.listSync(), isEmpty);
59-
});
59+
}, timeout: Timeout.factor(2));
6060

6161
test('running --quiet is quiet and does generate docs', () async {
6262
Directory outputDir =

test/dartdoc_test.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ void main() {
258258
expect(p.name, 'test_package');
259259
expect(p.hasDocumentationFile, isTrue);
260260
// Total number of public libraries in test_package.
261-
expect(packageGraph.defaultPackage.publicLibraries, hasLength(14));
261+
expect(packageGraph.defaultPackage.publicLibraries, hasLength(15));
262262
expect(packageGraph.localPackages.length, equals(1));
263263
});
264264

@@ -327,7 +327,7 @@ void main() {
327327
PackageGraph p = results.packageGraph;
328328
expect(p.defaultPackage.name, 'test_package');
329329
expect(p.defaultPackage.hasDocumentationFile, isTrue);
330-
expect(p.localPublicLibraries, hasLength(13));
330+
expect(p.localPublicLibraries, hasLength(14));
331331
expect(p.localPublicLibraries.map((lib) => lib.name).contains('fake'),
332332
isFalse);
333333
});

0 commit comments

Comments
 (0)