[cfe-commits] PATCH: Add typo correction for the (non-template) type name in C++ "new" statements. (issue 5014044)
rikka at google.com
rikka at google.com
Tue Sep 13 17:16:25 PDT 2011
Reviewers: cfe-commits_cs.uiuc.edu,
Please review this at http://codereview.appspot.com/5014044/
Affected files:
M include/clang/Parse/Parser.h
M include/clang/Sema/Sema.h
M lib/Parse/ParseDecl.cpp
M lib/Parse/ParseExprCXX.cpp
M lib/Parse/Parser.cpp
M lib/Sema/SemaDecl.cpp
M test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
-------------- next part --------------
Index: include/clang/Parse/Parser.h
diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h
index 906958f323539e5d16e35050f0f7efdde89acba4..3b7540dc039e31fdee8b4985aaba06a18432e593 100644
--- a/include/clang/Parse/Parser.h
+++ b/include/clang/Parse/Parser.h
@@ -436,7 +436,8 @@ private:
Tok.setAnnotationValue(ER.get());
}
- bool TryAnnotateTypeOrScopeToken(bool EnteringContext = false);
+ bool TryAnnotateTypeOrScopeToken(bool EnteringContext = false,
+ bool TryTypoCorrection = false);
bool TryAnnotateCXXScopeToken(bool EnteringContext = false);
/// TryAltiVecToken - Check for context-sensitive AltiVec identifier tokens,
@@ -1312,7 +1313,7 @@ private:
/// simple-type-specifier.
void ParseCXXSimpleTypeSpecifier(DeclSpec &DS);
- bool ParseCXXTypeSpecifierSeq(DeclSpec &DS);
+ bool ParseCXXTypeSpecifierSeq(DeclSpec &DS, bool TryTypoCorrection=false);
//===--------------------------------------------------------------------===//
// C++ 5.3.4 and 5.3.5: C++ new and delete
@@ -1499,7 +1500,8 @@ private:
const char *&PrevSpec,
unsigned &DiagID,
const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(),
- bool SuppressDeclarations = false);
+ bool SuppressDeclarations = false,
+ bool TryTypoCorrection = false);
void ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS = AS_none);
Index: include/clang/Sema/Sema.h
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index c08244490afdb1e8a4c5d71f182b75dff24f9570..0eaa59fbaa9aa0f33353ab61d866d25a968b8de4 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -863,7 +863,8 @@ public:
bool isClassName = false,
bool HasTrailingDot = false,
ParsedType ObjectType = ParsedType(),
- bool WantNontrivialTypeSourceInfo = false);
+ bool WantNontrivialTypeSourceInfo = false,
+ IdentifierInfo **CorrectedII = 0);
TypeSpecifierType isTagName(IdentifierInfo &II, Scope *S);
bool isMicrosoftMissingTypename(const CXXScopeSpec *SS);
bool DiagnoseUnknownTypeName(const IdentifierInfo &II,
Index: lib/Parse/ParseDecl.cpp
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index 16c25a47904fb37dffeb999f81158728ca4804ea..5fcb318ffda3ba82f2d533da399cd40cf8a88eeb 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -2215,7 +2215,8 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, bool& isInvalid,
const char *&PrevSpec,
unsigned &DiagID,
const ParsedTemplateInfo &TemplateInfo,
- bool SuppressDeclarations) {
+ bool SuppressDeclarations,
+ bool TryTypoCorrection) {
SourceLocation Loc = Tok.getLocation();
switch (Tok.getKind()) {
@@ -2232,12 +2233,12 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, bool& isInvalid,
case tok::kw_typename: // typename foo::bar
// Annotate typenames and C++ scope specifiers. If we get one, just
// recurse to handle whatever we get.
- if (TryAnnotateTypeOrScopeToken())
+ if (TryAnnotateTypeOrScopeToken(false, TryTypoCorrection))
return true;
if (Tok.is(tok::identifier))
return false;
return ParseOptionalTypeSpecifier(DS, isInvalid, PrevSpec, DiagID,
- TemplateInfo, SuppressDeclarations);
+ TemplateInfo, SuppressDeclarations, TryTypoCorrection);
case tok::coloncolon: // ::foo::bar
if (NextToken().is(tok::kw_new) || // ::new
NextToken().is(tok::kw_delete)) // ::delete
@@ -2245,10 +2246,10 @@ bool Parser::ParseOptionalTypeSpecifier(DeclSpec &DS, bool& isInvalid,
// Annotate typenames and C++ scope specifiers. If we get one, just
// recurse to handle whatever we get.
- if (TryAnnotateTypeOrScopeToken())
+ if (TryAnnotateTypeOrScopeToken(false, TryTypoCorrection))
return true;
return ParseOptionalTypeSpecifier(DS, isInvalid, PrevSpec, DiagID,
- TemplateInfo, SuppressDeclarations);
+ TemplateInfo, SuppressDeclarations, TryTypoCorrection);
// simple-type-specifier:
case tok::annot_typename: {
Index: lib/Parse/ParseExprCXX.cpp
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index 1faeebc37cd4edd0390832d4e12990f2644a5259..233226a37effb4c35af182315c6eb961e429c915 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -1382,7 +1382,7 @@ void Parser::ParseCXXSimpleTypeSpecifier(DeclSpec &DS) {
/// type-specifier-seq: [C++ 8.1]
/// type-specifier type-specifier-seq[opt]
///
-bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
+bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS, bool TryTypoCorrection) {
DS.SetRangeStart(Tok.getLocation());
const char *PrevSpec = 0;
unsigned DiagID;
@@ -1390,7 +1390,7 @@ bool Parser::ParseCXXTypeSpecifierSeq(DeclSpec &DS) {
// Parse one or more of the type specifiers.
if (!ParseOptionalTypeSpecifier(DS, isInvalid, PrevSpec, DiagID,
- ParsedTemplateInfo(), /*SuppressDeclarations*/true)) {
+ ParsedTemplateInfo(), /*SuppressDeclarations*/true, TryTypoCorrection)) {
Diag(Tok, diag::err_expected_type);
return true;
}
@@ -2046,7 +2046,7 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
TypeIdParens.getBegin()));
} else {
MaybeParseGNUAttributes(DeclaratorInfo);
- if (ParseCXXTypeSpecifierSeq(DS))
+ if (ParseCXXTypeSpecifierSeq(DS, true))
DeclaratorInfo.setInvalidType(true);
else {
DeclaratorInfo.SetSourceRange(DS.getSourceRange());
@@ -2059,7 +2059,7 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) {
// A new-type-id is a simplified type-id, where essentially the
// direct-declarator is replaced by a direct-new-declarator.
MaybeParseGNUAttributes(DeclaratorInfo);
- if (ParseCXXTypeSpecifierSeq(DS))
+ if (ParseCXXTypeSpecifierSeq(DS, true))
DeclaratorInfo.setInvalidType(true);
else {
DeclaratorInfo.SetSourceRange(DS.getSourceRange());
Index: lib/Parse/Parser.cpp
diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp
index c31e1634a0a6243e920ab8cae39f634301fec5e8..38cbd80c615e476d492cd86ca4132188a530a71e 100644
--- a/lib/Parse/Parser.cpp
+++ b/lib/Parse/Parser.cpp
@@ -1204,7 +1204,8 @@ TemplateIdAnnotation *Parser::takeTemplateIdAnnotation(const Token &tok) {
///
/// Note that this routine emits an error if you call it with ::new or ::delete
/// as the current tokens, so only call it in contexts where these are invalid.
-bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
+bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext,
+ bool TryTypoCorrection) {
assert((Tok.is(tok::identifier) || Tok.is(tok::coloncolon)
|| Tok.is(tok::kw_typename) || Tok.is(tok::annot_cxxscope)) &&
"Cannot be a type or scope token!");
@@ -1278,13 +1279,19 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) {
return true;
if (Tok.is(tok::identifier)) {
+ IdentifierInfo *CorrectedII = NULL;
// Determine whether the identifier is a type name.
if (ParsedType Ty = Actions.getTypeName(*Tok.getIdentifierInfo(),
Tok.getLocation(), getCurScope(),
&SS, false,
NextToken().is(tok::period),
ParsedType(),
- /*NonTrivialTypeSourceInfo*/true)) {
+ /*NonTrivialTypeSourceInfo*/true,
+ TryTypoCorrection ? &CorrectedII
+ : NULL)) {
+ // A FixIt was applied as a result of typo correction
+ if (CorrectedII)
+ Tok.setIdentifierInfo(CorrectedII);
// This is a typename. Replace the current token in-place with an
// annotation type token.
Tok.setKind(tok::annot_typename);
Index: lib/Sema/SemaDecl.cpp
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 96531d41459bc1e78f4ac234297be0925a970329..5961ea1ea8cb86fa556e1cbcb542e924103e9299 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -70,7 +70,8 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
Scope *S, CXXScopeSpec *SS,
bool isClassName, bool HasTrailingDot,
ParsedType ObjectTypePtr,
- bool WantNontrivialTypeSourceInfo) {
+ bool WantNontrivialTypeSourceInfo,
+ IdentifierInfo **CorrectedII) {
// Determine where we will perform name lookup.
DeclContext *LookupCtx = 0;
if (ObjectTypePtr) {
@@ -145,6 +146,40 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc,
switch (Result.getResultKind()) {
case LookupResult::NotFound:
case LookupResult::NotFoundInCurrentInstantiation:
+ if (CorrectedII) {
+ TypoCorrection Corr = CorrectTypo(Result.getLookupNameInfo(),
+ Sema::LookupOrdinaryName,
+ S, SS, NULL, false,
+ Sema::CTC_Type);
+ IdentifierInfo *NewII = Corr.getCorrectionAsIdentifierInfo();
+ TemplateTy Template;
+ bool MemberOfUnknownSpecialization;
+ UnqualifiedId TemplateName;
+ TemplateName.setIdentifier(NewII, NameLoc);
+ if (Corr && (Corr.getCorrectionSpecifier() || NewII != &II) &&
+ // Ignore a correction to a template type as the to-be-corrected
+ // identifier is not a template (typo correction for template names is
+ // handled elsewhere).
+ !isTemplateName(S, *SS, false, TemplateName, ParsedType(), false,
+ Template, MemberOfUnknownSpecialization)) {
+ std::string CorrectedStr(Corr.getAsString(getLangOptions()));
+ std::string CorrectedQuotedStr(Corr.getQuoted(getLangOptions()));
+ Diag(NameLoc, diag::err_unknown_typename_suggest)
+ << Result.getLookupName() << CorrectedQuotedStr
+ << FixItHint::CreateReplacement(SourceRange(NameLoc), CorrectedStr);
+ if (NamedDecl *FirstDecl = Corr.getCorrectionDecl())
+ Diag(FirstDecl->getLocation(), diag::note_previous_decl)
+ << CorrectedQuotedStr;
+
+ if (NestedNameSpecifier *NNS = Corr.getCorrectionSpecifier())
+ SS->MakeTrivial(Context, NNS, SourceRange(NameLoc));
+ *CorrectedII = NewII;
+ return getTypeName(**CorrectedII, NameLoc, S, SS, isClassName,
+ HasTrailingDot, ObjectTypePtr,
+ WantNontrivialTypeSourceInfo);
+ }
+ }
+ // If typo correction failed or was not performed, fall through
case LookupResult::FoundOverloaded:
case LookupResult::FoundUnresolvedValue:
Result.suppressDiagnostics();
Index: test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
diff --git a/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
index 85e3e7ed08fe66f92fb10082b0c5ac2adabf55b2..76ceea1d4d5b89eea31bf90111e6d0c59369b5b1 100644
--- a/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
+++ b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp
@@ -1,8 +1,10 @@
// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++0x-extensions %s
-namespace fizbin { class Foobar; } // expected-note{{'fizbin::Foobar' declared here}}
+namespace fizbin { class Foobar {}; } // expected-note 2 {{'fizbin::Foobar' declared here}} \
+ // expected-note {{'Foobar' declared here}}
Foobar *my_bar // expected-error{{unknown type name 'Foobar'; did you mean 'fizbin::Foobar'?}}
- = new Foobar; // expected-error{{expected a type}}
+ = new Foobar; // expected-error{{unknown type name 'Foobar'; did you mean 'fizbin::Foobar'?}}
+fizbin::Foobar *my_foo = new fizbin::FooBar; // expected-error{{unknown type name 'FooBar'; did you mean 'Foobar'?}}
namespace barstool { int toFoobar() { return 1; } } // expected-note 3 {{'barstool::toFoobar' declared here}}
int Double(int x) { return x + x; }
@@ -62,11 +64,13 @@ void f() {
// Test case from http://llvm.org/bugs/show_bug.cgi?id=10318
namespace llvm {
- template <typename T> class GraphWriter {}; // expected-note{{'llvm::GraphWriter' declared here}}
+ template <typename T> class GraphWriter {}; // expected-note {{'llvm::GraphWriter' declared here}} \
+ // expected-note {{'GraphWriter' declared here}}
}
struct S {};
void bar() {
GraphWriter<S> x; //expected-error{{no template named 'GraphWriter'; did you mean 'llvm::GraphWriter'?}}
-
+ (void)new llvm::GraphWriter; // expected-error {{expected a type}}
+ (void)new llvm::Graphwriter<S>; // expected-error {{no template named 'Graphwriter' in namespace 'llvm'; did you mean 'GraphWriter'?}}
}
More information about the cfe-commits
mailing list