Coverage Report

Created: 2026-09-14 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmFortranParserImpl.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include <cassert>
4
#include <cstdio>
5
#include <set>
6
#include <stack>
7
#include <string>
8
#include <utility>
9
#include <vector>
10
11
#include "cmFortranParser.h"
12
#include "cmStringAlgorithms.h"
13
#include "cmSystemTools.h"
14
15
bool cmFortranParser_s::FindIncludeFile(char const* dir,
16
                                        char const* includeName,
17
                                        std::string& fileName)
18
6.07k
{
19
  // If the file is a full path, include it directly.
20
6.07k
  if (cmSystemTools::FileIsFullPath(includeName)) {
21
1.14k
    fileName = includeName;
22
1.14k
    return cmSystemTools::FileExists(fileName, true);
23
1.14k
  }
24
  // Check for the file in the directory containing the including
25
  // file.
26
4.93k
  std::string fullName = cmStrCat(dir, '/', includeName);
27
4.93k
  if (cmSystemTools::FileExists(fullName, true)) {
28
66
    fileName = fullName;
29
66
    return true;
30
66
  }
31
32
  // Search the include path for the file.
33
4.86k
  for (std::string const& i : this->IncludePath) {
34
0
    fullName = cmStrCat(i, '/', includeName);
35
0
    if (cmSystemTools::FileExists(fullName, true)) {
36
0
      fileName = fullName;
37
0
      return true;
38
0
    }
39
0
  }
40
4.86k
  return false;
41
4.86k
}
42
43
cmFortranParser_s::cmFortranParser_s(cmFortranCompiler fc,
44
                                     std::vector<std::string> includes,
45
                                     std::set<std::string> defines,
46
                                     cmFortranSourceInfo& info)
47
3.61k
  : Compiler(std::move(fc))
48
3.61k
  , IncludePath(std::move(includes))
49
3.61k
  , PPDefinitions(std::move(defines))
50
3.61k
  , Info(info)
51
3.61k
{
52
3.61k
  this->InInterface = false;
53
3.61k
  this->InPPFalseBranch = 0;
54
55
  // Initialize the lexical scanner.
56
3.61k
  cmFortran_yylex_init(&this->Scanner);
57
3.61k
  cmFortran_yyset_extra(this, this->Scanner);
58
59
  // Create a dummy buffer that is never read but is the fallback
60
  // buffer when the last file is popped off the stack.
61
3.61k
  YY_BUFFER_STATE buffer =
62
3.61k
    cmFortran_yy_create_buffer(nullptr, 4, this->Scanner);
63
3.61k
  cmFortran_yy_switch_to_buffer(buffer, this->Scanner);
64
3.61k
}
65
66
cmFortranParser_s::~cmFortranParser_s()
67
3.61k
{
68
3.61k
  cmFortran_yylex_destroy(this->Scanner);
69
3.61k
}
70
71
std::string cmFortranParser_s::ModName(std::string const& mod_name) const
72
40.3k
{
73
40.3k
  return mod_name + ".mod";
74
40.3k
}
75
76
std::string cmFortranParser_s::SModName(std::string const& mod_name,
77
                                        std::string const& sub_name) const
78
25.2k
{
79
25.2k
  std::string const& SModExt =
80
25.2k
    this->Compiler.SModExt.empty() ? ".mod" : this->Compiler.SModExt;
81
  // An empty separator means that the compiler does not use a prefix.
82
25.2k
  if (this->Compiler.SModSep.empty()) {
83
0
    return sub_name + SModExt;
84
0
  }
85
25.2k
  return mod_name + this->Compiler.SModSep + sub_name + SModExt;
86
25.2k
}
87
88
bool cmFortranParser_FilePush(cmFortranParser* parser, char const* fname)
89
3.80k
{
90
  // Do not revisit already processed files; treat them as non-existing.
91
  // Avoids infinite recursion caused by misinterpreted include guards.
92
3.80k
  if (parser->VisitedFilePaths.insert(fname).second) {
93
    // Open the new file and push it onto the stack.  Save the old
94
    // buffer with it on the stack.
95
3.66k
    if (FILE* file = cmsys::SystemTools::Fopen(fname, "rb")) {
96
3.66k
      YY_BUFFER_STATE current =
97
3.66k
        cmFortranLexer_GetCurrentBuffer(parser->Scanner);
98
3.66k
      std::string dir = cmSystemTools::GetParentDirectory(fname);
99
3.66k
      cmFortranFile f(file, current, dir);
100
3.66k
      YY_BUFFER_STATE buffer =
101
3.66k
        cmFortran_yy_create_buffer(nullptr, 16384, parser->Scanner);
102
3.66k
      cmFortran_yy_switch_to_buffer(buffer, parser->Scanner);
103
3.66k
      parser->FileStack.push(f);
104
3.66k
      return true;
105
3.66k
    }
106
3.66k
  }
107
139
  return false;
108
3.80k
}
109
110
bool cmFortranParser_FilePop(cmFortranParser* parser)
111
7.28k
{
112
  // Pop one file off the stack and close it.  Switch the lexer back
113
  // to the next one on the stack.
114
7.28k
  if (parser->FileStack.empty()) {
115
3.61k
    return false;
116
3.61k
  }
117
3.66k
  cmFortranFile f = parser->FileStack.top();
118
3.66k
  parser->FileStack.pop();
119
3.66k
  fclose(f.File);
120
3.66k
  YY_BUFFER_STATE current = cmFortranLexer_GetCurrentBuffer(parser->Scanner);
121
3.66k
  cmFortran_yy_delete_buffer(current, parser->Scanner);
122
3.66k
  cmFortran_yy_switch_to_buffer(f.Buffer, parser->Scanner);
123
3.66k
  return true;
124
7.28k
}
125
126
int cmFortranParser_Input(cmFortranParser* parser, char* buffer,
127
                          size_t bufferSize)
