diff --git a/CHANGES.txt b/CHANGES.txt
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,5 +1,8 @@
 Changelog for record-dot-preprocessor
 
+0.2.8, released 2021-02-21
+    Support GHC 9.0
+    #38, make the preprocessor avoid quasi quotes
 0.2.7, released 2020-10-02
     #29, deal with records containing type families in field types
 0.2.6, released 2020-08-12
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Neil Mitchell 2018-2020.
+Copyright Neil Mitchell 2018-2021.
 
 Licensed under either of:
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# record-dot-preprocessor [![Hackage version](https://img.shields.io/hackage/v/record-dot-preprocessor.svg?label=Hackage)](https://hackage.haskell.org/package/record-dot-preprocessor) [![Stackage version](https://www.stackage.org/package/record-dot-preprocessor/badge/nightly?label=Stackage)](https://www.stackage.org/package/record-dot-preprocessor) [![Build status](https://img.shields.io/travis/ndmitchell/record-dot-preprocessor/master.svg?label=Build)](https://travis-ci.org/ndmitchell/record-dot-preprocessor)
+# record-dot-preprocessor [![Hackage version](https://img.shields.io/hackage/v/record-dot-preprocessor.svg?label=Hackage)](https://hackage.haskell.org/package/record-dot-preprocessor) [![Stackage version](https://www.stackage.org/package/record-dot-preprocessor/badge/nightly?label=Stackage)](https://www.stackage.org/package/record-dot-preprocessor) [![Build status](https://img.shields.io/github/workflow/status/ndmitchell/record-dot-preprocessor/ci.svg)](https://github.com/ndmitchell/record-dot-preprocessor/actions)
 
 In almost every programming language `a.b` will get the `b` field from the `a` data type, and many different data types can have a `b` field. The reason this feature is ubiquitous is because it's _useful_. This feature has been [proposed for Haskell](https://github.com/ghc-proposals/ghc-proposals/pull/282) as `RecordDotSyntax`. The `record-dot-preprocessor` brings this feature to Haskell today. Some examples:
 
@@ -20,9 +20,9 @@
 First install `record-dot-preprocessor` with either `stack install record-dot-preprocessor` or `cabal update && cabal install record-dot-preprocessor`. Then at the top of the file add:
 
 * Either: `{-# OPTIONS_GHC -F -pgmF=record-dot-preprocessor #-}` for the preprocessor.
-* Or: `{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}` and `{-# LANGUAGE DuplicateRecordFields, TypeApplications, FlexibleContexts, DataKinds, MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances #-}` for the GHC plugin.
+* Or: `{-# OPTIONS_GHC -fplugin=RecordDotPreprocessor #-}` and `{-# LANGUAGE DuplicateRecordFields, TypeApplications, FlexibleContexts, DataKinds, MultiParamTypeClasses, TypeSynonymInstances, FlexibleInstances, UndecidableInstances, GADTs #-}` for the GHC plugin.
 
