Coverage Report

Created: 2026-03-12 06:35

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
0
{
19
  // If the file is a full path, include it directly.
20
0
  if (cmSystemTools::FileIsFullPath(includeName)) {
21
0
    fileName = includeName;
22
0
    return cmSystemTools::FileExists(fileName, true);
23
0
  }
24
  // Check for the file in the directory containing the including
25
  // file.
26
0
  std::string fullName = cmStrCat(dir, '/', includeName);
27
0
  if (cmSystemTools::FileExists(fullName, true)) {
28
0
    fileName = fullName;
29
0
    return true;
30
0
  }
31
32
  // Search the include path for the file.
33
0
  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
0
  return false;
41
0
}
42
43
cmFortranParser_s::cmFortranParser_s(cmFortranCompiler fc,
44
                                     std::vector<std::string> includes,
45
                                     std::set<std::string> defines,
46
                                     cmFortranSourceInfo& info)
47
0
  : Compiler(std::move(fc))
48
0
  , IncludePath(std::move(includes))
49
0
  , PPDefinitions(std::move(defines))
50
0
  , Info(info)
51
0
{
52
0
  this->InInterface = false;
53
0
  this->InPPFalseBranch = 0;
54
55
  // Initialize the lexical scanner.
56
0
  cmFortran_yylex_init(&this->Scanner);
57
0
  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
0
  YY_BUFFER_STATE buffer =
62
0
    cmFortran_yy_create_buffer(nullptr, 4, this->Scanner);
63
0
  cmFortran_yy_switch_to_buffer(buffer, this->Scanner);
64
0
}
65
66
cmFortranParser_s::~cmFortranParser_s()
67
0
{
68
0
  cmFortran_yylex_destroy(this->Scanner);
69
0
}
70
71
std::string cmFortranParser_s::ModName(std::string const& mod_name) const
72
0
{
73
0
  return mod_name + ".mod";
74
0
}
75
76
std::string cmFortranParser_s::SModName(std::string const& mod_name,
77
                                        std::string const& sub_name) const
78
0
{
79
0
  std::string const& SModExt =
80
0
    this->Compiler.SModExt.empty() ? ".mod" : this->Compiler.SModExt;
81
  // An empty separator means that the compiler does not use a prefix.
82
0
  if (this->Compiler.SModSep.empty()) {
83
0
    return sub_name + SModExt;
84
0
  }
85
0
  return mod_name + this->Compiler.SModSep + sub_name + SModExt;
86
0
}
87
88
bool cmFortranParser_FilePush(cmFortranParser* parser, char const* fname)
89
0
{
90
  // Do not revisit already processed files; treat them as non-existing.
91
  // Avoids infinite recursion caused by misinterpreted include guards.
92
0
  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
0
    if (FILE* file = cmsys::SystemTools::Fopen(fname, "rb")) {
96
0
      YY_BUFFER_STATE current =
97
0
        cmFortranLexer_GetCurrentBuffer(parser->Scanner);
98
0
      std::string dir = cmSystemTools::GetParentDirectory(fname);
99
0
      cmFortranFile f(file, current, dir);
100
0
      YY_BUFFER_STATE buffer =
101
0
        cmFortran_yy_create_buffer(nullptr, 16384, parser->Scanner);
102
0
      cmFortran_yy_switch_to_buffer(buffer, parser->Scanner);
103
0
      parser->FileStack.push(f);
104
0
      return true;
105
0
    }
106
0
  }
107
0
  return false;
108
0
}
109
110
bool cmFortranParser_FilePop(cmFortranParser* parser)
111
0
{
112
  // Pop one file off the stack and close it.  Switch the lexer back
113
  // to the next one on the stack.
114
0
  if (parser->FileStack.empty()) {
115
0
    return false;
116
0
  }
117
0
  cmFortranFile f = parser->FileStack.top();
118
0
  parser->FileStack.pop();
119
0
  fclose(f.File);
120
0
  YY_BUFFER_STATE current = cmFortranLexer_GetCurrentBuffer(parser->Scanner);
121
0
  cmFortran_yy_delete_buffer(current, parser->Scanner);
122
0
  cmFortran_yy_switch_to_buffer(f.Buffer, parser->Scanner);
123
0
  return true;
124
0
}
125
126
int cmFortranParser_Input(cmFortranParser* parser, char* buffer,
127
                          size_t bufferSize)
