From cd78e612d6fa8e214e6a6bb0e739a0c3e419df91 Mon Sep 17 00:00:00 2001 From: Kaelyn Uhrain Date: Wed, 25 Jan 2012 20:49:08 +0000 Subject: [PATCH] Avoid correcting unknown identifiers to types where types aren't allowed. Pass a typo correction callback object from ParseCastExpr to Sema::ActOnIdExpression to be a bit more selective about what kinds of corrections will be allowed for unknown identifiers. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@148973 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Parser.h | 15 +++++++--- include/clang/Sema/Sema.h | 3 +- lib/Parse/ParseExpr.cpp | 49 +++++++++++++++++++++++++------- lib/Parse/ParseExprCXX.cpp | 2 +- lib/Sema/SemaExpr.cpp | 5 ++-- test/SemaCXX/typo-correction.cpp | 12 ++++++++ 6 files changed, 68 insertions(+), 18 deletions(-) diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index ea79b987e1..07f39603f0 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1260,10 +1260,17 @@ private: //===--------------------------------------------------------------------===// // C99 6.5: Expressions. - ExprResult ParseExpression(); + /// TypeCastState - State whether an expression is or may be a type cast. + enum TypeCastState { + NotTypeCast = 0, + MaybeTypeCast, + IsTypeCast + }; + + ExprResult ParseExpression(TypeCastState isTypeCast = NotTypeCast); ExprResult ParseConstantExpression(); // Expr that doesn't include commas. - ExprResult ParseAssignmentExpression(); + ExprResult ParseAssignmentExpression(TypeCastState isTypeCast = NotTypeCast); ExprResult ParseExpressionWithLeadingAt(SourceLocation AtLoc); @@ -1274,10 +1281,10 @@ private: ExprResult ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, bool &NotCastExpr, - bool isTypeCast); + TypeCastState isTypeCast); ExprResult ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand = false, - bool isTypeCast = false); + TypeCastState isTypeCast = NotTypeCast); /// Returns true if the next token would start a postfix-expression /// suffix. diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index cf77a7e9e5..77ae346e67 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2291,7 +2291,8 @@ public: SourceRange getExprRange(Expr *E) const; ExprResult ActOnIdExpression(Scope *S, CXXScopeSpec &SS, UnqualifiedId &Id, - bool HasTrailingLParen, bool IsAddressOfOperand); + bool HasTrailingLParen, bool IsAddressOfOperand, + CorrectionCandidateCallback *CCC = 0); void DecomposeUnqualifiedId(const UnqualifiedId &Id, TemplateArgumentListInfo &Buffer, diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 606c108d7d..22c5841e45 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -23,6 +23,7 @@ #include "clang/Sema/DeclSpec.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ParsedTemplate.h" +#include "clang/Sema/TypoCorrection.h" #include "clang/Basic/PrettyStackTrace.h" #include "RAIIObjectsForParser.h" #include "llvm/ADT/SmallVector.h" @@ -174,8 +175,8 @@ static prec::Level getBinOpPrecedence(tok::TokenKind Kind, /// expression: [C99 6.5.17] /// assignment-expression ...[opt] /// expression ',' assignment-expression ...[opt] -ExprResult Parser::ParseExpression() { - ExprResult LHS(ParseAssignmentExpression()); +ExprResult Parser::ParseExpression(TypeCastState isTypeCast) { + ExprResult LHS(ParseAssignmentExpression(isTypeCast)); return ParseRHSOfBinaryExpression(move(LHS), prec::Comma); } @@ -211,7 +212,7 @@ Parser::ParseExpressionWithLeadingExtension(SourceLocation ExtLoc) { } /// ParseAssignmentExpression - Parse an expr that doesn't include commas. -ExprResult Parser::ParseAssignmentExpression() { +ExprResult Parser::ParseAssignmentExpression(TypeCastState isTypeCast) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression); cutOffParsing(); @@ -221,7 +222,9 @@ ExprResult Parser::ParseAssignmentExpression() { if (Tok.is(tok::kw_throw)) return ParseThrowExpression(); - ExprResult LHS = ParseCastExpression(/*isUnaryExpression=*/false); + ExprResult LHS = ParseCastExpression(/*isUnaryExpression=*/false, + /*isAddressOfOperand=*/false, + isTypeCast); return ParseRHSOfBinaryExpression(move(LHS), prec::Assignment); } @@ -417,7 +420,7 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { /// ExprResult Parser::ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, - bool isTypeCast) { + TypeCastState isTypeCast) { bool NotCastExpr; ExprResult Res = ParseCastExpression(isUnaryExpression, isAddressOfOperand, @@ -428,6 +431,29 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, return move(Res); } +namespace { +class CastExpressionIdValidator : public CorrectionCandidateCallback { + public: + CastExpressionIdValidator(bool AllowTypes, bool AllowNonTypes) + : AllowNonTypes(AllowNonTypes) { + WantTypeSpecifiers = AllowTypes; + } + + virtual bool ValidateCandidate(const TypoCorrection &candidate) { + NamedDecl *ND = candidate.getCorrectionDecl(); + if (!ND) + return candidate.isKeyword(); + + if (isa(ND)) + return WantTypeSpecifiers; + return AllowNonTypes; + } + + private: + bool AllowNonTypes; +}; +} + /// ParseCastExpression - Parse a cast-expression, or, if isUnaryExpression is /// true, parse a unary-expression. isAddressOfOperand exists because an /// id-expression that is the operand of address-of gets special treatment @@ -592,7 +618,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, ExprResult Parser::ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, bool &NotCastExpr, - bool isTypeCast) { + TypeCastState isTypeCast) { ExprResult Res; tok::TokenKind SavedKind = Tok.getKind(); NotCastExpr = false; @@ -623,7 +649,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, ColonProtectionRAIIObject X(*this, false); Res = ParseParenExpression(ParenExprType, false/*stopIfCastExr*/, - isTypeCast, CastTy, RParenLoc); + isTypeCast == IsTypeCast, CastTy, RParenLoc); } switch (ParenExprType) { @@ -769,9 +795,12 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, // not. UnqualifiedId Name; CXXScopeSpec ScopeSpec; + CastExpressionIdValidator Validator(isTypeCast != NotTypeCast, + isTypeCast != IsTypeCast); Name.setIdentifier(&II, ILoc); Res = Actions.ActOnIdExpression(getCurScope(), ScopeSpec, Name, - Tok.is(tok::l_paren), isAddressOfOperand); + Tok.is(tok::l_paren), isAddressOfOperand, + &Validator); break; } case tok::char_constant: // constant: character-constant @@ -1954,7 +1983,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, // TODO: For cast expression with CastTy. Result = ParseCastExpression(/*isUnaryExpression=*/false, /*isAddressOfOperand=*/false, - /*isTypeCast=*/true); + /*isTypeCast=*/IsTypeCast); if (!Result.isInvalid()) { Result = Actions.ActOnCastExpr(getCurScope(), OpenLoc, DeclaratorInfo, CastTy, @@ -1981,7 +2010,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, } else { InMessageExpressionRAIIObject InMessage(*this, false); - Result = ParseExpression(); + Result = ParseExpression(MaybeTypeCast); ExprType = SimpleExpr; // Don't build a paren expression unless we actually match a ')'. diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index 37d9b5b2b9..dfab03c1c3 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -2552,7 +2552,7 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, false/*isAddressofOperand*/, NotCastExpr, // type-id has priority. - true/*isTypeCast*/); + IsTypeCast); } // If we parsed a cast-expression, it's really a type-id, otherwise it's diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 08439e1868..8a53ece6b3 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1704,7 +1704,8 @@ ExprResult Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, UnqualifiedId &Id, bool HasTrailingLParen, - bool IsAddressOfOperand) { + bool IsAddressOfOperand, + CorrectionCandidateCallback *CCC) { assert(!(IsAddressOfOperand && HasTrailingLParen) && "cannot be direct & operand and have a trailing lparen"); @@ -1819,7 +1820,7 @@ ExprResult Sema::ActOnIdExpression(Scope *S, TemplateArgs); CorrectionCandidateCallback DefaultValidator; - if (DiagnoseEmptyLookup(S, SS, R, DefaultValidator)) + if (DiagnoseEmptyLookup(S, SS, R, CCC ? *CCC : DefaultValidator)) return ExprError(); assert(!R.empty() && diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp index b8795e7d00..6a3a2976a5 100644 --- a/test/SemaCXX/typo-correction.cpp +++ b/test/SemaCXX/typo-correction.cpp @@ -114,6 +114,18 @@ struct TestRedecl : public BaseDecl { }; void TestRedecl::add_in(int i) {} // expected-error{{out-of-line definition of 'add_in' does not match any declaration in 'TestRedecl'; did you mean 'add_it'?}} +// Test the improved typo correction for the Parser::ParseCastExpr => +// Sema::ActOnIdExpression => Sema::DiagnoseEmptyLookup call path. +class SomeNetMessage; +class Message {}; +void foo(Message&); +void foo(SomeNetMessage&); +void doit(void *data) { + Message somenetmsg; // expected-note{{'somenetmsg' declared here}} + foo(somenetmessage); // expected-error{{use of undeclared identifier 'somenetmessage'; did you mean 'somenetmsg'?}} + foo((somenetmessage)data); // expected-error{{use of undeclared identifier 'somenetmessage'; did you mean 'SomeNetMessage'?}} +} + // Test the typo-correction callback in BuildRecoveryCallExpr. // Solves the main issue in PR 9320 of suggesting corrections that take the // wrong number of arguments. -- 2.40.0