Loading...
Searching...
No Matches
line.f90
Go to the documentation of this file.
1!> @file
2!! @defgroup group_line Line
3!! Standard-compliant handling of the `#line` directive.
4!!
5!! This module implements support for the ISO C preprocessor `#line`
6!! directive, allowing the logical source location used by the preprocessor
7!! to be modified during preprocessing.
8!!
9!! The directive affects the information stored in the active
10!! @link fpx_context::context context @endlink object and therefore influences:
11!!
12!! - Diagnostic messages and source locations,
13!! - Expansions of predefined macros such as `__LINE__`,
14!! - Expansions of `__FILE__` and `__FILENAME__`,
15!! - The apparent origin of generated or transformed source code.
16!!
17!! This functionality is particularly useful for:
18!!
19!! - Source-to-source translators,
20!! - Code generators,
21!! - Literate programming systems,
22!! - Template engines,
23!! - Tools that emit Fortran intended to preserve original source locations.
24!!
25!! The following standard forms are supported:
26!!
27!! - `#line <number>`
28!! - `#line <number> "<filename>"`
29!!
30!! When encountered, the directive immediately updates the logical
31!! source position used for all subsequent processing.
32!!
33!! @note
34!! The specified line number refers to the line immediately following
35!! the directive itself, matching the behaviour of the ISO C preprocessor.
36!!
37!! @note
38!! Malformed directives generate warnings and are ignored.
39!!
40!! @section line_examples Examples
41!!
42!! 1. Reset the logical line number:
43!! @code{.f90}
44!! #line 100
45!! print *, "Reported as line 100"
46!! ...
47!! @endcode
48!!
49!! 2. Change both line number and filename:
50!! @code{.f90}
51!! #line 42 "generated.f90"
52!! integer :: x
53!!
54!! ! Diagnostics now refer to generated.f90:42
55!! ...
56!! @endcode
57!!
58!! 3. Improve diagnostics in generated code:
59!! @code{.f90}
60!! #line 215 "input_template.f90"
61!! call generated_procedure()
62!! ...
63!! @endcode
64module fpx_line
65 use fpx_path
66 use fpx_logging
67 use fpx_context
68
69 implicit none; private
70
71 public :: handle_line
72
73contains
74
75 !> Process a `#line` directive.
76 !!
77 !! Parses the directive arguments, validates the requested logical
78 !! line number, and updates the active @ref context object.
79 !!
80 !! Supported forms are:
81 !!
82 !! @code{.txt}
83 !! #line <number>
84 !! #line <number> "<filename>"
85 !! @endcode
86 !!
87 !! The specified line number becomes the number associated with the
88 !! source line immediately following the directive.
89 !!
90 !! If a filename is supplied, subsequent diagnostics and predefined
91 !! file-related macros use the new filename.
92 !!
93 !! Invalid directives produce warnings and leave the current context
94 !! unchanged.
95 !!
96 !! @param[inout] ctx
97 !! Current source context. Its logical line number and optional
98 !! filename are updated in place.
99 !! @param[in] token
100 !! Directive keyword used to identify the `#line` directive,
101 !! typically `"line"`.
102 !!
103 !! @ingroup group_line
104 subroutine handle_line(ctx, token)
105 type(context), intent(inout) :: ctx
106 character(*), intent(in) :: token
107 !private
108 character(:), allocatable :: temp, num_str, fname
109 integer :: pos, iostat, new_line, closing
110 logical :: has_filename
111
112 ! Skip #line keyword
113 pos = index(lowercase(ctx%content), token) + len(token)
114 temp = trim(adjustl(ctx%content(pos:)))
115
116 if (len_trim(temp) == 0) then
117 call printf(render(diagnostic_report(level_warning, &
118 message='Syntax error', &
119 label=label_type('#line directive with no arguments', index(token, lowercase(ctx%content)) + len(token) + 1, 1)&
120 , &
121 source=ctx%path), &
122 trim(ctx%content), ctx%line))
123 return
124 end if
125
126 ! Extract line number
127 pos = index(temp, ' ')
128 if (pos > 0) then
129 num_str = temp(:pos - 1)
130 fname = trim(adjustl(temp(pos:)))
131 has_filename = .true.
132 else
133 num_str = trim(temp)
134 has_filename = .false.
135 end if
136
137 ! Parse line number
138 read(num_str, *, iostat=iostat) new_line
139 if (iostat /= 0 .or. new_line < 1) then
140 call printf(render(diagnostic_report(level_warning, &
141 message='Syntax error', &
142 label=label_type('Invalid line number in #line directive', index(token, lowercase(ctx%content)) + len(token) + &
143 1, len(num_str)), &
144 source=ctx%path), &
145 trim(ctx%content), ctx%line))
146 return
147 end if
148
149 ! Update current line number (subtract 1 because the next line will be +1)
150 ctx%line = new_line - 1
151
152 ! Update filename if provided (strip quotes)
153 if (has_filename) then
154 if (fname(1:1) == '"' .and. len(fname) > 1) then
155 closing = index(fname(2:), '"')
156 if (closing == 0) then
157 call printf(render(diagnostic_report(level_error, &
158 message='Malformed #line directive', &
159 label=label_type('Missing closing quotation mark', &
160 index(ctx%content,'"'),1), &
161 source=trim(ctx%path)), &
162 ctx%content, ctx%line))
163 end if
164 fname = fname(2:closing)
165 end if
166 if (len_trim(fname) > 0) then
167 ctx%path = trim(fname)
168 end if
169 end if
170 end subroutine
171end module
subroutine, public handle_line(ctx, token)
Process a #line directive.
Definition line.f90:105
Generic renderer for diagnostics and source excerpts.
Definition logging.f90:208
Snapshot of a source location within the preprocessing stream.
Definition context.f90:114
Structured compiler diagnostic.
Definition logging.f90:337
Diagnostic label identifying a region of source text.
Definition logging.f90:304