Add support for extension methods (#2012)

* Add suport for extensions

* Add options file to test package

* remove path dependencies

* Address review comments
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4d4a40c..41f08e0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.28.6-dev
+* Support for 0.38.2 version of `package:analyzer`.
+* Support generating docs for extension methods (#2001).
+
 ## 0.28.5
 * Support the latest version of `package:analyzer`.
 * Fix hyperlinks to overriden methods (#1994).
diff --git a/analysis_options.yaml b/analysis_options.yaml
index 5bbf4f3..9ab324b 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -1,6 +1,9 @@
 include: package:pedantic/analysis_options.1.8.0.yaml
 
 analyzer:
+  # enable for analysis on test package sources.
+  enable-experiment:
+    - extension-methods
   exclude:
     - 'doc/**'
     - 'lib/src/third_party/pkg/**'
diff --git a/dartdoc_options.yaml b/dartdoc_options.yaml
index 289fcaf..7a9b637 100644
--- a/dartdoc_options.yaml
+++ b/dartdoc_options.yaml
@@ -1,4 +1,4 @@
 dartdoc:
   linkToSource:
     root: '.'
-    uriTemplate: 'https://github.com/dart-lang/dartdoc/blob/v0.28.5/%f%#L%l%'
+    uriTemplate: 'https://github.com/dart-lang/dartdoc/blob/v0.28.6-dev/%f%#L%l%'
diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart
index 0d2598c..e03e188 100644
--- a/lib/src/element_type.dart
+++ b/lib/src/element_type.dart
@@ -191,7 +191,7 @@
   /// would ordinarily do.
   @override
   bool get isPublic {
-    Class canonicalClass =
+    Container canonicalClass =
         element.packageGraph.findCanonicalModelElementFor(element.element) ??
             element;
     return canonicalClass.isPublic;
diff --git a/lib/src/html/html_generator_instance.dart b/lib/src/html/html_generator_instance.dart
index e542532..00a8cbe 100644
--- a/lib/src/html/html_generator_instance.dart
+++ b/lib/src/html/html_generator_instance.dart
@@ -171,6 +171,37 @@
           }
         }
 
+        for (var extension in filterNonPublic(lib.extensions)) {
+          generateExtension(_packageGraph, lib, extension);
+
+          for (var constant in filterNonDocumented(extension.constants)) {
+            generateConstant(_packageGraph, lib, extension, constant);
+          }
+
+          for (var property
+              in filterNonDocumented(extension.staticProperties)) {
+            generateProperty(_packageGraph, lib, extension, property);
+          }
+
+          for (var method
+              in filterNonDocumented(extension.allPublicInstanceMethods)) {
+            generateMethod(_packageGraph, lib, extension, method);
+          }
+
+          for (var method in filterNonDocumented(extension.staticMethods)) {
+            generateMethod(_packageGraph, lib, extension, method);
+          }
+
+          for (var operator in filterNonDocumented(extension.allOperators)) {
+            generateMethod(_packageGraph, lib, extension, operator);
+          }
+
+          for (var property
+              in filterNonDocumented(extension.allInstanceFields)) {
+            generateProperty(_packageGraph, lib, extension, property);
+          }
+        }
+
         for (var mixin in filterNonDocumented(lib.mixins)) {
           generateMixins(_packageGraph, lib, mixin);
           for (var constructor in filterNonDocumented(mixin.constructors)) {
@@ -275,6 +306,13 @@
     _build(path.joinAll(clazz.href.split('/')), _templates.classTemplate, data);
   }
 
+  void generateExtension(
+      PackageGraph packageGraph, Library lib, Extension ext) {
+    TemplateData data = ExtensionTemplateData(_options, packageGraph, lib, ext);
+    _build(
+        path.joinAll(ext.href.split('/')), _templates.extensionTemplate, data);
+  }
+
   void generateMixins(PackageGraph packageGraph, Library lib, Mixin mixin) {
     TemplateData data = MixinTemplateData(_options, packageGraph, lib, mixin);
     _build(path.joinAll(mixin.href.split('/')), _templates.mixinTemplate, data);
@@ -305,7 +343,7 @@
   }
 
   void generateMethod(
-      PackageGraph packageGraph, Library lib, Class clazz, Method method) {
+      PackageGraph packageGraph, Library lib, Container clazz, Method method) {
     TemplateData data =
         MethodTemplateData(_options, packageGraph, lib, clazz, method);
 
@@ -314,7 +352,7 @@
   }
 
   void generateConstant(
-      PackageGraph packageGraph, Library lib, Class clazz, Field property) {
+      PackageGraph packageGraph, Library lib, Container clazz, Field property) {
     TemplateData data =
         ConstantTemplateData(_options, packageGraph, lib, clazz, property);
 
@@ -323,7 +361,7 @@
   }
 
   void generateProperty(
-      PackageGraph packageGraph, Library lib, Class clazz, Field property) {
+      PackageGraph packageGraph, Library lib, Container clazz, Field property) {
     TemplateData data =
         PropertyTemplateData(_options, packageGraph, lib, clazz, property);
 
diff --git a/lib/src/html/template_data.dart b/lib/src/html/template_data.dart
index fe1b058..271ce70 100644
--- a/lib/src/html/template_data.dart
+++ b/lib/src/html/template_data.dart
@@ -185,6 +185,34 @@
   }
 }
 
+/// Base template data class for [Extension].
+class ExtensionTemplateData<T extends Extension> extends TemplateData<T> {
+  final T extension;
+  final Library library;
+
+  ExtensionTemplateData(HtmlOptions htmlOptions, PackageGraph packageGraph,
+      this.library, this.extension)
+      : super(htmlOptions, packageGraph);
+
+  @override
+  T get self => extension;
+
+  @override
+  String get title =>
+      '${extension.name} ${extension.kind} - ${library.name} library - Dart API';
+  @override
+  String get metaDescription =>
+      'API docs for the ${extension.name} ${extension.kind} from the '
+      '${library.name} library, for the Dart programming language.';
+
+  @override
+  String get layoutTitle => _layoutTitle(extension.name, extension.kind, false);
+  @override
+  List get navLinks => [packageGraph.defaultPackage, library];
+  @override
+  String get htmlBase => '..';
+}
+
 class ConstructorTemplateData extends TemplateData<Constructor> {
   final Library library;
   final Class clazz;
@@ -255,23 +283,26 @@
 class MethodTemplateData extends TemplateData<Method> {
   final Library library;
   final Method method;
-  final Class clazz;
+  final Container clazz;
+  String container;
 
   MethodTemplateData(HtmlOptions htmlOptions, PackageGraph packageGraph,
       this.library, this.clazz, this.method)
-      : super(htmlOptions, packageGraph);
+      : super(htmlOptions, packageGraph) {
+    container = clazz.isClass ? 'class' : 'extension';
+  }
 
   @override
   Method get self => method;
   @override
-  String get title => '${method.name} method - ${clazz.name} class - '
+  String get title => '${method.name} method - ${clazz.name} ${container} - '
       '${library.name} library - Dart API';
   @override
   String get layoutTitle => _layoutTitle(
       method.nameWithGenerics, method.fullkind, method.isDeprecated);
   @override
   String get metaDescription =>
-      'API docs for the ${method.name} method from the ${clazz.name} class, '
+      'API docs for the ${method.name} method from the ${clazz.name} ${container}, '
       'for the Dart programming language.';
   @override
   List get navLinks => [packageGraph.defaultPackage, library];
@@ -283,25 +314,28 @@
 
 class PropertyTemplateData extends TemplateData<Field> {
   final Library library;
-  final Class clazz;
+  final Container clazz;
   final Field property;
+  String container;
 
   PropertyTemplateData(HtmlOptions htmlOptions, PackageGraph packageGraph,
       this.library, this.clazz, this.property)
-      : super(htmlOptions, packageGraph);
+      : super(htmlOptions, packageGraph) {
+    container = clazz.isClass ? 'class' : 'extension';
+  }
 
   @override
   Field get self => property;
 
   @override
-  String get title => '${property.name} $type - ${clazz.name} class - '
+  String get title => '${property.name} $type - ${clazz.name} ${container} - '
       '${library.name} library - Dart API';
   @override
   String get layoutTitle =>
       _layoutTitle(property.name, type, property.isDeprecated);
   @override
   String get metaDescription =>
-      'API docs for the ${property.name} $type from the ${clazz.name} class, '
+      'API docs for the ${property.name} $type from the ${clazz.name} ${container}, '
       'for the Dart programming language.';
   @override
   List get navLinks => [packageGraph.defaultPackage, library];
@@ -315,7 +349,7 @@
 
 class ConstantTemplateData extends PropertyTemplateData {
   ConstantTemplateData(HtmlOptions htmlOptions, PackageGraph packageGraph,
-      Library library, Class clazz, Field property)
+      Library library, Container clazz, Field property)
       : super(htmlOptions, packageGraph, library, clazz, property);
 
   @override
diff --git a/lib/src/html/templates.dart b/lib/src/html/templates.dart
index ca8e31b..aa78da0 100644
--- a/lib/src/html/templates.dart
+++ b/lib/src/html/templates.dart
@@ -18,6 +18,7 @@
   'categorization',
   'class',
   'constant',
+  'extension',
   'footer',
   'head',
   'library',
@@ -31,6 +32,7 @@
   'sidebar_for_class',
   'sidebar_for_category',
   'sidebar_for_enum',
+  'sidebar_for_extension',
   'source_code',
   'source_link',
   'sidebar_for_library',
@@ -45,6 +47,7 @@
   'constant.html',
   'constructor.html',
   'enum.html',
+  'extension.html',
   'function.html',
   'index.html',
   'library.html',
@@ -65,7 +68,6 @@
     List<String> headerPaths,
     List<String> footerPaths,
     List<String> footerTextPaths) async {
-
   headerPaths ??= [];
   footerPaths ??= [];
   footerTextPaths ??= [];
@@ -75,8 +77,8 @@
   void replacePlaceholder(String key, String placeholder, List<String> paths) {
     var template = partials[key];
     if (template != null && paths != null && paths.isNotEmpty) {
-      String replacement = paths.map((p) => File(p).readAsStringSync())
-          .join('\n');
+      String replacement =
+          paths.map((p) => File(p).readAsStringSync()).join('\n');
       template = template.replaceAll(placeholder, replacement);
       partials[key] = template;
     }
@@ -140,6 +142,7 @@
 class Templates {
   final Template categoryTemplate;
   final Template classTemplate;
+  final Template extensionTemplate;
   final Template enumTemplate;
   final Template constantTemplate;
   final Template constructorTemplate;
@@ -164,8 +167,7 @@
         footerTextPaths: footerTextPaths);
   }
 
-  static Future<Templates> fromDirectory(
-      Directory dir,
+  static Future<Templates> fromDirectory(Directory dir,
       {List<String> headerPaths,
       List<String> footerPaths,
       List<String> footerTextPaths}) async {
@@ -185,13 +187,12 @@
     }
   }
 
-  static Future<Templates> _create(
-      _TemplatesLoader templatesLoader,
+  static Future<Templates> _create(_TemplatesLoader templatesLoader,
       {List<String> headerPaths,
       List<String> footerPaths,
       List<String> footerTextPaths}) async {
-    var partials =
-        await _loadPartials(templatesLoader, headerPaths, footerPaths, footerTextPaths);
+    var partials = await _loadPartials(
+        templatesLoader, headerPaths, footerPaths, footerTextPaths);
 
     Template _partial(String name) {
       String partial = partials[name];
@@ -202,7 +203,8 @@
     }
 
     Future<Template> _loadTemplate(String templatePath) async {
-      String templateContents = await templatesLoader.loadTemplate(templatePath);
+      String templateContents =
+          await templatesLoader.loadTemplate(templatePath);
       return Template(templateContents, partialResolver: _partial);
     }
 
@@ -210,6 +212,7 @@
     var libraryTemplate = await _loadTemplate('library.html');
     var categoryTemplate = await _loadTemplate('category.html');
     var classTemplate = await _loadTemplate('class.html');
+    var extensionTemplate = await _loadTemplate('extension.html');
     var enumTemplate = await _loadTemplate('enum.html');
     var functionTemplate = await _loadTemplate('function.html');
     var methodTemplate = await _loadTemplate('method.html');
@@ -229,6 +232,7 @@
         categoryTemplate,
         libraryTemplate,
         classTemplate,
+        extensionTemplate,
         enumTemplate,
         functionTemplate,
         methodTemplate,
@@ -247,6 +251,7 @@
       this.categoryTemplate,
       this.libraryTemplate,
       this.classTemplate,
+      this.extensionTemplate,
       this.enumTemplate,
       this.functionTemplate,
       this.methodTemplate,
diff --git a/lib/src/markdown_processor.dart b/lib/src/markdown_processor.dart
index aac01e2..486334b 100644
--- a/lib/src/markdown_processor.dart
+++ b/lib/src/markdown_processor.dart
@@ -183,7 +183,7 @@
 // Calculate a class hint for findCanonicalModelElementFor.
 ModelElement _getPreferredClass(ModelElement modelElement) {
   if (modelElement is EnclosedElement &&
-      (modelElement as EnclosedElement).enclosingElement is Class) {
+      (modelElement as EnclosedElement).enclosingElement is Container) {
     return (modelElement as EnclosedElement).enclosingElement;
   } else if (modelElement is Class) {
     return modelElement;
@@ -204,7 +204,7 @@
 
   // Try expensive not-scoped lookup.
   if (refModelElement == null && element is ModelElement) {
-    Class preferredClass = _getPreferredClass(element);
+    Container preferredClass = _getPreferredClass(element);
     refModelElement =
         _MarkdownCommentReference(codeRef, element, commentRefs, preferredClass)
             .computeReferredElement();
@@ -692,7 +692,7 @@
   }
 
   // Add a result, but make it canonical.
-  void _addCanonicalResult(ModelElement modelElement, Class tryClass) {
+  void _addCanonicalResult(ModelElement modelElement, Container tryClass) {
     results.add(packageGraph.findCanonicalModelElementFor(modelElement.element,
         preferredClass: tryClass));
   }
diff --git a/lib/src/model.dart b/lib/src/model.dart
index ebba432..eed06ba 100644
--- a/lib/src/model.dart
+++ b/lib/src/model.dart
@@ -161,8 +161,8 @@
   bool get isInherited;
 
   bool _canonicalEnclosingClassIsSet = false;
-  Class _canonicalEnclosingClass;
-  Class _definingEnclosingClass;
+  Container _canonicalEnclosingClass;
+  Container _definingEnclosingClass;
 
   ModelElement get definingEnclosingElement {
     if (_definingEnclosingClass == null) {
@@ -174,12 +174,21 @@
 
   @override
   ModelElement _buildCanonicalModelElement() {
-    return canonicalEnclosingElement?.allCanonicalModelElements?.firstWhere(
-        (m) => m.name == name && m.isPropertyAccessor == isPropertyAccessor,
-        orElse: () => null);
+    if (canonicalEnclosingElement is Extension) {
+      return this;
+    }
+    if (canonicalEnclosingElement is Class) {
+      return (canonicalEnclosingElement as Class)
+          ?.allCanonicalModelElements
+          ?.firstWhere(
+              (m) =>
+                  m.name == name && m.isPropertyAccessor == isPropertyAccessor,
+              orElse: () => null);
+    }
+    return null;
   }
 
-  Class get canonicalEnclosingElement {
+  Container get canonicalEnclosingElement {
     Element searchElement = element;
     if (!_canonicalEnclosingClassIsSet) {
       if (isInherited) {
@@ -222,7 +231,8 @@
             definingEnclosingElement.isPublic) {
           assert(definingEnclosingElement == _canonicalEnclosingClass);
         }
-      } else {
+      } else if (enclosingElement is! Extension ||
+          (enclosingElement is Extension && enclosingElement.isDocumented)) {
         _canonicalEnclosingClass =
             packageGraph.findCanonicalModelElementFor(enclosingElement.element);
       }
@@ -275,6 +285,10 @@
       //
       // We use canonical elements here where possible to deal with reexports
       // as seen in Flutter.
+      if (enclosingElement is Extension) {
+        _isOverride = false;
+        return _isOverride;
+      }
       Class enclosingCanonical = enclosingElement;
       if (enclosingElement is ModelElement) {
         enclosingCanonical =
@@ -355,7 +369,7 @@
   bool get isInherited => _isInherited;
 
   @override
-  Class get enclosingElement {
+  Container get enclosingElement {
     if (_enclosingElement == null) {
       _enclosingElement = super.enclosingElement;
     }
@@ -582,27 +596,144 @@
   String get kind => 'mixin';
 }
 
-class Class extends ModelElement
+// Can be either a Class or Extension, used in the package graph and template data.
+// Aggregates some of the common getters.
+abstract class Container extends ModelElement {
+  List<Method> _allMethods;
+  List<Field> _constants;
+  List<Operator> _operators;
+  List<Method> _staticMethods;
+  List<Method> _instanceMethods;
+  List<Field> _fields;
+  List<Field> _staticFields;
+  List<Field> _instanceFields;
+  List<TypeParameter> _typeParameters;
+
+  Container(Element element, Library library, PackageGraph packageGraph)
+      : super(element, library, packageGraph, null);
+
+  bool get isClass => element is ClassElement;
+
+  bool get isExtension => element is ExtensionElement;
+
+  List<Method> get _methods => _allMethods;
+
+  List<Method> get instanceMethods {
+    if (_instanceMethods != null) return _instanceMethods;
+
+    _instanceMethods = _methods
+        .where((m) => !m.isStatic && !m.isOperator)
+        .toList(growable: false)
+          ..sort(byName);
+    return _instanceMethods;
+  }
+
+  bool get hasPublicMethods => filterNonPublic(instanceMethods).isNotEmpty;
+
+  Iterable<Method> get allPublicInstanceMethods =>
+      filterNonPublic(instanceMethods);
+
+  List<Method> get staticMethods {
+    if (_staticMethods != null) return _staticMethods;
+
+    _staticMethods = _methods.where((m) => m.isStatic).toList(growable: false)
+      ..sort(byName);
+
+    return _staticMethods;
+  }
+
+  bool get hasPublicStaticMethods => filterNonPublic(staticMethods).isNotEmpty;
+
+  Iterable<Method> get publicStaticMethods => filterNonPublic(staticMethods);
+
+  List<Operator> get operators {
+    if (_operators != null) return _operators;
+    _operators = _methods
+        .where((m) => m.isOperator)
+        .cast<Operator>()
+        .toList(growable: false)
+          ..sort(byName);
+    return _operators;
+  }
+
+  Iterable<Operator> get allOperators => operators;
+
+  bool get hasPublicOperators => publicOperators.isNotEmpty;
+
+  Iterable<Operator> get allPublicOperators => filterNonPublic(allOperators);
+
+  Iterable<Operator> get publicOperators => filterNonPublic(operators);
+
+  List<Field> get _allFields => [];
+
+  List<Field> get staticProperties {
+    if (_staticFields != null) return _staticFields;
+    _staticFields = _allFields
+        .where((f) => f.isStatic && !f.isConst)
+        .toList(growable: false)
+          ..sort(byName);
+
+    return _staticFields;
+  }
+
+  Iterable<Field> get publicStaticProperties =>
+      filterNonPublic(staticProperties);
+
+  bool get hasPublicStaticProperties => publicStaticProperties.isNotEmpty;
+
+  List<Field> get instanceProperties {
+    if (_instanceFields != null) return _instanceFields;
+    _instanceFields = _allFields
+        .where((f) => !f.isStatic && !f.isInherited && !f.isConst)
+        .toList(growable: false)
+          ..sort(byName);
+
+    return _instanceFields;
+  }
+
+  Iterable<Field> get publicInstanceProperties =>
+      filterNonPublic(instanceProperties);
+
+  bool get hasPublicProperties => publicInstanceProperties.isNotEmpty;
+
+  Iterable<Field> get allInstanceFields => instanceProperties;
+
+  Iterable<Field> get allPublicInstanceProperties =>
+      filterNonPublic(allInstanceFields);
+
+  bool isInheritingFrom(Container other) => false;
+
+  List<Field> get constants {
+    if (_constants != null) return _constants;
+    _constants = _allFields.where((f) => f.isConst).toList(growable: false)
+      ..sort(byName);
+
+    return _constants;
+  }
+
+  Iterable<Field> get publicConstants => filterNonPublic(constants);
+
+  bool get hasPublicConstants => publicConstants.isNotEmpty;
+
+  Iterable<Accessor> get allAccessors => quiver.concat([
+        allInstanceFields.expand((f) => f.allAccessors),
+        constants.map((c) => c.getter)
+      ]);
+}
+
+class Class extends Container
     with TypeParameters, Categorization
     implements EnclosedElement {
   List<DefinedElementType> _mixins;
   DefinedElementType supertype;
   List<DefinedElementType> _interfaces;
   List<Constructor> _constructors;
-  List<Method> _allMethods;
-  List<Operator> _operators;
   List<Operator> _inheritedOperators;
   List<Method> _inheritedMethods;
-  List<Method> _staticMethods;
-  List<Method> _instanceMethods;
-  List<Field> _fields;
-  List<Field> _staticFields;
-  List<Field> _constants;
-  List<Field> _instanceFields;
   List<Field> _inheritedProperties;
 
   Class(ClassElement element, Library library, PackageGraph packageGraph)
-      : super(element, library, packageGraph, null) {
+      : super(element, library, packageGraph) {
     packageGraph.specialClasses.addSpecial(this);
     _mixins = _cls.mixins
         .map((f) {
@@ -634,44 +765,27 @@
   Iterable<Method> get allInstanceMethods =>
       quiver.concat([instanceMethods, inheritedMethods]);
 
+  @override
   Iterable<Method> get allPublicInstanceMethods =>
       filterNonPublic(allInstanceMethods);
 
   bool get allPublicInstanceMethodsInherited =>
       instanceMethods.every((f) => f.isInherited);
 
+  @override
   Iterable<Field> get allInstanceFields =>
       quiver.concat([instanceProperties, inheritedProperties]);
 
-  Iterable<Accessor> get allAccessors => quiver.concat([
-        allInstanceFields.expand((f) => f.allAccessors),
-        constants.map((c) => c.getter)
-      ]);
-
-  Iterable<Field> get allPublicInstanceProperties =>
-      filterNonPublic(allInstanceFields);
-
   bool get allPublicInstancePropertiesInherited =>
       allPublicInstanceProperties.every((f) => f.isInherited);
 
+  @override
   Iterable<Operator> get allOperators =>
       quiver.concat([operators, inheritedOperators]);
 
-  Iterable<Operator> get allPublicOperators => filterNonPublic(allOperators);
-
   bool get allPublicOperatorsInherited =>
       allPublicOperators.every((f) => f.isInherited);
 
-  List<Field> get constants {
-    if (_constants != null) return _constants;
-    _constants = _allFields.where((f) => f.isConst).toList(growable: false)
-      ..sort(byName);
-
-    return _constants;
-  }
-
-  Iterable<Field> get publicConstants => filterNonPublic(constants);
-
   Map<Element, ModelElement> _allElements;
 
   Map<Element, ModelElement> get allElements {
@@ -786,18 +900,15 @@
     return kind;
   }
 
-  bool get hasPublicConstants => publicConstants.isNotEmpty;
-
   bool get hasPublicConstructors => publicConstructors.isNotEmpty;
 
   bool get hasPublicImplementors => publicImplementors.isNotEmpty;
 
-  bool get hasInstanceMethods => instanceMethods.isNotEmpty;
-
   bool get hasInstanceProperties => instanceProperties.isNotEmpty;
 
   bool get hasPublicInterfaces => publicInterfaces.isNotEmpty;
 
+  @override
   bool get hasPublicMethods =>
       publicInstanceMethods.isNotEmpty || publicInheritedMethods.isNotEmpty;
 
@@ -810,17 +921,18 @@
       hasPublicSuperChainReversed ||
       hasPublicImplementors;
 
+  @override
   bool get hasPublicOperators =>
       publicOperators.isNotEmpty || publicInheritedOperators.isNotEmpty;
 
+  @override
   bool get hasPublicProperties =>
       publicInheritedProperties.isNotEmpty ||
       publicInstanceProperties.isNotEmpty;
 
+  @override
   bool get hasPublicStaticMethods => publicStaticMethods.isNotEmpty;
 
-  bool get hasPublicStaticProperties => publicStaticProperties.isNotEmpty;
-
   bool get hasPublicSuperChainReversed => publicSuperChainReversed.isNotEmpty;
 
   @override
@@ -903,31 +1015,8 @@
   Iterable<Field> get publicInheritedProperties =>
       filterNonPublic(inheritedProperties);
 
-  List<Method> get instanceMethods {
-    if (_instanceMethods != null) return _instanceMethods;
-
-    _instanceMethods = _methods
-        .where((m) => !m.isStatic && !m.isOperator)
-        .toList(growable: false)
-          ..sort(byName);
-    return _instanceMethods;
-  }
-
   Iterable<Method> get publicInstanceMethods => instanceMethods;
 
-  List<Field> get instanceProperties {
-    if (_instanceFields != null) return _instanceFields;
-    _instanceFields = _allFields
-        .where((f) => !f.isStatic && !f.isInherited && !f.isConst)
-        .toList(growable: false)
-          ..sort(byName);
-
-    return _instanceFields;
-  }
-
-  Iterable<Field> get publicInstanceProperties =>
-      filterNonPublic(instanceProperties);
-
   List<DefinedElementType> get interfaces => _interfaces;
 
   Iterable<DefinedElementType> get publicInterfaces =>
@@ -951,7 +1040,8 @@
   }
 
   /// Returns true if [other] is a parent class for this class.
-  bool isInheritingFrom(Class other) =>
+  @override
+  bool isInheritingFrom(covariant Class other) =>
       superChain.map((et) => (et.element as Class)).contains(other);
 
   @override
@@ -964,42 +1054,6 @@
   @override
   DefinedElementType get modelType => super.modelType;
 
-  List<Operator> get operators {
-    if (_operators != null) return _operators;
-    _operators = _methods
-        .where((m) => m.isOperator)
-        .cast<Operator>()
-        .toList(growable: false)
-          ..sort(byName);
-    return _operators;
-  }
-
-  Iterable<Operator> get publicOperators => filterNonPublic(operators);
-
-  List<Method> get staticMethods {
-    if (_staticMethods != null) return _staticMethods;
-
-    _staticMethods = _methods.where((m) => m.isStatic).toList(growable: false)
-      ..sort(byName);
-
-    return _staticMethods;
-  }
-
-  Iterable<Method> get publicStaticMethods => filterNonPublic(staticMethods);
-
-  List<Field> get staticProperties {
-    if (_staticFields != null) return _staticFields;
-    _staticFields = _allFields
-        .where((f) => f.isStatic && !f.isConst)
-        .toList(growable: false)
-          ..sort(byName);
-
-    return _staticFields;
-  }
-
-  Iterable<Field> get publicStaticProperties =>
-      filterNonPublic(staticProperties);
-
   /// Not the same as superChain as it may include mixins.
   /// It's really not even the same as ordinary Dart inheritance, either,
   /// because we pretend that interfaces are part of the inheritance chain
@@ -1074,6 +1128,7 @@
 
   /// Internal only because subclasses are allowed to override how
   /// these are mapped to [allInheritedFields] and so forth.
+  @override
   List<Field> get _allFields {
     if (_fields != null) return _fields;
     _fields = [];
@@ -1181,6 +1236,7 @@
 
   ClassElement get _cls => (element as ClassElement);
 
+  @override
   List<Method> get _methods {
     if (_allMethods != null) return _allMethods;
 
@@ -1192,8 +1248,6 @@
     return _allMethods;
   }
 
-  List<TypeParameter> _typeParameters;
-
   // a stronger hash?
   @override
   List<TypeParameter> get typeParameters {
@@ -1214,6 +1268,104 @@
       o.library.package.name == library.package.name;
 }
 
+/// Extension methods
+class Extension extends Container
+    with TypeParameters, Categorization
+    implements EnclosedElement {
+  DefinedElementType extendedType;
+
+  Extension(
+      ExtensionElement element, Library library, PackageGraph packageGraph)
+      : super(element, library, packageGraph) {
+    extendedType = ElementType.from(_extension.extendedType, packageGraph);
+  }
+
+  @override
+  ModelElement get enclosingElement => library;
+
+  ExtensionElement get _extension => (element as ExtensionElement);
+
+  @override
+  String get kind => 'extension';
+
+  @override
+  List<Method> get _methods {
+    if (_allMethods != null) return _allMethods;
+
+    _allMethods = _extension.methods.map((e) {
+      return ModelElement.from(e, library, packageGraph) as Method;
+    }).toList(growable: false)
+      ..sort(byName);
+
+    return _allMethods;
+  }
+
+  @override
+  List<Field> get _allFields {
+    if (_fields != null) return _fields;
+    _fields = _extension.fields.map((f) {
+      Accessor getter, setter;
+      if (f.getter != null) {
+        getter = InheritableAccessor(f.getter, library, packageGraph);
+      }
+      if (f.setter != null) {
+        setter = InheritableAccessor(f.setter, library, packageGraph);
+      }
+
+      return ModelElement.from(f, library, packageGraph,
+          enclosingClass: this, getter: getter, setter: setter) as Field;
+    }).toList(growable: false)
+      ..sort(byName);
+
+    return _fields;
+  }
+
+  // a stronger hash?
+  @override
+  List<TypeParameter> get typeParameters {
+    if (_typeParameters == null) {
+      _typeParameters = _extension.typeParameters.map((f) {
+        var lib = Library(f.enclosingElement.library, packageGraph);
+        return ModelElement.from(f, lib, packageGraph) as TypeParameter;
+      }).toList();
+    }
+    return _typeParameters;
+  }
+
+  @override
+  DefinedElementType get modelType => super.modelType;
+
+  List<ModelElement> _allModelElements;
+
+  List<ModelElement> get allModelElements {
+    if (_allModelElements == null) {
+      _allModelElements = List.from(
+          quiver.concat([
+            instanceMethods,
+            allInstanceFields,
+            allAccessors,
+            allOperators,
+            constants,
+            staticMethods,
+            staticProperties,
+            typeParameters,
+          ]),
+          growable: false);
+    }
+    return _allModelElements;
+  }
+
+  @override
+  String get href {
+    if (!identical(canonicalModelElement, this)) {
+      return canonicalModelElement?.href;
+    }
+    assert(canonicalLibrary != null);
+    assert(canonicalLibrary == library);
+    return '${package.baseHref}${library.dirName}/$fileName';
+  }
+}
+
 class Constructor extends ModelElement
     with TypeParameters
     implements EnclosedElement {
@@ -1711,7 +1863,7 @@
     with GetterSetterCombo, Inheritable
     implements EnclosedElement {
   bool _isInherited = false;
-  Class _enclosingClass;
+  Container _enclosingClass;
   @override
   final InheritableAccessor getter;
   @override
@@ -2205,6 +2357,16 @@
         .toList(growable: false);
   }
 
+  @override
+  Iterable<Extension> get extensions {
+    if (_extensions != null) return _extensions;
+    _extensions = _libraryElement.definingCompilationUnit.extensions
+        .map((e) => ModelElement.from(e, this, packageGraph) as Extension)
+        .toList(growable: false)
+          ..sort(byName);
+    return _extensions;
+  }
+
   SdkLibrary get sdkLib {
     if (packageGraph.sdkLibrarySources.containsKey(element.librarySource)) {
       return packageGraph.sdkLibrarySources[element.librarySource];
@@ -2661,6 +2823,12 @@
         library.functions,
         library.properties,
         library.typedefs,
+        library.extensions.expand((e) {
+          return quiver.concat([
+            [e],
+            e.allModelElements
+          ]);
+        }),
         library.allClasses.expand((c) {
           return quiver.concat([
             [c],
@@ -2715,7 +2883,7 @@
     with Inheritable, TypeParameters
     implements EnclosedElement {
   bool _isInherited = false;
-  Class _enclosingClass;
+  Container _enclosingClass;
   @override
   List<TypeParameter> typeParameters = [];
 
@@ -2788,6 +2956,9 @@
 
   @override
   Method get overriddenElement {
+    if (_enclosingClass is Extension) {
+      return null;
+    }
     ClassElement parent = element.enclosingElement;
     for (InterfaceType t in parent.allSupertypes) {
       Element e = t.getMethod(element.name);
@@ -2941,7 +3112,7 @@
   /// Specify enclosingClass only if this is to be an inherited object.
   factory ModelElement.from(
       Element e, Library library, PackageGraph packageGraph,
-      {Class enclosingClass, Accessor getter, Accessor setter}) {
+      {Container enclosingClass, Accessor getter, Accessor setter}) {
     assert(packageGraph != null && e != null);
     assert(library != null ||
         e is ParameterElement ||
@@ -2962,7 +3133,8 @@
       originalMember = e;
       e = basest;
     }
-    Tuple3<Element, Library, Class> key = Tuple3(e, library, enclosingClass);
+    Tuple3<Element, Library, Container> key =
+        Tuple3(e, library, enclosingClass);
     ModelElement newModelElement;
     if (e.kind != ElementKind.DYNAMIC &&
         packageGraph._allConstructedModelElements.containsKey(key)) {
@@ -2989,6 +3161,9 @@
             newModelElement = Class(e, library, packageGraph);
           }
         }
+        if (e is ExtensionElement) {
+          newModelElement = Extension(e, library, packageGraph);
+        }
         if (e is FunctionElement) {
           newModelElement = ModelFunction(e, library, packageGraph);
         } else if (e is GenericFunctionTypeElement) {
@@ -3018,16 +3193,23 @@
               newModelElement = EnumField.forConstant(
                   index, e, library, packageGraph, getter);
               // ignore: unnecessary_cast
-            } else if ((e.enclosingElement as ClassElement).isEnum) {
+            } else if (e.enclosingElement is ExtensionElement) {
+              newModelElement = Field(e, library, packageGraph, getter, setter);
+            } else if (e.enclosingElement is ClassElement &&
+                (e.enclosingElement as ClassElement).isEnum) {
               newModelElement =
                   EnumField(e, library, packageGraph, getter, setter);
             } else {
               newModelElement = Field(e, library, packageGraph, getter, setter);
             }
           } else {
-            // EnumFields can't be inherited, so this case is simpler.
-            newModelElement = Field.inherited(
-                e, enclosingClass, library, packageGraph, getter, setter);
+            if (enclosingClass is Extension) {
+              newModelElement = Field(e, library, packageGraph, getter, setter);
+            } else {
+              // EnumFields can't be inherited, so this case is simpler.
+              newModelElement = Field.inherited(
+                  e, enclosingClass, library, packageGraph, getter, setter);
+            }
           }
         }
         if (e is ConstructorElement) {
@@ -3173,6 +3355,9 @@
       } else if (enclosingElement is Class &&
           !(enclosingElement as Class).isPublic) {
         _isPublic = false;
+      } else if (enclosingElement is Extension &&
+          !(enclosingElement as Extension).isPublic) {
+        _isPublic = false;
       } else {
         String docComment = documentationComment;
         if (docComment == null) {
@@ -3251,8 +3436,8 @@
       element is ExecutableElement || element is FunctionTypedElement;
 
   ModelElement _buildCanonicalModelElement() {
-    Class preferredClass;
-    if (enclosingElement is Class) {
+    Container preferredClass;
+    if (enclosingElement is Class || enclosingElement is Extension) {
       preferredClass = enclosingElement;
     }
     return packageGraph.findCanonicalModelElementFor(element,
@@ -4963,7 +5148,7 @@
   PackageWarningCounter _packageWarningCounter;
 
   /// All ModelElements constructed for this package; a superset of [allModelElements].
-  final Map<Tuple3<Element, Library, Class>, ModelElement>
+  final Map<Tuple3<Element, Library, Container>, ModelElement>
       _allConstructedModelElements = Map();
 
   /// Anything that might be inheritable, place here for later lookup.
@@ -5468,11 +5653,12 @@
   /// This doesn't know anything about [PackageGraph.inheritThrough] and probably
   /// shouldn't, so using it with [Inheritable]s without special casing is
   /// not advised.
-  ModelElement findCanonicalModelElementFor(Element e, {Class preferredClass}) {
+  ModelElement findCanonicalModelElementFor(Element e,
+      {Container preferredClass}) {
     assert(allLibrariesAdded);
     Library lib = findCanonicalLibraryFor(e);
-    if (preferredClass != null) {
-      Class canonicalClass =
+    if (preferredClass != null && preferredClass is Container) {
+      Container canonicalClass =
           findCanonicalModelElementFor(preferredClass.element);
       if (canonicalClass != null) preferredClass = canonicalClass;
     }
@@ -5480,6 +5666,15 @@
       lib = findCanonicalLibraryFor(preferredClass.element);
     }
     ModelElement modelElement;
+    // For elements defined in extensions, they are canonical.
+    if (e?.enclosingElement is ExtensionElement) {
+      lib ??= Library(e.enclosingElement.library, packageGraph);
+      // (TODO:keertip) Find a better way to exclude members of extensions
+      //  when libraries are specified using the "--include" flag
+      if (lib?.isDocumented) {
+        return ModelElement.from(e, lib, packageGraph);
+      }
+    }
     // TODO(jcollins-g): Special cases are pretty large here.  Refactor to split
     // out into helpers.
     // TODO(jcollins-g): The data structures should be changed to eliminate guesswork
@@ -5523,7 +5718,9 @@
 
       // 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) {
+      if (matches.length > 1 &&
+          preferredClass != null &&
+          preferredClass is Class) {
         // Search for matches inside our superchain.
         List<Class> superChain = preferredClass.superChain
             .map((et) => et.element)
@@ -5692,6 +5889,7 @@
 /// of a [TopLevelContainer].
 abstract class TopLevelContainer implements Nameable {
   List<Class> _classes;
+  List<Extension> _extensions;
   List<Enum> _enums;
   List<Mixin> _mixins;
   List<Class> _exceptions;
@@ -5702,6 +5900,8 @@
 
   Iterable<Class> get classes => _classes;
 
+  Iterable<Extension> get extensions => _extensions;
+
   Iterable<Enum> get enums => _enums;
 
   Iterable<Mixin> get mixins => _mixins;
@@ -5718,6 +5918,8 @@
 
   bool get hasPublicClasses => publicClasses.isNotEmpty;
 
+  bool get hasPublicExtensions => publicExtensions.isNotEmpty;
+
   bool get hasPublicConstants => publicConstants.isNotEmpty;
 
   bool get hasPublicEnums => publicEnums.isNotEmpty;
@@ -5734,6 +5936,8 @@
 
   Iterable<Class> get publicClasses => filterNonPublic(classes);
 
+  Iterable<Extension> get publicExtensions => filterNonPublic(extensions);
+
   Iterable<TopLevelVariable> get publicConstants => filterNonPublic(constants);
 
   Iterable<Enum> get publicEnums => filterNonPublic(enums);
@@ -5876,6 +6080,7 @@
     _functions = [];
     _mixins = [];
     _typedefs = [];
+    _extensions = [];
   }
 
   void addItem(Categorization c) {
@@ -5903,6 +6108,8 @@
       _functions.add(c);
     } else if (c is Typedef) {
       _typedefs.add(c);
+    } else if (c is Extension) {
+      _extensions.add(c);
     } else {
       throw UnimplementedError("Unrecognized element");
     }
@@ -6739,7 +6946,8 @@
       AnalysisOptionsImpl options = AnalysisOptionsImpl();
 
       // TODO(jcollins-g): pass in an ExperimentStatus instead?
-      options.enabledExperiments = config.enableExperiment;
+      options.enabledExperiments = config.enableExperiment
+        ..add('extension-methods');
 
       // TODO(jcollins-g): Make use of currently not existing API for managing
       //                   many AnalysisDrivers
diff --git a/lib/src/version.dart b/lib/src/version.dart
index 6d46787..d25679d 100644
--- a/lib/src/version.dart
+++ b/lib/src/version.dart
@@ -1,2 +1,2 @@
 // Generated code. Do not modify.
-const packageVersion = '0.28.5';
+const packageVersion = '0.28.6-dev';
diff --git a/lib/templates/_extension.html b/lib/templates/_extension.html
new file mode 100644
index 0000000..d35e458
--- /dev/null
+++ b/lib/templates/_extension.html
@@ -0,0 +1,7 @@
+<dt id="{{htmlId}}">
+    <span class="name {{#isDeprecated}}deprecated{{/isDeprecated}}">{{{linkedName}}}</span> {{>categorization}}
+</dt>
+<dd>
+    {{{ oneLineDoc }}}
+</dd>
+
diff --git a/lib/templates/_sidebar_for_class.html b/lib/templates/_sidebar_for_class.html
index 3a0880e..d0b1bb0 100644
--- a/lib/templates/_sidebar_for_class.html
+++ b/lib/templates/_sidebar_for_class.html
@@ -1,5 +1,7 @@
 <ol>
   {{#clazz}}
+
+  {{#isClass}}
   {{#hasPublicConstructors}}
   <li class="section-title"><a href="{{{href}}}#constructors">Constructors</a></li>
   {{#publicConstructors}}
@@ -50,5 +52,52 @@
   <li>{{{linkedName}}}</li>
   {{/publicConstants}}
   {{/hasPublicConstants}}
+  {{/isClass}}
+
+  {{#isExtension}}
+  {{#hasPublicProperties}}
+  <li class="section-title"> <a href="{{{href}}}#instance-properties">Properties</a>
+  </li>
+  {{#allPublicInstanceProperties}}
+  <li>{{{ linkedName }}}</li>
+  {{/allPublicInstanceProperties}}
+  {{/hasPublicProperties}}
+
+  {{#hasPublicMethods}}
+  <li class="section-title"><a href="{{{href}}}#instance-methods">Methods</a></li>
+  {{#allPublicInstanceMethods}}
+  <li>{{{ linkedName }}}</li>
+  {{/allPublicInstanceMethods}}
+  {{/hasPublicMethods}}
+
+  {{#hasPublicOperators}}
+  <li class="section-title"><a href="{{{href}}}#operators">Operators</a></li>
+  {{#allPublicOperators}}
+  <li>{{{ linkedName }}}</li>
+  {{/allPublicOperators}}
+  {{/hasPublicOperators}}
+
+  {{#hasPublicStaticProperties}}
+  <li class="section-title"><a href="{{{href}}}#static-properties">Static properties</a></li>
+  {{#publicStaticProperties}}
+  <li>{{{ linkedName }}}</li>
+  {{/publicStaticProperties}}
+  {{/hasPublicStaticProperties}}
+
+  {{#hasPublicStaticMethods}}
+  <li class="section-title"><a href="{{{href}}}#static-methods">Static methods</a></li>
+  {{#publicStaticMethods}}
+  <li>{{{ linkedName }}}</li>
+  {{/publicStaticMethods}}
+  {{/hasPublicStaticMethods}}
+
+  {{#hasPublicConstants}}
+  <li class="section-title"><a href="{{{href}}}#constants">Constants</a></li>
+  {{#publicConstants}}
+  <li>{{{linkedName}}}</li>
+  {{/publicConstants}}
+  {{/hasPublicConstants}}
+  {{/isExtension}}
+
   {{/clazz}}
 </ol>
diff --git a/lib/templates/_sidebar_for_extension.html b/lib/templates/_sidebar_for_extension.html
new file mode 100644
index 0000000..138db39
--- /dev/null
+++ b/lib/templates/_sidebar_for_extension.html
@@ -0,0 +1,48 @@
+<ol>
+    {{#extension}}
+
+    {{#hasPublicProperties}}
+    <li class="section-title"> <a href="{{{href}}}#instance-properties">Properties</a>
+    </li>
+    {{#allPublicInstanceProperties}}
+    <li>{{{ linkedName }}}</li>
+    {{/allPublicInstanceProperties}}
+    {{/hasPublicProperties}}
+
+    {{#hasPublicMethods}}
+    <li class="section-title"><a href="{{{href}}}#instance-methods">Methods</a></li>
+    {{#allPublicInstanceMethods}}
+    <li>{{{ linkedName }}}</li>
+    {{/allPublicInstanceMethods}}
+    {{/hasPublicMethods}}
+
+    {{#hasPublicOperators}}
+    <li class="section-title"><a href="{{{href}}}#operators">Operators</a></li>
+    {{#allPublicOperators}}
+    <li>{{{ linkedName }}}</li>
+    {{/allPublicOperators}}
+    {{/hasPublicOperators}}
+
+    {{#hasPublicStaticProperties}}
+    <li class="section-title"><a href="{{{href}}}#static-properties">Static properties</a></li>
+    {{#publicStaticProperties}}
+    <li>{{{ linkedName }}}</li>
+    {{/publicStaticProperties}}
+    {{/hasPublicStaticProperties}}
+
+    {{#hasPublicStaticMethods}}
+    <li class="section-title"><a href="{{{href}}}#static-methods">Static methods</a></li>
+    {{#publicStaticMethods}}
+    <li>{{{ linkedName }}}</li>
+    {{/publicStaticMethods}}
+    {{/hasPublicStaticMethods}}
+
+    {{#hasPublicConstants}}
+    <li class="section-title"><a href="{{{href}}}#constants">Constants</a></li>
+    {{#publicConstants}}
+    <li>{{{linkedName}}}</li>
+    {{/publicConstants}}
+    {{/hasPublicConstants}}
+
+    {{/extension}}
+</ol>
diff --git a/lib/templates/_sidebar_for_library.html b/lib/templates/_sidebar_for_library.html
index 43dfc8a..726b309 100644
--- a/lib/templates/_sidebar_for_library.html
+++ b/lib/templates/_sidebar_for_library.html
@@ -7,6 +7,13 @@
   {{/publicClasses}}
   {{/hasPublicClasses}}
 
+  {{#hasPublicExtensions}}
+  <li class="section-title"><a href="{{{href}}}#extension">Extensions</a></li>
+  {{#publicExtensions}}
+  <li>{{{ linkedName }}}</li>
+  {{/publicExtensions}}
+  {{/hasPublicExtensions}}
+
   {{#hasPublicMixins}}
   <li class="section-title"><a href="{{{href}}}#mixins">Mixins</a></li>
   {{#publicMixins}}
diff --git a/lib/templates/extension.html b/lib/templates/extension.html
new file mode 100644
index 0000000..9fdee1c
--- /dev/null
+++ b/lib/templates/extension.html
@@ -0,0 +1,103 @@
+{{>head}}
+
+<div id="dartdoc-sidebar-left" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left">
+    {{>search_sidebar}}
+    <h5>{{parent.name}} {{parent.kind}}</h5>
+    {{>sidebar_for_library}}
+</div>
+
+<div id="dartdoc-main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
+    {{#self}}
+    <div>{{>source_link}}<h1><span class="kind-class">{{{nameWithGenerics}}}</span> {{kind}} {{>categorization}}</h1></div>
+    {{/self}}
+
+    {{#extension}}
+    {{>documentation}}
+
+    <dt>on</dt>
+    <dd>
+        <ul class="comma-separated clazz-relationships">
+            {{#extendedType}}
+            <li>{{{linkedName}}}</li>
+            {{/extendedType}}
+        </ul>
+    </dd>
+
+
+    {{#hasPublicProperties}}
+    <section class="summary offset-anchor" id="instance-properties">
+        <h2>Properties</h2>
+
+        <dl class="properties">
+            {{#allPublicInstanceProperties}}
+            {{>property}}
+            {{/allPublicInstanceProperties}}
+        </dl>
+    </section>
+    {{/hasPublicProperties}}
+
+    {{#hasPublicMethods}}
+    <section class="summary offset-anchor" id="instance-methods">
+        <h2>Methods</h2>
+        <dl class="callables">
+            {{#allPublicInstanceMethods}}
+            {{>callable}}
+            {{/allPublicInstanceMethods}}
+        </dl>
+    </section>
+    {{/hasPublicMethods}}
+
+    {{#hasPublicOperators}}
+    <section class="summary offset-anchor" id="operators">
+        <h2>Operators</h2>
+        <dl class="callables">
+            {{#allPublicOperators}}
+            {{>callable}}
+            {{/allPublicOperators}}
+        </dl>
+    </section>
+    {{/hasPublicOperators}}
+
+    {{#hasPublicStaticProperties}}
+    <section class="summary offset-anchor" id="static-properties">
+        <h2>Static Properties</h2>
+
+        <dl class="properties">
+            {{#publicStaticProperties}}
+            {{>property}}
+            {{/publicStaticProperties}}
+        </dl>
+    </section>
+    {{/hasPublicStaticProperties}}
+
+    {{#hasPublicStaticMethods}}
+    <section class="summary offset-anchor" id="static-methods">
+        <h2>Static Methods</h2>
+        <dl class="callables">
+            {{#publicStaticMethods}}
+            {{>callable}}
+            {{/publicStaticMethods}}
+        </dl>
+    </section>
+    {{/hasPublicStaticMethods}}
+
+    {{#hasPublicConstants}}
+    <section class="summary offset-anchor" id="constants">
+        <h2>Constants</h2>
+
+        <dl class="properties">
+            {{#publicConstants}}
+            {{>constant}}
+            {{/publicConstants}}
+        </dl>
+    </section>
+    {{/hasPublicConstants}}
+    {{/extension}}
+
+</div> <!-- /.main-content -->
+
+<div id="dartdoc-sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
+    {{>sidebar_for_extension}}
+</div><!--/.sidebar-offcanvas-->
+
+{{>footer}}
diff --git a/lib/templates/library.html b/lib/templates/library.html
index 1aa24b2..6e08376 100644
--- a/lib/templates/library.html
+++ b/lib/templates/library.html
@@ -39,6 +39,18 @@
     </section>
     {{/library.hasPublicMixins}}
 
+    {{#library.hasPublicExtensions}}
+    <section class="summary offset-anchor" id="extensions">
+      <h2>Extensions</h2>
+
+      <dl>
+        {{#library.publicExtensions}}
+        {{>extension}}
+        {{/library.publicExtensions}}
+      </dl>
+    </section>
+    {{/library.hasPublicExtensions}}
+
     {{#library.hasPublicConstants}}
     <section class="summary offset-anchor" id="constants">
       <h2>Constants</h2>
diff --git a/pubspec.yaml b/pubspec.yaml
index e11d8cb..fa96ecb 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,6 +1,6 @@
 name: dartdoc
 # Run `grind build` after updating.
-version: 0.28.5
+version: 0.28.6-dev
 author: Dart Team <misc@dartlang.org>
 description: A documentation generator for Dart.
 homepage: https://github.com/dart-lang/dartdoc
@@ -8,7 +8,7 @@
   sdk: '>=2.1.0 <3.0.0'
 
 dependencies:
-  analyzer: '>=0.36.3 <0.38.0'
+  analyzer: ^0.38.2
   args: '>=1.4.1 <2.0.0'
   collection: ^1.2.0
   crypto: ^2.0.6
diff --git a/test/dartdoc_test.dart b/test/dartdoc_test.dart
index 66bdbde..c47ade0 100644
--- a/test/dartdoc_test.dart
+++ b/test/dartdoc_test.dart
@@ -365,10 +365,10 @@
     });
 
     test('generate docs with custom templates', () async {
-      String templatesDir = path.join(testPackageCustomTemplates.path, 'templates');
-      Dartdoc dartdoc =
-          await buildDartdoc(['--templates-dir', templatesDir],
-              testPackageCustomTemplates, tempDir);
+      String templatesDir =
+          path.join(testPackageCustomTemplates.path, 'templates');
+      Dartdoc dartdoc = await buildDartdoc(['--templates-dir', templatesDir],
+          testPackageCustomTemplates, tempDir);
 
       DartdocResults results = await dartdoc.generateDocs();
       expect(results.packageGraph, isNotNull);
@@ -381,18 +381,21 @@
     test('generate docs with missing required template fails', () async {
       var templatesDir = path.join(path.current, 'test/templates');
       try {
-        await buildDartdoc(['--templates-dir', templatesDir], testPackageCustomTemplates, tempDir);
+        await buildDartdoc(['--templates-dir', templatesDir],
+            testPackageCustomTemplates, tempDir);
         fail('dartdoc should fail with missing required template');
       } catch (e) {
         expect(e is DartdocFailure, isTrue);
-        expect((e as DartdocFailure).message, startsWith('Missing required template file'));
+        expect((e as DartdocFailure).message,
+            startsWith('Missing required template file'));
       }
     });
 
     test('generate docs with bad templatesDir path fails', () async {
       String badPath = path.join(tempDir.path, 'BAD');
       try {
-        await buildDartdoc(['--templates-dir', badPath], testPackageCustomTemplates, tempDir);
+        await buildDartdoc(
+            ['--templates-dir', badPath], testPackageCustomTemplates, tempDir);
         fail('dartdoc should fail with bad templatesDir path');
       } catch (e) {
         expect(e is DartdocFailure, isTrue);
diff --git a/test/model_test.dart b/test/model_test.dart
index 1ef9067..031618f 100644
--- a/test/model_test.dart
+++ b/test/model_test.dart
@@ -2099,6 +2099,71 @@
     });
   });
 
+  group('Extension', () {
+    Extension ext, anExt, fancyList;
+    Method s;
+    List<Extension> extensions;
+
+    setUpAll(() {
+      ext = exLibrary.extensions.firstWhere((e) => e.name == 'AppleExtension');
+      anExt = exLibrary.extensions.firstWhere((e) => e.name == 'AnExtension');
+      fancyList = exLibrary.extensions.firstWhere((e) => e.name == 'FancyList');
+      extensions = exLibrary.publicExtensions.toList();
+    });
+
+    test('has a fully qualified name', () {
+      expect(ext.fullyQualifiedName, 'ex.AppleExtension');
+    });
+
+    test('has enclosing element', () {
+      expect(ext.enclosingElement.name, equals(exLibrary.name));
+    });
+
+    test('member method has href', () {
+      s = ext.instanceMethods.firstWhere((m) => m.name == 's');
+      expect(s.href, 'ex/AppleExtension/s.html');
+    });
+
+    test('has extended type', () {
+      expect(ext.extendedType.name, equals("Apple"));
+    });
+
+    test('extension name with generics', () {
+      expect(
+          fancyList.nameWithGenerics,
+          equals(
+              'FancyList&lt;<wbr><span class="type-parameter">Z</span>&gt;'));
+    });
+
+    test('get methods', () {
+      expect(fancyList.allPublicInstanceMethods, hasLength(1));
+    });
+
+    test('get operators', () {
+      expect(fancyList.allPublicOperators, hasLength(1));
+    });
+
+    test('get static methods', () {
+      expect(fancyList.publicStaticMethods, hasLength(1));
+    });
+
+    test('get properties', () {
+      expect(fancyList.publicInstanceProperties, hasLength(1));
+    });
+
+    test('get contants', () {
+      expect(fancyList.publicConstants, hasLength(0));
+    });
+
+    test('correctly finds all the extensions', () {
+      expect(exLibrary.extensions, hasLength(7));
+    });
+
+    test('correctly finds all the public extensions', () {
+      expect(extensions, hasLength(5));
+    });
+  });
+
   group('Enum', () {
     Enum animal;
 
diff --git a/testing/test_package/.analysis_options b/testing/test_package/.analysis_options
deleted file mode 100644
index 234da62..0000000
--- a/testing/test_package/.analysis_options
+++ /dev/null
@@ -1 +0,0 @@
-{ analyzer: { exclude: ['**'] } }
diff --git a/testing/test_package/analysis_options.yaml b/testing/test_package/analysis_options.yaml
new file mode 100644
index 0000000..8b7c84a
--- /dev/null
+++ b/testing/test_package/analysis_options.yaml
@@ -0,0 +1,6 @@
+analyzer:
+  enable-experiment:
+    - extension-methods
+  exclude: ['**']
+
+
diff --git a/testing/test_package/lib/example.dart b/testing/test_package/lib/example.dart
index b464043..4cba5f5 100644
--- a/testing/test_package/lib/example.dart
+++ b/testing/test_package/lib/example.dart
@@ -200,6 +200,16 @@
   final ParameterizedTypedef<bool> fieldWithTypedef;
 }
 
+
+/// Extension on Apple
+extension AppleExtension on Apple {
+/// Can call s on Apple
+  void s() {
+    print('Extension on Apple');
+  }
+}
+
+
 class WithGeneric<T> {
   T prop;
   WithGeneric(this.prop);
@@ -608,3 +618,37 @@
   /// {@end-tool}
   void injectHtmlFromTool();
 }
+
+/// Extension on a class defined in the package
+extension AnExtension<Q> on WithGeneric<Q> {
+  int call(String s) => 0;
+}
+
+/// Extension on List
+extension FancyList<Z> on List<Z> {
+  int get doubleLength => this.length * 2;
+  List<Z> operator-() => this.reversed.toList();
+  List<List<Z>> split(int at) =>
+      <List<Z>>[this.sublist(0, at), this.sublist(at)];
+  static List<Z> big() => List(1000000);
+}
+
+extension SymDiff<Q> on Set<Q> {
+  Set<Q> symmetricDifference(Set<Q> other) =>
+    this.difference(other).union(other.difference(this));
+}
+
+/// Extensions can be made specific.
+extension IntSet on Set<int> {
+  int sum() => this.fold(0, (prev, element) => prev + element);
+}
+
+// Extensions can be private.
+extension _Shhh on Object {
+  void secret() { }
+}
+
+// Extension with no name
+extension on Object {
+  void bar() { }
+}
\ No newline at end of file
diff --git a/testing/test_package_custom_templates/templates/extension.html b/testing/test_package_custom_templates/templates/extension.html
new file mode 100644
index 0000000..9fdee1c
--- /dev/null
+++ b/testing/test_package_custom_templates/templates/extension.html
@@ -0,0 +1,103 @@
+{{>head}}
+
+<div id="dartdoc-sidebar-left" class="col-xs-6 col-sm-3 col-md-2 sidebar sidebar-offcanvas-left">
+    {{>search_sidebar}}
+    <h5>{{parent.name}} {{parent.kind}}</h5>
+    {{>sidebar_for_library}}
+</div>
+
+<div id="dartdoc-main-content" class="col-xs-12 col-sm-9 col-md-8 main-content">
+    {{#self}}
+    <div>{{>source_link}}<h1><span class="kind-class">{{{nameWithGenerics}}}</span> {{kind}} {{>categorization}}</h1></div>
+    {{/self}}
+
+    {{#extension}}
+    {{>documentation}}
+
+    <dt>on</dt>
+    <dd>
+        <ul class="comma-separated clazz-relationships">
+            {{#extendedType}}
+            <li>{{{linkedName}}}</li>
+            {{/extendedType}}
+        </ul>
+    </dd>
+
+
+    {{#hasPublicProperties}}
+    <section class="summary offset-anchor" id="instance-properties">
+        <h2>Properties</h2>
+
+        <dl class="properties">
+            {{#allPublicInstanceProperties}}
+            {{>property}}
+            {{/allPublicInstanceProperties}}
+        </dl>
+    </section>
+    {{/hasPublicProperties}}
+
+    {{#hasPublicMethods}}
+    <section class="summary offset-anchor" id="instance-methods">
+        <h2>Methods</h2>
+        <dl class="callables">
+            {{#allPublicInstanceMethods}}
+            {{>callable}}
+            {{/allPublicInstanceMethods}}
+        </dl>
+    </section>
+    {{/hasPublicMethods}}
+
+    {{#hasPublicOperators}}
+    <section class="summary offset-anchor" id="operators">
+        <h2>Operators</h2>
+        <dl class="callables">
+            {{#allPublicOperators}}
+            {{>callable}}
+            {{/allPublicOperators}}
+        </dl>
+    </section>
+    {{/hasPublicOperators}}
+
+    {{#hasPublicStaticProperties}}
+    <section class="summary offset-anchor" id="static-properties">
+        <h2>Static Properties</h2>
+
+        <dl class="properties">
+            {{#publicStaticProperties}}
+            {{>property}}
+            {{/publicStaticProperties}}
+        </dl>
+    </section>
+    {{/hasPublicStaticProperties}}
+
+    {{#hasPublicStaticMethods}}
+    <section class="summary offset-anchor" id="static-methods">
+        <h2>Static Methods</h2>
+        <dl class="callables">
+            {{#publicStaticMethods}}
+            {{>callable}}
+            {{/publicStaticMethods}}
+        </dl>
+    </section>
+    {{/hasPublicStaticMethods}}
+
+    {{#hasPublicConstants}}
+    <section class="summary offset-anchor" id="constants">
+        <h2>Constants</h2>
+
+        <dl class="properties">
+            {{#publicConstants}}
+            {{>constant}}
+            {{/publicConstants}}
+        </dl>
+    </section>
+    {{/hasPublicConstants}}
+    {{/extension}}
+
+</div> <!-- /.main-content -->
+
+<div id="dartdoc-sidebar-right" class="col-xs-6 col-sm-6 col-md-2 sidebar sidebar-offcanvas-right">
+    {{>sidebar_for_extension}}
+</div><!--/.sidebar-offcanvas-->
+
+{{>footer}}
diff --git a/testing/test_package_extensions/analysis_options.yaml b/testing/test_package_extensions/analysis_options.yaml
new file mode 100644
index 0000000..8b7c84a
--- /dev/null
+++ b/testing/test_package_extensions/analysis_options.yaml
@@ -0,0 +1,6 @@
+analyzer:
+  enable-experiment:
+    - extension-methods
+  exclude: ['**']
+
+
diff --git a/testing/test_package_extensions/lib/main.dart b/testing/test_package_extensions/lib/main.dart
new file mode 100644
index 0000000..16c6684
--- /dev/null
+++ b/testing/test_package_extensions/lib/main.dart
@@ -0,0 +1,52 @@
+/// library with extensions
+library ext;
+
+class Apple<M> {
+  final String name = 'name';
+
+  bool get isImplemented => true;
+}
+
+class _Private {
+  void sIs() {}
+}
+
+/// Extension on a class defined in the package
+extension AnExtension on Apple {
+  int call(String s) => 0;
+}
+
+/// Extension on List
+extension FancyList<Z> on List<Z> {
+  int get doubleLength => this.length * 2;
+  List<Z> operator-() => this.reversed.toList();
+  List<List<Z>> split(int at) =>
+  <List<Z>>[this.sublist(0, at), this.sublist(at)];
+  static List<Z> big() => List(1000000);
+}
+
+extension SymDiff<T> on Set<T> {
+  Set<T> symmetricDifference(Set<T> other) =>
+  this.difference(other).union(other.difference(this));
+}
+
+/// Extensions can be made specific.
+extension IntSet on Set<int> {
+  int sum() => this.fold(0, (prev, element) => prev + element);
+}
+
+// Extensions can be private.
+extension _Shhh on Object {
+  void secret() { }
+}
+
+// Or unnamed!
+extension on Object {
+  void bar() { }
+}
+
+extension Bar on Object {
+  void bar() { }
+}
+
+
diff --git a/testing/test_package_extensions/pubspec.yaml b/testing/test_package_extensions/pubspec.yaml
new file mode 100644
index 0000000..03eca81
--- /dev/null
+++ b/testing/test_package_extensions/pubspec.yaml
@@ -0,0 +1,5 @@
+name: test_package_extensions
+version: 0.0.1
+description: A simple console application.
+environment:
+  sdk: <=3.0.0