From fac9467d1676dc05761e12e41e13e01a3a3da52b Mon Sep 17 00:00:00 2001 From: Kaelyn Uhrain Date: Tue, 11 Oct 2011 01:02:41 +0000 Subject: [PATCH] Add typo correction for type names. The main motivation was to do typo correction in C++ "new" statements, though picking it up in other places where type names are expected was pretty much a freebie. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@141621 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Parse/Parser.h | 5 +- include/clang/Sema/Sema.h | 3 +- lib/Parse/ParseDecl.cpp | 6 ++- lib/Parse/Parser.cpp | 9 +++- lib/Sema/SemaDecl.cpp | 48 ++++++++++++++++++- ...g-namespace-qualifier-typo-corrections.cpp | 12 +++-- test/SemaCXX/typo-correction.cpp | 25 ++++++++++ 7 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 test/SemaCXX/typo-correction.cpp diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index a1f602c72a..e1e2402a2c 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -436,7 +436,10 @@ private: Tok.setAnnotationValue(ER.get()); } - bool TryAnnotateTypeOrScopeToken(bool EnteringContext = false); + // If NeedType is true, then TryAnnotateTypeOrScopeToken will try harder to + // find a type name by attempting typo correction. + bool TryAnnotateTypeOrScopeToken(bool EnteringContext = false, + bool NeedType = false); bool TryAnnotateCXXScopeToken(bool EnteringContext = false); /// TryAltiVecToken - Check for context-sensitive AltiVec identifier tokens, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index c171da820c..55b8f98567 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -873,7 +873,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, diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 82155e44fd..6de9e1b4eb 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -2303,7 +2303,8 @@ 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(/*EnteringContext=*/false, + /*NeedType=*/true)) return true; if (Tok.is(tok::identifier)) return false; @@ -2316,7 +2317,8 @@ 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(/*EnteringContext=*/false, + /*NeedType=*/true)) return true; return ParseOptionalTypeSpecifier(DS, isInvalid, PrevSpec, DiagID, TemplateInfo, SuppressDeclarations); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 131bd9d3c5..05a2b15abf 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1204,7 +1204,7 @@ 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 NeedType) { 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 +1278,18 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { return true; if (Tok.is(tok::identifier)) { + IdentifierInfo *CorrectedII = 0; // 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, + NeedType ? &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); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 4347c8c6b2..b510091ffa 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -71,7 +71,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) { @@ -146,6 +147,51 @@ ParsedType Sema::getTypeName(IdentifierInfo &II, SourceLocation NameLoc, switch (Result.getResultKind()) { case LookupResult::NotFound: case LookupResult::NotFoundInCurrentInstantiation: + if (CorrectedII) { + TypoCorrection Correction = CorrectTypo(Result.getLookupNameInfo(), + Kind, S, SS, 0, false, + Sema::CTC_Type); + IdentifierInfo *NewII = Correction.getCorrectionAsIdentifierInfo(); + TemplateTy Template; + bool MemberOfUnknownSpecialization; + UnqualifiedId TemplateName; + TemplateName.setIdentifier(NewII, NameLoc); + NestedNameSpecifier *NNS = Correction.getCorrectionSpecifier(); + CXXScopeSpec NewSS, *NewSSPtr = SS; + if (SS && NNS) { + NewSS.MakeTrivial(Context, NNS, SourceRange(NameLoc)); + NewSSPtr = &NewSS; + } + if (Correction && (NNS || 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). + !(getLangOptions().CPlusPlus && NewSSPtr && + isTemplateName(S, *NewSSPtr, false, TemplateName, ParsedType(), + false, Template, MemberOfUnknownSpecialization))) { + ParsedType Ty = getTypeName(*NewII, NameLoc, S, NewSSPtr, + isClassName, HasTrailingDot, ObjectTypePtr, + WantNontrivialTypeSourceInfo); + if (Ty) { + std::string CorrectedStr(Correction.getAsString(getLangOptions())); + std::string CorrectedQuotedStr( + Correction.getQuoted(getLangOptions())); + Diag(NameLoc, diag::err_unknown_typename_suggest) + << Result.getLookupName() << CorrectedQuotedStr + << FixItHint::CreateReplacement(SourceRange(NameLoc), + CorrectedStr); + if (NamedDecl *FirstDecl = Correction.getCorrectionDecl()) + Diag(FirstDecl->getLocation(), diag::note_previous_decl) + << CorrectedQuotedStr; + + if (SS && NNS) + SS->MakeTrivial(Context, NNS, SourceRange(NameLoc)); + *CorrectedII = NewII; + return Ty; + } + } + } + // If typo correction failed or was not performed, fall through case LookupResult::FoundOverloaded: case LookupResult::FoundUnresolvedValue: Result.suppressDiagnostics(); diff --git a/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp index 85e3e7ed08..76ceea1d4d 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 class GraphWriter {}; // expected-note{{'llvm::GraphWriter' declared here}} + template class GraphWriter {}; // expected-note {{'llvm::GraphWriter' declared here}} \ + // expected-note {{'GraphWriter' declared here}} } struct S {}; void bar() { GraphWriter 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; // expected-error {{no template named 'Graphwriter' in namespace 'llvm'; did you mean 'GraphWriter'?}} } diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp new file mode 100644 index 0000000000..adf317b8b9 --- /dev/null +++ b/test/SemaCXX/typo-correction.cpp @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++0x-extensions %s + +struct errc { + int v_; + operator int() const {return v_;} +}; + +class error_condition +{ + int _val_; +public: + error_condition() : _val_(0) {} + + error_condition(int _val) + : _val_(_val) {} + + template + error_condition(E _e) + {*this = make_error_condition(_e);} + +}; + +inline error_condition make_error_condition(errc _e) { + return error_condition(static_cast(_e)); +} -- 2.40.0