128
17.6k
{
129
  // Read from the file on top of the stack.  If the stack is empty,
130
  // the end of the translation unit has been reached.
131
17.6k
  if (!parser->FileStack.empty()) {
132
14.0k
    cmFortranFile& ff = parser->FileStack.top();
133
14.0k
    FILE* file = ff.File;
134
14.0k
    size_t n = fread(buffer, 1, bufferSize, file);
135
14.0k
    if (n > 0) {
136
7.33k
      ff.LastCharWasNewline = buffer[n - 1] == '\n';
137
7.33k
    } else if (!ff.LastCharWasNewline) {
138
      // The file ended without a newline.  Inject one so
139
      // that the file always ends in an end-of-statement.
140
3.01k
      buffer[0] = '\n';
141
3.01k
      n = 1;
142
3.01k
      ff.LastCharWasNewline = true;
143
3.01k
    }
144
14.0k
    return static_cast<int>(n);
145
14.0k
  }
146
3.61k
  return 0;
147
17.6k
}
148
149
void cmFortranParser_StringStart(cmFortranParser* parser)
150
494k
{
151
494k
  parser->TokenString.clear();
152
494k
}
153
154
char const* cmFortranParser_StringEnd(cmFortranParser* parser)
155
375k
{
156
375k
  return parser->TokenString.c_str();
157
375k
}
158
159
void cmFortranParser_StringAppend(cmFortranParser* parser, char c)
160
6.54M
{
161
6.54M
  parser->TokenString += c;
162
6.54M
}
163
164
void cmFortranParser_SetInInterface(cmFortranParser* parser, bool in)
165
0
{
166
0
  if (parser->InPPFalseBranch) {
167
0
    return;
168
0
  }
169
170
0
  parser->InInterface = in;
171
0
}
172
173
bool cmFortranParser_GetInInterface(cmFortranParser* parser)
174
0
{
175
0
  return parser->InInterface;
176
0
}
177
178
void cmFortranParser_SetOldStartcond(cmFortranParser* parser, int arg)
179
494k
{
180
494k
  parser->OldStartcond = arg;
181
494k
}
182
183
int cmFortranParser_GetOldStartcond(cmFortranParser* parser)
184
376k
{
185
376k
  return parser->OldStartcond;
186
376k
}
187
188
void cmFortranParser_Error(cmFortranParser* parser, char const* msg)
189
244k
{
190
244k
  parser->Error = msg ? msg : "unknown error";
191
244k
}
192
193
void cmFortranParser_RuleUse(cmFortranParser* parser, char const* module_name)
194
21.3k
{
195
21.3k
  if (parser->InPPFalseBranch) {
196
2.36k
    return;
197
2.36k
  }
198
199
  // syntax:   "use module_name"
200
  // requires: "module_name.mod"
201
18.9k
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
202
18.9k
  parser->Info.Requires.insert(parser->ModName(mod_name));
203
18.9k
}
204
205
void cmFortranParser_RuleUseIntrinsic(cmFortranParser* parser,
206
                                      char const* module_name)
207
5.06k
{
208
5.06k
  if (parser->InPPFalseBranch) {
209
1.27k
    return;
210
1.27k
  }
211
212
  // syntax:   "use, intrinsic:: module_name"
213
  // requires: "module_name.mod"
214
3.78k
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
215
3.78k
  parser->Info.Intrinsics.insert(parser->ModName(mod_name));
216
3.78k
}
217
218
void cmFortranParser_RuleLineDirective(cmFortranParser* parser,
219
                                       char const* filename)