128
0
{
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
0
  if (!parser->FileStack.empty()) {
132
0
    cmFortranFile& ff = parser->FileStack.top();
133
0
    FILE* file = ff.File;
134
0
    size_t n = fread(buffer, 1, bufferSize, file);
135
0
    if (n > 0) {
136
0
      ff.LastCharWasNewline = buffer[n - 1] == '\n';
137
0
    } 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
0
      buffer[0] = '\n';
141
0
      n = 1;
142
0
      ff.LastCharWasNewline = true;
143
0
    }
144
0
    return static_cast<int>(n);
145
0
  }
146
0
  return 0;
147
0
}
148
149
void cmFortranParser_StringStart(cmFortranParser* parser)
150
0
{
151
0
  parser->TokenString.clear();
152
0
}
153
154
char const* cmFortranParser_StringEnd(cmFortranParser* parser)
155
0
{
156
0
  return parser->TokenString.c_str();
157
0
}
158
159
void cmFortranParser_StringAppend(cmFortranParser* parser, char c)
160
0
{
161
0
  parser->TokenString += c;
162
0
}
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
0
{
180
0
  parser->OldStartcond = arg;
181
0
}
182
183
int cmFortranParser_GetOldStartcond(cmFortranParser* parser)
184
0
{
185
0
  return parser->OldStartcond;
186
0
}
187
188
void cmFortranParser_Error(cmFortranParser* parser, char const* msg)
189
0
{
190
0
  parser->Error = msg ? msg : "unknown error";
191
0
}
192
193
void cmFortranParser_RuleUse(cmFortranParser* parser, char const* module_name)
194
0
{
195
0
  if (parser->InPPFalseBranch) {
196
0
    return;
197
0
  }
198
199
  // syntax:   "use module_name"
200
  // requires: "module_name.mod"
201
0
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
202
0
  parser->Info.Requires.insert(parser->ModName(mod_name));
203
0
}
204
205
void cmFortranParser_RuleUseIntrinsic(cmFortranParser* parser,
206
                                      char const* module_name)
207
0
{
208
0
  if (parser->InPPFalseBranch) {
209
0
    return;
210
0
  }
211
212
  // syntax:   "use, intrinsic:: module_name"
213
  // requires: "module_name.mod"
214
0
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
215
0
  parser->Info.Intrinsics.insert(parser->ModName(mod_name));
216
0
}
217
218
void cmFortranParser_RuleLineDirective(cmFortranParser* parser,
219
                                       char const* filename)
220
0
{
221
  // This is a #line directive naming a file encountered during preprocessing.
222
0
  std::string included = filename;
223
224
  // Skip #line directives referencing non-files like
225
  // "<built-in>" or "<command-line>".
226
0
  if (included.empty() || included[0] == '<') {
227
0
    return;
228
0
  }
229
230
  // Fix windows file path separators since our lexer does not
231
  // process escape sequences in string literals.
232
0
  cmSystemTools::ReplaceString(included, "\\\\", "\\");
233
0
  cmSystemTools::ConvertToUnixSlashes(included);
234
235
  // Save the named file as included in the source.
236
0
  if (cmSystemTools::FileExists(included, true)) {
237
0
    parser->Info.Includes.insert(included);
238
0
  }
239
0
}
240
241
void cmFortranParser_RuleInclude(cmFortranParser* parser, char const* name)
242
0
{
243
0
  if (parser->InPPFalseBranch) {
244
0
    return;
245
0
  }
246
247
  // If processing an include statement there must be an open file.
248
0
  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
0
  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
0
  std::string fullName;
259
0
  if (parser->FindIncludeFile(dir.c_str(), name, fullName)) {
260
    // Found the included file.  Save it in the set of included files.
261
0
    parser->Info.Includes.insert(fullName);
262
263
    // Parse it immediately to translate the source inline.
264
0
    cmFortranParser_FilePush(parser, fullName.c_str());
265
0
  }
266
0
}
267
268
void cmFortranParser_RuleModule(cmFortranParser* parser,
269
                                char const* module_name)
270
0
{
271
0
  if (parser->InPPFalseBranch) {
272
0
    return;
273
0
  }
274
275
0
  if (!parser->InInterface) {
276
    // syntax:   "module module_name"
277
    // provides: "module_name.mod"
278
0
    std::string const& mod_name = cmSystemTools::LowerCase(module_name);
279
0
    parser->Info.Provides.insert(parser->ModName(mod_name));
280
0
  }
281
0
}
282
283
void cmFortranParser_RuleSubmodule(cmFortranParser* parser,
284
                                   char const* module_name,
285
                                   char const* submodule_name)
