{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -- | Functions for manipulating translation units. -- -- To start analyzing a translation unit, call 'Index.withNew' to create a new index and -- call 'withParsed' in the callback. Inside the callback for 'withParsed', you'll have -- access to a 'FFI.TranslationUnit' value; you can call 'getCursor' to turn that into an -- AST cursor which can be traversed using the functions in "Clang.Cursor". -- -- This module is intended to be imported qualified. module Clang.TranslationUnit ( -- * Creating a translation unit withParsed , withLoaded , withReparsing , FFI.TranslationUnitFlags , FFI.ReparseFlags , ReparsingCallback , ParseContinuation(..) -- * Saving , save , FFI.SaveTranslationUnitFlags -- AST traversal and metadata , getCursor , getDiagnosticSet , getSpelling ) where import Data.Traversable import Control.Monad.IO.Class import Data.Maybe (fromMaybe) import qualified Data.Vector as DV -- import System.FilePath (()) import Clang.Internal.BitFlags import Clang.Internal.Monad import qualified Clang.Internal.FFI as FFI -- import Paths_LibClang (getDataFileName) -- | Creates a translation unit by parsing source code. -- -- Note that not all command line arguments which are accepted by the \'clang\' frontend can -- be used here. You should avoid passing \'-c\', \'-o\', \'-fpch-deps\', and the various -- \'-M\' options. If you provide a 'FilePath' when calling 'withParsed', also be sure not -- to provide the filename in the command line arguments as well. withParsed :: ClangBase m => FFI.Index s' -- ^ The index into which the translation unit should be loaded. -> Maybe FilePath -- ^ The file to load, or 'Nothing' if the file is specified in -- the command line arguments. -> [String] -- ^ The command line arguments libclang should use when compiling this -- file. Most arguments which you'd use with the \'clang\' frontend -- are accepted. -> DV.Vector FFI.UnsavedFile -- ^ Unsaved files which may be needed to parse this -- translation unit. This may include the source -- file itself or any file it includes. -> [FFI.TranslationUnitFlags] -- ^ Flags that affect the processing of this -- translation unit. -> (forall s. FFI.TranslationUnit s -> ClangT s m a) -- ^ A callback. -> ClangT s' m (Maybe a) -- ^ The return value of the callback, or 'Nothing' -- if the file couldn't be parsed. withParsed idx mayPath args ufs flags f = do -- liftIO $ FFI.setClangResourcesPath idx =<< clangResourcesPath mayTU <- FFI.parseTranslationUnit idx mayPath args ufs (orFlags flags) traverse go mayTU where go tu = clangScope $ f =<< fromOuterScope tu -- | Creates a translation unit by loading a saved AST file. -- -- Such an AST file can be created using 'save'. withLoaded :: ClangBase m => FFI.Index s' -- ^ The index into which the translation unit should be loaded. -> FilePath -- ^ The file to load. -> (forall s. FFI.TranslationUnit s -> ClangT s m a) -- ^ A callback. -> ClangT s' m a withLoaded idx path f = do -- liftIO $ FFI.setClangResourcesPath idx =<< clangResourcesPath f =<< FFI.createTranslationUnit idx path -- | Creates a translation unit by parsing source code. -- -- This works like 'withParsed', except that the translation unit can be reparsed over and over -- again by returning a 'Reparse' value from the callback. This is useful for interactive -- analyses like code completion. Processing can be stopped by returning a 'ParseComplete' value. withReparsing :: ClangBase m => FFI.Index s' -- ^ The index into which the translation unit should be loaded. -> Maybe FilePath -- ^ The file to load, or 'Nothing' if the file is specified in -- the command line arguments. -> [String] -- ^ The command line arguments libclang should use when compiling -- this file. Most arguments which you'd use with the \'clang\' -- frontend are accepted. -> DV.Vector FFI.UnsavedFile -- ^ Unsaved files which may be needed to parse this -- translation unit. This may include the source -- file itself or any file it includes. -> [FFI.TranslationUnitFlags] -- ^ Flags that affect the processing of this -- translation unit. -> ReparsingCallback m r -- ^ A callback which uses the translation unit. May be -- called many times depending on the return value. -- See 'ParseContinuation' for more information. -> ClangT s' m (Maybe r) -- ^ The return value of the callback, as passed to -- 'ParseComplete', or 'Nothing' if the file could -- not be parsed. withReparsing idx mayPath args ufs flags f = do -- liftIO $ FFI.setClangResourcesPath idx =<< clangResourcesPath mayTU <- FFI.parseTranslationUnit idx mayPath args ufs (orFlags flags) case mayTU of Nothing -> return Nothing Just tu -> iterReparse f tu iterReparse :: ClangBase m => ReparsingCallback m r -> FFI.TranslationUnit s' -> ClangT s' m (Maybe r) iterReparse f tu = do cont <- clangScope $ f =<< fromOuterScope tu case cont of Reparse nextF ufs mayFlags -> do res <- FFI.reparseTranslationUnit tu ufs (makeFlags mayFlags) if res then iterReparse nextF tu else return Nothing ParseComplete finalVal -> return $ Just finalVal where makeFlags = orFlags . (fromMaybe [FFI.DefaultReparseFlags]) -- | A callback for use with 'withReparsing'. type ReparsingCallback m r = forall s. FFI.TranslationUnit s -> ClangT s m (ParseContinuation m r) -- | A continuation returned by a 'ReparsingCallback'. data ParseContinuation m r -- | 'Reparse' signals that the translation unit should be reparsed. It contains a callback -- which will be called with the updated translation unit after reparsing, a 'DV.Vector' of -- unsaved files which may be needed to reparse, and a set of flags affecting reparsing. The -- default reparsing flags can be requested by specifying 'Nothing'. = Reparse (ReparsingCallback m r) (DV.Vector FFI.UnsavedFile) (Maybe [FFI.ReparseFlags]) -- | 'ParseComplete' signals that processing is finished. It contains a final result which -- will be returned by 'withReparsing'. | ParseComplete r -- clangResourcesPath :: IO FilePath -- clangResourcesPath = -- getDataFileName $ "build" "out" "lib" "clang" "3.4" -- | Saves a translation unit as an AST file with can be loaded later using 'withLoaded'. save :: ClangBase m => FFI.TranslationUnit s' -- ^ The translation unit to save. -> FilePath -- ^ The filename to save to. -> Maybe [FFI.SaveTranslationUnitFlags] -- ^ Flags that affect saving, or 'Nothing' for -- the default set of flags. -> ClangT s m Bool save t fname mayFlags = liftIO $ FFI.saveTranslationUnit t fname (orFlags flags) where flags = fromMaybe [FFI.DefaultSaveTranslationUnitFlags] mayFlags {- -- | Reparses the provided translation unit using the same command line arguments -- that were originally used to parse it. If the file has changed on disk, or if -- the unsaved files have changed, those changes will become visible. -- -- Note that 'reparse' invalidates all cursors and source locations that refer into -- the reparsed translation unit. This makes it unsafe. However, 'reparse' can be -- more efficient than calling 'withParsed' a second time. reparse :: ClangBase m => FFI.TranslationUnit s' -- ^ The translation unit to reparse. -> DV.Vector FFI.UnsavedFile -- ^ Unsaved files which may be needed to reparse -- this translation unit. -> Maybe [FFI.ReparseFlags] -- ^ Flags that affect reparsing, or 'Nothing' for the -- default set of flags. -> ClangT s m Bool reparse t ufs mayFlags = FFI.reparseTranslationUnit t ufs (orFlags flags) where flags = fromMaybe [FFI.DefaultReparseFlags] mayFlags -} -- | Retrieve the cursor associated with this translation unit. This cursor is the root of -- this translation unit's AST; you can begin exploring the AST further using the functions -- in "Clang.Cursor". getCursor :: ClangBase m => FFI.TranslationUnit s' -> ClangT s m (FFI.Cursor s) getCursor tu = liftIO $ FFI.getTranslationUnitCursor mkProxy tu -- | Retrieve the complete set of diagnostics associated with the given translation unit. getDiagnosticSet :: ClangBase m => FFI.TranslationUnit s'-> ClangT s m (FFI.DiagnosticSet s) getDiagnosticSet = FFI.getDiagnosticSetFromTU -- | Retrieve a textual representation of this translation unit. getSpelling :: ClangBase m => FFI.TranslationUnit s' -> ClangT s m (FFI.ClangString s) getSpelling = FFI.getTranslationUnitSpelling