mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
Introduce 45 warning options, integrated with dialects. Update documentation with warning options and syntax supported by each dialect. gcc/cobol/ChangeLog: PR cobol/119329 PR cobol/119331 PR cobol/120786 * Make-lang.in: Add cobol/messages.o to cobol1 sources. * cbldiag.h (yywarn): Remove function. (struct cbl_loc_t): Introduce new location type independent of Bison. (enum cbl_diag_id_t): Enumerate diagnostic messages. (cbl_message): New function. (dialect_ok): Test for dialect, emit standard message. (dialect_not_ok): Emit standard message if syntax excluded by dialect. (dialect_error): Remove function. (cbl_unimplementedw): Use cbl_diag_id_t. (cbl_unimplemented): Whitespace. * cdf.y: Update token values. * cobol1.cc (enable_exceptions): Use cbl_message. (cobol_warning): Declare function. (cobol_langhook_handle_option): Add 44 new warning options. (cobol_langhook_type_for_mode): Remove function. * except.cc (cbl_enabled_exception_t::dump): Remove function. * exceptg.h (class exception_turn_t): Use cbl_diag_id_t. * gcobol.1: Document dialect syntax and new warning options. * genapi.cc (parser_label_label): Remove unused warning. * gengen.cc (gg_find_field_in_struct): Use cbl_internal_error. (gg_printf): Same. (gg_fprintf): Same. (gg_define_function): Same. (gg_get_function_decl): Same. (gg_call_expr): Same. (gg_call): Same. * lang-specs.h: Add warning options. * lang.opt: Add ISO dialect and options. * lexio.cc (parse_replacing_term): Use cbl_message. (parse_replacing_pair): Same. (preprocess_filter_add): Same. (cdftext::echo_input): Same. (cdftext::lex_open): Same. (cdftext::open_input): Same. * messages.cc: New file implements cbl_message. * parse.y: Use cbl_message. * parse_ante.h (dialect_proscribed): Remove function. (parser_move_carefully): Use dialect_ok. (goodnight_gracie): Convert warning to debug message. * scan.l: Use dialect_ok. * scan_ante.h (scanner_parsing): Use cbl_diag_id_t. (scanner_parsing_toggle): Same. (scanner_parsing_pop): Same. (verify_ws): Same. (level_of): Same. (typed_name): Same. (integer_of): Same. * scan_post.h (datetime_format_of): Use cbl_internal_error. (prelex): Emit only debug messages. * show_parse.h: Use cbl_internal_error. * symbols.cc (symbols_update): Remove dialect test because parser's problem. (cbl_field_t::internalize): Use cbl_message. * symbols.h (enum cbl_dialect_t): Add ISO to cbl_dialect_t. (cbl_dialect_str): Recognize ISO dialect. (dialect_has): New function. (cbl_diagnostic_kind): New function. (cbl_dialect_kind): New function. (struct cbl_alphabet_t): Emit only debug message. * token_names.h: Regenerate. * util.cc (gb4): Emit only debug message. (current_token_location): Add overload to set token_location. (yywarn): Remove function. (cobol_fileline_set): Use cbl_message. (cobol_parse_files): Same. (cbl_message): New diagnostic message function uses cbl_diag_id_t. (cbl_diagnostic_kind): New function. (cbl_diagnostic_option): New function. (cbl_unimplementedw): Use cbl_diag_id_t. (dialect_error): Remove function. * util.h (cbl_message): Remove obsolete prototype for cbl_message.
389 lines
13 KiB
C++
389 lines
13 KiB
C++
/*
|
|
* Copyright (c) 2021-2025 Symas Corporation
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of the Symas Corporation nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Define a table of diagnositic messages, each uniquely identified and
|
|
* grouped into dialects. The user can select on the command line which
|
|
* ones are in effect.
|
|
*/
|
|
|
|
#include <cobol-system.h>
|
|
#include <coretypes.h>
|
|
#include <tree.h>
|
|
#undef yy_flex_debug
|
|
|
|
#include <langinfo.h>
|
|
|
|
#include <coretypes.h>
|
|
#include <version.h>
|
|
#include <demangle.h>
|
|
#include <intl.h>
|
|
#include <backtrace.h>
|
|
#include <diagnostic.h>
|
|
#include <opts.h>
|
|
#include "util.h"
|
|
|
|
#include "cbldiag.h"
|
|
#include "cdfval.h"
|
|
#include "lexio.h"
|
|
|
|
#include "../../libgcobol/ec.h"
|
|
#include "../../libgcobol/common-defs.h"
|
|
#include "symbols.h"
|
|
#include "inspect.h"
|
|
#include "../../libgcobol/io.h"
|
|
#include "genapi.h"
|
|
#include "genutil.h"
|
|
#include "../../libgcobol/charmaps.h"
|
|
|
|
|
|
|
|
/*
|
|
* As of now, every diagnositc has one id, one message, one kind, and is
|
|
* associated with "one" dialect. The dialect could be ORed. If it is, that
|
|
* means among the dialects it belongs to, it is always of the same kind.
|
|
*
|
|
* The diagnositic mask in force during compilation may include/exclude
|
|
* features based on their associated dialect and/or by id. It may stipulate
|
|
* that a warning is treated as an error, too, but that's up the diagnostic
|
|
* framework. If a feature requires a dialect and is not specifically enabled,
|
|
* gcobol emits of message of the associated kind, and names the dialect
|
|
* required.
|
|
*/
|
|
struct cbl_diag_t {
|
|
cbl_diag_id_t id;
|
|
cbl_name_t option;
|
|
diagnostics::kind kind;
|
|
cbl_dialect_t dialect;
|
|
|
|
explicit cbl_diag_t( cbl_diag_id_t id )
|
|
: id(id), option(""), kind(diagnostics::kind::ignored), dialect(dialect_gcc_e)
|
|
{}
|
|
|
|
cbl_diag_t( cbl_diag_id_t id,
|
|
const char option[],
|
|
diagnostics::kind kind,
|
|
cbl_dialect_t dialect = dialect_iso_e )
|
|
: id(id), option(""), kind(kind), dialect(dialect)
|
|
{
|
|
gcc_assert(strlen(option) < sizeof(this->option));
|
|
strcpy(this->option, option);
|
|
}
|
|
|
|
bool operator<( const cbl_diag_t& that ) const {
|
|
return id < that.id;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* Initially, errors and warnings are set per the default, dialect gcc. If the
|
|
* user chooses dialect iso, all dialect-enabled features are turned into
|
|
* errors. If the user selects a more generous dialect, features associated
|
|
* with it are set to be ignored.
|
|
*
|
|
* Individual features may also be suppressed, and all warnings may be elevated
|
|
* to errors.
|
|
*/
|
|
const static auto dialect_mf_gnu = cbl_dialect_t(dialect_mf_e | dialect_gnu_e);
|
|
const static auto dialect_ibm_mf_gnu = cbl_dialect_t(dialect_ibm_e |
|
|
dialect_mf_e |
|
|
dialect_gnu_e);
|
|
|
|
std::set<cbl_diag_t> cbl_diagnostics {
|
|
{ CdfNotFoundW, "-Wcdf-name-not-found", diagnostics::kind::warning },
|
|
{ CdfParameterW, "-Wcdf-invalid-parameter", diagnostics::kind::warning },
|
|
|
|
{ EcUnknownW, "-Wec-unknown", diagnostics::kind::warning },
|
|
|
|
{ IbmEjectE, "-Wcobol-eject", diagnostics::kind::error, dialect_ibm_e },
|
|
{ IbmLengthOf, "-Wlength-of", diagnostics::kind::error, dialect_ibm_mf_gnu },
|
|
{ IbmEqualAssignE, "-Wequal-assign", diagnostics::kind::error, dialect_ibm_e },
|
|
{ IbmProcedurePointer, "-Wprocedure-pointer", diagnostics::kind::error, dialect_ibm_mf_gnu },
|
|
{ IbmSectionNegE, "-Wsegment-negative", diagnostics::kind::error, dialect_ibm_e },
|
|
{ IbmSectionRangeE, "-Wsegment-error", diagnostics::kind::error, dialect_ibm_e },
|
|
{ IbmSectionSegmentW, "-Wsegment", diagnostics::kind::warning, dialect_ibm_e },
|
|
{ IbmStopNumber, "-Wstop-number", diagnostics::kind::error, dialect_ibm_e },
|
|
{ IbmVolatileE, "-Wcobol-volatile", diagnostics::kind::error, dialect_ibm_e },
|
|
{ IbmVolatileW, "-Wcobol-volatile", diagnostics::kind::warning, dialect_ibm_e },
|
|
|
|
// RESUME not supported by IBM
|
|
{ IsoResume, "-Wcobol-resume", diagnostics::kind::error, dialect_ibm_e },
|
|
|
|
{ MfBinaryLongLong, "-Wbinary-long-long", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfCallGiving, "-Wcall-giving", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfCdfDollar, "-Wcdf-dollar", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfComp6, "-Wcomp-6", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfCompX, "-Wcomp-x", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfLevel_1_Occurs, "Wlevel-1-occurs", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfLevel78, "-Wlevel-78", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfMovePointer, "-Wmove-pointer", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfReturningNum, "-Wreturning-number", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfUsageTypename, "-Wusage-typename", diagnostics::kind::error, dialect_mf_gnu },
|
|
{ MfTrailing, "-Winspect-trailing", diagnostics::kind::error, dialect_mf_gnu },
|
|
|
|
{ LexIncludeE, "-Winclude-file-not-found", diagnostics::kind::error },
|
|
{ LexIncludeOkN, "-Winclude-file-found", diagnostics::kind::note },
|
|
{ LexIndicatorE, "-Wstray-indicator", diagnostics::kind::error },
|
|
{ LexInputN, "-Wcopybook-found", diagnostics::kind::note },
|
|
{ LexLineE, "-Wbad-line-directive", diagnostics::kind::error },
|
|
{ LexPreprocessE, "-Wpreprocessor-error", diagnostics::kind::error },
|
|
{ LexReplaceE, "-Wreplace-error", diagnostics::kind::error },
|
|
// mf and gnu do not require whitespace after relational operators
|
|
{ LexSeparatorE, "-Woperator-space", diagnostics::kind::error, dialect_mf_gnu },
|
|
|
|
{ Par78CdfDefinedW, "-Wlevel-78-defined", diagnostics::kind::warning },
|
|
{ ParIconvE, "-Wiconv-error", diagnostics::kind::note },
|
|
{ ParInfoI, "-Wentry-convention", diagnostics::kind::note },
|
|
{ ParLangInfoW, "-Wnllanginfo-error", diagnostics::kind::warning },
|
|
{ ParLiteral2W, "-Wliteral-concat", diagnostics::kind::warning },
|
|
{ ParLocaleW, "-Wlocale-error", diagnostics::kind::warning },
|
|
{ ParNoCorrespondingW, "-Wmove-corresponding", diagnostics::kind::warning },
|
|
{ ParNumstrW, "-Wbad-numeric", diagnostics::kind::warning },
|
|
{ ParUnresolvedProcE, "-Wprocedure-not-found", diagnostics::kind::error },
|
|
|
|
// unimplmeneted syntax warnings
|
|
{ SynApplyCommit, "-Wapply-commit", diagnostics::kind::warning },
|
|
{ SynFileCodeSet, "-Wfile-code-set", diagnostics::kind::warning },
|
|
{ SynHighOrderBit, "-Whigh-order-bit", diagnostics::kind::warning },
|
|
{ SynRecordingMode, "-Wrecording-mode", diagnostics::kind::warning },
|
|
{ SynSetLocaleTo, "-Wset-locale-to", diagnostics::kind::warning },
|
|
{ SynSetToLocale, "-Wset-to-locale", diagnostics::kind::warning },
|
|
|
|
};
|
|
|
|
static struct set_verify {
|
|
set_verify() {
|
|
gcc_assert(cbl_diagnostics.size() == DiagDiagDiag);
|
|
auto p = std::find_if(cbl_diagnostics.begin(), cbl_diagnostics.end(),
|
|
[]( const auto& diag ) {
|
|
return '?' == cbl_dialect_str(diag.dialect)[0];
|
|
} );
|
|
if( p != cbl_diagnostics.end() ) {
|
|
fprintf(stderr, "unregconized dialect '%04x (~%04x)'", p->dialect, ~p->dialect);
|
|
}
|
|
gcc_assert( std::none_of(cbl_diagnostics.begin(), cbl_diagnostics.end(),
|
|
[]( const auto& diag ) {
|
|
return '?' == cbl_dialect_str(diag.dialect)[0];
|
|
} ) );
|
|
}
|
|
} verify_consistent_message_count;
|
|
|
|
static inline diagnostics::kind
|
|
kind_of( cbl_diag_id_t id ) {
|
|
auto diag = cbl_diagnostics.find(cbl_diag_t(id));
|
|
if( diag != cbl_diagnostics.end() ) {
|
|
return diag->kind;
|
|
}
|
|
return diagnostics::kind::ice;
|
|
}
|
|
|
|
diagnostics::kind
|
|
cbl_diagnostic_kind( cbl_diag_id_t id ) {
|
|
return kind_of(id);
|
|
}
|
|
|
|
bool
|
|
cbl_diagnostic_kind( cbl_diag_id_t id, diagnostics::kind kind ) {
|
|
auto p = cbl_diagnostics.find( cbl_diag_t{id} );
|
|
if( p != cbl_diagnostics.end() ) {
|
|
auto diag(*p);
|
|
diag.kind = kind;
|
|
cbl_diagnostics.erase(p);
|
|
return cbl_diagnostics.insert(diag).second;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
cbl_diagnostic_kind( cbl_dialect_t dialect, diagnostics::kind kind ) {
|
|
bool ok = true;
|
|
for( auto diag : cbl_diagnostics ) {
|
|
if( diag.dialect == dialect ) {
|
|
if( ! cbl_diagnostic_kind(diag.id, kind) ) ok = false;
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
void
|
|
cobol_warning( cbl_diag_id_t id, int yn, bool warning_as_error ) {
|
|
gcc_assert( 0 <= yn && yn <= 1 );
|
|
|
|
diagnostics::kind kind = yn?
|
|
diagnostics::kind::warning : diagnostics::kind::ignored;
|
|
|
|
if( warning_as_error ) {
|
|
kind = diagnostics::kind::error;
|
|
}
|
|
|
|
cbl_diagnostic_kind(id, kind);
|
|
}
|
|
|
|
static inline const char *
|
|
option_of( cbl_diag_id_t id ) {
|
|
auto diag = cbl_diagnostics.find(cbl_diag_t(id));
|
|
if( diag != cbl_diagnostics.end() && diag->option[0] ) {
|
|
return diag->option;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const char *
|
|
cbl_diagnostic_option( cbl_diag_id_t id ) {
|
|
return option_of(id);
|
|
}
|
|
|
|
/*
|
|
* This is the general message looker-upper. It determines whether the
|
|
* diagnositic is in force, at what level, and the message text, and invokes
|
|
* the framework.
|
|
*/
|
|
extern int yychar;
|
|
extern YYLTYPE yylloc;
|
|
|
|
static const diagnostics::option_id option_zero;
|
|
|
|
location_t current_token_location();
|
|
location_t current_token_location(const location_t& loc);
|
|
|
|
bool
|
|
cbl_message( cbl_diag_id_t id, const char gmsgid[], ... ) {
|
|
auto_diagnostic_group d;
|
|
const char *option;
|
|
char *msg = nullptr;
|
|
|
|
diagnostics::kind kind = kind_of(id);
|
|
if( kind == diagnostics::kind::ignored ) return false;
|
|
|
|
if( (option = option_of(id)) != nullptr ) {
|
|
msg = xasprintf("%s [%s]", gmsgid, option);
|
|
gmsgid = msg;
|
|
}
|
|
|
|
va_list ap;
|
|
|
|
va_start (ap, gmsgid);
|
|
auto ret = emit_diagnostic_valist( kind, current_token_location(),
|
|
option_zero, gmsgid, &ap );
|
|
va_end (ap);
|
|
free(msg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool cbl_message( cbl_loc_t loc, cbl_diag_id_t id, const char gmsgid[], ... ) {
|
|
class temp_loc_t { // copied from util.cc
|
|
location_t orig;
|
|
public:
|
|
temp_loc_t() : orig(current_token_location()) {
|
|
if( yychar < 3 ) return;
|
|
|
|
gcc_location_set(yylloc); // use lookahead location
|
|
}
|
|
explicit temp_loc_t( const YYLTYPE& loc) : orig(current_token_location()) {
|
|
gcc_location_set(loc);
|
|
}
|
|
explicit temp_loc_t( const YDFLTYPE& loc) : orig(current_token_location()) {
|
|
gcc_location_set(loc);
|
|
}
|
|
~temp_loc_t() {
|
|
if( orig != current_token_location() ) {
|
|
current_token_location(orig);
|
|
}
|
|
}
|
|
};
|
|
|
|
auto_diagnostic_group d;
|
|
const char *option;
|
|
char *msg = nullptr;
|
|
|
|
diagnostics::kind kind = kind_of(id);
|
|
if( kind == diagnostics::kind::ignored ) return false;
|
|
|
|
if( (option = option_of(id)) != nullptr ) {
|
|
msg = xasprintf("%s [%s]", gmsgid, option);
|
|
gmsgid = msg;
|
|
}
|
|
|
|
temp_loc_t looker(loc);
|
|
va_list ap;
|
|
|
|
va_start (ap, gmsgid);
|
|
rich_location richloc (line_table, current_token_location());
|
|
auto ret = emit_diagnostic_valist( kind,
|
|
current_token_location(),
|
|
option_zero, gmsgid, &ap );
|
|
va_end (ap);
|
|
free(msg);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Verify the dialect associated with the id (and thus term) is covered by the
|
|
* dialects currently in effect. If not, issue a standard message of the kind
|
|
* defined by the id. Possible combinations:
|
|
* dialect required: ok, dialect matches feature dialect
|
|
* dialect prohibits not_ok, dialect matches feature ~dialect
|
|
*
|
|
* If ok is false, then a match means the dialect prohibits the feature.
|
|
*/
|
|
bool
|
|
dialect_ok( const cbl_loc_t& loc, cbl_diag_id_t id, const char term[], bool ok ) {
|
|
auto diag = cbl_diagnostics.find(cbl_diag_t(id));
|
|
|
|
const char *verb = "requires";
|
|
|
|
if( diag == cbl_diagnostics.end() ) {
|
|
gcc_unreachable();
|
|
}
|
|
|
|
if( diag->kind == diagnostics::kind::ignored ) return true;
|
|
|
|
if( dialect_has(diag->dialect) ) {
|
|
if( ok ) {
|
|
return true;
|
|
} else {
|
|
verb = "prohibits";
|
|
}
|
|
} else {
|
|
if( !ok ) return true; // current dialect correctly does not match the feature
|
|
}
|
|
|
|
cbl_message(loc, id, "%qs %s %<-dialect %s%>",
|
|
term, verb, cbl_dialect_str(diag->dialect));
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
|