From fbb501f48819ca22911a5de22bd8f4d0a93e53d0 Mon Sep 17 00:00:00 2001 From: Nick Lewycky <nicholas@mxc.ca> Date: Tue, 16 Dec 2014 21:39:02 +0000 Subject: [PATCH] Add a new flag, -fspell-checking-limit=<number> to control how many times we'll do spell checking. Note that spell checking will change the produced AST, so we don't automatically change this value when someone sets -ferror-limit=. With this, merge test typo-correction-pt2.cpp into typo-correction.cpp. Remove Sema::UnqualifiedTyposCorrected, a cache of corrected typos. It would only cache typo corrections that didn't provide ValidateCandidate of which there were few left, and it had a bug when we had the same identifier spelled wrong twice. See the last two tests in typo-correction.cpp for cases this fires. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@224375 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticOptions.def | 3 +- include/clang/Basic/DiagnosticOptions.h | 3 +- include/clang/Driver/CC1Options.td | 2 + include/clang/Driver/Options.td | 1 + include/clang/Sema/Sema.h | 18 +- lib/Driver/Tools.cpp | 6 +- lib/Frontend/CompilerInvocation.cpp | 3 + lib/Sema/SemaLookup.cpp | 85 +---- test/CXX/class.access/class.friend/p11.cpp | 10 +- test/SemaCXX/typo-correction-pt2.cpp | 358 -------------------- test/SemaCXX/typo-correction.cpp | 363 ++++++++++++++++++++- 11 files changed, 389 insertions(+), 463 deletions(-) delete mode 100644 test/SemaCXX/typo-correction-pt2.cpp diff --git a/include/clang/Basic/DiagnosticOptions.def b/include/clang/Basic/DiagnosticOptions.def index a360a5a84e..fdd325446e 100644 --- a/include/clang/Basic/DiagnosticOptions.def +++ b/include/clang/Basic/DiagnosticOptions.def @@ -81,6 +81,8 @@ VALUE_DIAGOPT(MacroBacktraceLimit, 32, DefaultMacroBacktraceLimit) VALUE_DIAGOPT(TemplateBacktraceLimit, 32, DefaultTemplateBacktraceLimit) /// Limit depth of constexpr backtrace. VALUE_DIAGOPT(ConstexprBacktraceLimit, 32, DefaultConstexprBacktraceLimit) +/// Limit number of times to perform spell checking. +VALUE_DIAGOPT(SpellCheckingLimit, 32, DefaultSpellCheckingLimit) VALUE_DIAGOPT(TabStop, 32, DefaultTabStop) /// The distance between tab stops. /// Column limit for formatting message diagnostics, or 0 if unused. @@ -92,4 +94,3 @@ VALUE_DIAGOPT(MessageLength, 32, 0) #undef SEMANTIC_DIAGOPT #undef SEMANTIC_ENUM_DIAGOPT #undef SEMANTIC_VALUE_DIAGOPT - diff --git a/include/clang/Basic/DiagnosticOptions.h b/include/clang/Basic/DiagnosticOptions.h index 3e4d0eefbc..058e00f73e 100644 --- a/include/clang/Basic/DiagnosticOptions.h +++ b/include/clang/Basic/DiagnosticOptions.h @@ -33,7 +33,8 @@ public: enum { DefaultTabStop = 8, MaxTabStop = 100, DefaultMacroBacktraceLimit = 6, DefaultTemplateBacktraceLimit = 10, - DefaultConstexprBacktraceLimit = 10 }; + DefaultConstexprBacktraceLimit = 10, + DefaultSpellCheckingLimit = 50 }; // Define simple diagnostic options (with no accessors). #define DIAGOPT(Name, Bits, Default) unsigned Name : Bits; diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index d31672a592..e043001410 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -279,6 +279,8 @@ def ftemplate_backtrace_limit : Separate<["-"], "ftemplate-backtrace-limit">, Me HelpText<"Set the maximum number of entries to print in a template instantiation backtrace (0 = no limit).">; def fconstexpr_backtrace_limit : Separate<["-"], "fconstexpr-backtrace-limit">, MetaVarName<"<N>">, HelpText<"Set the maximum number of entries to print in a constexpr evaluation backtrace (0 = no limit).">; +def fspell_checking_limit : Separate<["-"], "fspell-checking-limit">, MetaVarName<"<N>">, + HelpText<"Set the maximum number of times to perform spell checking on unrecognized identifiers (0 = no limit).">; def fmessage_length : Separate<["-"], "fmessage-length">, MetaVarName<"<N>">, HelpText<"Format message diagnostics so that they fit within N columns or fewer, when possible.">; def verify : Flag<["-"], "verify">, diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index acf9acc2bd..766c964284 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -862,6 +862,7 @@ def fshow_overloads_EQ : Joined<["-"], "fshow-overloads=">, Group<f_Group>, Flag def fshow_column : Flag<["-"], "fshow-column">, Group<f_Group>, Flags<[CC1Option]>; def fshow_source_location : Flag<["-"], "fshow-source-location">, Group<f_Group>; def fspell_checking : Flag<["-"], "fspell-checking">, Group<f_Group>; +def fspell_checking_limit_EQ : Joined<["-"], "fspell-checking-limit=">, Group<f_Group>; def fsigned_bitfields : Flag<["-"], "fsigned-bitfields">, Group<f_Group>; def fsigned_char : Flag<["-"], "fsigned-char">, Group<f_Group>; def fno_signed_char : Flag<["-"], "fno-signed-char">, Flags<[CC1Option]>, diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 17154b636d..8786b6ae78 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -2630,7 +2630,7 @@ private: std::unique_ptr<CorrectionCandidateCallback> CCC, DeclContext *MemberContext, bool EnteringContext, const ObjCObjectPointerType *OPT, - bool ErrorRecovery, bool &IsUnqualifiedLookup); + bool ErrorRecovery); public: const TypoExprState &getTypoExprState(TypoExpr *TE) const; @@ -2985,10 +2985,7 @@ private: /// \brief Record the typo correction failure and return an empty correction. TypoCorrection FailedCorrection(IdentifierInfo *Typo, SourceLocation TypoLoc, - bool RecordFailure = true, - bool IsUnqualifiedLookup = false) { - if (IsUnqualifiedLookup) - (void)UnqualifiedTyposCorrected[Typo]; + bool RecordFailure = true) { if (RecordFailure) TypoCorrectionFailures[Typo].insert(TypoLoc); return TypoCorrection(); @@ -6688,17 +6685,6 @@ public: /// \brief The number of typos corrected by CorrectTypo. unsigned TyposCorrected; - typedef llvm::DenseMap<IdentifierInfo *, TypoCorrection> - UnqualifiedTyposCorrectedMap; - - /// \brief A cache containing the results of typo correction for unqualified - /// name lookup. - /// - /// The string is the string that we corrected to (which may be empty, if - /// there was no correction), while the boolean will be true when the - /// string represents a keyword. - UnqualifiedTyposCorrectedMap UnqualifiedTyposCorrected; - typedef llvm::SmallSet<SourceLocation, 2> SrcLocSet; typedef llvm::DenseMap<IdentifierInfo *, SrcLocSet> IdentifierSourceLocations; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 940be7e22c..111a383480 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -3546,6 +3546,11 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(A->getValue()); } + if (Arg *A = Args.getLastArg(options::OPT_fspell_checking_limit_EQ)) { + CmdArgs.push_back("-fspell-checking-limit"); + CmdArgs.push_back(A->getValue()); + } + // Pass -fmessage-length=. CmdArgs.push_back("-fmessage-length"); if (Arg *A = Args.getLastArg(options::OPT_fmessage_length_EQ)) { @@ -8306,4 +8311,3 @@ void CrossWindows::Link::ConstructJob(Compilation &C, const JobAction &JA, C.addCommand(llvm::make_unique<Command>(JA, *this, Exec, CmdArgs)); } - diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 610bfffc7e..97be3d3fbf 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -707,6 +707,9 @@ bool clang::ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.ConstexprBacktraceLimit = getLastArgIntValue( Args, OPT_fconstexpr_backtrace_limit, DiagnosticOptions::DefaultConstexprBacktraceLimit, Diags); + Opts.SpellCheckingLimit = getLastArgIntValue( + Args, OPT_fspell_checking_limit, + DiagnosticOptions::DefaultSpellCheckingLimit, Diags); Opts.TabStop = getLastArgIntValue(Args, OPT_ftabstop, DiagnosticOptions::DefaultTabStop, Diags); if (Opts.TabStop == 0 || Opts.TabStop > DiagnosticOptions::MaxTabStop) { diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index e1a9a554b3..3445264461 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -4023,8 +4023,7 @@ std::unique_ptr<TypoCorrectionConsumer> Sema::makeTypoCorrectionConsumer( Scope *S, CXXScopeSpec *SS, std::unique_ptr<CorrectionCandidateCallback> CCC, DeclContext *MemberContext, bool EnteringContext, - const ObjCObjectPointerType *OPT, bool ErrorRecovery, - bool &IsUnqualifiedLookup) { + const ObjCObjectPointerType *OPT, bool ErrorRecovery) { if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking || DisableTypoCorrection) @@ -4068,6 +4067,14 @@ std::unique_ptr<TypoCorrectionConsumer> Sema::makeTypoCorrectionConsumer( if (getLangOpts().AltiVec && Typo->isStr("vector")) return nullptr; + // Provide a stop gap for files that are just seriously broken. Trying + // to correct all typos can turn into a HUGE performance penalty, causing + // some files to take minutes to get rejected by the parser. + unsigned Limit = getDiagnostics().getDiagnosticOptions().SpellCheckingLimit; + if (Limit && TyposCorrected >= Limit) + return nullptr; + ++TyposCorrected; + // If we're handling a missing symbol error, using modules, and the // special search all modules option is used, look for a missing import. if (ErrorRecovery && getLangOpts().Modules && @@ -4082,13 +4089,8 @@ std::unique_ptr<TypoCorrectionConsumer> Sema::makeTypoCorrectionConsumer( *this, TypoName, LookupKind, S, SS, std::move(CCC), MemberContext, EnteringContext); - // If a callback object considers an empty typo correction candidate to be - // viable, assume it does not do any actual validation of the candidates. - TypoCorrection EmptyCorrection; - bool ValidatingCallback = !isCandidateViable(CCCRef, EmptyCorrection); - // Perform name lookup to find visible, similarly-named entities. - IsUnqualifiedLookup = false; + bool IsUnqualifiedLookup = false; DeclContext *QualifiedDC = MemberContext; if (MemberContext) { LookupVisibleDecls(MemberContext, LookupKind, *Consumer); @@ -4103,46 +4105,9 @@ std::unique_ptr<TypoCorrectionConsumer> Sema::makeTypoCorrectionConsumer( if (!QualifiedDC) return nullptr; - // Provide a stop gap for files that are just seriously broken. Trying - // to correct all typos can turn into a HUGE performance penalty, causing - // some files to take minutes to get rejected by the parser. - if (TyposCorrected + UnqualifiedTyposCorrected.size() >= 20) - return nullptr; - ++TyposCorrected; - LookupVisibleDecls(QualifiedDC, LookupKind, *Consumer); } else { IsUnqualifiedLookup = true; - UnqualifiedTyposCorrectedMap::iterator Cached - = UnqualifiedTyposCorrected.find(Typo); - if (Cached != UnqualifiedTyposCorrected.end()) { - // Add the cached value, unless it's a keyword or fails validation. In the - // keyword case, we'll end up adding the keyword below. - if (Cached->second) { - if (!Cached->second.isKeyword() && - isCandidateViable(CCCRef, Cached->second)) { - // Do not use correction that is unaccessible in the given scope. - NamedDecl *CorrectionDecl = Cached->second.getCorrectionDecl(); - DeclarationNameInfo NameInfo(CorrectionDecl->getDeclName(), - CorrectionDecl->getLocation()); - LookupResult R(*this, NameInfo, LookupOrdinaryName); - if (LookupName(R, S)) - Consumer->addCorrection(Cached->second); - } - } else { - // Only honor no-correction cache hits when a callback that will validate - // correction candidates is not being used. - if (!ValidatingCallback) - return nullptr; - } - } - if (Cached == UnqualifiedTyposCorrected.end()) { - // Provide a stop gap for files that are just seriously broken. Trying - // to correct all typos can turn into a HUGE performance penalty, causing - // some files to take minutes to get rejected by the parser. - if (TyposCorrected + UnqualifiedTyposCorrected.size() >= 20) - return nullptr; - } } // Determine whether we are going to search in the various namespaces for @@ -4249,30 +4214,24 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // for CTC_Unknown but not for CTC_ObjCMessageReceiver. bool ObjCMessageReceiver = CCC->WantObjCSuper && !CCC->WantRemainingKeywords; - TypoCorrection EmptyCorrection; - bool ValidatingCallback = !isCandidateViable(*CCC, EmptyCorrection); - IdentifierInfo *Typo = TypoName.getName().getAsIdentifierInfo(); - bool IsUnqualifiedLookup = false; auto Consumer = makeTypoCorrectionConsumer( TypoName, LookupKind, S, SS, std::move(CCC), MemberContext, - EnteringContext, OPT, Mode == CTK_ErrorRecovery, IsUnqualifiedLookup); + EnteringContext, OPT, Mode == CTK_ErrorRecovery); if (!Consumer) return TypoCorrection(); // If we haven't found anything, we're done. if (Consumer->empty()) - return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure, - IsUnqualifiedLookup); + return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure); // Make sure the best edit distance (prior to adding any namespace qualifiers) // is not more that about a third of the length of the typo's identifier. unsigned ED = Consumer->getBestEditDistance(true); unsigned TypoLen = Typo->getName().size(); if (ED > 0 && TypoLen / ED < 3) - return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure, - IsUnqualifiedLookup); + return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure); TypoCorrection BestTC = Consumer->getNextCorrection(); TypoCorrection SecondBestTC = Consumer->getNextCorrection(); @@ -4285,8 +4244,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // If this was an unqualified lookup and we believe the callback // object wouldn't have filtered out possible corrections, note // that no correction was found. - return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure, - IsUnqualifiedLookup && !ValidatingCallback); + return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure); } // If only a single name remains, return that result. @@ -4299,10 +4257,6 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, if (ED == 0 && Result.isKeyword()) return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure); - // Record the correction for unqualified lookup. - if (IsUnqualifiedLookup) - UnqualifiedTyposCorrected[Typo] = Result; - TypoCorrection TC = Result; TC.setCorrectionRange(SS, TypoName); checkCorrectionVisibility(*this, TC); @@ -4323,10 +4277,6 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, BestTC.getCorrection().getAsString() != "super") return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure); - // Record the correction for unqualified lookup. - if (IsUnqualifiedLookup) - UnqualifiedTyposCorrected[Typo] = BestTC; - BestTC.setCorrectionRange(SS, TypoName); return BestTC; } @@ -4334,8 +4284,7 @@ TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, // Record the failure's location if needed and return an empty correction. If // this was an unqualified lookup and we believe the callback object did not // filter out possible corrections, also cache the failure for the typo. - return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure, - IsUnqualifiedLookup && !ValidatingCallback); + return FailedCorrection(Typo, TypoName.getLoc(), RecordFailure); } /// \brief Try to "correct" a typo in the source code by finding @@ -4386,13 +4335,11 @@ TypoExpr *Sema::CorrectTypoDelayed( assert(CCC && "CorrectTypoDelayed requires a CorrectionCandidateCallback"); TypoCorrection Empty; - bool IsUnqualifiedLookup = false; auto Consumer = makeTypoCorrectionConsumer( TypoName, LookupKind, S, SS, std::move(CCC), MemberContext, EnteringContext, OPT, /*SearchModules=*/(Mode == CTK_ErrorRecovery) && getLangOpts().Modules && - getLangOpts().ModulesSearchAll, - IsUnqualifiedLookup); + getLangOpts().ModulesSearchAll); if (!Consumer || Consumer->empty()) return nullptr; diff --git a/test/CXX/class.access/class.friend/p11.cpp b/test/CXX/class.access/class.friend/p11.cpp index ba44a0d492..a5107fd207 100644 --- a/test/CXX/class.access/class.friend/p11.cpp +++ b/test/CXX/class.access/class.friend/p11.cpp @@ -24,6 +24,8 @@ namespace test2 { void foo() { // expected-note {{'::test2::foo' declared here}} struct S1 { friend void foo(); // expected-error {{no matching function 'foo' found in local scope; did you mean '::test2::foo'?}} + // expected-note@-1{{'::test2::foo' declared here}} + // TODO: the above note should go on line 24 }; void foo(); // expected-note {{local declaration nearly matches}} @@ -40,17 +42,19 @@ namespace test2 { { int foo; struct S3 { - friend void foo(); // expected-error {{no matching function found in local scope}} + friend void foo(); // expected-error {{no matching function 'foo' found in local scope; did you mean '::test2::foo'?}} }; } struct S4 { friend void bar(); // expected-error {{no matching function 'bar' found in local scope; did you mean '::test2::bar'?}} + // expected-note@-1 2 {{'::test2::bar' declared here}} + // TODO: the above two notes should go on line 22 }; { void bar(); } struct S5 { - friend void bar(); // expected-error {{no matching function found in local scope}} + friend void bar(); // expected-error {{no matching function 'bar' found in local scope; did you mean '::test2::bar'?}} }; { @@ -85,7 +89,7 @@ namespace test2 { void quux() {} void foo() { struct Inner1 { - friend void bar(); // expected-error {{no matching function found in local scope}} + friend void bar(); // expected-error {{no matching function 'bar' found in local scope; did you mean '::test2::bar'?}} friend void quux(); // expected-error {{no matching function found in local scope}} }; diff --git a/test/SemaCXX/typo-correction-pt2.cpp b/test/SemaCXX/typo-correction-pt2.cpp deleted file mode 100644 index 73c8cb5a55..0000000000 --- a/test/SemaCXX/typo-correction-pt2.cpp +++ /dev/null @@ -1,358 +0,0 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++11-extensions %s -// -// FIXME: This file is overflow from test/SemaCXX/typo-correction.cpp due to a -// hard-coded limit of 20 different typo corrections Sema::CorrectTypo will -// attempt within a single file (which is to avoid having very broken files take -// minutes to finally be rejected by the parser). - -namespace PR12951 { -// If there are two corrections that have the same identifier and edit distance -// and only differ by their namespaces, don't suggest either as a correction -// since both are equally likely corrections. -namespace foobar { struct Thing {}; } -namespace bazquux { struct Thing {}; } -void f() { Thing t; } // expected-error{{unknown type name 'Thing'}} -} - -namespace bogus_keyword_suggestion { -void test() { - status = "OK"; // expected-error-re {{use of undeclared identifier 'status'{{$}}}} - return status; // expected-error-re {{use of undeclared identifier 'status'{{$}}}} - } -} - -namespace PR13387 { -struct A { - void CreateFoo(float, float); - void CreateBar(float, float); -}; -struct B : A { - using A::CreateFoo; - void CreateFoo(int, int); // expected-note {{'CreateFoo' declared here}} -}; -void f(B &x) { - x.Createfoo(0,0); // expected-error {{no member named 'Createfoo' in 'PR13387::B'; did you mean 'CreateFoo'?}} -} -} - -struct DataStruct {void foo();}; -struct T { - DataStruct data_struct; - void f(); -}; -// should be void T::f(); -void f() { - data_struct->foo(); // expected-error-re{{use of undeclared identifier 'data_struct'{{$}}}} -} - -namespace PR12287 { -class zif { - void nab(int); -}; -void nab(); // expected-note{{'::PR12287::nab' declared here}} -void zif::nab(int) { - nab(); // expected-error{{too few arguments to function call, expected 1, have 0; did you mean '::PR12287::nab'?}} -} -} - -namespace TemplateFunction { -template <class T> -void A(T) { } // expected-note {{'::TemplateFunction::A' declared here}} - -template <class T> -void B(T) { } // expected-note {{'::TemplateFunction::B' declared here}} - -class Foo { - public: - void A(int, int) {} - void B() {} -}; - -void test(Foo F, int num) { - F.A(num); // expected-error {{too few arguments to function call, expected 2, have 1; did you mean '::TemplateFunction::A'?}} - F.B(num); // expected-error {{too many arguments to function call, expected 0, have 1; did you mean '::TemplateFunction::B'?}} -} -} -namespace using_suggestion_val_dropped_specifier { -void FFF() {} // expected-note {{'::using_suggestion_val_dropped_specifier::FFF' declared here}} -namespace N { } -using N::FFF; // expected-error {{no member named 'FFF' in namespace 'using_suggestion_val_dropped_specifier::N'; did you mean '::using_suggestion_val_dropped_specifier::FFF'?}} -} - -namespace class_member_typo_corrections { -class Outer { -public: - class Inner {}; // expected-note {{'Outer::Inner' declared here}} - Inner MyMethod(Inner arg); -}; - -Inner Outer::MyMethod(Inner arg) { // expected-error {{unknown type name 'Inner'; did you mean 'Outer::Inner'?}} - return Inner(); -} - -class Result { -public: - enum ResultType { - ENTITY, // expected-note {{'Result::ENTITY' declared here}} - PREDICATE, // expected-note {{'Result::PREDICATE' declared here}} - LITERAL // expected-note {{'Result::LITERAL' declared here}} - }; - - ResultType type(); -}; - -void test() { - Result result_cell; - switch (result_cell.type()) { - case ENTITY: // expected-error {{use of undeclared identifier 'ENTITY'; did you mean 'Result::ENTITY'?}} - case LITERAL: // expected-error {{use of undeclared identifier 'LITERAL'; did you mean 'Result::LITERAL'?}} - case PREDICAT: // expected-error {{use of undeclared identifier 'PREDICAT'; did you mean 'Result::PREDICATE'?}} - break; - } -} - -class Figure { - enum ResultType { - SQUARE, - TRIANGLE, - CIRCLE - }; - -public: - ResultType type(); -}; - -void testAccess() { - Figure obj; - switch (obj.type()) { // expected-warning {{enumeration values 'SQUARE', 'TRIANGLE', and 'CIRCLE' not handled in switch}} - case SQUARE: // expected-error-re {{use of undeclared identifier 'SQUARE'{{$}}}} - case TRIANGLE: // expected-error-re {{use of undeclared identifier 'TRIANGLE'{{$}}}} - case CIRCE: // expected-error-re {{use of undeclared identifier 'CIRCE'{{$}}}} - break; - } -} -} - -long readline(const char *, char *, unsigned long); -void assign_to_unknown_var() { - deadline_ = 1; // expected-error-re {{use of undeclared identifier 'deadline_'{{$}}}} -} - -namespace no_ns_before_dot { -namespace re2 {} -void test() { - req.set_check(false); // expected-error-re {{use of undeclared identifier 'req'{{$}}}} -} -} - -namespace PR17394 { - class A { - protected: - long zzzzzzzzzz; - }; - class B : private A {}; - B zzzzzzzzzy<>; // expected-error {{expected ';' after top level declarator}}{} -} - -namespace correct_fields_in_member_funcs { -struct S { - int my_member; // expected-note {{'my_member' declared here}} - void f() { my_menber = 1; } // expected-error {{use of undeclared identifier 'my_menber'; did you mean 'my_member'?}} -}; -} - -namespace PR17019 { - template<class F> - struct evil { - evil(F de) { // expected-note {{'de' declared here}} - de_; // expected-error {{use of undeclared identifier 'de_'; did you mean 'de'?}} \ - // expected-warning {{expression result unused}} - } - ~evil() { - de_->bar() // expected-error {{use of undeclared identifier 'de_'}} - } - }; - - void meow() { - evil<int> Q(0); // expected-note {{in instantiation of member function}} - } -} - -namespace fix_class_name_qualifier { -class MessageHeaders {}; -class MessageUtils { - public: - static void ParseMessageHeaders(int, int); // expected-note {{'MessageUtils::ParseMessageHeaders' declared here}} -}; - -void test() { - // No, we didn't mean to call MessageHeaders::MessageHeaders. - MessageHeaders::ParseMessageHeaders(5, 4); // expected-error {{no member named 'ParseMessageHeaders' in 'fix_class_name_qualifier::MessageHeaders'; did you mean 'MessageUtils::ParseMessageHeaders'?}} -} -} - -namespace PR18213 { // expected-note {{'PR18213' declared here}} -struct WrapperInfo { - int i; -}; - -template <typename T> struct Wrappable { - static WrapperInfo kWrapperInfo; -}; - -// Note the space before "::PR18213" is intended and needed, as it highlights -// the actual typo, which is the leading "::". -// TODO: Suggest removing the "::" from "::PR18213" (the right correction) -// instead of incorrectly suggesting dropping "PR18213::WrapperInfo::". -template <> -PR18213::WrapperInfo ::PR18213::Wrappable<int>::kWrapperInfo = { 0 }; // expected-error {{no member named 'PR18213' in 'PR18213::WrapperInfo'; did you mean simply 'PR18213'?}} \ - // expected-error {{C++ requires a type specifier for all declarations}} -} - -namespace PR18651 { -struct { - int x; -} a, b; - -int y = x; // expected-error-re {{use of undeclared identifier 'x'{{$}}}} -} - -namespace PR18685 { -template <class C, int I, int J> -class SetVector { - public: - SetVector() {} -}; - -template <class C, int I> -class SmallSetVector : public SetVector<C, I, 8> {}; - -class foo {}; -SmallSetVector<foo*, 2> fooSet; -} - -PR18685::BitVector Map; // expected-error-re {{no type named 'BitVector' in namespace 'PR18685'{{$}}}} - -namespace shadowed_template { -template <typename T> class Fizbin {}; // expected-note {{'::shadowed_template::Fizbin' declared here}} -class Baz { - int Fizbin(); - // TODO: Teach the parser to recover from the typo correction instead of - // continuing to treat the template name as an implicit-int declaration. - Fizbin<int> qux; // expected-error {{unknown type name 'Fizbin'; did you mean '::shadowed_template::Fizbin'?}} \ - // expected-error {{expected member name or ';' after declaration specifiers}} -}; -} - -namespace PR18852 { -void func() { - struct foo { - void bar() {} - }; - bar(); // expected-error-re {{use of undeclared identifier 'bar'{{$}}}} -} - -class Thread { - public: - void Start(); - static void Stop(); // expected-note {{'Thread::Stop' declared here}} -}; - -class Manager { - public: - void Start(int); // expected-note {{'Start' declared here}} - void Stop(int); // expected-note {{'Stop' declared here}} -}; - -void test(Manager *m) { - // Don't suggest Thread::Start as a correction just because it has the same - // (unqualified) name and accepts the right number of args; this is a method - // call on an object in an unrelated class. - m->Start(); // expected-error-re {{too few arguments to function call, expected 1, have 0{{$}}}} - m->Stop(); // expected-error-re {{too few arguments to function call, expected 1, have 0{{$}}}} - Stop(); // expected-error {{use of undeclared identifier 'Stop'; did you mean 'Thread::Stop'?}} -} - -} - -namespace std { -class bernoulli_distribution { - public: - double p() const; -}; -} -void test() { - // Make sure that typo correction doesn't suggest changing 'p' to - // 'std::bernoulli_distribution::p' as that is most likely wrong. - if (p) // expected-error-re {{use of undeclared identifier 'p'{{$}}}} - return; -} - -namespace PR19681 { - struct TypoA {}; - struct TypoB { - void test(); - private: - template<typename T> void private_memfn(T); // expected-note{{declared here}} - }; - void TypoB::test() { - // FIXME: should suggest 'PR19681::TypoB::private_memfn' instead of '::PR19681::TypoB::private_memfn' - (void)static_cast<void(TypoB::*)(int)>(&TypoA::private_memfn); // expected-error{{no member named 'private_memfn' in 'PR19681::TypoA'; did you mean '::PR19681::TypoB::private_memfn'?}} - } -} - -namespace testWantFunctionLikeCasts { - long test(bool a) { - if (a) - return struc(5.7); // expected-error-re {{use of undeclared identifier 'struc'{{$}}}} - else - return lon(8.0); // expected-error {{use of undeclared identifier 'lon'; did you mean 'long'?}} - } -} - -namespace testCXXDeclarationSpecifierParsing { -namespace test { - struct SomeSettings {}; // expected-note {{'test::SomeSettings' declared here}} -} -class Test {}; -int bar() { - Test::SomeSettings some_settings; // expected-error {{no type named 'SomeSettings' in 'testCXXDeclarationSpecifierParsing::Test'; did you mean 'test::SomeSettings'?}} -} -} - -namespace testNonStaticMemberHandling { -struct Foo { - bool usesMetadata; // expected-note {{'usesMetadata' declared here}} -}; -int test(Foo f) { - if (UsesMetadata) // expected-error-re {{use of undeclared identifier 'UsesMetadata'{{$}}}} - return 5; - if (f.UsesMetadata) // expected-error {{no member named 'UsesMetadata' in 'testNonStaticMemberHandling::Foo'; did you mean 'usesMetadata'?}} - return 11; - return 0; -} -}; - -namespace testMemberExprDeclarationNameInfo { - // The AST should only have the corrected name with no mention of 'data_'. - // FIXME: the second typo is being printed out with the location of the first - // because the TypoCorrection objects contain the SourceRange but the - // UnqualifiedTyposCorrected cache is keyed on IdentifierInfo. - void f(int); - struct S { - int data; // expected-note 2{{'data' declared here}} - void m_fn1() { - data_[] = // expected-error 2{{use of undeclared identifier 'data_'}} expected-error {{expected expression}} - f(data_); - } - }; -} - -namespace testArraySubscriptIndex { - struct S { - int foodata; // expected-note {{'foodata' declared here}} - void m_fn1() { - (+)[foodata_]; // expected-error{{expected expression}} expected-error {{use of undeclared identifier 'foodata_'; did you mean 'foodata'}} - } - }; -} diff --git a/test/SemaCXX/typo-correction.cpp b/test/SemaCXX/typo-correction.cpp index 3b31b8db79..324900150c 100644 --- a/test/SemaCXX/typo-correction.cpp +++ b/test/SemaCXX/typo-correction.cpp @@ -1,8 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++11-extensions %s -// -// WARNING: Do not add more typo correction test cases to this file lest you run -// afoul the hard-coded limit (escape hatch) of 20 different typos whose -// correction was attempted by Sema::CorrectTypo +// RUN: %clang_cc1 -fspell-checking-limit 0 -verify -Wno-c++11-extensions %s namespace PR21817{ int a(-rsing[2]); // expected-error {{undeclared identifier 'rsing'; did you mean 'using'?}} @@ -102,7 +98,7 @@ unknown_type_test::stream_out out; // expected-error{{no type named 'stream_out' // Demonstrate a case where using only the cached value returns the wrong thing // when the cached value was the result of a previous callback object that only // accepts a subset of the current callback object. -namespace { +namespace cache_invalidation_test { using namespace unknown_type_test; void bar(long i); void before_caching_classname() { @@ -289,13 +285,352 @@ namespace b6956809_test2 { } } -// This test should have one correction, followed by an error without a -// suggestion due to exceeding the maximum number of typos for which correction -// is attempted. -namespace CorrectTypo_has_reached_its_limit { -int flibberdy(); // expected-note{{'flibberdy' declared here}} -int no_correction() { - return hibberdy() + // expected-error{{use of undeclared identifier 'hibberdy'; did you mean 'flibberdy'?}} - gibberdy(); // expected-error-re{{use of undeclared identifier 'gibberdy'{{$}}}} +namespace PR12951 { +// If there are two corrections that have the same identifier and edit distance +// and only differ by their namespaces, don't suggest either as a correction +// since both are equally likely corrections. +namespace foobar { struct Thing {}; } +namespace bazquux { struct Thing {}; } +void f() { Thing t; } // expected-error{{unknown type name 'Thing'}} +} + +namespace bogus_keyword_suggestion { +void test() { + status = "OK"; // expected-error-re {{use of undeclared identifier 'status'{{$}}}} + return status; // expected-error-re {{use of undeclared identifier 'status'{{$}}}} + } +} + +namespace PR13387 { +struct A { + void CreateFoo(float, float); + void CreateBar(float, float); +}; +struct B : A { + using A::CreateFoo; + void CreateFoo(int, int); // expected-note {{'CreateFoo' declared here}} +}; +void f(B &x) { + x.Createfoo(0,0); // expected-error {{no member named 'Createfoo' in 'PR13387::B'; did you mean 'CreateFoo'?}} +} +} + +struct DataStruct {void foo();}; +struct T { + DataStruct data_struct; + void f(); +}; +// should be void T::f(); +void f() { + data_struct->foo(); // expected-error-re{{use of undeclared identifier 'data_struct'{{$}}}} +} + +namespace PR12287 { +class zif { + void nab(int); +}; +void nab(); // expected-note{{'::PR12287::nab' declared here}} +void zif::nab(int) { + nab(); // expected-error{{too few arguments to function call, expected 1, have 0; did you mean '::PR12287::nab'?}} +} +} + +namespace TemplateFunction { +template <class T> +void A(T) { } // expected-note {{'::TemplateFunction::A' declared here}} + +template <class T> +void B(T) { } // expected-note {{'::TemplateFunction::B' declared here}} + +class Foo { + public: + void A(int, int) {} + void B() {} +}; + +void test(Foo F, int num) { + F.A(num); // expected-error {{too few arguments to function call, expected 2, have 1; did you mean '::TemplateFunction::A'?}} + F.B(num); // expected-error {{too many arguments to function call, expected 0, have 1; did you mean '::TemplateFunction::B'?}} +} +} +namespace using_suggestion_val_dropped_specifier { +void FFF() {} // expected-note {{'::using_suggestion_val_dropped_specifier::FFF' declared here}} +namespace N { } +using N::FFF; // expected-error {{no member named 'FFF' in namespace 'using_suggestion_val_dropped_specifier::N'; did you mean '::using_suggestion_val_dropped_specifier::FFF'?}} +} + +namespace class_member_typo_corrections { +class Outer { +public: + class Inner {}; // expected-note {{'Outer::Inner' declared here}} + Inner MyMethod(Inner arg); +}; + +Inner Outer::MyMethod(Inner arg) { // expected-error {{unknown type name 'Inner'; did you mean 'Outer::Inner'?}} + return Inner(); +} + +class Result { +public: + enum ResultType { + ENTITY, // expected-note {{'Result::ENTITY' declared here}} + PREDICATE, // expected-note {{'Result::PREDICATE' declared here}} + LITERAL // expected-note {{'Result::LITERAL' declared here}} + }; + + ResultType type(); +}; + +void test() { + Result result_cell; + switch (result_cell.type()) { + case ENTITY: // expected-error {{use of undeclared identifier 'ENTITY'; did you mean 'Result::ENTITY'?}} + case LITERAL: // expected-error {{use of undeclared identifier 'LITERAL'; did you mean 'Result::LITERAL'?}} + case PREDICAT: // expected-error {{use of undeclared identifier 'PREDICAT'; did you mean 'Result::PREDICATE'?}} + break; + } +} + +class Figure { + enum ResultType { + SQUARE, + TRIANGLE, + CIRCLE + }; + +public: + ResultType type(); +}; + +void testAccess() { + Figure obj; + switch (obj.type()) { // expected-warning {{enumeration values 'SQUARE', 'TRIANGLE', and 'CIRCLE' not handled in switch}} + case SQUARE: // expected-error-re {{use of undeclared identifier 'SQUARE'{{$}}}} + case TRIANGLE: // expected-error-re {{use of undeclared identifier 'TRIANGLE'{{$}}}} + case CIRCE: // expected-error-re {{use of undeclared identifier 'CIRCE'{{$}}}} + break; + } +} +} + +long readline(const char *, char *, unsigned long); +void assign_to_unknown_var() { + deadline_ = 1; // expected-error-re {{use of undeclared identifier 'deadline_'{{$}}}} +} + +namespace no_ns_before_dot { +namespace re2 {} +void test() { + req.set_check(false); // expected-error-re {{use of undeclared identifier 'req'{{$}}}} +} +} + +namespace PR17394 { + class A { + protected: + long zzzzzzzzzz; + }; + class B : private A {}; + B zzzzzzzzzy<>; // expected-error {{expected ';' after top level declarator}}{} +} + +namespace correct_fields_in_member_funcs { +struct S { + int my_member; // expected-note {{'my_member' declared here}} + void f() { my_menber = 1; } // expected-error {{use of undeclared identifier 'my_menber'; did you mean 'my_member'?}} +}; +} + +namespace PR17019 { + template<class F> + struct evil { + evil(F de) { // expected-note {{'de' declared here}} + de_; // expected-error {{use of undeclared identifier 'de_'; did you mean 'de'?}} \ + // expected-warning {{expression result unused}} + } + ~evil() { + de_->bar() // expected-error {{use of undeclared identifier 'de_'}} + } + }; + + void meow() { + evil<int> Q(0); // expected-note {{in instantiation of member function}} + } +} + +namespace fix_class_name_qualifier { +class MessageHeaders {}; +class MessageUtils { + public: + static void ParseMessageHeaders(int, int); // expected-note {{'MessageUtils::ParseMessageHeaders' declared here}} +}; + +void test() { + // No, we didn't mean to call MessageHeaders::MessageHeaders. + MessageHeaders::ParseMessageHeaders(5, 4); // expected-error {{no member named 'ParseMessageHeaders' in 'fix_class_name_qualifier::MessageHeaders'; did you mean 'MessageUtils::ParseMessageHeaders'?}} +} +} + +namespace PR18213 { // expected-note {{'PR18213' declared here}} +struct WrapperInfo { + int i; +}; + +template <typename T> struct Wrappable { + static WrapperInfo kWrapperInfo; +}; + +// Note the space before "::PR18213" is intended and needed, as it highlights +// the actual typo, which is the leading "::". +// TODO: Suggest removing the "::" from "::PR18213" (the right correction) +// instead of incorrectly suggesting dropping "PR18213::WrapperInfo::". +template <> +PR18213::WrapperInfo ::PR18213::Wrappable<int>::kWrapperInfo = { 0 }; // expected-error {{no member named 'PR18213' in 'PR18213::WrapperInfo'; did you mean simply 'PR18213'?}} \ + // expected-error {{C++ requires a type specifier for all declarations}} +} + +namespace PR18651 { +struct { + int x; +} a, b; + +int y = x; // expected-error-re {{use of undeclared identifier 'x'{{$}}}} +} + +namespace PR18685 { +template <class C, int I, int J> +class SetVector { + public: + SetVector() {} +}; + +template <class C, int I> +class SmallSetVector : public SetVector<C, I, 8> {}; + +class foo {}; +SmallSetVector<foo*, 2> fooSet; +} + +PR18685::BitVector Map; // expected-error-re {{no type named 'BitVector' in namespace 'PR18685'{{$}}}} + +namespace shadowed_template { +template <typename T> class Fizbin {}; // expected-note {{'::shadowed_template::Fizbin' declared here}} +class Baz { + int Fizbin(); + // TODO: Teach the parser to recover from the typo correction instead of + // continuing to treat the template name as an implicit-int declaration. + Fizbin<int> qux; // expected-error {{unknown type name 'Fizbin'; did you mean '::shadowed_template::Fizbin'?}} \ + // expected-error {{expected member name or ';' after declaration specifiers}} +}; +} + +namespace PR18852 { +void func() { + struct foo { + void bar() {} + }; + bar(); // expected-error-re {{use of undeclared identifier 'bar'{{$}}}} +} + +class Thread { + public: + void Start(); + static void Stop(); // expected-note {{'Thread::Stop' declared here}} +}; + +class Manager { + public: + void Start(int); // expected-note {{'Start' declared here}} + void Stop(int); // expected-note {{'Stop' declared here}} +}; + +void test(Manager *m) { + // Don't suggest Thread::Start as a correction just because it has the same + // (unqualified) name and accepts the right number of args; this is a method + // call on an object in an unrelated class. + m->Start(); // expected-error-re {{too few arguments to function call, expected 1, have 0{{$}}}} + m->Stop(); // expected-error-re {{too few arguments to function call, expected 1, have 0{{$}}}} + Stop(); // expected-error {{use of undeclared identifier 'Stop'; did you mean 'Thread::Stop'?}} +} + +} + +namespace std { +class bernoulli_distribution { + public: + double p() const; }; } +void test() { + // Make sure that typo correction doesn't suggest changing 'p' to + // 'std::bernoulli_distribution::p' as that is most likely wrong. + if (p) // expected-error-re {{use of undeclared identifier 'p'{{$}}}} + return; +} + +namespace PR19681 { + struct TypoA {}; + struct TypoB { + void test(); + private: + template<typename T> void private_memfn(T); // expected-note{{declared here}} + }; + void TypoB::test() { + // FIXME: should suggest 'PR19681::TypoB::private_memfn' instead of '::PR19681::TypoB::private_memfn' + (void)static_cast<void(TypoB::*)(int)>(&TypoA::private_memfn); // expected-error{{no member named 'private_memfn' in 'PR19681::TypoA'; did you mean '::PR19681::TypoB::private_memfn'?}} + } +} + +namespace testWantFunctionLikeCasts { + long test(bool a) { + if (a) + return struc(5.7); // expected-error-re {{use of undeclared identifier 'struc'{{$}}}} + else + return lon(8.0); // expected-error {{use of undeclared identifier 'lon'; did you mean 'long'?}} + } +} + +namespace testCXXDeclarationSpecifierParsing { +namespace test { + struct SomeSettings {}; // expected-note {{'test::SomeSettings' declared here}} +} +class Test {}; +int bar() { + Test::SomeSettings some_settings; // expected-error {{no type named 'SomeSettings' in 'testCXXDeclarationSpecifierParsing::Test'; did you mean 'test::SomeSettings'?}} +} +} + +namespace testNonStaticMemberHandling { +struct Foo { + bool usesMetadata; // expected-note {{'usesMetadata' declared here}} +}; +int test(Foo f) { + if (UsesMetadata) // expected-error-re {{use of undeclared identifier 'UsesMetadata'{{$}}}} + return 5; + if (f.UsesMetadata) // expected-error {{no member named 'UsesMetadata' in 'testNonStaticMemberHandling::Foo'; did you mean 'usesMetadata'?}} + return 11; + return 0; +} +}; + +namespace testMemberExprDeclarationNameInfo { + // The AST should only have the corrected name with no mention of 'data_'. + void f(int); + struct S { + int data; // expected-note 2{{'data' declared here}} + void m_fn1() { + data_ // expected-error {{use of undeclared identifier 'data_'}} + [] = // expected-error {{expected expression}} + f(data_); // expected-error {{use of undeclared identifier 'data_'}} + } + }; +} + +namespace testArraySubscriptIndex { + struct S { + int data; // expected-note {{'data' declared here}} + void m_fn1() { + (+)[data_]; // expected-error{{expected expression}} expected-error {{use of undeclared identifier 'data_'; did you mean 'data'}} + } + }; +} -- 2.40.0