Index: include/clang/Basic/Diagnostic.h =================================================================== --- include/clang/Basic/Diagnostic.h +++ include/clang/Basic/Diagnostic.h @@ -177,6 +177,9 @@ bool ElideType; // Elide common types of templates. bool PrintTemplateTree; // Print a tree when comparing templates. bool ShowColors; // Color printing is enabled. + + bool StoryTime; // Enable story printing + OverloadsShown ShowOverloads; // Which overload candidates to show. unsigned ErrorLimit; // Cap of # errors emitted, 0 -> no limit. unsigned TemplateBacktraceLimit; // Cap on depth of template backtrace stack, @@ -481,6 +484,10 @@ void setShowColors(bool Val = false) { ShowColors = Val; } bool getShowColors() { return ShowColors; } + /// \brief Set the story time mode. + void setStoryTime(bool Val = false) { StoryTime = Val; } + bool getStoryTime() { return StoryTime; } + /// \brief Specify which overload candidates to show when overload resolution /// fails. /// Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -755,3 +755,5 @@ // A warning group for things that will change semantics in the future. def FutureCompat : DiagGroup<"future-compat">; + +def Story : DiagGroup<"story">; Index: include/clang/Basic/DiagnosticIDs.h =================================================================== --- include/clang/Basic/DiagnosticIDs.h +++ include/clang/Basic/DiagnosticIDs.h @@ -275,6 +275,8 @@ /// ignoring suppression. void EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const; + void PostDiagnosticStoryTime(DiagnosticsEngine &Diag) const; + /// \brief Whether the diagnostic may leave the AST in a state where some /// invariants can break. bool isUnrecoverable(unsigned DiagID) const; Index: include/clang/Basic/DiagnosticOptions.def =================================================================== --- include/clang/Basic/DiagnosticOptions.def +++ include/clang/Basic/DiagnosticOptions.def @@ -74,6 +74,8 @@ DIAGOPT(ShowTemplateTree, 1, 0) /// Print a template tree when diffing DIAGOPT(CLFallbackMode, 1, 0) /// Format for clang-cl fallback mode +DIAGOPT(StoryTime, 1, 0) /// Story time, disabled by default + VALUE_DIAGOPT(ErrorLimit, 32, 0) /// Limit # errors emitted. /// Limit depth of macro expansion backtrace. VALUE_DIAGOPT(MacroBacktraceLimit, 32, DefaultMacroBacktraceLimit) Index: include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- include/clang/Basic/DiagnosticParseKinds.td +++ include/clang/Basic/DiagnosticParseKinds.td @@ -1007,4 +1007,14 @@ "expected ';' after module name">; } +let CategoryName = "Stories" in { +def story_brace_your_else : Remark<"\n\n" + " Brace Your Else \n\n" + " Ambigious Statments Are Coming \n">, + InGroup; + +def story2 : Remark<"">, +InGroup; +} + } // end of Parser diagnostics Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -7580,4 +7580,104 @@ } // end of instrumentation issue category +let CategoryName = "Stories" in { + +def story_true_his_wish : Remark< + "Once upon a time, a young string literal was told that he could be " + "anything when he grew up. He didn't much like being a string literal " + "and wanted very much to be a bool instead. So he journeyed far and wide, " + "looking for the perfect bit of code. Eventually, he came to be inside a " + "bool context. And thus, \"his wish\" became 'true'.\n">, + InGroup; + +def story_true_her_wish : Remark< + "Once upon a time, a young string literal was told that she could be " + "anything when she grew up. She didn't much like being a string literal " + "and wanted very much to be a bool instead. So she journeyed far and wide, " + "looking for the perfect bit of code. Eventually, she came to be inside a " + "bool context. And thus, \"her wish\" became 'true'.\n">, + InGroup; + +def story_my_nightmare : Remark< + "It was a dark and stormy night. The rain was coming down in torrents, " + "only drowned out by the monsterous roar of the cooling fans. The deadline " + "quickly was creeping up, though the mountains of code that still " + "needed to be penned would overwhelm any attempt I could muster. One " + "silver lining had appeared. A colleague had just dropped off a piece of " + "code. I eagerly merged it with my own code, hoping that this new " + "contribution would give a little reprieve. With the newfound lines " + "integrated in, I gave my colleague a shout that the code was compiled " + "and being tested. I should have paid more attention. My colleague, with " + "the wide eyes and half-hidden grin, watched my with a careful eye. I " + "split my glances, watching both the build and my colleague. The machine " + "innocently blinked, indicating the compile finished and the test was " + "about to begin. My colleague couldn't hold it anymore and burst out " + "laughing. I demanded an answer, but I could pry no information. At that " + "moment, my skin crawled and my face burned. Something had possessed my " + "body and yearned to be free. Confused, I pored over the code my " + "colleague handed in earlier. My horror, I notice what was covertly " + "slipped in:\n\n" + "...\n\n" + " __attribute__((noreturn))\n" + " void summon(int val);\n\n" + "...\n\n" + " int number;\n" + " summon(number);\n\n" + "...\n\n" + "My nose became incredibly stuffed, as ectoplasm dripped out. They had " + "picked my nostrils as the egress point. I turned and glared at my " + "colleague, who was manaically laughing now. I was tricked, forced into " + "become a vessel for summoning these demons. My nasal passages would " + "never be the same. As the first demon forced its way out, I realized " + "that \"my nightmare\" was now 'true'.">, + InGroup; + + +def story_12345 : Remark< + "12345? That's amazing! I've got the same combination on my luggage!\n">, + InGroup; + +def story_hunter2 : Remark< + "Hey, if you type your password in code, it will show up as stars.\n" + " const char[] password = \"********\";\n" + "See? You can go hunter2 my hunter2-ing hunter2.\n" + "No matter how many times you type hunter2, it will show up as *******\n">, + InGroup; + +def story_throw_stones : Remark< + "Careful! You might be in a glass house.">, InGroup; + +def story_throw_shoe : Remark< + "Ow! That hurt! Honestly, who throws a shoe?">, InGroup; + +def story_never_return : Remark< + "Dearest Love, I long for the day that I may join you again. Many paths I " + "have traveled in search of warm embrace. But the many twisty paths and " + "dark turns have thwarted my every attempt. You have been my hope, my " + "guiding light, but fate has kept us apart. From this point, the return is " + "unreachable, forever out of my grasp. I am sorry I failed you.">, + InGroup; + +def story_limerick : Remark< + "A Standard Limerick\n" + "\n" + "When writing a specialization,\n" + "be careful about its location;\n" + "or to make it compile\n" + "will be such a trial\n" + "as to kindle its self-immolation.">, + InGroup; + +def story_christmas_halloween : Remark< + "Why do programmers mix up Christmas and Halloween?\n" + "Because Oct 31 = Dec 25">, InGroup; + +def story_10_types_of_people : Remark< + "There are 10 types of people in the world.\n" + "Those who understand binary and those who don't">, InGroup; + +def story_two_hard_problems : Remark< + "There's two hard problems in computer science.\n" + "Cache invalidiation, naming things, and off-by-one errors.">, InGroup; +} } // end of sema component. Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1930,6 +1930,8 @@ defm underscoring : BooleanFFlag<"underscoring">, Group; defm whole_file : BooleanFFlag<"whole-file">, Group; +def story_flag : Flag<["-", "--"], "tell-me-a-story">, Flags<[CC1Option]>, + HelpText<"starts story time">; include "CC1Options.td" Index: lib/Basic/Diagnostic.cpp =================================================================== --- lib/Basic/Diagnostic.cpp +++ lib/Basic/Diagnostic.cpp @@ -52,6 +52,7 @@ ElideType = true; PrintTemplateTree = false; ShowColors = false; + StoryTime = false; ShowOverloads = Ovl_All; ExtBehavior = diag::Severity::Ignored; Index: lib/Basic/DiagnosticIDs.cpp =================================================================== --- lib/Basic/DiagnosticIDs.cpp +++ lib/Basic/DiagnosticIDs.cpp @@ -14,10 +14,12 @@ #include "clang/Basic/DiagnosticIDs.h" #include "clang/Basic/AllDiagnostics.h" #include "clang/Basic/DiagnosticCategories.h" +#include "clang/Basic/IdentifierTable.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/Process.h" #include using namespace clang; @@ -528,7 +530,7 @@ // An empty group is considered to be a warning group: we have empty groups // for GCC compatibility, and GCC does not have remarks. if (!Group->Members && !Group->SubGroups) - return Flavor == diag::Flavor::Remark; + return Flavor == diag::Flavor::Remark ? true : false; bool NotFound = true; @@ -680,11 +682,176 @@ return true; } +void DiagnosticIDs::PostDiagnosticStoryTime(DiagnosticsEngine &Diag) const { + if (!Diag.DiagOpts->StoryTime) + return; + + // Store the old diagnostic options. Set them to the story time values. + // Upon exiting this function, reset to the stored values. + class OptionChanger{ + public: + OptionChanger(DiagnosticOptions *Opts) + : Opts(Opts), ShowColumn(Opts->ShowColumn), + ShowLocation(Opts->ShowLocation), ShowCarets(Opts->ShowCarets), + ShowFixits(Opts->ShowFixits), + ShowSourceRanges(Opts->ShowSourceRanges), + ShowParseableFixits(Opts->ShowParseableFixits), + ShowPresumedLoc(Opts->ShowPresumedLoc), + ShowOptionNames(Opts->ShowOptionNames), + ShowCategories(Opts->ShowCategories) { + Opts->ShowColumn = false; + Opts->ShowLocation = false; + Opts->ShowCarets = false; + Opts->ShowFixits = false; + Opts->ShowSourceRanges = false; + Opts->ShowParseableFixits = false; + Opts->ShowPresumedLoc = false; + Opts->ShowOptionNames = false; + Opts->ShowCategories = false; + } + + ~OptionChanger(){ + Opts->ShowColumn = ShowColumn; + Opts->ShowLocation = ShowLocation; + Opts->ShowCarets = ShowCarets; + Opts->ShowFixits = ShowFixits; + Opts->ShowSourceRanges = ShowSourceRanges; + Opts->ShowParseableFixits = ShowParseableFixits; + Opts->ShowPresumedLoc = ShowPresumedLoc; + Opts->ShowOptionNames = ShowOptionNames; + Opts->ShowCategories = ShowCategories; + } + + DiagnosticOptions *Opts; + bool ShowColumn; + bool ShowLocation; + bool ShowCarets; + bool ShowFixits; + bool ShowSourceRanges; + bool ShowParseableFixits; + bool ShowPresumedLoc; + bool ShowOptionNames; + bool ShowCategories; + } Changer(Diag.DiagOpts.get()); + + // Give additional remarks for these specific diagnostics. + Diagnostic Info(&Diag); + switch (Diag.CurDiagID) { + case diag::err_drv_no_input_files: { + // Running Clang with no files, select a random story to tell. + switch (llvm::sys::Process::GetRandomNumber() % 4) { + default: + case 0: + Diag.CurDiagID = diag::story_limerick; + break; + case 1: + Diag.CurDiagID = diag::story_christmas_halloween; + break; + case 2: + Diag.CurDiagID = diag::story_10_types_of_people; + break; + case 3: + Diag.CurDiagID = diag::story_two_hard_problems; + break; + } + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } + case diag::warn_dangling_else: { + Diag.CurDiagID = diag::story_brace_your_else; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } + case diag::warn_impcast_string_literal_to_bool: + if (Info.getArgKind(2) == DiagnosticsEngine::ak_std_string) { + if (Info.getArgStdStr(2) == "his wish") { + Diag.CurDiagID = diag::story_true_his_wish; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } else if (Info.getArgStdStr(2) == "her wish") { + Diag.CurDiagID = diag::story_true_her_wish; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } else if (Info.getArgStdStr(2) == "my nightmare") { + Diag.CurDiagID = diag::story_my_nightmare; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } + } + break; + case diag::err_exceptions_disabled: { + StringRef Str = Info.getArgStdStr(1); + if (Str == "shoe") { + Diag.CurDiagID = diag::story_throw_shoe; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } else if (Str == "stones") { + Diag.CurDiagID = diag::story_throw_stones; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } + } + case diag::warn_unreachable_return: { + Diag.CurDiagID = diag::story_never_return; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } + } + + // If none of the previous cases triggered, format the args and see if + // it matches one of the strings below. + ArrayRef Args; + SmallVector Output; + ArrayRef Quals; + StringRef str; + for (unsigned I = 0, E = Info.getNumArgs(); I < E; ++I) { + Output.clear(); + switch (Info.getArgKind(I)) { + default: + return; + case DiagnosticsEngine::ak_qualtype_pair: + break; + case DiagnosticsEngine::ak_std_string: + str = Info.getArgStdStr(I); + break; + case DiagnosticsEngine::ak_c_string: + str = Info.getArgCStr(I); + break; + case DiagnosticsEngine::ak_qualtype: + case DiagnosticsEngine::ak_declarationname: + case DiagnosticsEngine::ak_nameddecl: + case DiagnosticsEngine::ak_nestednamespec: + case DiagnosticsEngine::ak_declcontext: + case DiagnosticsEngine::ak_attr: + Diag.ConvertArgToString(Info.getArgKind(I), Info.getRawArg(I), "", "", + Args, Output, Quals); + if (Output.size() > 2 && Output.front() == '\'' && Output.back() == '\'') + str = StringRef(Output.data() + 1, Output.size() - 2); + else + str = StringRef(Output.data(), Output.size()); + } + + if (str == "12345") { + Diag.CurDiagID = diag::story_12345; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } + + if (str == "hunter2") { + Diag.CurDiagID = diag::story_hunter2; + Diag.Client->HandleDiagnostic(DiagnosticsEngine::Remark, Info); + return; + } + } +} + void DiagnosticIDs::EmitDiag(DiagnosticsEngine &Diag, Level DiagLevel) const { Diagnostic Info(&Diag); assert(DiagLevel != DiagnosticIDs::Ignored && "Cannot emit ignored diagnostics!"); Diag.Client->HandleDiagnostic((DiagnosticsEngine::Level)DiagLevel, Info); + PostDiagnosticStoryTime(Diag); + if (Diag.Client->IncludeInDiagnosticCounts()) { if (DiagLevel == DiagnosticIDs::Warning) ++Diag.NumWarnings; Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -3719,6 +3719,8 @@ Args.AddLastArg(CmdArgs, options::OPT_fdiagnostics_show_template_tree); Args.AddLastArg(CmdArgs, options::OPT_fno_elide_type); + Args.AddLastArg(CmdArgs, options::OPT_story_flag); + const SanitizerArgs &Sanitize = getToolChain().getSanitizerArgs(); Sanitize.addArgs(Args, CmdArgs); Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -770,6 +770,7 @@ addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings); addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks); + Opts.StoryTime = Args.hasArg(OPT_story_flag); return Success; } Index: lib/Sema/SemaChecking.cpp =================================================================== --- lib/Sema/SemaChecking.cpp +++ lib/Sema/SemaChecking.cpp @@ -6395,25 +6395,25 @@ } /// Diagnose an implicit cast; purely a helper for CheckImplicitConversion. -static void DiagnoseImpCast(Sema &S, Expr *E, QualType SourceType, QualType T, +static void DiagnoseImpCast(Sema &S, Expr *E, QualType SourceType, QualType T, SourceLocation CContext, unsigned diag, - bool pruneControlFlow = false) { + StringRef Str = "", bool pruneControlFlow = false) { if (pruneControlFlow) { S.DiagRuntimeBehavior(E->getExprLoc(), E, S.PDiag(diag) << SourceType << T << E->getSourceRange() - << SourceRange(CContext)); + << SourceRange(CContext) << Str); return; } S.Diag(E->getExprLoc(), diag) - << SourceType << T << E->getSourceRange() << SourceRange(CContext); + << SourceType << T << E->getSourceRange() << SourceRange(CContext) << Str; } /// Diagnose an implicit cast; purely a helper for CheckImplicitConversion. static void DiagnoseImpCast(Sema &S, Expr *E, QualType T, SourceLocation CContext, unsigned diag, - bool pruneControlFlow = false) { - DiagnoseImpCast(S, E, E->getType(), T, CContext, diag, pruneControlFlow); + StringRef Str = "", bool pruneControlFlow = false) { + DiagnoseImpCast(S, E, E->getType(), T, CContext, diag, Str, pruneControlFlow); } /// Diagnose an implicit cast from a literal expression. Does not warn when the @@ -6554,12 +6554,13 @@ // Diagnose implicit casts to bool. if (Target->isSpecificBuiltinType(BuiltinType::Bool)) { - if (isa(E)) + if (const StringLiteral *SL = dyn_cast(E)) // Warn on string literal to bool. Checks for string literals in logical // and expressions, for instance, assert(0 && "error here"), are // prevented by a check in AnalyzeImplicitConversions(). - return DiagnoseImpCast(S, E, T, CC, - diag::warn_impcast_string_literal_to_bool); + return DiagnoseImpCast( + S, E, T, CC, diag::warn_impcast_string_literal_to_bool, + SL->getCharByteWidth() == 1 ? SL->getString() : ""); if (isa(E) || isa(E) || isa(E) || isa(E)) { // This covers the literal expressions that evaluate to Objective-C @@ -6715,7 +6716,7 @@ if (TargetRange.Width == 32 && S.Context.getIntWidth(E->getType()) == 64) return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_integer_64_32, - /* pruneControlFlow */ true); + /*Str*/ "", /* pruneControlFlow */ true); return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_integer_precision); } Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -640,8 +640,15 @@ bool IsThrownVarInScope) { // Don't report an error if 'throw' is used in system headers. if (!getLangOpts().CXXExceptions && - !getSourceManager().isInSystemHeader(OpLoc)) - Diag(OpLoc, diag::err_exceptions_disabled) << "throw"; + !getSourceManager().isInSystemHeader(OpLoc)) { + StringRef Name = ""; + if (Ex) { + if (DeclRefExpr *DRE = dyn_cast(Ex->IgnoreImpCasts())) { + Name = DRE->getDecl()->getName(); + } + } + Diag(OpLoc, diag::err_exceptions_disabled) << "throw" << Name; + } if (getCurScope() && getCurScope()->isOpenMPSimdDirectiveScope()) Diag(OpLoc, diag::err_omp_simd_region_cannot_use_stmt) << "throw"; Index: test/Misc/story/12345.cpp =================================================================== --- test/Misc/story/12345.cpp +++ test/Misc/story/12345.cpp @@ -0,0 +1,5 @@ +// RUN: %clang_cc1 -fsyntax-only -verify --tell-me-a-story %s + +char x = 12345; +// expected-warning@-1 {{implicit conversion from 'int' to 'char' changes value from 12345 to 57}} +// expected-remark@-2 {{12345? That's amazing! I've got the same combination on my luggage!}} Index: test/Misc/story/else.cpp =================================================================== --- test/Misc/story/else.cpp +++ test/Misc/story/else.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -fsyntax-only --tell-me-a-story %s 2>&1 | FileCheck %s + +void test(int x, int y) { + if (x) + if (y) + return; + else + return; + +// CHECK: add explicit braces to avoid dangling else + +// CHECK: Brace Your Else + +// CHECK: Ambigious Statments Are Coming + +} Index: test/Misc/story/hunter2.cpp =================================================================== --- test/Misc/story/hunter2.cpp +++ test/Misc/story/hunter2.cpp @@ -0,0 +1,9 @@ +// RUN: not %clang_cc1 -fsyntax-only --tell-me-a-story %s 2>&1 | FileCheck %s + +int x = hunter2; +// CHECK: error: use of undeclared identifier 'hunter2' +// CHECK: remark: Hey, if you type your password in code, it will show up as stars. +// CHECK: const char[] password = "********"; +// CHECK: See? You can go hunter2 my hunter2-ing hunter2. +// CHECK: No matter how many times you type hunter2, it will show up as ******* + Index: test/Misc/story/nightmare.cpp =================================================================== --- test/Misc/story/nightmare.cpp +++ test/Misc/story/nightmare.cpp @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fsyntax-only -verify --tell-me-a-story -Wstring-conversion %s + +void test() { + if ("my nightmare") {} + // expected-warning@-1{{}} + // expected-remark@-2{{It was a dark and stormy night.}} +} Index: test/Misc/story/return.cpp =================================================================== --- test/Misc/story/return.cpp +++ test/Misc/story/return.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify --tell-me-a-story -Wunreachable-code-aggressive %s + +__attribute__((noreturn)) + void stop(); + +void test() { + stop(); + return; + // expected-warning@-1{{'return' will never be executed}} + // expected-remark@-2{{Dearest Love, I long for the day that I may join you again. Many paths I have traveled in search of warm embrace. But the many twisty paths and dark turns have thwarted my every attempt. You have been my hope, my guiding light, but fate has kept us apart. From this point, the return is unreachable, forever out of my grasp. I am sorry I failed you.}} +} Index: test/Misc/story/throw.cpp =================================================================== --- test/Misc/story/throw.cpp +++ test/Misc/story/throw.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify --tell-me-a-story %s + +void test1() { + int shoe; + throw shoe; + // expected-error@-1{{cannot use 'throw' with exceptions disabled}} + // expected-remark@-2{{Ow! That hurt! Honestly, who throws a shoe?}} +} + +void test2() { + int stones; + throw stones; + // expected-error@-1{{cannot use 'throw' with exceptions disabled}} + // expected-remark@-2{{Careful! You might be in a glass house.}} +} Index: test/Misc/story/wish.cpp =================================================================== --- test/Misc/story/wish.cpp +++ test/Misc/story/wish.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify --tell-me-a-story -Wstring-conversion %s + +void test() { + if ("her wish") {} + // expected-warning@-1{{implicit conversion turns string literal into bool}} + // expected-remark@-2{{Once upon a time, a young string literal was told that she could be anything when she grew up. She didn't much like being a string literal and wanted very much to be a bool instead. So she journeyed far and wide, looking for the perfect bit of code. Eventually, she came to be inside a bool context. And thus, "her wish" became 'true'.}} + + if ("his wish") {} + // expected-warning@-1{{implicit conversion turns string literal into bool}} + // expected-remark@-2{{Once upon a time, a young string literal was told that he could be anything when he grew up. He didn't much like being a string literal and wanted very much to be a bool instead. So he journeyed far and wide, looking for the perfect bit of code. Eventually, he came to be inside a bool context. And thus, "his wish" became 'true'.}} +}