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/IdentifierTable.h =================================================================== --- include/clang/Basic/IdentifierTable.h +++ include/clang/Basic/IdentifierTable.h @@ -355,6 +355,19 @@ RecomputeNeedsHandleIdentifier(); } + /// Return true if this identifier is an editor placeholder. + /// + /// Editor placeholders are produced by the code-completion engine and are + /// represented as characters between '<#' and '#>' in the source code. An + /// example of auto-completed call with a placeholder parameter is shown + /// below: + /// \code + /// function(<#int x#>); + /// \endcode + bool isEditorPlaceholder() const { + return getName().startswith("<#") && getName().endswith("#>"); + } + /// \brief Provide less than operator for lexicographical sorting. bool operator<(const IdentifierInfo &RHS) const { return getName() < RHS.getName(); Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -266,6 +266,8 @@ LANGOPT(XRayInstrument, 1, 0, "controls whether to do XRay instrumentation") +LANGOPT(AllowEditorPlaceholders, 1, 0, "allow editor placeholders in source") + #undef LANGOPT #undef COMPATIBLE_LANGOPT #undef BENIGN_LANGOPT Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1487,6 +1487,12 @@ def fno_strict_return : Flag<["-"], "fno-strict-return">, Group, Flags<[CC1Option]>; +def fallow_editor_placeholders : Flag<["-"], "fallow-editor-placeholders">, + Group, Flags<[CC1Option]>, + HelpText<"Treat editor placeholders as valid source code">; +def fno_allow_editor_placeholders : Flag<["-"], + "fno-allow-editor-placeholders">, Group; + 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 @@ -638,6 +638,8 @@ bool IsStartOfConflictMarker(const char *CurPtr); bool HandleEndOfConflictMarker(const char *CurPtr); + bool lexEditorPlaceholder(Token &Result, const char *CurPtr); + bool isCodeCompletionPoint(const char *CurPtr) const; void cutOffLexing() { BufferPtr = BufferEnd; } Index: include/clang/Lex/Token.h =================================================================== --- include/clang/Lex/Token.h +++ include/clang/Lex/Token.h @@ -84,6 +84,7 @@ StringifiedInMacro = 0x100, // This string or character literal is formed by // macro stringizing or charizing operator. CommaAfterElided = 0x200, // The comma following this token was elided (MS). + IsEditorPlaceholder = 0x400, // This identifier is a placeholder. }; tok::TokenKind getKind() const { return Kind; } @@ -298,6 +299,13 @@ /// Returns true if the comma after this token was elided. bool commaAfterElided() const { return getFlag(CommaAfterElided); } + + /// Returns true if this token is an editor placeholder. + /// + /// Editor placeholders are produced by the code-completion engine and are + /// represented as characters between '<#' and '#>' in the source code. The + /// lexer uses identifier tokens to represent placeholders. + bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); } }; /// \brief Information about the conditional stack (\#if directives) 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_fallow_editor_placeholders, + options::OPT_fno_allow_editor_placeholders, false)) + CmdArgs.push_back("-fallow-editor-placeholders"); 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 @@ -2324,6 +2324,9 @@ Args.getAllArgValues(OPT_fxray_always_instrument); Opts.XRayNeverInstrumentFiles = Args.getAllArgValues(OPT_fxray_never_instrument); + + // -fallow-editor-placeholders + Opts.AllowEditorPlaceholders = Args.hasArg(OPT_fallow_editor_placeholders); } static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, Index: lib/Lex/Lexer.cpp =================================================================== --- lib/Lex/Lexer.cpp +++ lib/Lex/Lexer.cpp @@ -2714,6 +2714,37 @@ 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::lexEditorPlaceholder(Token &Result, const char *CurPtr) { + assert(CurPtr[-1] == '<' && CurPtr[0] == '#' && "Not a placeholder!"); + if (!PP || LexingRawMode) + return false; + const char *End = findPlaceholderEnd(CurPtr + 1, BufferEnd); + if (!End) + return false; + const char *Start = CurPtr - 1; + if (!LangOpts.AllowEditorPlaceholders) + Diag(Start, diag::err_placeholder_in_source); + Result.startToken(); + FormTokenWithChars(Result, End, tok::raw_identifier); + Result.setRawIdentifierData(Start); + PP->LookUpIdentifierInfo(Result); + Result.setFlag(Token::IsEditorPlaceholder); + BufferPtr = End; + return true; +} + bool Lexer::isCodeCompletionPoint(const char *CurPtr) const { if (PP && PP->isCodeCompletionEnabled()) { SourceLocation Loc = FileLoc.getLocWithOffset(CurPtr-BufferStart); @@ -3471,6 +3502,8 @@ } else if (LangOpts.Digraphs && Char == '%') { // '<%' -> '{' CurPtr = ConsumeChar(CurPtr, SizeTmp, Result); Kind = tok::l_brace; + } else if (Char == '#' && lexEditorPlaceholder(Result, CurPtr)) { + return true; } else { Kind = tok::less; } Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -851,6 +851,10 @@ default: dont_know: + if (Tok.isEditorPlaceholder()) { + ConsumeToken(); + return nullptr; + } // We can't tell whether this is a function-definition or declaration yet. return ParseDeclarationOrFunctionDefinition(attrs, DS); } @@ -1679,6 +1683,8 @@ return false; } } + if (Tok.isEditorPlaceholder()) + return true; Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename); return true; Index: lib/Sema/SemaCXXScopeSpec.cpp =================================================================== --- lib/Sema/SemaCXXScopeSpec.cpp +++ lib/Sema/SemaCXXScopeSpec.cpp @@ -480,6 +480,8 @@ bool ErrorRecoveryLookup, bool *IsCorrectedToColon, bool OnlyNamespace) { + if (IdInfo.Identifier->isEditorPlaceholder()) + return true; LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc, OnlyNamespace ? LookupNamespaceName : LookupNestedNameSpecifierName); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -628,6 +628,9 @@ CXXScopeSpec *SS, ParsedType &SuggestedType, bool AllowClassTemplates) { + // Don't report typename errors for editor placeholders. + if (II->isEditorPlaceholder()) + return; // We don't have anything to suggest (yet). SuggestedType = nullptr; Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -2129,6 +2129,12 @@ IdentifierInfo *II = Name.getAsIdentifierInfo(); SourceLocation NameLoc = NameInfo.getLoc(); + if (II && II->isEditorPlaceholder()) { + // FIXME: When typed placeholders are supported we can create a typed + // placeholder expression node. + return ExprError(); + } + // C++ [temp.dep.expr]p3: // An id-expression is type-dependent if it contains: // -- an identifier that was declared with a dependent type, 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 -fallow-editor-placeholders %s 2>&1 | FileCheck -check-prefix=CHECK-ALLOW-PLACEHOLDERS %s +// RUN: %clang -### -S -fno-allow-editor-placeholders %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ALLOW-PLACEHOLDERS %s +// CHECK-ALLOW-PLACEHOLDERS: -fallow-editor-placeholders +// CHECK-NO-ALLOW-PLACEHOLDERS-NOT: -fallow-editor-placeholders Index: test/Parser/editor-placeholder-recovery.cpp =================================================================== --- /dev/null +++ test/Parser/editor-placeholder-recovery.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -fallow-editor-placeholders -DSUPPRESS -verify %s + +struct Struct { +public: + void method(Struct &x); +}; + +struct <#struct name#> { + int <#field-name#>; +#ifndef SUPPRESS + // expected-error@-3 {{editor placeholder in source file}} + // expected-error@-3 {{editor placeholder in source file}} +#endif +}; + +typename <#typename#>::<#name#>; +decltype(<#expression#>) foobar; +typedef <#type#> <#name#>; +#ifndef SUPPRESS + // expected-error@-4 2 {{editor placeholder in source file}} + // expected-error@-4 {{editor placeholder in source file}} + // expected-error@-4 2 {{editor placeholder in source file}} +#endif + +namespace <#identifier#> { + <#declarations#> +#ifndef SUPPRESS + // expected-error@-3 {{editor placeholder in source file}} + // expected-error@-3 {{editor placeholder in source file}} +#endif + +} + +using <#qualifier#>::<#name#>; +#ifndef SUPPRESS + // expected-error@-2 2 {{editor placeholder in source file}} +#endif + +void avoidPlaceholderErrors(Struct &obj) { + static_cast< <#type#> >(<#expression#>); + while (<#condition#>) { + <#statements#> + } + obj.method(<#Struct &x#>); +#ifndef SUPPRESS + // expected-error@-6 2 {{editor placeholder in source file}} + // expected-error@-6 {{editor placeholder in source file}} + // expected-error@-6 {{editor placeholder in source file}} + // expected-error@-5 {{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'}} expected-error {{C++ requires a type specifier for all declarations}} +#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 @@ -1,11 +1,14 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +@protocol NSObject +@end + +@protocol <#protocol name#> // expected-error {{editor placeholder in source file}} +// expected-note@-1 {{protocol started here}} + // 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#> +<#methods#> // expected-error {{editor placeholder in source file}} -@end +@end // expected-error {{prefix attribute must be followed by an interface or protocol}} expected-error {{missing '@end'}}