220
43.7k
{
221
  // This is a #line directive naming a file encountered during preprocessing.
222
43.7k
  std::string included = filename;
223
224
  // Skip #line directives referencing non-files like
225
  // "<built-in>" or "<command-line>".
226
43.7k
  if (included.empty() || included[0] == '<') {
227
5.17k
    return;
228
5.17k
  }
229
230
  // Fix windows file path separators since our lexer does not
231
  // process escape sequences in string literals.
232
38.5k
  cmSystemTools::ReplaceString(included, "\\\\", "\\");
233
38.5k
  cmSystemTools::ConvertToUnixSlashes(included);
234
235
  // Save the named file as included in the source.
236
38.5k
  if (cmSystemTools::FileExists(included, true)) {
237
1.35k
    parser->Info.Includes.insert(included);
238
1.35k
  }
239
38.5k
}
240
241
void cmFortranParser_RuleInclude(cmFortranParser* parser, char const* name)
242
37.8k
{
243
37.8k
  if (parser->InPPFalseBranch) {
244
31.8k
    return;
245
31.8k
  }
246
247
  // If processing an include statement there must be an open file.
248
37.8k
  assert(!parser->FileStack.empty());
249
250
  // Get the directory containing the source in which the include
251
  // statement appears.  This is always the first search location for
252
  // Fortran include files.
253
6.07k
  std::string dir = parser->FileStack.top().Directory;
254
255
  // Find the included file.  If it cannot be found just ignore the
256
  // problem because either the source will not compile or the user
257
  // does not care about depending on this included source.
258
6.07k
  std::string fullName;
259
6.07k
  if (parser->FindIncludeFile(dir.c_str(), name, fullName)) {
260
    // Found the included file.  Save it in the set of included files.
261
192
    parser->Info.Includes.insert(fullName);
262
263
    // Parse it immediately to translate the source inline.
264
192
    cmFortranParser_FilePush(parser, fullName.c_str());
265
192
  }
266
6.07k
}
267
268
void cmFortranParser_RuleModule(cmFortranParser* parser,
269
                                char const* module_name)
270
5.65k
{
271
5.65k
  if (parser->InPPFalseBranch) {
272
742
    return;
273
742
  }
274
275
4.91k
  if (!parser->InInterface) {
276
    // syntax:   "module module_name"
277
    // provides: "module_name.mod"
278
4.91k
    std::string const& mod_name = cmSystemTools::LowerCase(module_name);
279
4.91k
    parser->Info.Provides.insert(parser->ModName(mod_name));
280
4.91k
  }
281
4.91k
}
282
283
void cmFortranParser_RuleSubmodule(cmFortranParser* parser,
284
                                   char const* module_name,
285
                                   char const* submodule_name)
286
14.0k
{
287
14.0k
  if (parser->InPPFalseBranch) {
288
1.40k
    return;
289
1.40k
  }
290
291
  // syntax:   "submodule (module_name) submodule_name"
292
  // requires: "module_name.mod"
293
  // provides: "module_name@submodule_name.smod"
294
  //
295
  // FIXME: Some compilers split the submodule part of a module into a
296
  // separate "module_name.smod" file.  Whether it is generated or
297
  // not depends on conditions more subtle than we currently detect.
298
  // For now we depend directly on "module_name.mod".
299
300
12.6k
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
301
12.6k
  std::string const& sub_name = cmSystemTools::LowerCase(submodule_name);
302
12.6k
  parser->Info.Requires.insert(parser->ModName(mod_name));
303
12.6k
  parser->Info.Provides.insert(parser->SModName(mod_name, sub_name));
304
12.6k
}
305
306
void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser,
307
                                         char const* module_name,
308
                                         char const* submodule_name,
309
                                         char const* nested_submodule_name)
