Author: vsapsai Date: Thu Sep 14 17:08:37 2017 New Revision: 313323 URL: http://llvm.org/viewvc/llvm-project?rev=313323&view=rev Log: [Sema] Correct typos in LHS, RHS before building a binop expression.
Specifically, typo correction should be done before dispatching between different kinds of binary operations like pseudo-object assignment, overloaded binary operation, etc. Without this change we hit an assertion Assertion failed: (!LHSExpr->hasPlaceholderType(BuiltinType::PseudoObject)), function CheckAssignmentOperands when in Objective-C we reference a property without `self` and there are 2 equally good typo correction candidates: ivar and a class name. In this case LHS expression in `BuildBinOp` is CXXDependentScopeMemberExpr `-TypoExpr and instead of handling Obj-C property assignment as pseudo-object assignment, we call `CreateBuiltinBinOp` which corrects typo to ObjCPropertyRefExpr '<pseudo-object type>' but cannot handle pseudo-objects and asserts about it (indirectly, through `CheckAssignmentOperands`). rdar://problem/33102722 Reviewers: rsmith, ahatanak, majnemer Reviewed By: ahatanak Subscribers: cfe-commits Differential Revision: https://reviews.llvm.org/D37322 Modified: cfe/trunk/lib/Sema/SemaExpr.cpp cfe/trunk/test/SemaObjC/typo-correction.m cfe/trunk/test/SemaObjCXX/typo-correction.mm Modified: cfe/trunk/lib/Sema/SemaExpr.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=313323&r1=313322&r2=313323&view=diff ============================================================================== --- cfe/trunk/lib/Sema/SemaExpr.cpp (original) +++ cfe/trunk/lib/Sema/SemaExpr.cpp Thu Sep 14 17:08:37 2017 @@ -11269,6 +11269,26 @@ static NamedDecl *getDeclFromExpr(Expr * return nullptr; } +static std::pair<ExprResult, ExprResult> +CorrectDelayedTyposInBinOp(Sema &S, BinaryOperatorKind Opc, Expr *LHSExpr, + Expr *RHSExpr) { + ExprResult LHS = LHSExpr, RHS = RHSExpr; + if (!S.getLangOpts().CPlusPlus) { + // C cannot handle TypoExpr nodes on either side of a binop because it + // doesn't handle dependent types properly, so make sure any TypoExprs have + // been dealt with before checking the operands. + LHS = S.CorrectDelayedTyposInExpr(LHS); + RHS = S.CorrectDelayedTyposInExpr(RHS, [Opc, LHS](Expr *E) { + if (Opc != BO_Assign) + return ExprResult(E); + // Avoid correcting the RHS to the same Expr as the LHS. + Decl *D = getDeclFromExpr(E); + return (D && D == getDeclFromExpr(LHS.get())) ? ExprError() : E; + }); + } + return std::make_pair(LHS, RHS); +} + /// CreateBuiltinBinOp - Creates a new built-in binary operation with /// operator @p Opc at location @c TokLoc. This routine only supports /// built-in operations; ActOnBinOp handles overloaded operators. @@ -11301,21 +11321,9 @@ ExprResult Sema::CreateBuiltinBinOp(Sour ExprValueKind VK = VK_RValue; ExprObjectKind OK = OK_Ordinary; - if (!getLangOpts().CPlusPlus) { - // C cannot handle TypoExpr nodes on either side of a binop because it - // doesn't handle dependent types properly, so make sure any TypoExprs have - // been dealt with before checking the operands. - LHS = CorrectDelayedTyposInExpr(LHSExpr); - RHS = CorrectDelayedTyposInExpr(RHSExpr, [Opc, LHS](Expr *E) { - if (Opc != BO_Assign) - return ExprResult(E); - // Avoid correcting the RHS to the same Expr as the LHS. - Decl *D = getDeclFromExpr(E); - return (D && D == getDeclFromExpr(LHS.get())) ? ExprError() : E; - }); - if (!LHS.isUsable() || !RHS.isUsable()) - return ExprError(); - } + std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); + if (!LHS.isUsable() || !RHS.isUsable()) + return ExprError(); if (getLangOpts().OpenCL) { QualType LHSTy = LHSExpr->getType(); @@ -11729,6 +11737,13 @@ static ExprResult BuildOverloadedBinOp(S ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr) { + ExprResult LHS, RHS; + std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); + if (!LHS.isUsable() || !RHS.isUsable()) + return ExprError(); + LHSExpr = LHS.get(); + RHSExpr = RHS.get(); + // We want to end up calling one of checkPseudoObjectAssignment // (if the LHS is a pseudo-object), BuildOverloadedBinOp (if // both expressions are overloadable or either is type-dependent), Modified: cfe/trunk/test/SemaObjC/typo-correction.m URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/typo-correction.m?rev=313323&r1=313322&r2=313323&view=diff ============================================================================== --- cfe/trunk/test/SemaObjC/typo-correction.m (original) +++ cfe/trunk/test/SemaObjC/typo-correction.m Thu Sep 14 17:08:37 2017 @@ -51,3 +51,23 @@ __attribute__ (( __objc_root_class__ )) } @end +// rdar://problem/33102722 +// Typo correction for a property when it has as correction candidates +// synthesized ivar and a class name, both at the same edit distance. +@class TypoCandidate; + +__attribute__ (( __objc_root_class__ )) +@interface PropertyType +@property int x; +@end + +__attribute__ (( __objc_root_class__ )) +@interface InterfaceC +@property(assign) PropertyType *typoCandidate; // expected-note {{'_typoCandidate' declared here}} +@end + +@implementation InterfaceC +-(void)method { + typoCandidate.x = 0; // expected-error {{use of undeclared identifier 'typoCandidate'; did you mean '_typoCandidate'?}} +} +@end Modified: cfe/trunk/test/SemaObjCXX/typo-correction.mm URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjCXX/typo-correction.mm?rev=313323&r1=313322&r2=313323&view=diff ============================================================================== --- cfe/trunk/test/SemaObjCXX/typo-correction.mm (original) +++ cfe/trunk/test/SemaObjCXX/typo-correction.mm Thu Sep 14 17:08:37 2017 @@ -36,3 +36,22 @@ void invalidNameInIvarAndPropertyBase() float a = ((InvalidNameInIvarAndPropertyBase*)node)->_a; // expected-error {{use of undeclared identifier 'node'}} float b = ((InvalidNameInIvarAndPropertyBase*)node)._b; // expected-error {{use of undeclared identifier 'node'}} } + +// rdar://problem/33102722 +// Typo correction for a property when it has as correction candidates +// synthesized ivar and a class name, both at the same edit distance. +@class TypoCandidate; + +@interface PropertyType : NSObject +@property int x; +@end + +@interface InterfaceC : NSObject +@property(assign) PropertyType *typoCandidate; // expected-note {{'_typoCandidate' declared here}} +@end + +@implementation InterfaceC +-(void)method { + typoCandidate.x = 0; // expected-error {{use of undeclared identifier 'typoCandidate'; did you mean '_typoCandidate'?}} +} +@end _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits