language-rust-0.2.0.27: Parsing and pretty printing of Rust code

Copyright(c) Alec Theriault 2017-2018
LicenseBSD-style
Maintaineralec.theriault@gmail.com
Stabilityexperimental
Portabilityportable
Safe HaskellNone
LanguageHaskell2010

Language.Rust.Pretty

Contents

Description

This module provides functions for turning ASTs into values of type Doc. These values can then be rendered into concrete string types using functions from the prettyprinter package. This has some advantages over printing plain old strings:

  • Backend independent: you can use a variety of existing backends to efficiently render to all sorts of formats like Text, String, HTML, and terminal.
  • Dynamic layouts: the AST will render differently depending on the desired page width

    >>> :set -XTypeApplications -XOverloadedStrings
    >>> import Language.Rust.Parser
    >>> import Data.Text.Prettyprint.Doc.Util ( putDocW )
    >>> let src = parse' @(SourceFile Span) "fn foo(x: i32, y: i32, z: i32) -> i32 { x - y + z }"
    >>> let doc = pretty' src <> "\n"
    >>> putDocW 80 doc
    fn foo(x: i32, y: i32, z: i32) -> i32 {
        x - y + z
    }
    >>> putDocW 10 doc
    fn foo(
      x: i32,
      y: i32,
      z: i32,
    ) -> i32 {
      x - y + z
    }
    
  • Annotations: Depending on the backend you are using to render the Doc, annotations can determine colours, styling, links, etc.

The examples below assume the following GHCi flag and import:

>>> :set -XOverloadedStrings
>>> import Language.Rust.Syntax.AST

Synopsis

Printing

pretty :: (Resolve a, Pretty a) => a -> Either ResolveFail (Doc b) Source #

Resolve (see the Resolve typeclass) and pretty print something.

>>> let one = Lit [] (Int Dec 1 Unsuffixed ()) ()
>>> let two = Lit [] (Int Dec 2 Unsuffixed ()) ()
>>> let three = Lit [] (Int Dec 3 Unsuffixed ()) ()
>>> let bogusVar = PathExpr [] Nothing (Path False [PathSegment "let" Nothing ()] ()) ()
>>> pretty (Binary [] MulOp (Binary [] AddOp one two ()) three ())
Right (1 + 2) * 3
>>> pretty (Binary [] AddOp one bogusVar ())
Left (invalid AST (identifier `let' is a keyword))

pretty' :: (Resolve a, Pretty a) => a -> Doc b Source #

Same as pretty, but throws a ResolveFail exception on invalid ASTs. This function is intended for situations in which you are already stuck catching exceptions - otherwise you should prefer pretty.

>>> let one = Lit [] (Int Dec 1 Unsuffixed ()) ()
>>> let two = Lit [] (Int Dec 2 Unsuffixed ()) ()
>>> let three = Lit [] (Int Dec 3 Unsuffixed ()) ()
>>> let bogusVar = PathExpr [] Nothing (Path False [PathSegment "let" Nothing ()] ()) ()
>>> pretty' (Binary [] MulOp (Binary [] AddOp one two ()) three ())
(1 + 2) * 3
>>> pretty' (Binary [] AddOp one bogusVar ())
*** Exception: invalid AST (identifier `let' is a keyword))

prettyAnnotated :: (Resolve (f a), PrettyAnnotated f) => f a -> Either ResolveFail (Doc a) Source #

Resolve (see the Resolve typeclass) and pretty print something with annotations. Read more about annotations in Data.Text.Prettyprint.Doc.

fmap Data.Text.Prettyprint.Doc.noAnnotate . prettyAnnotated = pretty

prettyAnnotated' :: (Resolve (f a), PrettyAnnotated f) => f a -> Doc a Source #

Same as prettyAnnotated, but throws a ResolveFail exception on invalid ASTs. This function is intended for situations in which you are already stuck catching exceptions - otherwise you should prefer prettyAnnotated.

Data.Text.Prettyprint.Doc.noAnnotate . prettyAnnotated' = pretty'

writeSourceFile :: (Monoid a, Typeable a) => Handle -> SourceFile a -> IO () Source #

Given a handle to a file, write a SourceFile in with a desired width of 100 characters.

writeTokens :: Handle -> [Spanned Token] -> IO () Source #

Given a handle to a file, write a SourceFile in with a desired width of 100 characters.

The Span associated with the tokens (if present) will be used as a hint for laying out and spacing the tokens.

class Pretty a where Source #

Describes things that can be pretty printed.

Minimal complete definition

prettyUnresolved

Methods

prettyUnresolved :: a -> Doc b Source #

Pretty print the given value without resolving it.

Instances

Pretty Ident Source # 
Pretty Span Source # 
Pretty Position Source # 
Pretty Token Source # 
Pretty LitTok Source # 
Pretty Unsafety Source # 
Pretty UnOp Source # 
Pretty TokenTree Source # 
Pretty TokenStream Source # 
Pretty RangeLimits Source # 
Pretty Mutability Source # 
Pretty Suffix Source # 
Pretty ImplPolarity Source # 
Pretty BindingMode Source # 
Pretty BinOp Source # 
Pretty Abi Source # 
Pretty (WherePredicate a) Source # 
Pretty (WhereClause a) Source # 
Pretty (Visibility a) Source # 
Pretty (UseTree a) Source # 
Pretty (Variant a) Source # 
Pretty (TyParamBound a) Source # 
Pretty (TyParam a) Source # 
Pretty (Ty a) Source # 

Methods

prettyUnresolved :: Ty a -> Doc b Source #

Pretty (TraitRef a) Source # 
Pretty (TraitItem a) Source # 
Pretty (StructField a) Source # 
Pretty (Stmt a) Source # 

Methods

prettyUnresolved :: Stmt a -> Doc b Source #

Pretty (PolyTraitRef a) Source # 
Pretty (Path a) Source # 

Methods

prettyUnresolved :: Path a -> Doc b Source #

Pretty (Pat a) Source # 

Methods

prettyUnresolved :: Pat a -> Doc b Source #

Pretty (Nonterminal a) Source # 
Pretty (Mac a) Source # 

Methods

prettyUnresolved :: Mac a -> Doc b Source #

Pretty (Lit a) Source # 

Methods

prettyUnresolved :: Lit a -> Doc b Source #

Pretty (SourceFile a) Source # 
Pretty (LifetimeDef a) Source # 
Pretty (Lifetime a) Source # 
Pretty (Item a) Source # 

Methods

prettyUnresolved :: Item a -> Doc b Source #

Pretty (ImplItem a) Source # 
Pretty (Generics a) Source # 
Pretty (ForeignItem a) Source # 
Pretty (FnDecl a) Source # 
Pretty (FieldPat a) Source # 
Pretty (Field a) Source # 

Methods

prettyUnresolved :: Field a -> Doc b Source #

Pretty (Expr a) Source # 

Methods

prettyUnresolved :: Expr a -> Doc b Source #

Pretty (Block a) Source # 

Methods

prettyUnresolved :: Block a -> Doc b Source #

Pretty (Attribute a) Source # 

class PrettyAnnotated p where Source #

Similar to Pretty, but for types which are parametrized over an annotation type.

Minimal complete definition

prettyAnnUnresolved

Methods

prettyAnnUnresolved :: p a -> Doc a Source #

Pretty print the given value without resolving it, adding annotations in the Doc whenever possible.

Instances

PrettyAnnotated WherePredicate Source # 
PrettyAnnotated WhereClause Source # 
PrettyAnnotated Visibility Source # 
PrettyAnnotated UseTree Source # 
PrettyAnnotated Variant Source # 
PrettyAnnotated TyParamBound Source # 
PrettyAnnotated TyParam Source # 
PrettyAnnotated Ty Source # 

Methods

prettyAnnUnresolved :: Ty a -> Doc a Source #

PrettyAnnotated TraitRef Source # 
PrettyAnnotated TraitItem Source # 
PrettyAnnotated StructField Source # 
PrettyAnnotated Stmt Source # 
PrettyAnnotated PolyTraitRef Source # 
PrettyAnnotated Path Source # 
PrettyAnnotated Pat Source # 
PrettyAnnotated Nonterminal Source # 
PrettyAnnotated Mac Source # 
PrettyAnnotated Lit Source # 
PrettyAnnotated SourceFile Source # 
PrettyAnnotated LifetimeDef Source # 
PrettyAnnotated Lifetime Source # 
PrettyAnnotated Item Source # 
PrettyAnnotated ImplItem Source # 
PrettyAnnotated Generics Source # 
PrettyAnnotated ForeignItem Source # 
PrettyAnnotated FnDecl Source # 
PrettyAnnotated FieldPat Source # 
PrettyAnnotated Field Source # 
PrettyAnnotated Expr Source # 
PrettyAnnotated Block Source # 
PrettyAnnotated Attribute Source #

This instance prints attributes inline

data Doc ann :: * -> * #

The abstract data type Doc ann represents pretty documents that have been annotated with data of type ann.

More specifically, a value of type Doc represents a non-empty set of possible layouts of a document. The layout functions select one of these possibilities, taking into account things like the width of the output document.

The annotation is an arbitrary piece of data associated with (part of) a document. Annotations may be used by the rendering backends in order to display output differently, such as

  • color information (e.g. when rendering to the terminal)
  • mouseover text (e.g. when rendering to rich HTML)
  • whether to show something or not (to allow simple or detailed versions)

The simplest way to display a Doc is via the Show class.

>>> putStrLn (show (vsep ["hello", "world"]))
hello
world

Instances

Functor Doc

Alter the document’s annotations.

This instance makes Doc more flexible (because it can be used in Functor-polymorphic values), but fmap is much less readable compared to using reAnnotate in code that only works for Doc anyway. Consider using the latter when the type does not matter.

Methods

fmap :: (a -> b) -> Doc a -> Doc b #

(<$) :: a -> Doc b -> Doc a #

Show (Doc ann)

(show doc) prettyprints document doc with defaultLayoutOptions, ignoring all annotations.

Methods

showsPrec :: Int -> Doc ann -> ShowS #

show :: Doc ann -> String #

showList :: [Doc ann] -> ShowS #

IsString (Doc ann)
>>> pretty ("hello\nworld")
hello
world

This instance uses the Pretty Text instance, and uses the same newline to line conversion.

Methods

fromString :: String -> Doc ann #

Generic (Doc ann) 

Associated Types

type Rep (Doc ann) :: * -> * #

Methods

from :: Doc ann -> Rep (Doc ann) x #

to :: Rep (Doc ann) x -> Doc ann #

Semigroup (Doc ann)
x <> y = hcat [x, y]
>>> "hello" <> "world" :: Doc ann
helloworld

Methods

(<>) :: Doc ann -> Doc ann -> Doc ann #

sconcat :: NonEmpty (Doc ann) -> Doc ann #

stimes :: Integral b => b -> Doc ann -> Doc ann #

Monoid (Doc ann)
mempty = emptyDoc
mconcat = hcat
>>> mappend "hello" "world" :: Doc ann
helloworld

Methods

mempty :: Doc ann #

mappend :: Doc ann -> Doc ann -> Doc ann #

mconcat :: [Doc ann] -> Doc ann #

type Rep (Doc ann) 
type Rep (Doc ann) = D1 * (MetaData "Doc" "Data.Text.Prettyprint.Doc.Internal" "prettyprinter-1.2.0.1-7ZuCLms8xajGeu88e33QHe" False) ((:+:) * ((:+:) * ((:+:) * (C1 * (MetaCons "Fail" PrefixI False) (U1 *)) ((:+:) * (C1 * (MetaCons "Empty" PrefixI False) (U1 *)) (C1 * (MetaCons "Char" PrefixI False) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 * Char))))) ((:+:) * (C1 * (MetaCons "Text" PrefixI False) ((:*:) * (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 * Int)) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 * Text)))) ((:+:) * (C1 * (MetaCons "Line" PrefixI False) (U1 *)) (C1 * (MetaCons "FlatAlt" PrefixI False) ((:*:) * (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Doc ann))) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Doc ann)))))))) ((:+:) * ((:+:) * (C1 * (MetaCons "Cat" PrefixI False) ((:*:) * (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Doc ann))) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Doc ann))))) ((:+:) * (C1 * (MetaCons "Nest" PrefixI False) ((:*:) * (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 * Int)) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Doc ann))))) (C1 * (MetaCons "Union" PrefixI False) ((:*:) * (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Doc ann))) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Doc ann))))))) ((:+:) * ((:+:) * (C1 * (MetaCons "Column" PrefixI False) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Int -> Doc ann)))) (C1 * (MetaCons "WithPageWidth" PrefixI False) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (PageWidth -> Doc ann))))) ((:+:) * (C1 * (MetaCons "Nesting" PrefixI False) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Int -> Doc ann)))) (C1 * (MetaCons "Annotated" PrefixI False) ((:*:) * (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * ann)) (S1 * (MetaSel (Nothing Symbol) NoSourceUnpackedness NoSourceStrictness DecidedLazy) (Rec0 * (Doc ann)))))))))

