Skip to content

Commit 1be800c

Browse files
committedApr 19, 2017
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 llvm-svn: 300667
1 parent 746b3c3 commit 1be800c

16 files changed

+172
-5
lines changed
 

‎clang/include/clang/Basic/DiagnosticLexKinds.td

+1
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ def warn_bad_character_encoding : ExtWarn<
242242
"illegal character encoding in character literal">,
243243
InGroup<InvalidSourceEncoding>;
244244
def err_lexing_string : Error<"failure when lexing a string">;
245+
def err_placeholder_in_source : Error<"editor placeholder in source file">;
245246

246247

247248
//===----------------------------------------------------------------------===//

‎clang/include/clang/Basic/IdentifierTable.h

+13
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,19 @@ class IdentifierInfo {
355355
RecomputeNeedsHandleIdentifier();
356356
}
357357

358+
/// Return true if this identifier is an editor placeholder.
359+
///
360+
/// Editor placeholders are produced by the code-completion engine and are
361+
/// represented as characters between '<#' and '#>' in the source code. An
362+
/// example of auto-completed call with a placeholder parameter is shown
363+
/// below:
364+
/// \code
365+
/// function(<#int x#>);
366+
/// \endcode
367+
bool isEditorPlaceholder() const {
368+
return getName().startswith("<#") && getName().endswith("#>");
369+
}
370+
358371
/// \brief Provide less than operator for lexicographical sorting.
359372
bool operator<(const IdentifierInfo &RHS) const {
360373
return getName() < RHS.getName();

‎clang/include/clang/Basic/LangOptions.def

+2
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan "
266266

267267
LANGOPT(XRayInstrument, 1, 0, "controls whether to do XRay instrumentation")
268268

269+
LANGOPT(AllowEditorPlaceholders, 1, 0, "allow editor placeholders in source")
270+
269271
#undef LANGOPT
270272
#undef COMPATIBLE_LANGOPT
271273
#undef BENIGN_LANGOPT

‎clang/include/clang/Driver/Options.td

+6
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,12 @@ def fstrict_return : Flag<["-"], "fstrict-return">, Group<f_Group>,
14871487
def fno_strict_return : Flag<["-"], "fno-strict-return">, Group<f_Group>,
14881488
Flags<[CC1Option]>;
14891489

1490+
def fallow_editor_placeholders : Flag<["-"], "fallow-editor-placeholders">,
1491+
Group<f_Group>, Flags<[CC1Option]>,
1492+
HelpText<"Treat editor placeholders as valid source code">;
1493+
def fno_allow_editor_placeholders : Flag<["-"],
1494+
"fno-allow-editor-placeholders">, Group<f_Group>;
1495+
14901496
def fdebug_types_section: Flag <["-"], "fdebug-types-section">, Group<f_Group>,
14911497
Flags<[CC1Option]>, HelpText<"Place debug types in their own section (ELF Only)">;
14921498
def fno_debug_types_section: Flag<["-"], "fno-debug-types-section">, Group<f_Group>,

‎clang/include/clang/Lex/Lexer.h

+2
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,8 @@ class Lexer : public PreprocessorLexer {
638638
bool IsStartOfConflictMarker(const char *CurPtr);
639639
bool HandleEndOfConflictMarker(const char *CurPtr);
640640

641+
bool lexEditorPlaceholder(Token &Result, const char *CurPtr);
642+
641643
bool isCodeCompletionPoint(const char *CurPtr) const;
642644
void cutOffLexing() { BufferPtr = BufferEnd; }
643645

‎clang/include/clang/Lex/Token.h

+8
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ class Token {
8484
StringifiedInMacro = 0x100, // This string or character literal is formed by
8585
// macro stringizing or charizing operator.
8686
CommaAfterElided = 0x200, // The comma following this token was elided (MS).
87+
IsEditorPlaceholder = 0x400, // This identifier is a placeholder.
8788
};
8889

8990
tok::TokenKind getKind() const { return Kind; }
@@ -298,6 +299,13 @@ class Token {
298299

299300
/// Returns true if the comma after this token was elided.
300301
bool commaAfterElided() const { return getFlag(CommaAfterElided); }
302+
303+
/// Returns true if this token is an editor placeholder.
304+
///
305+
/// Editor placeholders are produced by the code-completion engine and are
306+
/// represented as characters between '<#' and '#>' in the source code. The
307+
/// lexer uses identifier tokens to represent placeholders.
308+
bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); }
301309
};
302310

