Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -230,6 +230,7 @@ "The last /TC or /TP option takes precedence over earlier instances">; def note_drv_address_sanitizer_debug_runtime : Note< "AddressSanitizer doesn't support linking with debug runtime libraries yet">; +def note_drv_use_standard : Note<"use '%0' for '%1' standard">; def err_analyzer_config_no_value : Error< "analyzer-config option '%0' has a key but no value">; Index: include/clang/Frontend/LangStandard.h =================================================================== --- include/clang/Frontend/LangStandard.h +++ include/clang/Frontend/LangStandard.h @@ -29,7 +29,8 @@ Digraphs = (1 << 8), GNUMode = (1 << 9), HexFloat = (1 << 10), - ImplicitInt = (1 << 11) + ImplicitInt = (1 << 11), + OpenCL = (1 << 12) }; } @@ -91,6 +92,9 @@ /// hasImplicitInt - Language allows variables to be typed as int implicitly. bool hasImplicitInt() const { return Flags & frontend::ImplicitInt; } + /// isOpenCL - Language is a OpenCL variant. + bool isOpenCL() const { return Flags & frontend::OpenCL; } + static const LangStandard &getLangStandardForKind(Kind K); static const LangStandard *getLangStandardForName(StringRef Name); }; Index: include/clang/Frontend/LangStandards.def =================================================================== --- include/clang/Frontend/LangStandards.def +++ include/clang/Frontend/LangStandards.def @@ -142,16 +142,16 @@ // OpenCL LANGSTANDARD(opencl, "cl", "OpenCL 1.0", - LineComment | C99 | Digraphs | HexFloat) + LineComment | C99 | Digraphs | HexFloat | OpenCL) LANGSTANDARD(opencl11, "cl1.1", "OpenCL 1.1", - LineComment | C99 | Digraphs | HexFloat) + LineComment | C99 | Digraphs | HexFloat | OpenCL) LANGSTANDARD(opencl12, "cl1.2", "OpenCL 1.2", - LineComment | C99 | Digraphs | HexFloat) + LineComment | C99 | Digraphs | HexFloat | OpenCL) LANGSTANDARD(opencl20, "cl2.0", "OpenCL 2.0", - LineComment | C99 | Digraphs | HexFloat) + LineComment | C99 | Digraphs | HexFloat | OpenCL) LANGSTANDARD_ALIAS(opencl, "CL") LANGSTANDARD_ALIAS(opencl11, "CL1.1") Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1534,13 +1534,6 @@ Opts.AddVFSOverlayFile(A->getValue()); } -static bool isOpenCL(LangStandard::Kind LangStd) { - return LangStd == LangStandard::lang_opencl || - LangStd == LangStandard::lang_opencl11 || - LangStd == LangStandard::lang_opencl12 || - LangStd == LangStandard::lang_opencl20; -} - void CompilerInvocation::setLangDefaults(LangOptions &Opts, InputKind IK, const llvm::Triple &T, PreprocessorOptions &PPOpts, @@ -1611,7 +1604,7 @@ Opts.ImplicitInt = Std.hasImplicitInt(); // Set OpenCL Version. - Opts.OpenCL = isOpenCL(LangStd) || IK == IK_OpenCL; + Opts.OpenCL = Std.isOpenCL() || IK == IK_OpenCL; if (LangStd == LangStandard::lang_opencl) Opts.OpenCLVersion = 100; else if (LangStd == LangStandard::lang_opencl11) @@ -1680,6 +1673,63 @@ return DefaultVisibility; } +/// Check if input file kind and language standard are compatible. +static bool IsInputCompatibleWithStandard(InputKind IK, + const LangStandard &S) { + switch (IK) { + case IK_C: + case IK_ObjC: + case IK_PreprocessedC: + case IK_PreprocessedObjC: + if (S.isC89() || S.isC99()) + return true; + break; + case IK_CXX: + case IK_ObjCXX: + case IK_PreprocessedCXX: + case IK_PreprocessedObjCXX: + if (S.isCPlusPlus()) + return true; + break; + case IK_OpenCL: + if (S.isOpenCL()) + return true; + break; + case IK_CUDA: + case IK_PreprocessedCuda: + if (S.isCPlusPlus()) + return true; + break; + default: + llvm_unreachable("Cannot decide whether language standard and " + "input file kind are compatible!"); + } + return false; +} + +/// Get language name for given input kind. +static const StringRef GetInputKindName(InputKind IK) { + switch (IK) { + case IK_C: + case IK_ObjC: + case IK_PreprocessedC: + case IK_PreprocessedObjC: + return "C/ObjC"; + case IK_CXX: + case IK_ObjCXX: + case IK_PreprocessedCXX: + case IK_PreprocessedObjCXX: + return "C++/ObjC++"; + case IK_OpenCL: + return "OpenCL"; + case IK_CUDA: + case IK_PreprocessedCuda: + return "CUDA"; + default: + llvm_unreachable("Cannot decide on name for InputKind!"); + } +} + static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, const TargetOptions &TargetOpts, PreprocessorOptions &PPOpts, @@ -1694,43 +1744,27 @@ .Case(alias, LangStandard::lang_##id) #include "clang/Frontend/LangStandards.def" .Default(LangStandard::lang_unspecified); - if (LangStd == LangStandard::lang_unspecified) + if (LangStd == LangStandard::lang_unspecified) { Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << A->getValue(); - else { + // Report supported standards with short description. + for (unsigned KindValue = 0; + KindValue != LangStandard::lang_unspecified; + ++KindValue) { + const LangStandard &Std = LangStandard::getLangStandardForKind( + static_cast(KindValue)); + if (IsInputCompatibleWithStandard(IK, Std)) { + Diags.Report(diag::note_drv_use_standard) + << Std.getName() << Std.getDescription(); + } + } + } else { // Valid standard, check to make sure language and standard are // compatible. const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); - switch (IK) { - case IK_C: - case IK_ObjC: - case IK_PreprocessedC: - case IK_PreprocessedObjC: - if (!(Std.isC89() || Std.isC99())) - Diags.Report(diag::err_drv_argument_not_allowed_with) - << A->getAsString(Args) << "C/ObjC"; - break; - case IK_CXX: - case IK_ObjCXX: - case IK_PreprocessedCXX: - case IK_PreprocessedObjCXX: - if (!Std.isCPlusPlus()) - Diags.Report(diag::err_drv_argument_not_allowed_with) - << A->getAsString(Args) << "C++/ObjC++"; - break; - case IK_OpenCL: - if (!isOpenCL(LangStd)) - Diags.Report(diag::err_drv_argument_not_allowed_with) - << A->getAsString(Args) << "OpenCL"; - break; - case IK_CUDA: - case IK_PreprocessedCuda: - if (!Std.isCPlusPlus()) - Diags.Report(diag::err_drv_argument_not_allowed_with) - << A->getAsString(Args) << "CUDA"; - break; - default: - break; + if (!IsInputCompatibleWithStandard(IK, Std)) { + Diags.Report(diag::err_drv_argument_not_allowed_with) + << A->getAsString(Args) << GetInputKindName(IK); } } } @@ -1739,16 +1773,16 @@ // Override the -std option in this case. if (const Arg *A = Args.getLastArg(OPT_cl_std_EQ)) { LangStandard::Kind OpenCLLangStd - = llvm::StringSwitch(A->getValue()) - .Cases("cl", "CL", LangStandard::lang_opencl) - .Cases("cl1.1", "CL1.1", LangStandard::lang_opencl11) - .Cases("cl1.2", "CL1.2", LangStandard::lang_opencl12) - .Cases("cl2.0", "CL2.0", LangStandard::lang_opencl20) - .Default(LangStandard::lang_unspecified); + = llvm::StringSwitch(A->getValue()) + .Cases("cl", "CL", LangStandard::lang_opencl) + .Cases("cl1.1", "CL1.1", LangStandard::lang_opencl11) + .Cases("cl1.2", "CL1.2", LangStandard::lang_opencl12) + .Cases("cl2.0", "CL2.0", LangStandard::lang_opencl20) + .Default(LangStandard::lang_unspecified); if (OpenCLLangStd == LangStandard::lang_unspecified) { Diags.Report(diag::err_drv_invalid_value) - << A->getAsString(Args) << A->getValue(); + << A->getAsString(Args) << A->getValue(); } else LangStd = OpenCLLangStd; @@ -1850,7 +1884,7 @@ if (Args.hasArg(OPT_fgnu89_inline)) { if (Opts.CPlusPlus) Diags.Report(diag::err_drv_argument_not_allowed_with) << "-fgnu89-inline" - << "C++/ObjC++"; + << GetInputKindName(IK); else Opts.GNUInline = 1; } Index: test/Driver/unknown-std.c =================================================================== --- /dev/null +++ test/Driver/unknown-std.c @@ -0,0 +1,34 @@ +// This file checks output given when processing C/ObjC files. +// When user selects invalid language standard +// print out supported values with short description. + +// RUN: not %clang %s -std=foobar -c 2>&1 | \ +// RUN: FileCheck --match-full-lines %s + +// CHECK: error: invalid value 'foobar' in '-std=foobar' +// CHECK-NEXT: note: use 'c89' for 'ISO C 1990' standard +// CHECK-NEXT: note: use 'c90' for 'ISO C 1990' standard +// CHECK-NEXT: note: use 'iso9899:1990' for 'ISO C 1990' standard +// CHECK-NEXT: note: use 'iso9899:199409' for 'ISO C 1990 with amendment 1' standard +// CHECK-NEXT: note: use 'gnu89' for 'ISO C 1990 with GNU extensions' standard +// CHECK-NEXT: note: use 'gnu90' for 'ISO C 1990 with GNU extensions' standard +// CHECK-NEXT: note: use 'c99' for 'ISO C 1999' standard +// CHECK-NEXT: note: use 'c9x' for 'ISO C 1999' standard +// CHECK-NEXT: note: use 'iso9899:1999' for 'ISO C 1999' standard +// CHECK-NEXT: note: use 'iso9899:199x' for 'ISO C 1999' standard +// CHECK-NEXT: note: use 'gnu99' for 'ISO C 1999 with GNU extensions' standard +// CHECK-NEXT: note: use 'gnu9x' for 'ISO C 1999 with GNU extensions' standard +// CHECK-NEXT: note: use 'c11' for 'ISO C 2011' standard +// CHECK-NEXT: note: use 'c1x' for 'ISO C 2011' standard +// CHECK-NEXT: note: use 'iso9899:2011' for 'ISO C 2011' standard +// CHECK-NEXT: note: use 'iso9899:201x' for 'ISO C 2011' standard +// CHECK-NEXT: note: use 'gnu11' for 'ISO C 2011 with GNU extensions' standard +// CHECK-NEXT: note: use 'gnu1x' for 'ISO C 2011 with GNU extensions' standard +// CHECK-NEXT: note: use 'cl' for 'OpenCL 1.0' standard +// CHECK-NEXT: note: use 'cl1.1' for 'OpenCL 1.1' standard +// CHECK-NEXT: note: use 'cl1.2' for 'OpenCL 1.2' standard +// CHECK-NEXT: note: use 'cl2.0' for 'OpenCL 2.0' standard + +// Make sure that no other output is present. +// CHECK-NOT: {{^.+$}} + Index: test/Driver/unknown-std.cl =================================================================== --- /dev/null +++ test/Driver/unknown-std.cl @@ -0,0 +1,16 @@ +// This file checks output given when processing OpenCL files. +// When user selects invalid language standard +// print out supported values with short description. + +// RUN: not %clang %s -std=foobar -c 2>&1 | \ +// RUN: FileCheck --match-full-lines %s + +// CHECK: error: invalid value 'foobar' in '-std=foobar' +// CHECK-NEXT: note: use 'cl' for 'OpenCL 1.0' standard +// CHECK-NEXT: note: use 'cl1.1' for 'OpenCL 1.1' standard +// CHECK-NEXT: note: use 'cl1.2' for 'OpenCL 1.2' standard +// CHECK-NEXT: note: use 'cl2.0' for 'OpenCL 2.0' standard + +// Make sure that no other output is present. +// CHECK-NOT: {{^.+$}} + Index: test/Driver/unknown-std.cpp =================================================================== --- /dev/null +++ test/Driver/unknown-std.cpp @@ -0,0 +1,26 @@ +// This file checks output given when processing C++/ObjC++ files. +// When user selects invalid language standard +// print out supported values with short description. + +// RUN: not %clang %s -std=foobar -c 2>&1 | \ +// RUN: FileCheck --match-full-lines %s + +// CHECK: error: invalid value 'foobar' in '-std=foobar' +// CHECK-NEXT: note: use 'c++98' for 'ISO C++ 1998 with amendments' standard +// CHECK-NEXT: note: use 'c++03' for 'ISO C++ 1998 with amendments' standard +// CHECK-NEXT: note: use 'gnu++98' for 'ISO C++ 1998 with amendments and GNU extensions' standard +// CHECK-NEXT: note: use 'c++0x' for 'ISO C++ 2011 with amendments' standard +// CHECK-NEXT: note: use 'c++11' for 'ISO C++ 2011 with amendments' standard +// CHECK-NEXT: note: use 'gnu++0x' for 'ISO C++ 2011 with amendments and GNU extensions' standard +// CHECK-NEXT: note: use 'gnu++11' for 'ISO C++ 2011 with amendments and GNU extensions' standard +// CHECK-NEXT: note: use 'c++1y' for 'ISO C++ 2014 with amendments' standard +// CHECK-NEXT: note: use 'c++14' for 'ISO C++ 2014 with amendments' standard +// CHECK-NEXT: note: use 'gnu++1y' for 'ISO C++ 2014 with amendments and GNU extensions' standard +// CHECK-NEXT: note: use 'gnu++14' for 'ISO C++ 2014 with amendments and GNU extensions' standard +// CHECK-NEXT: note: use 'c++1z' for 'Working draft for ISO C++ 2017' standard +// CHECK-NEXT: note: use 'gnu++1z' for 'Working draft for ISO C++ 2017 with GNU extensions' standard +// CHECK-NEXT: note: use 'cuda' for 'NVIDIA CUDA(tm)' standard + +// Make sure that no other output is present. +// CHECK-NOT: {{^.+$}} +