310
11.5k
{
311
11.5k
  if (parser->InPPFalseBranch) {
312
5.21k
    return;
313
5.21k
  }
314
315
  // syntax:   "submodule (module_name:submodule_name) nested_submodule_name"
316
  // requires: "module_name@submodule_name.smod"
317
  // provides: "module_name@nested_submodule_name.smod"
318
319
6.29k
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
320
6.29k
  std::string const& sub_name = cmSystemTools::LowerCase(submodule_name);
321
6.29k
  std::string const& nest_name =
322
6.29k
    cmSystemTools::LowerCase(nested_submodule_name);
323
6.29k
  parser->Info.Requires.insert(parser->SModName(mod_name, sub_name));
324
6.29k
  parser->Info.Provides.insert(parser->SModName(mod_name, nest_name));
325
6.29k
}
326
327
void cmFortranParser_RuleDefine(cmFortranParser* parser, char const* macro)
328
75.0k
{
329
75.0k
  if (!parser->InPPFalseBranch) {
330
69.0k
    parser->PPDefinitions.insert(macro);
331
69.0k
  }
332
75.0k
}
333
334
void cmFortranParser_RuleUndef(cmFortranParser* parser, char const* macro)
335
68.9k
{
336
68.9k
  if (!parser->InPPFalseBranch) {
337
57.8k
    std::set<std::string>::iterator match;
338
57.8k
    match = parser->PPDefinitions.find(macro);
339
57.8k
    if (match != parser->PPDefinitions.end()) {
340
36.3k
      parser->PPDefinitions.erase(match);
341
36.3k
    }
342
57.8k
  }
343
68.9k
}
344
345
void cmFortranParser_RuleIfdef(cmFortranParser* parser, char const* macro)
346
19.0k
{
347
  // A new PP branch has been opened
348
19.0k
  parser->SkipToEnd.push(false);
349
350
19.0k
  if (parser->InPPFalseBranch) {
351
16.2k
    parser->InPPFalseBranch++;
352
16.2k
  } else if (parser->PPDefinitions.find(macro) ==
353
2.86k
             parser->PPDefinitions.end()) {
354
1.89k
    parser->InPPFalseBranch = 1;
355
1.89k
  } else {
356
967
    parser->SkipToEnd.top() = true;
357
967
  }
358
19.0k
}
359
360
void cmFortranParser_RuleIfndef(cmFortranParser* parser, char const* macro)
361
225k
{
362
  // A new PP branch has been opened
363
225k
  parser->SkipToEnd.push(false);
364
365
225k
  if (parser->InPPFalseBranch) {
366
200k
    parser->InPPFalseBranch++;
367
200k
  } else if (parser->PPDefinitions.find(macro) !=
368
24.6k
             parser->PPDefinitions.end()) {
369
220
    parser->InPPFalseBranch = 1;
370
24.4k
  } else {
371
    // ignore other branches
372
24.4k
    parser->SkipToEnd.top() = true;
373
24.4k
  }
374
225k
}
375
376
void cmFortranParser_RuleIf(cmFortranParser* parser)
377
81.2k
{
378
  /* Note: The current parser is _not_ able to get statements like
379
   *   #if 0
380
   *   #if 1
381
   *   #if MYSMBOL
382
   *   #if defined(MYSYMBOL)
383
   *   #if defined(MYSYMBOL) && ...
384
   * right.  The same for #elif.  Thus in
385
   *   #if SYMBOL_1
386
   *     ..
387
   *   #elif SYMBOL_2
388
   *     ...
389
   *     ...
390
   *   #elif SYMBOL_N
391
   *     ..
392
   *   #else
393
   *     ..
394
   *   #endif
395
   * _all_ N+1 branches are considered.  If you got something like this
396
   *   #if defined(MYSYMBOL)
397
   *   #if !defined(MYSYMBOL)
398
   * use
399
   *   #ifdef MYSYMBOL
400
   *   #ifndef MYSYMBOL
401
   * instead.
402
   */
403
404
  // A new PP branch has been opened
405
  // Never skip!  See note above.
406
81.2k
  parser->SkipToEnd.push(false);
407
81.2k
}
408
409
void cmFortranParser_RuleElif(cmFortranParser* parser)
410
3.37k
{
411
  /* Note: There are parser limitations.  See the note at
412
   * cmFortranParser_RuleIf(..)
413
   */
414
415
  // Always taken unless an #ifdef or #ifndef-branch has been taken
416
  // already.  If the second condition isn't meet already
417
  // (parser->InPPFalseBranch == 0) correct it.
418
3.37k
  if (!parser->SkipToEnd.empty() && parser->SkipToEnd.top() &&
419
478
      !parser->InPPFalseBranch) {
420
218
    parser->InPPFalseBranch = 1;
421
218
  }
422
3.37k
}
423
424
void cmFortranParser_RuleElse(cmFortranParser* parser)
425
15.4k
{
426
  // if the parent branch is false do nothing!
427
15.4k
  if (parser->InPPFalseBranch > 1) {
428
7.06k
    return;
429
7.06k
  }
430
431
  // parser->InPPFalseBranch is either 0 or 1.  We change it depending on
432
  // parser->SkipToEnd.top()
433
8.38k
  if (!parser->SkipToEnd.empty() && parser->SkipToEnd.top()) {
434
321
    parser->InPPFalseBranch = 1;
435
8.06k
  } else {
436
8.06k
    parser->InPPFalseBranch = 0;
437
8.06k
  }
438
8.38k
}
439
440
void cmFortranParser_RuleEndif(cmFortranParser* parser)
441
8.48k
{
442
8.48k
  if (!parser->SkipToEnd.empty()) {
443
6.64k
    parser->SkipToEnd.pop();
444
6.64k
  }
445
446
  // #endif doesn't know if there was a "#else" in before, so it
447
  // always decreases InPPFalseBranch
448
8.48k
  if (parser->InPPFalseBranch) {
449
3.11k
    parser->InPPFalseBranch--;
450
3.11k
  }
451
8.48k
}