Index: include/clang/Basic/Diagnostic.h =================================================================== --- include/clang/Basic/Diagnostic.h +++ include/clang/Basic/Diagnostic.h @@ -390,6 +390,9 @@ /// and '='. std::string FlagValue; + /// The source ranges in which all diagnostics should be supressed. + std::vector SuppressionRanges; + public: explicit DiagnosticsEngine(IntrusiveRefCntPtr Diags, DiagnosticOptions *DiagOpts, @@ -688,7 +691,15 @@ /// \brief Reset the state of the diagnostic object to its initial /// configuration. void Reset(); - + + /// Specify a new source range in which all of the diagnostics will be + /// supressed. + void suppressDiagnosticsInRange(SourceRange R); + + ArrayRef getSuppressionRanges() const { + return SuppressionRanges; + } + //===--------------------------------------------------------------------===// // DiagnosticsEngine classification and reporting interfaces. // Index: include/clang/Basic/DiagnosticLexKinds.td =================================================================== --- include/clang/Basic/DiagnosticLexKinds.td +++ include/clang/Basic/DiagnosticLexKinds.td @@ -242,6 +242,7 @@ "illegal character encoding in character literal">, InGroup; def err_lexing_string : Error<"failure when lexing a string">; +def err_placeholder_in_source : Error<"editor placeholder in source file">; //===----------------------------------------------------------------------===// Index: include/clang/Basic/DiagnosticOptions.def =================================================================== --- include/clang/Basic/DiagnosticOptions.def +++ include/clang/Basic/DiagnosticOptions.def @@ -92,6 +92,9 @@ /// Column limit for formatting message diagnostics, or 0 if unused. VALUE_DIAGOPT(MessageLength, 32, 0) +/// True when the 'editor placeholder in source file' has to be supressed. +DIAGOPT(SuppressEditorPlaceholderError, 1, 0) + #undef DIAGOPT #undef ENUM_DIAGOPT #undef VALUE_DIAGOPT Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1470,6 +1470,12 @@ def fno_strict_return : Flag<["-"], "fno-strict-return">, Group, Flags<[CC1Option]>; +def fsuppress_editor_placeholder_error : Flag<["-"], "fsuppress-editor-placeholder-error">, Group, + Flags<[CC1Option]>, + HelpText<"Suppress the 'editor placeholder in source file' errors">; +def fno_suppress_editor_placeholder_error : Flag<["-"], "fno-suppress-editor-placeholder-error">, Group, + Flags<[CC1Option]>; + def fdebug_types_section: Flag <["-"], "fdebug-types-section">, Group, Flags<[CC1Option]>, HelpText<"Place debug types in their own section (ELF Only)">; def fno_debug_types_section: Flag<["-"], "fno-debug-types-section">, Group, Index: include/clang/Lex/Lexer.h =================================================================== --- include/clang/Lex/Lexer.h +++ include/clang/Lex/Lexer.h @@ -89,6 +89,9 @@ // CurrentConflictMarkerState - The kind of conflict marker we are handling. ConflictMarkerKind CurrentConflictMarkerState; + /// The pointer to the end of the current placeholder. + const char *CurrentPlaceholderEnd; + Lexer(const Lexer &) = delete; void operator=(const Lexer &) = delete; friend class Preprocessor; @@ -638,6 +641,9 @@ bool IsStartOfConflictMarker(const char *CurPtr); bool HandleEndOfConflictMarker(const char *CurPtr); + bool lexEditorPlaceholderStart(const char *CurPtr); + bool lexEditorPlaceholderEnd(const char *CurPtr); + bool isCodeCompletionPoint(const char *CurPtr) const; void cutOffLexing() { BufferPtr = BufferEnd; } Index: lib/Basic/Diagnostic.cpp =================================================================== --- lib/Basic/Diagnostic.cpp +++ lib/Basic/Diagnostic.cpp @@ -374,6 +374,10 @@ setSeverity(Diag, Map, Loc); } +void DiagnosticsEngine::suppressDiagnosticsInRange(SourceRange R) { + SuppressionRanges.push_back(R); +} + void DiagnosticsEngine::Report(const StoredDiagnostic &storedDiag) { assert(CurDiagID == ~0U && "Multiple diagnostics in flight at once!"); Index: lib/Basic/DiagnosticIDs.cpp =================================================================== --- lib/Basic/DiagnosticIDs.cpp +++ lib/Basic/DiagnosticIDs.cpp @@ -477,6 +477,18 @@ Diag.getSourceManager().getExpansionLoc(Loc))) return diag::Severity::Ignored; + // Check if the diagnostic is in a suppressed range. + if (Loc.isValid() && Diag.hasSourceManager()) { + const SourceManager &SM = Diag.getSourceManager(); + for (const SourceRange &R : Diag.getSuppressionRanges()) { + if (Loc == R.getBegin() || Loc == R.getEnd()) + return diag::Severity::Ignored; + if (SM.isBeforeInTranslationUnit(R.getBegin(), Loc) && + SM.isBeforeInTranslationUnit(Loc, R.getEnd())) + return diag::Severity::Ignored; + } + } + return Result; } Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -2290,6 +2290,9 @@ if (!Args.hasFlag(options::OPT_fstrict_return, options::OPT_fno_strict_return, true)) CmdArgs.push_back("-fno-strict-return"); + if (Args.hasFlag(options::OPT_fsuppress_editor_placeholder_error, + options::OPT_fno_suppress_editor_placeholder_error, false)) + CmdArgs.push_back("-fsuppress-editor-placeholder-error"); if (Args.hasFlag(options::OPT_fstrict_vtable_pointers, options::OPT_fno_strict_vtable_pointers, false)) Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1092,6 +1092,8 @@ << Opts.TabStop << DiagnosticOptions::DefaultTabStop; } Opts.MessageLength = getLastArgIntValue(Args, OPT_fmessage_length, 0, Diags); + Opts.SuppressEditorPlaceholderError = + Args.hasArg(OPT_fsuppress_editor_placeholder_error); addDiagnosticArgs(Args, OPT_W_Group, OPT_W_value_Group, Opts.Warnings); addDiagnosticArgs(Args, OPT_R_Group, OPT_R_value_Group, Opts.Remarks); Index: lib/Lex/Lexer.cpp =================================================================== --- lib/Lex/Lexer.cpp +++ lib/Lex/Lexer.cpp @@ -108,6 +108,8 @@ // Default to not keeping comments. ExtendedTokenMode = 0; + + CurrentPlaceholderEnd = nullptr; } /// Lexer constructor - Create a new lexer object for the specified buffer @@ -2722,6 +2724,46 @@ return false; } +static const char *findPlaceholderEnd(const char *CurPtr, + const char *BufferEnd) { + if (CurPtr == BufferEnd) + return nullptr; + BufferEnd -= 1; // Scan until the second last character. + for (; CurPtr != BufferEnd; ++CurPtr) { + if (CurPtr[0] == '#' && CurPtr[1] == '>') + return CurPtr + 2; + } + return nullptr; +} + +bool Lexer::lexEditorPlaceholderStart(const char *CurPtr) { + assert(CurPtr[-1] == '<' && CurPtr[0] == '#' && "Not a placeholder!"); + if (!PP || CurrentPlaceholderEnd) + return false; + CurrentPlaceholderEnd = findPlaceholderEnd(CurPtr + 1, BufferEnd); + if (!CurrentPlaceholderEnd) + return false; + BufferPtr = CurPtr + 1; + if (!PP->getDiagnostics() + .getDiagnosticOptions() + .SuppressEditorPlaceholderError) + Diag(CurPtr - 1, diag::err_placeholder_in_source); + // Suppress the diagnostics in range of the editor placeholder. + PP->getDiagnostics().suppressDiagnosticsInRange(SourceRange( + FileLoc.getLocWithOffset(CurPtr - 1 - BufferStart), + FileLoc.getLocWithOffset(CurrentPlaceholderEnd - BufferStart))); + return true; +} + +bool Lexer::lexEditorPlaceholderEnd(const char *CurPtr) { + assert(CurPtr[-1] == '#' && CurPtr[0] == '>' && "Not a placeholder!"); + if (CurrentPlaceholderEnd != CurPtr + 1) + return false; + BufferPtr = CurPtr + 1; + CurrentPlaceholderEnd = nullptr; + return true; +} + bool Lexer::isCodeCompletionPoint(const char *CurPtr) const { if (PP && PP->isCodeCompletionEnabled()) { SourceLocation Loc = FileLoc.getLocWithOffset(CurPtr-BufferStart); @@ -3479,6 +3521,10 @@ } else if (LangOpts.Digraphs && Char == '%') { // '<%' -> '{' CurPtr = ConsumeChar(CurPtr, SizeTmp, Result); Kind = tok::l_brace; + } else if (Char == '#' && lexEditorPlaceholderStart(CurPtr)) { + // If this is '<#' and we're in an editor placeholder, then lex the tokens + // in the placeholder. + goto LexNextToken; } else { Kind = tok::less; } @@ -3581,6 +3627,8 @@ if (!isLexingRawMode()) Diag(BufferPtr, diag::ext_charize_microsoft); CurPtr = ConsumeChar(CurPtr, SizeTmp, Result); + } else if (Char == '>' && lexEditorPlaceholderEnd(CurPtr)) { + goto LexNextToken; } else { // We parsed a # character. If this occurs at the start of the line, // it's actually the start of a preprocessing directive. Callback to Index: test/Driver/clang_f_opts.c =================================================================== --- test/Driver/clang_f_opts.c +++ test/Driver/clang_f_opts.c @@ -494,3 +494,8 @@ // RUN: %clang -### -S -fdebug-info-for-profiling -fno-debug-info-for-profiling %s 2>&1 | FileCheck -check-prefix=CHECK-NO-PROFILE-DEBUG %s // CHECK-PROFILE-DEBUG: -fdebug-info-for-profiling // CHECK-NO-PROFILE-DEBUG-NOT: -fdebug-info-for-profiling + +// RUN: %clang -### -S -fsuppress-editor-placeholder-error %s 2>&1 | FileCheck -check-prefix=CHECK-SUPPRESS-PLACEHOLDER-ERROR %s +// RUN: %clang -### -S -fno-suppress-editor-placeholder-error %s 2>&1 | FileCheck -check-prefix=CHECK-NO-SUPPRESS-PLACEHOLDER-ERROR %s +// CHECK-SUPPRESS-PLACEHOLDER-ERROR: -fsuppress-editor-placeholder-error +// CHECK-NO-SUPPRESS-PLACEHOLDER-ERROR-NOT: -fsuppress-editor-placeholder-error Index: test/Lexer/editor-placeholder.cpp =================================================================== --- /dev/null +++ test/Lexer/editor-placeholder.cpp @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -fsuppress-editor-placeholder-error -DSUPPRESS -verify %s + +struct Struct { +public: + void method(Struct &x); +}; + +void avoidPlaceholderErrors(Struct &obj) { + while (<#condition#>) { + <#statements#> + } + obj.method(<#Struct &x#>); +#ifndef SUPPRESS + // expected-error@-5 {{editor placeholder in source file}} + // expected-error@-5 {{editor placeholder in source file}} + // expected-error@-4 {{editor placeholder in source file}} +#endif + switch (<#expression#>) { + case <#constant#>: + <#statements#> +#ifndef SUPPRESS + // expected-error@-4 {{editor placeholder in source file}} + // expected-error@-4 {{editor placeholder in source file}} + // expected-error@-4 {{editor placeholder in source file}} +#endif + break; + + default: + break; + } +} + +void Struct::method(<#Struct &x#>, noSupressionHere) { // expected-error {{unknown type name 'noSupressionHere'}} +#ifndef SUPPRESS + // expected-error@-2 {{editor placeholder in source file}} +#endif +} Index: test/Parser/placeholder-recovery.m =================================================================== --- test/Parser/placeholder-recovery.m +++ test/Parser/placeholder-recovery.m @@ -3,9 +3,9 @@ // FIXME: We could do much better with this, if we recognized // placeholders somehow. However, we're content with not generating // bogus 'archaic' warnings with bad location info. -@protocol <#protocol name#> // expected-error {{expected identifier or '('}} \ -// expected-error 2{{expected identifier}} \ -// expected-warning{{protocol has no object type specified; defaults to qualified 'id'}} -<#methods#> +@protocol <#protocol name#> // expected-error {{editor placeholder in source file}} +// expected-error@-1 {{expected identifier or '('}} + +<#methods#> // expected-error {{editor placeholder in source file}} @end