-The GHC plugin only runs on GHC 8.6 or higher, [doesn't work on Windows](https://gitlab.haskell.org/ghc/ghc/issues/16405) and has much better error messages. In contrast, the preprocessor runs everywhere and has more features.
+The GHC plugin only runs on GHC 8.6 or higher, [has some issues on Windows](https://gitlab.haskell.org/ghc/ghc/issues/16405) and has much better error messages. In contrast, the preprocessor runs everywhere and has more features.
 
 You must make sure that the `OPTIONS_GHC` is applied both to the file _where your records are defined_, and _where the record syntax is used_. The resulting program will require the [`record-hasfield` library](https://hackage.haskell.org/package/record-hasfield).
 
diff --git a/examples/Both.hs b/examples/Both.hs
--- a/examples/Both.hs
+++ b/examples/Both.hs
@@ -180,7 +180,7 @@
 -- ---------------------------------------------------------------------
 -- Deal with kind signatures
 
-data UserF (f :: * -> *) = UserF { userf_name :: String }
+data UserF (f :: T.Type -> T.Type) = UserF { userf_name :: String }
 
 test7 :: IO ()
 test7 = do
diff --git a/plugin/Compat.hs b/plugin/Compat.hs
--- a/plugin/Compat.hs
+++ b/plugin/Compat.hs
@@ -5,7 +5,14 @@
 module Compat(module Compat) where
 
 import GHC
+#if __GLASGOW_HASKELL__ < 900
 import BasicTypes
+import TcEvidence
+#else
+import GHC.Types.Basic
+import GHC.Unit.Types
+import GHC.Parser.Annotation
+#endif
 #if __GLASGOW_HASKELL__ < 810
 import HsSyn as Compat
 #else
@@ -26,11 +33,32 @@
 noE = noExtField
 #endif
 
+realSrcLoc :: SrcLoc -> Maybe RealSrcLoc
+#if __GLASGOW_HASKELL__ < 811
+realSrcLoc (RealSrcLoc x) = Just x
+#else
+realSrcLoc (RealSrcLoc x _) = Just x
+#endif
+realSrcLoc _ = Nothing
+
+#if __GLASGOW_HASKELL__ >= 900
+hsLTyVarBndrToType :: LHsTyVarBndr flag (GhcPass p) -> LHsType (GhcPass p)
+hsLTyVarBndrToType x = noL $ HsTyVar noE NotPromoted $ noL $ hsLTyVarName x
+#endif
+
 ---------------------------------------------------------------------
 -- COMMON SIGNATURES
 
+#if __GLASGOW_HASKELL__ < 811
+type Module = HsModule GhcPs
+#else
+type Module = HsModule
+#endif
+
 mkAppType :: LHsExpr GhcPs -> LHsType GhcPs -> LHsExpr GhcPs
 mkTypeAnn :: LHsExpr GhcPs -> LHsType GhcPs -> LHsExpr GhcPs
+mkFunTy :: LHsType GhcPs -> LHsType GhcPs -> LHsType GhcPs
+newFunBind :: Located RdrName -> MatchGroup GhcPs (LHsExpr GhcPs) -> HsBind GhcPs
 
 #if __GLASGOW_HASKELL__ < 807
 
@@ -46,7 +74,21 @@
 
 #endif
 
+#if __GLASGOW_HASKELL__ < 811
 
+-- GHC 8.10 and below
+mkFunTy a b = noL $ HsFunTy noE a b
+newFunBind a b = FunBind noE a b WpHole []
+
+#else
+
+-- GHC 9.0
+mkFunTy a b = noL $ HsFunTy noE (HsUnrestrictedArrow NormalSyntax) a b
+newFunBind a b = FunBind noE a b []
+
+#endif
+
+
 #if __GLASGOW_HASKELL__ < 807
 
 -- GHC 8.6
@@ -76,9 +118,16 @@
 qualifiedImplicitImport x = noL $ ImportDecl noE NoSourceText (noL x) Nothing False False
     True {- qualified -} True {- implicit -} Nothing Nothing
 
-#else
+#elif __GLASGOW_HASKELL__ < 811
 
+-- GHC 8.10
 qualifiedImplicitImport x = noL $ ImportDecl noE NoSourceText (noL x) Nothing False False
+    QualifiedPost {- qualified -} True {- implicit -} Nothing Nothing
+
+#else
+
+-- GHC 9.0
+qualifiedImplicitImport x = noL $ ImportDecl noE NoSourceText (noL x) Nothing NotBoot False
     QualifiedPost {- qualified -} True {- implicit -} Nothing Nothing
 
 #endif
diff --git a/plugin/RecordDotPreprocessor.hs b/plugin/RecordDotPreprocessor.hs
--- a/plugin/RecordDotPreprocessor.hs
+++ b/plugin/RecordDotPreprocessor.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE RecordWildCards, ViewPatterns, NamedFieldPuns, OverloadedStrings #-}
 {- HLINT ignore "Use camelCase" -}
 
@@ -8,13 +9,22 @@
 import Data.List.Extra
 import Data.Tuple.Extra
 import Compat
-import Bag
 import qualified GHC
+#if __GLASGOW_HASKELL__ < 900
+import Bag
 import qualified GhcPlugins as GHC
 import qualified PrelNames as GHC
 import SrcLoc
-import TcEvidence
+#else
+import GHC.Data.Bag
+import qualified GHC.Driver.Plugins as GHC
+import qualified GHC.Driver.Types as GHC
+import qualified GHC.Builtin.Names as GHC
+import qualified GHC.Plugins as GHC
+import GHC.Types.SrcLoc
+#endif
 
+
 ---------------------------------------------------------------------
 -- PLUGIN WRAPPER
 
@@ -43,7 +53,7 @@
 var_dot = GHC.mkRdrUnqual $ GHC.mkVarOcc "."
 
 
-onModule :: HsModule GhcPs -> HsModule GhcPs
+onModule :: Module -> Module
 onModule x = x { hsmodImports = onImports $ hsmodImports x
                , hsmodDecls = concatMap onDecl $ hsmodDecls x
                }
@@ -71,7 +81,7 @@
         typ = noL $ makeEqQualTy field (unLoc . typ')
 
         has :: LHsBindLR GhcPs GhcPs
-        has = noL $ FunBind noE (noL var_hasField) (mg1 eqn) WpHole []
+        has = noL $ newFunBind (noL var_hasField) (mg1 eqn)
             where
                 eqn = Match
                     { m_ext     = noE
@@ -88,7 +98,7 @@
                 update = RecordUpd noE (noL $ GHC.HsVar noE $ noL vR)
                     [noL $ HsRecField (noL (Unambiguous noE (rdrNameFieldOcc selector))) (noL $ GHC.HsVar noE $ noL vX) False]
                 get = mkApp
-                    (mkParen $ mkTypeAnn (noL $ GHC.HsVar noE $ rdrNameFieldOcc selector) (noL $ HsFunTy noE (noL record) (noL field)))
+                    (mkParen $ mkTypeAnn (noL $ GHC.HsVar noE $ rdrNameFieldOcc selector) (mkFunTy (noL record) (noL field)))
                     (noL $ GHC.HsVar noE $ noL vR)
 
         mg1 :: Match GhcPs (LHsExpr GhcPs) -> MatchGroup GhcPs (LHsExpr GhcPs)
@@ -224,7 +234,7 @@
 
 -- | Are the end of a and the start of b next to each other, no white space
 adjacentBy :: Int -> Located a -> Located b -> Bool
-adjacentBy i (L (srcSpanEnd -> RealSrcLoc a) _) (L (srcSpanStart -> RealSrcLoc b) _) =
+adjacentBy i (L (realSrcLoc . srcSpanEnd -> Just a) _) (L (realSrcLoc . srcSpanStart -> Just b) _) =
     srcLocFile a == srcLocFile b &&
     srcLocLine a == srcLocLine b &&
     srcLocCol a + i == srcLocCol b
diff --git a/preprocessor/Edit.hs b/preprocessor/Edit.hs
--- a/preprocessor/Edit.hs
+++ b/preprocessor/Edit.hs
@@ -54,6 +54,14 @@
 isCtor (Item x) = any isUpper $ take 1 $ lexeme x
 isCtor _ = False
 
+-- | This test does not check that the @quoter@ name is a qualified identifier,
+-- instead relying on lack of whitespace in the opener and existence of a paired
+-- closed (@|]@)
+isQuasiQuotation :: PL -> Bool
+isQuasiQuotation (Paren (L "[") inner@(Item qq : Item (L "|") : _) (L "]"))
+    | Item close@(L "|") <- last inner = null (whitespace qq) && null (whitespace close)
+isQuasiQuotation _ = False
+
 isField (x:_) = x == '_' || isLower x
 isField _ = False
 
@@ -95,6 +103,9 @@
 
 
 editLoop :: [PL] -> [PL]
+
+--  Leave quasiquotations alone
+editLoop (p : ps) | isQuasiQuotation p = p : editLoop ps
 
 -- | a.b.c ==> getField @'(b,c) a
 editLoop (NoW e : (spanFields -> (fields@(_:_), whitespace, rest)))
diff --git a/record-dot-preprocessor.cabal b/record-dot-preprocessor.cabal
--- a/record-dot-preprocessor.cabal
+++ b/record-dot-preprocessor.cabal
@@ -1,14 +1,14 @@
 cabal-version:      >= 1.18
 build-type:         Simple
 name:               record-dot-preprocessor
-version:            0.2.7
+version:            0.2.8
 license:            BSD3
 x-license:          BSD-3-Clause OR Apache-2.0
 license-file:       LICENSE
 category:           Development
 author:             Neil Mitchell <ndmitchell@gmail.com>
 maintainer:         Neil Mitchell <ndmitchell@gmail.com>
-copyright:          Neil Mitchell 2018-2020
+copyright:          Neil Mitchell 2018-2021
 synopsis:           Preprocessor to allow record.field syntax
 description:
     In almost every programming language @a.b@ will get the @b@ field from the @a@ data type, and many different data types can have a @b@ field.
@@ -19,7 +19,7 @@
 extra-doc-files:
     README.md
     CHANGES.txt
-tested-with:        GHC==8.10, GHC==8.8, GHC==8.6, GHC==8.4
+tested-with:        GHC==9.0, GHC==8.10, GHC==8.8, GHC==8.6
 extra-source-files:
     examples/Both.hs
     examples/Both2.hs
