@@ -281,6 +281,132 @@ static bool matchesDeclRefKind(ValueDecl *value, DeclRefKind refKind) {
281
281
llvm_unreachable (" bad declaration reference kind" );
282
282
}
283
283
284
+ static bool containsDeclRefKind (LookupResult &lookupResult,
285
+ DeclRefKind refKind) {
286
+ for (auto candidate : lookupResult) {
287
+ ValueDecl *D = candidate.Decl ;
288
+ if (!D || !D->hasType ())
289
+ continue ;
290
+ if (matchesDeclRefKind (D, refKind))
291
+ return true ;
292
+ }
293
+ return false ;
294
+ }
295
+
296
+ // / Emit a diagnostic with a fixit hint for an invalid binary operator, showing
297
+ // / how to split it according to splitCandidate.
298
+ static void diagnoseOperatorSplit (UnresolvedDeclRefExpr *UDRE,
299
+ std::pair<unsigned , bool > splitCandidate,
300
+ Diag<Identifier, Identifier, bool > diagID,
301
+ TypeChecker &TC) {
302
+
303
+ unsigned splitLoc = splitCandidate.first ;
304
+ bool isBinOpFirst = splitCandidate.second ;
305
+ StringRef nameStr = UDRE->getName ().str ();
306
+ auto startStr = nameStr.substr (0 , splitLoc);
307
+ auto endStr = nameStr.drop_front (splitLoc);
308
+
309
+ // One valid split found, it is almost certainly the right answer.
310
+ auto diag = TC.diagnose (UDRE->getLoc (), diagID,
311
+ TC.Context .getIdentifier (startStr),
312
+ TC.Context .getIdentifier (endStr), isBinOpFirst);
313
+ // Highlight the whole operator.
314
+ diag.highlight (UDRE->getLoc ());
315
+ // Insert whitespace on the left if the binop is at the start, or to the
316
+ // right if it is end.
317
+ if (isBinOpFirst)
318
+ diag.fixItInsert (UDRE->getLoc (), " " );
319
+ else
320
+ diag.fixItInsertAfter (UDRE->getLoc (), " " );
321
+
322
+ // Insert a space between the operators.
323
+ diag.fixItInsert (UDRE->getLoc ().getAdvancedLoc (splitLoc), " " );
324
+ }
325
+
326
+ // / If we failed lookup of a binary operator, check to see it to see if
327
+ // / it is a binary operator juxtaposed with a unary operator (x*-4) that
328
+ // / needs whitespace. If so, emit specific diagnostics for it and return true,
329
+ // / otherwise return false.
330
+ static bool diagnoseJuxtaposedBinOp (UnresolvedDeclRefExpr *UDRE,
331
+ DeclContext *DC,
332
+ TypeChecker &TC) {
333
+ Identifier name = UDRE->getName ();
334
+ StringRef nameStr = name.str ();
335
+ if (!name.isOperator () || nameStr.size () < 2 ||
336
+ UDRE->getRefKind () != DeclRefKind::BinaryOperator)
337
+ return false ;
338
+
339
+ // Relex the token, to decide whether it has whitespace around it or not. If
340
+ // it does, it isn't likely to be a case where a space was forgotten.
341
+ auto tok = Lexer::getTokenAtLocation (TC.Context .SourceMgr , UDRE->getLoc ());
342
+ if (tok.getKind () != tok::oper_binary_unspaced)
343
+ return false ;
344
+
345
+ // Okay, we have a failed lookup of a multicharacter unspaced binary operator.
346
+ // Check to see if lookup succeeds if a prefix or postfix operator is split
347
+ // off, and record the matches found. The bool indicated is false if the
348
+ // first half of the split is the unary operator (x!*4) or true if it is the
349
+ // binary operator (x*+4).
350
+ std::vector<std::pair<unsigned , bool >> WorkableSplits;
351
+
352
+ // Check all the potential splits.
353
+ for (unsigned splitLoc = 1 , e = nameStr.size (); splitLoc != e; ++splitLoc) {
354
+ // For it to be a valid split, the start and end section must be valid
355
+ // operators, spliting a unicode code point isn't kosher.
356
+ auto startStr = nameStr.substr (0 , splitLoc);
357
+ auto endStr = nameStr.drop_front (splitLoc);
358
+ if (!Lexer::isOperator (startStr) || !Lexer::isOperator (endStr))
359
+ continue ;
360
+
361
+ auto startName = TC.Context .getIdentifier (startStr);
362
+ auto endName = TC.Context .getIdentifier (endStr);
363
+
364
+ // Perform name lookup for the first and second pieces. If either fail to
365
+ // be found, then it isn't a valid split.
366
+ NameLookupOptions LookupOptions = defaultUnqualifiedLookupOptions;
367
+ if (isa<AbstractFunctionDecl>(DC))
368
+ LookupOptions |= NameLookupFlags::KnownPrivate;
369
+ auto startLookup = TC.lookupUnqualified (DC, startName, UDRE->getLoc (),
370
+ LookupOptions);
371
+ if (!startLookup) continue ;
372
+ auto endLookup = TC.lookupUnqualified (DC, endName, UDRE->getLoc (),
373
+ LookupOptions);
374
+ if (!endLookup) continue ;
375
+
376
+ // Look to see if the candidates found could possibly match.
377
+ if (containsDeclRefKind (startLookup, DeclRefKind::PostfixOperator) &&
378
+ containsDeclRefKind (endLookup, DeclRefKind::BinaryOperator))
379
+ WorkableSplits.push_back ({ splitLoc, false });
380
+
381
+ if (containsDeclRefKind (startLookup, DeclRefKind::BinaryOperator) &&
382
+ containsDeclRefKind (endLookup, DeclRefKind::PrefixOperator))
383
+ WorkableSplits.push_back ({ splitLoc, true });
384
+ }
385
+
386
+ switch (WorkableSplits.size ()) {
387
+ case 0 :
388
+ // No splits found, can't produce this diagnostic.
389
+ return false ;
390
+ case 1 :
391
+ // One candidate: produce an error with a fixit on it.
392
+ diagnoseOperatorSplit (UDRE, WorkableSplits[0 ],
393
+ diag::unspaced_operators_fixit, TC);
394
+ return true ;
395
+
396
+ default :
397
+ // Otherwise, we have to produce a series of notes listing the various
398
+ // options.
399
+ TC.diagnose (UDRE->getLoc (), diag::unspaced_operators)
400
+ .highlight (UDRE->getLoc ());
401
+
402
+ for (auto candidateSplit : WorkableSplits)
403
+ diagnoseOperatorSplit (UDRE, candidateSplit,
404
+ diag::unspaced_operators_candidate, TC);
405
+ return true ;
406
+ }
407
+ }
408
+
409
+
284
410
// / Bind an UnresolvedDeclRefExpr by performing name lookup and
285
411
// / returning the resultant expression. Context is the DeclContext used
286
412
// / for the lookup.
@@ -297,9 +423,14 @@ resolveDeclRefExpr(UnresolvedDeclRefExpr *UDRE, DeclContext *DC) {
297
423
auto Lookup = lookupUnqualified (DC, Name, Loc, LookupOptions);
298
424
299
425
if (!Lookup) {
300
- diagnose (Loc, diag::use_unresolved_identifier, Name,
301
- UDRE->getName ().isOperator ())
302
- .highlight (Loc);
426
+ // If we failed lookup of a binary operator, check to see it to see if
427
+ // it is a binary operator juxtaposed with a unary operator (x*-4) that
428
+ // needs whitespace. If so, emit specific diagnostics for it.
429
+ if (!diagnoseJuxtaposedBinOp (UDRE, DC, *this )) {
430
+ diagnose (Loc, diag::use_unresolved_identifier, Name,
431
+ UDRE->getName ().isOperator ())
432
+ .highlight (Loc);
433
+ }
303
434
return new (Context) ErrorExpr (Loc);
304
435
}
305
436
0 commit comments