[Sema] Allow an external sema source to handle delayed typo corrections.

This probably isn't perfectly perfect but allows correcting function calls
again.

llvm-svn: 270039
This commit is contained in:
Benjamin Kramer 2016-05-19 10:46:10 +00:00
parent b5cb227b98
commit b872733851
2 changed files with 90 additions and 23 deletions

View File

@ -4781,11 +4781,19 @@ TypoExpr *Sema::CorrectTypoDelayed(
const ObjCObjectPointerType *OPT) {
assert(CCC && "CorrectTypoDelayed requires a CorrectionCandidateCallback");
TypoCorrection Empty;
auto Consumer = makeTypoCorrectionConsumer(
TypoName, LookupKind, S, SS, std::move(CCC), MemberContext,
EnteringContext, OPT, Mode == CTK_ErrorRecovery);
// Give the external sema source a chance to correct the typo.
TypoCorrection ExternalTypo;
if (ExternalSource && Consumer) {
ExternalTypo = ExternalSource->CorrectTypo(
TypoName, LookupKind, S, SS, *CCC, MemberContext, EnteringContext, OPT);
if (ExternalTypo)
Consumer->addCorrection(ExternalTypo);
}
if (!Consumer || Consumer->empty())
return nullptr;
@ -4793,7 +4801,7 @@ TypoExpr *Sema::CorrectTypoDelayed(
// is not more that about a third of the length of the typo's identifier.
unsigned ED = Consumer->getBestEditDistance(true);
IdentifierInfo *Typo = TypoName.getName().getAsIdentifierInfo();
if (ED > 0 && Typo->getName().size() / ED < 3)
if (!ExternalTypo && ED > 0 && Typo->getName().size() / ED < 3)
return nullptr;
ExprEvalContexts.back().NumTypos++;

View File

@ -39,19 +39,18 @@ public:
bool Result;
};
// \brief Counts the number of err_using_directive_member_suggest diagnostics
// correcting from one namespace to another while still passing all diagnostics
// along a chain of consumers.
class NamespaceDiagnosticWatcher : public clang::DiagnosticConsumer {
/// Counts the number of typo-correcting diagnostics correcting from one name to
/// another while still passing all diagnostics along a chain of consumers.
class DiagnosticWatcher : public clang::DiagnosticConsumer {
DiagnosticConsumer *Chained;
std::string FromNS;
std::string ToNS;
std::string FromName;
std::string ToName;
public:
NamespaceDiagnosticWatcher(StringRef From, StringRef To)
: Chained(nullptr), FromNS(From), ToNS("'"), SeenCount(0) {
ToNS.append(To);
ToNS.append("'");
DiagnosticWatcher(StringRef From, StringRef To)
: Chained(nullptr), FromName(From), ToName("'"), SeenCount(0) {
ToName.append(To);
ToName.append("'");
}
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
@ -61,7 +60,12 @@ public:
if (Info.getID() - 1 == diag::err_using_directive_member_suggest) {
const IdentifierInfo *Ident = Info.getArgIdentifier(0);
const std::string &CorrectedQuotedStr = Info.getArgStdStr(1);
if (Ident->getName() == FromNS && CorrectedQuotedStr == ToNS)
if (Ident->getName() == FromName && CorrectedQuotedStr == ToName)
++SeenCount;
} else if (Info.getID() == diag::err_no_member_suggest) {
auto Ident = DeclarationName::getFromOpaqueInteger(Info.getRawArg(0));
const std::string &CorrectedQuotedStr = Info.getArgStdStr(3);
if (Ident.getAsString() == FromName && CorrectedQuotedStr == ToName)
++SeenCount;
}
}
@ -78,7 +82,7 @@ public:
return false;
}
NamespaceDiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) {
DiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) {
Chained = ToChain;
return this;
}
@ -130,11 +134,53 @@ public:
int CallCount;
};
// \brief Chains together a vector of NamespaceDiagnosticWatchers and
class FunctionTypoProvider : public clang::ExternalSemaSource {
std::string CorrectFrom;
std::string CorrectTo;
Sema *CurrentSema;
public:
FunctionTypoProvider(StringRef From, StringRef To)
: CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}
void InitializeSema(Sema &S) override { CurrentSema = &S; }
void ForgetSema() override { CurrentSema = nullptr; }
TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
Scope *S, CXXScopeSpec *SS,
CorrectionCandidateCallback &CCC,
DeclContext *MemberContext, bool EnteringContext,
const ObjCObjectPointerType *OPT) override {
++CallCount;
if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
DeclContext *DestContext = nullptr;
ASTContext &Context = CurrentSema->getASTContext();
if (SS)
DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
if (!DestContext)
DestContext = Context.getTranslationUnitDecl();
IdentifierInfo *ToIdent =
CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
auto *NewFunction = FunctionDecl::Create(
Context, DestContext, SourceLocation(), SourceLocation(), ToIdent,
Context.getFunctionType(Context.VoidTy, {}, {}), nullptr, SC_Static);
DestContext->addDecl(NewFunction);
TypoCorrection Correction(ToIdent);
Correction.addCorrectionDecl(NewFunction);
return Correction;
}
return TypoCorrection();
}
int CallCount;
};
// \brief Chains together a vector of DiagnosticWatchers and
// adds a vector of ExternalSemaSources to the CompilerInstance before
// performing semantic analysis.
class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
std::vector<NamespaceDiagnosticWatcher *> Watchers;
std::vector<DiagnosticWatcher *> Watchers;
std::vector<clang::ExternalSemaSource *> Sources;
std::unique_ptr<DiagnosticConsumer> OwnedClient;
@ -170,16 +216,14 @@ public:
Sources.push_back(Source);
}
void PushWatcher(NamespaceDiagnosticWatcher *Watcher) {
Watchers.push_back(Watcher);
}
void PushWatcher(DiagnosticWatcher *Watcher) { Watchers.push_back(Watcher); }
};
// Make sure that the NamespaceDiagnosticWatcher is not miscounting.
// Make sure that the DiagnosticWatcher is not miscounting.
TEST(ExternalSemaSource, SanityCheck) {
std::unique_ptr<ExternalSemaSourceInstaller> Installer(
new ExternalSemaSourceInstaller);
NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
DiagnosticWatcher Watcher("AAB", "BBB");
Installer->PushWatcher(&Watcher);
std::vector<std::string> Args(1, "-std=c++11");
ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
@ -193,7 +237,7 @@ TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) {
std::unique_ptr<ExternalSemaSourceInstaller> Installer(
new ExternalSemaSourceInstaller);
NamespaceTypoProvider Provider("AAB", "BBB");
NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
DiagnosticWatcher Watcher("AAB", "BBB");
Installer->PushSource(&Provider);
Installer->PushWatcher(&Watcher);
std::vector<std::string> Args(1, "-std=c++11");
@ -211,7 +255,7 @@ TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {
NamespaceTypoProvider First("XXX", "BBB");
NamespaceTypoProvider Second("AAB", "CCC");
NamespaceTypoProvider Third("AAB", "DDD");
NamespaceDiagnosticWatcher Watcher("AAB", "CCC");
DiagnosticWatcher Watcher("AAB", "CCC");
Installer->PushSource(&First);
Installer->PushSource(&Second);
Installer->PushSource(&Third);
@ -225,6 +269,21 @@ TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {
ASSERT_EQ(1, Watcher.SeenCount);
}
TEST(ExternalSemaSource, ExternalDelayedTypoCorrection) {
std::unique_ptr<ExternalSemaSourceInstaller> Installer(
new ExternalSemaSourceInstaller);
FunctionTypoProvider Provider("aaa", "bbb");
DiagnosticWatcher Watcher("aaa", "bbb");
Installer->PushSource(&Provider);
Installer->PushWatcher(&Watcher);
std::vector<std::string> Args(1, "-std=c++11");
ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
Installer.release(), "namespace AAA { } void foo() { AAA::aaa(); }",
Args));
ASSERT_LE(0, Provider.CallCount);
ASSERT_EQ(1, Watcher.SeenCount);
}
// We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise
// solve the problem.
TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) {