303311
/// \brief Information about the conditional stack (\#if directives)

‎clang/lib/Driver/ToolChains/Clang.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -2306,6 +2306,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
23062306
if (!Args.hasFlag(options::OPT_fstrict_return, options::OPT_fno_strict_return,
23072307
true))
23082308
CmdArgs.push_back("-fno-strict-return");
2309+
if (Args.hasFlag(options::OPT_fallow_editor_placeholders,
2310+
options::OPT_fno_allow_editor_placeholders, false))
2311+
CmdArgs.push_back("-fallow-editor-placeholders");
23092312
if (Args.hasFlag(options::OPT_fstrict_vtable_pointers,
23102313
options::OPT_fno_strict_vtable_pointers,
23112314
false))

‎clang/lib/Frontend/CompilerInvocation.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -2322,6 +2322,9 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK,
23222322
Args.getAllArgValues(OPT_fxray_always_instrument);
23232323
Opts.XRayNeverInstrumentFiles =
23242324
Args.getAllArgValues(OPT_fxray_never_instrument);
2325+
2326+
// -fallow-editor-placeholders
2327+
Opts.AllowEditorPlaceholders = Args.hasArg(OPT_fallow_editor_placeholders);
23252328
}
23262329

23272330
static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args,

‎clang/lib/Lex/Lexer.cpp

+33
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,37 @@ bool Lexer::HandleEndOfConflictMarker(const char *CurPtr) {
27162716
return false;
27172717
}
27182718

2719+
static const char *findPlaceholderEnd(const char *CurPtr,
2720+
const char *BufferEnd) {
2721+
if (CurPtr == BufferEnd)
2722+
return nullptr;
2723+
BufferEnd -= 1; // Scan until the second last character.
2724+
for (; CurPtr != BufferEnd; ++CurPtr) {
2725+
if (CurPtr[0] == '#' && CurPtr[1] == '>')
2726+
return CurPtr + 2;
2727+
}
2728+
return nullptr;
2729+
}
2730+
2731+
bool Lexer::lexEditorPlaceholder(Token &Result, const char *CurPtr) {
2732+
assert(CurPtr[-1] == '<' && CurPtr[0] == '#' && "Not a placeholder!");
2733+
if (!PP || LexingRawMode)
2734+
return false;
2735+
const char *End = findPlaceholderEnd(CurPtr + 1, BufferEnd);
2736+
if (!End)
2737+
return false;
2738+
const char *Start = CurPtr - 1;
2739+
if (!LangOpts.AllowEditorPlaceholders)
2740+
Diag(Start, diag::err_placeholder_in_source);
2741+
Result.startToken();
2742+
FormTokenWithChars(Result, End, tok::raw_identifier);
2743+
Result.setRawIdentifierData(Start);
2744+
PP->LookUpIdentifierInfo(Result);
2745+
Result.setFlag(Token::IsEditorPlaceholder);
2746+
BufferPtr = End;
2747+
return true;
2748+
}
2749+
27192750
bool Lexer::isCodeCompletionPoint(const char *CurPtr) const {
27202751
if (PP && PP->isCodeCompletionEnabled()) {
27212752
SourceLocation Loc = FileLoc.getLocWithOffset(CurPtr-BufferStart);
@@ -3473,6 +3504,8 @@ bool Lexer::LexTokenInternal(Token &Result, bool TokAtPhysicalStartOfLine) {
34733504
} else if (LangOpts.Digraphs && Char == '%') { // '<%' -> '{'
34743505
CurPtr = ConsumeChar(CurPtr, SizeTmp, Result);
34753506
Kind = tok::l_brace;
3507+
} else if (Char == '#' && lexEditorPlaceholder(Result, CurPtr)) {
3508+
return true;
34763509
} else {
34773510
Kind = tok::less;
34783511
}

‎clang/lib/Parse/Parser.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,10 @@ Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs,
851851

852852
default:
853853
dont_know:
854+
if (Tok.isEditorPlaceholder()) {
855+
ConsumeToken();
856+
return nullptr;
857+
}
854858
// We can't tell whether this is a function-definition or declaration yet.
855859
return ParseDeclarationOrFunctionDefinition(attrs, DS);
856860
}
@@ -1679,6 +1683,8 @@ bool Parser::TryAnnotateTypeOrScopeToken() {
16791683
return false;
16801684
}
16811685
}
1686+
if (Tok.isEditorPlaceholder())
1687+
return true;
16821688

