Author: arphaman Date: Wed Apr 19 03:58:56 2017 New Revision: 300667 URL: http://llvm.org/viewvc/llvm-project?rev=300667&view=rev Log: Add support for editor placeholders to Clang
This commit teaches Clang to recognize editor placeholders that are produced when an IDE like Xcode inserts a code-completion result that includes a placeholder. Now when the lexer sees a placeholder token, it emits an 'editor placeholder in source file' error and creates an identifier token that represents the placeholder. The parser/sema can now recognize the placeholders and can suppress the diagnostics related to the placeholders. This ensures that live issues in an IDE like Xcode won't get spurious diagnostics related to placeholders. This commit also adds a new compiler option named '-fallow-editor-placeholders' that silences the 'editor placeholder in source file' error. This is useful for an IDE like Xcode as we don't want to display those errors in live issues. rdar://31581400 Differential Revision: https://reviews.llvm.org/D32081 Added: cfe/trunk/test/Parser/editor-placeholder-recovery.cpp Modified: cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td cfe/trunk/include/clang/Basic/IdentifierTable.h cfe/trunk/include/clang/Basic/LangOptions.def cfe/trunk/include/clang/Driver/Options.td cfe/trunk/include/clang/Lex/Lexer.h cfe/trunk/include/clang/Lex/Token.h cfe/trunk/lib/Driver/ToolChains/Clang.cpp cfe/trunk/lib/Frontend/CompilerInvocation.cpp cfe/trunk/lib/Lex/Lexer.cpp cfe/trunk/lib/Parse/Parser.cpp cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp cfe/trunk/lib/Sema/SemaDecl.cpp cfe/trunk/lib/Sema/SemaExpr.cpp cfe/trunk/test/Driver/clang_f_opts.c cfe/trunk/test/Parser/placeholder-recovery.m Modified: cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td (original) +++ cfe/trunk/include/clang/Basic/DiagnosticLexKinds.td Wed Apr 19 03:58:56 2017 @@ -242,6 +242,7 @@ def warn_bad_character_encoding : ExtWar "illegal character encoding in character literal">, InGroup<InvalidSourceEncoding>; def err_lexing_string : Error<"failure when lexing a string">; +def err_placeholder_in_source : Error<"editor placeholder in source file">; //===----------------------------------------------------------------------===// Modified: cfe/trunk/include/clang/Basic/IdentifierTable.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/IdentifierTable.h?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/IdentifierTable.h (original) +++ cfe/trunk/include/clang/Basic/IdentifierTable.h Wed Apr 19 03:58:56 2017 @@ -355,6 +355,19 @@ public: 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(); Modified: cfe/trunk/include/clang/Basic/LangOptions.def URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/LangOptions.def?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/LangOptions.def (original) +++ cfe/trunk/include/clang/Basic/LangOptions.def Wed Apr 19 03:58:56 2017 @@ -266,6 +266,8 @@ LANGOPT(SanitizeAddressFieldPadding, 2, 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 Modified: cfe/trunk/include/clang/Driver/Options.td URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Driver/Options.td?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/include/clang/Driver/Options.td (original) +++ cfe/trunk/include/clang/Driver/Options.td Wed Apr 19 03:58:56 2017 @@ -1487,6 +1487,12 @@ def fstrict_return : Flag<["-"], "fstric def fno_strict_return : Flag<["-"], "fno-strict-return">, Group<f_Group>, Flags<[CC1Option]>; +def fallow_editor_placeholders : Flag<["-"], "fallow-editor-placeholders">, + Group<f_Group>, Flags<[CC1Option]>, + HelpText<"Treat editor placeholders as valid source code">; +def fno_allow_editor_placeholders : Flag<["-"], + "fno-allow-editor-placeholders">, Group<f_Group>; + def fdebug_types_section: Flag <["-"], "fdebug-types-section">, Group<f_Group>, Flags<[CC1Option]>, HelpText<"Place debug types in their own section (ELF Only)">; def fno_debug_types_section: Flag<["-"], "fno-debug-types-section">, Group<f_Group>, Modified: cfe/trunk/include/clang/Lex/Lexer.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/Lexer.h?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/include/clang/Lex/Lexer.h (original) +++ cfe/trunk/include/clang/Lex/Lexer.h Wed Apr 19 03:58:56 2017 @@ -638,6 +638,8 @@ private: 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; } Modified: cfe/trunk/include/clang/Lex/Token.h URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/Token.h?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/include/clang/Lex/Token.h (original) +++ cfe/trunk/include/clang/Lex/Token.h Wed Apr 19 03:58:56 2017 @@ -84,6 +84,7 @@ public: 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 @@ public: /// 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) Modified: cfe/trunk/lib/Driver/ToolChains/Clang.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/ToolChains/Clang.cpp?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/lib/Driver/ToolChains/Clang.cpp (original) +++ cfe/trunk/lib/Driver/ToolChains/Clang.cpp Wed Apr 19 03:58:56 2017 @@ -2306,6 +2306,9 @@ void Clang::ConstructJob(Compilation &C, 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)) Modified: cfe/trunk/lib/Frontend/CompilerInvocation.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/CompilerInvocation.cpp?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/lib/Frontend/CompilerInvocation.cpp (original) +++ cfe/trunk/lib/Frontend/CompilerInvocation.cpp Wed Apr 19 03:58:56 2017 @@ -2322,6 +2322,9 @@ static void ParseLangArgs(LangOptions &O 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, Modified: cfe/trunk/lib/Lex/Lexer.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/Lexer.cpp?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/lib/Lex/Lexer.cpp (original) +++ cfe/trunk/lib/Lex/Lexer.cpp Wed Apr 19 03:58:56 2017 @@ -2716,6 +2716,37 @@ bool Lexer::HandleEndOfConflictMarker(co 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); @@ -3473,6 +3504,8 @@ LexNextToken: } 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; } Modified: cfe/trunk/lib/Parse/Parser.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/Parser.cpp?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/lib/Parse/Parser.cpp (original) +++ cfe/trunk/lib/Parse/Parser.cpp Wed Apr 19 03:58:56 2017 @@ -851,6 +851,10 @@ Parser::ParseExternalDeclaration(ParsedA 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 @@ bool Parser::TryAnnotateTypeOrScopeToken return false; } } + if (Tok.isEditorPlaceholder()) + return true; Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename); return true; Modified: cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp (original) +++ cfe/trunk/lib/Sema/SemaCXXScopeSpec.cpp Wed Apr 19 03:58:56 2017 @@ -480,6 +480,8 @@ bool Sema::BuildCXXNestedNameSpecifier(S bool ErrorRecoveryLookup, bool *IsCorrectedToColon, bool OnlyNamespace) { + if (IdInfo.Identifier->isEditorPlaceholder()) + return true; LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc, OnlyNamespace ? LookupNamespaceName : LookupNestedNameSpecifierName); Modified: cfe/trunk/lib/Sema/SemaDecl.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaDecl.cpp (original) +++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Apr 19 03:58:56 2017 @@ -628,6 +628,9 @@ void Sema::DiagnoseUnknownTypeName(Ident 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; Modified: cfe/trunk/lib/Sema/SemaExpr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp (original) +++ cfe/trunk/lib/Sema/SemaExpr.cpp Wed Apr 19 03:58:56 2017 @@ -2129,6 +2129,12 @@ Sema::ActOnIdExpression(Scope *S, CXXSco 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, Modified: cfe/trunk/test/Driver/clang_f_opts.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/clang_f_opts.c?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/test/Driver/clang_f_opts.c (original) +++ cfe/trunk/test/Driver/clang_f_opts.c Wed Apr 19 03:58:56 2017 @@ -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 Added: cfe/trunk/test/Parser/editor-placeholder-recovery.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/editor-placeholder-recovery.cpp?rev=300667&view=auto ============================================================================== --- cfe/trunk/test/Parser/editor-placeholder-recovery.cpp (added) +++ cfe/trunk/test/Parser/editor-placeholder-recovery.cpp Wed Apr 19 03:58:56 2017 @@ -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 +} Modified: cfe/trunk/test/Parser/placeholder-recovery.m URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/placeholder-recovery.m?rev=300667&r1=300666&r2=300667&view=diff ============================================================================== --- cfe/trunk/test/Parser/placeholder-recovery.m (original) +++ cfe/trunk/test/Parser/placeholder-recovery.m Wed Apr 19 03:58:56 2017 @@ -1,11 +1,14 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s +@protocol NSObject +@end + +@protocol <#protocol name#> <NSObject> // 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#> <NSObject> // 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'}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits