Skip to content

Commit

Permalink
Add documentation for the Clang.Diagnostic module. #23
Browse files Browse the repository at this point in the history
  • Loading branch information
sethfowler committed May 5, 2014
1 parent a4e5e79 commit 81bea3c
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 83 deletions.
3 changes: 0 additions & 3 deletions src/Clang.hs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ module Clang (
, FFI.Version(..)
, FFI.PlatformAvailability(..)
, FFI.PlatformAvailabilityInfo(..)
, FFI.DiagnosticSeverity(..)
, FFI.DiagnosticDisplayOptions(..)
, FFI.LoadDiagError(..)
, FFI.Diagnostic
, FFI.DiagnosticSet
, FFI.File
Expand Down
158 changes: 111 additions & 47 deletions src/Clang/Diagnostic.hs
Original file line number Diff line number Diff line change
@@ -1,87 +1,151 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}

-- | Functions for manipulating 'FFI.Diagnostic's, which represent
-- diagnostics - warnings, errors, and the like - produced by libclang.
--
-- 'FFI.Diagnostic's are grouped into 'FFI.DiagnosticSet's. You
-- usually obtain a 'FFI.DiagnosticSet' from a 'FFI.TranslationUnit'
-- using 'Clang.TranslationUnit.getDiagnosticSet', and then analyze it
-- using the functions in this module.
--
-- This module is intended to be imported qualified.
module Clang.Diagnostic
( getDiagnostics
, getChildDiagnostics
, defaultDisplayOptions
, formatDiagnostic

, loadDiagnostics
, getDiagnosticSetFromTU
, getDiagnosticsInSet
(
-- * Diagnostic sets
getElements

-- * Diagnostics
, getChildren
, getSeverity
, FFI.Severity(..)
, getSpelling
, getOptions
, getCategory

, getCategoryName
, getCategoryId
, getRanges
, getFixIts

, getCategoryName
-- * Rendering diagnostics
, format
, FFI.DisplayOptions(..)

-- * Deserializing diagnostics
, load
, FFI.LoadError(..)
) where

import Control.Applicative
import Control.Monad.IO.Class

import Clang.Internal.BitFlags
import qualified Clang.Internal.FFI as FFI
import Clang.Internal.Monad

getDiagnostics :: ClangBase m => FFI.TranslationUnit s' -> ClangT s m [FFI.Diagnostic s]
getDiagnostics t = do
numDiags <- liftIO $ FFI.getNumDiagnostics t
mapM (FFI.getDiagnostic t) [0..(numDiags - 1)]

getChildDiagnostics :: ClangBase m => FFI.Diagnostic s' -> ClangT s m (FFI.DiagnosticSet s)
getChildDiagnostics = FFI.getChildDiagnostics

formatDiagnostic :: ClangBase m => Maybe [FFI.DiagnosticDisplayOptions] -> FFI.Diagnostic s'
-> ClangT s m (FFI.ClangString s)
formatDiagnostic Nothing diag = FFI.formatDiagnostic diag =<<
liftIO FFI.defaultDiagnosticDisplayOptions
formatDiagnostic (Just opts) diag = FFI.formatDiagnostic diag (orFlags opts)

loadDiagnostics :: ClangBase m => FilePath
-> ClangT s m (Either (FFI.LoadDiagError, FFI.ClangString s) (FFI.DiagnosticSet s))
loadDiagnostics = FFI.loadDiagnostics

getDiagnosticSetFromTU :: ClangBase m => FFI.TranslationUnit s'
-> ClangT s m (FFI.DiagnosticSet s)
getDiagnosticSetFromTU = FFI.getDiagnosticSetFromTU

getDiagnosticsInSet :: ClangBase m => FFI.DiagnosticSet s' -> ClangT s m [FFI.Diagnostic s]
getDiagnosticsInSet ds = do
-- | Retrieve the diagnostics contained in the given 'FFI.DiagnosticSet'.
--
-- You can obtain a 'FFI.DiagnosticSet' from a 'FFI.TranslationUnit'
-- using 'Clang.TranslationUnit.getDiagnosticSet', or by deserializing
-- it from a file using 'load'.
getElements :: ClangBase m => FFI.DiagnosticSet s' -> ClangT s m [FFI.Diagnostic s]
getElements ds = do
numDiags <- liftIO $ FFI.getNumDiagnosticsInSet ds
mapM (FFI.getDiagnosticInSet ds) [0..(numDiags - 1)]

getSeverity :: ClangBase m => FFI.Diagnostic s' -> ClangT s m FFI.DiagnosticSeverity
-- | Get the child diagnostics of the given diagnostic.
getChildren :: ClangBase m => FFI.Diagnostic s' -> ClangT s m (FFI.DiagnosticSet s)
getChildren = FFI.getChildDiagnostics

-- | Determine the severity of the given diagnostic.
getSeverity :: ClangBase m => FFI.Diagnostic s' -> ClangT s m FFI.Severity
getSeverity d = liftIO $ FFI.getDiagnosticSeverity d

-- | Retrieve the text of the given diagnostic.
getSpelling :: ClangBase m => FFI.Diagnostic s' -> ClangT s m (FFI.ClangString s)
getSpelling = FFI.getDiagnosticSpelling

getOptions :: ClangBase m => FFI.Diagnostic s' -> ClangT s m (FFI.ClangString s, FFI.ClangString s)
-- | Retrieve the name of the command-line option that enabled this diagnostic.
getOptions :: ClangBase m
=> FFI.Diagnostic s' -- ^ The diagnostic in question.
-> ClangT s m (FFI.ClangString s, FFI.ClangString s) -- ^ The options controlling
-- this diagnostic. The
-- first member of the pair
-- is the option to enable
-- this diagnostic. The
-- second member is the
-- option to disable it.
getOptions = FFI.getDiagnosticOption

defaultDisplayOptions :: ClangBase m => ClangT s m [FFI.DiagnosticDisplayOptions]
defaultDisplayOptions = unFlags <$> liftIO FFI.defaultDiagnosticDisplayOptions

getCategory :: ClangBase m => FFI.Diagnostic s' -> ClangT s m Int
getCategory d = liftIO $ FFI.getDiagnosticCategory d
-- | Get the name of the diagnostic category for the given diagnostic.
--
-- Diagnostics are categorized into groups along with other,
-- related diagnostics (e.g., diagnostics under the same warning
-- flag).
getCategoryName :: ClangBase m => FFI.Diagnostic s' -> ClangT s m (FFI.ClangString s)
getCategoryName = FFI.getDiagnosticCategoryText

-- | Retrieve the category id for this diagnostic.
--
-- For display to the user, use 'getCategoryName'.
getCategoryId :: ClangBase m => FFI.Diagnostic s' -> ClangT s m Int
getCategoryId d = liftIO $ FFI.getDiagnosticCategory d

-- | Retrieve the source ranges associated with this diagnostic.
--
-- A diagnostic's source ranges highlight important elements in the
-- source code. On the command line, Clang displays source ranges by
-- underlining them with '~' characters.
getRanges :: ClangBase m => FFI.Diagnostic s' -> ClangT s m [FFI.SourceRange s]
getRanges d = liftIO $ do
numRanges <- FFI.getDiagnosticNumRanges d
mapM (FFI.getDiagnosticRange d) [0..(numRanges - 1)]

getFixIts :: ClangBase m => FFI.Diagnostic s' -> ClangT s m [(FFI.SourceRange s, FFI.ClangString s)]
-- | Retrieve the fix-it hints associated with the given diagnostic.
--
-- Fix-its are described in terms of a source range whose contents
-- should be replaced by a string. This approach generalizes over
-- three kinds of operations: removal of source code (the range covers
-- the code to be removed and the replacement string is empty),
-- replacement of source code (the range covers the code to be
-- replaced and the replacement string provides the new code), and
-- insertion (both the start and end of the range point at the
-- insertion location, and the replacement string provides the text to
-- insert).
getFixIts :: ClangBase m
=> FFI.Diagnostic s' -- ^ The diagnostic in question.
-> ClangT s m [(FFI.SourceRange s, FFI.ClangString s)] -- ^ A list of replacement
-- ranges and strings. Note
-- that source ranges are
-- half-open ranges '[a, b)'
-- so the source code should
-- be replaced from 'a' up
-- to (but not including)
-- 'b'.
getFixIts d = do
numFixes <- liftIO $ FFI.getDiagnosticNumFixIts d
mapM (FFI.getDiagnosticFixIt d) [0..(numFixes - 1)]

-- Category functions

getCategoryName :: ClangBase m => FFI.Diagnostic s' -> ClangT s m (FFI.ClangString s)
getCategoryName = FFI.getDiagnosticCategoryText
-- | Format the given diagnostic in a manner that is suitable for
-- display.
format :: ClangBase m
=> Maybe [FFI.DisplayOptions] -- ^ An optional list of options
-- for rendering the diagnostic.
-- If 'Nothing' is given, default
-- options are used that mimic the
-- behavior of the Clang compiler
-- as closely as possible.
-> FFI.Diagnostic s' -- ^ The diagnostic to format.
-> ClangT s m (FFI.ClangString s) -- ^ A formatted rendering of the
-- given diagnostic.
format Nothing diag = FFI.formatDiagnostic diag =<<
liftIO FFI.defaultDiagnosticDisplayOptions
format (Just opts) diag = FFI.formatDiagnostic diag (orFlags opts)

-- | Deserialize a set of diagnostics from a Clang diagnostics bitcode file.
--
-- If an error is encountered, a 'FFI.LoadError' and a textual error
-- message suitable for display to the user are returned.
load :: ClangBase m
=> FilePath
-> ClangT s m (Either (FFI.LoadError, FFI.ClangString s) (FFI.DiagnosticSet s))
load = FFI.loadDiagnostics
72 changes: 39 additions & 33 deletions src/Clang/Internal/FFI.gc
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,18 @@ module Clang.Internal.FFI
, getFileLocation
, getRangeStart
, getRangeEnd
, DiagnosticSeverity(..)
, Severity(..)
, Diagnostic
, DiagnosticSet
, getNumDiagnosticsInSet
, getDiagnosticInSet
, LoadDiagError(..)
, LoadError(..)
, loadDiagnostics
, getChildDiagnostics
, getNumDiagnostics
, getDiagnostic
, getDiagnosticSetFromTU
, DiagnosticDisplayOptions(..)
, DisplayOptions(..)
, formatDiagnostic
, defaultDiagnosticDisplayOptions
, getDiagnosticSeverity
Expand Down Expand Up @@ -1000,12 +1000,14 @@ getPresumedLocation l = do
-- CXDiagnostic_Error = 3,
-- CXDiagnostic_Fatal = 4
-- };
%enum DiagnosticSeverity (Bounded, Enum, Eq, Ord, Read, Show, Typeable) Int
% [ CXDiagnostic_Ignored
% , CXDiagnostic_Note
% , CXDiagnostic_Warning
% , CXDiagnostic_Error
% , CXDiagnostic_Fatal

-- | The severity of a diagnostic.
%enum Severity (Bounded, Enum, Eq, Ord, Read, Show, Typeable) Int
% [ SeverityIgnored = CXDiagnostic_Ignored
% , SeverityNote = CXDiagnostic_Note
% , SeverityWarning = CXDiagnostic_Warning
% , SeverityError = CXDiagnostic_Error
% , SeverityFatal = CXDiagnostic_Fatal
% ]

-- typedef void* CXDiagnostic;
Expand Down Expand Up @@ -1075,17 +1077,19 @@ getDiagnosticInSet = (registerDiagnostic .) . unsafe_getDiagnosticInSet
-- CXLoadDiag_CannotLoad = 2,
-- CXLoadDiag_InvalidFile = 3
-- };
%enum LoadDiagError (Bounded, Enum, Eq, Ord, Read, Show, Typeable) Int
% [ CXLoadDiag_None
% , CXLoadDiag_Unknown
% , CXLoadDiag_CannotLoad
% , CXLoadDiag_InvalidFile

-- | An error encountered while loading a serialized diagnostics bitcode file.
%enum LoadError (Bounded, Enum, Eq, Ord, Read, Show, Typeable) Int
% [ LoadSuccessful = CXLoadDiag_None
% , LoadUnknownError = CXLoadDiag_Unknown
% , LoadCannotOpen = CXLoadDiag_CannotLoad
% , LoadInvalidFile = CXLoadDiag_InvalidFile
% ]

data LoadDiagsResult =
LoadDiagsResult LoadDiagError (ClangString ()) (DiagnosticSet ())
LoadDiagsResult LoadError (ClangString ()) (DiagnosticSet ())

%dis loadDiagsResult err eStrData eStrFlags ds = LoadDiagsResult (loadDiagError err) (clangString (ptr eStrData) (word32 eStrFlags)) (diagSet ds)
%dis loadDiagsResult err eStrData eStrFlags ds = LoadDiagsResult (loadError err) (clangString (ptr eStrData) (word32 eStrFlags)) (diagSet ds)

-- CXDiagnosticSet clang_loadDiagnostics(const char *file,
-- enum CXLoadDiag_Error *error,
Expand All @@ -1100,7 +1104,7 @@ data LoadDiagsResult =
%result (loadDiagsResult err outData outFlags r)

loadDiagnostics :: ClangBase m => FilePath
-> ClangT s m (Either (LoadDiagError, ClangString s) (DiagnosticSet s))
-> ClangT s m (Either (LoadError, ClangString s) (DiagnosticSet s))
loadDiagnostics path = do
result <- liftIO $ unsafe_loadDiagnostics path
case result of
Expand Down Expand Up @@ -1152,22 +1156,24 @@ getDiagnosticSetFromTU = registerDiagnosticSet . unsafe_getDiagnosticSetFromTU
-- CXDiagnostic_DisplayCategoryId = 0x10,
-- CXDiagnostic_DisplayCategoryName = 0x20
-- };
%enum DiagnosticDisplayOptions (Bounded, Enum, Eq, Ord, Read, Show, Typeable) Int
% [ CXDiagnostic_DisplaySourceLocation
% , CXDiagnostic_DisplayColumn
% , CXDiagnostic_DisplaySourceRanges
% , CXDiagnostic_DisplayOption
% , CXDiagnostic_DisplayCategoryId
% , CXDiagnostic_DisplayCategoryName

-- | Options for rendering of 'Diagnostic' values.
%enum DisplayOptions (Bounded, Enum, Eq, Ord, Read, Show, Typeable) Int
% [ DisplaySourceLocation = CXDiagnostic_DisplaySourceLocation
% , DisplayColumn = CXDiagnostic_DisplayColumn
% , DisplaySourceRanges = CXDiagnostic_DisplaySourceRanges
% , DisplayOption = CXDiagnostic_DisplayOption
% , DisplayCategoryId = CXDiagnostic_DisplayCategoryId
% , DisplayCategoryName = CXDiagnostic_DisplayCategoryName
% ]

instance BitFlags DiagnosticDisplayOptions where
toBit Diagnostic_DisplaySourceLocation = 0x1
toBit Diagnostic_DisplayColumn = 0x2
toBit Diagnostic_DisplaySourceRanges = 0x4
toBit Diagnostic_DisplayOption = 0x8
toBit Diagnostic_DisplayCategoryId = 0x10
toBit Diagnostic_DisplayCategoryName = 0x20
instance BitFlags DisplayOptions where
toBit DisplaySourceLocation = 0x1
toBit DisplayColumn = 0x2
toBit DisplaySourceRanges = 0x4
toBit DisplayOption = 0x8
toBit DisplayCategoryId = 0x10
toBit DisplayCategoryName = 0x20

-- CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options);
%fun unsafe_formatDiagnostic :: Diagnostic s -> Int -> IO (ClangString ())
Expand All @@ -1184,10 +1190,10 @@ formatDiagnostic = (registerClangString .) . unsafe_formatDiagnostic
%fun clang_defaultDiagnosticDisplayOptions :: IO Int

-- clang_getDiagnosticSeverity(CXDiagnostic);
%fun clang_getDiagnosticSeverity :: Diagnostic s -> IO DiagnosticSeverity
%fun clang_getDiagnosticSeverity :: Diagnostic s -> IO Severity
%call (diag d)
%code enum CXDiagnosticSeverity s = clang_getDiagnosticSeverity(d);
%result (diagnosticSeverity s)
%result (severity s)

-- CXSourceLocation clang_getDiagnosticLocation(CXDiagnostic);
%fun clang_getDiagnosticLocation :: Proxy s -> Diagnostic s' -> IO (SourceLocation s)
Expand Down
5 changes: 5 additions & 0 deletions src/Clang/TranslationUnit.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module Clang.TranslationUnit
, FFI.unsavedContents
, FFI.newUnsavedFile
, FFI.updateUnsavedContents
, getDiagnosticSet
) where

import Control.Applicative
Expand Down Expand Up @@ -108,3 +109,7 @@ setGlobalOptions i opts = liftIO $ FFI.cXIndex_setGlobalOptions i (orFlags opts)

getGlobalOptions :: ClangBase m => FFI.Index s' -> ClangT s m [FFI.GlobalOptFlags]
getGlobalOptions i = unFlags <$> liftIO (FFI.cXIndex_getGlobalOptions i)

-- | 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

0 comments on commit 81bea3c

Please sign in to comment.