Skip to content

Commit c2fa169

Browse files
committed
Add support for C++ namespace-aware typo correction, e.g., correcting
vector<int> to std::vector<int> Patch by Kaelyn Uhrain, with minor tweaks + PCH support from me. Fixes PR5776/<rdar://problem/8652971>. Thanks Kaelyn! llvm-svn: 134007
1 parent d79f966 commit c2fa169

25 files changed

+1060
-517
lines changed

clang/include/clang/Sema/ExternalSemaSource.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ class ExternalSemaSource : public ExternalASTSource {
4949
/// instance and factory methods, respectively, with this selector.
5050
virtual std::pair<ObjCMethodList,ObjCMethodList> ReadMethodPool(Selector Sel);
5151

52+
/// \brief Load the set of namespaces that are known to the external source,
53+
/// which will be used during typo correction.
54+
virtual void ReadKnownNamespaces(
55+
llvm::SmallVectorImpl<NamespaceDecl *> &Namespaces);
56+
5257
/// \brief Do last resort, unqualified lookup on a LookupResult that
5358
/// Sema cannot find.
5459
///

clang/include/clang/Sema/Sema.h

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "clang/Sema/ObjCMethodList.h"
2222
#include "clang/Sema/DeclSpec.h"
2323
#include "clang/Sema/LocInfoType.h"
24+
#include "clang/Sema/TypoCorrection.h"
2425
#include "clang/AST/Expr.h"
2526
#include "clang/AST/DeclarationName.h"
2627
#include "clang/AST/ExternalASTSource.h"
@@ -46,6 +47,8 @@ namespace clang {
4647
class ASTConsumer;
4748
class ASTContext;
4849
class ASTMutationListener;
50+
class ASTReader;
51+
class ASTWriter;
4952
class ArrayType;
5053
class AttributeList;
5154
class BlockDecl;
@@ -1623,6 +1626,16 @@ class Sema {
16231626
bool ConstThis,
16241627
bool VolatileThis);
16251628

1629+
// \brief The set of known/encountered (unique, canonicalized) NamespaceDecls.
1630+
//
1631+
// The boolean value will be true to indicate that the namespace was loaded
1632+
// from an AST/PCH file, or false otherwise.
1633+
llvm::DenseMap<NamespaceDecl*, bool> KnownNamespaces;
1634+
1635+
/// \brief Whether we have already loaded known namespaces from an extenal
1636+
/// source.
1637+
bool LoadedExternalKnownNamespaces;
1638+
16261639
public:
16271640
/// \brief Look up a name, looking for a single declaration. Return
16281641
/// null if the results were absent, ambiguous, or overloaded.
@@ -1699,11 +1712,13 @@ class Sema {
16991712
CTC_ObjCMessageReceiver
17001713
};
17011714

1702-
DeclarationName CorrectTypo(LookupResult &R, Scope *S, CXXScopeSpec *SS,
1703-
DeclContext *MemberContext = 0,
1704-
bool EnteringContext = false,
1705-
CorrectTypoContext CTC = CTC_Unknown,
1706-
const ObjCObjectPointerType *OPT = 0);
1715+
TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
1716+
Sema::LookupNameKind LookupKind,
1717+
Scope *S, CXXScopeSpec *SS,
1718+
DeclContext *MemberContext = NULL,
1719+
bool EnteringContext = false,
1720+
CorrectTypoContext CTC = CTC_Unknown,
1721+
const ObjCObjectPointerType *OPT = NULL);
17071722

17081723
void FindAssociatedClassesAndNamespaces(Expr **Args, unsigned NumArgs,
17091724
AssociatedNamespaceSet &AssociatedNamespaces,
@@ -4721,7 +4736,7 @@ class Sema {
47214736
/// \brief The number of typos corrected by CorrectTypo.
47224737
unsigned TyposCorrected;
47234738

4724-
typedef llvm::DenseMap<IdentifierInfo *, std::pair<llvm::StringRef, bool> >
4739+
typedef llvm::DenseMap<IdentifierInfo *, TypoCorrection>
47254740
UnqualifiedTyposCorrectedMap;
47264741

47274742
/// \brief A cache containing the results of typo correction for unqualified
@@ -5918,6 +5933,8 @@ class Sema {
59185933
protected:
59195934
friend class Parser;
59205935
friend class InitializationSequence;
5936+
friend class ASTReader;
5937+
friend class ASTWriter;
59215938

59225939
public:
59235940
/// \brief Retrieve the parser's current scope.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file defines the TypoCorrection class, which stores the results of
11+
// Sema's typo correction (Sema::CorrectTypo).
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H
16+
#define LLVM_CLANG_SEMA_TYPOCORRECTION_H
17+
18+
#include "clang/AST/DeclCXX.h"
19+
20+
namespace clang {
21+
22+
/// @brief Simple class containing the result of Sema::CorrectTypo
23+
class TypoCorrection {
24+
public:
25+
TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl,
26+
NestedNameSpecifier *NNS=NULL, unsigned distance=0)
27+
: CorrectionName(Name),
28+
CorrectionNameSpec(NNS),
29+
CorrectionDecl(NameDecl),
30+
EditDistance(distance) {}
31+
32+
TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=NULL,
33+
unsigned distance=0)
34+
: CorrectionName(Name->getDeclName()),
35+
CorrectionNameSpec(NNS),
36+
CorrectionDecl(Name),
37+
EditDistance(distance) {}
38+
39+
TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=NULL,
40+
unsigned distance=0)
41+
: CorrectionName(Name),
42+
CorrectionNameSpec(NNS),
43+
CorrectionDecl(NULL),
44+
EditDistance(distance) {}
45+
46+
TypoCorrection()
47+
: CorrectionName(), CorrectionNameSpec(NULL), CorrectionDecl(NULL),
48+
EditDistance(0) {}
49+
50+
/// \brief Gets the DeclarationName of the typo correction
51+
DeclarationName getCorrection() const { return CorrectionName; }
52+
IdentifierInfo* getCorrectionAsIdentifierInfo() const {
53+
return CorrectionName.getAsIdentifierInfo();
54+
}
55+
56+
/// \brief Gets the NestedNameSpecifier needed to use the typo correction
57+
NestedNameSpecifier* getCorrectionSpecifier() const {
58+
return CorrectionNameSpec;
59+
}
60+
void setCorrectionSpecifier(NestedNameSpecifier* NNS) {
61+
CorrectionNameSpec = NNS;
62+
}
63+
64+
/// \brief Gets the "edit distance" of the typo correction from the typo
65+
unsigned getEditDistance() const { return EditDistance; }
66+
67+
/// \brief Gets the pointer to the declaration of the typo correction
68+
NamedDecl* getCorrectionDecl() const {
69+
return isKeyword() ? NULL : CorrectionDecl;
70+
}
71+
template <class DeclClass>
72+
DeclClass *getCorrectionDeclAs() const {
73+
return dyn_cast_or_null<DeclClass>(getCorrectionDecl());
74+
}
75+
76+
void setCorrectionDecl(NamedDecl *CDecl) {
77+
CorrectionDecl = CDecl;
78+
if (!CorrectionName)
79+
CorrectionName = CDecl->getDeclName();
80+
}
81+
82+
std::string getAsString(const LangOptions &LO) const;
83+
std::string getQuoted(const LangOptions &LO) const {
84+
return "'" + getAsString(LO) + "'";
85+
}
86+
87+
operator bool() const { return bool(CorrectionName); }
88+
89+
static inline NamedDecl *KeywordDecl() { return (NamedDecl*)-1; }
90+
bool isKeyword() const { return CorrectionDecl == KeywordDecl(); }
91+
92+
// Returns true if the correction either is a keyword or has a known decl.
93+
bool isResolved() const { return CorrectionDecl != NULL; }
94+
95+
private:
96+
// Results.
97+
DeclarationName CorrectionName;
98+
NestedNameSpecifier *CorrectionNameSpec;
99+
NamedDecl *CorrectionDecl;
100+
unsigned EditDistance;
101+
};
102+
103+
}
104+
105+
#endif

clang/include/clang/Serialization/ASTBitCodes.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,11 @@ namespace clang {
371371

372372
/// \brief Record code for the table of offsets into the block
373373
/// of file source-location information.
374-
FILE_SOURCE_LOCATION_OFFSETS = 45
374+
FILE_SOURCE_LOCATION_OFFSETS = 45,
375+
376+
/// \brief Record code for the set of known namespaces, which are used
377+
/// for typo correction.
378+
KNOWN_NAMESPACES = 46
375379

376380
};
377381

clang/include/clang/Serialization/ASTReader.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,9 @@ class ASTReader
625625
/// \brief The OpenCL extension settings.
626626
llvm::SmallVector<uint64_t, 1> OpenCLExtensions;
627627

628+
/// \brief A list of the namespaces we've seen.
629+
llvm::SmallVector<uint64_t, 4> KnownNamespaces;
630+
628631
//@}
629632

630633
/// \brief Diagnostic IDs and their mappings that the user changed.
@@ -1125,6 +1128,11 @@ class ASTReader
11251128
virtual std::pair<ObjCMethodList, ObjCMethodList>
11261129
ReadMethodPool(Selector Sel);
11271130

1131+
/// \brief Load the set of namespaces that are known to the external source,
1132+
/// which will be used during typo correction.
1133+
virtual void ReadKnownNamespaces(
1134+
llvm::SmallVectorImpl<NamespaceDecl *> &Namespaces);
1135+
11281136
/// \brief Load a selector from disk, registering its ID if it exists.
11291137
void LoadSelector(Selector Sel);
11301138

clang/lib/Sema/Sema.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
154154
AnalysisWarnings(*this)
155155
{
156156
TUScope = 0;
157+
LoadedExternalKnownNamespaces = false;
158+
157159
if (getLangOptions().CPlusPlus)
158160
FieldCollector.reset(new CXXFieldCollector());
159161

@@ -778,6 +780,10 @@ ExternalSemaSource::ReadMethodPool(Selector Sel) {
778780
return std::pair<ObjCMethodList, ObjCMethodList>();
779781
}
780782

783+
void ExternalSemaSource::ReadKnownNamespaces(
784+
llvm::SmallVectorImpl<NamespaceDecl *> &Namespaces) {
785+
}
786+
781787
void PrettyDeclStackTraceEntry::print(llvm::raw_ostream &OS) const {
782788
SourceLocation Loc = this->Loc;
783789
if (!Loc.isValid() && TheDecl) Loc = TheDecl->getLocation();

clang/lib/Sema/SemaCXXScopeSpec.cpp

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -464,26 +464,29 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S,
464464
// We haven't found anything, and we're not recovering from a
465465
// different kind of error, so look for typos.
466466
DeclarationName Name = Found.getLookupName();
467-
if (CorrectTypo(Found, S, &SS, LookupCtx, EnteringContext,
468-
CTC_NoKeywords) &&
469-
Found.isSingleResult() &&
470-
isAcceptableNestedNameSpecifier(Found.getAsSingle<NamedDecl>())) {
467+
TypoCorrection Corrected;
468+
Found.clear();
469+
if ((Corrected = CorrectTypo(Found.getLookupNameInfo(),
470+
Found.getLookupKind(), S, &SS, LookupCtx,
471+
EnteringContext, CTC_NoKeywords)) &&
472+
isAcceptableNestedNameSpecifier(Corrected.getCorrectionDecl())) {
473+
std::string CorrectedStr(Corrected.getAsString(getLangOptions()));
474+
std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOptions()));
471475
if (LookupCtx)
472476
Diag(Found.getNameLoc(), diag::err_no_member_suggest)
473-
<< Name << LookupCtx << Found.getLookupName() << SS.getRange()
474-
<< FixItHint::CreateReplacement(Found.getNameLoc(),
475-
Found.getLookupName().getAsString());
477+
<< Name << LookupCtx << CorrectedQuotedStr << SS.getRange()
478+
<< FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr);
476479
else
477480
Diag(Found.getNameLoc(), diag::err_undeclared_var_use_suggest)
478-
<< Name << Found.getLookupName()
479-
<< FixItHint::CreateReplacement(Found.getNameLoc(),
480-
Found.getLookupName().getAsString());
481+
<< Name << CorrectedQuotedStr
482+
<< FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr);
481483

482-
if (NamedDecl *ND = Found.getAsSingle<NamedDecl>())
483-
Diag(ND->getLocation(), diag::note_previous_decl)
484-
<< ND->getDeclName();
484+
if (NamedDecl *ND = Corrected.getCorrectionDecl()) {
485+
Diag(ND->getLocation(), diag::note_previous_decl) << CorrectedQuotedStr;
486+
Found.addDecl(ND);
487+
}
488+
Found.setLookupName(Corrected.getCorrection());
485489
} else {
486-
Found.clear();
487490
Found.setLookupName(&Identifier);
488491
}
489492
}

0 commit comments

Comments
 (0)