Index: docs/UsersManual.rst =================================================================== --- docs/UsersManual.rst +++ docs/UsersManual.rst @@ -135,6 +135,17 @@ Error on language extensions. +.. option:: -fpermissive + + Do not error on language extensions. Diagnostics for invalid code using known + language extensions in the current language mode are downgraded to at most a + warning. It is strongly recommended that ``-Wno-foo`` or ``-Wno-error=foo`` + are used to silence or downgrade individual errors instead of using this + flag. + + Note that this does not generally enable extensions controlled by other + ``-f`` flags, such as ``-fms-extensions``. + .. option:: -Wsystem-headers Enable warnings from system headers. @@ -142,13 +153,13 @@ .. option:: -ferror-limit=123 Stop emitting diagnostics after 123 errors have been produced. The default is - 20, and the error limit can be disabled with `-ferror-limit=0`. + 20, and the error limit can be disabled with ``-ferror-limit=0``. .. option:: -ftemplate-backtrace-limit=123 Only emit up to 123 template instantiation notes within the template instantiation backtrace for a single warning or error. The default is 10, and - the limit can be disabled with `-ftemplate-backtrace-limit=0`. + the limit can be disabled with ``-ftemplate-backtrace-limit=0``. .. _cl_diag_formatting: Index: include/clang/Basic/Diagnostic.h =================================================================== --- include/clang/Basic/Diagnostic.h +++ include/clang/Basic/Diagnostic.h @@ -275,12 +275,13 @@ unsigned SuppressSystemWarnings : 1; // Map extensions to warnings or errors? - diag::Severity ExtBehavior = diag::Severity::Ignored; + ExtensionDiagMode ExtBehavior : 2; DiagState() : IgnoreAllWarnings(false), EnableAllWarnings(false), WarningsAsErrors(false), ErrorsAsFatal(false), - SuppressSystemWarnings(false) {} + SuppressSystemWarnings(false), + ExtBehavior(ExtensionDiagMode::Default) {} using iterator = llvm::DenseMap::iterator; using const_iterator = @@ -680,11 +681,12 @@ /// Controls whether otherwise-unmapped extension diagnostics are /// mapped onto ignore/warning/error. /// - /// This corresponds to the GCC -pedantic and -pedantic-errors option. - void setExtensionHandlingBehavior(diag::Severity H) { - GetCurDiagState()->ExtBehavior = H; + /// This corresponds to the GCC -fpermissive/-pedantic/-pedantic-errors + /// options. + void setExtensionHandlingBehavior(ExtensionDiagMode E) { + GetCurDiagState()->ExtBehavior = E; } - diag::Severity getExtensionHandlingBehavior() const { + ExtensionDiagMode getExtensionHandlingBehavior() const { return GetCurDiagState()->ExtBehavior; } Index: include/clang/Basic/DiagnosticOptions.h =================================================================== --- include/clang/Basic/DiagnosticOptions.h +++ include/clang/Basic/DiagnosticOptions.h @@ -59,6 +59,21 @@ raw_ostream& operator<<(raw_ostream& Out, DiagnosticLevelMask M); +/// An enumeration describing how extension diagnostics should be treated +/// by default. +enum class ExtensionDiagMode : unsigned { + /// Map all extension diagnostics to at most a warning. + Permissive, + /// Map all extension diagnostics as specified in the diagnostic. + Default, + /// Map all extension diagnostics to at least a warning. + Pedantic, + /// Map all extension diagnostics to at least an error. + PedanticErrors, +}; + +raw_ostream& operator<<(raw_ostream& Out, ExtensionDiagMode M); + /// Options for controlling the compiler diagnostics engine. class DiagnosticOptions : public RefCountedBase{ public: Index: include/clang/Basic/DiagnosticOptions.def =================================================================== --- include/clang/Basic/DiagnosticOptions.def +++ include/clang/Basic/DiagnosticOptions.def @@ -45,8 +45,8 @@ SEMANTIC_DIAGOPT(IgnoreWarnings, 1, 0) /// -w DIAGOPT(NoRewriteMacros, 1, 0) /// -Wno-rewrite-macros -DIAGOPT(Pedantic, 1, 0) /// -pedantic -DIAGOPT(PedanticErrors, 1, 0) /// -pedantic-errors +ENUM_DIAGOPT(ExtensionsMode, ExtensionDiagMode, 2, ExtensionDiagMode::Default) + /// -pedantic, -pedantic-errors, -fpermissive DIAGOPT(ShowColumn, 1, 1) /// Show column number on diagnostics. DIAGOPT(ShowLocation, 1, 1) /// Show source location information. DIAGOPT(AbsolutePath, 1, 0) /// Use absolute paths. Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -687,6 +687,9 @@ Flags<[DriverOption, CC1Option]>, HelpText<"Disable GNU style inline asm">; +def fpermissive : Flag<["-"], "fpermissive">, Group, Flags<[CC1Option]>, + HelpText<"Downgrade diagnostics for conforming extensions from errors to warnings">; +def fno_permissive : Flag<["-"], "fno-permissive">, Group; def fprofile_sample_use : Flag<["-"], "fprofile-sample-use">, Group, Flags<[CoreOption]>; def fno_profile_sample_use : Flag<["-"], "fno-profile-sample-use">, Group, @@ -3033,7 +3036,6 @@ defm ivopts : BooleanFFlag<"ivopts">, Group; defm non_call_exceptions : BooleanFFlag<"non-call-exceptions">, Group; defm peel_loops : BooleanFFlag<"peel-loops">, Group; -defm permissive : BooleanFFlag<"permissive">, Group; defm prefetch_loop_arrays : BooleanFFlag<"prefetch-loop-arrays">, Group; defm printf : BooleanFFlag<"printf">, Group; defm profile : BooleanFFlag<"profile">, Group; Index: lib/ARCMigrate/ARCMT.cpp =================================================================== --- lib/ARCMigrate/ARCMT.cpp +++ lib/ARCMigrate/ARCMT.cpp @@ -195,7 +195,9 @@ CInvok->getLangOpts()->ObjCAutoRefCount = true; CInvok->getLangOpts()->setGC(LangOptions::NonGC); CInvok->getDiagnosticOpts().ErrorLimit = 0; - CInvok->getDiagnosticOpts().PedanticErrors = 0; + if (CInvok->getDiagnosticOpts().getExtensionsMode() == + ExtensionDiagMode::PedanticErrors) + CInvok->getDiagnosticOpts().setExtensionsMode(ExtensionDiagMode::Pedantic); // Ignore -Werror flags when migrating. std::vector WarnOpts; Index: lib/Basic/DiagnosticIDs.cpp =================================================================== --- lib/Basic/DiagnosticIDs.cpp +++ lib/Basic/DiagnosticIDs.cpp @@ -448,9 +448,22 @@ return diag::Severity::Ignored; // For extension diagnostics that haven't been explicitly mapped, check if we - // should upgrade the diagnostic. - if (IsExtensionDiag && !Mapping.isUser()) - Result = std::max(Result, State->ExtBehavior); + // should upgrade or downgrade the diagnostic. + if (IsExtensionDiag && !Mapping.isUser()) { + switch (State->ExtBehavior) { + case ExtensionDiagMode::PedanticErrors: + Result = std::max(Result, diag::Severity::Error); + break; + case ExtensionDiagMode::Pedantic: + Result = std::max(Result, diag::Severity::Warning); + break; + case ExtensionDiagMode::Default: + break; + case ExtensionDiagMode::Permissive: + Result = std::min(Result, diag::Severity::Warning); + break; + } + } // At this point, ignored errors can no longer be upgraded. if (Result == diag::Severity::Ignored) @@ -460,7 +473,7 @@ // default (disregarding attempts to upgrade severity from Warning to Error), // as well as disabling all messages which are currently mapped to Warning // (whether by default or downgraded from Error via e.g. -Wno-error or #pragma - // diagnostic.) + // diagnostic or -fpermissive.) if (State->IgnoreAllWarnings) { if (Result == diag::Severity::Warning || (Result >= diag::Severity::Error && Index: lib/Basic/DiagnosticOptions.cpp =================================================================== --- lib/Basic/DiagnosticOptions.cpp +++ lib/Basic/DiagnosticOptions.cpp @@ -21,4 +21,14 @@ return Out << static_cast(M); } +raw_ostream &operator<<(raw_ostream &Out, ExtensionDiagMode E) { + switch (E) { + case ExtensionDiagMode::PedanticErrors: return Out << "pedantic-errors"; + case ExtensionDiagMode::Pedantic: return Out << "pedantic"; + case ExtensionDiagMode::Default: return Out << "default"; + case ExtensionDiagMode::Permissive: return Out << "permissive"; + } + llvm_unreachable("unknown extension diagnostic mode"); +} + } // namespace clang Index: lib/Basic/Warnings.cpp =================================================================== --- lib/Basic/Warnings.cpp +++ lib/Basic/Warnings.cpp @@ -62,12 +62,7 @@ // If -pedantic or -pedantic-errors was specified, then we want to map all // extension diagnostics onto WARNING or ERROR unless the user has futz'd // around with them explicitly. - if (Opts.PedanticErrors) - Diags.setExtensionHandlingBehavior(diag::Severity::Error); - else if (Opts.Pedantic) - Diags.setExtensionHandlingBehavior(diag::Severity::Warning); - else - Diags.setExtensionHandlingBehavior(diag::Severity::Ignored); + Diags.setExtensionHandlingBehavior(Opts.getExtensionsMode()); SmallVector _Diags; const IntrusiveRefCntPtr< DiagnosticIDs > DiagIDs = Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -4202,11 +4202,43 @@ Args.AddAllArgs(CmdArgs, options::OPT_R_Group); Args.AddAllArgs(CmdArgs, options::OPT_W_Group); - if (Args.hasFlag(options::OPT_pedantic, options::OPT_no_pedantic, false)) - CmdArgs.push_back("-pedantic"); - Args.AddLastArg(CmdArgs, options::OPT_pedantic_errors); Args.AddLastArg(CmdArgs, options::OPT_w); + // Extensions handling: last of -pedantic / -pedantic-errors / -no-pedantic + // takes precedence, except that -pedantic after -pedantic-errors has no + // effect. + const Arg *PedanticArg = nullptr; + if (const Arg *A = + Args.getLastArg(options::OPT_pedantic_errors, options::OPT_pedantic, + options::OPT_no_pedantic)) { + if (!A->getOption().matches(options::OPT_no_pedantic)) { + const Arg *PedErrors = Args.getLastArg(options::OPT_pedantic_errors, + options::OPT_no_pedantic); + if (PedErrors && + PedErrors->getOption().matches(options::OPT_pedantic_errors)) { + CmdArgs.push_back("-pedantic-errors"); + PedanticArg = PedErrors; + } else { + CmdArgs.push_back("-pedantic"); + PedanticArg = A; + } + } + } + if (Args.hasFlag(options::OPT_fpermissive, options::OPT_fno_permissive, + false)) { + // Unlike GCC, we apply -pedantic and -fpermissive to all extension + // diagnostics, and reject any combination of both flags. + // + // GCC allows (eg) -pedantic-errors -fpermissive, and responds by + // erroring on all extensions it would not error on by default, and + // only warning on all extensions it would error on by default. That + // does not seem like useful behavior, so we do not follow suit. + if (PedanticArg) + D.Diag(diag::err_drv_argument_not_allowed_with) + << PedanticArg->getAsString(Args) << "-fpermissive"; + CmdArgs.push_back("-fpermissive"); + } + // Fixed point flags if (Args.hasFlag(options::OPT_ffixed_point, options::OPT_fno_fixed_point, /*Default=*/false)) Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1429,8 +1429,14 @@ Opts.DiagnosticSerializationFile = A->getValue(); Opts.IgnoreWarnings = Args.hasArg(OPT_w); Opts.NoRewriteMacros = Args.hasArg(OPT_Wno_rewrite_macros); - Opts.Pedantic = Args.hasArg(OPT_pedantic); - Opts.PedanticErrors = Args.hasArg(OPT_pedantic_errors); + + if (Args.hasArg(OPT_pedantic_errors)) + Opts.setExtensionsMode(ExtensionDiagMode::PedanticErrors); + else if (Args.hasArg(OPT_pedantic)) + Opts.setExtensionsMode(ExtensionDiagMode::Pedantic); + else if (Args.hasArg(OPT_fpermissive)) + Opts.setExtensionsMode(ExtensionDiagMode::Permissive); + Opts.ShowCarets = !Args.hasArg(OPT_fno_caret_diagnostics); Opts.ShowColors = parseShowColorsArgs(Args, DefaultDiagColor); Opts.ShowColumn = Args.hasFlag(OPT_fshow_column, Index: lib/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1110,9 +1110,14 @@ // If the use of an extension results in an error diagnostic, extensions are // effectively unavailable, so just return false here. - if (PP.getDiagnostics().getExtensionHandlingBehavior() >= - diag::Severity::Error) - return false; + switch (PP.getDiagnostics().getExtensionHandlingBehavior()) { + case ExtensionDiagMode::PedanticErrors: + return false; + case ExtensionDiagMode::Pedantic: + case ExtensionDiagMode::Default: + case ExtensionDiagMode::Permissive: + break; + } const LangOptions &LangOpts = PP.getLangOpts(); Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -483,10 +483,16 @@ } static bool isExtHandlingFromDiagsError(DiagnosticsEngine &Diags) { - diag::Severity Ext = Diags.getExtensionHandlingBehavior(); - if (Ext == diag::Severity::Warning && Diags.getWarningsAsErrors()) + switch (Diags.getExtensionHandlingBehavior()) { + case ExtensionDiagMode::PedanticErrors: return true; - return Ext >= diag::Severity::Error; + case ExtensionDiagMode::Pedantic: + return Diags.getWarningsAsErrors(); + case ExtensionDiagMode::Default: + case ExtensionDiagMode::Permissive: + return false; + } + llvm_unreachable("unknown extension diagnostic mode"); } static bool checkDiagnosticMappings(DiagnosticsEngine &StoredDiags, @@ -5776,7 +5782,7 @@ Initial.WarningsAsErrors = Flags & 1; Flags >>= 1; Initial.EnableAllWarnings = Flags & 1; Flags >>= 1; Initial.IgnoreAllWarnings = Flags & 1; Flags >>= 1; - Initial.ExtBehavior = (diag::Severity)Flags; + Initial.ExtBehavior = (ExtensionDiagMode)Flags; FirstState = ReadDiagState(Initial, SourceLocation(), true); assert(F.OriginalSourceFileID.isValid()); Index: test/Driver/warning-options_pedantic.cpp =================================================================== --- test/Driver/warning-options_pedantic.cpp +++ test/Driver/warning-options_pedantic.cpp @@ -2,12 +2,37 @@ // Delimiters match the start of the cc1 and the start of the linker lines // DELIMITERS: {{^ *"}} +// RUN: %clang -### -pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic-errors %s 2>&1 | FileCheck -check-prefix=PEDANTIC_ERRORS -check-prefix=DELIMITERS %s +// RUN: %clang -### -Wpedantic %s 2>&1 | FileCheck -check-prefix=NO_PEDANTIC -check-prefix=DELIMITERS %s // RUN: %clang -### -pedantic -no-pedantic %s 2>&1 | FileCheck -check-prefix=NO_PEDANTIC -check-prefix=DELIMITERS %s // RUN: %clang -### -pedantic -Wno-pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic -no-pedantic -pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic -no-pedantic -pedantic-errors %s 2>&1 | FileCheck -check-prefix=PEDANTIC_ERRORS -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic-errors -no-pedantic -pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic -no-pedantic -pedantic-errors -pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC_ERRORS -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic -no-pedantic -Wpedantic %s 2>&1 | FileCheck -check-prefix=NO_PEDANTIC -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic-errors -no-pedantic -Wpedantic %s 2>&1 | FileCheck -check-prefix=NO_PEDANTIC -check-prefix=DELIMITERS %s + +// RUN: %clang -### -fpermissive %s 2>&1 | FileCheck -check-prefix=PERMISSIVE -check-prefix=DELIMITERS %s +// RUN: %clang -### -fpermissive -fno-permissive -pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC -check-prefix=DELIMITERS %s +// RUN: %clang -### -fpermissive -fno-permissive -pedantic-errors %s 2>&1 | FileCheck -check-prefix=PEDANTIC_ERRORS -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic -fpermissive -fno-permissive %s 2>&1 | FileCheck -check-prefix=PEDANTIC -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic-errors -fpermissive -fno-permissive %s 2>&1 | FileCheck -check-prefix=PEDANTIC_ERRORS -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic -no-pedantic -fpermissive %s 2>&1 | FileCheck -check-prefix=PERMISSIVE -check-prefix=DELIMITERS %s +// RUN: %clang -### -pedantic-errors -no-pedantic -fpermissive %s 2>&1 | FileCheck -check-prefix=PERMISSIVE -check-prefix=DELIMITERS %s + +// RUN: %clang -### -pedantic -fpermissive %s 2>&1 | FileCheck -check-prefix=PEDANTIC_PERMISSIVE %s +// RUN: %clang -### -pedantic-errors -fpermissive %s 2>&1 | FileCheck -check-prefix=PEDANTIC_ERRORS_PERMISSIVE %s +// RUN: %clang -### -pedantic-errors -pedantic -fpermissive %s 2>&1 | FileCheck -check-prefix=PEDANTIC_ERRORS_PERMISSIVE %s + // NO_PEDANTIC-NOT: -pedantic -// RUN: %clang -### -pedantic -pedantic -no-pedantic -pedantic %s 2>&1 | FileCheck -check-prefix=PEDANTIC -check-prefix=DELIMITERS %s -// RUN: %clang -### -pedantic -pedantic -no-pedantic -Wpedantic %s 2>&1 | FileCheck -check-prefix=NO_PEDANTIC -check-prefix=DELIMITERS %s -// PEDANTIC: -pedantic +// PEDANTIC: "-pedantic" +// PEDANTIC_ERRORS: "-pedantic-errors" +// PERMISSIVE: "-fpermissive" // REQUIRES: clang-driver // DELIMITERS: {{^ *"}} + +// PEDANTIC_PERMISSIVE: error: invalid argument '-pedantic' not allowed with '-fpermissive' +// PEDANTIC_ERRORS_PERMISSIVE: error: invalid argument '-pedantic-errors' not allowed with '-fpermissive' Index: test/Frontend/warning-mapping-2.c =================================================================== --- test/Frontend/warning-mapping-2.c +++ test/Frontend/warning-mapping-2.c @@ -1,7 +1,31 @@ +// Check that -pedantic-errors promotes extensions to errors +// RUN: %clang_cc1 -verify -pedantic-errors %s -DMODE=0 + // Check that -w takes precedence over -pedantic-errors. -// RUN: %clang_cc1 -verify -pedantic-errors -w %s +// RUN: %clang_cc1 -verify -pedantic-errors -w %s -DMODE=1 -// Expect *not* to see a diagnostic for "implicit declaration of function" -// expected-no-diagnostics +// Check that -fpermissive demotes errors to warnings. +// RUN: %clang_cc1 -verify -fpermissive %s -DMODE=2 + +// Check that -w suppresses diagnostics demoted by -fpermissive. +// RUN: %clang_cc1 -verify -fpermissive -w %s -DMODE=3 void f0() { f1(); } +#if MODE == 0 + // expected-error@-2 {{implicit declaration of function}} +#elif MODE == 1 + // no diagnostic +#elif MODE == 2 + // expected-warning@-6 {{implicit declaration of function}} +#else + // expected-no-diagnostics +#endif + +void f2() { + return 123; +#if MODE <= 1 + // expected-error@-2 {{should not return a value}} +#elif MODE == 2 + // expected-warning@-4 {{should not return a value}} +#endif +}