@@ -53,6 +53,11 @@ module Language.C.Inline.Internal
5353 , runParserInQ
5454 , splitTypedC
5555
56+ -- * Line directives
57+ , lineDirective
58+ , here
59+ , shiftLines
60+
5661 -- * Utility functions for writing quasiquoters
5762 , genericQuote
5863 , funPtrQuote
@@ -295,7 +300,7 @@ inlineCode Code{..} = do
295300 -- Write out definitions
296301 ctx <- getContext
297302 let out = fromMaybe id $ ctxOutput ctx
298- let directive = maybe " " ( \ l -> " #line " ++ show ( fst $ TH. loc_start l) ++ " " ++ show ( TH. loc_filename l ) ++ " \n " ) codeLoc
303+ let directive = maybe " " lineDirective codeLoc
299304 void $ emitVerbatim $ out $ directive ++ codeDefs
300305 -- Create and add the FFI declaration.
301306 ffiImportName <- uniqueFfiImportName
@@ -681,14 +686,37 @@ genericQuote purity build = quoteCode $ \rawStr -> do
681686 go (paramType : params) = do
682687 [t | $(return paramType) -> $(go params) |]
683688
684- splitTypedC :: String -> (String , String )
685- -- ^ Returns the type and the body separately
686- splitTypedC s = (trim ty, case body of
687- [] -> []
688- r -> r)
689+
690+ -- NOTE: splitTypedC wouldn't be necessary if inline-c-cpp could reuse C.block
691+ -- internals with a clean interface.
692+ -- This would be a significant refactoring but presumably it would lead to an
693+ -- api that could let users write their own quasiquoters a bit more conveniently.
694+
695+ -- | Returns the type and the body separately.
696+ splitTypedC :: String -> (String , String , Int )
697+ splitTypedC s = (trim ty, bodyIndent <> body, bodyLineShift)
689698 where (ty, body) = span (/= ' {' ) s
690699 trim x = L. dropWhileEnd C. isSpace (dropWhile C. isSpace x)
691700
701+ -- We may need to correct the line number of the body
702+ bodyLineShift = length (filter (== ' \n ' ) ty)
703+
704+ -- Indentation is relevant for error messages when the syntax is:
705+ -- [C.foo| type
706+ -- { foo(); }
707+ -- | ]
708+ bodyIndent =
709+ let precedingSpaceReversed =
710+ takeWhile (\ c -> C. isSpace c) $
711+ reverse $
712+ ty
713+ (precedingSpacesTabsReversed, precedingLine) =
714+ span (`notElem` (" \n\r " :: [Char ])) precedingSpaceReversed
715+ in case precedingLine of
716+ (' \n ' : _) -> reverse precedingSpacesTabsReversed
717+ (' \r ' : _) -> reverse precedingSpacesTabsReversed
718+ _ -> " " -- it wasn't indentation after all; just spaces after the type
719+
692720-- | Data to parse for the 'funPtr' quasi-quoter.
693721data FunPtrDecl = FunPtrDecl
694722 { funPtrReturnType :: C. Type C. CIdentifier
@@ -756,6 +784,52 @@ funPtrQuote callSafety = quoteCode $ \rawCode -> do
756784 ]
757785 return (s ++ s')
758786
787+ ------------------------------------------------------------------------
788+ -- Line directives
789+
790+ -- | Tell the C compiler where the next line came from.
791+ --
792+ -- Example:
793+ --
794+ -- @@@
795+ -- there <- location
796+ -- f (unlines
797+ -- [ lineDirective $(here)
798+ -- , "generated_code_user_did_not_write()"
799+ -- , lineDirective there
800+ -- ] ++ userCode
801+ -- ])
802+ -- @@@
803+ --
804+ -- Use @lineDirective $(C.here)@ when generating code, so that any errors or
805+ -- warnings report the location of the generating haskell module, rather than
806+ -- tangentially related user code that doesn't contain the actual problem.
807+ lineDirective :: TH. Loc -> String
808+ lineDirective l = " #line " ++ show (fst $ TH. loc_start l) ++ " " ++ show (TH. loc_filename l ) ++ " \n "
809+
810+ -- | Get the location of the code you're looking at, for use with
811+ -- 'lineDirective'; place before generated code that user did not write.
812+ here :: TH. ExpQ
813+ here = [| $ (TH. location >>= \ (TH. Loc a b c (d1, d2) (e1, e2)) ->
814+ [| Loc
815+ $ (TH. lift a)
816+ $ (TH. lift b)
817+ $ (TH. lift c)
818+ ($ (TH. lift d1), $ (TH. lift d2))
819+ ($ (TH. lift e1), $ (TH. lift e2))
820+ | ])
821+ | ]
822+
823+ shiftLines :: Int -> TH. Loc -> TH. Loc
824+ shiftLines n l = l
825+ { TH. loc_start =
826+ let (startLn, startCol) = TH. loc_start l
827+ in (startLn + n, startCol)
828+ , TH. loc_end =
829+ let (endLn, endCol) = TH. loc_end l
830+ in (endLn + n, endCol)
831+ }
832+
759833------------------------------------------------------------------------
760834-- Utils
761835
0 commit comments