Offer typo suggestions for 'using' declarations.
Patch courtesy of Luke Zarko <zarko@google.com> llvm-svn: 186019
This commit is contained in:
parent
fc3876118d
commit
8ec9f5f60e
|
@ -7024,6 +7024,35 @@ void Sema::HideUsingShadowDecl(Scope *S, UsingShadowDecl *Shadow) {
|
||||||
// be possible for this to happen, because...?
|
// be possible for this to happen, because...?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UsingValidatorCCC : public CorrectionCandidateCallback {
|
||||||
|
public:
|
||||||
|
UsingValidatorCCC(bool IsTypeName, bool IsInstantiation)
|
||||||
|
: IsTypeName(IsTypeName), IsInstantiation(IsInstantiation) {}
|
||||||
|
|
||||||
|
virtual bool ValidateCandidate(const TypoCorrection &Candidate) {
|
||||||
|
if (NamedDecl *ND = Candidate.getCorrectionDecl()) {
|
||||||
|
if (isa<NamespaceDecl>(ND))
|
||||||
|
return false;
|
||||||
|
// Completely unqualified names are invalid for a 'using' declaration.
|
||||||
|
bool droppedSpecifier = Candidate.WillReplaceSpecifier() &&
|
||||||
|
!Candidate.getCorrectionSpecifier();
|
||||||
|
if (droppedSpecifier)
|
||||||
|
return false;
|
||||||
|
else if (isa<TypeDecl>(ND))
|
||||||
|
return IsTypeName || !IsInstantiation;
|
||||||
|
else
|
||||||
|
return !IsTypeName;
|
||||||
|
} else {
|
||||||
|
// Keywords are not valid here.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool IsTypeName;
|
||||||
|
bool IsInstantiation;
|
||||||
|
};
|
||||||
|
|
||||||
/// Builds a using declaration.
|
/// Builds a using declaration.
|
||||||
///
|
///
|
||||||
/// \param IsInstantiation - Whether this call arises from an
|
/// \param IsInstantiation - Whether this call arises from an
|
||||||
|
@ -7133,11 +7162,32 @@ NamedDecl *Sema::BuildUsingDeclaration(Scope *S, AccessSpecifier AS,
|
||||||
|
|
||||||
LookupQualifiedName(R, LookupContext);
|
LookupQualifiedName(R, LookupContext);
|
||||||
|
|
||||||
|
// Try to correct typos if possible.
|
||||||
if (R.empty()) {
|
if (R.empty()) {
|
||||||
Diag(IdentLoc, diag::err_no_member)
|
UsingValidatorCCC CCC(IsTypeName, IsInstantiation);
|
||||||
<< NameInfo.getName() << LookupContext << SS.getRange();
|
if (TypoCorrection Corrected = CorrectTypo(R.getLookupNameInfo(),
|
||||||
UD->setInvalidDecl();
|
R.getLookupKind(), S, &SS, CCC)){
|
||||||
return UD;
|
// We reject any correction for which ND would be NULL.
|
||||||
|
NamedDecl *ND = Corrected.getCorrectionDecl();
|
||||||
|
std::string CorrectedStr(Corrected.getAsString(getLangOpts()));
|
||||||
|
std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOpts()));
|
||||||
|
R.setLookupName(Corrected.getCorrection());
|
||||||
|
R.addDecl(ND);
|
||||||
|
// We reject candidates where droppedSpecifier == true, hence the
|
||||||
|
// literal '0' below.
|
||||||
|
Diag(R.getNameLoc(), diag::err_no_member_suggest)
|
||||||
|
<< NameInfo.getName() << LookupContext << 0
|
||||||
|
<< CorrectedQuotedStr << SS.getRange()
|
||||||
|
<< FixItHint::CreateReplacement(Corrected.getCorrectionRange(),
|
||||||
|
CorrectedStr);
|
||||||
|
Diag(ND->getLocation(), diag::note_previous_decl)
|
||||||
|
<< CorrectedQuotedStr;
|
||||||
|
} else {
|
||||||
|
Diag(IdentLoc, diag::err_no_member)
|
||||||
|
<< NameInfo.getName() << LookupContext << SS.getRange();
|
||||||
|
UD->setInvalidDecl();
|
||||||
|
return UD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (R.isAmbiguous()) {
|
if (R.isAmbiguous()) {
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||||
|
// RUN: cp %s %t
|
||||||
|
// RUN: not %clang_cc1 -fsyntax-only -fixit -x c++ %t
|
||||||
|
// RUN: %clang_cc1 -fsyntax-only -pedantic -Werror -x c++ %t
|
||||||
|
// RUN: grep using_suggestion_tyname_ty_dropped_specifier %t
|
||||||
|
|
||||||
|
// These tests have been separated from typo.cpp to keep the maximum typo
|
||||||
|
// correction counter from ticking over; this causes spurious failures.
|
||||||
|
|
||||||
|
namespace using_suggestion_ty {
|
||||||
|
namespace N { class AAA {}; } // expected-note {{'AAA' declared here}}
|
||||||
|
using N::AAB; // expected-error {{no member named 'AAB' in namespace 'using_suggestion_ty::N'; did you mean 'AAA'?}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_tyname_ty {
|
||||||
|
namespace N { class AAA {}; } // expected-note {{'AAA' declared here}}
|
||||||
|
using typename N::AAB; // expected-error {{no member named 'AAB' in namespace 'using_suggestion_tyname_ty::N'; did you mean 'AAA'?}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_val {
|
||||||
|
namespace N { void FFF() {} } // expected-note {{'FFF' declared here}}
|
||||||
|
using N::FFG; // expected-error {{no member named 'FFG' in namespace 'using_suggestion_val::N'; did you mean 'FFF'?}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_ty_dropped_specifier {
|
||||||
|
class AAA {}; // expected-note {{'::using_suggestion_ty_dropped_specifier::AAA' declared here}}
|
||||||
|
namespace N { }
|
||||||
|
using N::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_ty_dropped_specifier::N'; did you mean '::using_suggestion_ty_dropped_specifier::AAA'?}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_tyname_ty_dropped_specifier {
|
||||||
|
class AAA {}; // expected-note {{'::using_suggestion_tyname_ty_dropped_specifier::AAA' declared here}}
|
||||||
|
namespace N { }
|
||||||
|
using typename N::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_tyname_ty_dropped_specifier::N'; did you mean '::using_suggestion_tyname_ty_dropped_specifier::AAA'?}}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 using_suggestion_member_ty {
|
||||||
|
class CCC { public: typedef int AAA; }; // expected-note {{'AAA' declared here}}
|
||||||
|
class DDD : public CCC { public: using CCC::AAB; }; // expected-error {{no member named 'AAB' in 'using_suggestion_member_ty::CCC'; did you mean 'AAA'?}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_member_val {
|
||||||
|
class CCC { public: void AAA() { } }; // expected-note {{'AAA' declared here}}
|
||||||
|
class DDD : public CCC { public: using CCC::AAB; }; // expected-error {{no member named 'AAB' in 'using_suggestion_member_val::CCC'; did you mean 'AAA'?}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_member_tyname_ty {
|
||||||
|
class CCC { public: typedef int AAA; }; // expected-note {{'AAA' declared here}}
|
||||||
|
class DDD : public CCC { public: using typename CCC::AAB; }; // expected-error {{no member named 'AAB' in 'using_suggestion_member_tyname_ty::CCC'; did you mean 'AAA'?}}
|
||||||
|
}
|
|
@ -118,3 +118,45 @@ namespace foo
|
||||||
using ::foo::Class1::Function; // expected-error{{incomplete type 'foo::Class1' named in nested name specifier}}
|
using ::foo::Class1::Function; // expected-error{{incomplete type 'foo::Class1' named in nested name specifier}}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't suggest non-typenames for positions requiring typenames.
|
||||||
|
namespace using_suggestion_tyname_val {
|
||||||
|
namespace N { void FFF() {} }
|
||||||
|
using typename N::FFG; // expected-error {{no member named 'FFG' in namespace 'using_suggestion_tyname_val::N'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_member_tyname_val {
|
||||||
|
class CCC { public: void AAA() { } };
|
||||||
|
class DDD : public CCC { public: using typename CCC::AAB; }; // expected-error {{no member named 'AAB' in 'using_suggestion_member_tyname_val::CCC'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_tyname_val_dropped_specifier {
|
||||||
|
void FFF() {}
|
||||||
|
namespace N { }
|
||||||
|
using typename N::FFG; // expected-error {{no member named 'FFG' in namespace 'using_suggestion_tyname_val_dropped_specifier::N'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently hints aren't provided to drop out the incorrect M::.
|
||||||
|
namespace using_suggestion_ty_dropped_nested_specifier {
|
||||||
|
namespace N {
|
||||||
|
class AAA {};
|
||||||
|
namespace M { }
|
||||||
|
}
|
||||||
|
using N::M::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_ty_dropped_nested_specifier::N::M'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_tyname_ty_dropped_nested_specifier {
|
||||||
|
namespace N {
|
||||||
|
class AAA {};
|
||||||
|
namespace M { }
|
||||||
|
}
|
||||||
|
using typename N::M::AAA; // expected-error {{no member named 'AAA' in namespace 'using_suggestion_tyname_ty_dropped_nested_specifier::N::M'}}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace using_suggestion_val_dropped_nested_specifier {
|
||||||
|
namespace N {
|
||||||
|
void FFF() {}
|
||||||
|
namespace M { }
|
||||||
|
}
|
||||||
|
using N::M::FFF; // expected-error {{no member named 'FFF' in namespace 'using_suggestion_val_dropped_nested_specifier::N::M'}}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue