[flang] Clause 13 (Input/output editing) syntax and semantic checking (flang-compiler/f18#498)

* Clause 13 (Input/output editing) syntax and semantic checking

Diagnose:
 - all clause syntax errors (independent of normal parsing)
 - all clause constraints
 - use of non-standard extensions under -Mstandard
 - obvious program requirement violations

The code:
 - is invoked for both format statement and I/O statement constant formats
 - should be useable by the runtime I/O library for runtime formats
 - is able to recover from errors
 - is able to diagnose multiple errors in a single format
 - has accurate markers for errors
 - can process (fixed character size) Hollerith strings
 - generates reasonable error messages for known error scenarios
 - should not require C++ run-time library support

The code is templatized for use with fixed size character kind={1,2,4} input,
but only the kind=1 variant is actually exercised.

* Review update.

* Review update.

* Review update.

* Improve error processing of unterminated formats.

Original-commit: flang-compiler/f18@c04b7518df
Reviewed-on: https://github.com/flang-compiler/f18/pull/498
This commit is contained in:
vdonaldson 2019-06-25 15:59:30 -07:00 committed by GitHub
parent a0b0bb43ed
commit 69f5f13738
15 changed files with 1507 additions and 112 deletions

2
flang/.gitignore vendored
View File

@ -6,7 +6,7 @@ tags
TAGS
*.o
.nfs*
*.swp
*.sw?
*~
*#
CMakeCache.txt

785
flang/lib/common/format.h Normal file
View File

@ -0,0 +1,785 @@
// Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef FORTRAN_COMMON_FORMAT_H_
#define FORTRAN_COMMON_FORMAT_H_
#include "Fortran.h"
#include <cstring>
// Define a FormatValidator class template to validate a format expression
// of a given CHAR kind. To enable use in runtime library code as well as
// compiler code, the implementation does its own parsing without recourse
// to compiler parser machinery, and avoids features that require C++ runtime
// library support. A format expression is a pointer to a fixed size
// character string, with an explicit length. Class function Check analyzes
// the expression for syntax and semantic errors and warnings. When an error
// or warning is found, a caller-supplied reporter function is called, which
// may request early termination of validation analysis when some threshold
// number of errors have been reported. If the context is a READ, WRITE,
// or PRINT statement, rather than a FORMAT statement, statement-specific
// checks are also done.
namespace Fortran::common {
struct FormatMessage {
const char *text; // message text; may have one %s argument
const char *arg; // optional %s argument value
int offset; // offset to message marker
int length; // length of message marker
bool isError; // vs. warning
};
template<typename CHAR = char> class FormatValidator {
public:
using Reporter = std::function<bool(const FormatMessage &)>;
FormatValidator(const CHAR *format, size_t length, Reporter reporter,
IoStmtKind stmt = IoStmtKind::None)
: format_{format}, end_{format + length}, reporter_{reporter}, stmt_{stmt},
cursor_{format - 1} {
CHECK(format);
}
bool Check();
private:
enum class TokenKind {
None,
A,
B,
BN,
BZ,
D,
DC,
DP,
DT,
E,
EN,
ES,
EX,
F,
G,
I,
L,
O,
P,
RC,
RD,
RN,
RP,
RU,
RZ,
S,
SP,
SS,
T,
TL,
TR,
X,
Z,
Colon,
Slash,
Backslash, // nonstandard: inhibit newline on output
Dollar, // nonstandard: inhibit newline on output on terminals
Star,
LParen,
RParen,
Comma,
Point,
Sign,
UnsignedInteger, // value in integerValue_
String, // char-literal-constant or Hollerith constant
};
struct Token {
Token &set_kind(TokenKind kind) {
kind_ = kind;
return *this;
}
Token &set_offset(int offset) {
offset_ = offset;
return *this;
}
Token &set_length(int length) {
length_ = length;
return *this;
}
TokenKind kind() const { return kind_; }
int offset() const { return offset_; }
int length() const { return length_; }
bool IsSet() { return kind_ != TokenKind::None; }
private:
TokenKind kind_{TokenKind::None};
int offset_{0};
int length_{1};
};
void ReportWarning(const char *text) { ReportWarning(text, token_); }
void ReportWarning(
const char *text, Token &token, const char *arg = nullptr) {
FormatMessage msg{
text, arg ? arg : argString_, token.offset(), token.length(), false};
reporterExit_ |= reporter_(msg);
}
void ReportError(const char *text) { ReportError(text, token_); }
void ReportError(const char *text, Token &token, const char *arg = nullptr) {
if (suppressMessageCascade_) {
return;
}
formatHasErrors_ = true;
suppressMessageCascade_ = true;
FormatMessage msg{
text, arg ? arg : argString_, token.offset(), token.length(), true};
reporterExit_ |= reporter_(msg);
}
void SetLength() { SetLength(token_); }
void SetLength(Token &token) {
token.set_length(cursor_ - format_ - token.offset() + (cursor_ < end_));
}
CHAR NextChar();
CHAR LookAheadChar();
void Advance(TokenKind);
void NextToken();
void check_r(bool allowed = true);
bool check_w();
void check_m();
bool check_d();
void check_e();
const CHAR *const format_; // format text
const CHAR *const end_; // one-past-last of format_ text
Reporter reporter_;
IoStmtKind stmt_;
const CHAR *cursor_{}; // current location in format_
const CHAR *laCursor_{}; // lookahead cursor
Token token_{}; // current token
int64_t integerValue_{-1}; // value of UnsignedInteger token
Token knrToken_{}; // k, n, or r UnsignedInteger token
int64_t knrValue_{-1}; // -1 ==> not present
int64_t wValue_{-1};
char argString_[3]{}; // 1-2 character msg arg; usually edit descriptor name
bool formatHasErrors_{false};
bool unterminatedFormatError_{false};
bool suppressMessageCascade_{false};
bool reporterExit_{false};
};
template<typename CHAR> CHAR FormatValidator<CHAR>::NextChar() {
for (++cursor_; cursor_ < end_; ++cursor_) {
if (*cursor_ != ' ') {
return toupper(*cursor_);
}
}
cursor_ = end_; // don't allow cursor_ > end_
return ' ';
}
template<typename CHAR> CHAR FormatValidator<CHAR>::LookAheadChar() {
for (laCursor_ = cursor_ + 1; laCursor_ < end_; ++laCursor_) {
if (*laCursor_ != ' ') {
return toupper(*laCursor_);
}
}
laCursor_ = end_; // don't allow laCursor_ > end_
return ' ';
}
// After a call to LookAheadChar, set token kind and advance cursor to laCursor.
template<typename CHAR> void FormatValidator<CHAR>::Advance(TokenKind tk) {
cursor_ = laCursor_;
token_.set_kind(tk);
}
template<typename CHAR> void FormatValidator<CHAR>::NextToken() {
// At entry, cursor_ points before the start of the next token.
// At exit, cursor_ points to last CHAR of token_.
CHAR c{NextChar()};
token_.set_kind(TokenKind::None);
token_.set_offset(cursor_ - format_);
token_.set_length(1);
if (c == '_' && integerValue_ >= 0) { // C1305, C1309, C1310, C1312, C1313
ReportError("Kind parameter '_' character in format expression");
}
integerValue_ = -1;
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
int64_t lastValue;
const CHAR *lastCursor;
integerValue_ = 0;
bool overflow{false};
do {
lastValue = integerValue_;
lastCursor = cursor_;
integerValue_ = 10 * integerValue_ + c - '0';
if (lastValue > integerValue_) {
overflow = true;
}
c = NextChar();
} while (c >= '0' && c <= '9');
cursor_ = lastCursor;
token_.set_kind(TokenKind::UnsignedInteger);
if (overflow) {
SetLength();
ReportError("Integer overflow in format expression");
break;
}
if (LookAheadChar() != 'H') {
break;
}
// Hollerith constant
if (laCursor_ + integerValue_ < end_) {
token_.set_kind(TokenKind::String);
cursor_ = laCursor_ + integerValue_;
} else {
token_.set_kind(TokenKind::None);
cursor_ = end_;
}
SetLength();
if (stmt_ == IoStmtKind::Read) { // 13.3.2p6
ReportError("'H' edit descriptor in READ format expression");
} else if (token_.kind() == TokenKind::None) {
ReportError("Unterminated 'H' edit descriptor");
} else {
ReportWarning("Legacy 'H' edit descriptor");
}
break;
}
case 'A': token_.set_kind(TokenKind::A); break;
case 'B':
switch (LookAheadChar()) {
case 'N': Advance(TokenKind::BN); break;
case 'Z': Advance(TokenKind::BZ); break;
default: token_.set_kind(TokenKind::B); break;
}
break;
case 'D':
switch (LookAheadChar()) {
case 'C': Advance(TokenKind::DC); break;
case 'P': Advance(TokenKind::DP); break;
case 'T': Advance(TokenKind::DT); break;
default: token_.set_kind(TokenKind::D); break;
}
break;
case 'E':
switch (LookAheadChar()) {
case 'N': Advance(TokenKind::EN); break;
case 'S': Advance(TokenKind::ES); break;
case 'X': Advance(TokenKind::EX); break;
default: token_.set_kind(TokenKind::E); break;
}
break;
case 'F': token_.set_kind(TokenKind::F); break;
case 'G': token_.set_kind(TokenKind::G); break;
case 'I': token_.set_kind(TokenKind::I); break;
case 'L': token_.set_kind(TokenKind::L); break;
case 'O': token_.set_kind(TokenKind::O); break;
case 'P': token_.set_kind(TokenKind::P); break;
case 'R':
switch (LookAheadChar()) {
case 'C': Advance(TokenKind::RC); break;
case 'D': Advance(TokenKind::RD); break;
case 'N': Advance(TokenKind::RN); break;
case 'P': Advance(TokenKind::RP); break;
case 'U': Advance(TokenKind::RU); break;
case 'Z': Advance(TokenKind::RZ); break;
default: token_.set_kind(TokenKind::None); break;
}
break;
case 'S':
switch (LookAheadChar()) {
case 'P': Advance(TokenKind::SP); break;
case 'S': Advance(TokenKind::SS); break;
default: token_.set_kind(TokenKind::S); break;
}
break;
case 'T':
switch (LookAheadChar()) {
case 'L': Advance(TokenKind::TL); break;
case 'R': Advance(TokenKind::TR); break;
default: token_.set_kind(TokenKind::T); break;
}
break;
case 'X': token_.set_kind(TokenKind::X); break;
case 'Z': token_.set_kind(TokenKind::Z); break;
case '-':
case '+': token_.set_kind(TokenKind::Sign); break;
case '/': token_.set_kind(TokenKind::Slash); break;
case '(': token_.set_kind(TokenKind::LParen); break;
case ')': token_.set_kind(TokenKind::RParen); break;
case '.': token_.set_kind(TokenKind::Point); break;
case ':': token_.set_kind(TokenKind::Colon); break;
case '\\': token_.set_kind(TokenKind::Backslash); break;
case '$': token_.set_kind(TokenKind::Dollar); break;
case '*':
token_.set_kind(LookAheadChar() == '(' ? TokenKind::Star : TokenKind::None);
break;
case ',': {
token_.set_kind(TokenKind::Comma);
CHAR laChar = LookAheadChar();
if (laChar == ',') {
Advance(TokenKind::Comma);
token_.set_offset(cursor_ - format_);
ReportError("Unexpected ',' in format expression");
} else if (laChar == ')') {
ReportError("Unexpected ',' before ')' in format expression");
}
break;
}
case '\'':
case '"':
for (++cursor_; cursor_ < end_; ++cursor_) {
if (*cursor_ == c) {
if (auto nc{cursor_ + 1}; nc < end_ && *nc != c) {
token_.set_kind(TokenKind::String);
break;
}
++cursor_;
}
}
SetLength();
if (stmt_ == IoStmtKind::Read) { // 13.3.2p6
ReportError("String edit descriptor in READ format expression");
} else if (token_.kind() != TokenKind::String) {
ReportError("Unterminated string");
}
break;
default:
if (cursor_ >= end_ && !unterminatedFormatError_) {
suppressMessageCascade_ = false;
ReportError("Unterminated format expression");
unterminatedFormatError_ = true;
}
token_.set_kind(TokenKind::None);
break;
}
SetLength();
}
template<typename CHAR> void FormatValidator<CHAR>::check_r(bool allowed) {
if (!allowed && knrValue_ >= 0) {
ReportError("Repeat specifier before '%s' edit descriptor", knrToken_);
} else if (knrValue_ == 0) {
ReportError("'%s' edit descriptor repeat specifier must be positive",
knrToken_); // C1304
}
};
// Return the predicate "w value is present" to control further processing.
template<typename CHAR> bool FormatValidator<CHAR>::check_w() {
if (token_.kind() == TokenKind::UnsignedInteger) {
wValue_ = integerValue_;
if (wValue_ == 0 &&
(*argString_ == 'A' || *argString_ == 'L' ||
stmt_ == IoStmtKind::Read)) { // C1306, 13.7.2.1p6
ReportError("'%s' edit descriptor 'w' value must be positive");
}
NextToken();
return true;
}
if (*argString_ != 'A') {
ReportWarning("Expected '%s' edit descriptor 'w' value"); // C1306
}
return false;
};
template<typename CHAR> void FormatValidator<CHAR>::check_m() {
if (token_.kind() != TokenKind::Point) {
return;
}
NextToken();
if (token_.kind() != TokenKind::UnsignedInteger) {
ReportError("Expected '%s' edit descriptor 'm' value after '.'");
return;
}
if ((stmt_ == IoStmtKind::Print || stmt_ == IoStmtKind::Write) &&
wValue_ > 0 && integerValue_ > wValue_) { // 13.7.2.2p5, 13.7.2.4p6
ReportError("'%s' edit descriptor 'm' value is greater than 'w' value");
}
NextToken();
};
// Return the predicate "d value is present" to control further processing.
template<typename CHAR> bool FormatValidator<CHAR>::check_d() {
if (token_.kind() != TokenKind::Point) {
ReportError("Expected '%s' edit descriptor '.d' value");
return false;
}
NextToken();
if (token_.kind() != TokenKind::UnsignedInteger) {
ReportError("Expected '%s' edit descriptor 'd' value after '.'");
return false;
}
NextToken();
return true;
};
template<typename CHAR> void FormatValidator<CHAR>::check_e() {
if (token_.kind() != TokenKind::E) {
return;
}
NextToken();
if (token_.kind() != TokenKind::UnsignedInteger) {
ReportError("Expected '%s' edit descriptor 'e' value after 'E'");
return;
}
NextToken();
};
template<typename CHAR> bool FormatValidator<CHAR>::Check() {
if (!*format_) {
ReportError("Empty format expression");
return formatHasErrors_;
}
NextToken();
if (token_.kind() != TokenKind::LParen) {
ReportError("Format expression must have an initial '('");
return formatHasErrors_;
}
NextToken();
int nestLevel{0}; // Outer level ()s are at level 0.
Token starToken{}; // unlimited format token
bool hasDataEditDesc{false};
// Subject to error recovery exceptions, a loop iteration processes an
// edit descriptor or does list management. The loop terminates when
// - a level-0 right paren is processed (format may be valid)
// - the end of an incomplete format is reached
// - the error reporter requests termination (error threshold reached)
while (!reporterExit_) {
Token signToken{};
knrValue_ = -1; // -1 ==> not present
wValue_ = -1;
bool commaRequired{true};
if (token_.kind() == TokenKind::Sign) {
signToken = token_;
NextToken();
}
if (token_.kind() == TokenKind::UnsignedInteger) {
knrToken_ = token_;
knrValue_ = integerValue_;
NextToken();
}
if (signToken.IsSet() && (knrValue_ < 0 || token_.kind() != TokenKind::P)) {
argString_[0] = format_[signToken.offset()];
argString_[1] = 0;
ReportError("Unexpected '%s' in format expression", signToken);
}
// Default message argument.
// Alphabetic edit descriptor names are one or two characters in length.
argString_[0] = toupper(format_[token_.offset()]);
argString_[1] = token_.length() > 1 ? toupper(*cursor_) : 0;
// Process one format edit descriptor or do format list management.
switch (token_.kind()) {
case TokenKind::A:
// R1307 data-edit-desc -> A [w]
hasDataEditDesc = true;
check_r();
NextToken();
check_w();
break;
case TokenKind::B:
case TokenKind::I:
case TokenKind::O:
case TokenKind::Z:
// R1307 data-edit-desc -> B w [. m] | I w [. m] | O w [. m] | Z w [. m]
hasDataEditDesc = true;
check_r();
NextToken();
if (check_w()) {
check_m();
}
break;
case TokenKind::D:
case TokenKind::F:
// R1307 data-edit-desc -> D w . d | F w . d
hasDataEditDesc = true;
check_r();
NextToken();
if (check_w()) {
check_d();
}
break;
case TokenKind::E:
case TokenKind::EN:
case TokenKind::ES:
case TokenKind::EX:
// R1307 data-edit-desc ->
// E w . d [E e] | EN w . d [E e] | ES w . d [E e] | EX w . d [E e]
hasDataEditDesc = true;
check_r();
NextToken();
if (check_w() && check_d()) {
check_e();
}
break;
case TokenKind::G:
// R1307 data-edit-desc -> G w [. d [E e]]
hasDataEditDesc = true;
check_r();
NextToken();
if (check_w()) {
if (wValue_ > 0) {
if (check_d()) { // C1307
check_e();
}
} else if (token_.kind() == TokenKind::Point && check_d() &&
token_.kind() == TokenKind::E) {
ReportError("Unexpected 'e' in 'G0' edit descriptor"); // C1308
NextToken();
if (token_.kind() == TokenKind::UnsignedInteger) {
NextToken();
}
}
}
break;
case TokenKind::L:
// R1307 data-edit-desc -> L w
hasDataEditDesc = true;
check_r();
NextToken();
check_w();
break;
case TokenKind::DT:
// R1307 data-edit-desc -> DT [char-literal-constant] [( v-list )]
hasDataEditDesc = true;
check_r();
NextToken();
if (token_.kind() == TokenKind::String) {
NextToken();
}
if (token_.kind() == TokenKind::LParen) {
do {
NextToken();
if (token_.kind() == TokenKind::Sign) {
NextToken();
}
if (token_.kind() != TokenKind::UnsignedInteger) {
ReportError(
"Expected integer constant in 'DT' edit descriptor v-list");
break;
}
NextToken();
} while (token_.kind() == TokenKind::Comma);
if (token_.kind() != TokenKind::RParen) {
ReportError("Expected ',' or ')' in 'DT' edit descriptor v-list");
while (cursor_ < end_ && token_.kind() != TokenKind::RParen) {
NextToken();
}
}
NextToken();
}
break;
case TokenKind::String:
// R1304 data-edit-desc -> char-string-edit-desc
if (knrValue_ >= 0) {
ReportError("Repeat specifier before character string edit descriptor",
knrToken_);
}
NextToken();
break;
case TokenKind::BN:
case TokenKind::BZ:
case TokenKind::DC:
case TokenKind::DP:
case TokenKind::RC:
case TokenKind::RD:
case TokenKind::RN:
case TokenKind::RP:
case TokenKind::RU:
case TokenKind::RZ:
case TokenKind::S:
case TokenKind::SP:
case TokenKind::SS:
// R1317 sign-edit-desc -> SS | SP | S
// R1318 blank-interp-edit-desc -> BN | BZ
// R1319 round-edit-desc -> RU | RD | RZ | RN | RC | RP
// R1320 decimal-edit-desc -> DC | DP
check_r(false);
NextToken();
break;
case TokenKind::P: {
// R1313 control-edit-desc -> k P
if (knrValue_ < 0) {
ReportError("'P' edit descriptor must have a scale factor");
}
// Diagnosing C1302 may require multiple token lookahead.
// Save current cursor position to enable backup.
const CHAR *saveCursor{cursor_};
NextToken();
if (token_.kind() == TokenKind::UnsignedInteger) {
NextToken();
}
switch (token_.kind()) {
case TokenKind::D:
case TokenKind::E:
case TokenKind::EN:
case TokenKind::ES:
case TokenKind::EX:
case TokenKind::F:
case TokenKind::G: commaRequired = false; break;
default:;
}
cursor_ = saveCursor;
NextToken();
break;
}
case TokenKind::T:
case TokenKind::TL:
case TokenKind::TR:
// R1315 position-edit-desc -> T n | TL n | TR n
check_r(false);
NextToken();
if (integerValue_ <= 0) { // C1311
ReportError("'%s' edit descriptor must have a positive position value");
}
NextToken();
break;
case TokenKind::X:
// R1315 position-edit-desc -> n X
if (knrValue_ == 0) { // C1311
ReportError("'X' edit descriptor must have a positive position value",
knrToken_);
} else if (knrValue_ < 0) {
ReportWarning(
"'X' edit descriptor must have a positive position value");
}
NextToken();
break;
case TokenKind::Colon:
// R1313 control-edit-desc -> :
check_r(false);
commaRequired = false;
NextToken();
break;
case TokenKind::Slash:
// R1313 control-edit-desc -> [r] /
commaRequired = false;
NextToken();
break;
case TokenKind::Backslash:
check_r(false);
ReportWarning("Non-standard '\\' edit descriptor");
NextToken();
break;
case TokenKind::Dollar:
check_r(false);
ReportWarning("Non-standard '$' edit descriptor");
NextToken();
break;
case TokenKind::Star:
// NextToken assigns a token kind of Star only if * is followed by (.
// So the next token is guaranteed to be LParen.
if (nestLevel > 0) {
ReportError("Nested unlimited format item list");
}
starToken = token_;
if (knrValue_ >= 0) {
ReportError(
"Repeat specifier before unlimited format item list", knrToken_);
}
hasDataEditDesc = false;
NextToken();
// fall through
case TokenKind::LParen:
if (knrValue_ == 0) {
ReportError("List repeat specifier must be positive", knrToken_);
}
++nestLevel;
break;
case TokenKind::RParen:
if (knrValue_ >= 0) {
ReportError("Unexpected integer constant", knrToken_);
}
do {
if (nestLevel == 0) {
// Any characters after level-0 ) are ignored.
return formatHasErrors_; // normal exit (may have messages)
}
if (nestLevel == 1 && starToken.IsSet() && !hasDataEditDesc) {
SetLength(starToken);
ReportError( // C1303
"Unlimited format item list must contain a data edit descriptor",
starToken);
}
--nestLevel;
NextToken();
} while (token_.kind() == TokenKind::RParen);
if (nestLevel == 0 && starToken.IsSet()) {
ReportError("Character in format after unlimited format item list");
}
break;
case TokenKind::Comma:
if (knrValue_ >= 0) {
ReportError("Unexpected integer constant", knrToken_);
}
if (suppressMessageCascade_ || reporterExit_) {
break;
}
// fall through
default: ReportError("Unexpected '%s' in format expression"); NextToken();
}
// Process comma separator and exit an incomplete format.
switch (token_.kind()) {
case TokenKind::Colon: // Comma not required; token not yet processed.
case TokenKind::Slash: // Comma not required; token not yet processed.
case TokenKind::RParen: // Comma not allowed; token not yet processed.
suppressMessageCascade_ = false;
break;
case TokenKind::LParen: // Comma not allowed; token already processed.
case TokenKind::Comma: // Normal comma case; move past token.
suppressMessageCascade_ = false;
NextToken();
break;
case TokenKind::Sign: // Error; main switch has a better message.
case TokenKind::None: // Error; token not yet processed.
if (cursor_ >= end_) {
return formatHasErrors_; // incomplete format error exit
}
break;
default:
// Possible first token of the next format item; token not yet processed.
if (commaRequired) {
ReportError("Expected ',' or ')' in format expression"); // C1302
}
}
}
return formatHasErrors_; // error reporter (message threshold) exit
}
}
#endif // FORTRAN_COMMON_FORMAT_H_

