From a36cabafe3449a84271924b70e5a4c806a143001 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 1 May 2017 09:51:19 -0700 Subject: [PATCH 01/12] Squash canonicalization overhaul part2 into single commit --- .idea/modules.xml | 1 + bin/dartdoc.dart | 30 +- dartdoc.iml | 4 +- lib/dartdoc.dart | 169 +++- lib/src/generator.dart | 3 + lib/src/html/html_generator.dart | 10 +- lib/src/html/html_generator_instance.dart | 9 +- lib/src/line_number_cache.dart | 15 + lib/src/markdown_processor.dart | 515 +++++++++-- lib/src/model.dart | 812 ++++++++++++++---- lib/src/reporting.dart | 18 - lib/templates/_property.html | 11 +- test/model_test.dart | 12 +- test/src/utils.dart | 6 +- .../test_package_docs/css/css-library.html | 2 +- testing/test_package_docs/ex/Apple-class.html | 11 +- testing/test_package_docs/ex/B-class.html | 6 +- .../test_package_docs/ex/CatString-class.html | 2 +- .../ex/CatString/writeAll.html | 2 +- testing/test_package_docs/ex/Dog-class.html | 16 +- testing/test_package_docs/ex/F-class.html | 9 +- .../test_package_docs/ex/Helper-class.html | 2 +- .../ex/WithGeneric-class.html | 2 +- .../ex/WithGenericSub-class.html | 2 +- testing/test_package_docs/ex/ex-library.html | 11 +- .../BaseForDocComments/doAwesomeStuff.html | 2 +- .../fake/ExtraSpecialList-class.html | 77 +- .../fake/LongFirstLine-class.html | 16 +- .../fake/SpecialList-class.html | 7 +- .../fake/SpecialList/elementAt.html | 2 +- .../fake/SpecialList/setAll.html | 4 +- .../fake/SpecialList/sublist.html | 2 +- .../fake/SuperAwesomeClass-class.html | 2 +- .../fake/WithGetterAndSetter-class.html | 7 +- .../test_package_docs/fake/fake-library.html | 16 +- .../Whataclass/noSuchMethod.html | 2 +- .../Whataclass/operator_equals.html | 2 +- .../Whataclass2/noSuchMethod.html | 2 +- .../Whataclass2/operator_equals.html | 2 +- .../two_exports/BaseClass-class.html | 7 +- .../two_exports/ExtendingClass-class.html | 9 +- .../two_exports/two_exports-library.html | 2 +- 42 files changed, 1470 insertions(+), 371 deletions(-) delete mode 100644 lib/src/reporting.dart diff --git a/.idea/modules.xml b/.idea/modules.xml index 7f80bff4ff..d9306e2cb1 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,6 +3,7 @@ + \ No newline at end of file diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart index 290deb6d18..e77bc3a53e 100644 --- a/bin/dartdoc.dart +++ b/bin/dartdoc.dart @@ -56,14 +56,14 @@ main(List arguments) async { var readme = args['sdk-readme']; if (readme != null && !(new File(readme).existsSync())) { stderr - .write(" Error: unable to locate the SDK description file at $readme."); + .write(" fatal error: unable to locate the SDK description file at $readme."); exit(1); } Directory inputDir = new Directory(args['input']); if (!inputDir.existsSync()) { stderr.write( - " Error: unable to locate the input directory at ${inputDir.path}."); + " fatal error: unable to locate the input directory at ${inputDir.path}."); exit(1); } @@ -77,7 +77,7 @@ main(List arguments) async { args['header'].map(_resolveTildePath).toList() as List; for (String headerFilePath in headerFilePaths) { if (!new File(headerFilePath).existsSync()) { - stderr.write(" Error: unable to locate header file: ${headerFilePath}."); + stderr.write(" fatal error: unable to locate header file: ${headerFilePath}."); exit(1); } } @@ -86,7 +86,7 @@ main(List arguments) async { args['footer'].map(_resolveTildePath).toList() as List; for (String footerFilePath in footerFilePaths) { if (!new File(footerFilePath).existsSync()) { - stderr.write(" Error: unable to locate footer file: ${footerFilePath}."); + stderr.write(" fatal error: unable to locate footer file: ${footerFilePath}."); exit(1); } } @@ -96,7 +96,7 @@ main(List arguments) async { for (String footerFilePath in footerTextFilePaths) { if (!new File(footerFilePath).existsSync()) { stderr.write( - " Error: unable to locate footer-text file: ${footerFilePath}."); + " fatal error: unable to locate footer-text file: ${footerFilePath}."); exit(1); } } @@ -110,7 +110,7 @@ main(List arguments) async { if (args.rest.isNotEmpty) { var unknownArgs = args.rest.join(' '); stderr.write( - 'Error: detected unknown command-line argument(s): $unknownArgs'); + ' fatal error: detected unknown command-line argument(s): $unknownArgs'); _printUsageAndExit(parser, exitCode: 1); } @@ -120,7 +120,7 @@ main(List arguments) async { if (!packageMeta.isValid) { stderr.writeln( - 'Unable to generate documentation: ${packageMeta.getInvalidReasons().first}.'); + ' fatal error: Unable to generate documentation: ${packageMeta.getInvalidReasons().first}.'); exit(1); } @@ -165,10 +165,14 @@ main(List arguments) async { autoIncludeDependencies: args['auto-include-dependencies'], categoryOrder: args['category-order']); - var dartdoc = new DartDoc(inputDir, excludeLibraries, sdkDir, generators, + DartDoc dartdoc = new DartDoc(inputDir, excludeLibraries, sdkDir, generators, outputDir, packageMeta, includeLibraries, includeExternals: includeExternals); + dartdoc.onCheckProgress.listen(_onProgress); + /*DartDocResults results = await dartdoc.generateDocs(); + print('\nSuccess! Docs generated into ${results.outDir.absolute.path}'); + */ Chain.capture(() async { DartDocResults results = await dartdoc.generateDocs(); print('\nSuccess! Docs generated into ${results.outDir.absolute.path}'); @@ -197,7 +201,7 @@ ArgParser _createArgsParser() { defaultsTo: false); parser.addFlag('sdk-docs', help: 'Generate ONLY the docs for the Dart SDK.', negatable: false); - parser.addFlag('show-warnings', help: 'Display warnings.', negatable: false); + parser.addFlag('show-warnings', help: 'Display warnings.', negatable: false, defaultsTo: false); parser.addFlag('show-progress', help: 'Display progress indications to console stdout', negatable: false); parser.addOption('sdk-readme', @@ -264,8 +268,12 @@ ArgParser _createArgsParser() { return parser; } -void _onProgress(File file) { - if (_showProgress) stdout.write('.'); +int _progressCounter = 0; +void _onProgress(var file) { + if (_showProgress && _progressCounter % 5 == 0) { + stdout.write('.'); + } + _progressCounter += 1; } /// Print help if we are passed the help option. diff --git a/dartdoc.iml b/dartdoc.iml index d56c2bad77..726840a663 100644 --- a/dartdoc.iml +++ b/dartdoc.iml @@ -1,6 +1,6 @@ - + @@ -12,7 +12,6 @@ - @@ -35,6 +34,5 @@ - \ No newline at end of file diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 1fba463f4a..7404ce3471 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -8,7 +8,7 @@ library dartdoc; import 'dart:async'; import 'dart:io'; -import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/element.dart' show LibraryElement; import 'package:analyzer/error/error.dart'; import 'package:analyzer/file_system/file_system.dart' as fileSystem; import 'package:analyzer/file_system/physical_file_system.dart'; @@ -21,6 +21,8 @@ import 'package:analyzer/src/generated/java_io.dart'; import 'package:analyzer/src/generated/sdk.dart'; import 'package:analyzer/src/generated/source.dart'; import 'package:analyzer/src/generated/source_io.dart'; +import 'package:html/dom.dart' show Element, Document; +import 'package:html/parser.dart' show parse; import 'package:package_config/discovery.dart' as package_config; import 'package:path/path.dart' as path; @@ -99,12 +101,20 @@ class DartDoc { final List includes; final List includeExternals; final List excludes; + final Set writtenFiles = new Set(); + + // Fires when the self checks make progress. + StreamController _onCheckProgress; Stopwatch _stopwatch; DartDoc(this.rootDir, this.excludes, this.sdkDir, this.generators, this.outputDir, this.packageMeta, this.includes, - {this.includeExternals: const []}); + {this.includeExternals: const []}) { + _onCheckProgress = new StreamController(sync: true); + } + + Stream get onCheckProgress => _onCheckProgress.stream; /// Generate DartDoc documentation. /// @@ -140,9 +150,16 @@ class DartDoc { }); } + PackageWarningOptions warningOptions = new PackageWarningOptions(); + // TODO(jcollins-g): explode this into detailed command line options. + if (config != null && config.showWarnings) { + for (PackageWarning kind in PackageWarning.values) { + warningOptions.warn(kind); + } + } Package package; if (config != null && config.autoIncludeDependencies) { - package = Package.withAutoIncludedDependencies(libraries, packageMeta); + package = Package.withAutoIncludedDependencies(libraries, packageMeta, warningOptions); libraries = package.libraries.map((l) => l.element).toList(); // remove excluded libraries again, in case they are picked up through // dependencies. @@ -152,10 +169,10 @@ class DartDoc { }); }); } - package = new Package(libraries, packageMeta); + package = new Package(libraries, packageMeta, warningOptions); print( - 'generating docs for libraries ${package.libraries.map((Library l) => l.name).join(', ')}\n'); + '\ngenerating docs for libraries ${package.libraries.map((Library l) => l.name).join(', ')}\n'); // Go through docs of every model element in package to prebuild the macros index // TODO(jcollins-g): move index building into a cached-on-demand generation @@ -167,21 +184,158 @@ class DartDoc { for (var generator in generators) { await generator.generate(package, outputDir); + generator.writtenFiles.forEach((p) => writtenFiles.add(path.normalize(p))); } + await verifyLinks(package, outputDir.path); + double seconds = _stopwatch.elapsedMilliseconds / 1000.0; print( "\nDocumented ${package.libraries.length} librar${package.libraries.length == 1 ? 'y' : 'ies'} " "in ${seconds.toStringAsFixed(1)} seconds."); + print ("Finished with: ${package.packageWarningCounter.warningCount} warnings, ${package.packageWarningCounter.errorCount} errors"); if (package.libraries.isEmpty) { - stderr.write( - "\ndartdoc could not find any libraries to document. Run `pub get` and try again."); + throw new DartDocFailure( + "dartdoc could not find any libraries to document. Run `pub get` and try again."); + } + + if (package.packageWarningCounter.errorCount > 0) { + throw new DartDocFailure("dartdoc encountered errors while processing"); } return new DartDocResults(packageMeta, package, outputDir); } + void _warn(Package package, PackageWarning kind, String p, String origin, {String source}) { + // Ordinarily this would go in [Package.warn], but we don't actually know what + // ModelElement to warn on yet. + Locatable referenceElement; + Set referenceElements; + + // Make all paths relative to origin. + if (path.isWithin(origin, p)) { + p = path.relative(p, from: origin); + } + if (source != null) { + if (path.isWithin(origin, source)) { + source = path.relative(source, from: origin); + } + // Source paths are always relative. + referenceElements = package.allHrefs[source]; + } else { + referenceElements = package.allHrefs[p]; + } + if (referenceElements != null) { + if (referenceElements.any((e) => e.isCanonical)) { + referenceElement = referenceElements.firstWhere((e) => e.isCanonical); + } else { + // If we don't have a canonical element, just pick one. + referenceElement = referenceElements.isEmpty ? null : referenceElements.first; + } + } + if (referenceElement == null && source == 'index.html') + referenceElement = package; + package.warn(referenceElement, kind, p); + } + + Future _doOrphanCheck(Package package, String origin, Set visited) async { + String normalOrigin = path.normalize(origin); + String staticAssets = path.joinAll([normalOrigin, 'static-assets', '']); + String indexJson = path.joinAll([normalOrigin, 'index.json']); + bool foundIndex = false; + await for (FileSystemEntity f in new Directory(normalOrigin).list(recursive: true)) { + var fullPath = path.normalize(f.path); + if (f is Directory) { + continue; + } + if (fullPath.startsWith(staticAssets)) { + continue; + } + if (fullPath == indexJson) { + foundIndex = true; + _onCheckProgress.add(fullPath); + continue; + } + if (visited.contains(fullPath)) continue; + if (!writtenFiles.contains(fullPath)) { + // This isn't a file we wrote (this time); don't claim we did. + _warn(package, PackageWarning.unknownFile, fullPath, normalOrigin); + } else { + _warn(package, PackageWarning.orphanedFile, fullPath, normalOrigin); + } + _onCheckProgress.add(fullPath); + } + + if (!foundIndex) { + _warn(package, PackageWarning.brokenLink, indexJson, normalOrigin); + _onCheckProgress.add(indexJson); + } + } + + _doCheck(Package package, String origin, Set visited, String pathToCheck, [String source, String fullPath]) { + if (fullPath == null) { + fullPath = path.joinAll([origin, pathToCheck]); + fullPath = path.normalize(fullPath); + } + + File file = new File("$fullPath"); + if (!file.existsSync()) { + _warn(package, PackageWarning.brokenLink, pathToCheck, path.normalize(origin), source: source); + _onCheckProgress.add(pathToCheck); + return; + } + Document doc = parse(file.readAsStringSync()); + Element base = doc.querySelector('base'); + String baseHref; + if (base != null) { + baseHref = base.attributes['href']; + } + List links = doc.querySelectorAll('a'); + Iterable stringLinks = links + .map((link) => link.attributes['href']) + .where((href) => href != null); + + for (String href in stringLinks) { + if (!href.startsWith('http') && !href.contains('#')) { + var full; + if (baseHref != null) { + full = '${path.dirname(pathToCheck)}/$baseHref/$href'; + } else { + full = '${path.dirname(pathToCheck)}/$href'; + } + var newPathToCheck = path.normalize(full); + String newFullPath = path.joinAll([origin, newPathToCheck]); + newFullPath = path.normalize(newFullPath); + if (!visited.contains(newFullPath)) { + visited.add(newFullPath); + _doCheck(package, origin, visited, newPathToCheck, pathToCheck, newFullPath); + } + } + } + + _onCheckProgress.add(pathToCheck); + 1+1; + //return; + } + + Map> _hrefs; + /// Don't call this method more than once, and only after you've + /// generated all docs for the Package. + Future verifyLinks(Package package, String origin) async { + assert(_hrefs == null); + _hrefs = package.allHrefs; + + Set visited = new Set(); + String start = 'index.html'; + visited.add(start); + stdout.write('\nvalidating docs'); + _doCheck(package, origin, visited, start); + await _doOrphanCheck(package, origin, visited); + // A do nothing line magically makes await _doOrphanCheck actually await? + return; + } + List _parseLibraries( List files, List includeExternals) { Set libraries = new Set(); @@ -220,6 +374,7 @@ class DartDoc { SourceFactory sourceFactory = new SourceFactory(resolvers); + // TODO(jcollins-g): fix this so it actually obeys analyzer options files. var options = new AnalysisOptionsImpl(); options.enableGenericMethods = true; options.enableAssertInitializer = true; diff --git a/lib/src/generator.dart b/lib/src/generator.dart index afa5a56d97..1c28c0145c 100644 --- a/lib/src/generator.dart +++ b/lib/src/generator.dart @@ -21,4 +21,7 @@ abstract class Generator { /// Fires when a file is created. Stream get onFileCreated; + + /// Fetches all filenames written by this generator. + Set get writtenFiles; } diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart index 37e5118818..dbbb42fa1c 100644 --- a/lib/src/html/html_generator.dart +++ b/lib/src/html/html_generator.dart @@ -35,6 +35,7 @@ typedef String Renderer(String input); class HtmlGenerator extends Generator { final Templates _templates; final HtmlGeneratorOptions _options; + HtmlGeneratorInstance _instance; final StreamController _onFileCreated = new StreamController(sync: true); @@ -42,6 +43,9 @@ class HtmlGenerator extends Generator { @override Stream get onFileCreated => _onFileCreated.stream; + @override + Set get writtenFiles => _instance.writtenFiles; + /// [url] - optional URL for where the docs will be hosted. static Future create( {HtmlGeneratorOptions options, @@ -61,9 +65,9 @@ class HtmlGenerator extends Generator { @override Future generate(Package package, Directory out) { - return new HtmlGeneratorInstance( - _options, _templates, package, out, _onFileCreated) - .generate(); + _instance = new HtmlGeneratorInstance( + _options, _templates, package, out, _onFileCreated); + return _instance.generate(); } } diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart index a7bdfd4851..2155e666ce 100644 --- a/lib/src/html/html_generator_instance.dart +++ b/lib/src/html/html_generator_instance.dart @@ -94,6 +94,8 @@ class HtmlGeneratorInstance implements HtmlOptions { generatePackage(); for (var lib in package.libraries) { + //if (lib.name != 'dart:convert') continue; + //if(lib.name != 'angular2.router') continue; generateLibrary(package, lib); for (var clazz in lib.allClasses) { @@ -178,7 +180,7 @@ class HtmlGeneratorInstance implements HtmlOptions { } void generatePackage() { - stdout.write('documenting ${package.name}'); + stdout.write('\ndocumenting ${package.name}'); TemplateData data = new PackageTemplateData(this, package, useCategories); @@ -188,7 +190,6 @@ class HtmlGeneratorInstance implements HtmlOptions { void generateLibrary(Package package, Library lib) { stdout .write('\ngenerating docs for library ${lib.name} from ${lib.path}...'); - if (!lib.isAnonymous && !lib.hasDocumentation) { package.warn(lib, PackageWarning.noLibraryLevelDocs); } @@ -290,11 +291,13 @@ class HtmlGeneratorInstance implements HtmlOptions { File destFile = new File(path.join(out.path, 'static-assets', destFileName)) ..createSync(recursive: true); - Uint8List resourceBytes = await loader.loadAsBytes(resourcePath); + Uint8List resourceBytes = (await loader.loadAsBytes(resourcePath)); destFile.writeAsBytesSync(resourceBytes); } } + get writtenFiles => _writtenFiles; + void _build(String filename, TemplateRenderer template, TemplateData data) { String fullName = path.join(out.path, filename); diff --git a/lib/src/line_number_cache.dart b/lib/src/line_number_cache.dart index 20b3b851d1..cf73c796ae 100644 --- a/lib/src/line_number_cache.dart +++ b/lib/src/line_number_cache.dart @@ -6,6 +6,7 @@ library dartdoc.cache; import 'dart:collection'; import 'dart:io'; +import 'package:tuple/tuple.dart'; String _getNewlineChar(String contents) { if (contents.contains("\r\n")) { @@ -51,6 +52,20 @@ class LineNumberCache { } } + Tuple2 lineAndColumn(String file, int offset) { + if (offset == 0) { + return new Tuple2(0, 0); + } else { + var lineMap = _lineNumbers.putIfAbsent( + file, () => _createLineNumbersMap(_fileContents(file))); + var lastKey = lineMap.lastKeyBefore(offset); + if (lastKey != null) { + return new Tuple2(lineMap[lastKey] + 1, offset - lastKey); + } + } + return null; + } + String _fileContents(String file) => __fileContents.putIfAbsent(file, () => new File(file).readAsStringSync()); } diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart index e116720ad0..6b98421718 100644 --- a/lib/src/markdown_processor.dart +++ b/lib/src/markdown_processor.dart @@ -9,18 +9,12 @@ import 'dart:convert'; import 'dart:math'; import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/element/element.dart' - show - LibraryElement, - Element, - ConstructorElement, - ParameterElement, - PropertyAccessorElement; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/member.dart' show Member; import 'package:html/parser.dart' show parse; import 'package:markdown/markdown.dart' as md; import 'model.dart'; -import 'reporting.dart'; const validHtmlTags = const [ "a", @@ -125,6 +119,23 @@ const validHtmlTags = const [ final nonHTMLRegexp = new RegExp(" ])\\w+[> ]"); +// Type parameters and other things to ignore at the end of doc references. +final trailingIgnoreStuff = + new RegExp(r'(<.*>|\(.*\))$'); + +// Things to ignore at the beginning of doc references +final leadingIgnoreStuff = + new RegExp(r'^(const|final|var)[\s]+', multiLine: true); + +// This is a constructor! +final isConstructor = new RegExp(r'^new[\s]+', multiLine: true); + +// This is probably not really intended as a doc reference, so don't try or +// warn about them. +// Covers anything with leading digits/symbols, empty string, weird punctuation, spaces. +final notARealDocReference = + new RegExp(r'''(^[^\w]|^[\d]|[,"'/]|^$)'''); + // We don't emit warnings currently: #572. const List _oneLinerSkipTags = const ["code", "pre"]; @@ -139,13 +150,28 @@ final RegExp _hide_schemes = new RegExp('^(http|https)://'); class MatchingLinkResult { final ModelElement element; final String label; - MatchingLinkResult(this.element, this.label); + final bool warn; + MatchingLinkResult(this.element, this.label, {this.warn: true}); +} + +// Calculate a class hint for findCanonicalModelElementFor. +ModelElement _getPreferredClass(ModelElement modelElement) { + if (modelElement is EnclosedElement && (modelElement as EnclosedElement).enclosingElement is Class) { + return (modelElement as EnclosedElement).enclosingElement; + } else if (modelElement is Class) { + return modelElement; + } + return null; } // TODO: this is in the wrong place NodeList _getCommentRefs(ModelElement modelElement) { - if (modelElement == null) return null; - if (modelElement.documentation == null && modelElement.canOverride()) { + Class preferredClass = _getPreferredClass(modelElement); + ModelElement cModelElement = modelElement.package.findCanonicalModelElementFor(modelElement.element, preferredClass: preferredClass); + if (cModelElement == null) + return null; + modelElement = cModelElement; + /*if (modelElement.documentation == null && modelElement.canOverride()) { var melement = modelElement.overriddenElement; if (melement != null && melement.element.computeNode() != null && @@ -155,6 +181,14 @@ NodeList _getCommentRefs(ModelElement modelElement) { if (docComment != null) return docComment.references; return null; } + }*/ + if (modelElement.element.documentationComment == null && modelElement.canOverride()) { + var node = modelElement.overriddenElement?.element?.computeNode(); + if (node is AnnotatedNode) { + if (node.documentationComment != null) { + return node.documentationComment.references; + } + } } if (modelElement.element.computeNode() is AnnotatedNode) { final AnnotatedNode annotatedNode = modelElement.element.computeNode(); @@ -174,31 +208,59 @@ NodeList _getCommentRefs(ModelElement modelElement) { } } } + if (modelElement.element is ClassMemberElement) { + var node = modelElement.element.getAncestor((e) => e is ClassElement).computeNode(); + if (node is AnnotatedNode) { + if (node.documentationComment != null) { + return node.documentationComment.references; + } + } + } /*else if (modelElement.element is Member) { + var node = modelElement.element.enclosingElement. + }*/ return null; } /// Returns null if element is a parameter. MatchingLinkResult _getMatchingLinkElement( - String codeRef, ModelElement element, List commentRefs, - {bool isConstructor: false}) { - if (commentRefs == null) return new MatchingLinkResult(null, null); + String codeRef, ModelElement element, List commentRefs) { + // By debugging inspection, it seems correct to not warn when we don't have + // CommentReferences; there's actually nothing that needs resolving in + // that case. + if (commentRefs == null) return new MatchingLinkResult(null, null, warn: false); + + if (!codeRef.contains(isConstructor) && codeRef.contains(notARealDocReference)) { + // Don't waste our time on things we won't ever find. + return new MatchingLinkResult(null, null, warn: false); + } Element refElement; - for (CommentReference ref in commentRefs) { - if (ref.identifier.name == codeRef) { - bool isConstrElement = ref.identifier.staticElement is ConstructorElement; - if (isConstructor && isConstrElement || - !isConstructor && !isConstrElement) { - refElement = ref.identifier.staticElement; - break; + + // Try expensive not-scoped lookup. + if (refElement == null) { + refElement = _findRefElementInLibrary(codeRef, element); + } + + // This is faster but does not take canonicalization into account; try + // only as a last resort. TODO(jcollins-g): make analyzer comment references + // dartdoc-canonicalization-aware? + if (refElement == null) { + for (CommentReference ref in commentRefs) { + if (ref.identifier.name == codeRef) { + bool isConstrElement = ref.identifier.staticElement is ConstructorElement; + // Constructors are now handled by library search. + if (!isConstrElement) { + refElement = ref.identifier.staticElement; + break; + } } } } - // Did not find an element in scope + // Did not find it anywhere. if (refElement == null) { - return _findRefElementInLibrary(codeRef, element, commentRefs); + return new MatchingLinkResult(null, null); } if (refElement is PropertyAccessorElement) { @@ -207,10 +269,15 @@ MatchingLinkResult _getMatchingLinkElement( refElement = (refElement as PropertyAccessorElement).variable; } - if (refElement is ParameterElement) return new MatchingLinkResult(null, null); + // Ignore all parameters. + if (refElement is ParameterElement || refElement is TypeParameterElement) return new MatchingLinkResult(null, null, warn: false); Library refLibrary = element.package.findOrCreateLibraryFor(refElement); - ModelElement refModelElement = refLibrary.modelElementsMap[refElement]; + Element searchElement = refElement is Member ? refElement.baseElement : refElement; + + Class preferredClass = _getPreferredClass(element); + //Set refModelElements = refLibrary.modelElementsMap[searchElement]; + ModelElement refModelElement = element.package.findCanonicalModelElementFor(searchElement, preferredClass: preferredClass); // There have been places in the code which helpfully cache entities // regardless of what package they are associated with. This assert // will protect us from reintroducing that. @@ -218,62 +285,353 @@ MatchingLinkResult _getMatchingLinkElement( if (refModelElement != null) { return new MatchingLinkResult(refModelElement, null); } - return new MatchingLinkResult(null, null); + refModelElement = new ModelElement.from(searchElement, refLibrary); + if (!refModelElement.isCanonical) { + refModelElement.warn(PackageWarning.noCanonicalFound); + // Don't warn about doc references because that's covered by the no + // canonical library found message. + return new MatchingLinkResult(null, null, warn: false); + } + assert(false); + return new MatchingLinkResult(refModelElement, null); } -MatchingLinkResult _findRefElementInLibrary( - String codeRef, ModelElement element, List commentRefs) { + +/// Returns true if this is a constructor we should consider in +/// _findRefElementInLibrary, or if this isn't a constructor. +bool _yesReallyThisConstructor(String codeRef, ModelElement modelElement) { + if (modelElement is! Constructor) return true; + if (codeRef.contains(isConstructor)) return true; + Constructor aConstructor = modelElement; + List codeRefParts = codeRef.split('.'); + if (codeRefParts.length > 1) { + if (codeRefParts[codeRefParts.length - 1] == codeRefParts[codeRefParts.length - 2]) { + // Foobar.Foobar -- assume they really do mean the constructor for this class. + return true; + } + } + if (aConstructor.name != aConstructor.enclosingElement.name) { + // This isn't a default constructor so treat it like anything else. + return true; + } + return false; +} + + +// Basic map of reference to ModelElement, for cases where we're searching +// outside of scope. +// TODO(jcollins-g): function caches with maps are very common in dartdoc. +// Extract into library. +Map> _findRefElementCache; +//final Map, Element> _findRefElementInLibraryCache = new Map(); +// TODO(jcollins-g): get some sort of consistency in resolving names +// TODO(jcollins-g): rewrite this to handle constructors in a less hacky way +// TODO(jcollins-g): this function breaks down naturally into many helpers, extract them +// TODO(jcollins-g): a complex package winds up spending a lot of cycles here. Optimize. +Element _findRefElementInLibrary(String codeRef, ModelElement element) { + assert(element.package.allLibrariesAdded); + //Tuple2 key = new Tuple2(codeRef, element); + //if (_findRefElementInLibraryCache.containsKey(key)) { + // return _findRefElementInLibraryCache[key]; + //} + + String codeRefChomped = codeRef.replaceFirst(isConstructor, ''); + final Library library = element.library; final Package package = library.package; - final Map result = {}; - - // TODO(jcollins-g): This is extremely inefficient and no longer necessary - // since allCanonicalModelElements is now stable and doesn't mutate after - // [Package] construction. So precompute and cache the result map somewhere, - // maybe in [Package]. - for (final modelElement in package.allCanonicalModelElements) { - // Constructors are handled in _linkDocReference. - if (codeRef == modelElement.fullyQualifiedName && - modelElement is! Constructor) { - result[modelElement.fullyQualifiedName] = modelElement; + final Set results = new Set(); + + if (element.fullyQualifiedName == 'dart:io.Platform' && codeRef == 'dart:io') { + 1+1; + } + + results.remove(null); + // Oh, and this might be an operator. Strip the operator prefix and try again. + if (results.isEmpty && codeRef.startsWith('operator')) { + String newCodeRef = codeRef.replaceFirst('operator', ''); + return _findRefElementInLibrary(newCodeRef, element); + } + + results.remove(null); + // Oh, and someone might have some type parameters or other garbage. + if (results.isEmpty && codeRef.contains(trailingIgnoreStuff)) { + String newCodeRef = codeRef.replaceFirst(trailingIgnoreStuff, ''); + return _findRefElementInLibrary(newCodeRef, element); + } + + results.remove(null); + // Oh, and someone might have thrown on a 'const' or 'final' in front. + if (results.isEmpty && codeRef.contains(leadingIgnoreStuff)) { + String newCodeRef = codeRef.replaceFirst(leadingIgnoreStuff, ''); + return _findRefElementInLibrary(newCodeRef, element); + } + + // Maybe this ModelElement has parameters, and this is one of them. + // We don't link these, but this keeps us from emitting warnings. Be sure to + // get members of parameters too (yes, people do this). + // TODO(jcollins): link to classes that are the types of parameters, where known + results.addAll(element.allParameters.where((p) => p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}."))); + /* + if (element.canHaveParameters && element.parameters.map((p) => p.name).contains(codeRef)) { + results.addAll(element.parameters.where((p) => p.name.contains(codeRef))); + } + if (element is GetterSetterCombo) { + Accessor setter = element.setter; + if (setter != null) { + if (setter.parameters.map((p) => p.name).contains(codeRef)) { + results.addAll(setter.parameters.where((p) => p.name.contains(codeRef))); + } + for (Parameter p in setter.parameters) { + + } + } + }*/ + results.remove(null); + + if (results.isEmpty) { + // Maybe this is local to a class. + // TODO(jcollins): pretty sure tryClasses is a strict subset of the superclass chain. + // Optimize if that's the case. + List tryClasses = [_getPreferredClass(element)]; + Class realClass = tryClasses.first; + if (element is Inheritable) { + ModelElement overriddenElement = element.overriddenElement; + while (overriddenElement != null) { + tryClasses.add((element.overriddenElement as EnclosedElement).enclosingElement); + overriddenElement = overriddenElement.overriddenElement; + } + } + /* + if (element.element.enclosingElement is ClassElement) { + tryClass = new ModelElement.from(element.element.enclosingElement, element.library); + } else if (element is Class) { + tryClass = element; + }*/ + + for (Class tryClass in tryClasses) { + if (tryClass != null) { + _getResultsForClass(tryClass, codeRefChomped, results, codeRef, package); + } + if (results.isNotEmpty) break; + } + // Sometimes documentation refers to classes that are further up the chain. + // Get those too. + if (results.isEmpty && realClass != null) { + for (Class superClass in realClass.superChain.map((et) => et.element as Class)) { + if (!tryClasses.contains(superClass)) { + _getResultsForClass(superClass, codeRefChomped, results, codeRef, package); + } + if (results.isNotEmpty) break; + } + } + } + results.remove(null); + + // We now need the ref element cache to keep from repeatedly searching [Package.allModelElements]. + if (results.isEmpty && _findRefElementCache == null) { + assert(package.allLibrariesAdded); + _findRefElementCache = new Map(); + for (final modelElement in package.allModelElements) { + _findRefElementCache.putIfAbsent(modelElement.fullyQualifiedNameWithoutLibrary, () => new Set()); + _findRefElementCache.putIfAbsent(modelElement.fullyQualifiedName, () => new Set()); + _findRefElementCache[modelElement.fullyQualifiedName].add(modelElement); + _findRefElementCache[modelElement.fullyQualifiedNameWithoutLibrary].add(modelElement); } } + // But if not, look for a fully qualified match. (That only makes sense + // if the codeRef might be qualified, and contains periods.) + if (results.isEmpty && codeRefChomped.contains('.') && _findRefElementCache.containsKey(codeRefChomped)) { + for (final modelElement in _findRefElementCache[codeRefChomped]) { + if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; + results.add(package.findCanonicalModelElementFor(modelElement.element)); + } + // TODO(jcollins-g): This is extremely inefficient and no longer necessary + // since allCanonicalModelElements is now stable and doesn't mutate after + // [Package] construction. So precompute and cache the result map somewhere, + // maybe in [Package]. + /*for (final modelElement in package.allModelElements) { + if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; + // Constructors are handled in _linkDocReference. + if (codeRefChomped == modelElement.fullyQualifiedName) { + results.add(package.findCanonicalModelElementFor(modelElement.element)); + } + // Try without the library too; sometimes people use that as shorthand. + // Not the same as partially qualified matches because here we have a '.'. + if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary) { + results.add(package.findCanonicalModelElementFor(modelElement.element)); + } + }*/ + } + results.remove(null); + + // Only look for partially qualified matches if we didn't find a fully qualified one. - if (result.isEmpty) { - for (final modelElement in library.allCanonicalModelElements) { - if (codeRef == modelElement.fullyQualifiedNameWithoutLibrary && - modelElement is! Constructor) { - result[modelElement.fullyQualifiedName] = modelElement; + if (results.isEmpty) { + for (final modelElement in library.allModelElements) { + if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; + if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary) { + results.add(package.findCanonicalModelElementFor(modelElement.element)); } } } + results.remove(null); + + // And if we still haven't found anything, just search the whole ball-of-wax. + if (results.isEmpty && _findRefElementCache.containsKey(codeRefChomped)) { + for (final modelElement in _findRefElementCache[codeRefChomped]) { + if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary || + (modelElement is Library && codeRefChomped == modelElement.fullyQualifiedName)) { + results.add(package.findCanonicalModelElementFor(modelElement.element)); + } + } + /*for (final modelElement in package.allModelElements) { + if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; + if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary) { + results.add(package.findCanonicalModelElementFor(modelElement.element)); + } + if (modelElement is Library && codeRefChomped == modelElement.fullyQualifiedName) { + results.add(package.findCanonicalModelElementFor(modelElement.element)); + } + }*/ + } - if (result.isEmpty) { - return new MatchingLinkResult(null, null); - } else if (result.length == 1) { - return new MatchingLinkResult( - result.values.first, result.values.first.name); + // This could conceivably be a reference to an enum member. They don't show up in allModelElements. + // TODO(jcollins-g): put enum members in allModelElements with useful hrefs without blowing up other assumptions. + // TODO(jcollins-g): this doesn't provide good warnings if an enum and class have the same name in different libraries in the same package. Fix that. + if (results.isEmpty) { + List codeRefChompedParts = codeRefChomped.split('.'); + if (codeRefChompedParts.length >= 2) { + String maybeEnumName = codeRefChompedParts.sublist(0, codeRefChompedParts.length - 1).join('.'); + String maybeEnumMember = codeRefChompedParts.last; + if (_findRefElementCache.containsKey(maybeEnumName)) { + for (final modelElement in _findRefElementCache[maybeEnumName]) { + if (modelElement is Enum) { + if (modelElement.constants.any((e) => e.name == maybeEnumMember)) { + results.add(modelElement); + break; + } + } + } + } + } + } + + results.remove(null); + Element result; + + if (results.length > 1) { + // If this name could refer to a class or a constructor, prefer the class. + if (results.any((r) => r is Class)) { + results.removeWhere((r) => r is Constructor); + } + } + + if (results.length > 1) { + // Attempt to disambiguate using the library. + // TODO(jcollins-g): we could have saved ourselves some work by using the analyzer + // to search the namespace, somehow. Do that instead. + if (results.any((r) => r.element.isAccessibleIn(element.library.element))) { + results.removeWhere((r) => !r.element.isAccessibleIn(element.library.element)); + } + } + + // TODO(jcollins-g): This is only necessary because we had to jettison commentRefs + // as a way to figure this out. We could reintroduce commentRefs, or we could + // compute this via other means. + if (results.length > 1) { + String startName = "${element.fullyQualifiedName}."; + String realName = "${element.fullyQualifiedName}.${codeRefChomped}"; + if (results.any((r) => r.fullyQualifiedName == realName)) { + results.removeWhere((r) => r.fullyQualifiedName != realName); + } + if (results.any((r) => r.fullyQualifiedName.startsWith(startName))) { + results.removeWhere((r) => !r.fullyQualifiedName.startsWith(startName)); + } + } + // TODO(jcollins-g): further disambiguations based on package information? + + if (results.isEmpty) { + result = null; + } else if (results.length == 1) { + result = results.first.element; } else { - warning("Ambiguous reference to [${codeRef}] in ${_elementLocation(element)}. " + - "We found matches to the following elements: ${result.keys.map((k) => "'${k}'").join(", ")}"); - return new MatchingLinkResult(null, null); + element.warn(PackageWarning.ambiguousDocReference, + "[$codeRef] => ${results.map((r) => "'${r.fullyQualifiedName}'").join(", ")}"); + result = results.first.element; } + //_findRefElementInLibraryCache[key] = result; + return result; } -String _linkDocReference(String reference, ModelElement element, + +// results is an output parameter +void _getResultsForClass(Class tryClass, String codeRefChomped, Set results, String codeRef, Package package) { + if ((tryClass.modelType.typeArguments.map((e) => e.name)).contains(codeRefChomped)) { + results.add(tryClass.modelType.typeArguments.firstWhere((e) => e.name == codeRefChomped).element); + } else { + // People like to use 'this' in docrefs too. + if (codeRef == 'this') { + results.add(package.findCanonicalModelElementFor(tryClass.element)); + } else { + // TODO(jcollins-g): get rid of reimplementation of identifier resolution + // or integrate into ModelElement in a simpler way. + List superChain = []; + superChain.add(tryClass); + superChain.addAll(tryClass.superChainRaw.map((t) => t.returnElement as Class)); + List codeRefParts = codeRefChomped.split('.'); + for (final c in superChain) { + for (final modelElement in c.allModelElements) { + if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; + String namePart = modelElement.fullyQualifiedName.split('.').last; + // TODO(jcollins-g): fix operators so we can use 'name' here or similar. + if (codeRefChomped == namePart) { + results.add(package.findCanonicalModelElementFor(modelElement.element, preferredClass: tryClass)); + continue; + } + // TODO(jcollins-g): fix partial qualifications logic so it can tell + // when it is referenced from a non-documented element? + if (codeRefParts.first == c.name && codeRefParts.last == namePart) { + results.add(package.findCanonicalModelElementFor(modelElement.element, preferredClass: tryClass)); + continue; + } + if (modelElement is Constructor) { + // Constructor names don't include the class, so we might miss them in the above search. + List codeRefParts = codeRefChomped.split('.'); + if (codeRefParts.length > 1) { + String codeRefClass = codeRefParts[codeRefParts.length - 2]; + String codeRefConstructor = codeRefParts.last; + if (codeRefClass == c.name && codeRefConstructor == modelElement.fullyQualifiedName.split('.').last) { + results.add(package.findCanonicalModelElementFor(modelElement.element, preferredClass: tryClass)); + continue; + } + } + } + } + if (results.isNotEmpty) break; + if (c.fullyQualifiedNameWithoutLibrary == codeRefChomped) { + results.add(c); + break; + } + } + } + } +} + +String _linkDocReference(String codeRef, ModelElement element, NodeList commentRefs) { + // We might not have the canonical ModelElement here. If we don't, + // try to get it. + /*if (!element.isCanonical) { + element = element.package.findCanonicalModelElementFor(element.element); + }*/ + element = element.overriddenDocumentedElement; + // support for [new Constructor] and [new Class.namedCtr] - var refs = reference.split(' '); MatchingLinkResult result; - if (refs.length == 2 && refs.first == 'new') { - result = _getMatchingLinkElement(refs[1], element, commentRefs, - isConstructor: true); - } else { - result = _getMatchingLinkElement(reference, element, commentRefs); - } + result = _getMatchingLinkElement(codeRef, element, commentRefs); final ModelElement linkedElement = result.element; - final String label = result.label ?? reference; + final String label = result.label ?? codeRef; if (linkedElement != null) { var classContent = ''; if (linkedElement.isDeprecated) { @@ -287,22 +645,13 @@ String _linkDocReference(String reference, ModelElement element, return '$label'; } } else { - warning( - "unresolved doc reference '$reference'${element != null ? " (in ${_elementLocation(element)}" : ""}"); + if (result.warn) { + element.warn(PackageWarning.unresolvedDocReference, codeRef); + } return '${HTML_ESCAPE.convert(label)}'; } } -// TODO(jcollins-g): Merge this into new [Package.warn] warning system -String _elementLocation(ModelElement element) { - while ((element.element.documentationComment == null || - element.element.documentationComment == "") && - element.overriddenElement != null) { - element = element.overriddenElement; - } - return "'${element.fullyQualifiedName}' (${element.sourceFileName}:${element.lineNumber})"; -} - String _renderMarkdownToHtml(String text, [ModelElement element]) { md.Node _linkResolver(String name) { NodeList commentRefs = _getCommentRefs(element); @@ -319,17 +668,21 @@ String _renderMarkdownToHtml(String text, [ModelElement element]) { // like a non HTML tag (a generic?) outside of a `[]` block. // https://github.com/dart-lang/dartdoc/issues/1250#issuecomment-269257942 void _showWarningsForGenericsOutsideSquareBracketsBlocks(String text, - [ModelElement element]) { + ModelElement element) { List tagPositions = findFreeHangingGenericsPositions(text); if (tagPositions.isNotEmpty) { tagPositions.forEach((int position) { - String errorMessage = "Generic type handled as HTML"; - if (element != null) { - errorMessage += " in ${_elementLocation(element)}"; - } - errorMessage += - " - '${text.substring(max(position - 20, 0), min(position + 20, text.length))}'"; - warning(errorMessage); + String priorContext = "${text.substring(max(position - 20, 0), position)}"; + String postContext = "${text.substring(position, min(position + 30, text.length))}"; + priorContext = priorContext.replaceAll(new RegExp(r'^.*\n', multiLine: true), ''); + postContext = postContext.replaceAll(new RegExp(r'\n.*$', multiLine: true), ''); + String errorMessage = "$priorContext$postContext"; + //String errorMessage = "${text.substring(max(position - 20, 0), min(position + 20, text.length))}"; + + // Strip pieces of lines before and after that + //errorMessage = errorMessage.replaceAll(new RegExp(r'(\n[^<>]*$|^[^<>]*\n)'), ''); + // TODO(jcollins-g): allow for more specific error location inside comments + element.warn(PackageWarning.typeAsHtml, errorMessage); }); } } diff --git a/lib/src/model.dart b/lib/src/model.dart index ad2708e8d2..1e6a44f1b9 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -84,6 +84,7 @@ int byFeatureOrdering(String a, String b) { return compareAsciiLowerCaseNatural(a, b); } + /// Mixin for subclasses of ModelElement representing Elements that can be /// inherited from one class to another. /// @@ -125,6 +126,8 @@ abstract class Inheritable { searchElement = (searchElement as Member).baseElement; } bool foundElement = false; + // TODO(jcollins-g): generate warning if an inherited element's definition + // is in an intermediate non-canonical class in the inheritance chain for (Class c in inheritance.reversed) { if (!foundElement && c.contains(searchElement)) { foundElement = true; @@ -139,7 +142,9 @@ abstract class Inheritable { assert(definingEnclosingElement == _canonicalEnclosingClass); } } else { - _canonicalEnclosingClass = enclosingElement; + if (enclosingElement.isCanonical) { + _canonicalEnclosingClass = enclosingElement; + } } _canonicalEnclosingClassIsSet = true; } @@ -165,9 +170,21 @@ abstract class Inheritable { class Accessor extends ModelElement with SourceCodeMixin implements EnclosedElement { - Accessor(PropertyAccessorElement element, Library library) + ModelElement _enclosingCombo; + Accessor(PropertyAccessorElement element, Library library, this._enclosingCombo) : super(element, library); + ModelElement get enclosingCombo => _enclosingCombo; + + @override + void warn(PackageWarning kind, [String message]) { + if (enclosingCombo != null) { + enclosingCombo.warn(kind, message); + } else { + super.warn(kind, message); + } + } + @override ModelElement get enclosingElement { if (_accessor.enclosingElement is CompilationUnitElement) { @@ -186,20 +203,30 @@ class Accessor extends ModelElement bool get isGetter => _accessor.isGetter; + ModelElement _overriddenElement; @override Accessor get overriddenElement { - Element parent = element.enclosingElement; - if (parent is ClassElement) { - for (InterfaceType t in getAllSupertypes(parent)) { - var accessor = this.isGetter - ? t.getGetter(element.name) - : t.getSetter(element.name); - if (accessor != null) { - return new ModelElement.from(accessor, library); + assert(package.allLibrariesAdded); + if (_overriddenElement == null) { + Element parent = element.enclosingElement; + if (parent is ClassElement) { + for (InterfaceType t in getAllSupertypes(parent)) { + var accessor = this.isGetter + ? t.getGetter(element.name) + : t.getSetter(element.name); + if (accessor != null) { + Class parentClass = new ModelElement.from(parent, package.findOrCreateLibraryFor(parent)); + List possibleFields = []; + possibleFields.addAll(parentClass.allInstanceProperties); + possibleFields.addAll(parentClass.staticProperties); + String fieldName = accessor.name.replaceFirst('=', ''); + _overriddenElement = new ModelElement.from(accessor, library, enclosingCombo: possibleFields.firstWhere((f) => f.element.name == fieldName)); + break; + } } } } - return null; + return _overriddenElement; } @override @@ -339,6 +366,29 @@ class Class extends ModelElement implements EnclosedElement { return _allElements.contains(element); } + final Set _allModelElements = new Set(); + List get allModelElements { + if (_allModelElements.isEmpty) { + _allModelElements + ..addAll(allInstanceMethods) + ..addAll(allInstanceProperties) + ..addAll(allOperators) + ..addAll(constants) + ..addAll(constructors) + ..addAll(staticMethods) + ..addAll(staticProperties) + ..addAll(allInstanceMethods) + ..addAll(_typeParameters); + } + return _allModelElements.toList(); + } + + List _allCanonicalModelElements; + List get allCanonicalModelElements { + return (_allCanonicalModelElements ??= + allModelElements.where((e) => e.isCanonical).toList()); + } + List get constructors { if (_constructors != null) return _constructors; @@ -467,11 +517,11 @@ class Class extends ModelElement implements EnclosedElement { _inheritedMethods.add(m); _genPageMethods.add(m); } else { - Library lib = package.findOrCreateLibraryFor(value.enclosingElement); - Class enclosingClass = - new ModelElement.from(value.enclosingElement, lib); - _inheritedMethods.add(new ModelElement.from(value, lib, - enclosingClass: enclosingClass)); + //Library lib = package.findOrCreateLibraryFor(value.enclosingElement); + //Class enclosingClass = + // new ModelElement.from(value.enclosingElement, lib); + _inheritedMethods.add(new ModelElement.from(value, library, + enclosingClass: this)); } } } @@ -528,10 +578,8 @@ class Class extends ModelElement implements EnclosedElement { _inheritedOperators.add(o); _genPageOperators.add(o); } else { - Library lib = package.findOrCreateLibraryFor(value.enclosingElement); - _inheritedOperators.add(new ModelElement.from(value, lib, - enclosingClass: - new ModelElement.from(value.enclosingElement, lib))); + _inheritedOperators.add(new ModelElement.from(value, library, + enclosingClass: this)); } } @@ -828,7 +876,7 @@ class Constructor extends ModelElement String constructorName = element.name; Class c = new ModelElement.from(element.enclosingElement, library) as Class; if (constructorName.isEmpty) { - return c.name; + return '${c.name}'; } else { return '${c.name}.$constructorName'; } @@ -892,7 +940,7 @@ class Enum extends Class { _enumFields = _cls.fields .where(isPublic) .where((f) => f.isConst) - .map((field) => new EnumField.forConstant(index++, field, library)) + .map((field) => new ModelElement.from(field, library, index: index++)) .toList(growable: false) ..sort(byName); @@ -903,7 +951,7 @@ class Enum extends Class { List get instanceProperties { return super .instanceProperties - .map((Field p) => new EnumField(p.element, p.library)) + .map((Field p) => new ModelElement.from(p.element, p.library)) .toList(growable: false); } @@ -912,6 +960,20 @@ class Enum extends Class { @override String get kind => 'enum'; + /* + @override + Set _allModelElements = new Set(); + @override + List get allModelElements { + if (_allModelElements.isEmpty) { + _allModelElements.addAll(super.allModelElements); + _allModelElements.addAll(constants); + } + return _allModelElements.toList(); + } + + @override + List get allCanonicalModelElements => allModelElements.where((m) => m.isCanonical);*/ } /// Enum's fields are virtual, so we do a little work to create @@ -955,7 +1017,14 @@ class EnumField extends Field { String get linkedName => name; @override - bool get isCanonical => false; + bool get isCanonical { + if (name == 'index') return false; + // TODO(jcollins-g): This is a bit hacky, and doesn't allow for docrefs + if (_index == null) { + return super.isCanonical; + } + return true; + } @override String get oneLineDoc => documentationAsHtml; @@ -981,6 +1050,7 @@ class Field extends ModelElement assert(enclosingElement != definingEnclosingElement); } + String get constantValue { if (_constantValue != null) return _constantValue; @@ -1009,6 +1079,7 @@ class Field extends ModelElement @override bool get hasSetter => _field.setter != null; + @override String get href { String retval; @@ -1016,13 +1087,15 @@ class Field extends ModelElement if (enclosingElement is Class) { if (canonicalEnclosingElement == null) return null; retval = - '${canonicalEnclosingElement.library.dirName}/${canonicalEnclosingElement.name}/$_fileName'; + '${canonicalEnclosingElement.canonicalLibrary.dirName}/${canonicalEnclosingElement.name}/$_fileName'; } else if (enclosingElement is Library) { retval = '${canonicalLibrary.dirName}/$_fileName'; } else { throw new StateError( '$name is not in a class or library, instead it is a ${enclosingElement.element}'); } + if (retval.contains('animations/AnimationWithParentMixin')) + 1+1; return retval; } @@ -1043,15 +1116,10 @@ class Field extends ModelElement @override String get kind => 'property'; - String get linkedReturnType => modelType.linkedName; - bool get readOnly => hasGetter && !hasSetter; - bool get readWrite => hasGetter && hasSetter; String get typeName => "property"; - bool get writeOnly => hasSetter && !hasGetter; - @override List get annotations { List all_annotations = new List(); @@ -1102,20 +1170,20 @@ class Field extends ModelElement t, new ModelElement.from( t.element, _findOrCreateEnclosingLibraryFor(t.element))); - } else { + } /*else { var s = _field.setter.parameters.first.type; _modelType = new ElementType( s, new ModelElement.from( s.element, _findOrCreateEnclosingLibraryFor(s.element))); - } + }*/ } } /// Mixin for top-level variables and fields (aka properties) -abstract class GetterSetterCombo { +abstract class GetterSetterCombo implements ModelElement { Accessor get getter { - return _getter == null ? null : new ModelElement.from(_getter, library); + return _getter == null ? null : new ModelElement.from(_getter, library, enclosingCombo: this); } String get getterSetterDocumentationComment { @@ -1136,6 +1204,23 @@ abstract class GetterSetterCombo { return buffer.toString(); } + String get linkedReturnType { + if (hasGetter) return modelType.linkedName; + return null; + } + + @override + String get genericParameters { + if (hasSetter) return setter.genericParameters; + return null; + } + + @override + String get linkedParamsNoMetadata { + if (hasSetter) return setter.linkedParamsNoMetadata; + return null; + } + bool get hasExplicitGetter => hasGetter && !_getter.isSynthetic; bool get hasExplicitSetter => hasSetter && !_setter.isSynthetic; @@ -1145,10 +1230,25 @@ abstract class GetterSetterCombo { bool get hasSetter; - Library get library; + bool get hasGetterOrSetterWithoutParams { + return (hasGetter || (hasSetter && !hasExplicitSetter)); + } + + String get arrow { + if (readOnly) return r'→'; + if (writeOnly) return r'←'; + if (readWrite) return r'↔'; + assert(false); + return null; + } + + bool get readOnly => hasGetter && !hasSetter; + bool get readWrite => hasGetter && hasSetter; + + bool get writeOnly => hasSetter && !hasGetter; Accessor get setter { - return _setter == null ? null : new ModelElement.from(_setter, library); + return _setter == null ? null : new ModelElement.from(_setter, library, enclosingCombo: this); } PropertyAccessorElement get _getter; @@ -1207,7 +1307,7 @@ class Library extends ModelElement { .where((element) => element is ClassElement && element.isEnum)); _enums = enumClasses .where(isPublic) - .map((e) => new Enum(e, this)) + .map((e) => new ModelElement.from(e, this)) .toList(growable: false) ..sort(byName); @@ -1442,8 +1542,8 @@ class Library extends ModelElement { } } - Map _modelElementsMap; - Map get modelElementsMap { + Map> _modelElementsMap; + Map> get modelElementsMap { if (_modelElementsMap == null) { final Set results = new Set(); results @@ -1455,23 +1555,28 @@ class Library extends ModelElement { ..addAll(library.typedefs); library.allClasses.forEach((c) { - results.addAll(c.allInstanceMethods); - results.addAll(c.allInstanceProperties); - results.addAll(c.allOperators); - results.addAll(c.constructors); - results.addAll(c.staticMethods); - results.addAll(c.staticProperties); + results.addAll(c.allModelElements); + results.add(c); }); - _modelElementsMap = new Map(); - results.forEach((modelElement) => - _modelElementsMap[modelElement.element] = modelElement); - _modelElementsMap[element] = this; + _modelElementsMap = new Map>(); + results.forEach((modelElement) { + _modelElementsMap.putIfAbsent(modelElement.element, () => new Set()); + _modelElementsMap[modelElement.element].add(modelElement); + }); + _modelElementsMap.putIfAbsent(element, () => new Set()); + _modelElementsMap[element].add(this); } return _modelElementsMap; } - Iterable get allModelElements => modelElementsMap.values; + Iterable get allModelElements sync* { + for (Set modelElements in modelElementsMap.values) { + for (ModelElement modelElement in modelElements) { + yield modelElement; + } + } + } List _allCanonicalModelElements; Iterable get allCanonicalModelElements { @@ -1525,7 +1630,7 @@ class Method extends ModelElement String get href { if (canonicalLibrary == null || canonicalEnclosingElement == null) return null; - return '${canonicalEnclosingElement.library.dirName}/${canonicalEnclosingElement.name}/${fileName}'; + return '${canonicalEnclosingElement.canonicalLibrary.dirName}/${canonicalEnclosingElement.name}/${fileName}'; } @override @@ -1602,7 +1707,7 @@ class Method extends ModelElement /// helps prevent subtle bugs as generated output for a non-canonical /// ModelElement will reference itself as part of the "wrong" [Library] /// from the public interface perspective. -abstract class ModelElement implements Comparable, Nameable, Documentable { +abstract class ModelElement implements Comparable, Nameable, Documentable, Locatable { final Element _element; final Library _library; @@ -1623,27 +1728,41 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { /// Do not construct any ModelElements unless they are from this constructor. /// TODO(jcollins-g): enforce this. /// Specify enclosingClass only if this is to be an inherited object. + /// Specify index only if this is to be an EnumField.forConstant. + /// Specify enclosingField only if this is to be an Accessor. /// TODO(jcollins-g): this way of using the optional parameter is messy, /// clean that up. factory ModelElement.from(Element e, Library library, - {Class enclosingClass}) { - Tuple3 key = - new Tuple3(e, library, enclosingClass); + {Class enclosingClass, int index, ModelElement enclosingCombo}) { + // We don't need index in this key because it isn't a disambiguator. + // It isn't a disambiguator because EnumFields are not inherited, ever. + // TODO(jcollins-g): cleanup class hierarchy so that EnumFields aren't + // Inheritable, somehow? + if (e is Member) e = (e as Member).baseElement; + Tuple4 key = + new Tuple4(e, library, enclosingClass, enclosingCombo); ModelElement newModelElement; if (e.kind != ElementKind.DYNAMIC && - library.package._all_model_elements.containsKey(key)) { - newModelElement = library.package._all_model_elements[key]; + library.package._allConstructedModelElements.containsKey(key)) { + newModelElement = library.package._allConstructedModelElements[key]; } else { if (e.kind == ElementKind.DYNAMIC) { newModelElement = new Dynamic(e, library); } + if (e is LibraryElement) { + newModelElement = new Library(e, library.package); + } // Also handles enums if (e is ClassElement) { - newModelElement = new Class(e, library); - if (newModelElement.library.name == 'dart:core' && - newModelElement.name == 'Object') { - // We've found Object. This is an important object, so save it in the package. - newModelElement.library.package._objectElement = newModelElement; + if (!e.isEnum) { + newModelElement = new Class(e, library); + if (newModelElement.library.name == 'dart:core' && + newModelElement.name == 'Object') { + // We've found Object. This is an important object, so save it in the package. + newModelElement.library.package._objectElement = newModelElement; + } + } else { + newModelElement = new Enum(e, library); } } if (e is FunctionElement) { @@ -1653,10 +1772,19 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { newModelElement = new Typedef(e, library); } if (e is FieldElement) { - if (enclosingClass == null) - newModelElement = new Field(e, library); - else - newModelElement = new Field.inherited(e, enclosingClass, library); + if (enclosingClass == null) { + if (index != null) { + newModelElement = new EnumField.forConstant(index, e, library); + } else { + if (e.enclosingElement.isEnum) { + newModelElement = new EnumField(e, library); + } else { + newModelElement = new Field(e, library); + } + } + } else { + newModelElement = new Field.inherited(e, enclosingClass, library); + } } if (e is ConstructorElement) { newModelElement = new Constructor(e, library); @@ -1672,12 +1800,13 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { newModelElement = new Method(e, library); else newModelElement = new Method.inherited(e, enclosingClass, library); + 1+1; } if (e is TopLevelVariableElement) { newModelElement = new TopLevelVariable(e, library); } if (e is PropertyAccessorElement) { - newModelElement = new Accessor(e, library); + newModelElement = new Accessor(e, library, enclosingCombo); } if (e is TypeParameterElement) { newModelElement = new TypeParameter(e, library); @@ -1688,8 +1817,22 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { } if (newModelElement == null) throw "Unknown type ${e.runtimeType}"; if (enclosingClass != null) assert(newModelElement is Inheritable); - if (library != null) - library.package._all_model_elements[key] = newModelElement; + if (library != null) { + library.package._allConstructedModelElements[key] = newModelElement; + if (newModelElement is Inheritable) { + Tuple2 iKey = new Tuple2(e, library); + library.package._allInheritableElements.putIfAbsent(iKey, () => new Set()); + library.package._allInheritableElements[iKey].add(newModelElement); + } + } + if (newModelElement.fullyQualifiedName == 'animation.AnimationController.view') + 1+1; + if (newModelElement is Accessor) { + assert(newModelElement.enclosingCombo == enclosingCombo); + } else { + assert(enclosingCombo == null); + } + return newModelElement; } @@ -1821,7 +1964,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { // If path inspection or other disambiguation heuristics are needed, // they should go here. if (candidateLibraries.length > 1) { - library.package.warn(this, PackageWarning.ambiguousReexport, + warn(PackageWarning.ambiguousReexport, "${candidateLibraries.map((l) => l.name)}"); } if (candidateLibraries.isNotEmpty) @@ -1842,6 +1985,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { return _canonicalLibrary; } + @override bool get isCanonical { if (library == canonicalLibrary) { if (this is Inheritable) { @@ -1864,23 +2008,39 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { @override String get documentationAsHtml => _documentation.asHtml; + @override Element get element => _element; + @override + String get elementLocation { + // Call nothing from here that can emit warnings. + if (lineAndColumn != null) { + return "(${p.toUri(sourceFileName)}:${lineAndColumn.item1}:${lineAndColumn.item2})"; + } + return "(${p.toUri(sourceFileName)})"; + } + /// Returns the fully qualified name. /// /// For example: libraryName.className.methodName + @override String get fullyQualifiedName { return (_fullyQualifiedName ??= _buildFullyQualifiedName()); } String get fullyQualifiedNameWithoutLibrary { - return (_fullyQualifiedNameWithoutLibrary ??= - fullyQualifiedName.split(".").skip(1).join(".")); + // Remember, periods are legal in library names. + if (_fullyQualifiedNameWithoutLibrary == null) { + _fullyQualifiedNameWithoutLibrary = fullyQualifiedName.replaceFirst("${library.fullyQualifiedName}.", ''); + } + return _fullyQualifiedNameWithoutLibrary; + //return (_fullyQualifiedNameWithoutLibrary ??= + // fullyQualifiedName.split(".").skip(1).join(".")); } String get sourceFileName => element.source.fullName; - int _lineNumber; + /*int _lineNumber; bool _isLineNumberComputed = false; int get lineNumber { if (!_isLineNumberComputed) { @@ -1895,6 +2055,17 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { } return _lineNumber; } + */ + Tuple2 _lineAndColumn; + bool _isLineNumberComputed = false; + @override + Tuple2 get lineAndColumn { + // TODO(jcollins-g): we should always be able to get line numbers. Why can't we, sometimes? + if (!_isLineNumberComputed) { + _lineAndColumn = lineNumberCache.lineAndColumn(element.source.fullName, element.nameOffset); + } + return _lineAndColumn; + } bool get hasAnnotations => annotations.isNotEmpty; @@ -1906,6 +2077,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { /// If canonicalLibrary (or canonicalEnclosingElement, for Inheritable /// subclasses) is null, href should be null. + @override String get href; String get htmlId => name; @@ -1986,6 +2158,23 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { ModelElement get overriddenElement => null; + // TODO(jcollins-g): rewrite this + ModelElement _overriddenDocumentedElement; + bool _overriddenDocumentedElementIsSet = false; + ModelElement get overriddenDocumentedElement { + if (!_overriddenDocumentedElementIsSet) { + ModelElement found = this; + while ((found.element.documentationComment == null || + found.element.documentationComment == "") && !found.isCanonical && + found.overriddenElement != null) { + found = found.overriddenElement; + } + _overriddenDocumentedElement = found; + _overriddenDocumentedElementIsSet = true; + } + return _overriddenDocumentedElement; + } + int _overriddenDepth; int get overriddenDepth { if (_overriddenDepth == null) { @@ -2002,6 +2191,30 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { Package get package => (this is Library) ? (this as Library).package : this.library.package; + List _allParameters; + List get allParameters { + if (_allParameters == null) { + final Set recursedParameters = new Set(); + final Set newParameters = new Set(); + if (this is GetterSetterCombo && (this as GetterSetterCombo).setter != null) { + newParameters.addAll((this as GetterSetterCombo).setter.parameters); + } else { + if (canHaveParameters) newParameters.addAll(parameters); + } + while (newParameters.isNotEmpty) { + recursedParameters.addAll(newParameters); + newParameters.clear(); + for (Parameter p in recursedParameters) { + if (p.modelType.element.canHaveParameters ) { + newParameters.addAll(p.modelType.element.parameters.where((p) => !recursedParameters.contains(p))); + } + } + } + _allParameters = recursedParameters.toList(); + } + return _allParameters; + } + List get parameters { if (!canHaveParameters) { throw new StateError("$element cannot have parameters"); @@ -2027,6 +2240,17 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { return _parameters; } + /// Sugar for the package warning system. + void warn(PackageWarning kind, [String message]) { + if (kind == PackageWarning.unresolvedDocReference && overriddenElement != null) { + // The documentation we're using for this element came from somewhere else. + // Attach the warning to that element to deduplicate. + overriddenElement.warn(kind, message); + } else { + library.package.warn(this, kind, message); + } + } + String get _computeDocumentationComment => element.documentationComment; Documentation get _documentation { @@ -2204,7 +2428,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable { } if (href == null) { - package.warn(this, PackageWarning.noCanonicalFound); + warn(PackageWarning.noCanonicalFound); return HTML_ESCAPE.convert(name); } @@ -2486,27 +2710,177 @@ class Operator extends Method { // The kinds of warnings that can be displayed when documenting a package. enum PackageWarning { + ambiguousDocReference, ambiguousReexport, noCanonicalFound, noLibraryLevelDocs, categoryOrderGivesMissingPackageName, + unresolvedDocReference, + brokenLink, + orphanedFile, + unknownFile, + typeAsHtml, +} + +/// Provides description text and command line flags for warnings. +Map> packageWarningText = { + PackageWarning.ambiguousDocReference: + ["ambiguous-doc-reference", "A comment reference could refer to two or more different objects"], + PackageWarning.ambiguousReexport: + ["ambiguous-reexport", "A symbol is exported from private to public in more than one place and dartdoc is forced to guess which one is canonical"], + PackageWarning.noCanonicalFound: + ["no-canonical-found", "A symbol is is part of the public interface for this package, but no library documented with this package documents it so dartdoc can not link to it"], + PackageWarning.noLibraryLevelDocs: + ["no-library-level-docs", "There are no library level docs for this library"], + PackageWarning.categoryOrderGivesMissingPackageName: + ["category-order-gives-missing-package-name", "The category-order flag on the command line was given the name of a nonexistent package"], + PackageWarning.unresolvedDocReference: + ["unresolved-doc-reference", "A comment reference could not be found in parameters, enclosing class, enclosing library, or at the top level of any documented library with the package"], + PackageWarning.brokenLink: + ["brokenLink", "Dartdoc generated a link to a non-existent file"], + PackageWarning.orphanedFile: + ["orphanedFile", "Dartdoc generated files that are unreachable from the index"], + PackageWarning.unknownFile: + ["unknownFile", "A leftover file exists in the tree that dartdoc did not write in this pass"], + PackageWarning.typeAsHtml: + ["typeAsHtml", "Use of <> in a comment for type parameters is being treated as HTML by markdown"], +}; + +// Something that can be located for warning purposes. +abstract class Locatable { + String get fullyQualifiedName; + String get href; + Element get element; + String get elementLocation; + Tuple2 get lineAndColumn; + bool get isCanonical; } -class Package implements Nameable, Documentable { +class PackageWarningOptions { + // PackageWarnings must be in one of _ignoreWarnings or union(_asWarnings, _asErrors) + final Set _ignoreWarnings = new Set(); + // PackageWarnings can be in both asWarnings and asErrors, latter takes precedence + final Set _asWarnings = new Set(); + final Set _asErrors = new Set(); + + Set get ignoreWarnings => _ignoreWarnings; + Set get asWarnings => _asWarnings; + Set get asErrors => _asErrors; + + PackageWarningOptions() { + _asWarnings.addAll(PackageWarning.values); + ignore(PackageWarning.typeAsHtml); + //error(PackageWarning.brokenLink); + //error(PackageWarning.orphanedFile); + } + + void _assertInvariantsOk() { + assert(_asWarnings.union(_asErrors).union(_ignoreWarnings).containsAll(PackageWarning.values.toSet())); + assert(_asWarnings.union(_asErrors).intersection(_ignoreWarnings).isEmpty); + } + + void ignore(PackageWarning kind) { + _assertInvariantsOk(); + _asWarnings.remove(kind); + _asErrors.remove(kind); + _ignoreWarnings.add(kind); + _assertInvariantsOk(); + } + + void warn(PackageWarning kind) { + _assertInvariantsOk(); + _ignoreWarnings.remove(kind); + _asWarnings.add(kind); + _asErrors.remove(kind); + _assertInvariantsOk(); + } + + void error(PackageWarning kind) { + _assertInvariantsOk(); + _ignoreWarnings.remove(kind); + _asWarnings.add(kind); + _asErrors.add(kind); + _assertInvariantsOk(); + } +} + +class PackageWarningCounter { + final Map>> _countedWarnings = new Map(); + final Map _warningCounts = new Map(); + final PackageWarningOptions options; + + PackageWarningCounter(this.options); + + /// Actually write out the warning. Assumes it is already counted with add. + void _writeWarning(PackageWarning kind, String fullMessage) { + if (options.ignoreWarnings.contains(kind)) return; + String toWrite; + if (!options.asErrors.contains(kind)) { + if (options.asWarnings.contains(kind)) toWrite = "warning: ${fullMessage}"; + } else { + if (options.asErrors.contains(kind)) toWrite = "error: ${fullMessage}"; + } + if (toWrite != null) stderr.write("\n ${toWrite}"); + } + + /// Returns true if we've already warned for this. + bool hasWarning(Element element, PackageWarning kind, String message) { + Tuple2 warningData = new Tuple2(kind, message); + if (_countedWarnings.containsKey(element)) { + return _countedWarnings[element].contains(warningData); + } + return false; + } + + /// Adds the warning to the counter, and writes out the fullMessage string + /// if configured to do so. + void addWarning(Element element, PackageWarning kind, String message, + String fullMessage) { + assert(!hasWarning(element, kind, message)); + Tuple2 warningData = new Tuple2(kind, message); + _warningCounts.putIfAbsent(kind, () => 0); + _warningCounts[kind] += 1; + _countedWarnings.putIfAbsent(element, () => new Set()); + _countedWarnings[element].add(warningData); + _writeWarning(kind, fullMessage); + } + + int get errorCount { + return _warningCounts.keys.map((w) => options.asErrors.contains(w) ? _warningCounts[w] : 0).reduce((a, b) => a + b); + } + + int get warningCount { + return _warningCounts.keys.map((w) => options.asWarnings.contains(w) && !options.asErrors.contains(w) ? _warningCounts[w] : 0).reduce((a, b) => a + b); + } + + @override + String toString() { + String errors = '$errorCount ${errorCount == 1 ? "error" : "errors"}'; + String warnings = '$warningCount ${warningCount == 1 ? "warning" : "warnings"}'; + return [errors, warnings].join(', '); + } +} + +class Package implements Nameable, Documentable, Locatable { // Library objects serving as entry points for documentation. final List _libraries = []; // All library objects related to this package; a superset of _libraries. - final Map _all_libraries = new Map(); - // All ModelElements constructed for this package; a superset of allModelElements. - final Map, ModelElement> _all_model_elements = + final Map _allLibraries = new Map(); + + + final PackageWarningOptions packageWarningOptions; + PackageWarningCounter _packageWarningCounter; + // All ModelElements constructed for this package; a superset of allModelElements. + final Map, ModelElement> _allConstructedModelElements = new Map(); + // Anything that might be inheritable, place here for later lookup. + final Map, Set> _allInheritableElements = new Map(); + /// Map of Class.href to a list of classes implementing that class final Map> _implementors = new Map(); - final Map> _displayedWarnings = new Map(); - final Map> _displayedNonElementWarnings = - new Map(); + //final Map>> _displayedWarnings = new Map(); final PackageMeta packageMeta; @@ -2515,15 +2889,16 @@ class Package implements Nameable, Documentable { final Map _macros = {}; bool allLibrariesAdded = false; - Package(Iterable libraryElements, this.packageMeta) { - assert(_all_model_elements.isEmpty); - assert(_all_libraries.isEmpty); + Package(Iterable libraryElements, this.packageMeta, this.packageWarningOptions) { + assert(_allConstructedModelElements.isEmpty); + assert(_allLibraries.isEmpty); + _packageWarningCounter = new PackageWarningCounter(packageWarningOptions); libraryElements.forEach((element) { // add only if the element should be included in the public api if (isPublic(element)) { var lib = new Library._(element, this); _libraries.add(lib); - _all_libraries[element] = lib; + _allLibraries[element] = lib; assert(!_elementToLibrary.containsKey(lib.element)); _elementToLibrary[element] = lib; } @@ -2538,40 +2913,77 @@ class Package implements Nameable, Documentable { _implementors.values.forEach((l) => l.sort()); } - void warn(ModelElement modelElement, PackageWarning kind, [String message]) { - String elementName; - String fullElementName; - if (modelElement != null) { - if (_displayedWarnings.containsKey(modelElement.element) && - _displayedWarnings[modelElement.element].contains(kind)) { - return; - } + @override + Element get element => null; + + @override + String get elementLocation => '(top level package)'; + + @override + Tuple2 get lineAndColumn => null; + + @override + String get fullyQualifiedName => name; + + @override + bool get isCanonical => true; + + PackageWarningCounter get packageWarningCounter => _packageWarningCounter; - if (modelElement.element is ClassMemberElement) { - Element theElement = modelElement.element; - ClassElement enclosingClass; - while (theElement is! ClassElement && - theElement.enclosingElement != null) { - theElement = theElement.enclosingElement; + void warn(Locatable modelElement, PackageWarning kind, [String message]) { + //Tuple2 warningData = new Tuple2(kind, message); + if (modelElement != null) { + // This sort of warning is only applicable to top level elements. + if (kind == PackageWarning.ambiguousReexport) { + Element topLevelElement = modelElement.element; + while (topLevelElement.enclosingElement is! CompilationUnitElement) { + topLevelElement = topLevelElement.enclosingElement; } - enclosingClass = theElement as ClassElement; + modelElement = new ModelElement.from(topLevelElement, findOrCreateLibraryFor(topLevelElement)); + } + if (modelElement is Accessor) { + // This might be part of a Field, if so, assign this warning to the field + // rather than the Accessor. + if ((modelElement as Accessor).enclosingCombo != null) + modelElement = (modelElement as Accessor).enclosingCombo; + } + /*if (modelElement.element is ClassMemberElement) { + ClassElement enclosingClass = modelElement.element.getAncestor((e) => e is ClassElement); if (_displayedWarnings.containsKey(enclosingClass) && _displayedWarnings[enclosingClass].contains(kind)) return; elementName = "${enclosingClass.name}.${modelElement.name}"; } else { elementName = "${modelElement.name}"; } + fullElementName = "${elementName} (${modelElement.library.element.location})"; + */ } else { // If we don't have an element, we need a message to disambiguate. assert(message != null); - // Checking for kinds here seems a bit redundant right now, but for - // consistency... - if (_displayedNonElementWarnings.containsKey(message) && - _displayedNonElementWarnings[message].contains(kind)) { - return; + } + if (_packageWarningCounter.hasWarning(modelElement?.element, kind, message)) { + return; + } + /*if (_displayedWarnings.containsKey(modelElement?.element) && + _displayedWarnings[modelElement?.element].contains(warningData)) { + return; + }*/ + // Elements that are part of the Dart SDK can have colons in their FQNs. + // This confuses IntelliJ and makes it so it can't link to the location + // of the error in the console window, so separate out the library from + // the path. + String nameSplitFromColonPieces; + if (modelElement != null) { + nameSplitFromColonPieces = modelElement.fullyQualifiedName; + /* + List nameParts = nameSplitFromColonPieces.split('.'); + if (nameParts.length > 0 && nameParts[0].contains(':')) { + nameSplitFromColonPieces = '${nameParts[0]} ${nameParts.sublist(1).join(".")}'; } + */ + nameSplitFromColonPieces = modelElement.fullyQualifiedName.replaceFirst(':', '-'); } String warningMessage; switch (kind) { @@ -2581,38 +2993,63 @@ class Package implements Nameable, Documentable { // TODO(jcollins-g): add a dartdoc flag to enable external website linking for non-canonical elements, using .packages for versioning // TODO(jcollins-g): support documenting multiple packages at once and linking between them warningMessage = - "no canonical library found for ${fullElementName}, not linking"; + "no canonical library found for ${nameSplitFromColonPieces}, not linking"; break; case PackageWarning.ambiguousReexport: // Fix these warnings by adding the original library exporting the // symbol with --include, or by using --auto-include-dependencies. // TODO(jcollins-g): add a dartdoc flag to force a particular resolution order for (or drop) ambiguous reexports warningMessage = - "ambiguous reexport of ${fullElementName}, canonicalization candidates: ${message}"; + "ambiguous reexport of ${nameSplitFromColonPieces}, canonicalization candidates: ${message}"; break; case PackageWarning.noLibraryLevelDocs: warningMessage = - "${fullElementName} has no library level documentation comments"; + "${modelElement.fullyQualifiedName} has no library level documentation comments"; + break; + case PackageWarning.ambiguousDocReference: + warningMessage = + "ambiguous doc reference in ${nameSplitFromColonPieces}: ${message}"; break; case PackageWarning.categoryOrderGivesMissingPackageName: warningMessage = - "--category-order gives invalid package name: ${message}"; + "--category-order gives invalid package name: '${message}'"; + break; + case PackageWarning.unresolvedDocReference: + warningMessage = + "unresolved doc reference [${message}], from ${nameSplitFromColonPieces}"; + break; + case PackageWarning.brokenLink: + warningMessage = + 'dartdoc generated a broken link to: ${message}, from ${nameSplitFromColonPieces == null ? "" : nameSplitFromColonPieces}'; + break; + case PackageWarning.orphanedFile: + warningMessage = + 'dartdoc generated a file orphan: ${message}, from ${nameSplitFromColonPieces == null ? "" : nameSplitFromColonPieces}'; + break; + case PackageWarning.unknownFile: + warningMessage = + 'dartdoc detected an unknown file in the doc tree: ${message}'; + break; + case PackageWarning.typeAsHtml: + warningMessage = + 'generic type handled as HTML: """${message}"""'; break; } - stderr.write("\n warning: ${warningMessage}"); - if (modelElement != null) { - _displayedWarnings.putIfAbsent(modelElement.element, () => new Set()); - _displayedWarnings[modelElement.element].add(kind); - } else { - _displayedNonElementWarnings.putIfAbsent(message, () => new Set()); - _displayedNonElementWarnings[message].add(kind); - } + // warningMessage should not contain "file:" or "dart:" -- confuses IntelliJ. + ['file:', 'dart:'].forEach((s) { + // message can contain user text; nothing we can do about that. + assert(!warningMessage.contains(s) || message.contains(s)); + }); + String fullMessage = "${warningMessage} ${modelElement != null ? modelElement.elementLocation : ''}"; + packageWarningCounter.addWarning(modelElement?.element, kind, message, fullMessage); + //_displayedWarnings.putIfAbsent(modelElement?.element, () => new Set()); + //_displayedWarnings[modelElement?.element].add(warningData); } static Package _withAutoIncludedDependencies( - Set libraryElements, PackageMeta packageMeta) { + Set libraryElements, PackageMeta packageMeta, PackageWarningOptions options) { var startLength = libraryElements.length; - Package package = new Package(libraryElements, packageMeta); + Package package = new Package(libraryElements, packageMeta, options); // TODO(jcollins-g): this is inefficient; keep track of modelElements better package.allModelElements.forEach((modelElement) { @@ -2632,14 +3069,14 @@ class Package implements Nameable, Documentable { }); if (libraryElements.length > startLength) - return _withAutoIncludedDependencies(libraryElements, packageMeta); + return _withAutoIncludedDependencies(libraryElements, packageMeta, options); return package; } static Package withAutoIncludedDependencies( - Iterable libraryElements, PackageMeta packageMeta) { + Iterable libraryElements, PackageMeta packageMeta, PackageWarningOptions options) { return _withAutoIncludedDependencies( - new Set()..addAll(libraryElements), packageMeta); + new Set()..addAll(libraryElements), packageMeta, options); } List get categories { @@ -2698,6 +3135,24 @@ class Package implements Nameable, Documentable { return hasDocumentationFile ? documentationFile.contents : null; } + /// A lookup index for hrefs to allow warnings to indicate where a broken + /// link or orphaned file may have come from. Not cached because + /// [ModelElement]s can be created at any time and we're basing this + /// on more than just [allModelElements] to make the error messages + /// comprehensive. + Map> get allHrefs { + Map> hrefMap = new Map(); + // toList to avoid concurrent modification. + // TODO(jcollins): handle calculating hrefs causing new elements better + // than toList() + for (ModelElement modelElement in _allConstructedModelElements.values.toList()) { + if (modelElement.href == null) continue; + hrefMap.putIfAbsent(modelElement.href, () => new Set()); + hrefMap[modelElement.href].add(modelElement); + } + return hrefMap; + } + @override String get documentationAsHtml { if (_docsAsHtml != null) return _docsAsHtml; @@ -2719,6 +3174,7 @@ class Package implements Nameable, Documentable { bool get hasDocumentationFile => documentationFile != null; // TODO: make this work + @override String get href => 'index.html'; /// Does this package represent the SDK? @@ -2797,34 +3253,104 @@ class Package implements Nameable, Documentable { String toString() => isSdk ? 'SDK' : 'Package $name'; final Map _canonicalLibraryFor = new Map(); - /// Tries to find a top level library that references this element. Library findCanonicalLibraryFor(Element e) { assert(allLibrariesAdded); + Element searchElement = e; + if (e is PropertyAccessorElement) { + searchElement = e.variable; + } if (_canonicalLibraryFor.containsKey(e)) { return _canonicalLibraryFor[e]; } _canonicalLibraryFor[e] = null; for (Library library in libraries) { - if (library.modelElementsMap.containsKey(e)) { - if (library.modelElementsMap[e].isCanonical) { - _canonicalLibraryFor[e] = library; - break; + if (library.modelElementsMap.containsKey(searchElement)) { + for (ModelElement modelElement in library.modelElementsMap[searchElement]) { + if (modelElement.isCanonical) { + _canonicalLibraryFor[e] = library; + break; + } } } } return _canonicalLibraryFor[e]; } - /// Tries to find a canonical ModelElement for this element. - /// Do not use this to find Inheritable ModelElements; we can't - /// figure out whether this element is inherited by ourselves. - ModelElement findCanonicalModelElementFor(Element e) { + /// Tries to find a canonical ModelElement for this element. If we know + /// this element is related to a particular class, pass preferredClass to + /// disambiguate. + ModelElement findCanonicalModelElementFor(Element e, {Class preferredClass}) { + assert(allLibrariesAdded); Library lib = findCanonicalLibraryFor(e); ModelElement modelElement; - if (lib != null) modelElement = new ModelElement.from(e, lib); - assert(modelElement is! Inheritable); + // TODO(jcollins-g): The data structures should be changed to eliminate guesswork + // with member elements. + if (e is ClassMemberElement || e is PropertyAccessorElement) { + // Prefer Fields over Accessors. + if (e is PropertyAccessorElement) e = (e as PropertyAccessorElement).variable; + Set candidates = new Set(); + Tuple2 iKey = new Tuple2(e, lib); + Tuple4 key = new Tuple4(e, lib, null, null); + Tuple4 keyWithClass = new Tuple4(e, lib, preferredClass, null); + if (_allConstructedModelElements.containsKey(key)) { + candidates.add(_allConstructedModelElements[key]); + } + if (_allConstructedModelElements.containsKey(keyWithClass)) { + candidates.add(_allConstructedModelElements[keyWithClass]); + } + if (candidates.isEmpty && _allInheritableElements.containsKey(iKey)) { + candidates.addAll(_allInheritableElements[iKey].where((me) => me.isCanonical)); + } + Class canonicalClass = findCanonicalModelElementFor(e.enclosingElement); + if (canonicalClass != null) { + candidates.addAll(canonicalClass.allCanonicalModelElements.where((m) { + if (m.element is FieldElement) { + FieldElement fieldElement = m.element as FieldElement; + Element getter; + Element setter; + if (fieldElement.getter?.isSynthetic == true) { + getter = fieldElement.getter.variable; + } else { + getter = fieldElement.getter; + } + if (fieldElement.setter?.isSynthetic == true) { + setter = fieldElement.setter.variable; + } else { + setter = fieldElement.setter; + } + if (setter == e || getter == e) return true; + } + if (m.element == e) return true; + return false; + })); + } + Set matches = new Set()..addAll(candidates.where((me) => me.isCanonical)); + /*if (matches.length > 1) { + if (preferredClass != null && matches.any((me) => (me as EnclosedElement).enclosingElement == preferredClass)) { + matches.removeWhere((me) => (me as EnclosedElement).enclosingElement != preferredClass); + } + }*/ + // This is for situations where multiple classes may derive + if (matches.length > 1 && preferredClass != null) { + // Search for matches inside our superchain. + List superChain = preferredClass.superChainRaw.map((et) => et.element).toList(); + superChain.add(preferredClass); + // Pretty sure this is not correct: FIXME before submission + matches.removeWhere((me) => !superChain.contains((me as EnclosedElement).enclosingElement)); + } + assert(matches.length <= 1); + if (!matches.isEmpty) modelElement = matches.first; + if (modelElement == null && e.enclosingElement.library.name != 'dart._internal' && !e.name.startsWith('_')) + 1+1; + } else { + if (lib != null) modelElement = new ModelElement.from(e, lib); + assert(modelElement is! Inheritable); + if (modelElement != null && !modelElement.isCanonical) { + modelElement = null; + } + } return modelElement; } @@ -2833,8 +3359,8 @@ class Package implements Nameable, Documentable { /// set of canonical Libraries). Library findOrCreateLibraryFor(Element e) { // This is just a cache to avoid creating lots of libraries over and over. - if (_all_libraries.containsKey(e.library)) { - return _all_libraries[e.library]; + if (_allLibraries.containsKey(e.library)) { + return _allLibraries[e.library]; } // can be null if e is for dynamic if (e.library == null) { @@ -2844,13 +3370,14 @@ class Package implements Nameable, Documentable { if (foundLibrary == null) { foundLibrary = new Library._(e.library, this); - _all_libraries[e.library] = foundLibrary; + _allLibraries[e.library] = foundLibrary; } return foundLibrary; } List _allModelElements; Iterable get allModelElements { + assert(allLibrariesAdded); if (_allModelElements == null) { _allModelElements = []; this.libraries.forEach((library) { @@ -2984,7 +3511,7 @@ abstract class SourceCodeMixin { } } - int get lineNumber; + Tuple2 get lineAndColumn; Element get element; @@ -3068,9 +3595,9 @@ abstract class SourceCodeMixin { } String get _crossdartUrl { - if (lineNumber != null && _crossdartPath != null) { + if (lineAndColumn != null && _crossdartPath != null) { String url = "//www.crossdart.info/p/${_crossdartPath}.html"; - return "${url}#line-${lineNumber}"; + return "${url}#line-${lineAndColumn.item1}"; } else { return null; } @@ -3142,13 +3669,6 @@ class TopLevelVariable extends ModelElement @override String get kind => 'top-level property'; - String get linkedReturnType => modelType.linkedName; - - bool get readOnly => hasGetter && !hasSetter; - bool get readWrite => hasGetter && hasSetter; - - bool get writeOnly => hasSetter && !hasGetter; - @override Set get features { Set all_features = super.features; diff --git a/lib/src/reporting.dart b/lib/src/reporting.dart deleted file mode 100644 index f52f9748d7..0000000000 --- a/lib/src/reporting.dart +++ /dev/null @@ -1,18 +0,0 @@ -// 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. - -library reporting; - -import 'dart:io'; - -import 'config.dart'; - -// TODO(jcollins-g): Merge this with the new [Package] error reporting. -void warning(String message) { - // TODO: Could handle fatal warnings here, or print to stderr, or remember - // that we had at least one warning, and exit with non-null exit code in this case. - if (config != null && config.showWarnings) { - stderr.writeln("warning: ${message}"); - } -} diff --git a/lib/templates/_property.html b/lib/templates/_property.html index 47f83107dd..7ac2c275e2 100644 --- a/lib/templates/_property.html +++ b/lib/templates/_property.html @@ -1,7 +1,16 @@ +{{ #hasExplicitSetter }} +
+ {{{linkedName}}}{{{genericParameters}}} + {{{ arrow }}} {{{ linkedParamsNoMetadata }}} + +
+{{ /hasExplicitSetter }} +{{ #hasGetterOrSetterWithoutParams }}
{{{linkedName}}} - → {{{ linkedReturnType }}} + {{{ arrow }}} {{{ linkedReturnType }}}
+{{ /hasGetterOrSetterWithoutParams }} {{{ oneLineDoc }}} {{>features}} diff --git a/test/model_test.dart b/test/model_test.dart index c2aa4a3702..0142f3147b 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -37,7 +37,7 @@ void main() { Package sdkAsPackage = Package.withAutoIncludedDependencies( getSdkLibrariesToDocument(utils.sdkDir, utils.analyzerHelper.context), - new PackageMeta.fromSdk(sdkDir)); + new PackageMeta.fromSdk(sdkDir), new PackageWarningOptions()); group('Package', () { group('test package', () { @@ -314,9 +314,9 @@ void main() { }); test( - 'link to a name in another library in this package, but is not imported into this library, is codeified', + 'link to a name in another library in this package, but is not imported into this library, should still be linked', () { - expect(docsAsHtml, contains('doesStuff')); + expect(docsAsHtml, contains('doesStuff')); }); test( @@ -327,7 +327,7 @@ void main() { expect(helperClass.documentationAsHtml, contains('Apple')); expect(helperClass.documentationAsHtml, - contains('B')); + contains('ex.B')); }); test( @@ -478,7 +478,7 @@ void main() { expect(resolved, isNotNull); expect(resolved, contains('BaseClass')); - expect(resolved, contains('linking over to Apple.')); + expect(resolved, contains('linking over to Apple.')); }); test('references to class and constructors', () { @@ -1469,7 +1469,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect(appleDefaultConstructor.enclosingElement.name, equals(apple.name)); }); - test('has contructor', () { + test('has constructor', () { expect(appleDefaultConstructor, isNotNull); expect(appleDefaultConstructor.name, equals('Apple')); expect(appleDefaultConstructor.shortName, equals('Apple')); diff --git a/test/src/utils.dart b/test/src/utils.dart index e69de759bc..f66ae0b661 100644 --- a/test/src/utils.dart +++ b/test/src/utils.dart @@ -69,10 +69,12 @@ Package _bootPackage(Iterable libPaths, String dirPath, if (withAutoIncludedDependencies) { return Package.withAutoIncludedDependencies( - libElements, new PackageMeta.fromDir(new Directory(dirPath))); + libElements, new PackageMeta.fromDir(new Directory(dirPath)), + new PackageWarningOptions()); } else { return new Package( - libElements, new PackageMeta.fromDir(new Directory(dirPath))); + libElements, new PackageMeta.fromDir(new Directory(dirPath)), + new PackageWarningOptions()); } } diff --git a/testing/test_package_docs/css/css-library.html b/testing/test_package_docs/css/css-library.html index d2a331e4c4..d6603ad877 100644 --- a/testing/test_package_docs/css/css-library.html +++ b/testing/test_package_docs/css/css-library.html @@ -97,7 +97,7 @@

Properties

theOnlyThingInTheLibrary - → String + ↔ String

diff --git a/testing/test_package_docs/ex/Apple-class.html b/testing/test_package_docs/ex/Apple-class.html index a6ba69caaa..100a01a1e1 100644 --- a/testing/test_package_docs/ex/Apple-class.html +++ b/testing/test_package_docs/ex/Apple-class.html @@ -191,15 +191,20 @@

Properties

m - → int + ↔ int

The read-write field m.

read / write
+
+ s + String something + +
s - → String + ↔ String

The getter for s

@@ -332,7 +337,7 @@

Static Properties

string - → String + ↔ String

diff --git a/testing/test_package_docs/ex/B-class.html b/testing/test_package_docs/ex/B-class.html index 2266668605..265fdb4233 100644 --- a/testing/test_package_docs/ex/B-class.html +++ b/testing/test_package_docs/ex/B-class.html @@ -180,7 +180,7 @@

Properties

autoCompress - → bool + ↔ bool

The default value is false (compression disabled). @@ -197,7 +197,7 @@

Properties

list - → List<String> + ↔ List<String>

A list of Strings

@@ -229,7 +229,7 @@

Properties

m - → int + ↔ int

The read-write field m.

diff --git a/testing/test_package_docs/ex/CatString-class.html b/testing/test_package_docs/ex/CatString-class.html index a00f3b668c..fcbe2a9563 100644 --- a/testing/test_package_docs/ex/CatString-class.html +++ b/testing/test_package_docs/ex/CatString-class.html @@ -259,7 +259,7 @@

Methods

-

Iterates over the given objects and writes them in sequence.

+

Iterates over the given objects and writes them in sequence.

inherited
diff --git a/testing/test_package_docs/ex/CatString/writeAll.html b/testing/test_package_docs/ex/CatString/writeAll.html index 33c5b428ec..6cf5419995 100644 --- a/testing/test_package_docs/ex/CatString/writeAll.html +++ b/testing/test_package_docs/ex/CatString/writeAll.html @@ -107,7 +107,7 @@
CatString
writeAll(Iterable objects, [ String separator = "" ])
-

Iterates over the given objects and writes them in sequence.

+

Iterates over the given objects and writes them in sequence.

diff --git a/testing/test_package_docs/ex/Dog-class.html b/testing/test_package_docs/ex/Dog-class.html index 7d82d10bca..01a9947c21 100644 --- a/testing/test_package_docs/ex/Dog-class.html +++ b/testing/test_package_docs/ex/Dog-class.html @@ -220,7 +220,7 @@

Properties

deprecatedField - → int + ↔ int

@@ -235,8 +235,9 @@

Properties

@deprecated, read-only
- deprecatedSetter - → int + deprecatedSetter + int value +

@@ -252,7 +253,7 @@

Properties

name - → String + ↔ String

@@ -409,9 +410,14 @@

Static Properties

A tasty static + final property.

final
+
+ staticGetterSetter + x + +
staticGetterSetter - → int + ↔ int

diff --git a/testing/test_package_docs/ex/F-class.html b/testing/test_package_docs/ex/F-class.html index 3464e6cc22..cdd8b02726 100644 --- a/testing/test_package_docs/ex/F-class.html +++ b/testing/test_package_docs/ex/F-class.html @@ -195,7 +195,7 @@

Properties

deprecatedField - → int + ↔ int

@@ -210,8 +210,9 @@

Properties

@deprecated, read-only, inherited
- deprecatedSetter - → int + deprecatedSetter + int value +

@@ -235,7 +236,7 @@

Properties

name - → String + ↔ String

diff --git a/testing/test_package_docs/ex/Helper-class.html b/testing/test_package_docs/ex/Helper-class.html index 76cf0cd2c6..c1b4701a78 100644 --- a/testing/test_package_docs/ex/Helper-class.html +++ b/testing/test_package_docs/ex/Helper-class.html @@ -139,7 +139,7 @@
ex

Even unresolved references in the same library should be resolved Apple -B

+ex.B

diff --git a/testing/test_package_docs/ex/WithGeneric-class.html b/testing/test_package_docs/ex/WithGeneric-class.html index f295f681fc..f1e4adb7fa 100644 --- a/testing/test_package_docs/ex/WithGeneric-class.html +++ b/testing/test_package_docs/ex/WithGeneric-class.html @@ -169,7 +169,7 @@

Properties

prop - → T + ↔ T

diff --git a/testing/test_package_docs/ex/WithGenericSub-class.html b/testing/test_package_docs/ex/WithGenericSub-class.html index 4326ef2f9c..68dd03019c 100644 --- a/testing/test_package_docs/ex/WithGenericSub-class.html +++ b/testing/test_package_docs/ex/WithGenericSub-class.html @@ -179,7 +179,7 @@

Properties

prop - Apple + ↔ T

diff --git a/testing/test_package_docs/ex/ex-library.html b/testing/test_package_docs/ex/ex-library.html index c388687b9e..5bdc4d055b 100644 --- a/testing/test_package_docs/ex/ex-library.html +++ b/testing/test_package_docs/ex/ex-library.html @@ -182,7 +182,7 @@

Classes

Even unresolved references in the same library should be resolved Apple -B

+ex.B

Klass @@ -342,7 +342,7 @@

Properties

deprecatedField - → int + ↔ int

@@ -357,8 +357,9 @@

Properties

read-only
- deprecatedSetter - → int + deprecatedSetter + int value +

@@ -366,7 +367,7 @@

Properties

number - → double + ↔ double

diff --git a/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html b/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html index 2f8f68f34c..365fb6b169 100644 --- a/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html +++ b/testing/test_package_docs/fake/BaseForDocComments/doAwesomeStuff.html @@ -115,7 +115,7 @@
BaseForDocComments

Reference to a top-level const in another library incorrectDocReferenceFromEx

Reference to prefixed-name from another lib css.theOnlyThingInTheLibrary xx

Reference to a name that exists in this package, but is not imported -in this library doesStuff xx

+in this library doesStuff xx

Reference to a name of a class from an import of a library that exported the name BaseClass xx

diff --git a/testing/test_package_docs/fake/ExtraSpecialList-class.html b/testing/test_package_docs/fake/ExtraSpecialList-class.html index 61194114c3..926ea2ba95 100644 --- a/testing/test_package_docs/fake/ExtraSpecialList-class.html +++ b/testing/test_package_docs/fake/ExtraSpecialList-class.html @@ -188,7 +188,7 @@

Properties

first - → dynamic + → E

@@ -220,7 +220,7 @@

Properties

iterator - → Iterator + → Iterator<E>

@@ -228,15 +228,20 @@

Properties

last - → dynamic + → E

read-only, inherited
+
+ length + int length + +
length - → int + ↔ int

@@ -244,7 +249,7 @@

Properties

reversed - → Iterable + → Iterable<E>

@@ -260,7 +265,7 @@

Properties

single - → dynamic + → E

@@ -273,7 +278,7 @@

Properties

Methods

- add(element) + add(E element) → void
@@ -283,7 +288,7 @@

Methods

inherited
- addAll(Iterable iterable) + addAll(Iterable<E> iterable) → void
@@ -302,7 +307,7 @@

Methods

asMap() - → Map<int, dynamic> + → Map<int, E>
@@ -330,7 +335,7 @@

Methods

elementAt(int index) - → dynamic + → E
@@ -356,7 +361,7 @@

Methods

inherited
- fillRange(int start, int end, [ fill ]) + fillRange(int start, int end, [ E fill ]) → void
@@ -366,8 +371,8 @@

Methods

inherited
- firstWhere(bool test(E element), { dynamic orElse() }) - → dynamic + firstWhere(bool test(E element), { E orElse() }) + → E
@@ -396,7 +401,7 @@

Methods

getRange(int start, int end) - → Iterable + → Iterable<E>
@@ -414,7 +419,7 @@

Methods

inherited
- insert(int index, element) + insert(int index, E element) → void
@@ -423,7 +428,7 @@

Methods

inherited
- insertAll(int index, Iterable iterable) + insertAll(int index, Iterable<E> iterable) → void
@@ -452,8 +457,8 @@

Methods

inherited
- lastWhere(bool test(E element), { dynamic orElse() }) - → dynamic + lastWhere(bool test(E element), { E orElse() }) + → E
@@ -480,8 +485,8 @@

Methods

inherited
- reduce(dynamic combine(E previousValue, E element)) - → dynamic + reduce(E combine(E previousValue, E element)) + → E
@@ -500,7 +505,7 @@

Methods

removeAt(int index) - → dynamic + → E
@@ -509,7 +514,7 @@

Methods

removeLast() - → dynamic + → E
@@ -535,7 +540,7 @@

Methods

inherited
- replaceRange(int start, int end, Iterable newContents) + replaceRange(int start, int end, Iterable<E> newContents) → void
@@ -554,7 +559,7 @@

Methods

inherited
- setAll(int index, Iterable iterable) + setAll(int index, Iterable<E> iterable) → void
@@ -564,7 +569,7 @@

Methods

inherited
- setRange(int start, int end, Iterable iterable, [ int skipCount = 0 ]) + setRange(int start, int end, Iterable<E> iterable, [ int skipCount = 0 ]) → void
@@ -584,7 +589,7 @@

Methods

singleWhere(bool test(E element)) - → dynamic + → E
@@ -593,7 +598,7 @@

Methods

skip(int count) - → Iterable + → Iterable<E>
@@ -602,7 +607,7 @@

Methods

skipWhile(bool test(E element)) - → Iterable + → Iterable<E>
@@ -620,7 +625,7 @@

Methods

sublist(int start, [ int end ]) - → List + → List<E>
@@ -630,7 +635,7 @@

Methods

take(int count) - → Iterable + → Iterable<E>
@@ -639,7 +644,7 @@

Methods

takeWhile(bool test(E element)) - → Iterable + → Iterable<E>
@@ -648,7 +653,7 @@

Methods

toList({bool growable: true }) - → List + → List<E>
@@ -657,7 +662,7 @@

Methods

toSet() - → Set + → Set<E>
@@ -675,7 +680,7 @@

Methods

where(bool test(E element)) - → Iterable + → Iterable<E>
@@ -700,7 +705,7 @@

Operators

operator [](int index) - → dynamic + → E
@@ -708,7 +713,7 @@

Operators

inherited
- operator []=(int index, value) + operator []=(int index, E value) → void
diff --git a/testing/test_package_docs/fake/LongFirstLine-class.html b/testing/test_package_docs/fake/LongFirstLine-class.html index ab354b6086..679fd26adc 100644 --- a/testing/test_package_docs/fake/LongFirstLine-class.html +++ b/testing/test_package_docs/fake/LongFirstLine-class.html @@ -219,7 +219,7 @@

Properties

aStringProperty - → String + ↔ String

An instance string property. Readable and writable.

@@ -234,8 +234,9 @@

Properties

read-only
- onlySetter - → double + onlySetter + double d +

Only a setter, with a single param, of type double.

@@ -251,7 +252,7 @@

Properties

powers - → List<String> + ↔ List<String>

In the super class.

@@ -385,7 +386,7 @@

Static Properties

meaningOfLife - → int + ↔ int

A static int property.

@@ -400,8 +401,9 @@

Static Properties

read-only
- staticOnlySetter - → bool + staticOnlySetter + bool thing +

diff --git a/testing/test_package_docs/fake/SpecialList-class.html b/testing/test_package_docs/fake/SpecialList-class.html index 24d2dfc6b9..f9b0e0c43a 100644 --- a/testing/test_package_docs/fake/SpecialList-class.html +++ b/testing/test_package_docs/fake/SpecialList-class.html @@ -189,9 +189,14 @@

Constructors

Properties

+
+ length + int length + +
length - → int + ↔ int

diff --git a/testing/test_package_docs/fake/SpecialList/elementAt.html b/testing/test_package_docs/fake/SpecialList/elementAt.html index c8ab943294..5fff7cdb5a 100644 --- a/testing/test_package_docs/fake/SpecialList/elementAt.html +++ b/testing/test_package_docs/fake/SpecialList/elementAt.html @@ -152,7 +152,7 @@
SpecialList

Returns the indexth element.

-

The index must be non-negative and less than length. +

The index must be non-negative and less than length. Index zero represents the first element (so iterable.elementAt(0) is equivalent to iterable.first).

May iterate through the elements in iteration order, skipping the diff --git a/testing/test_package_docs/fake/SpecialList/setAll.html b/testing/test_package_docs/fake/SpecialList/setAll.html index 9bd51922be..6981d6a7d6 100644 --- a/testing/test_package_docs/fake/SpecialList/setAll.html +++ b/testing/test_package_docs/fake/SpecialList/setAll.html @@ -158,9 +158,9 @@

SpecialList
list.join(', '); // 'a, bee, sea'

This operation does not increase the length of this.

-

The index must be non-negative and no greater than length.

+

The index must be non-negative and no greater than length.

The iterable must not have more elements than what can fit from index -to length.

+to length.

If iterable is based on this list, its values may change /during/ the setAll operation.

diff --git a/testing/test_package_docs/fake/SpecialList/sublist.html b/testing/test_package_docs/fake/SpecialList/sublist.html index bf6a57eaa2..764cc2a2cc 100644 --- a/testing/test_package_docs/fake/SpecialList/sublist.html +++ b/testing/test_package_docs/fake/SpecialList/sublist.html @@ -156,7 +156,7 @@
SpecialList
List<String> colors = ['red', 'green', 'blue', 'orange', 'pink'];
 colors.sublist(1, 3); // ['green', 'blue']
 
-

If end is omitted, the length of this is used.

+

If end is omitted, the length of this is used.

colors.sublist(1);  // ['green', 'blue', 'orange', 'pink']
 

An error occurs if start is outside the range 0 .. length or if diff --git a/testing/test_package_docs/fake/SuperAwesomeClass-class.html b/testing/test_package_docs/fake/SuperAwesomeClass-class.html index 6b6e7239d6..ed954de849 100644 --- a/testing/test_package_docs/fake/SuperAwesomeClass-class.html +++ b/testing/test_package_docs/fake/SuperAwesomeClass-class.html @@ -189,7 +189,7 @@

Properties

powers - → List<String> + ↔ List<String>

In the super class.

diff --git a/testing/test_package_docs/fake/WithGetterAndSetter-class.html b/testing/test_package_docs/fake/WithGetterAndSetter-class.html index bdecb1d22d..a2f0daa109 100644 --- a/testing/test_package_docs/fake/WithGetterAndSetter-class.html +++ b/testing/test_package_docs/fake/WithGetterAndSetter-class.html @@ -183,9 +183,14 @@

Constructors

Properties

+
+ lengthX + int _length + +
lengthX - → int + ↔ int

Returns a length.

diff --git a/testing/test_package_docs/fake/fake-library.html b/testing/test_package_docs/fake/fake-library.html index 0817b2c394..a72bc27484 100644 --- a/testing/test_package_docs/fake/fake-library.html +++ b/testing/test_package_docs/fake/fake-library.html @@ -390,8 +390,9 @@

Properties

read-only
- justSetter - → int + justSetter + int value +

Just a setter. No partner getter.

@@ -399,7 +400,7 @@

Properties

mapWithDynamicKeys - → Map<dynamic, String> + ↔ Map<dynamic, String>

@@ -413,9 +414,14 @@

Properties

Final property.

final
+
+ setAndGet + String thing + +
setAndGet - → String + ↔ String

The getter for setAndGet.

@@ -423,7 +429,7 @@

Properties

simpleProperty - → String + ↔ String

Simple property

diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass/noSuchMethod.html b/testing/test_package_docs/test_package_imported.main/Whataclass/noSuchMethod.html index 3ff5fcdec7..a496e89654 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass/noSuchMethod.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass/noSuchMethod.html @@ -100,7 +100,7 @@
Whataclass

Invoked when a non-existent method or property is accessed.

-

Classes can override noSuchMethod to provide custom behavior.

+

Classes can override noSuchMethod to provide custom behavior.

If a value is returned, it becomes the result of the original invocation.

The default behavior is to throw a NoSuchMethodError.

diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html b/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html index 9a51e33579..ce5893ca74 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html @@ -116,7 +116,7 @@
Whataclass

If a subclass overrides the equality operator it should override -the hashCode method as well to maintain consistency.

+the hashCode method as well to maintain consistency.

diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass2/noSuchMethod.html b/testing/test_package_docs/test_package_imported.main/Whataclass2/noSuchMethod.html index 55738eef96..cb1eadf0a4 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass2/noSuchMethod.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass2/noSuchMethod.html @@ -100,7 +100,7 @@
Whataclass2<

Invoked when a non-existent method or property is accessed.

-

Classes can override noSuchMethod to provide custom behavior.

+

Classes can override noSuchMethod to provide custom behavior.

If a value is returned, it becomes the result of the original invocation.

The default behavior is to throw a NoSuchMethodError.

diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html b/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html index bef098f517..50669add2c 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html @@ -116,7 +116,7 @@
Whataclass2< so whether two objects are equal should only change if at least one of the objects was modified.

If a subclass overrides the equality operator it should override -the hashCode method as well to maintain consistency.

+the hashCode method as well to maintain consistency.

diff --git a/testing/test_package_docs/two_exports/BaseClass-class.html b/testing/test_package_docs/two_exports/BaseClass-class.html index e9db42b99c..2fdca4253d 100644 --- a/testing/test_package_docs/two_exports/BaseClass-class.html +++ b/testing/test_package_docs/two_exports/BaseClass-class.html @@ -135,9 +135,14 @@

Properties

The hash code for this object.

read-only, inherited
+
+ lengthX + int _length + +
lengthX - → int + ↔ int

Returns a length.

diff --git a/testing/test_package_docs/two_exports/ExtendingClass-class.html b/testing/test_package_docs/two_exports/ExtendingClass-class.html index 8fcca5f4d0..97af11e691 100644 --- a/testing/test_package_docs/two_exports/ExtendingClass-class.html +++ b/testing/test_package_docs/two_exports/ExtendingClass-class.html @@ -93,7 +93,7 @@
two_exports

Extending class extends BaseClass.

Also check out topLevelVariable.

-

This should not work: linking over to Apple.

+

This should not work: linking over to Apple.

@@ -137,9 +137,14 @@

Properties

The hash code for this object.

read-only, inherited
+
+ lengthX + int _length + +
lengthX - → int + ↔ int

Returns a length.

diff --git a/testing/test_package_docs/two_exports/two_exports-library.html b/testing/test_package_docs/two_exports/two_exports-library.html index 3fc1e3a2f4..c1c3dd70b0 100644 --- a/testing/test_package_docs/two_exports/two_exports-library.html +++ b/testing/test_package_docs/two_exports/two_exports-library.html @@ -112,7 +112,7 @@

Properties

topLevelVariable - → int + ↔ int

From 00769a50d8c7283c27996e376d9ce2578dad6823 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 1 May 2017 13:26:26 -0700 Subject: [PATCH 02/12] Cleanup and add comments --- CHANGELOG.md | 63 +++++++- bin/dartdoc.dart | 5 +- lib/dartdoc.dart | 2 +- lib/src/html/html_generator_instance.dart | 4 +- lib/src/markdown_processor.dart | 144 +++++------------- lib/src/model.dart | 122 ++++----------- pubspec.yaml | 2 +- test/model_test.dart | 2 +- testing/test_package/lib/src/extending.dart | 2 +- testing/test_package_docs/index.html | 2 +- .../two_exports/ExtendingClass-class.html | 2 +- 11 files changed, 133 insertions(+), 217 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff7ff7bba1..240a07a9a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,61 @@ -## unreleased - -* added a new `--footer-text` command-line option, to allow adding additional - text in the package name and copyright section of the footer -* Reduced stack depth by not recomputing findCanonicalLibraryFor (#1381) +## 0.11.0 + +* Many cleanups to dartdoc stdout/stderr, error messages, and warnings: + * Display fatal errors with 'fatal error' string to distinguish them from ordinary errors + * Upgrades to new Package.warn system. + * Fully integrated all scattered "warnings" and added new ones for the link checker. + * Allow for setting which warnings are errors in the library. + * Change location output to something IntelliJ can understand and link to + * Display location output for all warnings including line number plus column, when available + from analyzer (still some bugs in our resolution). It still doesn't do code references quite + right but at least gets you to the neighborhood. + * Add a warn method to ModelElements so they can warn on themselves without help from the Package. + * Warn correctly and squelch duplicates across doc inheritance and canonicalization almost + everywhere. + * Change --show-warnings to show all warnings, even those that might not be useful yet. + * Display a count of all warnings/errors after document generation. + * Make the progress counter tick slower. +* Added a built-in link checker and orphaned file generator, and tied it into Package.warn so + that when debugging dartdoc we can breakpoint and discover what about that ModelElement + caused to create the broken link. +* Fix bug where canonicalEnclosingElement could return a non-canonical Class. +* Fix bug where findCanonicalModelElementFor could return a non-canonical Class. +* Fix overriddenElement for Accessors to generate using enclosingCombo hint to ModelElement factory. +* Fix fullyQualifiedNameWithoutLibrary when periods are part of the library name. +* Add an allModelElements for Classes to support comment references. +* Make allModelElements for Libraries work using Class.allModelElements recursively. +* Squish some bugs related to duplicate logic for instantiating inherited class members. + * Enum and a few other places could still generate duplicate ModelElements for the + same thing. This is now fixed. + * EnumField is now handled by ModelElement.from factory. + * Added hints for EnumField and Accessors (index, enclosingCombo) to offload the buggy + logic for figuring this out from callers to ModelElement.from. +* Fix broken link generation when a canonical class's defining library isn't canonical. +* Partial rewrite of GetterSetterCombo and Fields/TopLevelVariable handling + * Link correctly to generic types for Fields/TopLevelVariables. + * Use right, left, and bidirectional arrows for read-only, write-only, and read-write + parameters. +* Partial rewrite of comment reference system + * Handle gracefully a variety of things users try in the real world, like prefixing operators + with 'operator', embedded newlines in comment references, and cases that shouldn't be + considered at all (comment refs that are really array references in sample docs, etc). + * Handle canonicalization correctly for comment references: point to the right places and + only to canonical elements. + * In general, warnings related to comment references should be much more useful now. + * Many fewer ambiguous doc reference warnings now and the ones that exist should be more + easily understandable and fixable with the new warning message. + * Understand references to parameters even though we don't do anything useful with them just yet + * Generics outside square brackets (#1250) are now warned with better context information that + takes newlines into account, but there are so many of them in complex packages like Flutter + that we still only show those with --show-warnings. + * Cache the traversal of allModelElements. + * Change handling of enum constant linking in codeRefs to work properly, though warnings about + that aren't right in some edge cases still. + * Only use analyzer resolving of commentRefs as a last resort since they don't take dartdoc + canonicalization into account. +* Added a new `--footer-text` command-line option, to allow adding additional + text in the package name and copyright section of the footer. +* Reduced stack depth by not recomputing findCanonicalLibraryFor. (#1381) * Workaround for (#1367) forces on enableAssertInitializer. * Work around analyzer-0.29 bug where embedded SDK uri's aren't properly reversed. diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart index e77bc3a53e..fb9f070492 100644 --- a/bin/dartdoc.dart +++ b/bin/dartdoc.dart @@ -170,9 +170,6 @@ main(List arguments) async { includeExternals: includeExternals); dartdoc.onCheckProgress.listen(_onProgress); - /*DartDocResults results = await dartdoc.generateDocs(); - print('\nSuccess! Docs generated into ${results.outDir.absolute.path}'); - */ Chain.capture(() async { DartDocResults results = await dartdoc.generateDocs(); print('\nSuccess! Docs generated into ${results.outDir.absolute.path}'); @@ -201,7 +198,7 @@ ArgParser _createArgsParser() { defaultsTo: false); parser.addFlag('sdk-docs', help: 'Generate ONLY the docs for the Dart SDK.', negatable: false); - parser.addFlag('show-warnings', help: 'Display warnings.', negatable: false, defaultsTo: false); + parser.addFlag('show-warnings', help: 'Display warnings.', negatable: false); parser.addFlag('show-progress', help: 'Display progress indications to console stdout', negatable: false); parser.addOption('sdk-readme', diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 7404ce3471..9002d7da00 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -43,7 +43,7 @@ export 'src/sdk.dart'; const String name = 'dartdoc'; // Update when pubspec version changes. -const String version = '0.10.0'; +const String version = '0.11.0'; final String defaultOutDir = path.join('doc', 'api'); diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart index 2155e666ce..e1bb2045d5 100644 --- a/lib/src/html/html_generator_instance.dart +++ b/lib/src/html/html_generator_instance.dart @@ -94,8 +94,6 @@ class HtmlGeneratorInstance implements HtmlOptions { generatePackage(); for (var lib in package.libraries) { - //if (lib.name != 'dart:convert') continue; - //if(lib.name != 'angular2.router') continue; generateLibrary(package, lib); for (var clazz in lib.allClasses) { @@ -291,7 +289,7 @@ class HtmlGeneratorInstance implements HtmlOptions { File destFile = new File(path.join(out.path, 'static-assets', destFileName)) ..createSync(recursive: true); - Uint8List resourceBytes = (await loader.loadAsBytes(resourcePath)); + Uint8List resourceBytes = await loader.loadAsBytes(resourcePath); destFile.writeAsBytesSync(resourceBytes); } } diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart index 6b98421718..0e6256cf74 100644 --- a/lib/src/markdown_processor.dart +++ b/lib/src/markdown_processor.dart @@ -127,7 +127,7 @@ final trailingIgnoreStuff = final leadingIgnoreStuff = new RegExp(r'^(const|final|var)[\s]+', multiLine: true); -// This is a constructor! +// This is explicitly intended as a reference to a constructor. final isConstructor = new RegExp(r'^new[\s]+', multiLine: true); // This is probably not really intended as a doc reference, so don't try or @@ -171,17 +171,7 @@ NodeList _getCommentRefs(ModelElement modelElement) { if (cModelElement == null) return null; modelElement = cModelElement; - /*if (modelElement.documentation == null && modelElement.canOverride()) { - var melement = modelElement.overriddenElement; - if (melement != null && - melement.element.computeNode() != null && - melement.element.computeNode() is AnnotatedNode) { - var docComment = (melement.element.computeNode() as AnnotatedNode) - .documentationComment; - if (docComment != null) return docComment.references; - return null; - } - }*/ + if (modelElement.element.documentationComment == null && modelElement.canOverride()) { var node = modelElement.overriddenElement?.element?.computeNode(); if (node is AnnotatedNode) { @@ -190,6 +180,7 @@ NodeList _getCommentRefs(ModelElement modelElement) { } } } + if (modelElement.element.computeNode() is AnnotatedNode) { final AnnotatedNode annotatedNode = modelElement.element.computeNode(); if (annotatedNode.documentationComment != null) { @@ -208,6 +199,10 @@ NodeList _getCommentRefs(ModelElement modelElement) { } } } + + // Our references might come from somewhere up in the inheritance chain. + // TODO(jcollins-g): rationalize this and all other places where docs are + // inherited to be consistent. if (modelElement.element is ClassMemberElement) { var node = modelElement.element.getAncestor((e) => e is ClassElement).computeNode(); if (node is AnnotatedNode) { @@ -215,9 +210,7 @@ NodeList _getCommentRefs(ModelElement modelElement) { return node.documentationComment.references; } } - } /*else if (modelElement.element is Member) { - var node = modelElement.element.enclosingElement. - }*/ + } return null; } @@ -236,7 +229,6 @@ MatchingLinkResult _getMatchingLinkElement( Element refElement; - // Try expensive not-scoped lookup. if (refElement == null) { refElement = _findRefElementInLibrary(codeRef, element); @@ -276,7 +268,6 @@ MatchingLinkResult _getMatchingLinkElement( Element searchElement = refElement is Member ? refElement.baseElement : refElement; Class preferredClass = _getPreferredClass(element); - //Set refModelElements = refLibrary.modelElementsMap[searchElement]; ModelElement refModelElement = element.package.findCanonicalModelElementFor(searchElement, preferredClass: preferredClass); // There have been places in the code which helpfully cache entities // regardless of what package they are associated with. This assert @@ -299,7 +290,7 @@ MatchingLinkResult _getMatchingLinkElement( /// Returns true if this is a constructor we should consider in /// _findRefElementInLibrary, or if this isn't a constructor. -bool _yesReallyThisConstructor(String codeRef, ModelElement modelElement) { +bool _ConsiderIfConstructor(String codeRef, ModelElement modelElement) { if (modelElement is! Constructor) return true; if (codeRef.contains(isConstructor)) return true; Constructor aConstructor = modelElement; @@ -311,7 +302,7 @@ bool _yesReallyThisConstructor(String codeRef, ModelElement modelElement) { } } if (aConstructor.name != aConstructor.enclosingElement.name) { - // This isn't a default constructor so treat it like anything else. + // This isn't a default constructor so treat it like any other member. return true; } return false; @@ -323,17 +314,11 @@ bool _yesReallyThisConstructor(String codeRef, ModelElement modelElement) { // TODO(jcollins-g): function caches with maps are very common in dartdoc. // Extract into library. Map> _findRefElementCache; -//final Map, Element> _findRefElementInLibraryCache = new Map(); -// TODO(jcollins-g): get some sort of consistency in resolving names -// TODO(jcollins-g): rewrite this to handle constructors in a less hacky way -// TODO(jcollins-g): this function breaks down naturally into many helpers, extract them -// TODO(jcollins-g): a complex package winds up spending a lot of cycles here. Optimize. +// TODO(jcollins-g): Rewrite this to handle constructors in a less hacky way +// TODO(jcollins-g): This function breaks down naturally into many helpers, extract them +// TODO(jcollins-g): A complex package winds up spending a lot of cycles in here. Optimize. Element _findRefElementInLibrary(String codeRef, ModelElement element) { assert(element.package.allLibrariesAdded); - //Tuple2 key = new Tuple2(codeRef, element); - //if (_findRefElementInLibraryCache.containsKey(key)) { - // return _findRefElementInLibraryCache[key]; - //} String codeRefChomped = codeRef.replaceFirst(isConstructor, ''); @@ -341,10 +326,6 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { final Package package = library.package; final Set results = new Set(); - if (element.fullyQualifiedName == 'dart:io.Platform' && codeRef == 'dart:io') { - 1+1; - } - results.remove(null); // Oh, and this might be an operator. Strip the operator prefix and try again. if (results.isEmpty && codeRef.startsWith('operator')) { @@ -368,30 +349,14 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { // Maybe this ModelElement has parameters, and this is one of them. // We don't link these, but this keeps us from emitting warnings. Be sure to - // get members of parameters too (yes, people do this). - // TODO(jcollins): link to classes that are the types of parameters, where known + // get members of parameters too. + // TODO(jcollins-g): link to classes that are the types of parameters, where known results.addAll(element.allParameters.where((p) => p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}."))); - /* - if (element.canHaveParameters && element.parameters.map((p) => p.name).contains(codeRef)) { - results.addAll(element.parameters.where((p) => p.name.contains(codeRef))); - } - if (element is GetterSetterCombo) { - Accessor setter = element.setter; - if (setter != null) { - if (setter.parameters.map((p) => p.name).contains(codeRef)) { - results.addAll(setter.parameters.where((p) => p.name.contains(codeRef))); - } - for (Parameter p in setter.parameters) { - - } - } - }*/ results.remove(null); if (results.isEmpty) { // Maybe this is local to a class. - // TODO(jcollins): pretty sure tryClasses is a strict subset of the superclass chain. - // Optimize if that's the case. + // TODO(jcollins-g): tryClasses is a strict subset of the superclass chain. Optimize. List tryClasses = [_getPreferredClass(element)]; Class realClass = tryClasses.first; if (element is Inheritable) { @@ -401,12 +366,6 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { overriddenElement = overriddenElement.overriddenElement; } } - /* - if (element.element.enclosingElement is ClassElement) { - tryClass = new ModelElement.from(element.element.enclosingElement, element.library); - } else if (element is Class) { - tryClass = element; - }*/ for (Class tryClass in tryClasses) { if (tryClass != null) { @@ -428,6 +387,10 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { results.remove(null); // We now need the ref element cache to keep from repeatedly searching [Package.allModelElements]. + // TODO(jcollins-g): Find somewhere to cache elements outside package.libraries + // so we can give the right warning (no canonical found) + // when referring to objects in libraries outside the + // documented set. if (results.isEmpty && _findRefElementCache == null) { assert(package.allLibrariesAdded); _findRefElementCache = new Map(); @@ -443,33 +406,16 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { // if the codeRef might be qualified, and contains periods.) if (results.isEmpty && codeRefChomped.contains('.') && _findRefElementCache.containsKey(codeRefChomped)) { for (final modelElement in _findRefElementCache[codeRefChomped]) { - if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; + if (!_ConsiderIfConstructor(codeRef, modelElement)) continue; results.add(package.findCanonicalModelElementFor(modelElement.element)); } - // TODO(jcollins-g): This is extremely inefficient and no longer necessary - // since allCanonicalModelElements is now stable and doesn't mutate after - // [Package] construction. So precompute and cache the result map somewhere, - // maybe in [Package]. - /*for (final modelElement in package.allModelElements) { - if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; - // Constructors are handled in _linkDocReference. - if (codeRefChomped == modelElement.fullyQualifiedName) { - results.add(package.findCanonicalModelElementFor(modelElement.element)); - } - // Try without the library too; sometimes people use that as shorthand. - // Not the same as partially qualified matches because here we have a '.'. - if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary) { - results.add(package.findCanonicalModelElementFor(modelElement.element)); - } - }*/ } results.remove(null); - // Only look for partially qualified matches if we didn't find a fully qualified one. if (results.isEmpty) { for (final modelElement in library.allModelElements) { - if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; + if (!_ConsiderIfConstructor(codeRef, modelElement)) continue; if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary) { results.add(package.findCanonicalModelElementFor(modelElement.element)); } @@ -485,20 +431,11 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { results.add(package.findCanonicalModelElementFor(modelElement.element)); } } - /*for (final modelElement in package.allModelElements) { - if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; - if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary) { - results.add(package.findCanonicalModelElementFor(modelElement.element)); - } - if (modelElement is Library && codeRefChomped == modelElement.fullyQualifiedName) { - results.add(package.findCanonicalModelElementFor(modelElement.element)); - } - }*/ } // This could conceivably be a reference to an enum member. They don't show up in allModelElements. - // TODO(jcollins-g): put enum members in allModelElements with useful hrefs without blowing up other assumptions. - // TODO(jcollins-g): this doesn't provide good warnings if an enum and class have the same name in different libraries in the same package. Fix that. + // TODO(jcollins-g): Put enum members in allModelElements with useful hrefs without blowing up other assumptions about what that means. + // TODO(jcollins-g): This doesn't provide good warnings if an enum and class have the same name in different libraries in the same package. Fix that. if (results.isEmpty) { List codeRefChompedParts = codeRefChomped.split('.'); if (codeRefChompedParts.length >= 2) { @@ -564,8 +501,8 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { return result; } - -// results is an output parameter +// _getResultsForClass assumes codeRefChomped might be a member of tryClass (inherited or not) +// and will add to [results] void _getResultsForClass(Class tryClass, String codeRefChomped, Set results, String codeRef, Package package) { if ((tryClass.modelType.typeArguments.map((e) => e.name)).contains(codeRefChomped)) { results.add(tryClass.modelType.typeArguments.firstWhere((e) => e.name == codeRefChomped).element); @@ -578,19 +515,28 @@ void _getResultsForClass(Class tryClass, String codeRefChomped, Set superChain = []; superChain.add(tryClass); + // This seems duplicitous with our caller, but the preferredClass + // hint matters with findCanonicalModelElementFor. + // TODO(jcollins-g): This makes our caller ~O(n^2) vs length of superChain. + // Fortunately superChains are short, but optimize this if it matters. superChain.addAll(tryClass.superChainRaw.map((t) => t.returnElement as Class)); List codeRefParts = codeRefChomped.split('.'); for (final c in superChain) { + // TODO(jcollins-g): add a hash-map-enabled lookup function to Class? for (final modelElement in c.allModelElements) { - if (!_yesReallyThisConstructor(codeRef, modelElement)) continue; + if (!_ConsiderIfConstructor(codeRef, modelElement)) continue; String namePart = modelElement.fullyQualifiedName.split('.').last; // TODO(jcollins-g): fix operators so we can use 'name' here or similar. if (codeRefChomped == namePart) { results.add(package.findCanonicalModelElementFor(modelElement.element, preferredClass: tryClass)); continue; } - // TODO(jcollins-g): fix partial qualifications logic so it can tell + // Handle non-documented class documentation being imported into a + // documented class when it refers to itself (with help from caller's + // iteration on tryClasses). + // TODO(jcollins-g): Fix partial qualifications in _findRefElementInLibrary so it can tell // when it is referenced from a non-documented element? + // TODO(jcollins-g): We could probably check this early. if (codeRefParts.first == c.name && codeRefParts.last == namePart) { results.add(package.findCanonicalModelElementFor(modelElement.element, preferredClass: tryClass)); continue; @@ -620,14 +566,10 @@ void _getResultsForClass(Class tryClass, String codeRefChomped, Set commentRefs) { - // We might not have the canonical ModelElement here. If we don't, - // try to get it. - /*if (!element.isCanonical) { - element = element.package.findCanonicalModelElementFor(element.element); - }*/ + // TODO(jcollins-g): Refactor so that doc operations work on the + // documented element. element = element.overriddenDocumentedElement; - // support for [new Constructor] and [new Class.namedCtr] MatchingLinkResult result; result = _getMatchingLinkElement(codeRef, element, commentRefs); final ModelElement linkedElement = result.element; @@ -637,8 +579,8 @@ String _linkDocReference(String codeRef, ModelElement element, if (linkedElement.isDeprecated) { classContent = 'class="deprecated" '; } - // this would be linkedElement.linkedName, but link bodies are slightly - // different for doc references. sigh. + // This would be linkedElement.linkedName, but link bodies are slightly + // different for doc references. if (linkedElement.href == null) { return '${HTML_ESCAPE.convert(label)}'; } else { @@ -677,10 +619,6 @@ void _showWarningsForGenericsOutsideSquareBracketsBlocks(String text, priorContext = priorContext.replaceAll(new RegExp(r'^.*\n', multiLine: true), ''); postContext = postContext.replaceAll(new RegExp(r'\n.*$', multiLine: true), ''); String errorMessage = "$priorContext$postContext"; - //String errorMessage = "${text.substring(max(position - 20, 0), min(position + 20, text.length))}"; - - // Strip pieces of lines before and after that - //errorMessage = errorMessage.replaceAll(new RegExp(r'(\n[^<>]*$|^[^<>]*\n)'), ''); // TODO(jcollins-g): allow for more specific error location inside comments element.warn(PackageWarning.typeAsHtml, errorMessage); }); diff --git a/lib/src/model.dart b/lib/src/model.dart index 1e6a44f1b9..8743c23a4c 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -84,7 +84,6 @@ int byFeatureOrdering(String a, String b) { return compareAsciiLowerCaseNatural(a, b); } - /// Mixin for subclasses of ModelElement representing Elements that can be /// inherited from one class to another. /// @@ -960,20 +959,6 @@ class Enum extends Class { @override String get kind => 'enum'; - /* - @override - Set _allModelElements = new Set(); - @override - List get allModelElements { - if (_allModelElements.isEmpty) { - _allModelElements.addAll(super.allModelElements); - _allModelElements.addAll(constants); - } - return _allModelElements.toList(); - } - - @override - List get allCanonicalModelElements => allModelElements.where((m) => m.isCanonical);*/ } /// Enum's fields are virtual, so we do a little work to create @@ -1019,10 +1004,14 @@ class EnumField extends Field { @override bool get isCanonical { if (name == 'index') return false; - // TODO(jcollins-g): This is a bit hacky, and doesn't allow for docrefs + // If this is something inherited from Object, e.g. hashCode, let the + // normal rules apply. if (_index == null) { return super.isCanonical; } + // TODO(jcollins-g): We don't actually document this as a separate entity; + // do that or change this to false and deal with the + // consequences. return true; } @@ -1050,7 +1039,6 @@ class Field extends ModelElement assert(enclosingElement != definingEnclosingElement); } - String get constantValue { if (_constantValue != null) return _constantValue; @@ -1079,7 +1067,6 @@ class Field extends ModelElement @override bool get hasSetter => _field.setter != null; - @override String get href { String retval; @@ -1094,8 +1081,6 @@ class Field extends ModelElement throw new StateError( '$name is not in a class or library, instead it is a ${enclosingElement.element}'); } - if (retval.contains('animations/AnimationWithParentMixin')) - 1+1; return retval; } @@ -1116,8 +1101,6 @@ class Field extends ModelElement @override String get kind => 'property'; - - String get typeName => "property"; @override @@ -1170,13 +1153,7 @@ class Field extends ModelElement t, new ModelElement.from( t.element, _findOrCreateEnclosingLibraryFor(t.element))); - } /*else { - var s = _field.setter.parameters.first.type; - _modelType = new ElementType( - s, - new ModelElement.from( - s.element, _findOrCreateEnclosingLibraryFor(s.element))); - }*/ + } } } @@ -1729,7 +1706,8 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat /// TODO(jcollins-g): enforce this. /// Specify enclosingClass only if this is to be an inherited object. /// Specify index only if this is to be an EnumField.forConstant. - /// Specify enclosingField only if this is to be an Accessor. + /// Specify enclosingCombo (a GetterSetterCombo) only if this is to be an + /// Accessor. /// TODO(jcollins-g): this way of using the optional parameter is messy, /// clean that up. factory ModelElement.from(Element e, Library library, @@ -1800,7 +1778,6 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat newModelElement = new Method(e, library); else newModelElement = new Method.inherited(e, enclosingClass, library); - 1+1; } if (e is TopLevelVariableElement) { newModelElement = new TopLevelVariable(e, library); @@ -1825,8 +1802,6 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat library.package._allInheritableElements[iKey].add(newModelElement); } } - if (newModelElement.fullyQualifiedName == 'animation.AnimationController.view') - 1+1; if (newModelElement is Accessor) { assert(newModelElement.enclosingCombo == enclosingCombo); } else { @@ -2013,7 +1988,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat @override String get elementLocation { - // Call nothing from here that can emit warnings. + // Call nothing from here that can emit warnings or you'll cause stack overflows. if (lineAndColumn != null) { return "(${p.toUri(sourceFileName)}:${lineAndColumn.item1}:${lineAndColumn.item2})"; } @@ -2034,28 +2009,10 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat _fullyQualifiedNameWithoutLibrary = fullyQualifiedName.replaceFirst("${library.fullyQualifiedName}.", ''); } return _fullyQualifiedNameWithoutLibrary; - //return (_fullyQualifiedNameWithoutLibrary ??= - // fullyQualifiedName.split(".").skip(1).join(".")); } String get sourceFileName => element.source.fullName; - /*int _lineNumber; - bool _isLineNumberComputed = false; - int get lineNumber { - if (!_isLineNumberComputed) { - var node = element.computeNode(); - if (node is Declaration && node.element != null) { - var element = node.element; - var lineNumber = lineNumberCache.lineNumber( - element.source.fullName, element.nameOffset); - _lineNumber = lineNumber + 1; - } - _isLineNumberComputed = true; - } - return _lineNumber; - } - */ Tuple2 _lineAndColumn; bool _isLineNumberComputed = false; @override @@ -2158,9 +2115,10 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat ModelElement get overriddenElement => null; - // TODO(jcollins-g): rewrite this ModelElement _overriddenDocumentedElement; bool _overriddenDocumentedElementIsSet = false; + // TODO(jcollins-g): This method prefers canonical elements, but it isn't + // guaranteed and is probably the source of bugs or confusing warnings. ModelElement get overriddenDocumentedElement { if (!_overriddenDocumentedElementIsSet) { ModelElement found = this; @@ -2192,6 +2150,8 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat (this is Library) ? (this as Library).package : this.library.package; List _allParameters; + // TODO(jcollins-g): This is in the wrong place. Move parts to GetterSetterCombo, + // elsewhere as appropriate? List get allParameters { if (_allParameters == null) { final Set recursedParameters = new Set(); @@ -2240,7 +2200,6 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat return _parameters; } - /// Sugar for the package warning system. void warn(PackageWarning kind, [String message]) { if (kind == PackageWarning.unresolvedDocReference && overriddenElement != null) { // The documentation we're using for this element came from somewhere else. @@ -2723,6 +2682,7 @@ enum PackageWarning { } /// Provides description text and command line flags for warnings. +/// TODO(jcollins-g): Actually use this for command line flags. Map> packageWarningText = { PackageWarning.ambiguousDocReference: ["ambiguous-doc-reference", "A comment reference could refer to two or more different objects"], @@ -2867,8 +2827,8 @@ class Package implements Nameable, Documentable, Locatable { // All library objects related to this package; a superset of _libraries. final Map _allLibraries = new Map(); - - final PackageWarningOptions packageWarningOptions; + // Objects to keep track of warnings. + final PackageWarningOptions _packageWarningOptions; PackageWarningCounter _packageWarningCounter; // All ModelElements constructed for this package; a superset of allModelElements. final Map, ModelElement> _allConstructedModelElements = @@ -2880,8 +2840,6 @@ class Package implements Nameable, Documentable, Locatable { /// Map of Class.href to a list of classes implementing that class final Map> _implementors = new Map(); - //final Map>> _displayedWarnings = new Map(); - final PackageMeta packageMeta; final Map _elementToLibrary = {}; @@ -2889,10 +2847,10 @@ class Package implements Nameable, Documentable, Locatable { final Map _macros = {}; bool allLibrariesAdded = false; - Package(Iterable libraryElements, this.packageMeta, this.packageWarningOptions) { + Package(Iterable libraryElements, this.packageMeta, this._packageWarningOptions) { assert(_allConstructedModelElements.isEmpty); assert(_allLibraries.isEmpty); - _packageWarningCounter = new PackageWarningCounter(packageWarningOptions); + _packageWarningCounter = new PackageWarningCounter(_packageWarningOptions); libraryElements.forEach((element) { // add only if the element should be included in the public api if (isPublic(element)) { @@ -2931,7 +2889,6 @@ class Package implements Nameable, Documentable, Locatable { PackageWarningCounter get packageWarningCounter => _packageWarningCounter; void warn(Locatable modelElement, PackageWarning kind, [String message]) { - //Tuple2 warningData = new Tuple2(kind, message); if (modelElement != null) { // This sort of warning is only applicable to top level elements. if (kind == PackageWarning.ambiguousReexport) { @@ -2947,18 +2904,6 @@ class Package implements Nameable, Documentable, Locatable { if ((modelElement as Accessor).enclosingCombo != null) modelElement = (modelElement as Accessor).enclosingCombo; } - /*if (modelElement.element is ClassMemberElement) { - ClassElement enclosingClass = modelElement.element.getAncestor((e) => e is ClassElement); - if (_displayedWarnings.containsKey(enclosingClass) && - _displayedWarnings[enclosingClass].contains(kind)) return; - elementName = "${enclosingClass.name}.${modelElement.name}"; - } else { - elementName = "${modelElement.name}"; - } - - fullElementName = - "${elementName} (${modelElement.library.element.location})"; - */ } else { // If we don't have an element, we need a message to disambiguate. assert(message != null); @@ -2966,23 +2911,15 @@ class Package implements Nameable, Documentable, Locatable { if (_packageWarningCounter.hasWarning(modelElement?.element, kind, message)) { return; } - /*if (_displayedWarnings.containsKey(modelElement?.element) && - _displayedWarnings[modelElement?.element].contains(warningData)) { - return; - }*/ // Elements that are part of the Dart SDK can have colons in their FQNs. // This confuses IntelliJ and makes it so it can't link to the location // of the error in the console window, so separate out the library from // the path. + // TODO(jcollins-g): What about messages that may include colons? Substituting + // them out doesn't work as well there since it might confuse + // the user, yet we still want IntelliJ to link properly. String nameSplitFromColonPieces; if (modelElement != null) { - nameSplitFromColonPieces = modelElement.fullyQualifiedName; - /* - List nameParts = nameSplitFromColonPieces.split('.'); - if (nameParts.length > 0 && nameParts[0].contains(':')) { - nameSplitFromColonPieces = '${nameParts[0]} ${nameParts.sublist(1).join(".")}'; - } - */ nameSplitFromColonPieces = modelElement.fullyQualifiedName.replaceFirst(':', '-'); } String warningMessage; @@ -3042,8 +2979,6 @@ class Package implements Nameable, Documentable, Locatable { }); String fullMessage = "${warningMessage} ${modelElement != null ? modelElement.elementLocation : ''}"; packageWarningCounter.addWarning(modelElement?.element, kind, message, fullMessage); - //_displayedWarnings.putIfAbsent(modelElement?.element, () => new Set()); - //_displayedWarnings[modelElement?.element].add(warningData); } static Package _withAutoIncludedDependencies( @@ -3142,9 +3077,8 @@ class Package implements Nameable, Documentable, Locatable { /// comprehensive. Map> get allHrefs { Map> hrefMap = new Map(); - // toList to avoid concurrent modification. - // TODO(jcollins): handle calculating hrefs causing new elements better - // than toList() + // TODO(jcollins-g ): handle calculating hrefs causing new elements better + // than toList(). for (ModelElement modelElement in _allConstructedModelElements.values.toList()) { if (modelElement.href == null) continue; hrefMap.putIfAbsent(modelElement.href, () => new Set()); @@ -3327,17 +3261,13 @@ class Package implements Nameable, Documentable, Locatable { })); } Set matches = new Set()..addAll(candidates.where((me) => me.isCanonical)); - /*if (matches.length > 1) { - if (preferredClass != null && matches.any((me) => (me as EnclosedElement).enclosingElement == preferredClass)) { - matches.removeWhere((me) => (me as EnclosedElement).enclosingElement != preferredClass); - } - }*/ - // This is for situations where multiple classes may derive + + // This is for situations where multiple classes may actually be canonical + // for an inherited element whose defining Class is not canonical. if (matches.length > 1 && preferredClass != null) { // Search for matches inside our superchain. List superChain = preferredClass.superChainRaw.map((et) => et.element).toList(); superChain.add(preferredClass); - // Pretty sure this is not correct: FIXME before submission matches.removeWhere((me) => !superChain.contains((me as EnclosedElement).enclosingElement)); } assert(matches.length <= 1); diff --git a/pubspec.yaml b/pubspec.yaml index 9a163c5889..0c7e9536ec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dartdoc # Also update the `version` field in lib/dartdoc.dart. -version: 0.10.0 +version: 0.11.0 author: Dart Team description: A documentation generator for Dart. homepage: https://github.com/dart-lang/dartdoc diff --git a/test/model_test.dart b/test/model_test.dart index 0142f3147b..e60ccc242f 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -478,7 +478,7 @@ void main() { expect(resolved, isNotNull); expect(resolved, contains('BaseClass')); - expect(resolved, contains('linking over to Apple.')); + expect(resolved, contains('Linking over to Apple')); }); test('references to class and constructors', () { diff --git a/testing/test_package/lib/src/extending.dart b/testing/test_package/lib/src/extending.dart index c60eb1d95f..c84e5cfbe0 100644 --- a/testing/test_package/lib/src/extending.dart +++ b/testing/test_package/lib/src/extending.dart @@ -8,5 +8,5 @@ int topLevelVariable = 1; /// /// Also check out [topLevelVariable]. /// -/// This should not work: linking over to [Apple]. +/// Linking over to [Apple] should work. class ExtendingClass extends BaseClass {} diff --git a/testing/test_package_docs/index.html b/testing/test_package_docs/index.html index 74245fbfe1..53ad189993 100644 --- a/testing/test_package_docs/index.html +++ b/testing/test_package_docs/index.html @@ -4,7 +4,7 @@ - + test_package - Dart API docs diff --git a/testing/test_package_docs/two_exports/ExtendingClass-class.html b/testing/test_package_docs/two_exports/ExtendingClass-class.html index 97af11e691..839b97f7fe 100644 --- a/testing/test_package_docs/two_exports/ExtendingClass-class.html +++ b/testing/test_package_docs/two_exports/ExtendingClass-class.html @@ -93,7 +93,7 @@
two_exports

Extending class extends BaseClass.

Also check out topLevelVariable.

-

This should not work: linking over to Apple.

+

Linking over to Apple should work.

From dd0c072a8333e7db75066a0fa456ffa8f2bb2ab4 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Mon, 1 May 2017 13:27:16 -0700 Subject: [PATCH 03/12] dartfmt --- bin/dartdoc.dart | 13 +- lib/dartdoc.dart | 37 ++++-- lib/src/html/html_generator.dart | 2 +- lib/src/markdown_processor.dart | 130 ++++++++++++------- lib/src/model.dart | 213 ++++++++++++++++++++----------- test/model_test.dart | 11 +- test/src/utils.dart | 6 +- 7 files changed, 273 insertions(+), 139 deletions(-) diff --git a/bin/dartdoc.dart b/bin/dartdoc.dart index fb9f070492..c368664f43 100644 --- a/bin/dartdoc.dart +++ b/bin/dartdoc.dart @@ -55,8 +55,8 @@ main(List arguments) async { var readme = args['sdk-readme']; if (readme != null && !(new File(readme).existsSync())) { - stderr - .write(" fatal error: unable to locate the SDK description file at $readme."); + stderr.write( + " fatal error: unable to locate the SDK description file at $readme."); exit(1); } @@ -77,7 +77,8 @@ main(List arguments) async { args['header'].map(_resolveTildePath).toList() as List; for (String headerFilePath in headerFilePaths) { if (!new File(headerFilePath).existsSync()) { - stderr.write(" fatal error: unable to locate header file: ${headerFilePath}."); + stderr.write( + " fatal error: unable to locate header file: ${headerFilePath}."); exit(1); } } @@ -86,7 +87,8 @@ main(List arguments) async { args['footer'].map(_resolveTildePath).toList() as List; for (String footerFilePath in footerFilePaths) { if (!new File(footerFilePath).existsSync()) { - stderr.write(" fatal error: unable to locate footer file: ${footerFilePath}."); + stderr.write( + " fatal error: unable to locate footer file: ${footerFilePath}."); exit(1); } } @@ -219,7 +221,8 @@ ArgParser _createArgsParser() { parser.addOption('footer-text', allowMultiple: true, splitCommas: true, - help: 'paths to footer-text files (optional text next to the copyright).'); + help: + 'paths to footer-text files (optional text next to the copyright).'); parser.addOption('exclude', allowMultiple: true, splitCommas: true, help: 'Library names to ignore.'); parser.addOption('include', diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 9002d7da00..86ab18ce62 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -159,7 +159,8 @@ class DartDoc { } Package package; if (config != null && config.autoIncludeDependencies) { - package = Package.withAutoIncludedDependencies(libraries, packageMeta, warningOptions); + package = Package.withAutoIncludedDependencies( + libraries, packageMeta, warningOptions); libraries = package.libraries.map((l) => l.element).toList(); // remove excluded libraries again, in case they are picked up through // dependencies. @@ -184,7 +185,8 @@ class DartDoc { for (var generator in generators) { await generator.generate(package, outputDir); - generator.writtenFiles.forEach((p) => writtenFiles.add(path.normalize(p))); + generator.writtenFiles + .forEach((p) => writtenFiles.add(path.normalize(p))); } await verifyLinks(package, outputDir.path); @@ -193,7 +195,8 @@ class DartDoc { print( "\nDocumented ${package.libraries.length} librar${package.libraries.length == 1 ? 'y' : 'ies'} " "in ${seconds.toStringAsFixed(1)} seconds."); - print ("Finished with: ${package.packageWarningCounter.warningCount} warnings, ${package.packageWarningCounter.errorCount} errors"); + print( + "Finished with: ${package.packageWarningCounter.warningCount} warnings, ${package.packageWarningCounter.errorCount} errors"); if (package.libraries.isEmpty) { throw new DartDocFailure( @@ -207,7 +210,8 @@ class DartDoc { return new DartDocResults(packageMeta, package, outputDir); } - void _warn(Package package, PackageWarning kind, String p, String origin, {String source}) { + void _warn(Package package, PackageWarning kind, String p, String origin, + {String source}) { // Ordinarily this would go in [Package.warn], but we don't actually know what // ModelElement to warn on yet. Locatable referenceElement; @@ -231,7 +235,8 @@ class DartDoc { referenceElement = referenceElements.firstWhere((e) => e.isCanonical); } else { // If we don't have a canonical element, just pick one. - referenceElement = referenceElements.isEmpty ? null : referenceElements.first; + referenceElement = + referenceElements.isEmpty ? null : referenceElements.first; } } if (referenceElement == null && source == 'index.html') @@ -239,12 +244,14 @@ class DartDoc { package.warn(referenceElement, kind, p); } - Future _doOrphanCheck(Package package, String origin, Set visited) async { + Future _doOrphanCheck( + Package package, String origin, Set visited) async { String normalOrigin = path.normalize(origin); String staticAssets = path.joinAll([normalOrigin, 'static-assets', '']); String indexJson = path.joinAll([normalOrigin, 'index.json']); bool foundIndex = false; - await for (FileSystemEntity f in new Directory(normalOrigin).list(recursive: true)) { + await for (FileSystemEntity f + in new Directory(normalOrigin).list(recursive: true)) { var fullPath = path.normalize(f.path); if (f is Directory) { continue; @@ -258,7 +265,7 @@ class DartDoc { continue; } if (visited.contains(fullPath)) continue; - if (!writtenFiles.contains(fullPath)) { + if (!writtenFiles.contains(fullPath)) { // This isn't a file we wrote (this time); don't claim we did. _warn(package, PackageWarning.unknownFile, fullPath, normalOrigin); } else { @@ -273,7 +280,9 @@ class DartDoc { } } - _doCheck(Package package, String origin, Set visited, String pathToCheck, [String source, String fullPath]) { + _doCheck( + Package package, String origin, Set visited, String pathToCheck, + [String source, String fullPath]) { if (fullPath == null) { fullPath = path.joinAll([origin, pathToCheck]); fullPath = path.normalize(fullPath); @@ -281,7 +290,9 @@ class DartDoc { File file = new File("$fullPath"); if (!file.existsSync()) { - _warn(package, PackageWarning.brokenLink, pathToCheck, path.normalize(origin), source: source); + _warn(package, PackageWarning.brokenLink, pathToCheck, + path.normalize(origin), + source: source); _onCheckProgress.add(pathToCheck); return; } @@ -309,17 +320,19 @@ class DartDoc { newFullPath = path.normalize(newFullPath); if (!visited.contains(newFullPath)) { visited.add(newFullPath); - _doCheck(package, origin, visited, newPathToCheck, pathToCheck, newFullPath); + _doCheck(package, origin, visited, newPathToCheck, pathToCheck, + newFullPath); } } } _onCheckProgress.add(pathToCheck); - 1+1; + 1 + 1; //return; } Map> _hrefs; + /// Don't call this method more than once, and only after you've /// generated all docs for the Package. Future verifyLinks(Package package, String origin) async { diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart index dbbb42fa1c..ecc35f6b45 100644 --- a/lib/src/html/html_generator.dart +++ b/lib/src/html/html_generator.dart @@ -66,7 +66,7 @@ class HtmlGenerator extends Generator { @override Future generate(Package package, Directory out) { _instance = new HtmlGeneratorInstance( - _options, _templates, package, out, _onFileCreated); + _options, _templates, package, out, _onFileCreated); return _instance.generate(); } } diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart index 0e6256cf74..26f6509e77 100644 --- a/lib/src/markdown_processor.dart +++ b/lib/src/markdown_processor.dart @@ -120,8 +120,7 @@ final nonHTMLRegexp = new RegExp(" ])\\w+[> ]"); // Type parameters and other things to ignore at the end of doc references. -final trailingIgnoreStuff = - new RegExp(r'(<.*>|\(.*\))$'); +final trailingIgnoreStuff = new RegExp(r'(<.*>|\(.*\))$'); // Things to ignore at the beginning of doc references final leadingIgnoreStuff = @@ -133,8 +132,7 @@ final isConstructor = new RegExp(r'^new[\s]+', multiLine: true); // This is probably not really intended as a doc reference, so don't try or // warn about them. // Covers anything with leading digits/symbols, empty string, weird punctuation, spaces. -final notARealDocReference = - new RegExp(r'''(^[^\w]|^[\d]|[,"'/]|^$)'''); +final notARealDocReference = new RegExp(r'''(^[^\w]|^[\d]|[,"'/]|^$)'''); // We don't emit warnings currently: #572. const List _oneLinerSkipTags = const ["code", "pre"]; @@ -156,7 +154,8 @@ class MatchingLinkResult { // Calculate a class hint for findCanonicalModelElementFor. ModelElement _getPreferredClass(ModelElement modelElement) { - if (modelElement is EnclosedElement && (modelElement as EnclosedElement).enclosingElement is Class) { + if (modelElement is EnclosedElement && + (modelElement as EnclosedElement).enclosingElement is Class) { return (modelElement as EnclosedElement).enclosingElement; } else if (modelElement is Class) { return modelElement; @@ -167,12 +166,14 @@ ModelElement _getPreferredClass(ModelElement modelElement) { // TODO: this is in the wrong place NodeList _getCommentRefs(ModelElement modelElement) { Class preferredClass = _getPreferredClass(modelElement); - ModelElement cModelElement = modelElement.package.findCanonicalModelElementFor(modelElement.element, preferredClass: preferredClass); - if (cModelElement == null) - return null; + ModelElement cModelElement = modelElement.package + .findCanonicalModelElementFor(modelElement.element, + preferredClass: preferredClass); + if (cModelElement == null) return null; modelElement = cModelElement; - if (modelElement.element.documentationComment == null && modelElement.canOverride()) { + if (modelElement.element.documentationComment == null && + modelElement.canOverride()) { var node = modelElement.overriddenElement?.element?.computeNode(); if (node is AnnotatedNode) { if (node.documentationComment != null) { @@ -204,7 +205,9 @@ NodeList _getCommentRefs(ModelElement modelElement) { // TODO(jcollins-g): rationalize this and all other places where docs are // inherited to be consistent. if (modelElement.element is ClassMemberElement) { - var node = modelElement.element.getAncestor((e) => e is ClassElement).computeNode(); + var node = modelElement.element + .getAncestor((e) => e is ClassElement) + .computeNode(); if (node is AnnotatedNode) { if (node.documentationComment != null) { return node.documentationComment.references; @@ -220,9 +223,11 @@ MatchingLinkResult _getMatchingLinkElement( // By debugging inspection, it seems correct to not warn when we don't have // CommentReferences; there's actually nothing that needs resolving in // that case. - if (commentRefs == null) return new MatchingLinkResult(null, null, warn: false); + if (commentRefs == null) + return new MatchingLinkResult(null, null, warn: false); - if (!codeRef.contains(isConstructor) && codeRef.contains(notARealDocReference)) { + if (!codeRef.contains(isConstructor) && + codeRef.contains(notARealDocReference)) { // Don't waste our time on things we won't ever find. return new MatchingLinkResult(null, null, warn: false); } @@ -240,7 +245,8 @@ MatchingLinkResult _getMatchingLinkElement( if (refElement == null) { for (CommentReference ref in commentRefs) { if (ref.identifier.name == codeRef) { - bool isConstrElement = ref.identifier.staticElement is ConstructorElement; + bool isConstrElement = + ref.identifier.staticElement is ConstructorElement; // Constructors are now handled by library search. if (!isConstrElement) { refElement = ref.identifier.staticElement; @@ -262,13 +268,17 @@ MatchingLinkResult _getMatchingLinkElement( } // Ignore all parameters. - if (refElement is ParameterElement || refElement is TypeParameterElement) return new MatchingLinkResult(null, null, warn: false); + if (refElement is ParameterElement || refElement is TypeParameterElement) + return new MatchingLinkResult(null, null, warn: false); Library refLibrary = element.package.findOrCreateLibraryFor(refElement); - Element searchElement = refElement is Member ? refElement.baseElement : refElement; + Element searchElement = + refElement is Member ? refElement.baseElement : refElement; Class preferredClass = _getPreferredClass(element); - ModelElement refModelElement = element.package.findCanonicalModelElementFor(searchElement, preferredClass: preferredClass); + ModelElement refModelElement = element.package.findCanonicalModelElementFor( + searchElement, + preferredClass: preferredClass); // There have been places in the code which helpfully cache entities // regardless of what package they are associated with. This assert // will protect us from reintroducing that. @@ -287,7 +297,6 @@ MatchingLinkResult _getMatchingLinkElement( return new MatchingLinkResult(refModelElement, null); } - /// Returns true if this is a constructor we should consider in /// _findRefElementInLibrary, or if this isn't a constructor. bool _ConsiderIfConstructor(String codeRef, ModelElement modelElement) { @@ -296,7 +305,8 @@ bool _ConsiderIfConstructor(String codeRef, ModelElement modelElement) { Constructor aConstructor = modelElement; List codeRefParts = codeRef.split('.'); if (codeRefParts.length > 1) { - if (codeRefParts[codeRefParts.length - 1] == codeRefParts[codeRefParts.length - 2]) { + if (codeRefParts[codeRefParts.length - 1] == + codeRefParts[codeRefParts.length - 2]) { // Foobar.Foobar -- assume they really do mean the constructor for this class. return true; } @@ -308,7 +318,6 @@ bool _ConsiderIfConstructor(String codeRef, ModelElement modelElement) { return false; } - // Basic map of reference to ModelElement, for cases where we're searching // outside of scope. // TODO(jcollins-g): function caches with maps are very common in dartdoc. @@ -351,7 +360,8 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { // We don't link these, but this keeps us from emitting warnings. Be sure to // get members of parameters too. // TODO(jcollins-g): link to classes that are the types of parameters, where known - results.addAll(element.allParameters.where((p) => p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}."))); + results.addAll(element.allParameters.where((p) => + p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}."))); results.remove(null); if (results.isEmpty) { @@ -362,23 +372,27 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { if (element is Inheritable) { ModelElement overriddenElement = element.overriddenElement; while (overriddenElement != null) { - tryClasses.add((element.overriddenElement as EnclosedElement).enclosingElement); + tryClasses.add( + (element.overriddenElement as EnclosedElement).enclosingElement); overriddenElement = overriddenElement.overriddenElement; } } for (Class tryClass in tryClasses) { if (tryClass != null) { - _getResultsForClass(tryClass, codeRefChomped, results, codeRef, package); + _getResultsForClass( + tryClass, codeRefChomped, results, codeRef, package); } if (results.isNotEmpty) break; } // Sometimes documentation refers to classes that are further up the chain. // Get those too. if (results.isEmpty && realClass != null) { - for (Class superClass in realClass.superChain.map((et) => et.element as Class)) { + for (Class superClass + in realClass.superChain.map((et) => et.element as Class)) { if (!tryClasses.contains(superClass)) { - _getResultsForClass(superClass, codeRefChomped, results, codeRef, package); + _getResultsForClass( + superClass, codeRefChomped, results, codeRef, package); } if (results.isNotEmpty) break; } @@ -395,16 +409,21 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { assert(package.allLibrariesAdded); _findRefElementCache = new Map(); for (final modelElement in package.allModelElements) { - _findRefElementCache.putIfAbsent(modelElement.fullyQualifiedNameWithoutLibrary, () => new Set()); - _findRefElementCache.putIfAbsent(modelElement.fullyQualifiedName, () => new Set()); + _findRefElementCache.putIfAbsent( + modelElement.fullyQualifiedNameWithoutLibrary, () => new Set()); + _findRefElementCache.putIfAbsent( + modelElement.fullyQualifiedName, () => new Set()); _findRefElementCache[modelElement.fullyQualifiedName].add(modelElement); - _findRefElementCache[modelElement.fullyQualifiedNameWithoutLibrary].add(modelElement); + _findRefElementCache[modelElement.fullyQualifiedNameWithoutLibrary] + .add(modelElement); } } // But if not, look for a fully qualified match. (That only makes sense // if the codeRef might be qualified, and contains periods.) - if (results.isEmpty && codeRefChomped.contains('.') && _findRefElementCache.containsKey(codeRefChomped)) { + if (results.isEmpty && + codeRefChomped.contains('.') && + _findRefElementCache.containsKey(codeRefChomped)) { for (final modelElement in _findRefElementCache[codeRefChomped]) { if (!_ConsiderIfConstructor(codeRef, modelElement)) continue; results.add(package.findCanonicalModelElementFor(modelElement.element)); @@ -427,7 +446,8 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { if (results.isEmpty && _findRefElementCache.containsKey(codeRefChomped)) { for (final modelElement in _findRefElementCache[codeRefChomped]) { if (codeRefChomped == modelElement.fullyQualifiedNameWithoutLibrary || - (modelElement is Library && codeRefChomped == modelElement.fullyQualifiedName)) { + (modelElement is Library && + codeRefChomped == modelElement.fullyQualifiedName)) { results.add(package.findCanonicalModelElementFor(modelElement.element)); } } @@ -439,7 +459,9 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { if (results.isEmpty) { List codeRefChompedParts = codeRefChomped.split('.'); if (codeRefChompedParts.length >= 2) { - String maybeEnumName = codeRefChompedParts.sublist(0, codeRefChompedParts.length - 1).join('.'); + String maybeEnumName = codeRefChompedParts + .sublist(0, codeRefChompedParts.length - 1) + .join('.'); String maybeEnumMember = codeRefChompedParts.last; if (_findRefElementCache.containsKey(maybeEnumName)) { for (final modelElement in _findRefElementCache[maybeEnumName]) { @@ -469,7 +491,8 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { // TODO(jcollins-g): we could have saved ourselves some work by using the analyzer // to search the namespace, somehow. Do that instead. if (results.any((r) => r.element.isAccessibleIn(element.library.element))) { - results.removeWhere((r) => !r.element.isAccessibleIn(element.library.element)); + results.removeWhere( + (r) => !r.element.isAccessibleIn(element.library.element)); } } @@ -503,9 +526,13 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { // _getResultsForClass assumes codeRefChomped might be a member of tryClass (inherited or not) // and will add to [results] -void _getResultsForClass(Class tryClass, String codeRefChomped, Set results, String codeRef, Package package) { - if ((tryClass.modelType.typeArguments.map((e) => e.name)).contains(codeRefChomped)) { - results.add(tryClass.modelType.typeArguments.firstWhere((e) => e.name == codeRefChomped).element); +void _getResultsForClass(Class tryClass, String codeRefChomped, + Set results, String codeRef, Package package) { + if ((tryClass.modelType.typeArguments.map((e) => e.name)) + .contains(codeRefChomped)) { + results.add(tryClass.modelType.typeArguments + .firstWhere((e) => e.name == codeRefChomped) + .element); } else { // People like to use 'this' in docrefs too. if (codeRef == 'this') { @@ -519,7 +546,8 @@ void _getResultsForClass(Class tryClass, String codeRefChomped, Set t.returnElement as Class)); + superChain + .addAll(tryClass.superChainRaw.map((t) => t.returnElement as Class)); List codeRefParts = codeRefChomped.split('.'); for (final c in superChain) { // TODO(jcollins-g): add a hash-map-enabled lookup function to Class? @@ -528,7 +556,9 @@ void _getResultsForClass(Class tryClass, String codeRefChomped, Set 1) { String codeRefClass = codeRefParts[codeRefParts.length - 2]; String codeRefConstructor = codeRefParts.last; - if (codeRefClass == c.name && codeRefConstructor == modelElement.fullyQualifiedName.split('.').last) { - results.add(package.findCanonicalModelElementFor(modelElement.element, preferredClass: tryClass)); + if (codeRefClass == c.name && + codeRefConstructor == + modelElement.fullyQualifiedName.split('.').last) { + results.add(package.findCanonicalModelElementFor( + modelElement.element, + preferredClass: tryClass)); continue; } } @@ -609,15 +645,19 @@ String _renderMarkdownToHtml(String text, [ModelElement element]) { // (like, [Apple]). @Hixie asked for a warning when there's something, that looks // like a non HTML tag (a generic?) outside of a `[]` block. // https://github.com/dart-lang/dartdoc/issues/1250#issuecomment-269257942 -void _showWarningsForGenericsOutsideSquareBracketsBlocks(String text, - ModelElement element) { +void _showWarningsForGenericsOutsideSquareBracketsBlocks( + String text, ModelElement element) { List tagPositions = findFreeHangingGenericsPositions(text); if (tagPositions.isNotEmpty) { tagPositions.forEach((int position) { - String priorContext = "${text.substring(max(position - 20, 0), position)}"; - String postContext = "${text.substring(position, min(position + 30, text.length))}"; - priorContext = priorContext.replaceAll(new RegExp(r'^.*\n', multiLine: true), ''); - postContext = postContext.replaceAll(new RegExp(r'\n.*$', multiLine: true), ''); + String priorContext = + "${text.substring(max(position - 20, 0), position)}"; + String postContext = + "${text.substring(position, min(position + 30, text.length))}"; + priorContext = + priorContext.replaceAll(new RegExp(r'^.*\n', multiLine: true), ''); + postContext = + postContext.replaceAll(new RegExp(r'\n.*$', multiLine: true), ''); String errorMessage = "$priorContext$postContext"; // TODO(jcollins-g): allow for more specific error location inside comments element.warn(PackageWarning.typeAsHtml, errorMessage); diff --git a/lib/src/model.dart b/lib/src/model.dart index 8743c23a4c..72b15b42f7 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -170,7 +170,8 @@ class Accessor extends ModelElement with SourceCodeMixin implements EnclosedElement { ModelElement _enclosingCombo; - Accessor(PropertyAccessorElement element, Library library, this._enclosingCombo) + Accessor( + PropertyAccessorElement element, Library library, this._enclosingCombo) : super(element, library); ModelElement get enclosingCombo => _enclosingCombo; @@ -214,12 +215,15 @@ class Accessor extends ModelElement ? t.getGetter(element.name) : t.getSetter(element.name); if (accessor != null) { - Class parentClass = new ModelElement.from(parent, package.findOrCreateLibraryFor(parent)); + Class parentClass = new ModelElement.from( + parent, package.findOrCreateLibraryFor(parent)); List possibleFields = []; possibleFields.addAll(parentClass.allInstanceProperties); possibleFields.addAll(parentClass.staticProperties); String fieldName = accessor.name.replaceFirst('=', ''); - _overriddenElement = new ModelElement.from(accessor, library, enclosingCombo: possibleFields.firstWhere((f) => f.element.name == fieldName)); + _overriddenElement = new ModelElement.from(accessor, library, + enclosingCombo: possibleFields + .firstWhere((f) => f.element.name == fieldName)); break; } } @@ -519,8 +523,8 @@ class Class extends ModelElement implements EnclosedElement { //Library lib = package.findOrCreateLibraryFor(value.enclosingElement); //Class enclosingClass = // new ModelElement.from(value.enclosingElement, lib); - _inheritedMethods.add(new ModelElement.from(value, library, - enclosingClass: this)); + _inheritedMethods + .add(new ModelElement.from(value, library, enclosingClass: this)); } } } @@ -577,8 +581,8 @@ class Class extends ModelElement implements EnclosedElement { _inheritedOperators.add(o); _genPageOperators.add(o); } else { - _inheritedOperators.add(new ModelElement.from(value, library, - enclosingClass: this)); + _inheritedOperators + .add(new ModelElement.from(value, library, enclosingClass: this)); } } @@ -1160,7 +1164,9 @@ class Field extends ModelElement /// Mixin for top-level variables and fields (aka properties) abstract class GetterSetterCombo implements ModelElement { Accessor get getter { - return _getter == null ? null : new ModelElement.from(_getter, library, enclosingCombo: this); + return _getter == null + ? null + : new ModelElement.from(_getter, library, enclosingCombo: this); } String get getterSetterDocumentationComment { @@ -1225,7 +1231,9 @@ abstract class GetterSetterCombo implements ModelElement { bool get writeOnly => hasSetter && !hasGetter; Accessor get setter { - return _setter == null ? null : new ModelElement.from(_setter, library, enclosingCombo: this); + return _setter == null + ? null + : new ModelElement.from(_setter, library, enclosingCombo: this); } PropertyAccessorElement get _getter; @@ -1684,7 +1692,8 @@ class Method extends ModelElement /// helps prevent subtle bugs as generated output for a non-canonical /// ModelElement will reference itself as part of the "wrong" [Library] /// from the public interface perspective. -abstract class ModelElement implements Comparable, Nameable, Documentable, Locatable { +abstract class ModelElement + implements Comparable, Nameable, Documentable, Locatable { final Element _element; final Library _library; @@ -1761,7 +1770,7 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat } } } else { - newModelElement = new Field.inherited(e, enclosingClass, library); + newModelElement = new Field.inherited(e, enclosingClass, library); } } if (e is ConstructorElement) { @@ -1798,7 +1807,8 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat library.package._allConstructedModelElements[key] = newModelElement; if (newModelElement is Inheritable) { Tuple2 iKey = new Tuple2(e, library); - library.package._allInheritableElements.putIfAbsent(iKey, () => new Set()); + library.package._allInheritableElements + .putIfAbsent(iKey, () => new Set()); library.package._allInheritableElements[iKey].add(newModelElement); } } @@ -2006,7 +2016,8 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat String get fullyQualifiedNameWithoutLibrary { // Remember, periods are legal in library names. if (_fullyQualifiedNameWithoutLibrary == null) { - _fullyQualifiedNameWithoutLibrary = fullyQualifiedName.replaceFirst("${library.fullyQualifiedName}.", ''); + _fullyQualifiedNameWithoutLibrary = + fullyQualifiedName.replaceFirst("${library.fullyQualifiedName}.", ''); } return _fullyQualifiedNameWithoutLibrary; } @@ -2019,7 +2030,8 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat Tuple2 get lineAndColumn { // TODO(jcollins-g): we should always be able to get line numbers. Why can't we, sometimes? if (!_isLineNumberComputed) { - _lineAndColumn = lineNumberCache.lineAndColumn(element.source.fullName, element.nameOffset); + _lineAndColumn = lineNumberCache.lineAndColumn( + element.source.fullName, element.nameOffset); } return _lineAndColumn; } @@ -2123,7 +2135,8 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat if (!_overriddenDocumentedElementIsSet) { ModelElement found = this; while ((found.element.documentationComment == null || - found.element.documentationComment == "") && !found.isCanonical && + found.element.documentationComment == "") && + !found.isCanonical && found.overriddenElement != null) { found = found.overriddenElement; } @@ -2156,7 +2169,8 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat if (_allParameters == null) { final Set recursedParameters = new Set(); final Set newParameters = new Set(); - if (this is GetterSetterCombo && (this as GetterSetterCombo).setter != null) { + if (this is GetterSetterCombo && + (this as GetterSetterCombo).setter != null) { newParameters.addAll((this as GetterSetterCombo).setter.parameters); } else { if (canHaveParameters) newParameters.addAll(parameters); @@ -2165,8 +2179,9 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat recursedParameters.addAll(newParameters); newParameters.clear(); for (Parameter p in recursedParameters) { - if (p.modelType.element.canHaveParameters ) { - newParameters.addAll(p.modelType.element.parameters.where((p) => !recursedParameters.contains(p))); + if (p.modelType.element.canHaveParameters) { + newParameters.addAll(p.modelType.element.parameters + .where((p) => !recursedParameters.contains(p))); } } } @@ -2201,7 +2216,8 @@ abstract class ModelElement implements Comparable, Nameable, Documentable, Locat } void warn(PackageWarning kind, [String message]) { - if (kind == PackageWarning.unresolvedDocReference && overriddenElement != null) { + if (kind == PackageWarning.unresolvedDocReference && + overriddenElement != null) { // The documentation we're using for this element came from somewhere else. // Attach the warning to that element to deduplicate. overriddenElement.warn(kind, message); @@ -2684,26 +2700,46 @@ enum PackageWarning { /// Provides description text and command line flags for warnings. /// TODO(jcollins-g): Actually use this for command line flags. Map> packageWarningText = { - PackageWarning.ambiguousDocReference: - ["ambiguous-doc-reference", "A comment reference could refer to two or more different objects"], - PackageWarning.ambiguousReexport: - ["ambiguous-reexport", "A symbol is exported from private to public in more than one place and dartdoc is forced to guess which one is canonical"], - PackageWarning.noCanonicalFound: - ["no-canonical-found", "A symbol is is part of the public interface for this package, but no library documented with this package documents it so dartdoc can not link to it"], - PackageWarning.noLibraryLevelDocs: - ["no-library-level-docs", "There are no library level docs for this library"], - PackageWarning.categoryOrderGivesMissingPackageName: - ["category-order-gives-missing-package-name", "The category-order flag on the command line was given the name of a nonexistent package"], - PackageWarning.unresolvedDocReference: - ["unresolved-doc-reference", "A comment reference could not be found in parameters, enclosing class, enclosing library, or at the top level of any documented library with the package"], - PackageWarning.brokenLink: - ["brokenLink", "Dartdoc generated a link to a non-existent file"], - PackageWarning.orphanedFile: - ["orphanedFile", "Dartdoc generated files that are unreachable from the index"], - PackageWarning.unknownFile: - ["unknownFile", "A leftover file exists in the tree that dartdoc did not write in this pass"], - PackageWarning.typeAsHtml: - ["typeAsHtml", "Use of <> in a comment for type parameters is being treated as HTML by markdown"], + PackageWarning.ambiguousDocReference: [ + "ambiguous-doc-reference", + "A comment reference could refer to two or more different objects" + ], + PackageWarning.ambiguousReexport: [ + "ambiguous-reexport", + "A symbol is exported from private to public in more than one place and dartdoc is forced to guess which one is canonical" + ], + PackageWarning.noCanonicalFound: [ + "no-canonical-found", + "A symbol is is part of the public interface for this package, but no library documented with this package documents it so dartdoc can not link to it" + ], + PackageWarning.noLibraryLevelDocs: [ + "no-library-level-docs", + "There are no library level docs for this library" + ], + PackageWarning.categoryOrderGivesMissingPackageName: [ + "category-order-gives-missing-package-name", + "The category-order flag on the command line was given the name of a nonexistent package" + ], + PackageWarning.unresolvedDocReference: [ + "unresolved-doc-reference", + "A comment reference could not be found in parameters, enclosing class, enclosing library, or at the top level of any documented library with the package" + ], + PackageWarning.brokenLink: [ + "brokenLink", + "Dartdoc generated a link to a non-existent file" + ], + PackageWarning.orphanedFile: [ + "orphanedFile", + "Dartdoc generated files that are unreachable from the index" + ], + PackageWarning.unknownFile: [ + "unknownFile", + "A leftover file exists in the tree that dartdoc did not write in this pass" + ], + PackageWarning.typeAsHtml: [ + "typeAsHtml", + "Use of <> in a comment for type parameters is being treated as HTML by markdown" + ], }; // Something that can be located for warning purposes. @@ -2735,7 +2771,10 @@ class PackageWarningOptions { } void _assertInvariantsOk() { - assert(_asWarnings.union(_asErrors).union(_ignoreWarnings).containsAll(PackageWarning.values.toSet())); + assert(_asWarnings + .union(_asErrors) + .union(_ignoreWarnings) + .containsAll(PackageWarning.values.toSet())); assert(_asWarnings.union(_asErrors).intersection(_ignoreWarnings).isEmpty); } @@ -2765,7 +2804,8 @@ class PackageWarningOptions { } class PackageWarningCounter { - final Map>> _countedWarnings = new Map(); + final Map>> _countedWarnings = + new Map(); final Map _warningCounts = new Map(); final PackageWarningOptions options; @@ -2776,7 +2816,8 @@ class PackageWarningCounter { if (options.ignoreWarnings.contains(kind)) return; String toWrite; if (!options.asErrors.contains(kind)) { - if (options.asWarnings.contains(kind)) toWrite = "warning: ${fullMessage}"; + if (options.asWarnings.contains(kind)) + toWrite = "warning: ${fullMessage}"; } else { if (options.asErrors.contains(kind)) toWrite = "error: ${fullMessage}"; } @@ -2795,7 +2836,7 @@ class PackageWarningCounter { /// Adds the warning to the counter, and writes out the fullMessage string /// if configured to do so. void addWarning(Element element, PackageWarning kind, String message, - String fullMessage) { + String fullMessage) { assert(!hasWarning(element, kind, message)); Tuple2 warningData = new Tuple2(kind, message); _warningCounts.putIfAbsent(kind, () => 0); @@ -2806,17 +2847,25 @@ class PackageWarningCounter { } int get errorCount { - return _warningCounts.keys.map((w) => options.asErrors.contains(w) ? _warningCounts[w] : 0).reduce((a, b) => a + b); + return _warningCounts.keys + .map((w) => options.asErrors.contains(w) ? _warningCounts[w] : 0) + .reduce((a, b) => a + b); } int get warningCount { - return _warningCounts.keys.map((w) => options.asWarnings.contains(w) && !options.asErrors.contains(w) ? _warningCounts[w] : 0).reduce((a, b) => a + b); + return _warningCounts.keys + .map((w) => + options.asWarnings.contains(w) && !options.asErrors.contains(w) + ? _warningCounts[w] + : 0) + .reduce((a, b) => a + b); } @override String toString() { String errors = '$errorCount ${errorCount == 1 ? "error" : "errors"}'; - String warnings = '$warningCount ${warningCount == 1 ? "warning" : "warnings"}'; + String warnings = + '$warningCount ${warningCount == 1 ? "warning" : "warnings"}'; return [errors, warnings].join(', '); } } @@ -2831,11 +2880,12 @@ class Package implements Nameable, Documentable, Locatable { final PackageWarningOptions _packageWarningOptions; PackageWarningCounter _packageWarningCounter; // All ModelElements constructed for this package; a superset of allModelElements. - final Map, ModelElement> _allConstructedModelElements = - new Map(); + final Map, ModelElement> + _allConstructedModelElements = new Map(); // Anything that might be inheritable, place here for later lookup. - final Map, Set> _allInheritableElements = new Map(); + final Map, Set> + _allInheritableElements = new Map(); /// Map of Class.href to a list of classes implementing that class final Map> _implementors = new Map(); @@ -2847,7 +2897,8 @@ class Package implements Nameable, Documentable, Locatable { final Map _macros = {}; bool allLibrariesAdded = false; - Package(Iterable libraryElements, this.packageMeta, this._packageWarningOptions) { + Package(Iterable libraryElements, this.packageMeta, + this._packageWarningOptions) { assert(_allConstructedModelElements.isEmpty); assert(_allLibraries.isEmpty); _packageWarningCounter = new PackageWarningCounter(_packageWarningOptions); @@ -2896,7 +2947,8 @@ class Package implements Nameable, Documentable, Locatable { while (topLevelElement.enclosingElement is! CompilationUnitElement) { topLevelElement = topLevelElement.enclosingElement; } - modelElement = new ModelElement.from(topLevelElement, findOrCreateLibraryFor(topLevelElement)); + modelElement = new ModelElement.from( + topLevelElement, findOrCreateLibraryFor(topLevelElement)); } if (modelElement is Accessor) { // This might be part of a Field, if so, assign this warning to the field @@ -2908,7 +2960,8 @@ class Package implements Nameable, Documentable, Locatable { // If we don't have an element, we need a message to disambiguate. assert(message != null); } - if (_packageWarningCounter.hasWarning(modelElement?.element, kind, message)) { + if (_packageWarningCounter.hasWarning( + modelElement?.element, kind, message)) { return; } // Elements that are part of the Dart SDK can have colons in their FQNs. @@ -2920,7 +2973,8 @@ class Package implements Nameable, Documentable, Locatable { // the user, yet we still want IntelliJ to link properly. String nameSplitFromColonPieces; if (modelElement != null) { - nameSplitFromColonPieces = modelElement.fullyQualifiedName.replaceFirst(':', '-'); + nameSplitFromColonPieces = + modelElement.fullyQualifiedName.replaceFirst(':', '-'); } String warningMessage; switch (kind) { @@ -2968,8 +3022,7 @@ class Package implements Nameable, Documentable, Locatable { 'dartdoc detected an unknown file in the doc tree: ${message}'; break; case PackageWarning.typeAsHtml: - warningMessage = - 'generic type handled as HTML: """${message}"""'; + warningMessage = 'generic type handled as HTML: """${message}"""'; break; } // warningMessage should not contain "file:" or "dart:" -- confuses IntelliJ. @@ -2977,12 +3030,16 @@ class Package implements Nameable, Documentable, Locatable { // message can contain user text; nothing we can do about that. assert(!warningMessage.contains(s) || message.contains(s)); }); - String fullMessage = "${warningMessage} ${modelElement != null ? modelElement.elementLocation : ''}"; - packageWarningCounter.addWarning(modelElement?.element, kind, message, fullMessage); + String fullMessage = + "${warningMessage} ${modelElement != null ? modelElement.elementLocation : ''}"; + packageWarningCounter.addWarning( + modelElement?.element, kind, message, fullMessage); } static Package _withAutoIncludedDependencies( - Set libraryElements, PackageMeta packageMeta, PackageWarningOptions options) { + Set libraryElements, + PackageMeta packageMeta, + PackageWarningOptions options) { var startLength = libraryElements.length; Package package = new Package(libraryElements, packageMeta, options); @@ -3004,12 +3061,15 @@ class Package implements Nameable, Documentable, Locatable { }); if (libraryElements.length > startLength) - return _withAutoIncludedDependencies(libraryElements, packageMeta, options); + return _withAutoIncludedDependencies( + libraryElements, packageMeta, options); return package; } static Package withAutoIncludedDependencies( - Iterable libraryElements, PackageMeta packageMeta, PackageWarningOptions options) { + Iterable libraryElements, + PackageMeta packageMeta, + PackageWarningOptions options) { return _withAutoIncludedDependencies( new Set()..addAll(libraryElements), packageMeta, options); } @@ -3079,7 +3139,8 @@ class Package implements Nameable, Documentable, Locatable { Map> hrefMap = new Map(); // TODO(jcollins-g ): handle calculating hrefs causing new elements better // than toList(). - for (ModelElement modelElement in _allConstructedModelElements.values.toList()) { + for (ModelElement modelElement + in _allConstructedModelElements.values.toList()) { if (modelElement.href == null) continue; hrefMap.putIfAbsent(modelElement.href, () => new Set()); hrefMap[modelElement.href].add(modelElement); @@ -3187,6 +3248,7 @@ class Package implements Nameable, Documentable, Locatable { String toString() => isSdk ? 'SDK' : 'Package $name'; final Map _canonicalLibraryFor = new Map(); + /// Tries to find a top level library that references this element. Library findCanonicalLibraryFor(Element e) { assert(allLibrariesAdded); @@ -3201,7 +3263,8 @@ class Package implements Nameable, Documentable, Locatable { _canonicalLibraryFor[e] = null; for (Library library in libraries) { if (library.modelElementsMap.containsKey(searchElement)) { - for (ModelElement modelElement in library.modelElementsMap[searchElement]) { + for (ModelElement modelElement + in library.modelElementsMap[searchElement]) { if (modelElement.isCanonical) { _canonicalLibraryFor[e] = library; break; @@ -3223,11 +3286,14 @@ class Package implements Nameable, Documentable, Locatable { // with member elements. if (e is ClassMemberElement || e is PropertyAccessorElement) { // Prefer Fields over Accessors. - if (e is PropertyAccessorElement) e = (e as PropertyAccessorElement).variable; + if (e is PropertyAccessorElement) + e = (e as PropertyAccessorElement).variable; Set candidates = new Set(); Tuple2 iKey = new Tuple2(e, lib); - Tuple4 key = new Tuple4(e, lib, null, null); - Tuple4 keyWithClass = new Tuple4(e, lib, preferredClass, null); + Tuple4 key = + new Tuple4(e, lib, null, null); + Tuple4 keyWithClass = + new Tuple4(e, lib, preferredClass, null); if (_allConstructedModelElements.containsKey(key)) { candidates.add(_allConstructedModelElements[key]); } @@ -3235,7 +3301,8 @@ class Package implements Nameable, Documentable, Locatable { candidates.add(_allConstructedModelElements[keyWithClass]); } if (candidates.isEmpty && _allInheritableElements.containsKey(iKey)) { - candidates.addAll(_allInheritableElements[iKey].where((me) => me.isCanonical)); + candidates.addAll( + _allInheritableElements[iKey].where((me) => me.isCanonical)); } Class canonicalClass = findCanonicalModelElementFor(e.enclosingElement); if (canonicalClass != null) { @@ -3260,20 +3327,24 @@ class Package implements Nameable, Documentable, Locatable { return false; })); } - Set matches = new Set()..addAll(candidates.where((me) => me.isCanonical)); + Set matches = new Set() + ..addAll(candidates.where((me) => me.isCanonical)); // This is for situations where multiple classes may actually be canonical // for an inherited element whose defining Class is not canonical. if (matches.length > 1 && preferredClass != null) { // Search for matches inside our superchain. - List superChain = preferredClass.superChainRaw.map((et) => et.element).toList(); + List superChain = + preferredClass.superChainRaw.map((et) => et.element).toList(); superChain.add(preferredClass); - matches.removeWhere((me) => !superChain.contains((me as EnclosedElement).enclosingElement)); + matches.removeWhere((me) => + !superChain.contains((me as EnclosedElement).enclosingElement)); } assert(matches.length <= 1); if (!matches.isEmpty) modelElement = matches.first; - if (modelElement == null && e.enclosingElement.library.name != 'dart._internal' && !e.name.startsWith('_')) - 1+1; + if (modelElement == null && + e.enclosingElement.library.name != 'dart._internal' && + !e.name.startsWith('_')) 1 + 1; } else { if (lib != null) modelElement = new ModelElement.from(e, lib); assert(modelElement is! Inheritable); diff --git a/test/model_test.dart b/test/model_test.dart index e60ccc242f..96b68ec131 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -37,7 +37,8 @@ void main() { Package sdkAsPackage = Package.withAutoIncludedDependencies( getSdkLibrariesToDocument(utils.sdkDir, utils.analyzerHelper.context), - new PackageMeta.fromSdk(sdkDir), new PackageWarningOptions()); + new PackageMeta.fromSdk(sdkDir), + new PackageWarningOptions()); group('Package', () { group('test package', () { @@ -316,7 +317,10 @@ void main() { test( 'link to a name in another library in this package, but is not imported into this library, should still be linked', () { - expect(docsAsHtml, contains('doesStuff')); + expect( + docsAsHtml, + contains( + 'doesStuff')); }); test( @@ -478,7 +482,8 @@ void main() { expect(resolved, isNotNull); expect(resolved, contains('BaseClass')); - expect(resolved, contains('Linking over to Apple')); + expect(resolved, + contains('Linking over to Apple')); }); test('references to class and constructors', () { diff --git a/test/src/utils.dart b/test/src/utils.dart index f66ae0b661..ac12e3bc54 100644 --- a/test/src/utils.dart +++ b/test/src/utils.dart @@ -69,11 +69,13 @@ Package _bootPackage(Iterable libPaths, String dirPath, if (withAutoIncludedDependencies) { return Package.withAutoIncludedDependencies( - libElements, new PackageMeta.fromDir(new Directory(dirPath)), + libElements, + new PackageMeta.fromDir(new Directory(dirPath)), new PackageWarningOptions()); } else { return new Package( - libElements, new PackageMeta.fromDir(new Directory(dirPath)), + libElements, + new PackageMeta.fromDir(new Directory(dirPath)), new PackageWarningOptions()); } } From 09706d81c03d18eba84cef143b70b36cb7687d1a Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 2 May 2017 08:48:06 -0700 Subject: [PATCH 04/12] Update changelog, reduce memory usage for link checker --- CHANGELOG.md | 10 +++++----- lib/dartdoc.dart | 46 ++++++++++++++++++++++++++++------------------ lib/src/model.dart | 3 --- 3 files changed, 33 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 240a07a9a2..867bf1469f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ * Many cleanups to dartdoc stdout/stderr, error messages, and warnings: * Display fatal errors with 'fatal error' string to distinguish them from ordinary errors * Upgrades to new Package.warn system. - * Fully integrated all scattered "warnings" and added new ones for the link checker. + * Fully integrated all scattered "warnings" (#1369) and added new ones for the link checker. * Allow for setting which warnings are errors in the library. * Change location output to something IntelliJ can understand and link to * Display location output for all warnings including line number plus column, when available @@ -17,7 +17,7 @@ * Make the progress counter tick slower. * Added a built-in link checker and orphaned file generator, and tied it into Package.warn so that when debugging dartdoc we can breakpoint and discover what about that ModelElement - caused to create the broken link. + caused us to create the broken link. (#1380) * Fix bug where canonicalEnclosingElement could return a non-canonical Class. * Fix bug where findCanonicalModelElementFor could return a non-canonical Class. * Fix overriddenElement for Accessors to generate using enclosingCombo hint to ModelElement factory. @@ -27,7 +27,7 @@ * Squish some bugs related to duplicate logic for instantiating inherited class members. * Enum and a few other places could still generate duplicate ModelElements for the same thing. This is now fixed. - * EnumField is now handled by ModelElement.from factory. + * EnumField is now handled by ModelElement.from factory, fixing #1239. * Added hints for EnumField and Accessors (index, enclosingCombo) to offload the buggy logic for figuring this out from callers to ModelElement.from. * Fix broken link generation when a canonical class's defining library isn't canonical. @@ -35,13 +35,13 @@ * Link correctly to generic types for Fields/TopLevelVariables. * Use right, left, and bidirectional arrows for read-only, write-only, and read-write parameters. -* Partial rewrite of comment reference system +* Partial rewrite of comment reference system (#1391, #1285 partial) * Handle gracefully a variety of things users try in the real world, like prefixing operators with 'operator', embedded newlines in comment references, and cases that shouldn't be considered at all (comment refs that are really array references in sample docs, etc). * Handle canonicalization correctly for comment references: point to the right places and only to canonical elements. - * In general, warnings related to comment references should be much more useful now. + * In general, warnings related to comment references should be much more useful now. (#1343) * Many fewer ambiguous doc reference warnings now and the ones that exist should be more easily understandable and fixable with the new warning message. * Understand references to parameters even though we don't do anything useful with them just yet diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 86ab18ce62..10c96a1022 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -26,6 +26,7 @@ import 'package:html/parser.dart' show parse; import 'package:package_config/discovery.dart' as package_config; import 'package:path/path.dart' as path; +import 'package:tuple/tuple.dart'; import 'src/config.dart'; import 'src/generator.dart'; import 'src/html/html_generator.dart'; @@ -280,21 +281,12 @@ class DartDoc { } } - _doCheck( - Package package, String origin, Set visited, String pathToCheck, - [String source, String fullPath]) { - if (fullPath == null) { - fullPath = path.joinAll([origin, pathToCheck]); - fullPath = path.normalize(fullPath); - } - + // This is extracted to save memory during the check; be careful not to hang + // on to anything referencing the full file and doc tree. + Tuple2, String> _getStringLinksAndHref(String fullPath) { File file = new File("$fullPath"); if (!file.existsSync()) { - _warn(package, PackageWarning.brokenLink, pathToCheck, - path.normalize(origin), - source: source); - _onCheckProgress.add(pathToCheck); - return; + return null; } Document doc = parse(file.readAsStringSync()); Element base = doc.querySelector('base'); @@ -303,9 +295,30 @@ class DartDoc { baseHref = base.attributes['href']; } List links = doc.querySelectorAll('a'); - Iterable stringLinks = links + List stringLinks = links .map((link) => link.attributes['href']) - .where((href) => href != null); + .where((href) => href != null).toList(); + return new Tuple2(stringLinks, baseHref); + } + + void _doCheck( + Package package, String origin, Set visited, String pathToCheck, + [String source, String fullPath]) { + if (fullPath == null) { + fullPath = path.joinAll([origin, pathToCheck]); + fullPath = path.normalize(fullPath); + } + + Tuple2 stringLinksAndHref = _getStringLinksAndHref(fullPath); + if (stringLinksAndHref == null) { + _warn(package, PackageWarning.brokenLink, pathToCheck, + path.normalize(origin), + source: source); + _onCheckProgress.add(pathToCheck); + return null; + } + Iterable stringLinks = stringLinksAndHref.item1; + String baseHref = stringLinksAndHref.item2; for (String href in stringLinks) { if (!href.startsWith('http') && !href.contains('#')) { @@ -325,10 +338,7 @@ class DartDoc { } } } - _onCheckProgress.add(pathToCheck); - 1 + 1; - //return; } Map> _hrefs; diff --git a/lib/src/model.dart b/lib/src/model.dart index 72b15b42f7..588fb346fd 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -3342,9 +3342,6 @@ class Package implements Nameable, Documentable, Locatable { } assert(matches.length <= 1); if (!matches.isEmpty) modelElement = matches.first; - if (modelElement == null && - e.enclosingElement.library.name != 'dart._internal' && - !e.name.startsWith('_')) 1 + 1; } else { if (lib != null) modelElement = new ModelElement.from(e, lib); assert(modelElement is! Inheritable); From 48f30cd6d66727aae89a9be46023c7db112d2a7b Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 2 May 2017 08:48:29 -0700 Subject: [PATCH 05/12] dartfmt --- lib/dartdoc.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 10c96a1022..cfef13296d 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -297,7 +297,8 @@ class DartDoc { List links = doc.querySelectorAll('a'); List stringLinks = links .map((link) => link.attributes['href']) - .where((href) => href != null).toList(); + .where((href) => href != null) + .toList(); return new Tuple2(stringLinks, baseHref); } @@ -311,7 +312,7 @@ class DartDoc { Tuple2 stringLinksAndHref = _getStringLinksAndHref(fullPath); if (stringLinksAndHref == null) { - _warn(package, PackageWarning.brokenLink, pathToCheck, + _warn(package, PackageWarning.brokenLink, pathToCheck, path.normalize(origin), source: source); _onCheckProgress.add(pathToCheck); From ef98fc16723cb0f174450472f2625c3378ced8e2 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 2 May 2017 09:14:51 -0700 Subject: [PATCH 06/12] delete dartdoc.iml --- dartdoc.iml | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 dartdoc.iml diff --git a/dartdoc.iml b/dartdoc.iml deleted file mode 100644 index 726840a663..0000000000 --- a/dartdoc.iml +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file From a009d9dac7d7138740a474a85fa4da2679ab4714 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 2 May 2017 11:16:36 -0700 Subject: [PATCH 07/12] Review comments --- lib/dartdoc.dart | 22 ++++++------- lib/src/html/html_generator.dart | 4 +++ lib/src/html/html_generator_instance.dart | 2 +- lib/src/line_number_cache.dart | 1 + lib/src/markdown_processor.dart | 38 +++++++++++++++-------- lib/src/model.dart | 14 +++++---- 6 files changed, 48 insertions(+), 33 deletions(-) diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index cfef13296d..24871b0c94 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -186,11 +186,10 @@ class DartDoc { for (var generator in generators) { await generator.generate(package, outputDir); - generator.writtenFiles - .forEach((p) => writtenFiles.add(path.normalize(p))); + writtenFiles.addAll(generator.writtenFiles.map(path.normalize)); } - await verifyLinks(package, outputDir.path); + verifyLinks(package, outputDir.path); double seconds = _stopwatch.elapsedMilliseconds / 1000.0; print( @@ -245,14 +244,13 @@ class DartDoc { package.warn(referenceElement, kind, p); } - Future _doOrphanCheck( - Package package, String origin, Set visited) async { + void _doOrphanCheck( + Package package, String origin, Set visited) { String normalOrigin = path.normalize(origin); String staticAssets = path.joinAll([normalOrigin, 'static-assets', '']); String indexJson = path.joinAll([normalOrigin, 'index.json']); bool foundIndex = false; - await for (FileSystemEntity f - in new Directory(normalOrigin).list(recursive: true)) { + for (FileSystemEntity f in new Directory(normalOrigin).listSync(recursive: true)) { var fullPath = path.normalize(f.path); if (f is Directory) { continue; @@ -346,18 +344,16 @@ class DartDoc { /// Don't call this method more than once, and only after you've /// generated all docs for the Package. - Future verifyLinks(Package package, String origin) async { + void verifyLinks(Package package, String origin) { assert(_hrefs == null); _hrefs = package.allHrefs; - Set visited = new Set(); - String start = 'index.html'; + final Set visited = new Set(); + final String start = 'index.html'; visited.add(start); stdout.write('\nvalidating docs'); _doCheck(package, origin, visited, start); - await _doOrphanCheck(package, origin, visited); - // A do nothing line magically makes await _doOrphanCheck actually await? - return; + _doOrphanCheck(package, origin, visited); } List _parseLibraries( diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart index ecc35f6b45..0fc444744a 100644 --- a/lib/src/html/html_generator.dart +++ b/lib/src/html/html_generator.dart @@ -64,7 +64,11 @@ class HtmlGenerator extends Generator { HtmlGenerator._(this._options, this._templates); @override + + /// Actually write out the documentation for [package]. + /// Stores the HtmlGeneratorInstance so we can access it in [writtenFiles]. Future generate(Package package, Directory out) { + assert (_instance == null); _instance = new HtmlGeneratorInstance( _options, _templates, package, out, _onFileCreated); return _instance.generate(); diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart index e1bb2045d5..436e6a234a 100644 --- a/lib/src/html/html_generator_instance.dart +++ b/lib/src/html/html_generator_instance.dart @@ -294,7 +294,7 @@ class HtmlGeneratorInstance implements HtmlOptions { } } - get writtenFiles => _writtenFiles; + Set get writtenFiles => _writtenFiles; void _build(String filename, TemplateRenderer template, TemplateData data) { String fullName = path.join(out.path, filename); diff --git a/lib/src/line_number_cache.dart b/lib/src/line_number_cache.dart index cf73c796ae..a2cdc49c92 100644 --- a/lib/src/line_number_cache.dart +++ b/lib/src/line_number_cache.dart @@ -6,6 +6,7 @@ library dartdoc.cache; import 'dart:collection'; import 'dart:io'; + import 'package:tuple/tuple.dart'; String _getNewlineChar(String contents) { diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart index 26f6509e77..659031ba2f 100644 --- a/lib/src/markdown_processor.dart +++ b/lib/src/markdown_processor.dart @@ -116,23 +116,23 @@ const validHtmlTags = const [ "video", "wbr" ]; -final nonHTMLRegexp = +final RegExp nonHTML = new RegExp(" ])\\w+[> ]"); // Type parameters and other things to ignore at the end of doc references. -final trailingIgnoreStuff = new RegExp(r'(<.*>|\(.*\))$'); +final RegExp trailingIgnoreStuff = new RegExp(r'(<.*>|\(.*\))$'); // Things to ignore at the beginning of doc references -final leadingIgnoreStuff = +final RegExp leadingIgnoreStuff = new RegExp(r'^(const|final|var)[\s]+', multiLine: true); // This is explicitly intended as a reference to a constructor. -final isConstructor = new RegExp(r'^new[\s]+', multiLine: true); +final RegExp isConstructor = new RegExp(r'^new[\s]+', multiLine: true); // This is probably not really intended as a doc reference, so don't try or // warn about them. // Covers anything with leading digits/symbols, empty string, weird punctuation, spaces. -final notARealDocReference = new RegExp(r'''(^[^\w]|^[\d]|[,"'/]|^$)'''); +final RegExp notARealDocReference = new RegExp(r'''(^[^\w]|^[\d]|[,"'/]|^$)'''); // We don't emit warnings currently: #572. const List _oneLinerSkipTags = const ["code", "pre"]; @@ -293,6 +293,11 @@ MatchingLinkResult _getMatchingLinkElement( // canonical library found message. return new MatchingLinkResult(null, null, warn: false); } + // We should never get here unless there's a bug in findCanonicalModelElementFor. + // findCanonicalModelElementFor(searchElement, preferredClass: preferredClass) + // should only return null if ModelElement.from(searchElement, refLibrary) + // would return a non-canonical element. However, outside of checked mode, + // at least we have a canonical element, so proceed. assert(false); return new MatchingLinkResult(refModelElement, null); } @@ -325,6 +330,8 @@ bool _ConsiderIfConstructor(String codeRef, ModelElement modelElement) { Map> _findRefElementCache; // TODO(jcollins-g): Rewrite this to handle constructors in a less hacky way // TODO(jcollins-g): This function breaks down naturally into many helpers, extract them +// TODO(jcollins-g): Subcomponents of this function shouldn't be adding nulls to results, strip the +// removes out that are gratuitous and // TODO(jcollins-g): A complex package winds up spending a lot of cycles in here. Optimize. Element _findRefElementInLibrary(String codeRef, ModelElement element) { assert(element.package.allLibrariesAdded); @@ -335,8 +342,7 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { final Package package = library.package; final Set results = new Set(); - results.remove(null); - // Oh, and this might be an operator. Strip the operator prefix and try again. + // This might be an operator. Strip the operator prefix and try again. if (results.isEmpty && codeRef.startsWith('operator')) { String newCodeRef = codeRef.replaceFirst('operator', ''); return _findRefElementInLibrary(newCodeRef, element); @@ -362,8 +368,8 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { // TODO(jcollins-g): link to classes that are the types of parameters, where known results.addAll(element.allParameters.where((p) => p.name == codeRefChomped || codeRefChomped.startsWith("${p.name}."))); - results.remove(null); + results.remove(null); if (results.isEmpty) { // Maybe this is local to a class. // TODO(jcollins-g): tryClasses is a strict subset of the superclass chain. Optimize. @@ -476,9 +482,9 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { } } - results.remove(null); Element result; + results.remove(null); if (results.length > 1) { // If this name could refer to a class or a constructor, prefer the class. if (results.any((r) => r is Class)) { @@ -520,7 +526,6 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { "[$codeRef] => ${results.map((r) => "'${r.fullyQualifiedName}'").join(", ")}"); result = results.first.element; } - //_findRefElementInLibraryCache[key] = result; return result; } @@ -641,6 +646,13 @@ String _renderMarkdownToHtml(String text, [ModelElement element]) { inlineSyntaxes: _markdown_syntaxes, linkResolver: _linkResolver); } + +// Maximum number of characters to display before a suspected generic. +const maxPriorContext = 20; +// Maximum number of characters to display after the beginning of a suspected generic. +const maxPostContext = 30; + + // Generics should be wrapped into `[]` blocks, to avoid handling them as HTML tags // (like, [Apple]). @Hixie asked for a warning when there's something, that looks // like a non HTML tag (a generic?) outside of a `[]` block. @@ -651,9 +663,9 @@ void _showWarningsForGenericsOutsideSquareBracketsBlocks( if (tagPositions.isNotEmpty) { tagPositions.forEach((int position) { String priorContext = - "${text.substring(max(position - 20, 0), position)}"; + "${text.substring(max(position - maxPriorContext, 0), position)}"; String postContext = - "${text.substring(position, min(position + 30, text.length))}"; + "${text.substring(position, min(position + maxPostContext, text.length))}"; priorContext = priorContext.replaceAll(new RegExp(r'^.*\n', multiLine: true), ''); postContext = @@ -672,7 +684,7 @@ List findFreeHangingGenericsPositions(String string) { while (true) { final int nextOpenBracket = string.indexOf("[", currentPosition); final int nextCloseBracket = string.indexOf("]", currentPosition); - final int nextNonHTMLTag = string.indexOf(nonHTMLRegexp, currentPosition); + final int nextNonHTMLTag = string.indexOf(nonHTML, currentPosition); final Iterable nextPositions = [ nextOpenBracket, nextCloseBracket, diff --git a/lib/src/model.dart b/lib/src/model.dart index 588fb346fd..c461f015ba 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -520,9 +520,6 @@ class Class extends ModelElement implements EnclosedElement { _inheritedMethods.add(m); _genPageMethods.add(m); } else { - //Library lib = package.findOrCreateLibraryFor(value.enclosingElement); - //Class enclosingClass = - // new ModelElement.from(value.enclosingElement, lib); _inheritedMethods .add(new ModelElement.from(value, library, enclosingClass: this)); } @@ -879,7 +876,7 @@ class Constructor extends ModelElement String constructorName = element.name; Class c = new ModelElement.from(element.enclosingElement, library) as Class; if (constructorName.isEmpty) { - return '${c.name}'; + return c.name; } else { return '${c.name}.$constructorName'; } @@ -1218,9 +1215,14 @@ abstract class GetterSetterCombo implements ModelElement { } String get arrow { + // → if (readOnly) return r'→'; + // ← if (writeOnly) return r'←'; + // ↔ if (readWrite) return r'↔'; + // A GetterSetterCombo should always be one of readOnly, writeOnly, + // or readWrite. assert(false); return null; } @@ -2766,8 +2768,6 @@ class PackageWarningOptions { PackageWarningOptions() { _asWarnings.addAll(PackageWarning.values); ignore(PackageWarning.typeAsHtml); - //error(PackageWarning.brokenLink); - //error(PackageWarning.orphanedFile); } void _assertInvariantsOk() { @@ -3022,6 +3022,8 @@ class Package implements Nameable, Documentable, Locatable { 'dartdoc detected an unknown file in the doc tree: ${message}'; break; case PackageWarning.typeAsHtml: + // The message for this warning can contain many punctuation and other symbols, + // so bracket with a triple quote for defense. warningMessage = 'generic type handled as HTML: """${message}"""'; break; } From bc887df80d1e3678435a991e63ca83b3fcd45ff1 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Tue, 2 May 2017 11:17:01 -0700 Subject: [PATCH 08/12] dartfmt --- lib/dartdoc.dart | 6 +++--- lib/src/html/html_generator.dart | 2 +- lib/src/markdown_processor.dart | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/dartdoc.dart b/lib/dartdoc.dart index 24871b0c94..ec88c8f781 100644 --- a/lib/dartdoc.dart +++ b/lib/dartdoc.dart @@ -244,13 +244,13 @@ class DartDoc { package.warn(referenceElement, kind, p); } - void _doOrphanCheck( - Package package, String origin, Set visited) { + void _doOrphanCheck(Package package, String origin, Set visited) { String normalOrigin = path.normalize(origin); String staticAssets = path.joinAll([normalOrigin, 'static-assets', '']); String indexJson = path.joinAll([normalOrigin, 'index.json']); bool foundIndex = false; - for (FileSystemEntity f in new Directory(normalOrigin).listSync(recursive: true)) { + for (FileSystemEntity f + in new Directory(normalOrigin).listSync(recursive: true)) { var fullPath = path.normalize(f.path); if (f is Directory) { continue; diff --git a/lib/src/html/html_generator.dart b/lib/src/html/html_generator.dart index 0fc444744a..f1bb79a862 100644 --- a/lib/src/html/html_generator.dart +++ b/lib/src/html/html_generator.dart @@ -68,7 +68,7 @@ class HtmlGenerator extends Generator { /// Actually write out the documentation for [package]. /// Stores the HtmlGeneratorInstance so we can access it in [writtenFiles]. Future generate(Package package, Directory out) { - assert (_instance == null); + assert(_instance == null); _instance = new HtmlGeneratorInstance( _options, _templates, package, out, _onFileCreated); return _instance.generate(); diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart index 659031ba2f..9f1033e74e 100644 --- a/lib/src/markdown_processor.dart +++ b/lib/src/markdown_processor.dart @@ -646,13 +646,11 @@ String _renderMarkdownToHtml(String text, [ModelElement element]) { inlineSyntaxes: _markdown_syntaxes, linkResolver: _linkResolver); } - // Maximum number of characters to display before a suspected generic. const maxPriorContext = 20; // Maximum number of characters to display after the beginning of a suspected generic. const maxPostContext = 30; - // Generics should be wrapped into `[]` blocks, to avoid handling them as HTML tags // (like, [Apple]). @Hixie asked for a warning when there's something, that looks // like a non HTML tag (a generic?) outside of a `[]` block. From c31115e5279dfa08d770a20f25a6aba714262b15 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Wed, 3 May 2017 07:35:30 -0700 Subject: [PATCH 09/12] whitespace in changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 867bf1469f..44daa6cf8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,11 @@ * Display location output for all warnings including line number plus column, when available from analyzer (still some bugs in our resolution). It still doesn't do code references quite right but at least gets you to the neighborhood. - * Add a warn method to ModelElements so they can warn on themselves without help from the Package. + * Add a warn method to ModelElements so they can warn on themselves without help from the + Package. * Warn correctly and squelch duplicates across doc inheritance and canonicalization almost everywhere. - * Change --show-warnings to show all warnings, even those that might not be useful yet. + * Change --show-warnings to show all warnings, even those that might not be useful yet. * Display a count of all warnings/errors after document generation. * Make the progress counter tick slower. * Added a built-in link checker and orphaned file generator, and tied it into Package.warn so From 93b97a9ad23b7af8628071cdbc31e2a21b154e78 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 4 May 2017 07:32:51 -0700 Subject: [PATCH 10/12] Comment enhancements --- lib/src/markdown_processor.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart index 9f1033e74e..eaa2589bbc 100644 --- a/lib/src/markdown_processor.dart +++ b/lib/src/markdown_processor.dart @@ -331,7 +331,7 @@ Map> _findRefElementCache; // TODO(jcollins-g): Rewrite this to handle constructors in a less hacky way // TODO(jcollins-g): This function breaks down naturally into many helpers, extract them // TODO(jcollins-g): Subcomponents of this function shouldn't be adding nulls to results, strip the -// removes out that are gratuitous and +// removes out that are gratuitous and debug the individual pieces. // TODO(jcollins-g): A complex package winds up spending a lot of cycles in here. Optimize. Element _findRefElementInLibrary(String codeRef, ModelElement element) { assert(element.package.allLibrariesAdded); @@ -533,6 +533,8 @@ Element _findRefElementInLibrary(String codeRef, ModelElement element) { // and will add to [results] void _getResultsForClass(Class tryClass, String codeRefChomped, Set results, String codeRef, Package package) { + // This might be part of the type arguments for the class, if so, add them. + // Otherwise, search the class. if ((tryClass.modelType.typeArguments.map((e) => e.name)) .contains(codeRefChomped)) { results.add(tryClass.modelType.typeArguments From 5efd477cb400ada834f84c0d8e2c9331ba2097fc Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 4 May 2017 07:38:36 -0700 Subject: [PATCH 11/12] Correct changelog to 'orphaned file checker' --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44daa6cf8d..0e6f305916 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,7 @@ * Change --show-warnings to show all warnings, even those that might not be useful yet. * Display a count of all warnings/errors after document generation. * Make the progress counter tick slower. -* Added a built-in link checker and orphaned file generator, and tied it into Package.warn so +* Added a built-in link checker and orphaned file checker, and tied it into Package.warn so that when debugging dartdoc we can breakpoint and discover what about that ModelElement caused us to create the broken link. (#1380) * Fix bug where canonicalEnclosingElement could return a non-canonical Class. From 9aa79d122b9eca650150197f8ce3265e9941b4d6 Mon Sep 17 00:00:00 2001 From: Janice Collins Date: Thu, 4 May 2017 10:21:06 -0700 Subject: [PATCH 12/12] update test package docs -- whitespace change in SDK --- testing/test_package_docs/ex/Animal/hashCode.html | 8 ++++---- testing/test_package_docs/ex/Animal/operator_equals.html | 2 +- testing/test_package_docs/ex/Apple/hashCode.html | 8 ++++---- testing/test_package_docs/ex/Apple/operator_equals.html | 2 +- testing/test_package_docs/ex/Cat/hashCode.html | 8 ++++---- testing/test_package_docs/ex/Cat/operator_equals.html | 2 +- testing/test_package_docs/ex/CatString/hashCode.html | 8 ++++---- .../test_package_docs/ex/CatString/operator_equals.html | 2 +- testing/test_package_docs/ex/ConstantCat/hashCode.html | 8 ++++---- .../test_package_docs/ex/ConstantCat/operator_equals.html | 2 +- testing/test_package_docs/ex/Deprecated/hashCode.html | 8 ++++---- .../test_package_docs/ex/Deprecated/operator_equals.html | 2 +- testing/test_package_docs/ex/Dog/hashCode.html | 8 ++++---- testing/test_package_docs/ex/Dog/operator_equals.html | 2 +- testing/test_package_docs/ex/E/hashCode.html | 8 ++++---- testing/test_package_docs/ex/E/operator_equals.html | 2 +- testing/test_package_docs/ex/ForAnnotation/hashCode.html | 8 ++++---- .../ex/ForAnnotation/operator_equals.html | 2 +- testing/test_package_docs/ex/HasAnnotation/hashCode.html | 8 ++++---- .../ex/HasAnnotation/operator_equals.html | 2 +- testing/test_package_docs/ex/Helper/hashCode.html | 8 ++++---- testing/test_package_docs/ex/Helper/operator_equals.html | 2 +- testing/test_package_docs/ex/Klass/hashCode.html | 8 ++++---- testing/test_package_docs/ex/Klass/operator_equals.html | 2 +- testing/test_package_docs/ex/MyError/hashCode.html | 8 ++++---- testing/test_package_docs/ex/MyError/operator_equals.html | 2 +- .../test_package_docs/ex/MyErrorImplements/hashCode.html | 8 ++++---- .../ex/MyErrorImplements/operator_equals.html | 2 +- testing/test_package_docs/ex/MyException/hashCode.html | 8 ++++---- .../test_package_docs/ex/MyException/operator_equals.html | 2 +- .../ex/MyExceptionImplements/hashCode.html | 8 ++++---- .../ex/MyExceptionImplements/operator_equals.html | 2 +- .../ex/PublicClassExtendsPrivateClass/hashCode.html | 8 ++++---- .../PublicClassExtendsPrivateClass/operator_equals.html | 2 +- .../PublicClassImplementsPrivateInterface/hashCode.html | 8 ++++---- .../operator_equals.html | 2 +- testing/test_package_docs/ex/ShapeType/hashCode.html | 8 ++++---- .../test_package_docs/ex/ShapeType/operator_equals.html | 2 +- .../ex/SpecializedDuration/hashCode.html | 8 ++++---- testing/test_package_docs/ex/WithGeneric/hashCode.html | 8 ++++---- .../test_package_docs/ex/WithGeneric/operator_equals.html | 2 +- testing/test_package_docs/ex/aThingToDo/hashCode.html | 8 ++++---- .../test_package_docs/ex/aThingToDo/operator_equals.html | 2 +- testing/test_package_docs/fake/Annotation/hashCode.html | 8 ++++---- .../fake/Annotation/operator_equals.html | 2 +- .../test_package_docs/fake/AnotherInterface/hashCode.html | 8 ++++---- .../fake/AnotherInterface/operator_equals.html | 2 +- .../fake/BaseForDocComments/hashCode.html | 8 ++++---- .../fake/BaseForDocComments/operator_equals.html | 2 +- testing/test_package_docs/fake/Color/hashCode.html | 8 ++++---- testing/test_package_docs/fake/Color/operator_equals.html | 2 +- .../test_package_docs/fake/ConstantClass/hashCode.html | 8 ++++---- .../fake/ConstantClass/operator_equals.html | 2 +- testing/test_package_docs/fake/Cool/hashCode.html | 8 ++++---- testing/test_package_docs/fake/Cool/operator_equals.html | 2 +- testing/test_package_docs/fake/Doh/hashCode.html | 8 ++++---- testing/test_package_docs/fake/Doh/operator_equals.html | 2 +- testing/test_package_docs/fake/Foo2/hashCode.html | 8 ++++---- testing/test_package_docs/fake/Foo2/operator_equals.html | 2 +- .../fake/HasGenericWithExtends/hashCode.html | 8 ++++---- .../fake/HasGenericWithExtends/operator_equals.html | 2 +- testing/test_package_docs/fake/HasGenerics/hashCode.html | 8 ++++---- .../fake/HasGenerics/operator_equals.html | 2 +- testing/test_package_docs/fake/Interface/hashCode.html | 8 ++++---- .../test_package_docs/fake/Interface/operator_equals.html | 2 +- testing/test_package_docs/fake/MixMeIn/hashCode.html | 8 ++++---- .../test_package_docs/fake/MixMeIn/operator_equals.html | 2 +- testing/test_package_docs/fake/Oops/hashCode.html | 8 ++++---- testing/test_package_docs/fake/Oops/operator_equals.html | 2 +- .../fake/OperatorReferenceClass/hashCode.html | 8 ++++---- .../fake/OperatorReferenceClass/operator_equals.html | 2 +- .../fake/OtherGenericsThing/hashCode.html | 8 ++++---- .../fake/OtherGenericsThing/operator_equals.html | 2 +- testing/test_package_docs/fake/SpecialList/contains.html | 2 +- testing/test_package_docs/fake/SpecialList/hashCode.html | 8 ++++---- .../fake/SpecialList/operator_equals.html | 2 +- .../fake/SuperAwesomeClass/hashCode.html | 8 ++++---- .../fake/SuperAwesomeClass/operator_equals.html | 2 +- .../fake/WithGetterAndSetter/hashCode.html | 8 ++++---- .../fake/WithGetterAndSetter/operator_equals.html | 2 +- .../test_package_imported.main/Whataclass/hashCode.html | 8 ++++---- .../Whataclass/operator_equals.html | 2 +- .../test_package_imported.main/Whataclass2/hashCode.html | 8 ++++---- .../Whataclass2/operator_equals.html | 2 +- 84 files changed, 210 insertions(+), 210 deletions(-) diff --git a/testing/test_package_docs/ex/Animal/hashCode.html b/testing/test_package_docs/ex/Animal/hashCode.html index d5c18e1c7d..01a7383e8a 100644 --- a/testing/test_package_docs/ex/Animal/hashCode.html +++ b/testing/test_package_docs/ex/Animal/hashCode.html @@ -110,12 +110,12 @@
Animal

The hash code for this object.

A hash code is a single integer which represents the state of the object that affects == comparisons.

-

All objects have hash codes. -The default hash code represents only the identity of the object, +

All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

-

If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

+

If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Animal/operator_equals.html b/testing/test_package_docs/ex/Animal/operator_equals.html index 03ef1986a4..73707d6ec0 100644 --- a/testing/test_package_docs/ex/Animal/operator_equals.html +++ b/testing/test_package_docs/ex/Animal/operator_equals.html @@ -116,7 +116,7 @@

Animal
either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Apple/hashCode.html b/testing/test_package_docs/ex/Apple/hashCode.html index c30625c02f..65fbd473b8 100644 --- a/testing/test_package_docs/ex/Apple/hashCode.html +++ b/testing/test_package_docs/ex/Apple/hashCode.html @@ -121,12 +121,12 @@

    Apple

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Apple/operator_equals.html b/testing/test_package_docs/ex/Apple/operator_equals.html index 306885c7a4..0876e37ed3 100644 --- a/testing/test_package_docs/ex/Apple/operator_equals.html +++ b/testing/test_package_docs/ex/Apple/operator_equals.html @@ -127,7 +127,7 @@

    Apple
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Cat/hashCode.html b/testing/test_package_docs/ex/Cat/hashCode.html index d5cd2d826d..e8dd98febd 100644 --- a/testing/test_package_docs/ex/Cat/hashCode.html +++ b/testing/test_package_docs/ex/Cat/hashCode.html @@ -108,12 +108,12 @@

    Cat

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Cat/operator_equals.html b/testing/test_package_docs/ex/Cat/operator_equals.html index a5f5679850..92c823ade6 100644 --- a/testing/test_package_docs/ex/Cat/operator_equals.html +++ b/testing/test_package_docs/ex/Cat/operator_equals.html @@ -114,7 +114,7 @@

    Cat
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/CatString/hashCode.html b/testing/test_package_docs/ex/CatString/hashCode.html index 842d809447..820424adf3 100644 --- a/testing/test_package_docs/ex/CatString/hashCode.html +++ b/testing/test_package_docs/ex/CatString/hashCode.html @@ -114,12 +114,12 @@

    CatString

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/CatString/operator_equals.html b/testing/test_package_docs/ex/CatString/operator_equals.html index 91a7250f6c..05bb9b4221 100644 --- a/testing/test_package_docs/ex/CatString/operator_equals.html +++ b/testing/test_package_docs/ex/CatString/operator_equals.html @@ -120,7 +120,7 @@

    CatString
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/ConstantCat/hashCode.html b/testing/test_package_docs/ex/ConstantCat/hashCode.html index 4a6b3abcb4..4e34f54a4a 100644 --- a/testing/test_package_docs/ex/ConstantCat/hashCode.html +++ b/testing/test_package_docs/ex/ConstantCat/hashCode.html @@ -109,12 +109,12 @@

    ConstantCat

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/ConstantCat/operator_equals.html b/testing/test_package_docs/ex/ConstantCat/operator_equals.html index 8f4103a532..68fef4f80f 100644 --- a/testing/test_package_docs/ex/ConstantCat/operator_equals.html +++ b/testing/test_package_docs/ex/ConstantCat/operator_equals.html @@ -115,7 +115,7 @@

    ConstantCat
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Deprecated/hashCode.html b/testing/test_package_docs/ex/Deprecated/hashCode.html index 2d393e72e8..3c116f094d 100644 --- a/testing/test_package_docs/ex/Deprecated/hashCode.html +++ b/testing/test_package_docs/ex/Deprecated/hashCode.html @@ -107,12 +107,12 @@

    Deprecated

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Deprecated/operator_equals.html b/testing/test_package_docs/ex/Deprecated/operator_equals.html index 0398088484..2ba28d5678 100644 --- a/testing/test_package_docs/ex/Deprecated/operator_equals.html +++ b/testing/test_package_docs/ex/Deprecated/operator_equals.html @@ -113,7 +113,7 @@

    Deprecated
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Dog/hashCode.html b/testing/test_package_docs/ex/Dog/hashCode.html index 2963aaa398..fcae183b52 100644 --- a/testing/test_package_docs/ex/Dog/hashCode.html +++ b/testing/test_package_docs/ex/Dog/hashCode.html @@ -131,12 +131,12 @@

    Dog

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Dog/operator_equals.html b/testing/test_package_docs/ex/Dog/operator_equals.html index c94cedf92a..721ad0678b 100644 --- a/testing/test_package_docs/ex/Dog/operator_equals.html +++ b/testing/test_package_docs/ex/Dog/operator_equals.html @@ -142,7 +142,7 @@

    Dog
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/E/hashCode.html b/testing/test_package_docs/ex/E/hashCode.html index 9f13ff1d81..7830b694cf 100644 --- a/testing/test_package_docs/ex/E/hashCode.html +++ b/testing/test_package_docs/ex/E/hashCode.html @@ -106,12 +106,12 @@

    E

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/E/operator_equals.html b/testing/test_package_docs/ex/E/operator_equals.html index 7f18a9f51c..2906598cc3 100644 --- a/testing/test_package_docs/ex/E/operator_equals.html +++ b/testing/test_package_docs/ex/E/operator_equals.html @@ -112,7 +112,7 @@

    E
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/ForAnnotation/hashCode.html b/testing/test_package_docs/ex/ForAnnotation/hashCode.html index 835387950a..52aba875c2 100644 --- a/testing/test_package_docs/ex/ForAnnotation/hashCode.html +++ b/testing/test_package_docs/ex/ForAnnotation/hashCode.html @@ -107,12 +107,12 @@

    ForAnnotation

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/ForAnnotation/operator_equals.html b/testing/test_package_docs/ex/ForAnnotation/operator_equals.html index ba5bd8d4f2..7904e47f03 100644 --- a/testing/test_package_docs/ex/ForAnnotation/operator_equals.html +++ b/testing/test_package_docs/ex/ForAnnotation/operator_equals.html @@ -113,7 +113,7 @@

    ForAnnotation
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/HasAnnotation/hashCode.html b/testing/test_package_docs/ex/HasAnnotation/hashCode.html index a231dd1cba..c711b3d424 100644 --- a/testing/test_package_docs/ex/HasAnnotation/hashCode.html +++ b/testing/test_package_docs/ex/HasAnnotation/hashCode.html @@ -106,12 +106,12 @@

    HasAnnotation

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/HasAnnotation/operator_equals.html b/testing/test_package_docs/ex/HasAnnotation/operator_equals.html index 6e87e683ae..2b56344e51 100644 --- a/testing/test_package_docs/ex/HasAnnotation/operator_equals.html +++ b/testing/test_package_docs/ex/HasAnnotation/operator_equals.html @@ -112,7 +112,7 @@

    HasAnnotation
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Helper/hashCode.html b/testing/test_package_docs/ex/Helper/hashCode.html index 2abda39210..5d3f3f10da 100644 --- a/testing/test_package_docs/ex/Helper/hashCode.html +++ b/testing/test_package_docs/ex/Helper/hashCode.html @@ -107,12 +107,12 @@

    Helper

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Helper/operator_equals.html b/testing/test_package_docs/ex/Helper/operator_equals.html index 96158da057..1c5c87659c 100644 --- a/testing/test_package_docs/ex/Helper/operator_equals.html +++ b/testing/test_package_docs/ex/Helper/operator_equals.html @@ -113,7 +113,7 @@

    Helper
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/Klass/hashCode.html b/testing/test_package_docs/ex/Klass/hashCode.html index 6bc06e31f5..7183b9df02 100644 --- a/testing/test_package_docs/ex/Klass/hashCode.html +++ b/testing/test_package_docs/ex/Klass/hashCode.html @@ -111,12 +111,12 @@

    Klass

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/Klass/operator_equals.html b/testing/test_package_docs/ex/Klass/operator_equals.html index 57cf125c6d..ed5ca14dee 100644 --- a/testing/test_package_docs/ex/Klass/operator_equals.html +++ b/testing/test_package_docs/ex/Klass/operator_equals.html @@ -117,7 +117,7 @@

    Klass
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/MyError/hashCode.html b/testing/test_package_docs/ex/MyError/hashCode.html index af1ab6d8e3..ec1d9d2d9b 100644 --- a/testing/test_package_docs/ex/MyError/hashCode.html +++ b/testing/test_package_docs/ex/MyError/hashCode.html @@ -107,12 +107,12 @@

    MyError

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/MyError/operator_equals.html b/testing/test_package_docs/ex/MyError/operator_equals.html index 71a8acf70e..b225eb6940 100644 --- a/testing/test_package_docs/ex/MyError/operator_equals.html +++ b/testing/test_package_docs/ex/MyError/operator_equals.html @@ -113,7 +113,7 @@

    MyError
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/MyErrorImplements/hashCode.html b/testing/test_package_docs/ex/MyErrorImplements/hashCode.html index f91f5fe1d7..2512c0642b 100644 --- a/testing/test_package_docs/ex/MyErrorImplements/hashCode.html +++ b/testing/test_package_docs/ex/MyErrorImplements/hashCode.html @@ -107,12 +107,12 @@

    MyErrorImplements

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/MyErrorImplements/operator_equals.html b/testing/test_package_docs/ex/MyErrorImplements/operator_equals.html index 7b2d61d4bd..bafe071e36 100644 --- a/testing/test_package_docs/ex/MyErrorImplements/operator_equals.html +++ b/testing/test_package_docs/ex/MyErrorImplements/operator_equals.html @@ -113,7 +113,7 @@

    MyErrorImplements
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/MyException/hashCode.html b/testing/test_package_docs/ex/MyException/hashCode.html index 94cf102186..cc9f542ab9 100644 --- a/testing/test_package_docs/ex/MyException/hashCode.html +++ b/testing/test_package_docs/ex/MyException/hashCode.html @@ -106,12 +106,12 @@

    MyException

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/MyException/operator_equals.html b/testing/test_package_docs/ex/MyException/operator_equals.html index 74c4d6088d..af1216230d 100644 --- a/testing/test_package_docs/ex/MyException/operator_equals.html +++ b/testing/test_package_docs/ex/MyException/operator_equals.html @@ -112,7 +112,7 @@

    MyException
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/MyExceptionImplements/hashCode.html b/testing/test_package_docs/ex/MyExceptionImplements/hashCode.html index df4606aff1..9dcf8dc4e1 100644 --- a/testing/test_package_docs/ex/MyExceptionImplements/hashCode.html +++ b/testing/test_package_docs/ex/MyExceptionImplements/hashCode.html @@ -106,12 +106,12 @@

    MyExceptionImplements

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/MyExceptionImplements/operator_equals.html b/testing/test_package_docs/ex/MyExceptionImplements/operator_equals.html index 4615901bdb..076faa4f31 100644 --- a/testing/test_package_docs/ex/MyExceptionImplements/operator_equals.html +++ b/testing/test_package_docs/ex/MyExceptionImplements/operator_equals.html @@ -112,7 +112,7 @@

    MyExceptionImplements
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/hashCode.html b/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/hashCode.html index b74a5e2629..488af7d7eb 100644 --- a/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/hashCode.html +++ b/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/hashCode.html @@ -107,12 +107,12 @@

    PublicClassExtendsPri

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/operator_equals.html b/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/operator_equals.html index 0c423bdf0d..a1f35950f8 100644 --- a/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/operator_equals.html +++ b/testing/test_package_docs/ex/PublicClassExtendsPrivateClass/operator_equals.html @@ -113,7 +113,7 @@

    PublicClassExtendsPri either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/hashCode.html b/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/hashCode.html index 42bcd3519d..28876f4138 100644 --- a/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/hashCode.html +++ b/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/hashCode.html @@ -107,12 +107,12 @@

    PublicClassImp

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/operator_equals.html b/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/operator_equals.html index 3031490b27..09a75a047f 100644 --- a/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/operator_equals.html +++ b/testing/test_package_docs/ex/PublicClassImplementsPrivateInterface/operator_equals.html @@ -113,7 +113,7 @@

    PublicClassImp either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/ShapeType/hashCode.html b/testing/test_package_docs/ex/ShapeType/hashCode.html index 329e5e4ccf..7be697f8c0 100644 --- a/testing/test_package_docs/ex/ShapeType/hashCode.html +++ b/testing/test_package_docs/ex/ShapeType/hashCode.html @@ -108,12 +108,12 @@

    ShapeType

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/ShapeType/operator_equals.html b/testing/test_package_docs/ex/ShapeType/operator_equals.html index 34b93923e3..6ecb649626 100644 --- a/testing/test_package_docs/ex/ShapeType/operator_equals.html +++ b/testing/test_package_docs/ex/ShapeType/operator_equals.html @@ -114,7 +114,7 @@

    ShapeType
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/SpecializedDuration/hashCode.html b/testing/test_package_docs/ex/SpecializedDuration/hashCode.html index 7569eebd4f..8fd78415ca 100644 --- a/testing/test_package_docs/ex/SpecializedDuration/hashCode.html +++ b/testing/test_package_docs/ex/SpecializedDuration/hashCode.html @@ -124,12 +124,12 @@

    SpecializedDuration

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/WithGeneric/hashCode.html b/testing/test_package_docs/ex/WithGeneric/hashCode.html index 6d74340fbb..e867de5dbd 100644 --- a/testing/test_package_docs/ex/WithGeneric/hashCode.html +++ b/testing/test_package_docs/ex/WithGeneric/hashCode.html @@ -107,12 +107,12 @@

    WithGeneric

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/WithGeneric/operator_equals.html b/testing/test_package_docs/ex/WithGeneric/operator_equals.html index 4b5e566c20..e3103d8bf2 100644 --- a/testing/test_package_docs/ex/WithGeneric/operator_equals.html +++ b/testing/test_package_docs/ex/WithGeneric/operator_equals.html @@ -113,7 +113,7 @@

    WithGeneric
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/ex/aThingToDo/hashCode.html b/testing/test_package_docs/ex/aThingToDo/hashCode.html index d6308badbb..69f6f0f7d7 100644 --- a/testing/test_package_docs/ex/aThingToDo/hashCode.html +++ b/testing/test_package_docs/ex/aThingToDo/hashCode.html @@ -108,12 +108,12 @@

    aThingToDo

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/ex/aThingToDo/operator_equals.html b/testing/test_package_docs/ex/aThingToDo/operator_equals.html index 0f1e1caf02..faeb52bc1e 100644 --- a/testing/test_package_docs/ex/aThingToDo/operator_equals.html +++ b/testing/test_package_docs/ex/aThingToDo/operator_equals.html @@ -114,7 +114,7 @@

    aThingToDo
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Annotation/hashCode.html b/testing/test_package_docs/fake/Annotation/hashCode.html index 1806621bf3..07a3f14239 100644 --- a/testing/test_package_docs/fake/Annotation/hashCode.html +++ b/testing/test_package_docs/fake/Annotation/hashCode.html @@ -107,12 +107,12 @@

    Annotation

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Annotation/operator_equals.html b/testing/test_package_docs/fake/Annotation/operator_equals.html index a4b54588cb..99d25aeefd 100644 --- a/testing/test_package_docs/fake/Annotation/operator_equals.html +++ b/testing/test_package_docs/fake/Annotation/operator_equals.html @@ -113,7 +113,7 @@

    Annotation
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/AnotherInterface/hashCode.html b/testing/test_package_docs/fake/AnotherInterface/hashCode.html index 48c2785f18..c330a55865 100644 --- a/testing/test_package_docs/fake/AnotherInterface/hashCode.html +++ b/testing/test_package_docs/fake/AnotherInterface/hashCode.html @@ -106,12 +106,12 @@

    AnotherInterface

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/AnotherInterface/operator_equals.html b/testing/test_package_docs/fake/AnotherInterface/operator_equals.html index 5f5e167613..0be4a23da6 100644 --- a/testing/test_package_docs/fake/AnotherInterface/operator_equals.html +++ b/testing/test_package_docs/fake/AnotherInterface/operator_equals.html @@ -112,7 +112,7 @@

    AnotherInterface
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/BaseForDocComments/hashCode.html b/testing/test_package_docs/fake/BaseForDocComments/hashCode.html index a12e2b2273..a6dc06e724 100644 --- a/testing/test_package_docs/fake/BaseForDocComments/hashCode.html +++ b/testing/test_package_docs/fake/BaseForDocComments/hashCode.html @@ -108,12 +108,12 @@

    BaseForDocComments

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/BaseForDocComments/operator_equals.html b/testing/test_package_docs/fake/BaseForDocComments/operator_equals.html index 89b435345c..5a5774430a 100644 --- a/testing/test_package_docs/fake/BaseForDocComments/operator_equals.html +++ b/testing/test_package_docs/fake/BaseForDocComments/operator_equals.html @@ -114,7 +114,7 @@

    BaseForDocComments
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Color/hashCode.html b/testing/test_package_docs/fake/Color/hashCode.html index b424393d1a..0bda804355 100644 --- a/testing/test_package_docs/fake/Color/hashCode.html +++ b/testing/test_package_docs/fake/Color/hashCode.html @@ -114,12 +114,12 @@

    Color

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Color/operator_equals.html b/testing/test_package_docs/fake/Color/operator_equals.html index af37750aa7..66fd402bf5 100644 --- a/testing/test_package_docs/fake/Color/operator_equals.html +++ b/testing/test_package_docs/fake/Color/operator_equals.html @@ -120,7 +120,7 @@

    Color
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/ConstantClass/hashCode.html b/testing/test_package_docs/fake/ConstantClass/hashCode.html index e443b5ddb6..d49d3ec4da 100644 --- a/testing/test_package_docs/fake/ConstantClass/hashCode.html +++ b/testing/test_package_docs/fake/ConstantClass/hashCode.html @@ -109,12 +109,12 @@

    ConstantClass

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/ConstantClass/operator_equals.html b/testing/test_package_docs/fake/ConstantClass/operator_equals.html index 7afb37377f..dabf509f65 100644 --- a/testing/test_package_docs/fake/ConstantClass/operator_equals.html +++ b/testing/test_package_docs/fake/ConstantClass/operator_equals.html @@ -115,7 +115,7 @@

    ConstantClass
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Cool/hashCode.html b/testing/test_package_docs/fake/Cool/hashCode.html index 83b5b27fb2..0de602ac67 100644 --- a/testing/test_package_docs/fake/Cool/hashCode.html +++ b/testing/test_package_docs/fake/Cool/hashCode.html @@ -107,12 +107,12 @@

    Cool

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Cool/operator_equals.html b/testing/test_package_docs/fake/Cool/operator_equals.html index 30cd768a6e..d868c1636f 100644 --- a/testing/test_package_docs/fake/Cool/operator_equals.html +++ b/testing/test_package_docs/fake/Cool/operator_equals.html @@ -113,7 +113,7 @@

    Cool
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Doh/hashCode.html b/testing/test_package_docs/fake/Doh/hashCode.html index b92cc8244b..b8c976bb00 100644 --- a/testing/test_package_docs/fake/Doh/hashCode.html +++ b/testing/test_package_docs/fake/Doh/hashCode.html @@ -107,12 +107,12 @@

    Doh

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Doh/operator_equals.html b/testing/test_package_docs/fake/Doh/operator_equals.html index 37e6164651..1e530a7feb 100644 --- a/testing/test_package_docs/fake/Doh/operator_equals.html +++ b/testing/test_package_docs/fake/Doh/operator_equals.html @@ -113,7 +113,7 @@

    Doh
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Foo2/hashCode.html b/testing/test_package_docs/fake/Foo2/hashCode.html index b3d86e49ef..73622be4bd 100644 --- a/testing/test_package_docs/fake/Foo2/hashCode.html +++ b/testing/test_package_docs/fake/Foo2/hashCode.html @@ -110,12 +110,12 @@

    Foo2

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Foo2/operator_equals.html b/testing/test_package_docs/fake/Foo2/operator_equals.html index 2aaeb6975b..ca93fc4e93 100644 --- a/testing/test_package_docs/fake/Foo2/operator_equals.html +++ b/testing/test_package_docs/fake/Foo2/operator_equals.html @@ -116,7 +116,7 @@

    Foo2
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/HasGenericWithExtends/hashCode.html b/testing/test_package_docs/fake/HasGenericWithExtends/hashCode.html index 44adfcb2bd..841877474d 100644 --- a/testing/test_package_docs/fake/HasGenericWithExtends/hashCode.html +++ b/testing/test_package_docs/fake/HasGenericWithExtends/hashCode.html @@ -106,12 +106,12 @@

    HasGenericWithExtendsThe hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/HasGenericWithExtends/operator_equals.html b/testing/test_package_docs/fake/HasGenericWithExtends/operator_equals.html index 279a7fefea..cbf50282c9 100644 --- a/testing/test_package_docs/fake/HasGenericWithExtends/operator_equals.html +++ b/testing/test_package_docs/fake/HasGenericWithExtends/operator_equals.html @@ -112,7 +112,7 @@

    HasGenericWithExtends
  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/HasGenerics/hashCode.html b/testing/test_package_docs/fake/HasGenerics/hashCode.html index b4ea3cd76d..95e72d7632 100644 --- a/testing/test_package_docs/fake/HasGenerics/hashCode.html +++ b/testing/test_package_docs/fake/HasGenerics/hashCode.html @@ -110,12 +110,12 @@

    HasGenerics

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/HasGenerics/operator_equals.html b/testing/test_package_docs/fake/HasGenerics/operator_equals.html index 4213c97e67..dedc508f66 100644 --- a/testing/test_package_docs/fake/HasGenerics/operator_equals.html +++ b/testing/test_package_docs/fake/HasGenerics/operator_equals.html @@ -116,7 +116,7 @@

    HasGenerics
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Interface/hashCode.html b/testing/test_package_docs/fake/Interface/hashCode.html index 8e62d1a8dd..2b33b03215 100644 --- a/testing/test_package_docs/fake/Interface/hashCode.html +++ b/testing/test_package_docs/fake/Interface/hashCode.html @@ -106,12 +106,12 @@

    Interface

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Interface/operator_equals.html b/testing/test_package_docs/fake/Interface/operator_equals.html index 4879cf49e3..7f30bb524a 100644 --- a/testing/test_package_docs/fake/Interface/operator_equals.html +++ b/testing/test_package_docs/fake/Interface/operator_equals.html @@ -112,7 +112,7 @@

    Interface
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/MixMeIn/hashCode.html b/testing/test_package_docs/fake/MixMeIn/hashCode.html index 4783f47420..a9fc3cf529 100644 --- a/testing/test_package_docs/fake/MixMeIn/hashCode.html +++ b/testing/test_package_docs/fake/MixMeIn/hashCode.html @@ -106,12 +106,12 @@

    MixMeIn

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/MixMeIn/operator_equals.html b/testing/test_package_docs/fake/MixMeIn/operator_equals.html index c83eebb85a..182d3f86d6 100644 --- a/testing/test_package_docs/fake/MixMeIn/operator_equals.html +++ b/testing/test_package_docs/fake/MixMeIn/operator_equals.html @@ -112,7 +112,7 @@

    MixMeIn
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/Oops/hashCode.html b/testing/test_package_docs/fake/Oops/hashCode.html index 83e358e7b0..d5b137cec2 100644 --- a/testing/test_package_docs/fake/Oops/hashCode.html +++ b/testing/test_package_docs/fake/Oops/hashCode.html @@ -107,12 +107,12 @@

    Oops

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/Oops/operator_equals.html b/testing/test_package_docs/fake/Oops/operator_equals.html index 995f457827..16d8174e85 100644 --- a/testing/test_package_docs/fake/Oops/operator_equals.html +++ b/testing/test_package_docs/fake/Oops/operator_equals.html @@ -113,7 +113,7 @@

    Oops
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/OperatorReferenceClass/hashCode.html b/testing/test_package_docs/fake/OperatorReferenceClass/hashCode.html index 36f7898467..190a350509 100644 --- a/testing/test_package_docs/fake/OperatorReferenceClass/hashCode.html +++ b/testing/test_package_docs/fake/OperatorReferenceClass/hashCode.html @@ -106,12 +106,12 @@

    OperatorReferenceClass<

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/OperatorReferenceClass/operator_equals.html b/testing/test_package_docs/fake/OperatorReferenceClass/operator_equals.html index a1d337e8ae..01a09e2290 100644 --- a/testing/test_package_docs/fake/OperatorReferenceClass/operator_equals.html +++ b/testing/test_package_docs/fake/OperatorReferenceClass/operator_equals.html @@ -117,7 +117,7 @@

    OperatorReferenceClass< either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/OtherGenericsThing/hashCode.html b/testing/test_package_docs/fake/OtherGenericsThing/hashCode.html index 402046c294..ced19403b1 100644 --- a/testing/test_package_docs/fake/OtherGenericsThing/hashCode.html +++ b/testing/test_package_docs/fake/OtherGenericsThing/hashCode.html @@ -107,12 +107,12 @@

    OtherGenericsThing

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/OtherGenericsThing/operator_equals.html b/testing/test_package_docs/fake/OtherGenericsThing/operator_equals.html index 9d3b4f731b..ac1672c33b 100644 --- a/testing/test_package_docs/fake/OtherGenericsThing/operator_equals.html +++ b/testing/test_package_docs/fake/OtherGenericsThing/operator_equals.html @@ -113,7 +113,7 @@

    OtherGenericsThing
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/SpecialList/contains.html b/testing/test_package_docs/fake/SpecialList/contains.html index 97f01065b6..4503a938a3 100644 --- a/testing/test_package_docs/fake/SpecialList/contains.html +++ b/testing/test_package_docs/fake/SpecialList/contains.html @@ -159,7 +159,7 @@

    SpecialList
    the iterable defaults to the Object.== of the element.

    Some types of iterable may have a different equality used for its elements. For example, a Set may have a custom equality -(see Set.identical) that its contains uses. +(see Set.identity) that its contains uses. Likewise the Iterable returned by a Map.keys call should use the same equality that the Map uses for keys.

    diff --git a/testing/test_package_docs/fake/SpecialList/hashCode.html b/testing/test_package_docs/fake/SpecialList/hashCode.html index 23578081d8..3d9ffab70b 100644 --- a/testing/test_package_docs/fake/SpecialList/hashCode.html +++ b/testing/test_package_docs/fake/SpecialList/hashCode.html @@ -158,12 +158,12 @@
    SpecialList

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/SpecialList/operator_equals.html b/testing/test_package_docs/fake/SpecialList/operator_equals.html index 8eafef8060..d0afe1f01c 100644 --- a/testing/test_package_docs/fake/SpecialList/operator_equals.html +++ b/testing/test_package_docs/fake/SpecialList/operator_equals.html @@ -164,7 +164,7 @@

    SpecialList
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/SuperAwesomeClass/hashCode.html b/testing/test_package_docs/fake/SuperAwesomeClass/hashCode.html index fa91a26cfa..15cbf7b5bf 100644 --- a/testing/test_package_docs/fake/SuperAwesomeClass/hashCode.html +++ b/testing/test_package_docs/fake/SuperAwesomeClass/hashCode.html @@ -109,12 +109,12 @@

    SuperAwesomeClass

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/SuperAwesomeClass/operator_equals.html b/testing/test_package_docs/fake/SuperAwesomeClass/operator_equals.html index 6b73e7c63d..3465381b81 100644 --- a/testing/test_package_docs/fake/SuperAwesomeClass/operator_equals.html +++ b/testing/test_package_docs/fake/SuperAwesomeClass/operator_equals.html @@ -115,7 +115,7 @@

    SuperAwesomeClass
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/fake/WithGetterAndSetter/hashCode.html b/testing/test_package_docs/fake/WithGetterAndSetter/hashCode.html index 80b405d04d..9070fa6fd3 100644 --- a/testing/test_package_docs/fake/WithGetterAndSetter/hashCode.html +++ b/testing/test_package_docs/fake/WithGetterAndSetter/hashCode.html @@ -107,12 +107,12 @@

    WithGetterAndSetter

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/fake/WithGetterAndSetter/operator_equals.html b/testing/test_package_docs/fake/WithGetterAndSetter/operator_equals.html index dc53a7d481..ce60d29ea7 100644 --- a/testing/test_package_docs/fake/WithGetterAndSetter/operator_equals.html +++ b/testing/test_package_docs/fake/WithGetterAndSetter/operator_equals.html @@ -113,7 +113,7 @@

    WithGetterAndSetter
    either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass/hashCode.html b/testing/test_package_docs/test_package_imported.main/Whataclass/hashCode.html index d2a3b8f0c3..e2b614e22e 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass/hashCode.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass/hashCode.html @@ -106,12 +106,12 @@

    WhataclassThe hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html b/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html index ce5893ca74..6670115618 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass/operator_equals.html @@ -112,7 +112,7 @@

    Whataclass
  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass2/hashCode.html b/testing/test_package_docs/test_package_imported.main/Whataclass2/hashCode.html index 06cd89b236..223e3c7588 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass2/hashCode.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass2/hashCode.html @@ -106,12 +106,12 @@

    Whataclass2<

    The hash code for this object.

    A hash code is a single integer which represents the state of the object that affects == comparisons.

    -

    All objects have hash codes. -The default hash code represents only the identity of the object, +

    All objects have hash codes. +The default hash code represents only the identity of the object, the same way as the default == implementation only considers objects equal if they are identical (see identityHashCode).

    -

    If == is overridden to use the object state instead, -the hash code must also be changed to represent that state.

    +

    If == is overridden to use the object state instead, +the hash code must also be changed to represent that state.

    Hash codes must be the same for objects that are equal to each other according to ==. The hash code of an object should only change if the object changes diff --git a/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html b/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html index 50669add2c..e7acb8e7bf 100644 --- a/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html +++ b/testing/test_package_docs/test_package_imported.main/Whataclass2/operator_equals.html @@ -112,7 +112,7 @@

    Whataclass2< either both be true, or both be false.

  • Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

  • -

    The method should also be consistent over time, +

    The method should also be consistent over time, so whether two objects are equal should only change if at least one of the objects was modified.

    If a subclass overrides the equality operator it should override