@@ -213,6 +213,9 @@ void Prescanner::Statement() {
213
213
if (preprocessed->HasRedundantBlanks ()) {
214
214
preprocessed->RemoveRedundantBlanks ();
215
215
}
216
+ while (CompilerDirectiveContinuation (*preprocessed, ppl.sentinel )) {
217
+ newlineProvenance = GetCurrentProvenance ();
218
+ }
216
219
NormalizeCompilerDirectiveCommentMarker (*preprocessed);
217
220
preprocessed->ToLowerCase ();
218
221
SourceFormChange (preprocessed->ToString ());
@@ -227,6 +230,9 @@ void Prescanner::Statement() {
227
230
preprocessed->RemoveBlanks (/* after column*/ 6 );
228
231
}
229
232
} else {
233
+ while (SourceLineContinuation (*preprocessed)) {
234
+ newlineProvenance = GetCurrentProvenance ();
235
+ }
230
236
if (preprocessed->HasRedundantBlanks ()) {
231
237
preprocessed->RemoveRedundantBlanks ();
232
238
}
@@ -239,12 +245,17 @@ void Prescanner::Statement() {
239
245
break ;
240
246
}
241
247
} else {
242
- tokens.ToLowerCase ();
243
248
if (line.kind == LineClassification::Kind::CompilerDirective) {
249
+ while (CompilerDirectiveContinuation (tokens, line.sentinel )) {
250
+ newlineProvenance = GetCurrentProvenance ();
251
+ }
252
+ tokens.ToLowerCase ();
244
253
SourceFormChange (tokens.ToString ());
245
- }
246
- if (inFixedForm_ && line.kind == LineClassification::Kind::Source) {
247
- EnforceStupidEndStatementRules (tokens);
254
+ } else { // Kind::Source
255
+ tokens.ToLowerCase ();
256
+ if (inFixedForm_) {
257
+ EnforceStupidEndStatementRules (tokens);
258
+ }
248
259
}
249
260
tokens.CheckBadFortranCharacters (messages_)
250
261
.CheckBadParentheses (messages_)
@@ -1132,8 +1143,10 @@ bool Prescanner::FreeFormContinuation() {
1132
1143
if (*p != ' \n ' ) {
1133
1144
if (inCharLiteral_) {
1134
1145
return false ;
1135
- } else if (*p != ' !' &&
1136
- features_.ShouldWarn (LanguageFeature::CruftAfterAmpersand)) {
1146
+ } else if (*p == ' !' ) { // & ! comment - ok
1147
+ } else if (ampersand && isPossibleMacroCall_ && (*p == ' ,' || *p == ' )' )) {
1148
+ return false ; // allow & at end of a macro argument
1149
+ } else if (features_.ShouldWarn (LanguageFeature::CruftAfterAmpersand)) {
1137
1150
Say (GetProvenance (p), " missing ! before comment after &" _warn_en_US);
1138
1151
}
1139
1152
}
@@ -1318,4 +1331,107 @@ void Prescanner::SourceFormChange(std::string &&dir) {
1318
1331
inFixedForm_ = true ;
1319
1332
}
1320
1333
}
1334
+
1335
+ // Acquire and append compiler directive continuation lines to
1336
+ // the tokens that constitute a compiler directive, even when those
1337
+ // directive continuation lines are the result of macro expansion.
1338
+ // (Not used when neither the original compiler directive line nor
1339
+ // the directive continuation line result from preprocessing; regular
1340
+ // line continuation during tokenization handles that normal case.)
1341
+ bool Prescanner::CompilerDirectiveContinuation (
1342
+ TokenSequence &tokens, const char *origSentinel) {
1343
+ if (inFixedForm_ || tokens.empty () ||
1344
+ tokens.TokenAt (tokens.SizeInTokens () - 1 ) != " &" ) {
1345
+ return false ;
1346
+ }
1347
+ LineClassification followingLine{ClassifyLine (nextLine_)};
1348
+ if (followingLine.kind == LineClassification::Kind::Comment) {
1349
+ nextLine_ += followingLine.payloadOffset ; // advance to '!' or newline
1350
+ NextLine ();
1351
+ return true ;
1352
+ }
1353
+ CHECK (origSentinel != nullptr );
1354
+ directiveSentinel_ = origSentinel; // so IsDirective() is true
1355
+ const char *nextContinuation{
1356
+ followingLine.kind == LineClassification::Kind::CompilerDirective
1357
+ ? FreeFormContinuationLine (true )
1358
+ : nullptr };
1359
+ if (!nextContinuation &&
1360
+ followingLine.kind != LineClassification::Kind::Source) {
1361
+ return false ;
1362
+ }
1363
+ auto origNextLine{nextLine_};
1364
+ BeginSourceLine (nextLine_);
1365
+ NextLine ();
1366
+ TokenSequence followingTokens;
1367
+ if (nextContinuation) {
1368
+ // What follows is !DIR$ & xxx; skip over the & so that it
1369
+ // doesn't cause a spurious continuation.
1370
+ at_ = nextContinuation;
1371
+ } else {
1372
+ // What follows looks like a source line before macro expansion,
1373
+ // but might become a directive continuation afterwards.
1374
+ SkipSpaces ();
1375
+ }
1376
+ while (NextToken (followingTokens)) {
1377
+ }
1378
+ if (auto followingPrepro{
1379
+ preprocessor_.MacroReplacement (followingTokens, *this )}) {
1380
+ followingTokens = std::move (*followingPrepro);
1381
+ }
1382
+ followingTokens.RemoveRedundantBlanks ();
1383
+ std::size_t startAt{0 };
1384
+ std::size_t keep{followingTokens.SizeInTokens ()};
1385
+ bool ok{false };
1386
+ if (nextContinuation) {
1387
+ ok = true ;
1388
+ } else {
1389
+ if (keep >= 3 && followingTokens.TokenAt (0 ) == " !" &&
1390
+ followingTokens.TokenAt (2 ) == " &" ) {
1391
+ CharBlock sentinel{followingTokens.TokenAt (1 )};
1392
+ if (!sentinel.empty () &&
1393
+ std::memcmp (sentinel.begin (), origSentinel, sentinel.size ()) == 0 ) {
1394
+ startAt = 3 ;
1395
+ keep -= 3 ;
1396
+ ok = true ;
1397
+ }
1398
+ }
1399
+ }
1400
+ if (ok) {
1401
+ tokens.pop_back (); // delete original '&'
1402
+ tokens.Put (followingTokens, startAt, keep);
1403
+ } else {
1404
+ nextLine_ = origNextLine;
1405
+ }
1406
+ return ok;
1407
+ }
1408
+
1409
+ // Similar, but for source line continuation after macro replacement.
1410
+ bool Prescanner::SourceLineContinuation (TokenSequence &tokens) {
1411
+ if (!inFixedForm_ && !tokens.empty () &&
1412
+ tokens.TokenAt (tokens.SizeInTokens () - 1 ) == " &" ) {
1413
+ LineClassification followingLine{ClassifyLine (nextLine_)};
1414
+ if (followingLine.kind == LineClassification::Kind::Comment) {
1415
+ nextLine_ += followingLine.payloadOffset ; // advance to '!' or newline
1416
+ NextLine ();
1417
+ return true ;
1418
+ } else if (const char *nextContinuation{FreeFormContinuationLine (true )}) {
1419
+ BeginSourceLine (nextLine_);
1420
+ NextLine ();
1421
+ TokenSequence followingTokens;
1422
+ at_ = nextContinuation;
1423
+ while (NextToken (followingTokens)) {
1424
+ }
1425
+ if (auto followingPrepro{
1426
+ preprocessor_.MacroReplacement (followingTokens, *this )}) {
1427
+ followingTokens = std::move (*followingPrepro);
1428
+ }
1429
+ followingTokens.RemoveRedundantBlanks ();
1430
+ tokens.pop_back (); // delete original '&'
1431
+ tokens.Put (followingTokens);
1432
+ return true ;
1433
+ }
1434
+ }
1435
+ return false ;
1436
+ }
1321
1437
} // namespace Fortran::parser
0 commit comments