View File

@ -15,12 +15,83 @@
#include "check-io.h"
#include "expression.h"
#include "tools.h"
#include "../common/format.h"
#include "../parser/tools.h"
namespace Fortran::semantics {
// TODO: C1234, C1235 -- defined I/O constraints
class FormatErrorReporter {
public:
FormatErrorReporter(SemanticsContext &context,
const parser::CharBlock &formatCharBlock, int errorAllowance = 3)
: context_{context}, formatCharBlock_{formatCharBlock},
errorAllowance_{errorAllowance} {}
bool Say(const common::FormatMessage &);
private:
SemanticsContext &context_;
const parser::CharBlock &formatCharBlock_;
int errorAllowance_; // initialized to maximum number of errors to report
};
bool FormatErrorReporter::Say(const common::FormatMessage &msg) {
if (!msg.isError && !context_.warnOnNonstandardUsage()) {
return false;
}
parser::MessageFormattedText text{
parser::MessageFixedText(msg.text, strlen(msg.text), msg.isError),
msg.arg};
if (formatCharBlock_.size()) {
// The input format is a folded expression. Error markers span the full
// original unfolded expression in formatCharBlock_.
context_.Say(formatCharBlock_, text);
} else {
// The input format is a source expression. Error markers have an offset
// and length relative to the beginning of formatCharBlock_.
parser::CharBlock messageCharBlock{
parser::CharBlock(formatCharBlock_.begin() + msg.offset, msg.length)};
context_.Say(messageCharBlock, text);
}
return msg.isError && --errorAllowance_ <= 0;
}
void IoChecker::Enter(
const parser::Statement<common::Indirection<parser::FormatStmt>> &stmt) {
if (!stmt.label.has_value()) {
context_.Say("Format statement must be labeled"_err_en_US); // C1301
}
const char *formatStart{static_cast<const char *>(
std::memchr(stmt.source.begin(), '(', stmt.source.size()))};
parser::CharBlock reporterCharBlock{formatStart, static_cast<std::size_t>(0)};
FormatErrorReporter reporter{context_, reporterCharBlock};
auto reporterWrapper{[&](const auto &msg) { return reporter.Say(msg); }};
switch (context_.GetDefaultKind(TypeCategory::Character)) {
case 1: {
common::FormatValidator<char> validator{formatStart,
stmt.source.size() - (formatStart - stmt.source.begin()),
reporterWrapper};
validator.Check();
break;
}
case 2: { // TODO: Get this to work.
common::FormatValidator<char16_t> validator{
/*???*/ nullptr, /*???*/ 0, reporterWrapper};
validator.Check();
break;
}
case 4: { // TODO: Get this to work.
common::FormatValidator<char32_t> validator{
/*???*/ nullptr, /*???*/ 0, reporterWrapper};
validator.Check();
break;
}
default: CRASH_NO_CASE;
}
}
void IoChecker::Enter(const parser::ConnectSpec &spec) {
// ConnectSpec context FileNameExpr
if (std::get_if<parser::FileNameExpr>(&spec.u)) {
@ -96,15 +167,53 @@ void IoChecker::Enter(const parser::FileUnitNumber &spec) {
void IoChecker::Enter(const parser::Format &spec) {
SetSpecifier(IoSpecKind::Fmt);
flags_.set(Flag::FmtOrNml);
if (std::get_if<parser::Star>(&spec.u)) {
flags_.set(Flag::StarFmt);
} else if (std::get_if<parser::Label>(&spec.u)) {
// Format statement format should be validated elsewhere.
flags_.set(Flag::LabelFmt);
} else {
flags_.set(Flag::CharFmt);
// TODO: validate compile-time constant format -- 12.6.2.2
}
std::visit(
common::visitors{
[&](const parser::Label &) { flags_.set(Flag::LabelFmt); },
[&](const parser::Star &) { flags_.set(Flag::StarFmt); },
[&](const parser::DefaultCharExpr &format) {
flags_.set(Flag::CharFmt);
const std::optional<std::string> constantFormat{
GetConstExpr<std::string>(format)};
if (!constantFormat) {
return;
}
// validate constant format -- 12.6.2.2
bool isFolded{constantFormat->size() !=
format.thing.value().source.size() - 2};
parser::CharBlock reporterCharBlock{isFolded
? parser::CharBlock{format.thing.value().source}
: parser::CharBlock{format.thing.value().source.begin() + 1,
static_cast<std::size_t>(0)}};
FormatErrorReporter reporter{context_, reporterCharBlock};
auto reporterWrapper{
[&](const auto &msg) { return reporter.Say(msg); }};
switch (context_.GetDefaultKind(TypeCategory::Character)) {
case 1: {
common::FormatValidator<char> validator{constantFormat->c_str(),
constantFormat->length(), reporterWrapper, stmt_};
validator.Check();
break;
}
case 2: {
// TODO: Get this to work. (Maybe combine with earlier instance?)
common::FormatValidator<char16_t> validator{
/*???*/ nullptr, /*???*/ 0, reporterWrapper, stmt_};
validator.Check();
break;
}
case 4: {
// TODO: Get this to work. (Maybe combine with earlier instance?)
common::FormatValidator<char32_t> validator{
/*???*/ nullptr, /*???*/ 0, reporterWrapper, stmt_};
validator.Check();
break;
}
default: CRASH_NO_CASE;
}
},
},
spec.u);
}
void IoChecker::Enter(const parser::IdExpr &spec) {
@ -272,7 +381,7 @@ void IoChecker::Enter(const parser::IoUnit &spec) {
if (!ExprTypeKindIsDefault(*expr, context_)) {
// This may be too restrictive; other kinds may be valid.
context_.Say( // C1202
"invalid character kind for an internal file variable"_err_en_US);
"Invalid character kind for an internal file variable"_err_en_US);
}
}
SetSpecifier(IoSpecKind::Unit);
@ -311,7 +420,7 @@ void IoChecker::Enter(const parser::StatusExpr &spec) {
CHECK(stmt_ == IoStmtKind::Close);
if (s != "DELETE" && s != "KEEP") {
context_.Say(parser::FindSourceLocation(spec),
"invalid STATUS value '%s'"_err_en_US, *charConst);
"Invalid STATUS value '%s'"_err_en_US, *charConst);
}
}
}
@ -474,7 +583,7 @@ void IoChecker::SetSpecifier(IoSpecKind specKind) {
}
// C1203, C1207, C1210, C1236, C1239, C1242, C1245
if (specifierSet_.test(specKind)) {
context_.Say("duplicate %s specifier"_err_en_US,
context_.Say("Duplicate %s specifier"_err_en_US,
parser::ToUpperCaseLetters(common::EnumToString(specKind)));
}
specifierSet_.set(specKind);
@ -504,7 +613,7 @@ void IoChecker::CheckStringValue(IoSpecKind specKind, const std::string &value,
{IoSpecKind::Dispose, {"DELETE", "KEEP"}},
};
if (!specValues.at(specKind).count(parser::ToUpperCaseLetters(value))) {
context_.Say(source, "invalid %s value '%s'"_err_en_US,
context_.Say(source, "Invalid %s value '%s'"_err_en_US,
parser::ToUpperCaseLetters(common::EnumToString(specKind)), value);
}
}
@ -534,7 +643,7 @@ void IoChecker::CheckForRequiredSpecifier(
void IoChecker::CheckForRequiredSpecifier(
IoSpecKind specKind1, IoSpecKind specKind2) const {
if (specifierSet_.test(specKind1) && !specifierSet_.test(specKind2)) {
context_.Say("if %s appears, %s must also appear"_err_en_US,
context_.Say("If %s appears, %s must also appear"_err_en_US,
parser::ToUpperCaseLetters(common::EnumToString(specKind1)),
parser::ToUpperCaseLetters(common::EnumToString(specKind2)));
}
@ -543,7 +652,7 @@ void IoChecker::CheckForRequiredSpecifier(
void IoChecker::CheckForRequiredSpecifier(
IoSpecKind specKind, bool condition, const std::string &s) const {
if (specifierSet_.test(specKind) && !condition) {
context_.Say("if %s appears, %s must also appear"_err_en_US,
context_.Say("If %s appears, %s must also appear"_err_en_US,
parser::ToUpperCaseLetters(common::EnumToString(specKind)), s);
}
}
@ -551,7 +660,7 @@ void IoChecker::CheckForRequiredSpecifier(
void IoChecker::CheckForRequiredSpecifier(
bool condition, const std::string &s, IoSpecKind specKind) const {
if (condition && !specifierSet_.test(specKind)) {
context_.Say("if %s appears, %s must also appear"_err_en_US, s,
context_.Say("If %s appears, %s must also appear"_err_en_US, s,
parser::ToUpperCaseLetters(common::EnumToString(specKind)));
}
}
@ -559,7 +668,7 @@ void IoChecker::CheckForRequiredSpecifier(
void IoChecker::CheckForRequiredSpecifier(bool condition1,
const std::string &s1, bool condition2, const std::string &s2) const {
if (condition1 && !condition2) {
context_.Say("if %s appears, %s must also appear"_err_en_US, s1, s2);
context_.Say("If %s appears, %s must also appear"_err_en_US, s1, s2);
}
}
@ -574,7 +683,7 @@ void IoChecker::CheckForProhibitedSpecifier(IoSpecKind specKind) const {
void IoChecker::CheckForProhibitedSpecifier(
IoSpecKind specKind1, IoSpecKind specKind2) const {
if (specifierSet_.test(specKind1) && specifierSet_.test(specKind2)) {
context_.Say("if %s appears, %s must not appear"_err_en_US,
context_.Say("If %s appears, %s must not appear"_err_en_US,
parser::ToUpperCaseLetters(common::EnumToString(specKind1)),
parser::ToUpperCaseLetters(common::EnumToString(specKind2)));
}
@ -583,7 +692,7 @@ void IoChecker::CheckForProhibitedSpecifier(
void IoChecker::CheckForProhibitedSpecifier(
IoSpecKind specKind, bool condition, const std::string &s) const {
if (specifierSet_.test(specKind) && condition) {
context_.Say("if %s appears, %s must not appear"_err_en_US,
context_.Say("If %s appears, %s must not appear"_err_en_US,
parser::ToUpperCaseLetters(common::EnumToString(specKind)), s);
}
}
@ -591,7 +700,7 @@ void IoChecker::CheckForProhibitedSpecifier(
void IoChecker::CheckForProhibitedSpecifier(
bool condition, const std::string &s, IoSpecKind specKind) const {
if (condition && specifierSet_.test(specKind)) {
context_.Say("if %s appears, %s must not appear"_err_en_US, s,
context_.Say("If %s appears, %s must not appear"_err_en_US, s,
parser::ToUpperCaseLetters(common::EnumToString(specKind)));
}
}

View File

@ -40,6 +40,9 @@ public:
void Enter(const parser::WaitStmt &) { Init(IoStmtKind::Wait); }
void Enter(const parser::WriteStmt &) { Init(IoStmtKind::Write); }
void Enter(
const parser::Statement<common::Indirection<parser::FormatStmt>> &);
void Enter(const parser::ConnectSpec &);
void Enter(const parser::ConnectSpec::CharExpr &);
void Enter(const parser::ConnectSpec::Newunit &);
@ -84,7 +87,7 @@ private:
ENUM_CLASS(Flag, IoControlList, InternalUnit, NumberUnit, StarUnit, CharFmt,
LabelFmt, StarFmt, FmtOrNml, KnownAccess, AccessDirect, AccessStream,
AdvanceYes, AsynchronousYes, KnownStatus, StatusNew, StatusReplace,
StatusScratch, DataList);
StatusScratch, DataList)
template<typename R, typename T> std::optional<R> GetConstExpr(const T &x) {
using DefaultCharConstantType =

View File

@ -32,6 +32,10 @@ set(ERROR_TESTS
io04.f90
io05.f90
io06.f90
io07.f90
io08.f90
io09.f90
io10.f90
kinds02.f90
resolve01.f90
resolve02.f90

View File

@ -76,35 +76,35 @@
open(access='STREAM', 90) ! nonstandard
!ERROR: OPEN statement must have a UNIT or NEWUNIT specifier
!ERROR: if ACCESS='DIRECT' appears, RECL must also appear
!ERROR: If ACCESS='DIRECT' appears, RECL must also appear
open(access='direct')
!ERROR: if STATUS='STREAM' appears, RECL must not appear
!ERROR: If STATUS='STREAM' appears, RECL must not appear
open(10, access='st'//'ream', recl=13)
!ERROR: duplicate NEWUNIT specifier
!ERROR: if NEWUNIT appears, FILE or STATUS must also appear
!ERROR: Duplicate NEWUNIT specifier
!ERROR: If NEWUNIT appears, FILE or STATUS must also appear
open(newunit=n, newunit=nn, iostat=stat4)
!ERROR: duplicate UNIT specifier
!ERROR: Duplicate UNIT specifier
open(unit=100, unit=100)
!ERROR: duplicate UNIT specifier
!ERROR: Duplicate UNIT specifier
open(101, delim=delim_(1), unit=102)
!ERROR: duplicate UNIT specifier
!ERROR: Duplicate UNIT specifier
open(unit=103, &
unit=104, iostat=stat8)
!ERROR: duplicate UNIT specifier
!ERROR: if ACCESS='DIRECT' appears, RECL must also appear
!ERROR: Duplicate UNIT specifier
!ERROR: If ACCESS='DIRECT' appears, RECL must also appear
open(access='dir'//'ect', 9, 9) ! nonstandard
!ERROR: duplicate ROUND specifier
!ERROR: Duplicate ROUND specifier
open(105, round=round_(1), pad='no', round='nearest')
!ERROR: if NEWUNIT appears, UNIT must not appear
!ERROR: if NEWUNIT appears, FILE or STATUS must also appear
!ERROR: If NEWUNIT appears, UNIT must not appear
!ERROR: If NEWUNIT appears, FILE or STATUS must also appear
open(106, newunit=n)
!ERROR: RECL value (-30) must be positive
@ -113,27 +113,27 @@
!ERROR: RECL value (-36) must be positive
open(108, recl=- - (-36)) ! nonstandard
!ERROR: invalid ACTION value 'reedwrite'
!ERROR: Invalid ACTION value 'reedwrite'
open(109, access=Access, action='reedwrite', recl=77)
!ERROR: invalid ACTION value 'nonsense'
!ERROR: Invalid ACTION value 'nonsense'
open(110, action=''//'non'//'sense', recl=77)
!ERROR: invalid STATUS value 'cold'
!ERROR: Invalid STATUS value 'cold'
open(111, status='cold')
!ERROR: invalid STATUS value 'Keep'
!ERROR: Invalid STATUS value 'Keep'
open(112, status='Keep')
!ERROR: if STATUS='NEW' appears, FILE must also appear
!ERROR: If STATUS='NEW' appears, FILE must also appear
open(113, status='new')
!ERROR: if STATUS='REPLACE' appears, FILE must also appear
!ERROR: If STATUS='REPLACE' appears, FILE must also appear
open(114, status='replace')
!ERROR: if STATUS='SCRATCH' appears, FILE must not appear
!ERROR: If STATUS='SCRATCH' appears, FILE must not appear
open(115, file='abc', status='scratch')
!ERROR: if NEWUNIT appears, FILE or STATUS='SCRATCH' must also appear
!ERROR: If NEWUNIT appears, FILE or STATUS='SCRATCH' must also appear
open(newunit=nn, status='old')
end

View File

@ -29,16 +29,16 @@
!ERROR: CLOSE statement must have a UNIT number specifier
close(iostat=stat1)
!ERROR: duplicate UNIT specifier
!ERROR: Duplicate UNIT specifier
close(13, unit=14, err=9)
!ERROR: duplicate ERR specifier
!ERROR: Duplicate ERR specifier
close(err=9, unit=15, err=9, iostat=stat8)
!ERROR: invalid STATUS value 'kept'
!ERROR: Invalid STATUS value 'kept'
close(status='kept', unit=16)
!ERROR: invalid STATUS value 'old'
!ERROR: Invalid STATUS value 'old'
close(status='old', unit=17)
9 continue

View File

@ -59,19 +59,19 @@
decimal='comma', end=9, eor=9, err=9, id=id, iomsg=msg, iostat=stat2, &
pad='no', round='processor_defined', size=kk) jj
!ERROR: invalid character kind for an internal file variable
!ERROR: Invalid character kind for an internal file variable
read(internal_file2, *) jj
!ERROR: invalid character kind for an internal file variable
!ERROR: Invalid character kind for an internal file variable
read(internal_file4, *) jj
!ERROR: duplicate IOSTAT specifier
!ERROR: Duplicate IOSTAT specifier
read(11, pos=ipos, iostat=stat1, iostat=stat2)
!ERROR: duplicate END specifier
!ERROR: Duplicate END specifier
read(11, end=9, pos=ipos, end=9)
!ERROR: duplicate NML specifier
!ERROR: Duplicate NML specifier
read(10, nml=mmm, nml=nnn)
!ERROR: READ statement must have a UNIT specifier
@ -81,66 +81,66 @@
!ERROR: READ statement must not have a SIGN specifier
read(10, delim='quote', sign='plus') jj
!ERROR: if NML appears, REC must not appear
!ERROR: If NML appears, REC must not appear
read(10, nnn, rec=nn)
!ERROR: if NML appears, FMT must not appear
!ERROR: if NML appears, a data list must not appear
!ERROR: If NML appears, FMT must not appear
!ERROR: If NML appears, a data list must not appear
read(10, fmt=*, nml=nnn) jj
!ERROR: if UNIT=* appears, REC must not appear
!ERROR: If UNIT=* appears, REC must not appear
read(*, rec=13)
!ERROR: if UNIT=* appears, POS must not appear
!ERROR: If UNIT=* appears, POS must not appear
read(*, pos=13)
!ERROR: if UNIT=internal-file appears, REC must not appear
!ERROR: If UNIT=internal-file appears, REC must not appear
read(internal_file, rec=13)
!ERROR: if UNIT=internal-file appears, POS must not appear
!ERROR: If UNIT=internal-file appears, POS must not appear
read(internal_file, pos=13)
!ERROR: if REC appears, END must not appear
!ERROR: If REC appears, END must not appear
read(10, fmt='(I4)', end=9, rec=13) jj
!ERROR: if REC appears, FMT=* must not appear
!ERROR: If REC appears, FMT=* must not appear
read(10, *, rec=13) jj
!ERROR: if ADVANCE appears, UNIT=internal-file must not appear
!ERROR: If ADVANCE appears, UNIT=internal-file must not appear
read(internal_file, '(I4)', eor=9, advance='no') jj
!ERROR: if ADVANCE appears, an explicit format must also appear
!ERROR: if EOR appears, ADVANCE with value 'NO' must also appear
!ERROR: If ADVANCE appears, an explicit format must also appear
!ERROR: If EOR appears, ADVANCE with value 'NO' must also appear
read(10, eor=9, advance='yes')
!ERROR: if EOR appears, ADVANCE with value 'NO' must also appear
!ERROR: If EOR appears, ADVANCE with value 'NO' must also appear
read(10, eor=9)
!ERROR: invalid ASYNCHRONOUS value 'nay'
!ERROR: Invalid ASYNCHRONOUS value 'nay'
read(10, asynchronous='nay') ! prog req
!ERROR: if ASYNCHRONOUS='YES' appears, UNIT=number must also appear
!ERROR: If ASYNCHRONOUS='YES' appears, UNIT=number must also appear
read(*, asynchronous='yes')
!ERROR: if ASYNCHRONOUS='YES' appears, UNIT=number must also appear
!ERROR: If ASYNCHRONOUS='YES' appears, UNIT=number must also appear
read(internal_file, asynchronous='y'//'es')
!ERROR: if ID appears, ASYNCHRONOUS='YES' must also appear
!ERROR: If ID appears, ASYNCHRONOUS='YES' must also appear
read(10, id=id)
!ERROR: if ID appears, ASYNCHRONOUS='YES' must also appear
!ERROR: If ID appears, ASYNCHRONOUS='YES' must also appear
read(10, asynchronous='n'//'o', id=id)
!ERROR: if POS appears, REC must not appear
!ERROR: If POS appears, REC must not appear
read(10, pos=13, rec=13) jj
!ERROR: if DECIMAL appears, FMT or NML must also appear
!ERROR: if BLANK appears, FMT or NML must also appear
!ERROR: invalid DECIMAL value 'Punkt'
!ERROR: If DECIMAL appears, FMT or NML must also appear
!ERROR: If BLANK appears, FMT or NML must also appear
!ERROR: Invalid DECIMAL value 'Punkt'
read(10, decimal='Punkt', blank='null') jj
!ERROR: if ROUND appears, FMT or NML must also appear
!ERROR: if PAD appears, FMT or NML must also appear
!ERROR: If ROUND appears, FMT or NML must also appear
!ERROR: If PAD appears, FMT or NML must also appear
read(10, pad='no', round='nearest') jj
!ERROR: ID kind (2) is smaller than default INTEGER kind (4)

View File

@ -58,7 +58,7 @@
print*
print*, 'Ok'
!ERROR: duplicate UNIT specifier
!ERROR: Duplicate UNIT specifier
write(internal_file, unit=*)
!ERROR: WRITE statement must have a UNIT specifier
@ -70,63 +70,65 @@
!ERROR: WRITE statement must not have a PAD specifier
write(*, eor=9, blank='zero', end=9, pad='no')
!ERROR: if NML appears, REC must not appear
!ERROR: if NML appears, FMT must not appear
!ERROR: if NML appears, a data list must not appear
!ERROR: If NML appears, REC must not appear
!ERROR: If NML appears, FMT must not appear
!ERROR: If NML appears, a data list must not appear
write(10, nnn, rec=40, fmt=1) 'Ok'
!ERROR: if UNIT=* appears, POS must not appear
!ERROR: If UNIT=* appears, POS must not appear
write(*, pos=n, nml=nnn)
!ERROR: if UNIT=* appears, REC must not appear
!ERROR: If UNIT=* appears, REC must not appear
write(*, rec=n)
!ERROR: if UNIT=internal-file appears, POS must not appear
!ERROR: If UNIT=internal-file appears, POS must not appear
write(internal_file, err=9, pos=n, nml=nnn)
!ERROR: if UNIT=internal-file appears, REC must not appear
!ERROR: If UNIT=internal-file appears, REC must not appear
write(internal_file, rec=n, err=9)
!ERROR: if UNIT=* appears, REC must not appear
!ERROR: If UNIT=* appears, REC must not appear
write(*, rec=13) 'Ok'
!ERROR: if ADVANCE appears, UNIT=internal-file must not appear
!ERROR: If ADVANCE appears, UNIT=internal-file must not appear
write(internal_file, advance='yes', fmt=1) 'Ok'
!ERROR: if ADVANCE appears, an explicit format must also appear
!ERROR: If ADVANCE appears, an explicit format must also appear
write(10, advance='yes') 'Ok'
!ERROR: invalid ASYNCHRONOUS value 'non'
!ERROR: Invalid ASYNCHRONOUS value 'non'
write(*, asynchronous='non')
!ERROR: if ASYNCHRONOUS='YES' appears, UNIT=number must also appear
!ERROR: If ASYNCHRONOUS='YES' appears, UNIT=number must also appear
write(*, asynchronous='yes')
!ERROR: if ASYNCHRONOUS='YES' appears, UNIT=number must also appear
!ERROR: If ASYNCHRONOUS='YES' appears, UNIT=number must also appear
write(internal_file, asynchronous='yes')
!ERROR: if ID appears, ASYNCHRONOUS='YES' must also appear
!ERROR: If ID appears, ASYNCHRONOUS='YES' must also appear
write(10, *, id=id) "Ok"
!ERROR: if ID appears, ASYNCHRONOUS='YES' must also appear
!ERROR: If ID appears, ASYNCHRONOUS='YES' must also appear
write(10, *, id=id, asynchronous='no') "Ok"
!ERROR: if POS appears, REC must not appear
!ERROR: If POS appears, REC must not appear
write(10, pos=13, rec=13) 'Ok'
!ERROR: if DECIMAL appears, FMT or NML must also appear
!ERROR: if ROUND appears, FMT or NML must also appear
!ERROR: if SIGN appears, FMT or NML must also appear
!ERROR: invalid DECIMAL value 'Komma'
!ERROR: If DECIMAL appears, FMT or NML must also appear
!ERROR: If ROUND appears, FMT or NML must also appear
!ERROR: If SIGN appears, FMT or NML must also appear
!ERROR: Invalid DECIMAL value 'Komma'
write(10, decimal='Komma', sign='plus', round='down') jj
!ERROR: if DELIM appears, FMT=* or NML must also appear
!ERROR: invalid DELIM value 'Nix'
write(delim='Nix', fmt='(A)', unit=10) 'Ok' !C1228
!ERROR: If DELIM appears, FMT=* or NML must also appear
!ERROR: Invalid DELIM value 'Nix'
write(delim='Nix', fmt='(A)', unit=10) 'Ok'
!ERROR: ID kind (1) is smaller than default INTEGER kind (4)
write(id=id1, unit=10, asynchronous='Yes') 'Ok'
write(*, '(X)')
1 format (A)
9 continue
end

View File

@ -49,24 +49,24 @@
!ERROR: INQUIRE statement must have a UNIT number or FILE specifier
inquire(err=9)
!ERROR: if FILE appears, UNIT must not appear
!ERROR: If FILE appears, UNIT must not appear
inquire(10, file='abc', blank=c(22), iostat=stat8)
!ERROR: duplicate FILE specifier
!ERROR: Duplicate FILE specifier
inquire(file='abc', file='xyz')
!ERROR: duplicate FORM specifier
!ERROR: Duplicate FORM specifier
inquire(form=c(1), iostat=stat1, form=c(2), file='abc')
!ERROR: duplicate SIGN specifier
!ERROR: duplicate READ specifier
!ERROR: duplicate WRITE specifier
!ERROR: Duplicate SIGN specifier
!ERROR: Duplicate READ specifier
!ERROR: Duplicate WRITE specifier
inquire(1, read=c(1), write=c(2), sign=c(3), sign=c(4), read=c(5), write=c(1))
!ERROR: duplicate IOMSG specifier
!ERROR: Duplicate IOMSG specifier
inquire(10, iomsg=msg, pos=ipos, iomsg=msg)
!ERROR: if ID appears, PENDING must also appear
!ERROR: If ID appears, PENDING must also appear
inquire(file='abc', id=id)
9 continue

View File

@ -35,22 +35,22 @@
wait(10)
wait(99, id=id1, end=9, eor=9, err=9, iostat=stat1, iomsg=msg1)
!ERROR: duplicate UNIT specifier
!ERROR: Duplicate UNIT specifier
backspace(10, unit=11)
!ERROR: duplicate IOSTAT specifier
!ERROR: Duplicate IOSTAT specifier
endfile(iostat=stat2, err=9, unit=10, iostat=stat8, iomsg=msg1)
!ERROR: REWIND statement must have a UNIT number specifier
rewind(iostat=stat2)
!ERROR: duplicate ERR specifier
!ERROR: duplicate ERR specifier
!ERROR: Duplicate ERR specifier
!ERROR: Duplicate ERR specifier
flush(err=9, unit=10, &
err=9, &
err=9)
!ERROR: duplicate ID specifier
!ERROR: Duplicate ID specifier
!ERROR: WAIT statement must have a UNIT number specifier
wait(id=id2, eor=9, id=id3)

View File

@ -0,0 +1,96 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
1001 format(A)
!ERROR: Format statement must be labeled
format(A)
2001 format(3I8, 3Z8)
2002 format(3I8, Z8)
2003 format( 3 I 8 , 3 Z 8 )
2004 format(20PF10.2)
2005 format(20P,F10.2)
2006 format(20P7F10.2)
2007 format(1X/)
2008 format(/02x)
2009 format(1x/02x)
2010 format(2L2:)
2011 format(:2L2)
2012 format(2L2 : 2L2)
!ERROR: Expected ',' or ')' in format expression
2101 format(3I83Z8, 'abc')
!ERROR: Expected ',' or ')' in format expression
2102 format( 3 I 8 3 Z 8 )
!ERROR: Expected ',' or ')' in format expression
2103 format(3I8 3Z8)
!ERROR: Expected ',' or ')' in format expression
2104 format(3I8 Z8)
!ERROR: Expected ',' or ')' in format expression
2105 format(1X3/)
!ERROR: Expected ',' or ')' in format expression
2106 format(1X003/)
!ERROR: Expected ',' or ')' in format expression
2107 format(3P7I2)
!ERROR: Expected ',' or ')' in format expression
2108 format(3PI2)
3001 format(*(I3))
3002 format(5X,*(2(A)))
!ERROR: Unlimited format item list must contain a data edit descriptor
3101 format(*(X))
!ERROR: Unlimited format item list must contain a data edit descriptor
3102 format(5X,*(2(/)))
!ERROR: Unlimited format item list must contain a data edit descriptor
3103 format(5X, 'abc', *((:)))
4001 format(2(X))
!ERROR: List repeat specifier must be positive
!ERROR: 'DT' edit descriptor repeat specifier must be positive
4101 format(0(X), 0dt)
6001 format(((I0, B0)))
!ERROR: 'A' edit descriptor 'w' value must be positive
!ERROR: 'L' edit descriptor 'w' value must be positive
6101 format((A0), ((L0)))
!ERROR: 'L' edit descriptor 'w' value must be positive
6102 format((3(((L 0 0 0)))))
7001 format(17G8.1, 17G8.1e3)
!ERROR: Expected 'G' edit descriptor '.d' value
7101 format(17G8)
8001 format(9G0.5)
!ERROR: Unexpected 'e' in 'G0' edit descriptor
8101 format(9(G0.5e1))
!ERROR: Unexpected 'e' in 'G0' edit descriptor
8102 format(9(G0.5 E 1))
end

View File

@ -0,0 +1,327 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
write(*,*)
write(*,'()')
write(*,'(A)')
write(*,'(2X:2X)')
write(*,'(2X/2X)')
write(*,'(3/2X)')
write(*,'(3PF5.2)')
write(*,'(+3PF5.2)')
write(*,'(-3PF5.2)')
write(*,'(000p,10p,0p)')
write(*,'(3P7D5.2)')
write(*,'(3P,7F5.2)')
write(*,'(2X,(i3))')
write(*,'(5X,*(2X,I2))')
write(*,'(5X,*(2X,DT))')
write(*,'(*(DT))')
write(*,'(*(DT"value"))')
write(*,'(*(DT(+1,0,-1)))')
write(*,'(*(DT"value"(+1,000,-1)))')
write(*,'(*(DT(0)))')
write(*,'(S,(RZ),2E10.3)')
write(*,'(7I2)')
write(*,'(07I02)')
write(*,'(07I02.01)')
write(*,'(07I02.02)')
write(*,'(I0)')
write(*,'(G4.2)')
write(*,'(G0.8)')
write(*,'(T3)')
write(*,'("abc")')
write(*,'("""abc""")')
write(*,'("a""""bc", 2x)')
write(*,'(3Habc)')
write(*,'(3Habc, 2X, 3X)')
write(*,'(987654321098765432X)')
write(*,'($)')
write(*,'(\)')
write(*,'(RZ,RU,RP,RN,RD,RC,SS,SP,S,3G15.3e2)')
!ERROR: Empty format expression
write(*,"")
!ERROR: Empty format expression
write(*,"" // '' // "")
!ERROR: Format expression must have an initial '('
write(*,'I3')
!ERROR: Unexpected '+' in format expression
write(*,'(+7I2)')
!ERROR: Unexpected '-' in format expression
write(*,'(-7I2)')
!ERROR: 'P' edit descriptor must have a scale factor
write(*,'(P7F5.2)')
!ERROR: 'P' edit descriptor must have a scale factor
write(*,'(P7F' // '5.2)')
!ERROR: Expected ',' or ')' in format expression
write(*,'(3P7I2)')
!ERROR: Expected ',' or ')' in format expression
write(*,'(5X i3)')
!ERROR: Unexpected integer constant
write(*,'(X,3,3L4)')
!ERROR: Unexpected ',' before ')' in format expression
write(*,'(X,i3,)')
!ERROR: Unexpected ',' in format expression
write(*,'(X,i3,,)')
!ERROR: Unexpected ',' in format expression
!ERROR: Unexpected ',' before ')' in format expression
write(*,'(X,i3,,,)')
!ERROR: Unexpected ',' before ')' in format expression
write(*,'(X,(i3,))')
!ERROR: Unexpected '*' in format expression
write(*,'(*)')
!ERROR: Expected integer constant in 'DT' edit descriptor v-list
write(*,'(*(DT(+1,0,=1)))')
!ERROR: Expected integer constant in 'DT' edit descriptor v-list
write(*,'(DT(1,0,+))')
!ERROR: Expected integer constant in 'DT' edit descriptor v-list
write(*,'(DT(1,0,*))')
!ERROR: Expected ',' or ')' in 'DT' edit descriptor v-list
write(*,'(DT(1,0,2*))')
!ERROR: Expected ',' or ')' in 'DT' edit descriptor v-list
write(*,'(DT(1,0,2*,+,?))')
!ERROR: Expected integer constant in 'DT' edit descriptor v-list
!ERROR: Unterminated format expression
write(*,'(DT(1,0,*)')
!ERROR: Expected ',' or ')' in 'DT' edit descriptor v-list
!ERROR: Unterminated format expression
write(*,'(DT(1,0,2*,+,?)')
!ERROR: Unexpected '?' in format expression
!ERROR: Unexpected ',' in format expression
write(*,'(?,*(DT(+1,,1)))')
!ERROR: Repeat specifier before unlimited format item list
!ERROR: Unlimited format item list must contain a data edit descriptor
write(*,'(5X,3*(2(X)))')
!ERROR: Nested unlimited format item list
write(*,'(D12.2,(*(F10.2)))')
!ERROR: Unlimited format item list must contain a data edit descriptor
write(*,'(5X,*(2(X)))')
!ERROR: Character in format after unlimited format item list
write(*,'(*(Z5),*(2F20.3))')
!ERROR: Character in format after unlimited format item list
write(*,'(*(B5),*(2(I5)))')
!ERROR: Character in format after unlimited format item list
write(*,'(*(I5), D12.7)')
!ERROR: 'I' edit descriptor 'm' value is greater than 'w' value
write(*,'(07I02.0 3)')
!ERROR: 'Z' edit descriptor 'm' value is greater than 'w' value
write(*,'(07Z02.4)')
!ERROR: 'I' edit descriptor repeat specifier must be positive
write(*,'(0I2)')
!ERROR: List repeat specifier must be positive
write(*,'(0(I2))')
!ERROR: List repeat specifier must be positive
write(*,'(000(I2))')
!ERROR: List repeat specifier must be positive
!ERROR: 'I' edit descriptor repeat specifier must be positive
write(*,'(0(0I2))')
!ERROR: Kind parameter '_' character in format expression
write(*,'(5_4X)')
!ERROR: Unexpected '+' in format expression
write(*,'(I+3)')
!ERROR: Unexpected '-' in format expression
write(*,'(I-3)')
!ERROR: Unexpected '-' in format expression
write(*,'(I-3, X)')
!ERROR: 'X' edit descriptor must have a positive position value
write(*,'(0X)')
!ERROR: Unexpected 'Y' in format expression
write(*,'(XY)')
!ERROR: Unexpected 'Y' in format expression
write(*,'(XYM)')
!ERROR: Unexpected 'M' in format expression
write(*,'(MXY)')
!ERROR: Expected ',' or ')' in format expression
write(*,'(XEN)')
!ERROR: Unexpected 'R' in format expression
!ERROR: Unexpected 'R' in format expression
write(*,"(RR, RV)")
!ERROR: Unexpected '-' in format expression
!ERROR: Unexpected 'Y' in format expression
write(*,'(I-3, XY)')
!ERROR: 'A' edit descriptor 'w' value must be positive
write(*,'(A0)')
!ERROR: 'L' edit descriptor 'w' value must be positive
write(*,'(L0)')
!ERROR: Expected 'G' edit descriptor '.d' value
write(*,'(G4)')
!ERROR: Unexpected 'e' in 'G0' edit descriptor
write(*,'(G0.8e)')
!ERROR: Unexpected 'e' in 'G0' edit descriptor
write(*,'(G0.8e2)')
!ERROR: Kind parameter '_' character in format expression
write(*,'(I5_4)')
!ERROR: Kind parameter '_' character in format expression
write(*,'(5_4P)')
!ERROR: 'T' edit descriptor must have a positive position value
write(*,'(T0)')
!ERROR: 'T' edit descriptor must have a positive position value
!ERROR: Unterminated format expression
write(*,'(T0')
!ERROR: 'TL' edit descriptor must have a positive position value
!ERROR: 'T' edit descriptor must have a positive position value
!ERROR: Expected 'EN' edit descriptor 'd' value after '.'
write(*,'(TL0,T0,EN12.)')
!ERROR: Expected 'EX' edit descriptor 'e' value after 'E'
write(*,'(EX12.3e2, EX12.3e)')
!ERROR: 'TL' edit descriptor must have a positive position value
!ERROR: 'T' edit descriptor must have a positive position value
!ERROR: Unterminated format expression
write(*,'(TL00,T000')
!ERROR: Unterminated format expression
write(*,'(')
!ERROR: Unterminated format expression
write(*,'(-')
!ERROR: Unterminated format expression
write(*,'(I3+')
!ERROR: Unterminated format expression
write(*,'(I3,-')
!ERROR: Unexpected integer constant
write(*,'(3)')
!ERROR: Unexpected ',' before ')' in format expression
write(*,'(3,)')
!ERROR: Unexpected ',' in format expression
write(*,'(,3)')
!ERROR: Unexpected ',' before ')' in format expression
write(*,'(,)')
!ERROR: Unterminated format expression
write(*,'(X')
!ERROR: Expected ',' or ')' in format expression
!ERROR: Unterminated format expression
write(*,'(XX')
!ERROR: Unexpected '@' in format expression
!ERROR: Unexpected '#' in format expression
!ERROR: Unexpected '&' in format expression
write(*,'(@@, # ,&&& &&, ignore error 4)')
!ERROR: Repeat specifier before 'TR' edit descriptor
write(*,'(3TR0)')
!ERROR: 'TR' edit descriptor must have a positive position value
write(*,'(TR0)')
!ERROR: Kind parameter '_' character in format expression
write(*,'(3_4X)')
!ERROR: Kind parameter '_' character in format expression
write(*,'(1_"abc")')
!ERROR: Unterminated string
!ERROR: Unterminated format expression
write(*,'("abc)')
!ERROR: Unexpected '_' in format expression
write(*,'("abc"_1)')
!ERROR: Unexpected '@' in format expression
write(*,'(3Habc, 3@, X)')
!ERROR: Unterminated format expression
write(*,'(4Habc)')
!ERROR: Unterminated 'H' edit descriptor
!ERROR: Unterminated format expression
write(*,'(5Habc)')
!ERROR: Unterminated 'H' edit descriptor
!ERROR: Unterminated format expression
write(*,'(50Habc)')
!ERROR: Integer overflow in format expression
write(*,'(9876543210987654321X)')
!ERROR: Integer overflow in format expression
write(*,'(98765432109876543210X)')
!ERROR: Integer overflow in format expression
write(*,'(I98765432109876543210)')
!ERROR: Integer overflow in format expression
write(*,'(45I20.98765432109876543210, 45I20)')
!ERROR: Integer overflow in format expression
write(*,'(45' // ' I20.9876543' // '2109876543210, 45I20)')
!ERROR: Repeat specifier before '$' edit descriptor
write(*,'(7$)')
end

View File

@ -0,0 +1,31 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!ERROR: String edit descriptor in READ format expression
read(*,'("abc")')
!ERROR: String edit descriptor in READ format expression
!ERROR: Unterminated format expression
read(*,'("abc)')
!ERROR: 'H' edit descriptor in READ format expression
read(*,'(3Habc)')
!ERROR: 'H' edit descriptor in READ format expression
!ERROR: Unterminated format expression
read(*,'(5Habc)')
!ERROR: 'I' edit descriptor 'w' value must be positive
read(*,'(I0)')
end

View File

@ -0,0 +1,38 @@
! Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
!
! Licensed under the Apache License, Version 2.0 (the "License");
! you may not use this file except in compliance with the License.
! You may obtain a copy of the License at
!
! http://www.apache.org/licenses/LICENSE-2.0
!
! Unless required by applicable law or agreed to in writing, software
! distributed under the License is distributed on an "AS IS" BASIS,
! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
! See the License for the specific language governing permissions and
! limitations under the License.
!OPTIONS: -Mstandard
write(*, '(B0)')
write(*, '(B3)')
!WARNING: Expected 'B' edit descriptor 'w' value
write(*, '(B)')
!WARNING: Expected 'EN' edit descriptor 'w' value
!WARNING: Non-standard '$' edit descriptor
write(*, '(EN,$)')
!WARNING: Expected 'G' edit descriptor 'w' value
write(*, '(3G)')
!WARNING: Non-standard '\' edit descriptor
write(*,'(A, \)') 'Hello'
!WARNING: 'X' edit descriptor must have a positive position value
write(*, '(X)')
!WARNING: Legacy 'H' edit descriptor
write(*, '(3Habc)')
end