Resolving

class Resolve a where Source #

Since it is possible to have well-typed Haskell expressions which represent invalid Rust ASTs, it is convenient to fix, warn, or fail ASTs before printing them. The Resolve typeclass provides such a facility.

A non-exhaustive list of the more obvious issues it covers:

  • missing parens
  • invalid identifiers
  • invalid paths (for example, generic arguments on a module path)
  • inner attributes on things that support only outer attributes (and vice-versa)

Minimal complete definition

resolveM

Methods

resolve :: a -> Either ResolveFail a Source #

Convert some value to its resolved form. Informally, resolving a value involves checking that its invariants hold and, if they don't, report an error message or adjust the value so that the invariant holds.

A value of a type satsifying Parse and Pretty is resolved if parse . pretty is an identity operation on it. We further expect that resolve be an identity operation on any output of parse.

resolve' :: a -> a Source #

Same as resolve, but throws a ResolveFail exception if it cannot resolve. Although this function should not be used, it summarizes nicely the laws around Resolve:

parse' . pretty' . resolve' == id
resolve' . parse' = parse'

resolveVerbose :: a -> (a, Severity, [Issue]) Source #

Run resolution and get back the altered syntax tree, the highest Severity of issues, and the list of issues found. This allows you to see what corrections were applied to the tree. If the output severity is Error, the syntax tree returned will still be invalid.