286
0
{
287
0
  if (parser->InPPFalseBranch) {
288
0
    return;
289
0
  }
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
0
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
301
0
  std::string const& sub_name = cmSystemTools::LowerCase(submodule_name);
302
0
  parser->Info.Requires.insert(parser->ModName(mod_name));
303
0
  parser->Info.Provides.insert(parser->SModName(mod_name, sub_name));
304
0
}
305
306
void cmFortranParser_RuleSubmoduleNested(cmFortranParser* parser,
307
                                         char const* module_name,
308
                                         char const* submodule_name,
309
                                         char const* nested_submodule_name)
310
0
{
311
0
  if (parser->InPPFalseBranch) {
312
0
    return;
313
0
  }
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
0
  std::string const& mod_name = cmSystemTools::LowerCase(module_name);
320
0
  std::string const& sub_name = cmSystemTools::LowerCase(submodule_name);
321
0
  std::string const& nest_name =
322
0
    cmSystemTools::LowerCase(nested_submodule_name);
323
0
  parser->Info.Requires.insert(parser->SModName(mod_name, sub_name));
324
0
  parser->Info.Provides.insert(parser->SModName(mod_name, nest_name));
325
0
}
326
327
void cmFortranParser_RuleDefine(cmFortranParser* parser, char const* macro)
328
0
{
329
0
  if (!parser->InPPFalseBranch) {
330
0
    parser->PPDefinitions.insert(macro);
331
0
  }
332
0
}
333
334
void cmFortranParser_RuleUndef(cmFortranParser* parser, char const* macro)
335
0
{
336
0
  if (!parser->InPPFalseBranch) {
337
0
    std::set<std::string>::iterator match;
338
0
    match = parser->PPDefinitions.find(macro);
339
0
    if (match != parser->PPDefinitions.end()) {
340
0
      parser->PPDefinitions.erase(match);
341
0
    }
342
0
  }
343
0
}
344
345
void cmFortranParser_RuleIfdef(cmFortranParser* parser, char const* macro)
346
0
{
347
  // A new PP branch has been opened
348
0
  parser->SkipToEnd.push(false);
349
350
0
  if (parser->InPPFalseBranch) {
351
0
    parser->InPPFalseBranch++;
352
0
  } else if (parser->PPDefinitions.find(macro) ==
353
0
             parser->PPDefinitions.end()) {
354
0
    parser->InPPFalseBranch = 1;
355
0
  } else {
356
0
    parser->SkipToEnd.top() = true;
357
0
  }
358
0
}
359
360
void cmFortranParser_RuleIfndef(cmFortranParser* parser, char const* macro)
361
0
{
362
  // A new PP branch has been opened
363
0
  parser->SkipToEnd.push(false);
364
365
0
  if (parser->InPPFalseBranch) {
366
0
    parser->InPPFalseBranch++;
367
0
  } else if (parser->PPDefinitions.find(macro) !=
368
0
             parser->PPDefinitions.end()) {
369
0
    parser->InPPFalseBranch = 1;
370
0
  } else {
371
    // ignore other branches
372
0
    parser->SkipToEnd.top() = true;
373
0
  }
374
0
}
375
376
void cmFortranParser_RuleIf(cmFortranParser* parser)
377
0
{
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
0
  parser->SkipToEnd.push(false);
407
0
}
408
409
void cmFortranParser_RuleElif(cmFortranParser* parser)
410
0
{
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
0
  if (!parser->SkipToEnd.empty() && parser->SkipToEnd.top() &&
419
0
      !parser->InPPFalseBranch) {
420
0
    parser->InPPFalseBranch = 1;
421
0
  }
422
0
}
423
424
void cmFortranParser_RuleElse(cmFortranParser* parser)
425
0
{
426
  // if the parent branch is false do nothing!
427
0
  if (parser->InPPFalseBranch > 1) {
428
0
    return;
429
0
  }
430
431
  // parser->InPPFalseBranch is either 0 or 1.  We change it depending on
432
  // parser->SkipToEnd.top()
433
0
  if (!parser->SkipToEnd.empty() && parser->SkipToEnd.top()) {
434
0
    parser->InPPFalseBranch = 1;
435
0
  } else {
436
0
    parser->InPPFalseBranch = 0;
437
0
  }
438
0
}
439
440
void cmFortranParser_RuleEndif(cmFortranParser* parser)
441
0
{
442
0
  if (!parser->SkipToEnd.empty()) {
443
0
    parser->SkipToEnd.pop();
444
0
  }
445
446
  // #endif doesn't know if there was a "#else" in before, so it
447
  // always decreases InPPFalseBranch
448
0
  if (parser->InPPFalseBranch) {
449
0
    parser->InPPFalseBranch--;
450
0
  }
451
0
}