@@ -155,7 +155,8 @@ void IoChecker::Enter(const parser::ConnectSpec::CharExpr &spec) {
155155 }
156156}
157157
158- void IoChecker::Enter (const parser::ConnectSpec::Newunit &) {
158+ void IoChecker::Enter (const parser::ConnectSpec::Newunit &var) {
159+ CheckForDefinableVariable (var, " NEWUNIT" );
159160 SetSpecifier (IoSpecKind::Newunit);
160161}
161162
@@ -266,10 +267,11 @@ void IoChecker::Enter(const parser::IdExpr &) { SetSpecifier(IoSpecKind::Id); }
266267
267268void IoChecker::Enter (const parser::IdVariable &spec) {
268269 SetSpecifier (IoSpecKind::Id);
269- auto expr{GetExpr (spec)};
270+ const auto * expr{GetExpr (spec)};
270271 if (!expr || !expr->GetType ()) {
271272 return ;
272273 }
274+ CheckForDefinableVariable (spec, " ID" );
273275 int kind{expr->GetType ()->kind ()};
274276 int defaultKind{context_.GetDefaultKind (TypeCategory::Integer)};
275277 if (kind < defaultKind) {
@@ -281,21 +283,18 @@ void IoChecker::Enter(const parser::IdVariable &spec) {
281283
282284void IoChecker::Enter (const parser::InputItem &spec) {
283285 flags_.set (Flag::DataList);
284- if (const parser::Variable * var{std::get_if<parser::Variable>(&spec.u )}) {
285- const parser::Name &name{GetLastName (*var)};
286- if (name.symbol ) {
287- if (auto *details{name.symbol ->detailsIf <ObjectEntityDetails>()}) {
288- // TODO: Determine if this check is needed at all, and if so, replace
289- // the false subcondition with a check for a whole array. Otherwise,
290- // the check incorrectly flags array element and section references.
291- if (details->IsAssumedSize () && false ) {
292- // This check may be superseded by C928 or C1002.
293- context_.Say (name.source ,
294- " '%s' must not be a whole assumed size array" _err_en_US,
295- name.source ); // C1231
296- }
297- }
298- }
286+ const parser::Variable *var{std::get_if<parser::Variable>(&spec.u )};
287+ if (!var) {
288+ return ;
289+ }
290+ CheckForDefinableVariable (*var, " Input" );
291+ const auto &name{GetLastName (*var)};
292+ const auto *expr{GetExpr (*var)};
293+ if (name.symbol && IsAssumedSizeArray (*name.symbol ) && expr &&
294+ !evaluate::IsArrayElement (*GetExpr (*var))) {
295+ context_.Say (name.source ,
296+ " Whole assumed size array '%s' may not be an input item" _err_en_US,
297+ name.source ); // C1231
299298 }
300299}
301300
@@ -386,6 +385,8 @@ void IoChecker::Enter(const parser::InquireSpec::CharVar &spec) {
386385 specKind = IoSpecKind::Dispose;
387386 break ;
388387 }
388+ CheckForDefinableVariable (std::get<parser::ScalarDefaultCharVariable>(spec.t ),
389+ parser::ToUpperCaseLetters (common::EnumToString (specKind)));
389390 SetSpecifier (specKind);
390391}
391392
@@ -412,6 +413,8 @@ void IoChecker::Enter(const parser::InquireSpec::IntVar &spec) {
412413 specKind = IoSpecKind::Size;
413414 break ;
414415 }
416+ CheckForDefinableVariable (std::get<parser::ScalarIntVariable>(spec.t ),
417+ parser::ToUpperCaseLetters (common::EnumToString (specKind)));
415418 SetSpecifier (specKind);
416419}
417420
@@ -500,17 +503,23 @@ void IoChecker::Enter(const parser::IoControlSpec::Rec &) {
500503 SetSpecifier (IoSpecKind::Rec);
501504}
502505
503- void IoChecker::Enter (const parser::IoControlSpec::Size &) {
506+ void IoChecker::Enter (const parser::IoControlSpec::Size &var) {
507+ CheckForDefinableVariable (var, " SIZE" );
504508 SetSpecifier (IoSpecKind::Size);
505509}
506510
507511void IoChecker::Enter (const parser::IoUnit &spec) {
508512 if (const parser::Variable * var{std::get_if<parser::Variable>(&spec.u )}) {
509- // TODO: C1201 - internal file variable must not be an array section ...
510- if (auto expr{GetExpr (*var)}) {
511- if (!ExprTypeKindIsDefault (*expr, context_)) {
513+ if (stmt_ == IoStmtKind::Write) {
514+ CheckForDefinableVariable (*var, " Internal file" );
515+ }
516+ if (const auto *expr{GetExpr (*var)}) {
517+ if (HasVectorSubscript (*expr)) {
518+ context_.Say (parser::FindSourceLocation (*var), // C1201
519+ " Internal file must not have a vector subscript" _err_en_US);
520+ } else if (!ExprTypeKindIsDefault (*expr, context_)) {
512521 // This may be too restrictive; other kinds may be valid.
513- context_.Say ( // C1202
522+ context_.Say (parser::FindSourceLocation (*var), // C1202
514523 " Invalid character kind for an internal file variable" _err_en_US);
515524 }
516525 }
@@ -522,13 +531,26 @@ void IoChecker::Enter(const parser::IoUnit &spec) {
522531 }
523532}
524533
525- void IoChecker::Enter (const parser::MsgVariable &) {
534+ void IoChecker::Enter (const parser::MsgVariable &var) {
535+ if (stmt_ == IoStmtKind::None) {
536+ // allocate, deallocate, image control
537+ CheckForDefinableVariable (var, " ERRMSG" );
538+ return ;
539+ }
540+ CheckForDefinableVariable (var, " IOMSG" );
526541 SetSpecifier (IoSpecKind::Iomsg);
527542}
528543
529- void IoChecker::Enter (const parser::OutputItem &) {
544+ void IoChecker::Enter (const parser::OutputItem &item ) {
530545 flags_.set (Flag::DataList);
531- // TODO: C1233 - output item must not be a procedure pointer
546+ if (const auto *x{std::get_if<parser::Expr>(&item.u )}) {
547+ if (const auto *expr{GetExpr (*x)}) {
548+ if (IsProcedurePointer (*expr)) {
549+ context_.Say (parser::FindSourceLocation (*x),
550+ " Output item must not be a procedure pointer" _err_en_US); // C1233
551+ }
552+ }
553+ }
532554}
533555
534556void IoChecker::Enter (const parser::StatusExpr &spec) {
@@ -555,12 +577,14 @@ void IoChecker::Enter(const parser::StatusExpr &spec) {
555577 }
556578}
557579
558- void IoChecker::Enter (const parser::StatVariable &) {
580+ void IoChecker::Enter (const parser::StatVariable &var ) {
559581 if (stmt_ == IoStmtKind::None) {
560- // ALLOCATE & DEALLOCATE
561- } else {
562- SetSpecifier (IoSpecKind::Iostat) ;
582+ // allocate, deallocate, image control
583+ CheckForDefinableVariable (var, " STAT " );
584+ return ;
563585 }
586+ CheckForDefinableVariable (var, " IOSTAT" );
587+ SetSpecifier (IoSpecKind::Iostat);
564588}
565589
566590void IoChecker::Leave (const parser::BackspaceStmt &) {
@@ -808,7 +832,7 @@ void IoChecker::CheckStringValue(IoSpecKind specKind, const std::string &value,
808832
809833// CheckForRequiredSpecifier and CheckForProhibitedSpecifier functions
810834// need conditions to check, and string arguments to insert into a message.
811- // A IoSpecKind provides both an absence/presence condition and a string
835+ // An IoSpecKind provides both an absence/presence condition and a string
812836// argument (its name). A (condition, string) pair provides an arbitrary
813837// condition and an arbitrary string.
814838
@@ -893,6 +917,17 @@ void IoChecker::CheckForProhibitedSpecifier(
893917 }
894918}
895919
920+ template <typename A>
921+ void IoChecker::CheckForDefinableVariable (
922+ const A &var, const std::string &s) const {
923+ const Symbol *sym{
924+ GetFirstName (*parser::Unwrap<parser::Variable>(var)).symbol };
925+ if (WhyNotModifiable (*sym, context_.FindScope (*context_.location ()))) {
926+ context_.Say (parser::FindSourceLocation (var),
927+ " %s variable '%s' must be definable" _err_en_US, s, sym->name ());
928+ }
929+ }
930+
896931void IoChecker::CheckForPureSubprogram () const { // C1597
897932 CHECK (context_.location ());
898933 if (FindPureProcedureContaining (context_.FindScope (*context_.location ()))) {
0 commit comments