Instances

Resolve Ident Source # 
Resolve TokenTree Source # 
Resolve TokenStream Source # 
(Typeable * a, Monoid a) => Resolve (WherePredicate a) Source # 
(Typeable * a, Monoid a) => Resolve (WhereClause a) Source # 
(Typeable * a, Monoid a) => Resolve (Visibility a) Source # 
(Typeable * a, Monoid a) => Resolve (UseTree a) Source # 
(Typeable * a, Monoid a) => Resolve (VariantData a) Source # 
(Typeable * a, Monoid a) => Resolve (Variant a) Source # 
(Typeable * a, Monoid a) => Resolve (TyParamBound a) Source # 
(Typeable * a, Monoid a) => Resolve (TyParam a) Source # 
(Typeable * a, Monoid a) => Resolve (Ty a) Source # 

Methods

resolve :: Ty a -> Either ResolveFail (Ty a) Source #

resolve' :: Ty a -> Ty a Source #

resolveVerbose :: Ty a -> (Ty a, Severity, [Issue]) Source #

resolveM :: Ty a -> ResolveM (Ty a)

(Typeable * a, Monoid a) => Resolve (TraitRef a) Source # 
(Typeable * a, Monoid a) => Resolve (TraitItem a) Source # 
(Typeable * a, Monoid a) => Resolve (StructField a) Source # 
(Typeable * a, Monoid a) => Resolve (Stmt a) Source # 

Methods

resolve :: Stmt a -> Either ResolveFail (Stmt a) Source #

resolve' :: Stmt a -> Stmt a Source #

resolveVerbose :: Stmt a -> (Stmt a, Severity, [Issue]) Source #

resolveM :: Stmt a -> ResolveM (Stmt a)

(Typeable * a, Monoid a) => Resolve (QSelf a) Source # 
(Typeable * a, Monoid a) => Resolve (PolyTraitRef a) Source # 
(Typeable * a, Monoid a) => Resolve (PathParameters a) Source # 
(Typeable * a, Monoid a) => Resolve (Path a) Source #

There are three potential instances for resolving a path (depending on what type it is). The Resolve instance for Path will let through any path.

Methods

resolve :: Path a -> Either ResolveFail (Path a) Source #

resolve' :: Path a -> Path a Source #

resolveVerbose :: Path a -> (Path a, Severity, [Issue]) Source #

resolveM :: Path a -> ResolveM (Path a)

(Typeable * a, Monoid a) => Resolve (Pat a) Source # 

Methods