16831689
Diag(Tok.getLocation(), diag::err_expected_qualified_after_typename);
16841690
return true;

‎clang/lib/Sema/SemaCXXScopeSpec.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,8 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
480480
bool ErrorRecoveryLookup,
481481
bool *IsCorrectedToColon,
482482
bool OnlyNamespace) {
483+
if (IdInfo.Identifier->isEditorPlaceholder())
484+
return true;
483485
LookupResult Found(*this, IdInfo.Identifier, IdInfo.IdentifierLoc,
484486
OnlyNamespace ? LookupNamespaceName
485487
: LookupNestedNameSpecifierName);

‎clang/lib/Sema/SemaDecl.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,9 @@ void Sema::DiagnoseUnknownTypeName(IdentifierInfo *&II,
628628
CXXScopeSpec *SS,
629629
ParsedType &SuggestedType,
630630
bool AllowClassTemplates) {
631+
// Don't report typename errors for editor placeholders.
632+
if (II->isEditorPlaceholder())
633+
return;
631634
// We don't have anything to suggest (yet).
632635
SuggestedType = nullptr;
633636

‎clang/lib/Sema/SemaExpr.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -2129,6 +2129,12 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS,
21292129
IdentifierInfo *II = Name.getAsIdentifierInfo();
21302130
SourceLocation NameLoc = NameInfo.getLoc();
21312131

2132+
if (II && II->isEditorPlaceholder()) {
2133+
// FIXME: When typed placeholders are supported we can create a typed
2134+
// placeholder expression node.
2135+
return ExprError();
2136+
}
2137+
21322138
// C++ [temp.dep.expr]p3:
21332139
// An id-expression is type-dependent if it contains:
21342140
// -- an identifier that was declared with a dependent type,

‎clang/test/Driver/clang_f_opts.c

+5
Original file line numberDiff line numberDiff line change
@@ -494,3 +494,8 @@
494494
// RUN: %clang -### -S -fdebug-info-for-profiling -fno-debug-info-for-profiling %s 2>&1 | FileCheck -check-prefix=CHECK-NO-PROFILE-DEBUG %s
495495
// CHECK-PROFILE-DEBUG: -fdebug-info-for-profiling
496496
// CHECK-NO-PROFILE-DEBUG-NOT: -fdebug-info-for-profiling
497+
498+
// RUN: %clang -### -S -fallow-editor-placeholders %s 2>&1 | FileCheck -check-prefix=CHECK-ALLOW-PLACEHOLDERS %s
499+
// RUN: %clang -### -S -fno-allow-editor-placeholders %s 2>&1 | FileCheck -check-prefix=CHECK-NO-ALLOW-PLACEHOLDERS %s
500+
// CHECK-ALLOW-PLACEHOLDERS: -fallow-editor-placeholders
501+
// CHECK-NO-ALLOW-PLACEHOLDERS-NOT: -fallow-editor-placeholders
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s
2+
// RUN: %clang_cc1 -fsyntax-only -std=c++11 -fallow-editor-placeholders -DSUPPRESS -verify %s
3+
4+
struct Struct {
5+
public:
6+
void method(Struct &x);
7+
};
8+
9+
struct <#struct name#> {
10+
int <#field-name#>;
11+
#ifndef SUPPRESS
12+
// expected-error@-3 {{editor placeholder in source file}}
13+
// expected-error@-3 {{editor placeholder in source file}}
14+
#endif
15+
};
16+
17+
typename <#typename#>::<#name#>;
18+
decltype(<#expression#>) foobar;
19+
typedef <#type#> <#name#>;
20+
#ifndef SUPPRESS
21+
// expected-error@-4 2 {{editor placeholder in source file}}
22+
// expected-error@-4 {{editor placeholder in source file}}
23+
// expected-error@-4 2 {{editor placeholder in source file}}
24+
#endif
25+
26+
namespace <#identifier#> {
27+
<#declarations#>
28+
#ifndef SUPPRESS
29+
// expected-error@-3 {{editor placeholder in source file}}
30+
// expected-error@-3 {{editor placeholder in source file}}
31+
#endif
32+
33+
}
34+
35+
using <#qualifier#>::<#name#>;
36+
#ifndef SUPPRESS
37+
// expected-error@-2 2 {{editor placeholder in source file}}
38+
#endif
39+
40+
void avoidPlaceholderErrors(Struct &obj) {
41+
static_cast< <#type#> >(<#expression#>);
42+
while (<#condition#>) {
43+
<#statements#>
44+
}
45+
obj.method(<#Struct &x#>);
46+
#ifndef SUPPRESS
47+
// expected-error@-6 2 {{editor placeholder in source file}}
48+
// expected-error@-6 {{editor placeholder in source file}}
49+
// expected-error@-6 {{editor placeholder in source file}}
50+
// expected-error@-5 {{editor placeholder in source file}}
51+
#endif
52+
switch (<#expression#>) {
53+
case <#constant#>:
54+
<#statements#>
55+
#ifndef SUPPRESS
56+
// expected-error@-4 {{editor placeholder in source file}}
57+
// expected-error@-4 {{editor placeholder in source file}}
58+
// expected-error@-4 {{editor placeholder in source file}}
59+
#endif
60+
break;
61+
62+
default:
63+
break;
64+
}
65+
}
66+
67+
void Struct::method(<#Struct &x#>, noSupressionHere) { // expected-error {{unknown type name 'noSupressionHere'}} expected-error {{C++ requires a type specifier for all declarations}}
68+
#ifndef SUPPRESS
69+
// expected-error@-2 {{editor placeholder in source file}}
70+
#endif
71+
}
+8-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
// RUN: %clang_cc1 -fsyntax-only -verify %s
22

3+
@protocol NSObject
4+
@end
5+
6+
@protocol <#protocol name#> <NSObject> // expected-error {{editor placeholder in source file}}
7+
// expected-note@-1 {{protocol started here}}
8+
39
// FIXME: We could do much better with this, if we recognized
410
// placeholders somehow. However, we're content with not generating
511
// bogus 'archaic' warnings with bad location info.
6-
@protocol <#protocol name#> <NSObject> // expected-error {{expected identifier or '('}} \
7-
// expected-error 2{{expected identifier}} \
8-
// expected-warning{{protocol has no object type specified; defaults to qualified 'id'}}
9-
<#methods#>
12+
<#methods#> // expected-error {{editor placeholder in source file}}
1013

11-
@end
14+
@end // expected-error {{prefix attribute must be followed by an interface or protocol}} expected-error {{missing '@end'}}

0 commit comments

Comments
 (0)
Please sign in to comment.