resolve :: Pat a -> Either ResolveFail (Pat a) Source #

resolve' :: Pat a -> Pat a Source #

resolveVerbose :: Pat a -> (Pat a, Severity, [Issue]) Source #

resolveM :: Pat a -> ResolveM (Pat a)

(Typeable * a, Monoid a) => Resolve (MethodSig a) Source # 
(Typeable * a, Monoid a) => Resolve (Mac a) Source # 

Methods

resolve :: Mac a -> Either ResolveFail (Mac a) Source #

resolve' :: Mac a -> Mac a Source #

resolveVerbose :: Mac a -> (Mac a, Severity, [Issue]) Source #

resolveM :: Mac a -> ResolveM (Mac a)

Resolve (Lit a) Source # 

Methods

resolve :: Lit a -> Either ResolveFail (Lit a) Source #

resolve' :: Lit a -> Lit a Source #

resolveVerbose :: Lit a -> (Lit a, Severity, [Issue]) Source #

resolveM :: Lit a -> ResolveM (Lit a)

(Typeable * a, Monoid a) => Resolve (SourceFile a) Source # 
(Typeable * a, Monoid a) => Resolve (LifetimeDef a) Source # 
Typeable * a => Resolve (Lifetime a) Source # 
(Typeable * a, Monoid a) => Resolve (Item a) Source # 

Methods

resolve :: Item a -> Either ResolveFail (Item a) Source #

resolve' :: Item a -> Item a Source #

resolveVerbose :: Item a -> (Item a, Severity, [Issue]) Source #

resolveM :: Item a -> ResolveM (Item a)

(Typeable * a, Monoid a) => Resolve (ImplItem a) Source # 
(Typeable * a, Monoid a) => Resolve (Generics a) Source # 
(Typeable * a, Monoid a) => Resolve (ForeignItem a) Source # 
(Typeable * a, Monoid a) => Resolve (FnDecl a) Source # 
(Typeable * a, Monoid a) => Resolve (FieldPat a) Source # 
(Typeable * a, Monoid a) => Resolve (Field a) Source # 
(Typeable * a, Monoid a) => Resolve (Expr a) Source # 

Methods

resolve :: Expr a -> Either ResolveFail (Expr a) Source #

resolve' :: Expr a -> Expr a Source #

resolveVerbose :: Expr a -> (Expr a, Severity, [Issue]) Source #

resolveM :: Expr a -> ResolveM (Expr a)

(Typeable * a, Monoid a) => Resolve (Block a) Source # 
(Typeable * a, Monoid a) => Resolve (Attribute a) Source # 
(Typeable * a, Monoid a) => Resolve (Arm a) Source # 

Methods

resolve :: Arm a -> Either ResolveFail (Arm a) Source #

resolve' :: Arm a -> Arm a Source #

resolveVerbose :: Arm a -> (Arm a, Severity, [Issue]) Source #

resolveM :: Arm a -> ResolveM (Arm a)

(Typeable * a, Monoid a) => Resolve (Arg a) Source # 

Methods

resolve :: Arg a -> Either ResolveFail (Arg a) Source #

resolve' :: Arg a -> Arg a Source #

resolveVerbose :: Arg a -> (Arg a, Severity, [Issue]) Source #

resolveM :: Arg a -> ResolveM (Arg a)

Error reporting

data ResolveFail Source #

Exceptions that occur during resolving. Unlike parse errors, we don't have positional information. Instead, we try to provide some context via a list of syntax trees which let you "zoom out" from the problematic node.

Constructors

ResolveFail [Dynamic] String 

data Issue Source #

Localized information about an issue in a syntax tree.

Constructors

Issue 

Fields

  • description :: String

    Description of the issue

  • severity :: !Severity

    Severity of the issue

  • location :: [Dynamic]

    The first element in this list is the syntax tree where the issue occurs. The next elements are increasingly zoomed out syntax trees centered on the first element. In lieu of positional information, this provides a next best way of debugging exactly where the problem is.

Instances

data Severity Source #

Diagnostic for how severe an Issue is.

Constructors

Clean

Everything is normal (this variant is returned when there was nothing to resolve)

Warning

There is something fishy looking (AST is valid, but may not be what you expect)

Correction

The AST was invalid, but in a way that could be corrected

Error

The AST was invalid in some way that could not be automatically fixed